summaryrefslogtreecommitdiff
path: root/sql
diff options
context:
space:
mode:
Diffstat (limited to 'sql')
-rw-r--r--sql/ChangeLog5
-rw-r--r--sql/Makefile.am21
-rw-r--r--sql/filesort.cc172
-rw-r--r--sql/ha_myisam.cc26
-rw-r--r--sql/ha_myisam.h1
-rw-r--r--sql/ha_myisammrg.cc4
-rw-r--r--sql/handler.cc2
-rw-r--r--sql/handler.h1
-rw-r--r--sql/item.cc2
-rw-r--r--sql/item_create.cc5
-rw-r--r--sql/item_create.h1
-rw-r--r--sql/item_strfunc.cc9
-rw-r--r--sql/item_sum.cc175
-rw-r--r--sql/item_sum.h27
-rw-r--r--sql/lex.h15
-rw-r--r--sql/log.cc46
-rw-r--r--sql/log_event.cc628
-rw-r--r--sql/log_event.h192
-rw-r--r--sql/md5.c14
-rw-r--r--sql/md5.h12
-rw-r--r--sql/mini_client.cc523
-rw-r--r--sql/mini_client.h11
-rw-r--r--sql/mysql_priv.h54
-rw-r--r--sql/mysqld.cc147
-rw-r--r--sql/net_serv.cc293
-rw-r--r--sql/opt_range.cc158
-rw-r--r--sql/opt_range.h14
-rw-r--r--sql/share/czech/errmsg.txt3
-rw-r--r--sql/share/danish/errmsg.txt3
-rw-r--r--sql/share/dutch/errmsg.txt3
-rw-r--r--sql/share/english/errmsg.txt7
-rw-r--r--sql/share/estonian/errmsg.txt27
-rw-r--r--sql/share/french/errmsg.txt3
-rw-r--r--sql/share/german/errmsg.txt3
-rw-r--r--sql/share/greek/errmsg.txt3
-rw-r--r--sql/share/hungarian/errmsg.txt3
-rw-r--r--sql/share/italian/errmsg.txt3
-rw-r--r--sql/share/japanese/errmsg.txt3
-rw-r--r--sql/share/korean/errmsg.txt3
-rw-r--r--sql/share/norwegian-ny/errmsg.txt3
-rw-r--r--sql/share/norwegian/errmsg.txt3
-rw-r--r--sql/share/polish/errmsg.txt3
-rw-r--r--sql/share/portuguese/errmsg.txt3
-rw-r--r--sql/share/romanian/errmsg.txt3
-rw-r--r--sql/share/russian/errmsg.txt3
-rw-r--r--sql/share/slovak/errmsg.txt3
-rw-r--r--sql/share/spanish/errmsg.txt3
-rw-r--r--sql/share/swedish/errmsg.OLD8
-rw-r--r--sql/share/swedish/errmsg.txt3
-rw-r--r--sql/slave.cc171
-rw-r--r--sql/slave.h19
-rw-r--r--sql/sql_analyse.cc31
-rw-r--r--sql/sql_analyse.h21
-rw-r--r--sql/sql_base.cc195
-rw-r--r--sql/sql_class.cc12
-rw-r--r--sql/sql_class.h81
-rw-r--r--sql/sql_db.cc91
-rw-r--r--sql/sql_delete.cc679
-rw-r--r--sql/sql_handler.cc254
-rw-r--r--sql/sql_insert.cc2
-rw-r--r--sql/sql_lex.cc18
-rw-r--r--sql/sql_lex.h70
-rw-r--r--sql/sql_parse.cc502
-rw-r--r--sql/sql_repl.cc453
-rw-r--r--sql/sql_repl.h18
-rw-r--r--sql/sql_select.cc190
-rw-r--r--sql/sql_select.h3
-rw-r--r--sql/sql_show.cc127
-rw-r--r--sql/sql_sort.h54
-rw-r--r--sql/sql_table.cc129
-rw-r--r--sql/sql_test.cc3
-rw-r--r--sql/sql_unions.cc34
-rw-r--r--sql/sql_update.cc50
-rw-r--r--sql/sql_yacc.yy907
-rw-r--r--sql/structs.h1
-rw-r--r--sql/table.h9
-rw-r--r--sql/uniques.cc164
-rw-r--r--sql/violite.c430
78 files changed, 5629 insertions, 1746 deletions
diff --git a/sql/ChangeLog b/sql/ChangeLog
index 2289765afad..a75e9761766 100644
--- a/sql/ChangeLog
+++ b/sql/ChangeLog
@@ -1,3 +1,8 @@
+2000-12-07 Jeremy Cole <jeremy@mysql.com>
+
+* Added UPDATE ... ORDER BY ...
+* Added DELETE ... ORDER BY ...
+
2000-11-11 Jeremy Cole <jeremy@mysql.com>
* Added ALTER TABLE ... ORDER BY ...
diff --git a/sql/Makefile.am b/sql/Makefile.am
index 774ef06f17c..70415be03a4 100644
--- a/sql/Makefile.am
+++ b/sql/Makefile.am
@@ -24,7 +24,7 @@ INCLUDES = @MT_INCLUDES@ \
@bdb_includes@ @innodb_includes@ @gemini_includes@ \
-I$(srcdir)/../include \
-I$(srcdir)/../regex \
- -I$(srcdir) -I../include -I.. -I.
+ -I$(srcdir) -I../include -I.. -I. $(openssl_includes)
WRAPLIBS= @WRAPLIBS@
SUBDIRS = share
bin_PROGRAMS = mysqlbinlog
@@ -36,13 +36,16 @@ LDADD = ../isam/libnisam.a \
../myisam/libmyisam.a \
../myisammrg/libmyisammrg.a \
../heap/libheap.a \
+ ../vio/libvio.a \
../mysys/libmysys.a \
../dbug/libdbug.a \
../regex/libregex.a \
../strings/libmystrings.a
+
mysqld_LDADD = @MYSQLD_EXTRA_LDFLAGS@ \
- @bdb_libs@ @innodb_libs@ @gemini_libs@ \
- $(LDADD) $(CXXLDFLAGS) $(WRAPLIBS) @LIBDL@
+ @bdb_libs@ @innodb_libs@ @pstack_libs@ \
+ @gemini_libs@ \
+ $(LDADD) $(CXXLDFLAGS) $(WRAPLIBS) @LIBDL@ @openssl_libs@
noinst_HEADERS = item.h item_func.h item_sum.h item_cmpfunc.h \
item_strfunc.h item_timefunc.h item_uniq.h \
item_create.h mysql_priv.h \
@@ -55,18 +58,18 @@ noinst_HEADERS = item.h item_func.h item_sum.h item_cmpfunc.h \
sql_select.h structs.h table.h sql_udf.h hash_filo.h\
lex.h lex_symbol.h sql_acl.h sql_crypt.h md5.h \
log_event.h mini_client.h sql_repl.h slave.h \
- stacktrace.h
-mysqld_SOURCES = sql_lex.cc \
+ stacktrace.h sql_sort.h
+mysqld_SOURCES = sql_lex.cc sql_handler.cc \
item.cc item_sum.cc item_buff.cc item_func.cc \
item_cmpfunc.cc item_strfunc.cc item_timefunc.cc \
thr_malloc.cc item_create.cc \
field.cc key.cc sql_class.cc sql_list.cc \
- net_serv.cc violite.c net_pkg.cc lock.cc my_lock.c \
+ net_serv.cc net_pkg.cc lock.cc my_lock.c \
sql_string.cc sql_manager.cc sql_map.cc \
mysqld.cc password.c hash_filo.cc hostname.cc \
convert.cc sql_parse.cc sql_yacc.yy \
sql_base.cc table.cc sql_select.cc sql_insert.cc \
- sql_update.cc sql_delete.cc \
+ sql_update.cc sql_delete.cc uniques.cc \
procedure.cc item_uniq.cc sql_test.cc \
log.cc log_event.cc init.cc derror.cc sql_acl.cc \
unireg.cc \
@@ -80,11 +83,11 @@ mysqld_SOURCES = sql_lex.cc \
sql_udf.cc sql_analyse.cc sql_analyse.h sql_cache.cc \
slave.cc sql_repl.cc \
mini_client.cc mini_client_errors.c \
- md5.c stacktrace.c
+ md5.c stacktrace.c sql_unions.cc
gen_lex_hash_SOURCES = gen_lex_hash.cc
gen_lex_hash_LDADD = $(LDADD) $(CXXLDFLAGS)
mysqlbinlog_SOURCES = mysqlbinlog.cc mini_client.cc net_serv.cc \
- mini_client_errors.c violite.c password.c
+ mini_client_errors.c password.c
mysqlbinlog_LDADD = $(LDADD) $(CXXLDFLAGS) $(mysqld_LDADD)
DEFS = -DMYSQL_SERVER \
diff --git a/sql/filesort.cc b/sql/filesort.cc
index 610fe2e966f..fe06d3eb9b6 100644
--- a/sql/filesort.cc
+++ b/sql/filesort.cc
@@ -22,13 +22,11 @@
#include <stddef.h> /* for macro offsetof */
#endif
#include <m_ctype.h>
+#include "sql_sort.h"
+
#ifndef THREAD
#define SKIPP_DBUG_IN_FILESORT
#endif
- /* static variabels */
-
-#define MERGEBUFF 7
-#define MERGEBUFF2 15
/* How to write record_ref. */
@@ -36,28 +34,6 @@
if (my_b_write((file),(byte*) (from),param->ref_length)) \
DBUG_RETURN(1);
-typedef struct st_buffpek { /* Struktur om sorteringsbuffrarna */
- my_off_t file_pos; /* Where we are in the sort file */
- uchar *base,*key; /* key pointers */
- ha_rows count; /* Number of rows in table */
- ulong mem_count; /* numbers of keys in memory */
- ulong max_keys; /* Max keys in buffert */
-} BUFFPEK;
-
-
-typedef struct st_sort_param {
- uint sort_length; /* Length of sortarg */
- uint keys; /* Max antal nycklar / buffert */
- uint ref_length; /* Length of record ref. */
- ha_rows max_rows,examined_rows;
- TABLE *sort_form; /* For quicker make_sortkey */
- SORT_FIELD *local_sortorder;
- SORT_FIELD *end;
-#ifdef USE_STRCOLL
- char* tmp_buffer;
-#endif
-} SORTPARAM;
-
/* functions defined in this file */
static char **make_char_array(register uint fields, uint length, myf my_flag);
@@ -70,27 +46,26 @@ static int write_keys(SORTPARAM *param,uchar * *sort_keys,
IO_CACHE *tempfile);
static void make_sortkey(SORTPARAM *param,uchar *to,
byte *ref_pos);
-static bool save_index(SORTPARAM *param,uchar **sort_keys, uint count);
-static int merge_many_buff(SORTPARAM *param,uchar * *sort_keys,
- BUFFPEK *buffpek,
- uint *maxbuffer, IO_CACHE *t_file);
-static uint read_to_buffer(IO_CACHE *fromfile,BUFFPEK *buffpek,
- uint sort_length);
-static int merge_buffers(SORTPARAM *param,IO_CACHE *from_file,
- IO_CACHE *to_file,uchar * *sort_keys,
- BUFFPEK *lastbuff,BUFFPEK *Fb,
- BUFFPEK *Tb,int flag);
-static int merge_index(SORTPARAM *param,uchar * *sort_keys,
+static int merge_index(SORTPARAM *param,uchar *sort_buffer,
BUFFPEK *buffpek,
uint maxbuffer,IO_CACHE *tempfile,
IO_CACHE *outfile);
+static bool save_index(SORTPARAM *param,uchar **sort_keys, uint count);
static uint sortlength(SORT_FIELD *sortorder,uint length);
- /* Makes a indexfil of recordnumbers of a sorted database */
- /* outfile is reset before data is written to it, if it wasn't
- open a new file is opened */
+ /*
+ Creates a set of pointers that can be used to read the rows
+ in sorted order. This should be done with the functions
+ in records.cc
-ha_rows filesort(TABLE **table, SORT_FIELD *sortorder, uint s_length,
+ Before calling filesort, one must have done
+ table->file->info(HA_STATUS_VARIABLE)
+
+ The result set is stored in table->io_cache or
+ table->record_pointers
+ */
+
+ha_rows filesort(TABLE *table, SORT_FIELD *sortorder, uint s_length,
SQL_SELECT *select, ha_rows special, ha_rows max_rows,
ha_rows *examined_rows)
{
@@ -102,19 +77,20 @@ ha_rows filesort(TABLE **table, SORT_FIELD *sortorder, uint s_length,
IO_CACHE tempfile,*selected_records_file,*outfile;
SORTPARAM param;
DBUG_ENTER("filesort");
- DBUG_EXECUTE("info",TEST_filesort(table,sortorder,s_length,special););
+ DBUG_EXECUTE("info",TEST_filesort(sortorder,s_length,special););
#ifdef SKIPP_DBUG_IN_FILESORT
DBUG_PUSH(""); /* No DBUG here */
#endif
- outfile= table[0]->io_cache;
+ outfile= table->io_cache;
my_b_clear(&tempfile);
buffpek= (BUFFPEK *) NULL; sort_keys= (uchar **) NULL; error= 1;
maxbuffer=1;
- param.ref_length= table[0]->file->ref_length;
+ param.ref_length= table->file->ref_length;
param.sort_length=sortlength(sortorder,s_length)+ param.ref_length;
param.max_rows= max_rows;
param.examined_rows=0;
+ param.unique_buff=0;
if (select && select->quick)
{
@@ -139,17 +115,14 @@ ha_rows filesort(TABLE **table, SORT_FIELD *sortorder, uint s_length,
#ifdef CAN_TRUST_RANGE
else if (select && select->quick && select->quick->records > 0L)
{
- /* Get record-count */
- table[0]->file->info(HA_STATUS_VARIABLE | HA_STATUS_NO_LOCK);
records=min((ha_rows) (select->quick->records*2+EXTRA_RECORDS*2),
- table[0]->file->records)+EXTRA_RECORDS;
+ table->file->records)+EXTRA_RECORDS;
selected_records_file=0;
}
#endif
else
{
- table[0]->file->info(HA_STATUS_VARIABLE | HA_STATUS_NO_LOCK);/* Get record-count */
- records=table[0]->file->estimate_number_of_rows();
+ records=table->file->estimate_number_of_rows();
selected_records_file= 0;
}
if (param.sort_length == param.ref_length && records > param.max_rows)
@@ -203,7 +176,7 @@ ha_rows filesort(TABLE **table, SORT_FIELD *sortorder, uint s_length,
my_error(ER_OUTOFMEMORY,MYF(ME_ERROR+ME_WAITTANG),sortbuff_size);
goto err;
}
- param.sort_form= table[0];
+ param.sort_form= table;
param.end=(param.local_sortorder=sortorder)+s_length;
if ((records=find_all_keys(&param,select,sort_keys,buffpek,&maxbuffer,
&tempfile, selected_records_file)) ==
@@ -225,12 +198,14 @@ ha_rows filesort(TABLE **table, SORT_FIELD *sortorder, uint s_length,
param.keys=((param.keys*(param.sort_length+sizeof(char*))) /
param.sort_length-1);
- if (merge_many_buff(&param,sort_keys,buffpek,&maxbuffer,&tempfile))
+ if (merge_many_buff(&param,(uchar*) sort_keys,buffpek,&maxbuffer,
+ &tempfile))
goto err;
if (flush_io_cache(&tempfile) ||
reinit_io_cache(&tempfile,READ_CACHE,0L,0,0))
goto err;
- if (merge_index(&param,sort_keys,buffpek,maxbuffer,&tempfile,outfile))
+ if (merge_index(&param,(uchar*) sort_keys,buffpek,maxbuffer,&tempfile,
+ outfile))
goto err;
}
if (records > param.max_rows)
@@ -629,8 +604,8 @@ static bool save_index(SORTPARAM *param, uchar **sort_keys, uint count)
/* Merge buffers to make < MERGEBUFF2 buffers */
-static int merge_many_buff(SORTPARAM *param, uchar **sort_keys,
- BUFFPEK *buffpek, uint *maxbuffer, IO_CACHE *t_file)
+int merge_many_buff(SORTPARAM *param, uchar *sort_buffer,
+ BUFFPEK *buffpek, uint *maxbuffer, IO_CACHE *t_file)
{
register int i;
IO_CACHE t_file2,*from_file,*to_file,*temp;
@@ -652,11 +627,11 @@ static int merge_many_buff(SORTPARAM *param, uchar **sort_keys,
lastbuff=buffpek;
for (i=0 ; i <= (int) *maxbuffer-MERGEBUFF*3/2 ; i+=MERGEBUFF)
{
- if (merge_buffers(param,from_file,to_file,sort_keys,lastbuff++,
+ if (merge_buffers(param,from_file,to_file,sort_buffer,lastbuff++,
buffpek+i,buffpek+i+MERGEBUFF-1,0))
break; /* purecov: inspected */
}
- if (merge_buffers(param,from_file,to_file,sort_keys,lastbuff++,
+ if (merge_buffers(param,from_file,to_file,sort_buffer,lastbuff++,
buffpek+i,buffpek+ *maxbuffer,0))
break; /* purecov: inspected */
if (flush_io_cache(to_file))
@@ -675,8 +650,8 @@ static int merge_many_buff(SORTPARAM *param, uchar **sort_keys,
/* Read data to buffer */
/* This returns (uint) -1 if something goes wrong */
-static uint read_to_buffer(IO_CACHE *fromfile, BUFFPEK *buffpek,
- uint sort_length)
+uint read_to_buffer(IO_CACHE *fromfile, BUFFPEK *buffpek,
+ uint sort_length)
{
register uint count;
uint length;
@@ -697,39 +672,38 @@ static uint read_to_buffer(IO_CACHE *fromfile, BUFFPEK *buffpek,
/* Merge buffers to one buffer */
-static int merge_buffers(SORTPARAM *param, IO_CACHE *from_file,
- IO_CACHE *to_file, uchar **sort_keys,
- BUFFPEK *lastbuff, BUFFPEK *Fb, BUFFPEK *Tb,
- int flag)
+int merge_buffers(SORTPARAM *param, IO_CACHE *from_file,
+ IO_CACHE *to_file, uchar *sort_buffer,
+ BUFFPEK *lastbuff, BUFFPEK *Fb, BUFFPEK *Tb,
+ int flag)
{
int error;
uint sort_length,offset;
ulong maxcount;
- ha_rows count,max_rows;
+ ha_rows max_rows,org_max_rows;
my_off_t to_start_filepos;
uchar *strpos;
BUFFPEK *buffpek,**refpek;
QUEUE queue;
volatile bool *killed= &current_thd->killed;
+ qsort2_cmp cmp;
DBUG_ENTER("merge_buffers");
statistic_increment(filesort_merge_passes, &LOCK_status);
- count=error=0;
- offset=param->sort_length-param->ref_length;
+ error=0;
+ offset=(sort_length=param->sort_length)-param->ref_length;
maxcount=(ulong) (param->keys/((uint) (Tb-Fb) +1));
to_start_filepos=my_b_tell(to_file);
- strpos=(uchar*) sort_keys;
- sort_length=param->sort_length;
- max_rows=param->max_rows;
+ strpos=(uchar*) sort_buffer;
+ org_max_rows=max_rows=param->max_rows;
if (init_queue(&queue,(uint) (Tb-Fb)+1,offsetof(BUFFPEK,key),0,
(int (*) (void *, byte *,byte*))
- get_ptr_compare(sort_length),(void*) &sort_length))
+ (cmp=get_ptr_compare(sort_length)),(void*) &sort_length))
DBUG_RETURN(1); /* purecov: inspected */
for (buffpek= Fb ; buffpek <= Tb ; buffpek++)
{
- count+= buffpek->count;
buffpek->base= strpos;
buffpek->max_keys=maxcount;
strpos+= (uint) (error=(int) read_to_buffer(from_file,buffpek,
@@ -739,6 +713,30 @@ static int merge_buffers(SORTPARAM *param, IO_CACHE *from_file,
queue_insert(&queue,(byte*) buffpek);
}
+ if (param->unique_buff)
+ {
+ /*
+ Called by Unique::get()
+ Copy the first argument to param->unique_buff for unique removal.
+ Store it also in 'to_file'.
+
+ This is safe as we know that there is always more than one element
+ in each block to merge (This is guaranteed by the Unique:: algorithm
+ */
+ buffpek=(BUFFPEK*) queue_top(&queue);
+ memcpy(param->unique_buff, buffpek->key, sort_length);
+ if (my_b_write(to_file,(byte*) buffpek->key, sort_length))
+ {
+ error=1; goto err; /* purecov: inspected */
+ }
+ buffpek->key+=sort_length;
+ buffpek->mem_count--;
+ max_rows--;
+ queue_replaced(&queue); // Top element has been used
+ }
+ else
+ cmp=0; // Not unique
+
while (queue.elements > 1)
{
if (*killed)
@@ -748,6 +746,13 @@ static int merge_buffers(SORTPARAM *param, IO_CACHE *from_file,
for (;;)
{
buffpek=(BUFFPEK*) queue_top(&queue);
+ if (cmp) // Remove duplicates
+ {
+ if (!(*cmp)(&sort_length, &(param->unique_buff),
+ (uchar**) &buffpek->key))
+ goto skip_duplicate;
+ memcpy(param->unique_buff, (uchar*) buffpek->key,sort_length);
+ }
if (flag == 0)
{
if (my_b_write(to_file,(byte*) buffpek->key, sort_length))
@@ -764,6 +769,8 @@ static int merge_buffers(SORTPARAM *param, IO_CACHE *from_file,
error=0; /* purecov: inspected */
goto end; /* purecov: inspected */
}
+
+ skip_duplicate:
buffpek->key+=sort_length;
if (! --buffpek->mem_count)
{
@@ -796,14 +803,28 @@ static int merge_buffers(SORTPARAM *param, IO_CACHE *from_file,
break; /* One buffer have been removed */
}
else if (error == -1)
- goto err; /* purecov: inspected */
+ goto err; /* purecov: inspected */
}
queue_replaced(&queue); /* Top element has been replaced */
}
}
buffpek=(BUFFPEK*) queue_top(&queue);
- buffpek->base=(uchar *) sort_keys;
+ buffpek->base= sort_buffer;
buffpek->max_keys=param->keys;
+
+ /*
+ As we know all entries in the buffer are unique, we only have to
+ check if the first one is the same as the last one we wrote
+ */
+ if (cmp)
+ {
+ if (!(*cmp)(&sort_length, &(param->unique_buff), (uchar**) &buffpek->key))
+ {
+ buffpek->key+=sort_length; // Remove duplicate
+ --buffpek->mem_count;
+ }
+ }
+
do
{
if ((ha_rows) buffpek->mem_count > max_rows)
@@ -811,6 +832,7 @@ static int merge_buffers(SORTPARAM *param, IO_CACHE *from_file,
buffpek->mem_count=(uint) max_rows;
buffpek->count=0; /* Don't read more */
}
+ max_rows-=buffpek->mem_count;
if (flag == 0)
{
if (my_b_write(to_file,(byte*) buffpek->key,
@@ -835,7 +857,7 @@ static int merge_buffers(SORTPARAM *param, IO_CACHE *from_file,
!= -1 && error != 0);
end:
- lastbuff->count=min(count,param->max_rows);
+ lastbuff->count=min(org_max_rows-max_rows,param->max_rows);
lastbuff->file_pos=to_start_filepos;
err:
delete_queue(&queue);
@@ -845,12 +867,12 @@ err:
/* Do a merge to output-file (save only positions) */
-static int merge_index(SORTPARAM *param, uchar **sort_keys,
+static int merge_index(SORTPARAM *param, uchar *sort_buffer,
BUFFPEK *buffpek, uint maxbuffer,
IO_CACHE *tempfile, IO_CACHE *outfile)
{
DBUG_ENTER("merge_index");
- if (merge_buffers(param,tempfile,outfile,sort_keys,buffpek,buffpek,
+ if (merge_buffers(param,tempfile,outfile,sort_buffer,buffpek,buffpek,
buffpek+maxbuffer,1))
DBUG_RETURN(1); /* purecov: inspected */
DBUG_RETURN(0);
diff --git a/sql/ha_myisam.cc b/sql/ha_myisam.cc
index 6409ec5d019..5ecaa83cce9 100644
--- a/sql/ha_myisam.cc
+++ b/sql/ha_myisam.cc
@@ -527,8 +527,8 @@ int ha_myisam::repair(THD *thd, MI_CHECK &param, bool optimize)
int error=0;
uint extra_testflag=0;
bool optimize_done= !optimize, statistics_done=0;
- char fixed_name[FN_REFLEN];
const char *old_proc_info=thd->proc_info;
+ char fixed_name[FN_REFLEN];
MYISAM_SHARE* share = file->s;
ha_rows rows= file->state->records;
DBUG_ENTER("ha_myisam::repair");
@@ -540,8 +540,7 @@ int ha_myisam::repair(THD *thd, MI_CHECK &param, bool optimize)
param.thd=thd;
param.tmpdir=mysql_tmpdir;
param.out_flag=0;
- VOID(fn_format(fixed_name,file->filename,"",MI_NAME_IEXT,
- 4+ (param.opt_follow_links ? 16 : 0)));
+ strmov(fixed_name,file->filename);
// Don't lock tables if we have used LOCK TABLE
if (!thd->locked_tables && mi_lock_database(file,F_WRLCK))
@@ -813,6 +812,8 @@ void ha_myisam::position(const byte* record)
void ha_myisam::info(uint flag)
{
MI_ISAMINFO info;
+ char name_buff[FN_REFLEN];
+
(void) mi_status(file,&info,flag);
if (flag & HA_STATUS_VARIABLE)
{
@@ -842,6 +843,18 @@ void ha_myisam::info(uint flag)
raid_type=info.raid_type;
raid_chunks=info.raid_chunks;
raid_chunksize=info.raid_chunksize;
+
+ /*
+ Set data_file_name and index_file_name to point at the symlink value
+ if table is symlinked (Ie; Real name is not same as generated name)
+ */
+ data_file_name=index_file_name=0;
+ fn_format(name_buff, file->filename, "", MI_NAME_DEXT, 2);
+ if (strcmp(name_buff, info.data_file_name))
+ data_file_name=info.data_file_name;
+ strmov(fn_ext(name_buff),MI_NAME_IEXT);
+ if (strcmp(name_buff, info.index_file_name))
+ index_file_name=info.index_file_name;
}
if (flag & HA_STATUS_ERRKEY)
{
@@ -897,6 +910,7 @@ THR_LOCK_DATA **ha_myisam::store_lock(THD *thd,
void ha_myisam::update_create_info(HA_CREATE_INFO *create_info)
{
+ MI_ISAMINFO info;
table->file->info(HA_STATUS_AUTO | HA_STATUS_CONST);
if (!(create_info->used_fields & HA_CREATE_USED_AUTO))
{
@@ -908,6 +922,8 @@ void ha_myisam::update_create_info(HA_CREATE_INFO *create_info)
create_info->raid_chunks= raid_chunks;
create_info->raid_chunksize= raid_chunksize;
}
+ create_info->data_file_name=data_file_name;
+ create_info->index_file_name=index_file_name;
}
@@ -1079,8 +1095,10 @@ int ha_myisam::create(const char *name, register TABLE *form,
create_info.raid_type=info->raid_type;
create_info.raid_chunks=info->raid_chunks ? info->raid_chunks : RAID_DEFAULT_CHUNKS;
create_info.raid_chunksize=info->raid_chunksize ? info->raid_chunksize : RAID_DEFAULT_CHUNKSIZE;
+ create_info.data_file_name= info->data_file_name;
+ create_info.index_file_name=info->index_file_name;
- error=mi_create(fn_format(buff,name,"","",2+4+16),
+ error=mi_create(fn_format(buff,name,"","",2+4),
form->keys,keydef,
(uint) (recinfo_pos-recinfo), recinfo,
0, (MI_UNIQUEDEF*) 0,
diff --git a/sql/ha_myisam.h b/sql/ha_myisam.h
index 6451e2b80ee..0a4a465a44b 100644
--- a/sql/ha_myisam.h
+++ b/sql/ha_myisam.h
@@ -38,6 +38,7 @@ class ha_myisam: public handler
{
MI_INFO *file;
uint int_option_flag;
+ char *data_file_name, *index_file_name;
int repair(THD *thd, MI_CHECK &param, bool optimize);
public:
diff --git a/sql/ha_myisammrg.cc b/sql/ha_myisammrg.cc
index b842c15cce0..e3e1d959438 100644
--- a/sql/ha_myisammrg.cc
+++ b/sql/ha_myisammrg.cc
@@ -232,7 +232,7 @@ void ha_myisammrg::update_create_info(HA_CREATE_INFO *create_info)
for (table=file->open_tables ; table != file->end_table ; table++)
{
- char *name=table->table->s->filename;
+ char *name=table->table->filename;
char buff[FN_REFLEN];
TABLE_LIST *ptr;
if (!(ptr = (TABLE_LIST *) thd->calloc(sizeof(TABLE_LIST))))
@@ -278,7 +278,7 @@ void ha_myisammrg::append_create_info(String *packet)
for (first=table=file->open_tables ; table != file->end_table ; table++)
{
- char *name=table->table->s->filename;
+ char *name=table->table->filename;
fn_format(buff,name,"","",3);
if (table != first)
packet->append(',');
diff --git a/sql/handler.cc b/sql/handler.cc
index bac24a6dba7..5b5d6d4764c 100644
--- a/sql/handler.cc
+++ b/sql/handler.cc
@@ -853,5 +853,5 @@ static int NEAR_F delete_file(const char *name,const char *ext,int extflag)
{
char buff[FN_REFLEN];
VOID(fn_format(buff,name,"",ext,extflag | 4));
- return(my_delete(buff,MYF(MY_WME)));
+ return(my_delete_with_symlink(buff,MYF(MY_WME)));
}
diff --git a/sql/handler.h b/sql/handler.h
index 7a28dc07a81..fc20e563f9f 100644
--- a/sql/handler.h
+++ b/sql/handler.h
@@ -142,6 +142,7 @@ typedef struct st_ha_create_information
ulonglong max_rows,min_rows;
ulonglong auto_increment_value;
char *comment,*password;
+ char *data_file_name, *index_file_name;
uint options; /* OR of HA_CREATE_ options */
uint raid_type,raid_chunks;
ulong raid_chunksize;
diff --git a/sql/item.cc b/sql/item.cc
index b268c5eb928..44bbf9a9cbc 100644
--- a/sql/item.cc
+++ b/sql/item.cc
@@ -561,7 +561,7 @@ bool Item_ref::fix_fields(THD *thd,TABLE_LIST *tables)
{
if (!ref)
{
- if (!(ref=find_item_in_list(this,thd->lex.item_list)))
+ if (!(ref=find_item_in_list(this,thd->lex.select->item_list)))
return 1;
max_length= (*ref)->max_length;
maybe_null= (*ref)->maybe_null;
diff --git a/sql/item_create.cc b/sql/item_create.cc
index ef9f5f2d38b..55f8bb140a9 100644
--- a/sql/item_create.cc
+++ b/sql/item_create.cc
@@ -129,6 +129,11 @@ Item *create_func_floor(Item* a)
return new Item_func_floor(a);
}
+Item *create_func_found_rows(void)
+{
+ return new Item_int("FOUND_ROWS()",(longlong) current_thd->found_rows(),21);
+}
+
Item *create_func_from_days(Item* a)
{
return new Item_func_from_days(a);
diff --git a/sql/item_create.h b/sql/item_create.h
index cc7497b0183..54d2ff035ea 100644
--- a/sql/item_create.h
+++ b/sql/item_create.h
@@ -37,6 +37,7 @@ Item *create_func_degrees(Item *);
Item *create_func_exp(Item* a);
Item *create_func_find_in_set(Item* a, Item *b);
Item *create_func_floor(Item* a);
+Item *create_func_found_rows(void);
Item *create_func_from_days(Item* a);
Item *create_func_get_lock(Item* a, Item *b);
Item *create_func_hex(Item *a);
diff --git a/sql/item_strfunc.cc b/sql/item_strfunc.cc
index 80f72c30e57..9d69b713611 100644
--- a/sql/item_strfunc.cc
+++ b/sql/item_strfunc.cc
@@ -30,7 +30,6 @@
#ifdef HAVE_CRYPT_H
#include <crypt.h>
#endif
-
#include "md5.h"
String empty_string("");
@@ -66,13 +65,13 @@ String *Item_func_md5::val_str(String *str)
String * sptr= args[0]->val_str(str);
if (sptr)
{
- MD5_CTX context;
+ my_MD5_CTX context;
unsigned char digest[16];
null_value=0;
- MD5Init (&context);
- MD5Update (&context,(unsigned char *) sptr->ptr(), sptr->length());
- MD5Final (digest, &context);
+ my_MD5Init (&context);
+ my_MD5Update (&context,(unsigned char *) sptr->ptr(), sptr->length());
+ my_MD5Final (digest, &context);
str->alloc(32); // Ensure that memory is free
sprintf((char *) str->ptr(),
"%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
diff --git a/sql/item_sum.cc b/sql/item_sum.cc
index 5b24a1eda90..2cf23d48bc6 100644
--- a/sql/item_sum.cc
+++ b/sql/item_sum.cc
@@ -788,11 +788,77 @@ String *Item_std_field::val_str(String *str)
#include "sql_select.h"
+static int simple_raw_key_cmp(void* arg, byte* key1, byte* key2)
+{
+ return memcmp(key1, key2, (int)arg);
+}
+
+static int simple_str_key_cmp(void* arg, byte* key1, byte* key2)
+{
+ return my_sortcmp(key1, key2, (int)arg);
+}
+
+// did not make this one static - at least gcc gets confused when
+// I try to declare a static function as a friend. If you can figure
+// out the syntax to make a static function a friend, make this one
+// static
+int composite_key_cmp(void* arg, byte* key1, byte* key2)
+{
+ Item_sum_count_distinct* item = (Item_sum_count_distinct*)arg;
+ Field** field = item->table->field, **field_end;
+ field_end = field + item->table->fields;
+ for(; field < field_end; ++field)
+ {
+ int res;
+ Field* f = *field;
+ int len = f->pack_length();
+ switch((*field)->type())
+ {
+ case FIELD_TYPE_STRING:
+ case FIELD_TYPE_VAR_STRING:
+ res = f->key_cmp(key1, key2);
+ break;
+ default:
+ res = memcmp(key1, key2, len);
+ break;
+ }
+ if(res)
+ return res;
+ key1 += len;
+ key2 += len;
+ }
+ return 0;
+}
+
+// helper function for walking the tree when we dump it to MyISAM -
+// tree_walk will call it for each
+// leaf
+
+int dump_leaf(byte* key, uint32 count __attribute__((unused)),
+ Item_sum_count_distinct* item)
+{
+ char* buf = item->table->record[0];
+ int error;
+ // the first item->rec_offset bytes are taken care of with
+ // restore_record(table,2) in setup()
+ memcpy(buf + item->rec_offset, key, item->tree.size_of_element);
+ if ((error = item->table->file->write_row(buf)))
+ {
+ if (error != HA_ERR_FOUND_DUPP_KEY &&
+ error != HA_ERR_FOUND_DUPP_UNIQUE)
+ return 1;
+ }
+
+ return 0;
+}
+
Item_sum_count_distinct::~Item_sum_count_distinct()
{
if (table)
free_tmp_table(current_thd, table);
delete tmp_table_param;
+ if(use_tree)
+ delete_tree(&tree);
}
@@ -829,22 +895,109 @@ bool Item_sum_count_distinct::setup(THD *thd)
tmp_table_param->cleanup();
}
if (!(table=create_tmp_table(thd, tmp_table_param, list, (ORDER*) 0, 1,
- 0, 0, current_lex->options | thd->options)))
+ 0, 0, current_lex->select->options | thd->options)))
return 1;
table->file->extra(HA_EXTRA_NO_ROWS); // Don't update rows
+ table->no_rows=1;
+
+
+ if(table->db_type == DB_TYPE_HEAP) // no blobs, otherwise it would be
+ // MyISAM
+ {
+ qsort_cmp2 compare_key;
+ void* cmp_arg;
+ int key_len;
+
+ // to make things easier for dump_leaf if we ever have to dump to
+ // MyISAM
+ restore_record(table,2);
+
+ if(table->fields == 1) // if we have only one field, which is
+ // the most common use of count(distinct), it is much faster
+ // to use a simpler key compare method that can take advantage
+ // of not having to worry about other fields
+ {
+ Field* field = table->field[0];
+ switch(field->type())
+ {
+ // if we have a string, we must take care of charsets
+ // and case sensitivity
+ case FIELD_TYPE_STRING:
+ case FIELD_TYPE_VAR_STRING:
+ compare_key = (qsort_cmp2)(field->binary() ? simple_raw_key_cmp:
+ simple_str_key_cmp);
+ break;
+ default: // since at this point we cannot have blobs
+ // anything else can be compared with memcmp
+ compare_key = (qsort_cmp2)simple_raw_key_cmp;
+ break;
+ }
+ cmp_arg = (void*)(key_len = field->pack_length());
+ rec_offset = 1;
+ }
+ else // too bad, cannot cheat - there is more than one field
+ {
+ bool all_binary = 1;
+ Field** field, **field_end;
+ field_end = (field = table->field) + table->fields;
+ for(key_len = 0; field < field_end; ++field)
+ {
+ key_len += (*field)->pack_length();
+ if(!(*field)->binary())
+ all_binary = 0;
+ }
+ rec_offset = table->reclength - key_len;
+ if(all_binary)
+ {
+ compare_key = (qsort_cmp2)simple_raw_key_cmp;
+ cmp_arg = (void*)key_len;
+ }
+ else
+ {
+ compare_key = (qsort_cmp2)composite_key_cmp ;
+ cmp_arg = (void*)this;
+ }
+ }
+
+ init_tree(&tree, min(max_heap_table_size, sortbuff_size/16),
+ key_len, compare_key, 0, 0);
+ tree.cmp_arg = cmp_arg;
+ use_tree = 1;
+
+ // the only time key_len could be 0 is if someone does
+ // count(distinct) on a char(0) field - stupid thing to do,
+ // but this has to be handled - otherwise someone can crash
+ // the server with a DoS attack
+ max_elements_in_tree = (key_len) ? max_heap_table_size/key_len :
+ 1;
+ }
+
return 0;
}
+int Item_sum_count_distinct::tree_to_myisam()
+{
+ if(create_myisam_from_heap(table, tmp_table_param,
+ HA_ERR_RECORD_FILE_FULL, 1) ||
+ tree_walk(&tree, (tree_walk_action)&dump_leaf, (void*)this,
+ left_root_right))
+ return 1;
+ delete_tree(&tree);
+ use_tree = 0;
+ return 0;
+}
void Item_sum_count_distinct::reset()
{
- if (table)
+ if (use_tree)
+ reset_tree(&tree);
+ else if (table)
{
table->file->extra(HA_EXTRA_NO_CACHE);
table->file->delete_all_rows();
table->file->extra(HA_EXTRA_WRITE_CACHE);
- (void) add();
}
+ (void) add();
}
bool Item_sum_count_distinct::add()
@@ -859,7 +1012,19 @@ bool Item_sum_count_distinct::add()
if ((*field)->is_real_null(0))
return 0; // Don't count NULL
- if ((error=table->file->write_row(table->record[0])))
+ if(use_tree)
+ {
+ // if the tree got too big, convert to MyISAM, otherwise
+ // insert into the tree
+ if(tree.elements_in_tree > max_elements_in_tree)
+ {
+ if(tree_to_myisam())
+ return 1;
+ }
+ else if(!tree_insert(&tree, table->record[0] + rec_offset, 0))
+ return 1;
+ }
+ else if ((error=table->file->write_row(table->record[0])))
{
if (error != HA_ERR_FOUND_DUPP_KEY &&
error != HA_ERR_FOUND_DUPP_UNIQUE)
@@ -875,6 +1040,8 @@ longlong Item_sum_count_distinct::val_int()
{
if (!table) // Empty query
return LL(0);
+ if(use_tree)
+ return tree.elements_in_tree;
table->file->info(HA_STATUS_VARIABLE | HA_STATUS_NO_LOCK);
return table->file->records;
}
diff --git a/sql/item_sum.h b/sql/item_sum.h
index f68dfee1b61..753a9de8b48 100644
--- a/sql/item_sum.h
+++ b/sql/item_sum.h
@@ -21,6 +21,8 @@
#pragma interface /* gcc class implementation */
#endif
+#include <my_tree.h>
+
class Item_sum :public Item_result_field
{
public:
@@ -145,12 +147,33 @@ class Item_sum_count_distinct :public Item_sum_int
table_map used_table_cache;
bool fix_fields(THD *thd,TABLE_LIST *tables);
TMP_TABLE_PARAM *tmp_table_param;
- bool always_null;
+ TREE tree;
+
+ // calculated based on max_heap_table_size. If reached,
+ // walk the tree and dump it into MyISAM table
+ uint max_elements_in_tree;
+
+ // the first few bytes of record ( at least one)
+ // are just markers for deleted and NULLs. We want to skip them since
+ // they will just bloat the tree without providing any valuable info
+ int rec_offset;
+
+ // If there are no blobs, we can use a tree, which
+ // is faster than heap table. In that case, we still use the table
+ // to help get things set up, but we insert nothing in it
+ bool use_tree;
+ bool always_null; // Set to 1 if the result is always NULL
+
+ int tree_to_myisam();
+
+ friend int composite_key_cmp(void* arg, byte* key1, byte* key2);
+ friend int dump_leaf(byte* key, uint32 count __attribute__((unused)),
+ Item_sum_count_distinct* item);
public:
Item_sum_count_distinct(List<Item> &list)
:Item_sum_int(list),table(0),used_table_cache(~(table_map) 0),
- tmp_table_param(0),always_null(0)
+ tmp_table_param(0),use_tree(0),always_null(0)
{ quick_group=0; }
~Item_sum_count_distinct();
table_map used_tables() const { return used_table_cache; }
diff --git a/sql/lex.h b/sql/lex.h
index d5df5ed5511..e9ab150f5b2 100644
--- a/sql/lex.h
+++ b/sql/lex.h
@@ -70,6 +70,7 @@ static SYMBOL symbols[] = {
{ "BIGINT", SYM(BIGINT),0,0},
{ "BIT", SYM(BIT_SYM),0,0},
{ "BINARY", SYM(BINARY),0,0},
+ { "BINLOG", SYM(BINLOG_SYM),0,0},
{ "BLOB", SYM(BLOB_SYM),0,0},
{ "BOOL", SYM(BOOL_SYM),0,0},
{ "BOTH", SYM(BOTH),0,0},
@@ -82,6 +83,7 @@ static SYMBOL symbols[] = {
{ "CHANGED", SYM(CHANGED),0,0},
{ "CHECK", SYM(CHECK_SYM),0,0},
{ "CHECKSUM", SYM(CHECKSUM_SYM),0,0},
+ { "CLOSE", SYM(CLOSE_SYM),0,0},
{ "COLUMN", SYM(COLUMN_SYM),0,0},
{ "COLUMNS", SYM(COLUMNS),0,0},
{ "COMMENT", SYM(COMMENT_SYM),0,0},
@@ -112,6 +114,8 @@ static SYMBOL symbols[] = {
{ "DELETE", SYM(DELETE_SYM),0,0},
{ "DESC", SYM(DESC),0,0},
{ "DESCRIBE", SYM(DESCRIBE),0,0},
+ { "DIRECTORY", SYM(DIRECTORY_SYM),0,0},
+ { "DISABLE", SYM(DISABLE_SYM),0,0},
{ "DISTINCT", SYM(DISTINCT),0,0},
{ "DISTINCTROW", SYM(DISTINCT),0,0}, /* Access likes this */
{ "DOUBLE", SYM(DOUBLE_SYM),0,0},
@@ -122,8 +126,10 @@ static SYMBOL symbols[] = {
{ "ELSE", SYM(ELSE),0,0},
{ "ESCAPE", SYM(ESCAPE_SYM),0,0},
{ "ESCAPED", SYM(ESCAPED),0,0},
+ { "ENABLE", SYM(ENABLE_SYM),0,0},
{ "ENCLOSED", SYM(ENCLOSED),0,0},
{ "ENUM", SYM(ENUM),0,0},
+ { "EVENTS", SYM(EVENTS_SYM),0,0},
{ "EXPLAIN", SYM(DESCRIBE),0,0},
{ "EXISTS", SYM(EXISTS),0,0},
{ "EXTENDED", SYM(EXTENDED_SYM),0,0},
@@ -153,6 +159,7 @@ static SYMBOL symbols[] = {
{ "GRANTS", SYM(GRANTS),0,0},
{ "GROUP", SYM(GROUP),0,0},
{ "HAVING", SYM(HAVING),0,0},
+ { "HANDLER", SYM(HANDLER_SYM),0,0},
{ "HEAP", SYM(HEAP_SYM),0,0},
{ "HIGH_PRIORITY", SYM(HIGH_PRIORITY),0,0},
{ "HOUR", SYM(HOUR_SYM),0,0},
@@ -163,6 +170,7 @@ static SYMBOL symbols[] = {
{ "IGNORE", SYM(IGNORE_SYM),0,0},
{ "IN", SYM(IN_SYM),0,0},
{ "INDEX", SYM(INDEX),0,0},
+ { "INDEXES", SYM(INDEXES),0,0},
{ "INFILE", SYM(INFILE),0,0},
{ "INNER", SYM(INNER_SYM),0,0},
{ "INNOBASE", SYM(INNOBASE_SYM),0,0},
@@ -186,6 +194,7 @@ static SYMBOL symbols[] = {
{ "KEY", SYM(KEY_SYM),0,0},
{ "KEYS", SYM(KEYS),0,0},
{ "KILL", SYM(KILL_SYM),0,0},
+ { "LAST", SYM(LAST_SYM),0,0},
{ "LAST_INSERT_ID", SYM(LAST_INSERT_ID),0,0},
{ "LEADING", SYM(LEADING),0,0},
{ "LEFT", SYM(LEFT),0,0},
@@ -227,11 +236,12 @@ static SYMBOL symbols[] = {
{ "MYISAM", SYM(MYISAM_SYM),0,0},
{ "NATURAL", SYM(NATURAL),0,0},
{ "NATIONAL", SYM(NATIONAL_SYM),0,0},
+ { "NEXT", SYM(NEXT_SYM),0,0},
{ "NCHAR", SYM(NCHAR_SYM),0,0},
- { "NUMERIC", SYM(NUMERIC_SYM),0,0},
{ "NO", SYM(NO_SYM),0,0},
{ "NOT", SYM(NOT),0,0},
{ "NULL", SYM(NULL_SYM),0,0},
+ { "NUMERIC", SYM(NUMERIC_SYM),0,0},
{ "ON", SYM(ON),0,0},
{ "OPEN", SYM(OPEN_SYM),0,0},
{ "OPTIMIZE", SYM(OPTIMIZE),0,0},
@@ -246,6 +256,7 @@ static SYMBOL symbols[] = {
{ "PASSWORD", SYM(PASSWORD),0,0},
{ "PURGE", SYM(PURGE),0,0},
{ "PRECISION", SYM(PRECISION),0,0},
+ { "PREV", SYM(PREV_SYM),0,0},
{ "PRIMARY", SYM(PRIMARY_SYM),0,0},
{ "PROCEDURE", SYM(PROCEDURE),0,0},
{ "PROCESS" , SYM(PROCESS),0,0},
@@ -288,6 +299,7 @@ static SYMBOL symbols[] = {
{ "SQL_BIG_SELECTS", SYM(SQL_BIG_SELECTS),0,0},
{ "SQL_BIG_TABLES", SYM(SQL_BIG_TABLES),0,0},
{ "SQL_BUFFER_RESULT", SYM(SQL_BUFFER_RESULT),0,0},
+ { "SQL_CALC_FOUND_ROWS", SYM(SQL_CALC_FOUND_ROWS),0,0},
{ "SQL_LOG_BIN", SYM(SQL_LOG_BIN),0,0},
{ "SQL_LOG_OFF", SYM(SQL_LOG_OFF),0,0},
{ "SQL_LOG_UPDATE", SYM(SQL_LOG_UPDATE),0,0},
@@ -393,6 +405,7 @@ static SYMBOL sql_functions[] = {
{ "FIND_IN_SET", SYM(FUNC_ARG2),0,CREATE_FUNC(create_func_find_in_set)},
{ "FLOOR", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_floor)},
{ "FORMAT", SYM(FORMAT_SYM),0,0},
+ { "FOUND_ROWS", SYM(FUNC_ARG0),0,CREATE_FUNC(create_func_found_rows)},
{ "FROM_DAYS", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_from_days)},
{ "FROM_UNIXTIME", SYM(FROM_UNIXTIME),0,0},
{ "GET_LOCK", SYM(FUNC_ARG2),0,CREATE_FUNC(create_func_get_lock)},
diff --git a/sql/log.cc b/sql/log.cc
index 1654f711af8..40e5d5673be 100644
--- a/sql/log.cc
+++ b/sql/log.cc
@@ -81,7 +81,7 @@ static int find_uniq_filename(char *name)
MYSQL_LOG::MYSQL_LOG(): last_time(0), query_start(0),index_file(-1),
name(0), log_type(LOG_CLOSED),write_error(0),
- inited(0), no_rotate(0)
+ inited(0), log_seq(1), no_rotate(0)
{
/*
We don't want to intialize LOCK_Log here as the thread system may
@@ -230,8 +230,11 @@ void MYSQL_LOG::open(const char *log_name, enum_log_type log_type_arg,
if ((do_magic && my_b_write(&log_file, (byte*) BINLOG_MAGIC, 4)) ||
open_index(O_APPEND | O_RDWR | O_CREAT))
goto err;
+
+ log_seq = 1;
Start_log_event s;
bool error;
+ s.set_log_seq(0, this);
s.write(&log_file);
flush_io_cache(&log_file);
pthread_mutex_lock(&LOCK_index);
@@ -531,6 +534,14 @@ void MYSQL_LOG::new_file()
to change base names at some point.
*/
Rotate_log_event r(new_name+dirname_length(new_name));
+ THD* thd = current_thd;
+ r.set_log_seq(0, this);
+ // this log rotation could have been initiated by a master of
+ // the slave running with log-bin
+ // we set the flag on rotate event to prevent inifinite log rotation
+ // loop
+ if(thd && slave_thd && thd == slave_thd)
+ r.flags |= LOG_EVENT_FORCED_ROTATE_F;
r.write(&log_file);
VOID(pthread_cond_broadcast(&COND_binlog_update));
}
@@ -626,6 +637,21 @@ bool MYSQL_LOG::write(THD *thd,enum enum_server_command command,
/* Write to binary log in a format to be used for replication */
+bool MYSQL_LOG::write(Slave_log_event* event_info)
+{
+ bool error;
+ if (!inited) // Can't use mutex if not init
+ return 0;
+ VOID(pthread_mutex_lock(&LOCK_log));
+ if(!event_info->log_seq)
+ event_info->set_log_seq(current_thd, this);
+ error = event_info->write(&log_file);
+ flush_io_cache(&log_file);
+ VOID(pthread_mutex_unlock(&LOCK_log));
+ return error;
+}
+
+
bool MYSQL_LOG::write(Query_log_event* event_info)
{
/* In most cases this is only called if 'is_open()' is true */
@@ -638,8 +664,12 @@ bool MYSQL_LOG::write(Query_log_event* event_info)
if (is_open())
{
THD *thd=event_info->thd;
+#ifdef USING_TRANSACTIONS
IO_CACHE *file = (event_info->cache_stmt ? &thd->transaction.trans_log :
&log_file);
+#else
+ IO_CACHE *file = &log_file;
+#endif
if ((!(thd->options & OPTION_BIN_LOG) &&
(thd->master_access & PROCESS_ACL)) ||
!db_ok(event_info->db, binlog_do_db, binlog_ignore_db))
@@ -652,16 +682,18 @@ bool MYSQL_LOG::write(Query_log_event* event_info)
if (thd->last_insert_id_used)
{
Intvar_log_event e((uchar)LAST_INSERT_ID_EVENT, thd->last_insert_id);
- if(thd->server_id)
- e.server_id = thd->server_id;
+ e.set_log_seq(thd, this);
+ if (thd->server_id)
+ e.server_id = thd->server_id;
if (e.write(file))
goto err;
}
if (thd->insert_id_used)
{
Intvar_log_event e((uchar)INSERT_ID_EVENT, thd->last_insert_id);
- if(thd->server_id)
- e.server_id = thd->server_id;
+ e.set_log_seq(thd, this);
+ if (thd->server_id)
+ e.server_id = thd->server_id;
if (e.write(file))
goto err;
}
@@ -674,10 +706,12 @@ bool MYSQL_LOG::write(Query_log_event* event_info)
// just in case somebody wants it later
thd->query_length = (uint)(p - buf);
Query_log_event e(thd, buf);
+ e.set_log_seq(thd, this);
if (e.write(file))
goto err;
thd->query_length = save_query_length; // clean up
}
+ event_info->set_log_seq(thd, this);
if (event_info->write(file) ||
file == &log_file && flush_io_cache(file))
goto err;
@@ -772,6 +806,7 @@ bool MYSQL_LOG::write(Load_log_event* event_info)
if ((thd->options & OPTION_BIN_LOG) ||
!(thd->master_access & PROCESS_ACL))
{
+ event_info->set_log_seq(thd, this);
if (event_info->write(&log_file) || flush_io_cache(&log_file))
{
if (!write_error)
@@ -923,6 +958,7 @@ void MYSQL_LOG::close(bool exiting)
if (log_type == LOG_BIN)
{
Stop_log_event s;
+ s.set_log_seq(0, this);
s.write(&log_file);
VOID(pthread_cond_broadcast(&COND_binlog_update));
}
diff --git a/sql/log_event.cc b/sql/log_event.cc
index ac985c266c8..5538e6c0b7f 100644
--- a/sql/log_event.cc
+++ b/sql/log_event.cc
@@ -20,6 +20,7 @@
#pragma implementation // gcc: Class implementation
#endif
#include "mysql_priv.h"
+#include "slave.h"
#endif /* MYSQL_CLIENT */
@@ -31,6 +32,7 @@ static void pretty_print_char(FILE* file, int c)
case '\r': fprintf(file, "\\r"); break;
case '\\': fprintf(file, "\\\\"); break;
case '\b': fprintf(file, "\\b"); break;
+ case '\t': fprintf(file, "\\t"); break;
case '\'': fprintf(file, "\\'"); break;
case 0 : fprintf(file, "\\0"); break;
default:
@@ -40,6 +42,220 @@ static void pretty_print_char(FILE* file, int c)
fputc('\'', file);
}
+#ifndef MYSQL_CLIENT
+
+static void pretty_print_char(String* packet, int c)
+{
+ packet->append('\'');
+ switch(c) {
+ case '\n': packet->append( "\\n"); break;
+ case '\r': packet->append( "\\r"); break;
+ case '\\': packet->append( "\\\\"); break;
+ case '\b': packet->append( "\\b"); break;
+ case '\t': packet->append( "\\t"); break;
+ case '\'': packet->append( "\\'"); break;
+ case 0 : packet->append( "\\0"); break;
+ default:
+ packet->append((char)c);
+ break;
+ }
+ packet->append('\'');
+}
+
+#endif
+
+const char* Log_event::get_type_str()
+{
+ switch(get_type_code())
+ {
+ case START_EVENT: return "Start";
+ case STOP_EVENT: return "Stop";
+ case QUERY_EVENT: return "Query";
+ case ROTATE_EVENT: return "Rotate";
+ case INTVAR_EVENT: return "Intvar";
+ case LOAD_EVENT: return "Load";
+ case SLAVE_EVENT: return "Slave";
+ default: /* impossible */ return "Unknown";
+ }
+}
+
+#ifndef MYSQL_CLIENT
+
+void Log_event::pack_info(String* packet)
+{
+ net_store_data(packet, "", 0);
+}
+
+void Query_log_event::pack_info(String* packet)
+{
+ String tmp;
+ if(db && db_len)
+ {
+ tmp.append("use ");
+ tmp.append(db, db_len);
+ tmp.append("; ", 2);
+ }
+
+ if(query && q_len)
+ tmp.append(query, q_len);
+ net_store_data(packet, (char*)tmp.ptr(), tmp.length());
+}
+
+void Start_log_event::pack_info(String* packet)
+{
+ String tmp;
+ char buf[22];
+
+ tmp.append("Server ver: ");
+ tmp.append(server_version);
+ tmp.append(", Binlog ver: ");
+ tmp.append(llstr(binlog_version, buf));
+ net_store_data(packet, tmp.ptr(), tmp.length());
+}
+
+void Load_log_event::pack_info(String* packet)
+{
+ String tmp;
+ if(db && db_len)
+ {
+ tmp.append("use ");
+ tmp.append(db, db_len);
+ tmp.append("; ", 2);
+ }
+
+ tmp.append("LOAD DATA INFILE '");
+ tmp.append(fname);
+ tmp.append("' ", 2);
+ if(sql_ex.opt_flags && REPLACE_FLAG )
+ tmp.append(" REPLACE ");
+ else if(sql_ex.opt_flags && IGNORE_FLAG )
+ tmp.append(" IGNORE ");
+
+ tmp.append("INTO TABLE ");
+ tmp.append(table_name);
+ if (!(sql_ex.empty_flags & FIELD_TERM_EMPTY))
+ {
+ tmp.append(" FIELDS TERMINATED BY ");
+ pretty_print_char(&tmp, sql_ex.field_term);
+ }
+
+ if (!(sql_ex.empty_flags & ENCLOSED_EMPTY))
+ {
+ if (sql_ex.opt_flags && OPT_ENCLOSED_FLAG )
+ tmp.append(" OPTIONALLY ");
+ tmp.append( " ENCLOSED BY ");
+ pretty_print_char(&tmp, sql_ex.enclosed);
+ }
+
+ if (!(sql_ex.empty_flags & ESCAPED_EMPTY))
+ {
+ tmp.append( " ESCAPED BY ");
+ pretty_print_char(&tmp, sql_ex.escaped);
+ }
+
+ if (!(sql_ex.empty_flags & LINE_TERM_EMPTY))
+ {
+ tmp.append(" LINES TERMINATED BY ");
+ pretty_print_char(&tmp, sql_ex.line_term);
+ }
+
+ if (!(sql_ex.empty_flags & LINE_START_EMPTY))
+ {
+ tmp.append(" LINES STARTING BY ");
+ pretty_print_char(&tmp, sql_ex.line_start);
+ }
+
+ if ((int)skip_lines > 0)
+ tmp.append( " IGNORE %ld LINES ", (long) skip_lines);
+
+ if (num_fields)
+ {
+ uint i;
+ const char* field = fields;
+ tmp.append(" (");
+ for(i = 0; i < num_fields; i++)
+ {
+ if(i)
+ tmp.append(" ,");
+ tmp.append( field);
+
+ field += field_lens[i] + 1;
+ }
+ tmp.append(')');
+ }
+
+ net_store_data(packet, tmp.ptr(), tmp.length());
+}
+
+void Rotate_log_event::pack_info(String* packet)
+{
+ String tmp;
+ char buf[22];
+ tmp.append(new_log_ident, ident_len);
+ tmp.append(";pos=");
+ tmp.append(llstr(pos,buf));
+ if(flags & LOG_EVENT_FORCED_ROTATE_F)
+ tmp.append("; forced by master");
+ net_store_data(packet, tmp.ptr(), tmp.length());
+}
+
+void Intvar_log_event::pack_info(String* packet)
+{
+ String tmp;
+ char buf[22];
+ tmp.append(get_var_type_name());
+ tmp.append('=');
+ tmp.append(llstr(val, buf));
+ net_store_data(packet, tmp.ptr(), tmp.length());
+}
+
+void Slave_log_event::pack_info(String* packet)
+{
+ String tmp;
+ char buf[22];
+ tmp.append("host=");
+ tmp.append(master_host);
+ tmp.append(",port=");
+ tmp.append(llstr(master_port,buf));
+ tmp.append(",log=");
+ tmp.append(master_log);
+ tmp.append(",pos=");
+ tmp.append(llstr(master_pos,buf));
+ net_store_data(packet, tmp.ptr(), tmp.length());
+}
+
+
+void Log_event::init_show_field_list(List<Item>* field_list)
+{
+ field_list->push_back(new Item_empty_string("Log_name", 20));
+ field_list->push_back(new Item_empty_string("Pos", 20));
+ field_list->push_back(new Item_empty_string("Event_type", 20));
+ field_list->push_back(new Item_empty_string("Server_id", 20));
+ field_list->push_back(new Item_empty_string("Log_seq", 20));
+ field_list->push_back(new Item_empty_string("Info", 20));
+}
+
+int Log_event::net_send(THD* thd, const char* log_name, ulong pos)
+{
+ String* packet = &thd->packet;
+ const char* p = strrchr(log_name, FN_LIBCHAR);
+ const char* event_type;
+ if (p)
+ log_name = p + 1;
+
+ packet->length(0);
+ net_store_data(packet, log_name, strlen(log_name));
+ net_store_data(packet, (longlong)pos);
+ event_type = get_type_str();
+ net_store_data(packet, event_type, strlen(event_type));
+ net_store_data(packet, server_id);
+ net_store_data(packet, log_seq);
+ pack_info(packet);
+ return my_net_write(&thd->net, (char*)packet->ptr(), packet->length());
+}
+
+#endif
+
int Query_log_event::write(IO_CACHE* file)
{
return query ? Log_event::write(file) : -1;
@@ -52,7 +268,6 @@ int Log_event::write(IO_CACHE* file)
int Log_event::write_header(IO_CACHE* file)
{
- // make sure to change this when the header gets bigger
char buf[LOG_EVENT_HEADER_LEN];
char* pos = buf;
int4store(pos, when); // timestamp
@@ -63,6 +278,10 @@ int Log_event::write_header(IO_CACHE* file)
long tmp=get_data_size() + LOG_EVENT_HEADER_LEN;
int4store(pos, tmp);
pos += 4;
+ int4store(pos, log_seq);
+ pos += 4;
+ int2store(pos, flags);
+ pos += 2;
return (my_b_write(file, (byte*) buf, (uint) (pos - buf)));
}
@@ -115,91 +334,51 @@ int Log_event::read_log_event(IO_CACHE* file, String* packet,
Log_event* Log_event::read_log_event(IO_CACHE* file, pthread_mutex_t* log_lock)
{
- time_t timestamp;
- uint32 server_id;
-
- char buf[LOG_EVENT_HEADER_LEN-4];
+ char head[LOG_EVENT_HEADER_LEN];
if(log_lock) pthread_mutex_lock(log_lock);
- if (my_b_read(file, (byte *) buf, sizeof(buf)))
+ if (my_b_read(file, (byte *) head, sizeof(head)))
{
if (log_lock) pthread_mutex_unlock(log_lock);
- return NULL;
- }
- timestamp = uint4korr(buf);
- server_id = uint4korr(buf + 5);
-
- switch(buf[EVENT_TYPE_OFFSET])
- {
- case QUERY_EVENT:
- {
- Query_log_event* q = new Query_log_event(file, timestamp, server_id);
- if(log_lock) pthread_mutex_unlock(log_lock);
- if (!q->query)
- {
- delete q;
- q=NULL;
- }
- return q;
- }
-
- case LOAD_EVENT:
- {
- Load_log_event* l = new Load_log_event(file, timestamp, server_id);
- if(log_lock) pthread_mutex_unlock(log_lock);
- if (!l->table_name)
- {
- delete l;
- l=NULL;
- }
- return l;
+ return 0;
}
+ uint data_len = uint4korr(head + EVENT_LEN_OFFSET);
+ char* buf = 0;
+ const char* error = 0;
+ Log_event* res = 0;
- case ROTATE_EVENT:
+ if (data_len > max_allowed_packet)
{
- Rotate_log_event* r = new Rotate_log_event(file, timestamp, server_id);
- if(log_lock) pthread_mutex_unlock(log_lock);
-
- if (!r->new_log_ident)
- {
- delete r;
- r=NULL;
- }
- return r;
+ error = "Event too big";
+ goto err;
}
- case INTVAR_EVENT:
+ if (data_len < LOG_EVENT_HEADER_LEN)
{
- Intvar_log_event* e = new Intvar_log_event(file, timestamp, server_id);
- if(log_lock) pthread_mutex_unlock(log_lock);
-
- if (e->type == INVALID_INT_EVENT)
- {
- delete e;
- e=NULL;
- }
- return e;
+ error = "Event too small";
+ goto err;
}
- case START_EVENT:
- {
- Start_log_event* e = new Start_log_event(file, timestamp, server_id);
- if(log_lock) pthread_mutex_unlock(log_lock);
- return e;
- }
- case STOP_EVENT:
- {
- Stop_log_event* e = new Stop_log_event(file, timestamp, server_id);
- if(log_lock) pthread_mutex_unlock(log_lock);
- return e;
- }
- default:
- break;
+ if (!(buf = my_malloc(data_len, MYF(MY_WME))))
+ {
+ error = "Out of memory";
+ goto err;
}
- // default
+ memcpy(buf, head, LOG_EVENT_HEADER_LEN);
+ if(my_b_read(file, (byte*) buf + LOG_EVENT_HEADER_LEN,
+ data_len - LOG_EVENT_HEADER_LEN))
+ {
+ error = "read error";
+ goto err;
+ }
+ res = read_log_event(buf, data_len);
+err:
if (log_lock) pthread_mutex_unlock(log_lock);
- return NULL;
+ if(error)
+ sql_print_error(error);
+ my_free(buf, MYF(MY_ALLOW_ZERO_PTR));
+ return res;
}
Log_event* Log_event::read_log_event(const char* buf, int event_len)
@@ -245,6 +424,17 @@ Log_event* Log_event::read_log_event(const char* buf, int event_len)
return r;
}
+ case SLAVE_EVENT:
+ {
+ Slave_log_event* s = new Slave_log_event(buf, event_len);
+ if (!s->master_host)
+ {
+ delete s;
+ return NULL;
+ }
+
+ return s;
+ }
case START_EVENT: return new Start_log_event(buf);
case STOP_EVENT: return new Stop_log_event(buf);
case INTVAR_EVENT: return new Intvar_log_event(buf);
@@ -305,6 +495,7 @@ void Stop_log_event::print(FILE* file, bool short_form, char* last_db)
void Rotate_log_event::print(FILE* file, bool short_form, char* last_db)
{
+ char buf[22];
if (short_form)
return;
@@ -313,51 +504,25 @@ void Rotate_log_event::print(FILE* file, bool short_form, char* last_db)
if (new_log_ident)
my_fwrite(file, (byte*) new_log_ident, (uint)ident_len,
MYF(MY_NABP | MY_WME));
- fprintf(file, "\n");
+ fprintf(file, "pos=%s\n", llstr(pos, buf));
fflush(file);
}
-Rotate_log_event::Rotate_log_event(IO_CACHE* file, time_t when_arg,
- uint32 server_id):
- Log_event(when_arg, 0, 0, server_id),new_log_ident(NULL),alloced(0)
-{
- char *tmp_ident;
- char buf[4];
-
- if (my_b_read(file, (byte*) buf, sizeof(buf)))
- return;
- ulong event_len;
- event_len = uint4korr(buf);
- if (event_len < ROTATE_EVENT_OVERHEAD)
- return;
-
- ident_len = (uchar)(event_len - ROTATE_EVENT_OVERHEAD);
- if (!(tmp_ident = (char*) my_malloc((uint)ident_len, MYF(MY_WME))))
- return;
- if (my_b_read( file, (byte*) tmp_ident, (uint) ident_len))
- {
- my_free((gptr) tmp_ident, MYF(0));
- return;
- }
-
- new_log_ident = tmp_ident;
- alloced = 1;
-}
-
Start_log_event::Start_log_event(const char* buf) :Log_event(buf)
{
- buf += EVENT_LEN_OFFSET + 4; // skip even length
- binlog_version = uint2korr(buf);
- memcpy(server_version, buf + 2, sizeof(server_version));
- created = uint4korr(buf + 2 + sizeof(server_version));
+ binlog_version = uint2korr(buf + LOG_EVENT_HEADER_LEN +
+ ST_BINLOG_VER_OFFSET);
+ memcpy(server_version, buf + ST_SERVER_VER_OFFSET + LOG_EVENT_HEADER_LEN,
+ ST_SERVER_VER_LEN);
+ created = uint4korr(buf + ST_CREATED_OFFSET + LOG_EVENT_HEADER_LEN);
}
int Start_log_event::write_data(IO_CACHE* file)
{
- char buff[sizeof(server_version)+2+4];
- int2store(buff,binlog_version);
- memcpy(buff+2,server_version,sizeof(server_version));
- int4store(buff+2+sizeof(server_version),created);
+ char buff[START_HEADER_LEN];
+ int2store(buff + ST_BINLOG_VER_OFFSET,binlog_version);
+ memcpy(buff + ST_SERVER_VER_OFFSET,server_version,ST_SERVER_VER_LEN);
+ int4store(buff + ST_CREATED_OFFSET,created);
return (my_b_write(file, (byte*) buff, sizeof(buff)) ? -1 : 0);
}
@@ -369,8 +534,10 @@ Rotate_log_event::Rotate_log_event(const char* buf, int event_len):
if(event_len < ROTATE_EVENT_OVERHEAD)
return;
+ pos = uint8korr(buf + R_POS_OFFSET + LOG_EVENT_HEADER_LEN);
ident_len = (uchar)(event_len - ROTATE_EVENT_OVERHEAD);
- if (!(new_log_ident = (char*) my_memdup((byte*) buf + LOG_EVENT_HEADER_LEN,
+ if (!(new_log_ident = (char*) my_memdup((byte*) buf + R_IDENT_OFFSET
+ + LOG_EVENT_HEADER_LEN,
(uint) ident_len, MYF(MY_WME))))
return;
@@ -379,42 +546,10 @@ Rotate_log_event::Rotate_log_event(const char* buf, int event_len):
int Rotate_log_event::write_data(IO_CACHE* file)
{
- return my_b_write(file, (byte*) new_log_ident, (uint) ident_len) ? -1 :0;
-}
-
-Query_log_event::Query_log_event(IO_CACHE* file, time_t when_arg,
- uint32 server_id):
- Log_event(when_arg,0,0,server_id),data_buf(0),query(NULL),db(NULL)
-{
- char buf[QUERY_HEADER_LEN + 4];
- ulong data_len;
- if (my_b_read(file, (byte*) buf, sizeof(buf)))
- return; // query == NULL will tell the
- // caller there was a problem
- data_len = uint4korr(buf);
- if (data_len < QUERY_EVENT_OVERHEAD)
- return; // tear-drop attack protection :)
-
- data_len -= QUERY_EVENT_OVERHEAD;
- exec_time = uint4korr(buf + 8);
- db_len = (uint)buf[12];
- error_code = uint2korr(buf + 13);
-
- /* Allocate one byte extra for end \0 */
- if (!(data_buf = (char*) my_malloc(data_len+1, MYF(MY_WME))))
- return;
- if (my_b_read( file, (byte*) data_buf, data_len))
- {
- my_free((gptr) data_buf, MYF(0));
- data_buf = 0;
- return;
- }
-
- thread_id = uint4korr(buf + 4);
- db = data_buf;
- query=data_buf + db_len + 1;
- q_len = data_len - 1 - db_len;
- *((char*) query + q_len) = 0; // Safety
+ char buf[ROTATE_HEADER_LEN];
+ int8store(buf, pos + R_POS_OFFSET);
+ return my_b_write(file, (byte*)buf, ROTATE_HEADER_LEN) ||
+ my_b_write(file, (byte*)new_log_ident, (uint) ident_len);
}
Query_log_event::Query_log_event(const char* buf, int event_len):
@@ -423,19 +558,19 @@ Query_log_event::Query_log_event(const char* buf, int event_len):
if ((uint)event_len < QUERY_EVENT_OVERHEAD)
return;
ulong data_len;
- buf += EVENT_LEN_OFFSET;
data_len = event_len - QUERY_EVENT_OVERHEAD;
+
- exec_time = uint4korr(buf + 8);
- error_code = uint2korr(buf + 13);
+ exec_time = uint4korr(buf + LOG_EVENT_HEADER_LEN + Q_EXEC_TIME_OFFSET);
+ error_code = uint2korr(buf + LOG_EVENT_HEADER_LEN + Q_ERR_CODE_OFFSET);
if (!(data_buf = (char*) my_malloc(data_len + 1, MYF(MY_WME))))
return;
- memcpy(data_buf, buf + QUERY_HEADER_LEN + 4, data_len);
- thread_id = uint4korr(buf + 4);
+ memcpy(data_buf, buf + LOG_EVENT_HEADER_LEN + Q_DATA_OFFSET, data_len);
+ thread_id = uint4korr(buf + LOG_EVENT_HEADER_LEN + Q_THREAD_ID_OFFSET);
db = data_buf;
- db_len = (uint)buf[12];
+ db_len = (uint)buf[LOG_EVENT_HEADER_LEN + Q_DB_LEN_OFFSET];
query=data_buf + db_len + 1;
q_len = data_len - 1 - db_len;
*((char*)query+q_len) = 0;
@@ -474,44 +609,38 @@ int Query_log_event::write_data(IO_CACHE* file)
if (!query) return -1;
char buf[QUERY_HEADER_LEN];
- char* pos = buf;
- int4store(pos, thread_id);
- pos += 4;
- int4store(pos, exec_time);
- pos += 4;
- *pos++ = (char)db_len;
- int2store(pos, error_code);
- pos += 2;
+ int4store(buf + Q_THREAD_ID_OFFSET, thread_id);
+ int4store(buf + Q_EXEC_TIME_OFFSET, exec_time);
+ buf[Q_DB_LEN_OFFSET] = (char)db_len;
+ int2store(buf + Q_ERR_CODE_OFFSET, error_code);
- return (my_b_write(file, (byte*) buf, (uint)(pos - buf)) ||
+ return (my_b_write(file, (byte*) buf, QUERY_HEADER_LEN) ||
my_b_write(file, (db) ? (byte*) db : (byte*)"", db_len + 1) ||
my_b_write(file, (byte*) query, q_len)) ? -1 : 0;
}
-Intvar_log_event:: Intvar_log_event(IO_CACHE* file, time_t when_arg,
- uint32 server_id)
- :Log_event(when_arg,0,0,server_id), type(INVALID_INT_EVENT)
+Intvar_log_event::Intvar_log_event(const char* buf):Log_event(buf)
{
- char buf[9+4];
- if (!my_b_read(file, (byte*) buf, sizeof(buf)))
- {
- type = buf[4];
- val = uint8korr(buf+1+4);
- }
+ buf += LOG_EVENT_HEADER_LEN;
+ type = buf[I_TYPE_OFFSET];
+ val = uint8korr(buf+I_VAL_OFFSET);
}
-Intvar_log_event::Intvar_log_event(const char* buf):Log_event(buf)
+const char* Intvar_log_event::get_var_type_name()
{
- buf += LOG_EVENT_HEADER_LEN;
- type = buf[0];
- val = uint8korr(buf+1);
+ switch(type)
+ {
+ case LAST_INSERT_ID_EVENT: return "LAST_INSERT_ID";
+ case INSERT_ID_EVENT: return "INSERT_ID";
+ default: /* impossible */ return "UNKNOWN";
+ }
}
int Intvar_log_event::write_data(IO_CACHE* file)
{
char buf[9];
- buf[0] = type;
- int8store(buf + 1, val);
+ buf[I_TYPE_OFFSET] = type;
+ int8store(buf + I_VAL_OFFSET, val);
return my_b_write(file, (byte*) buf, sizeof(buf));
}
@@ -542,12 +671,12 @@ void Intvar_log_event::print(FILE* file, bool short_form, char* last_db)
int Load_log_event::write_data(IO_CACHE* file)
{
char buf[LOAD_HEADER_LEN];
- int4store(buf, thread_id);
- int4store(buf + 4, exec_time);
- int4store(buf + 8, skip_lines);
- buf[12] = (char)table_name_len;
- buf[13] = (char)db_len;
- int4store(buf + 14, num_fields);
+ int4store(buf + L_THREAD_ID_OFFSET, thread_id);
+ int4store(buf + L_EXEC_TIME_OFFSET, exec_time);
+ int4store(buf + L_SKIP_LINES_OFFSET, skip_lines);
+ buf[L_TBL_LEN_OFFSET] = (char)table_name_len;
+ buf[L_DB_LEN_OFFSET] = (char)db_len;
+ int4store(buf + L_NUM_FIELDS_OFFSET, num_fields);
if(my_b_write(file, (byte*)buf, sizeof(buf)) ||
my_b_write(file, (byte*)&sql_ex, sizeof(sql_ex)))
@@ -566,52 +695,33 @@ int Load_log_event::write_data(IO_CACHE* file)
return 0;
}
-Load_log_event::Load_log_event(IO_CACHE* file, time_t when, uint32 server_id):
- Log_event(when,0,0,server_id),data_buf(0),num_fields(0),
- fields(0),field_lens(0),field_block_len(0),
- table_name(0),db(0),fname(0)
-{
- char buf[LOAD_HEADER_LEN + 4];
- ulong data_len;
- if (my_b_read(file, (byte*)buf, sizeof(buf)) ||
- my_b_read(file, (byte*)&sql_ex, sizeof(sql_ex)))
- return;
-
- data_len = uint4korr(buf) - LOAD_EVENT_OVERHEAD;
- if (!(data_buf = (char*)my_malloc(data_len + 1, MYF(MY_WME))))
- return;
- if (my_b_read(file, (byte*)data_buf, data_len))
- return;
- copy_log_event(buf,data_len);
-}
-
Load_log_event::Load_log_event(const char* buf, int event_len):
Log_event(buf),data_buf(0),num_fields(0),fields(0),
field_lens(0),field_block_len(0),
table_name(0),db(0),fname(0)
{
- ulong data_len;
-
+ uint data_len;
if((uint)event_len < (LOAD_EVENT_OVERHEAD + LOG_EVENT_HEADER_LEN))
return;
- buf += EVENT_LEN_OFFSET;
- memcpy(&sql_ex, buf + LOAD_HEADER_LEN + 4, sizeof(sql_ex));
- data_len = event_len;
-
+ memcpy(&sql_ex, buf + LOAD_HEADER_LEN + LOG_EVENT_HEADER_LEN,
+ sizeof(sql_ex));
+ data_len = event_len - LOAD_HEADER_LEN - LOG_EVENT_HEADER_LEN -
+ sizeof(sql_ex);
if(!(data_buf = (char*)my_malloc(data_len + 1, MYF(MY_WME))))
return;
- memcpy(data_buf, buf + 22 + sizeof(sql_ex), data_len);
+ memcpy(data_buf, buf +LOG_EVENT_HEADER_LEN + LOAD_HEADER_LEN
+ + sizeof(sql_ex), data_len);
copy_log_event(buf, data_len);
}
void Load_log_event::copy_log_event(const char *buf, ulong data_len)
{
- thread_id = uint4korr(buf+4);
- exec_time = uint4korr(buf+8);
- skip_lines = uint4korr(buf + 12);
- table_name_len = (uint)buf[16];
- db_len = (uint)buf[17];
- num_fields = uint4korr(buf + 18);
+ thread_id = uint4korr(buf + L_THREAD_ID_OFFSET + LOG_EVENT_HEADER_LEN);
+ exec_time = uint4korr(buf + L_EXEC_TIME_OFFSET + LOG_EVENT_HEADER_LEN);
+ skip_lines = uint4korr(buf + L_SKIP_LINES_OFFSET + LOG_EVENT_HEADER_LEN);
+ table_name_len = (uint)buf[L_TBL_LEN_OFFSET + LOG_EVENT_HEADER_LEN];
+ db_len = (uint)buf[L_DB_LEN_OFFSET + LOG_EVENT_HEADER_LEN];
+ num_fields = uint4korr(buf + L_NUM_FIELDS_OFFSET + LOG_EVENT_HEADER_LEN);
if (num_fields > data_len) // simple sanity check against corruption
return;
@@ -717,6 +827,12 @@ void Load_log_event::print(FILE* file, bool short_form, char* last_db)
#ifndef MYSQL_CLIENT
+void Log_event::set_log_seq(THD* thd, MYSQL_LOG* log)
+ {
+ log_seq = (thd && thd->log_seq) ? thd->log_seq++ : log->log_seq++;
+ }
+
+
void Load_log_event::set_fields(List<Item> &fields)
{
uint i;
@@ -729,4 +845,92 @@ void Load_log_event::set_fields(List<Item> &fields)
}
+Slave_log_event::Slave_log_event(THD* thd_arg,MASTER_INFO* mi):
+ Log_event(thd_arg->start_time, 0, 1, thd_arg->server_id),
+ mem_pool(0),master_host(0)
+{
+ if(!mi->inited)
+ return;
+ pthread_mutex_lock(&mi->lock);
+ master_host_len = strlen(mi->host);
+ master_log_len = strlen(mi->log_file_name);
+ // on OOM, just do not initialize the structure and print the error
+ if((mem_pool = (char*)my_malloc(get_data_size() + 1,
+ MYF(MY_WME))))
+ {
+ master_host = mem_pool + SL_MASTER_HOST_OFFSET ;
+ memcpy(master_host, mi->host, master_host_len + 1);
+ master_log = master_host + master_host_len + 1;
+ memcpy(master_log, mi->log_file_name, master_log_len + 1);
+ master_port = mi->port;
+ master_pos = mi->pos;
+ }
+ else
+ sql_print_error("Out of memory while recording slave event");
+ pthread_mutex_unlock(&mi->lock);
+}
+
+
#endif
+
+
+Slave_log_event::~Slave_log_event()
+{
+ my_free(mem_pool, MYF(MY_ALLOW_ZERO_PTR));
+}
+
+void Slave_log_event::print(FILE* file, bool short_form = 0,
+ char* last_db = 0)
+{
+ char llbuff[22];
+ if(short_form)
+ return;
+ print_header(file);
+ fputc('\n', file);
+ fprintf(file, "Slave: master_host='%s' master_port=%d \
+ master_log=%s master_pos=%s\n", master_host, master_port, master_log,
+ llstr(master_pos, llbuff));
+}
+
+int Slave_log_event::get_data_size()
+{
+ return master_host_len + master_log_len + 1 + SL_MASTER_HOST_OFFSET;
+}
+
+int Slave_log_event::write_data(IO_CACHE* file)
+{
+ int8store(mem_pool + SL_MASTER_POS_OFFSET, master_pos);
+ int2store(mem_pool + SL_MASTER_PORT_OFFSET, master_port);
+ // log and host are already there
+ return my_b_write(file, (byte*)mem_pool, get_data_size());
+}
+
+void Slave_log_event::init_from_mem_pool(int data_size)
+{
+ master_pos = uint8korr(mem_pool + SL_MASTER_POS_OFFSET);
+ master_port = uint2korr(mem_pool + SL_MASTER_PORT_OFFSET);
+ master_host = mem_pool + SL_MASTER_HOST_OFFSET;
+ master_host_len = strlen(master_host);
+ // safety
+ master_log = master_host + master_host_len + 1;
+ if(master_log > mem_pool + data_size)
+ {
+ master_host = 0;
+ return;
+ }
+
+ master_log_len = strlen(master_log);
+}
+
+Slave_log_event::Slave_log_event(const char* buf, int event_len):
+ Log_event(buf),mem_pool(0),master_host(0)
+{
+ event_len -= LOG_EVENT_HEADER_LEN;
+ if(event_len < 0)
+ return;
+ if(!(mem_pool = (char*)my_malloc(event_len + 1, MYF(MY_WME))))
+ return;
+ memcpy(mem_pool, buf + LOG_EVENT_HEADER_LEN, event_len);
+ mem_pool[event_len] = 0;
+ init_from_mem_pool(event_len);
+}
diff --git a/sql/log_event.h b/sql/log_event.h
index 41f847e8d92..f38ddef05a2 100644
--- a/sql/log_event.h
+++ b/sql/log_event.h
@@ -34,40 +34,110 @@
#define LOG_READ_TOO_LARGE -7
#define LOG_EVENT_OFFSET 4
-#define BINLOG_VERSION 1
+#define BINLOG_VERSION 2
+
+/* we could have used SERVER_VERSION_LENGTH, but this introduces an
+ obscure dependency - if somebody decided to change SERVER_VERSION_LENGTH
+ this would have broke the replication protocol
+*/
+#define ST_SERVER_VER_LEN 50
+
+/* Binary log consists of events. Each event has a fixed length header,
+ followed by possibly variable ( depending on the type of event) length
+ data body. The data body consists of an optional fixed length segment
+ (post-header), and an optional variable length segment. See #defines and
+ comments below for the format specifics
+*/
+
+/* event-specific post-header sizes */
+#define LOG_EVENT_HEADER_LEN 19
+#define QUERY_HEADER_LEN (4 + 4 + 1 + 2)
+#define LOAD_HEADER_LEN (4 + 4 + 4 + 1 +1 + 4)
+#define START_HEADER_LEN (2 + ST_SERVER_VER_LEN + 4)
+#define ROTATE_HEADER_LEN 8
+
+/* event header offsets */
-#define LOG_EVENT_HEADER_LEN 13
-#define QUERY_HEADER_LEN (sizeof(uint32) + sizeof(uint32) + \
- sizeof(uchar) + sizeof(uint16))
-#define LOAD_HEADER_LEN (sizeof(uint32) + sizeof(uint32) + \
- + sizeof(uint32) + 2 + sizeof(uint32))
-#define EVENT_LEN_OFFSET 9
#define EVENT_TYPE_OFFSET 4
+#define SERVER_ID_OFFSET 5
+#define EVENT_LEN_OFFSET 9
+#define LOG_SEQ_OFFSET 13
+#define FLAGS_OFFSET 17
+
+/* start event post-header */
+
+#define ST_BINLOG_VER_OFFSET 0
+#define ST_SERVER_VER_OFFSET 2
+#define ST_CREATED_OFFSET (ST_SERVER_VER_OFFSET + ST_SERVER_VER_LEN)
+
+/* slave event post-header */
+
+#define SL_MASTER_PORT_OFFSET 8
+#define SL_MASTER_POS_OFFSET 0
+#define SL_MASTER_HOST_OFFSET 10
+
+/* query event post-header */
+
+#define Q_THREAD_ID_OFFSET 0
+#define Q_EXEC_TIME_OFFSET 4
+#define Q_DB_LEN_OFFSET 8
+#define Q_ERR_CODE_OFFSET 9
+#define Q_DATA_OFFSET QUERY_HEADER_LEN
+
+/* Intvar event post-header */
+
+#define I_TYPE_OFFSET 0
+#define I_VAL_OFFSET 1
+
+/* Load event post-header */
+
+#define L_THREAD_ID_OFFSET 0
+#define L_EXEC_TIME_OFFSET 4
+#define L_SKIP_LINES_OFFSET 8
+#define L_DB_LEN_OFFSET 12
+#define L_TBL_LEN_OFFSET 13
+#define L_NUM_FIELDS_OFFSET 14
+#define L_DATA_OFFSET LOAD_HEADER_LEN
+
+/* Rotate event post-header */
+
+#define R_POS_OFFSET 0
+#define R_IDENT_OFFSET 8
+
#define QUERY_EVENT_OVERHEAD (LOG_EVENT_HEADER_LEN+QUERY_HEADER_LEN)
-#define ROTATE_EVENT_OVERHEAD LOG_EVENT_HEADER_LEN
+#define QUERY_DATA_OFFSET (LOG_EVENT_HEADER_LEN+QUERY_HEADER_LEN)
+#define ROTATE_EVENT_OVERHEAD (LOG_EVENT_HEADER_LEN+ROTATE_HEADER_LEN)
#define LOAD_EVENT_OVERHEAD (LOG_EVENT_HEADER_LEN+LOAD_HEADER_LEN+sizeof(sql_ex_info))
#define BINLOG_MAGIC "\xfe\x62\x69\x6e"
+#define LOG_EVENT_TIME_F 0x1
+#define LOG_EVENT_FORCED_ROTATE_F 0x2
+
enum Log_event_type { START_EVENT = 1, QUERY_EVENT =2,
STOP_EVENT=3, ROTATE_EVENT = 4, INTVAR_EVENT=5,
- LOAD_EVENT=6};
+ LOAD_EVENT=6, SLAVE_EVENT=7, FILE_EVENT=8};
enum Int_event_type { INVALID_INT_EVENT = 0, LAST_INSERT_ID_EVENT = 1, INSERT_ID_EVENT = 2
};
#ifndef MYSQL_CLIENT
class String;
+class MYSQL_LOG;
+class THD;
#endif
extern uint32 server_id;
+struct st_master_info;
+
class Log_event
{
public:
time_t when;
ulong exec_time;
- int valid_exec_time; // if false, the exec time setting is bogus
uint32 server_id;
+ uint32 log_seq;
+ uint16 flags;
static void *operator new(size_t size)
{
@@ -84,17 +154,22 @@ public:
virtual int write_data(IO_CACHE* file __attribute__((unused))) { return 0; }
virtual Log_event_type get_type_code() = 0;
Log_event(time_t when_arg, ulong exec_time_arg = 0,
- int valid_exec_time_arg = 0, uint32 server_id_arg = 0):
+ int valid_exec_time = 0, uint32 server_id_arg = 0,
+ uint32 log_seq_arg = 0, uint16 flags_arg = 0):
when(when_arg), exec_time(exec_time_arg),
- valid_exec_time(valid_exec_time_arg)
+ log_seq(log_seq_arg),flags(0)
{
server_id = server_id_arg ? server_id_arg : (::server_id);
+ if(valid_exec_time)
+ flags |= LOG_EVENT_TIME_F;
}
- Log_event(const char* buf): valid_exec_time(0)
+ Log_event(const char* buf)
{
when = uint4korr(buf);
- server_id = uint4korr(buf + 5);
+ server_id = uint4korr(buf + SERVER_ID_OFFSET);
+ log_seq = uint4korr(buf + LOG_SEQ_OFFSET);
+ flags = uint2korr(buf + FLAGS_OFFSET);
}
virtual ~Log_event() {}
@@ -108,10 +183,15 @@ public:
// if mutex is 0, the read will proceed without mutex
static Log_event* read_log_event(IO_CACHE* file, pthread_mutex_t* log_lock);
static Log_event* read_log_event(const char* buf, int event_len);
+ const char* get_type_str();
#ifndef MYSQL_CLIENT
static int read_log_event(IO_CACHE* file, String* packet,
pthread_mutex_t* log_lock);
+ void set_log_seq(THD* thd, MYSQL_LOG* log);
+ virtual void pack_info(String* packet);
+ int net_send(THD* thd, const char* log_name, ulong pos);
+ static void init_show_field_list(List<Item>* field_list);
#endif
};
@@ -134,7 +214,8 @@ public:
THD* thd;
bool cache_stmt;
Query_log_event(THD* thd_arg, const char* query_arg, bool using_trans=0):
- Log_event(thd_arg->start_time,0,1,thd_arg->server_id), data_buf(0),
+ Log_event(thd_arg->start_time,0,1,thd_arg->server_id,thd_arg->log_seq),
+ data_buf(0),
query(query_arg), db(thd_arg->db), q_len(thd_arg->query_length),
error_code(thd_arg->killed ? ER_SERVER_SHUTDOWN: thd_arg->net.last_errno),
thread_id(thd_arg->thread_id), thd(thd_arg),
@@ -146,9 +227,10 @@ public:
exec_time = (ulong) (end_time - thd->start_time);
db_len = (db) ? (uint32) strlen(db) : 0;
}
+
+ void pack_info(String* packet);
#endif
- Query_log_event(IO_CACHE* file, time_t when, uint32 server_id_arg);
Query_log_event(const char* buf, int event_len);
~Query_log_event()
{
@@ -172,6 +254,33 @@ public:
void print(FILE* file, bool short_form = 0, char* last_db = 0);
};
+class Slave_log_event: public Log_event
+{
+protected:
+ char* mem_pool;
+ void init_from_mem_pool(int data_size);
+public:
+ char* master_host;
+ int master_host_len;
+ uint16 master_port;
+ char* master_log;
+ int master_log_len;
+ ulonglong master_pos;
+
+#ifndef MYSQL_CLIENT
+ Slave_log_event(THD* thd_arg, struct st_master_info* mi);
+ void pack_info(String* packet);
+#endif
+
+ Slave_log_event(const char* buf, int event_len);
+ ~Slave_log_event();
+ int get_data_size();
+ Log_event_type get_type_code() { return SLAVE_EVENT; }
+ void print(FILE* file, bool short_form = 0, char* last_db = 0);
+ int write_data(IO_CACHE* file );
+
+};
+
#define DUMPFILE_FLAG 0x1
#define OPT_ENCLOSED_FLAG 0x2
#define REPLACE_FLAG 0x4
@@ -234,7 +343,6 @@ public:
time_t end_time;
time(&end_time);
exec_time = (ulong) (end_time - thd->start_time);
- valid_exec_time = 1;
db_len = (db) ? (uint32) strlen(db) : 0;
table_name_len = (table_name) ? (uint32) strlen(table_name) : 0;
fname_len = (fname) ? (uint) strlen(fname) : 0;
@@ -288,9 +396,9 @@ public:
fields = fields_buf.ptr();
}
void set_fields(List<Item> &fields_arg);
+ void pack_info(String* packet);
#endif
- Load_log_event(IO_CACHE * file, time_t when, uint32 server_id_arg);
Load_log_event(const char* buf, int event_len);
~Load_log_event()
{
@@ -322,23 +430,12 @@ class Start_log_event: public Log_event
public:
uint32 created;
uint16 binlog_version;
- char server_version[50];
+ char server_version[ST_SERVER_VER_LEN];
Start_log_event() :Log_event(time(NULL)),binlog_version(BINLOG_VERSION)
{
created = (uint32) when;
- memcpy(server_version, ::server_version, sizeof(server_version));
- }
- Start_log_event(IO_CACHE* file, time_t when_arg, uint32 server_id_arg) :
- Log_event(when_arg, 0, 0, server_id_arg)
- {
- char buf[sizeof(server_version) + 2 + 4 + 4];
- if (my_b_read(file, (byte*) buf, sizeof(buf)))
- return;
- binlog_version = uint2korr(buf+4);
- memcpy(server_version, buf + 6, sizeof(server_version));
- server_version[sizeof(server_version)-1]=0;
- created = uint4korr(buf + 6 + sizeof(server_version));
+ memcpy(server_version, ::server_version, ST_SERVER_VER_LEN);
}
Start_log_event(const char* buf);
@@ -347,9 +444,11 @@ public:
int write_data(IO_CACHE* file);
int get_data_size()
{
- // sizeof(binlog_version) + sizeof(server_version) sizeof(created)
- return 2 + sizeof(server_version) + 4;
+ return START_HEADER_LEN;
}
+#ifndef MYSQL_CLIENT
+ void pack_info(String* packet);
+#endif
void print(FILE* file, bool short_form = 0, char* last_db = 0);
};
@@ -361,12 +460,15 @@ public:
Intvar_log_event(uchar type_arg, ulonglong val_arg)
:Log_event(time(NULL)),val(val_arg),type(type_arg)
{}
- Intvar_log_event(IO_CACHE* file, time_t when, uint32 server_id_arg);
Intvar_log_event(const char* buf);
~Intvar_log_event() {}
Log_event_type get_type_code() { return INTVAR_EVENT;}
+ const char* get_var_type_name();
int get_data_size() { return sizeof(type) + sizeof(val);}
int write_data(IO_CACHE* file);
+#ifndef MYSQL_CLIENT
+ void pack_info(String* packet);
+#endif
void print(FILE* file, bool short_form = 0, char* last_db = 0);
@@ -377,12 +479,6 @@ class Stop_log_event: public Log_event
public:
Stop_log_event() :Log_event(time(NULL))
{}
- Stop_log_event(IO_CACHE* file, time_t when_arg, uint32 server_id_arg):
- Log_event(when_arg,0,0,server_id_arg)
- {
- byte skip[4];
- my_b_read(file, skip, sizeof(skip)); // skip the event length
- }
Stop_log_event(const char* buf):Log_event(buf)
{
}
@@ -396,16 +492,18 @@ class Rotate_log_event: public Log_event
public:
const char* new_log_ident;
uchar ident_len;
+ ulonglong pos;
bool alloced;
- Rotate_log_event(const char* new_log_ident_arg, uint ident_len_arg = 0) :
+ Rotate_log_event(const char* new_log_ident_arg, uint ident_len_arg = 0,
+ ulonglong pos_arg = 4) :
Log_event(time(NULL)),
new_log_ident(new_log_ident_arg),
- ident_len(ident_len_arg ? ident_len_arg : (uint) strlen(new_log_ident_arg)),
+ ident_len(ident_len_arg ? ident_len_arg :
+ (uint) strlen(new_log_ident_arg)), pos(pos_arg),
alloced(0)
{}
- Rotate_log_event(IO_CACHE* file, time_t when, uint32 server_id_arg) ;
Rotate_log_event(const char* buf, int event_len);
~Rotate_log_event()
{
@@ -413,10 +511,16 @@ public:
my_free((gptr) new_log_ident, MYF(0));
}
Log_event_type get_type_code() { return ROTATE_EVENT;}
- int get_data_size() { return ident_len;}
+ int get_data_size() { return ident_len + ROTATE_HEADER_LEN;}
int write_data(IO_CACHE* file);
void print(FILE* file, bool short_form = 0, char* last_db = 0);
+#ifndef MYSQL_CLIENT
+ void pack_info(String* packet);
+#endif
};
#endif
+
+
+
diff --git a/sql/md5.c b/sql/md5.c
index 0775ba3bd1a..a19f8639f3a 100644
--- a/sql/md5.c
+++ b/sql/md5.c
@@ -108,7 +108,7 @@ Rotation is separate from addition to prevent recomputation.
/* MD5 initialization. Begins an MD5 operation, writing a new context.
*/
-void MD5Init (MD5_CTX *context) /* context */
+void my_MD5Init (my_MD5_CTX *context) /* context */
{
context->count[0] = context->count[1] = 0;
/* Load magic initialization constants.
@@ -123,8 +123,8 @@ void MD5Init (MD5_CTX *context) /* context */
operation, processing another message block, and updating the
context.
*/
-void MD5Update (context, input, inputLen)
-MD5_CTX *context; /* context */
+void my_MD5Update (context, input, inputLen)
+my_MD5_CTX *context; /* context */
unsigned char *input; /* input block */
unsigned int inputLen; /* length of input block */
{
@@ -164,9 +164,9 @@ unsigned int inputLen; /* length of input block */
/* MD5 finalization. Ends an MD5 message-digest operation, writing the
the message digest and zeroizing the context.
*/
-void MD5Final (digest, context)
+void my_MD5Final (digest, context)
unsigned char digest[16]; /* message digest */
-MD5_CTX *context; /* context */
+my_MD5_CTX *context; /* context */
{
unsigned char bits[8];
unsigned int idx, padLen;
@@ -178,10 +178,10 @@ MD5_CTX *context; /* context */
*/
idx = (unsigned int)((context->count[0] >> 3) & 0x3f);
padLen = (idx < 56) ? (56 - idx) : (120 - idx);
- MD5Update (context, PADDING, padLen);
+ my_MD5Update (context, PADDING, padLen);
/* Append length (before padding) */
- MD5Update (context, bits, 8);
+ my_MD5Update (context, bits, 8);
/* Store state in digest */
Encode (digest, context->state, 16);
diff --git a/sql/md5.h b/sql/md5.h
index 862129391f1..6fe4e543bb0 100644
--- a/sql/md5.h
+++ b/sql/md5.h
@@ -57,22 +57,20 @@ If using PROTOTYPES, then PROTO_LIST returns the list, otherwise it
#else
#define PROTO_LIST(list) ()
#endif
-
-
/* MD5 context. */
typedef struct {
UINT4 state[4]; /* state (ABCD) */
UINT4 count[2]; /* number of bits, modulo 2^64 (lsb first) */
unsigned char buffer[64]; /* input buffer */
-} MD5_CTX;
+} my_MD5_CTX;
#ifdef __cplusplus
extern "C" {
#endif
- void MD5Init PROTO_LIST ((MD5_CTX *));
- void MD5Update PROTO_LIST
- ((MD5_CTX *, unsigned char *, unsigned int));
- void MD5Final PROTO_LIST ((unsigned char [16], MD5_CTX *));
+ void my_MD5Init PROTO_LIST ((my_MD5_CTX *));
+ void my_MD5Update PROTO_LIST
+ ((my_MD5_CTX *, unsigned char *, unsigned int));
+ void my_MD5Final PROTO_LIST ((unsigned char [16], my_MD5_CTX *));
#ifdef __cplusplus
}
diff --git a/sql/mini_client.cc b/sql/mini_client.cc
index 38180c0c6c8..8966b303000 100644
--- a/sql/mini_client.cc
+++ b/sql/mini_client.cc
@@ -28,6 +28,8 @@
#include <odbcinst.h>
#endif
#include <global.h>
+#include <mysql_com.h>
+#include <violite.h>
#include <my_sys.h>
#include <mysys_err.h>
#include <m_string.h>
@@ -37,7 +39,6 @@
#include "mysql_version.h"
#include "mysqld_error.h"
#include "errmsg.h"
-#include <violite.h>
extern "C" { // Because of SCO 3.2V4.2
#include <sys/stat.h>
@@ -69,9 +70,22 @@ extern "C" { // Because of SCO 3.2V4.2
}
+static void mc_free_rows(MYSQL_DATA *cur);
+static MYSQL_FIELD *unpack_fields(MYSQL_DATA *data,MEM_ROOT *alloc,uint fields,
+ my_bool default_value,
+ my_bool long_flag_protocol);
+
static void mc_end_server(MYSQL *mysql);
static int mc_sock_connect(File s, const struct sockaddr *name, uint namelen, uint to);
static void mc_free_old_query(MYSQL *mysql);
+static int mc_send_file_to_server(MYSQL *mysql, const char *filename);
+static my_ulonglong mc_net_field_length_ll(uchar **packet);
+static ulong mc_net_field_length(uchar **packet);
+static int mc_read_one_row(MYSQL *mysql,uint fields,MYSQL_ROW row,
+ ulong *lengths);
+static MYSQL_DATA *mc_read_rows(MYSQL *mysql,MYSQL_FIELD *mysql_fields,
+ uint fields);
+
#define CLIENT_CAPABILITIES (CLIENT_LONG_PASSWORD | CLIENT_LONG_FLAG | CLIENT_LOCAL_FILES)
@@ -735,18 +749,18 @@ mc_mysql_connect(MYSQL *mysql,const char *host, const char *user,
#ifdef HAVE_OPENSSL
/* Oops.. are we careful enough to not send ANY information */
/* without encryption? */
- if (client_flag & CLIENT_SSL)
+/* if (client_flag & CLIENT_SSL)
{
if (my_net_write(net,buff,(uint) (2)) || net_flush(net))
- goto error;
+ goto error;*/
/* Do the SSL layering. */
- DBUG_PRINT("info", ("IO layer change in progress..."));
+ /* DBUG_PRINT("info", ("IO layer change in progress..."));
VioSSLConnectorFd* connector_fd = (VioSSLConnectorFd*)
(mysql->connector_fd);
VioSocket* vio_socket = (VioSocket*)(mysql->net.vio);
VioSSL* vio_ssl = connector_fd->connect(vio_socket);
mysql->net.vio = (NetVio*)(vio_ssl);
- }
+ }*/
#endif /* HAVE_OPENSSL */
int3store(buff+2,max_allowed_packet);
@@ -816,11 +830,506 @@ mc_mysql_close(MYSQL *mysql)
bzero((char*) &mysql->options,sizeof(mysql->options));
mysql->net.vio = 0;
#ifdef HAVE_OPENSSL
- ((VioConnectorFd*)(mysql->connector_fd))->delete();
- mysql->connector_fd = 0;
+/* ((VioConnectorFd*)(mysql->connector_fd))->delete();
+ mysql->connector_fd = 0;*/
#endif /* HAVE_OPENSSL */
if (mysql->free_me)
my_free((gptr) mysql,MYF(0));
}
DBUG_VOID_RETURN;
}
+
+void STDCALL mc_mysql_free_result(MYSQL_RES *result)
+{
+ DBUG_ENTER("mc_mysql_free_result");
+ DBUG_PRINT("enter",("mysql_res: %lx",result));
+ if (result)
+ {
+ if (result->handle && result->handle->status == MYSQL_STATUS_USE_RESULT)
+ {
+ DBUG_PRINT("warning",("Not all rows in set were read; Ignoring rows"));
+ for (;;)
+ {
+ uint pkt_len;
+ if ((pkt_len=(uint) mc_net_safe_read(result->handle)) == packet_error)
+ break;
+ if (pkt_len == 1 && result->handle->net.read_pos[0] == 254)
+ break; /* End of data */
+ }
+ result->handle->status=MYSQL_STATUS_READY;
+ }
+ mc_free_rows(result->data);
+ if (result->fields)
+ free_root(&result->field_alloc,MYF(0));
+ if (result->row)
+ my_free((gptr) result->row,MYF(0));
+ my_free((gptr) result,MYF(0));
+ }
+ DBUG_VOID_RETURN;
+}
+
+static void mc_free_rows(MYSQL_DATA *cur)
+{
+ if (cur)
+ {
+ free_root(&cur->alloc,MYF(0));
+ my_free((gptr) cur,MYF(0));
+ }
+}
+
+static MYSQL_FIELD *
+mc_unpack_fields(MYSQL_DATA *data,MEM_ROOT *alloc,uint fields,
+ my_bool default_value, my_bool long_flag_protocol)
+{
+ MYSQL_ROWS *row;
+ MYSQL_FIELD *field,*result;
+ DBUG_ENTER("unpack_fields");
+
+ field=result=(MYSQL_FIELD*) alloc_root(alloc,sizeof(MYSQL_FIELD)*fields);
+ if (!result)
+ DBUG_RETURN(0);
+
+ for (row=data->data; row ; row = row->next,field++)
+ {
+ field->table= strdup_root(alloc,(char*) row->data[0]);
+ field->name= strdup_root(alloc,(char*) row->data[1]);
+ field->length= (uint) uint3korr(row->data[2]);
+ field->type= (enum enum_field_types) (uchar) row->data[3][0];
+ if (long_flag_protocol)
+ {
+ field->flags= uint2korr(row->data[4]);
+ field->decimals=(uint) (uchar) row->data[4][2];
+ }
+ else
+ {
+ field->flags= (uint) (uchar) row->data[4][0];
+ field->decimals=(uint) (uchar) row->data[4][1];
+ }
+ if (INTERNAL_NUM_FIELD(field))
+ field->flags|= NUM_FLAG;
+ if (default_value && row->data[5])
+ field->def=strdup_root(alloc,(char*) row->data[5]);
+ else
+ field->def=0;
+ field->max_length= 0;
+ }
+ mc_free_rows(data); /* Free old data */
+ DBUG_RETURN(result);
+}
+
+int STDCALL
+mc_mysql_send_query(MYSQL* mysql, const char* query, uint length)
+{
+ return mc_simple_command(mysql, COM_QUERY, query, length, 1);
+}
+
+int STDCALL mc_mysql_read_query_result(MYSQL *mysql)
+{
+ uchar *pos;
+ ulong field_count;
+ MYSQL_DATA *fields;
+ uint length;
+ DBUG_ENTER("mc_mysql_read_query_result");
+
+ if ((length = mc_net_safe_read(mysql)) == packet_error)
+ DBUG_RETURN(-1);
+ mc_free_old_query(mysql); /* Free old result */
+get_info:
+ pos=(uchar*) mysql->net.read_pos;
+ if ((field_count= mc_net_field_length(&pos)) == 0)
+ {
+ mysql->affected_rows= mc_net_field_length_ll(&pos);
+ mysql->insert_id= mc_net_field_length_ll(&pos);
+ if (mysql->server_capabilities & CLIENT_TRANSACTIONS)
+ {
+ mysql->server_status=uint2korr(pos); pos+=2;
+ }
+ if (pos < mysql->net.read_pos+length && mc_net_field_length(&pos))
+ mysql->info=(char*) pos;
+ DBUG_RETURN(0);
+ }
+ if (field_count == NULL_LENGTH) /* LOAD DATA LOCAL INFILE */
+ {
+ int error=mc_send_file_to_server(mysql,(char*) pos);
+ if ((length=mc_net_safe_read(mysql)) == packet_error || error)
+ DBUG_RETURN(-1);
+ goto get_info; /* Get info packet */
+ }
+ if (!(mysql->server_status & SERVER_STATUS_AUTOCOMMIT))
+ mysql->server_status|= SERVER_STATUS_IN_TRANS;
+
+ mysql->extra_info= mc_net_field_length_ll(&pos); /* Maybe number of rec */
+ if (!(fields=mc_read_rows(mysql,(MYSQL_FIELD*) 0,5)))
+ DBUG_RETURN(-1);
+ if (!(mysql->fields=mc_unpack_fields(fields,&mysql->field_alloc,
+ (uint) field_count,0,
+ (my_bool) test(mysql->server_capabilities &
+ CLIENT_LONG_FLAG))))
+ DBUG_RETURN(-1);
+ mysql->status=MYSQL_STATUS_GET_RESULT;
+ mysql->field_count=field_count;
+ DBUG_RETURN(0);
+}
+
+int STDCALL mc_mysql_query(MYSQL *mysql, const char *query, uint length)
+{
+ DBUG_ENTER("mysql_real_query");
+ DBUG_PRINT("enter",("handle: %lx",mysql));
+ DBUG_PRINT("query",("Query = \"%s\"",query));
+ if(!length)
+ length = strlen(query);
+ if (mc_simple_command(mysql,COM_QUERY,query,length,1))
+ DBUG_RETURN(-1);
+ DBUG_RETURN(mc_mysql_read_query_result(mysql));
+}
+
+static int mc_send_file_to_server(MYSQL *mysql, const char *filename)
+{
+ int fd, readcount;
+ char buf[IO_SIZE*15],*tmp_name;
+ DBUG_ENTER("send_file_to_server");
+
+ fn_format(buf,filename,"","",4); /* Convert to client format */
+ if (!(tmp_name=my_strdup(buf,MYF(0))))
+ {
+ strmov(mysql->net.last_error, ER(mysql->net.last_errno=CR_OUT_OF_MEMORY));
+ DBUG_RETURN(-1);
+ }
+ if ((fd = my_open(tmp_name,O_RDONLY, MYF(0))) < 0)
+ {
+ mysql->net.last_errno=EE_FILENOTFOUND;
+ sprintf(buf,EE(mysql->net.last_errno),tmp_name,errno);
+ strmake(mysql->net.last_error,buf,sizeof(mysql->net.last_error)-1);
+ my_net_write(&mysql->net,"",0); net_flush(&mysql->net);
+ my_free(tmp_name,MYF(0));
+ DBUG_RETURN(-1);
+ }
+
+ while ((readcount = (int) my_read(fd,buf,sizeof(buf),MYF(0))) > 0)
+ {
+ if (my_net_write(&mysql->net,buf,readcount))
+ {
+ mysql->net.last_errno=CR_SERVER_LOST;
+ strmov(mysql->net.last_error,ER(mysql->net.last_errno));
+ DBUG_PRINT("error",("Lost connection to MySQL server during LOAD DATA of local file"));
+ (void) my_close(fd,MYF(0));
+ my_free(tmp_name,MYF(0));
+ DBUG_RETURN(-1);
+ }
+ }
+ (void) my_close(fd,MYF(0));
+ /* Send empty packet to mark end of file */
+ if (my_net_write(&mysql->net,"",0) || net_flush(&mysql->net))
+ {
+ mysql->net.last_errno=CR_SERVER_LOST;
+ sprintf(mysql->net.last_error,ER(mysql->net.last_errno),errno);
+ my_free(tmp_name,MYF(0));
+ DBUG_RETURN(-1);
+ }
+ if (readcount < 0)
+ {
+ mysql->net.last_errno=EE_READ; /* the errmsg for not entire file read */
+ sprintf(buf,EE(mysql->net.last_errno),tmp_name,errno);
+ strmake(mysql->net.last_error,buf,sizeof(mysql->net.last_error)-1);
+ my_free(tmp_name,MYF(0));
+ DBUG_RETURN(-1);
+ }
+ DBUG_RETURN(0);
+}
+
+/* Get the length of next field. Change parameter to point at fieldstart */
+static ulong mc_net_field_length(uchar **packet)
+{
+ reg1 uchar *pos= *packet;
+ if (*pos < 251)
+ {
+ (*packet)++;
+ return (ulong) *pos;
+ }
+ if (*pos == 251)
+ {
+ (*packet)++;
+ return NULL_LENGTH;
+ }
+ if (*pos == 252)
+ {
+ (*packet)+=3;
+ return (ulong) uint2korr(pos+1);
+ }
+ if (*pos == 253)
+ {
+ (*packet)+=4;
+ return (ulong) uint3korr(pos+1);
+ }
+ (*packet)+=9; /* Must be 254 when here */
+ return (ulong) uint4korr(pos+1);
+}
+
+/* Same as above, but returns ulonglong values */
+
+static my_ulonglong mc_net_field_length_ll(uchar **packet)
+{
+ reg1 uchar *pos= *packet;
+ if (*pos < 251)
+ {
+ (*packet)++;
+ return (my_ulonglong) *pos;
+ }
+ if (*pos == 251)
+ {
+ (*packet)++;
+ return (my_ulonglong) NULL_LENGTH;
+ }
+ if (*pos == 252)
+ {
+ (*packet)+=3;
+ return (my_ulonglong) uint2korr(pos+1);
+ }
+ if (*pos == 253)
+ {
+ (*packet)+=4;
+ return (my_ulonglong) uint3korr(pos+1);
+ }
+ (*packet)+=9; /* Must be 254 when here */
+#ifdef NO_CLIENT_LONGLONG
+ return (my_ulonglong) uint4korr(pos+1);
+#else
+ return (my_ulonglong) uint8korr(pos+1);
+#endif
+}
+
+/* Read all rows (fields or data) from server */
+
+static MYSQL_DATA *mc_read_rows(MYSQL *mysql,MYSQL_FIELD *mysql_fields,
+ uint fields)
+{
+ uint field,pkt_len;
+ ulong len;
+ uchar *cp;
+ char *to;
+ MYSQL_DATA *result;
+ MYSQL_ROWS **prev_ptr,*cur;
+ NET *net = &mysql->net;
+ DBUG_ENTER("mc_read_rows");
+
+ if ((pkt_len=(uint) mc_net_safe_read(mysql)) == packet_error)
+ DBUG_RETURN(0);
+ if (!(result=(MYSQL_DATA*) my_malloc(sizeof(MYSQL_DATA),
+ MYF(MY_WME | MY_ZEROFILL))))
+ {
+ net->last_errno=CR_OUT_OF_MEMORY;
+ strmov(net->last_error,ER(net->last_errno));
+ DBUG_RETURN(0);
+ }
+ init_alloc_root(&result->alloc,8192,0); /* Assume rowlength < 8192 */
+ result->alloc.min_malloc=sizeof(MYSQL_ROWS);
+ prev_ptr= &result->data;
+ result->rows=0;
+ result->fields=fields;
+
+ while (*(cp=net->read_pos) != 254 || pkt_len != 1)
+ {
+ result->rows++;
+ if (!(cur= (MYSQL_ROWS*) alloc_root(&result->alloc,
+ sizeof(MYSQL_ROWS))) ||
+ !(cur->data= ((MYSQL_ROW)
+ alloc_root(&result->alloc,
+ (fields+1)*sizeof(char *)+pkt_len))))
+ {
+ mc_free_rows(result);
+ net->last_errno=CR_OUT_OF_MEMORY;
+ strmov(net->last_error,ER(net->last_errno));
+ DBUG_RETURN(0);
+ }
+ *prev_ptr=cur;
+ prev_ptr= &cur->next;
+ to= (char*) (cur->data+fields+1);
+ for (field=0 ; field < fields ; field++)
+ {
+ if ((len=(ulong) mc_net_field_length(&cp)) == NULL_LENGTH)
+ { /* null field */
+ cur->data[field] = 0;
+ }
+ else
+ {
+ cur->data[field] = to;
+ memcpy(to,(char*) cp,len); to[len]=0;
+ to+=len+1;
+ cp+=len;
+ if (mysql_fields)
+ {
+ if (mysql_fields[field].max_length < len)
+ mysql_fields[field].max_length=len;
+ }
+ }
+ }
+ cur->data[field]=to; /* End of last field */
+ if ((pkt_len=mc_net_safe_read(mysql)) == packet_error)
+ {
+ mc_free_rows(result);
+ DBUG_RETURN(0);
+ }
+ }
+ *prev_ptr=0; /* last pointer is null */
+ DBUG_PRINT("exit",("Got %d rows",result->rows));
+ DBUG_RETURN(result);
+}
+
+
+/*
+** Read one row. Uses packet buffer as storage for fields.
+** When next packet is read, the previous field values are destroyed
+*/
+
+
+static int mc_read_one_row(MYSQL *mysql,uint fields,MYSQL_ROW row,
+ ulong *lengths)
+{
+ uint field;
+ ulong pkt_len,len;
+ uchar *pos,*prev_pos;
+
+ if ((pkt_len=(uint) mc_net_safe_read(mysql)) == packet_error)
+ return -1;
+ if (pkt_len == 1 && mysql->net.read_pos[0] == 254)
+ return 1; /* End of data */
+ prev_pos= 0; /* allowed to write at packet[-1] */
+ pos=mysql->net.read_pos;
+ for (field=0 ; field < fields ; field++)
+ {
+ if ((len=(ulong) mc_net_field_length(&pos)) == NULL_LENGTH)
+ { /* null field */
+ row[field] = 0;
+ *lengths++=0;
+ }
+ else
+ {
+ row[field] = (char*) pos;
+ pos+=len;
+ *lengths++=len;
+ }
+ if (prev_pos)
+ *prev_pos=0; /* Terminate prev field */
+ prev_pos=pos;
+ }
+ row[field]=(char*) prev_pos+1; /* End of last field */
+ *prev_pos=0; /* Terminate last field */
+ return 0;
+}
+
+my_ulonglong STDCALL mc_mysql_num_rows(MYSQL_RES *res)
+{
+ return res->row_count;
+}
+
+unsigned int STDCALL mc_mysql_num_fields(MYSQL_RES *res)
+{
+ return res->field_count;
+}
+
+void STDCALL mc_mysql_data_seek(MYSQL_RES *result, my_ulonglong row)
+{
+ MYSQL_ROWS *tmp=0;
+ DBUG_PRINT("info",("mysql_data_seek(%ld)",(long) row));
+ if (result->data)
+ for (tmp=result->data->data; row-- && tmp ; tmp = tmp->next) ;
+ result->current_row=0;
+ result->data_cursor = tmp;
+}
+
+MYSQL_ROW STDCALL mc_mysql_fetch_row(MYSQL_RES *res)
+{
+ DBUG_ENTER("mc_mysql_fetch_row");
+ if (!res->data)
+ { /* Unbufferred fetch */
+ if (!res->eof)
+ {
+ if (!(mc_read_one_row(res->handle,res->field_count,res->row,
+ res->lengths)))
+ {
+ res->row_count++;
+ DBUG_RETURN(res->current_row=res->row);
+ }
+ else
+ {
+ DBUG_PRINT("info",("end of data"));
+ res->eof=1;
+ res->handle->status=MYSQL_STATUS_READY;
+ }
+ }
+ DBUG_RETURN((MYSQL_ROW) NULL);
+ }
+ {
+ MYSQL_ROW tmp;
+ if (!res->data_cursor)
+ {
+ DBUG_PRINT("info",("end of data"));
+ DBUG_RETURN(res->current_row=(MYSQL_ROW) NULL);
+ }
+ tmp = res->data_cursor->data;
+ res->data_cursor = res->data_cursor->next;
+ DBUG_RETURN(res->current_row=tmp);
+ }
+}
+
+int STDCALL mc_mysql_select_db(MYSQL *mysql, const char *db)
+{
+ int error;
+ DBUG_ENTER("mysql_select_db");
+ DBUG_PRINT("enter",("db: '%s'",db));
+
+ if ((error=mc_simple_command(mysql,COM_INIT_DB,db,(uint) strlen(db),0)))
+ DBUG_RETURN(error);
+ my_free(mysql->db,MYF(MY_ALLOW_ZERO_PTR));
+ mysql->db=my_strdup(db,MYF(MY_WME));
+ DBUG_RETURN(0);
+}
+
+
+MYSQL_RES * STDCALL mc_mysql_store_result(MYSQL *mysql)
+{
+ MYSQL_RES *result;
+ DBUG_ENTER("mysql_store_result");
+
+ if (!mysql->fields)
+ DBUG_RETURN(0);
+ if (mysql->status != MYSQL_STATUS_GET_RESULT)
+ {
+ strmov(mysql->net.last_error,
+ ER(mysql->net.last_errno=CR_COMMANDS_OUT_OF_SYNC));
+ DBUG_RETURN(0);
+ }
+ mysql->status=MYSQL_STATUS_READY; /* server is ready */
+ if (!(result=(MYSQL_RES*) my_malloc(sizeof(MYSQL_RES)+
+ sizeof(ulong)*mysql->field_count,
+ MYF(MY_WME | MY_ZEROFILL))))
+ {
+ mysql->net.last_errno=CR_OUT_OF_MEMORY;
+ strmov(mysql->net.last_error, ER(mysql->net.last_errno));
+ DBUG_RETURN(0);
+ }
+ result->eof=1; /* Marker for buffered */
+ result->lengths=(ulong*) (result+1);
+ if (!(result->data=mc_read_rows(mysql,mysql->fields,mysql->field_count)))
+ {
+ my_free((gptr) result,MYF(0));
+ DBUG_RETURN(0);
+ }
+ mysql->affected_rows= result->row_count= result->data->rows;
+ result->data_cursor= result->data->data;
+ result->fields= mysql->fields;
+ result->field_alloc= mysql->field_alloc;
+ result->field_count= mysql->field_count;
+ result->current_field=0;
+ result->current_row=0; /* Must do a fetch first */
+ mysql->fields=0; /* fields is now in result */
+ DBUG_RETURN(result); /* Data fetched */
+}
+
+
+
+
+
+
+
+
diff --git a/sql/mini_client.h b/sql/mini_client.h
index f7d95a1b66e..22cdb31f846 100644
--- a/sql/mini_client.h
+++ b/sql/mini_client.h
@@ -42,6 +42,17 @@ char * STDCALL mc_mysql_error(MYSQL *mysql);
int STDCALL mc_mysql_errno(MYSQL *mysql);
my_bool STDCALL mc_mysql_reconnect(MYSQL* mysql);
+int STDCALL mc_mysql_send_query(MYSQL* mysql, const char* query, uint length);
+int STDCALL mc_mysql_read_query_result(MYSQL *mysql);
+int STDCALL mc_mysql_query(MYSQL *mysql, const char *query, uint length);
+MYSQL_RES * STDCALL mc_mysql_store_result(MYSQL *mysql);
+void STDCALL mc_mysql_free_result(MYSQL_RES *result);
+void STDCALL mc_mysql_data_seek(MYSQL_RES *result, my_ulonglong row);
+my_ulonglong STDCALL mc_mysql_num_rows(MYSQL_RES *res);
+unsigned int STDCALL mc_mysql_num_fields(MYSQL_RES *res);
+MYSQL_ROW STDCALL mc_mysql_fetch_row(MYSQL_RES *res);
+int STDCALL mc_mysql_select_db(MYSQL *mysql, const char *db);
+
#endif
diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h
index f21c635dbdf..63c9478d236 100644
--- a/sql/mysql_priv.h
+++ b/sql/mysql_priv.h
@@ -26,7 +26,6 @@
#include <thr_lock.h>
#include <my_base.h> /* Needed by field.h */
#include <my_bitmap.h>
-#include <violite.h>
#undef write // remove pthread.h macro definition for EMX
@@ -35,6 +34,7 @@ typedef ulong key_map; /* Used for finding keys */
typedef ulong key_part_map; /* Used for finding key parts */
#include "mysql_com.h"
+#include <violite.h>
#include "unireg.h"
void init_sql_alloc(MEM_ROOT *root, uint block_size, uint pre_alloc_size);
@@ -145,9 +145,9 @@ void kill_one_thread(THD *thd, ulong id);
#define SELECT_DESCRIBE 4
#define SELECT_SMALL_RESULT 8
#define SELECT_BIG_RESULT 16
+#define OPTION_FOUND_ROWS 32
#define SELECT_HIGH_PRIORITY 64 /* Intern */
-#define SELECT_USE_CACHE 256 /* Intern */
-#define SELECT_COUNT_DISTINCT 512 /* Intern */
+#define SELECT_NO_JOIN_CACHE 256 /* Intern */
#define OPTION_BIG_TABLES 512 /* for SQL OPTION */
#define OPTION_BIG_SELECTS 1024 /* for SQL OPTION */
@@ -223,7 +223,7 @@ inline THD *_current_thd(void)
#include "opt_range.h"
-void mysql_create_db(THD *thd, char *db, uint create_info);
+int mysql_create_db(THD *thd, char *db, uint create_info);
void mysql_binlog_send(THD* thd, char* log_ident, ulong pos, ushort flags);
int mysql_rm_table(THD *thd,TABLE_LIST *tables, my_bool if_exists);
int quick_rm_table(enum db_type base,const char *db,
@@ -232,6 +232,7 @@ bool mysql_rename_tables(THD *thd, TABLE_LIST *table_list);
bool mysql_change_db(THD *thd,const char *name);
void mysql_parse(THD *thd,char *inBuf,uint length);
void mysql_init_select(LEX *lex);
+void mysql_new_select(LEX *lex);
void init_max_user_conn(void);
void free_max_user_conn(void);
pthread_handler_decl(handle_one_connection,arg);
@@ -241,9 +242,11 @@ void end_thread(THD *thd,bool put_in_cache);
void flush_thread_cache();
void mysql_execute_command(void);
bool do_command(THD *thd);
+bool dispatch_command(enum enum_server_command command, THD *thd,
+ char* packet, uint packet_length);
bool check_stack_overrun(THD *thd,char *dummy);
bool reload_acl_and_cache(THD *thd, uint options, TABLE_LIST *tables);
-void mysql_rm_db(THD *thd,char *db,bool if_exists);
+int mysql_rm_db(THD *thd,char *db,bool if_exists);
void table_cache_init(void);
void table_cache_free(void);
uint cached_tables(void);
@@ -251,6 +254,7 @@ void kill_mysql(void);
void close_connection(NET *net,uint errcode=0,bool lock=1);
bool check_access(THD *thd,uint access,const char *db=0,uint *save_priv=0,
bool no_grant=0);
+bool check_table_access(THD *thd,uint want_access, TABLE_LIST *tables);
bool check_process_priv(THD *thd=0);
int generate_table(THD *thd, TABLE_LIST *table_list,
@@ -301,6 +305,7 @@ int mysql_select(THD *thd,TABLE_LIST *tables,List<Item> &list,COND *conds,
List<Item_func_match> &ftfuncs,
ORDER *order, ORDER *group,Item *having,ORDER *proc_param,
uint select_type,select_result *result);
+int mysql_union(THD *thd,LEX *lex, uint no);
Field *create_tmp_field(TABLE *table,Item *item, Item::Type type,
Item_result_field ***copy_func, Field **from_field,
bool group,bool modify_item);
@@ -325,7 +330,9 @@ int mysql_alter_table(THD *thd, char *new_db, char *new_name,
List<Alter_column> &alter_list,
ORDER *order,
bool drop_primary,
- enum enum_duplicates handle_duplicates);
+ enum enum_duplicates handle_duplicates,
+ enum enum_enable_or_disable keys_onoff=LEAVE_AS_IS,
+ bool simple_alter=0);
bool mysql_rename_table(enum db_type base,
const char *old_db,
const char * old_name,
@@ -336,15 +343,16 @@ int mysql_create_index(THD *thd, TABLE_LIST *table_list, List<Key> &keys);
int mysql_drop_index(THD *thd, TABLE_LIST *table_list,
List<Alter_drop> &drop_list);
int mysql_update(THD *thd,TABLE_LIST *tables,List<Item> &fields,
- List<Item> &values,COND *conds, ha_rows limit,
+ List<Item> &values,COND *conds,
+ ORDER *order, ha_rows limit,
enum enum_duplicates handle_duplicates,
thr_lock_type lock_type);
int mysql_insert(THD *thd,TABLE_LIST *table,List<Item> &fields,
List<List_item> &values, enum_duplicates flag,
thr_lock_type lock_type);
void kill_delayed_threads(void);
-int mysql_delete(THD *thd,TABLE_LIST *table,COND *conds,ha_rows rows,
- thr_lock_type lock_type, ulong options);
+int mysql_delete(THD *thd, TABLE_LIST *table, COND *conds, ORDER *order,
+ ha_rows rows, thr_lock_type lock_type, ulong options);
TABLE *open_ltable(THD *thd, TABLE_LIST *table_list, thr_lock_type update);
TABLE *open_table(THD *thd,const char *db,const char *table,const char *alias,
bool *refresh);
@@ -365,7 +373,7 @@ Field *find_field_in_table(THD *thd,TABLE *table,const char *name,uint length,
/* sql_list.c */
int mysqld_show_dbs(THD *thd,const char *wild);
-int mysqld_show_open_tables(THD *thd,const char *db,const char *wild);
+int mysqld_show_open_tables(THD *thd,const char *wild);
int mysqld_show_tables(THD *thd,const char *db,const char *wild);
int mysqld_extend_show_tables(THD *thd,const char *db,const char *wild);
int mysqld_show_fields(THD *thd,TABLE_LIST *table, const char *wild,
@@ -381,6 +389,12 @@ int mysqld_show_status(THD *thd);
int mysqld_show_variables(THD *thd,const char *wild);
int mysqld_show(THD *thd, const char *wild, show_var_st *variables);
+/* sql_handler.cc */
+int mysql_ha_open(THD *thd, TABLE_LIST *tables);
+int mysql_ha_close(THD *thd, TABLE_LIST *tables);
+int mysql_ha_read(THD *, TABLE_LIST *,enum enum_ha_read_modes,char *,
+ List<Item> *,enum ha_rkey_function,Item *,ha_rows,ha_rows);
+
/* sql_base.cc */
void set_item_name(Item *item,char *pos,uint length);
bool add_field_to_list(char *field_name, enum enum_field_types type,
@@ -402,6 +416,9 @@ TABLE *unlink_open_table(THD *thd,TABLE *list,TABLE *find);
SQL_SELECT *make_select(TABLE *head, table_map const_tables,
table_map read_tables, COND *conds, int *error);
Item ** find_item_in_list(Item *item,List<Item> &items);
+bool insert_fields(THD *thd,TABLE_LIST *tables,
+ const char *db_name, const char *table_name,
+ List_iterator<Item> *it);
bool setup_tables(TABLE_LIST *tables);
int setup_fields(THD *thd,TABLE_LIST *tables,List<Item> &item,
bool set_query_id,List<Item> *sum_func_list);
@@ -418,6 +435,7 @@ bool send_fields(THD *thd,List<Item> &item,uint send_field_count);
void free_io_cache(TABLE *entry);
void intern_close_table(TABLE *entry);
void close_thread_tables(THD *thd,bool locked=0);
+bool close_thread_table(THD *thd, TABLE **table_ptr);
void close_temporary_tables(THD *thd);
TABLE **find_temporary_table(THD *thd, const char *db, const char *table_name);
bool close_temporary_table(THD *thd, const char *db, const char *table_name);
@@ -432,8 +450,7 @@ bool close_cached_tables(THD *thd, bool wait_for_refresh, TABLE_LIST *tables);
void copy_field_from_tmp_record(Field *field,int offset);
int fill_record(List<Item> &fields,List<Item> &values);
int fill_record(Field **field,List<Item> &values);
-int list_open_tables(THD *thd,List<char> *files, const char *db,const char *wild);
-char* query_table_status(THD *thd,const char *db,const char *table_name);
+OPEN_TABLE_LIST *list_open_tables(THD *thd,const char *wild);
/* sql_calc.cc */
bool eval_const_cond(COND *cond);
@@ -458,8 +475,7 @@ pthread_handler_decl(handle_manager, arg);
#ifndef DBUG_OFF
void print_where(COND *cond,const char *info);
void print_cached_tables(void);
-void TEST_filesort(TABLE **form,SORT_FIELD *sortorder,uint s_length,
- ha_rows special);
+void TEST_filesort(SORT_FIELD *sortorder,uint s_length, ha_rows special);
#endif
void mysql_print_status(THD *thd);
/* key.cc */
@@ -505,7 +521,7 @@ extern pthread_mutex_t LOCK_mysql_create_db,LOCK_Acl,LOCK_open,
LOCK_thread_count,LOCK_mapped_file,LOCK_user_locks, LOCK_status,
LOCK_grant, LOCK_error_log, LOCK_delayed_insert,
LOCK_delayed_status, LOCK_delayed_create, LOCK_crypt, LOCK_timezone,
- LOCK_binlog_update, LOCK_slave, LOCK_server_id;
+ LOCK_binlog_update, LOCK_slave, LOCK_server_id, LOCK_slave_list;
extern pthread_cond_t COND_refresh,COND_thread_count, COND_binlog_update,
COND_slave_stopped, COND_slave_start;
extern pthread_attr_t connection_attrib;
@@ -601,7 +617,7 @@ void init_read_record(READ_RECORD *info, THD *thd, TABLE *reg_form,
SQL_SELECT *select,
int use_record_cache, bool print_errors);
void end_read_record(READ_RECORD *info);
-ha_rows filesort(TABLE **form,struct st_sort_field *sortorder, uint s_length,
+ha_rows filesort(TABLE *form,struct st_sort_field *sortorder, uint s_length,
SQL_SELECT *select, ha_rows special,ha_rows max_rows,
ha_rows *examined_rows);
void change_double_for_sort(double nr,byte *to);
@@ -652,7 +668,7 @@ extern int sql_cache_hit(THD *thd, char *inBuf, uint length);
inline bool add_item_to_list(Item *item)
{
- return current_lex->item_list.push_back(item);
+ return current_lex->select->item_list.push_back(item);
}
inline bool add_value_to_list(Item *value)
{
@@ -660,11 +676,11 @@ inline bool add_value_to_list(Item *value)
}
inline bool add_order_to_list(Item *item,bool asc)
{
- return add_to_list(current_lex->order_list,item,asc);
+ return add_to_list(current_lex->select->order_list,item,asc);
}
inline bool add_group_to_list(Item *item,bool asc)
{
- return add_to_list(current_lex->group_list,item,asc);
+ return add_to_list(current_lex->select->group_list,item,asc);
}
inline void mark_as_null_row(TABLE *table)
{
diff --git a/sql/mysqld.cc b/sql/mysqld.cc
index 991b0e73c51..7c40b5f15ef 100644
--- a/sql/mysqld.cc
+++ b/sql/mysqld.cc
@@ -20,6 +20,7 @@
#include <my_dir.h>
#include "sql_acl.h"
#include "slave.h"
+#include "sql_repl.h"
#include "stacktrace.h"
#ifdef HAVE_BERKELEY_DB
#include "ha_berkeley.h"
@@ -39,6 +40,13 @@
#define ONE_THREAD
#endif
+/* do stack traces are only supported on linux intel */
+#if defined(__linux__) && defined(__i386__) && defined(USE_PSTACK)
+#define HAVE_STACK_TRACE_ON_SEGV
+#include "../pstack/pstack.h"
+char pstack_file_name[80];
+#endif /* __linux__ */
+
extern "C" { // Because of SCO 3.2V4.2
#include <errno.h>
#include <sys/stat.h>
@@ -198,9 +206,11 @@ SHOW_COMP_OPTION have_ssl=SHOW_OPTION_YES;
#else
SHOW_COMP_OPTION have_ssl=SHOW_OPTION_NO;
#endif
+SHOW_COMP_OPTION have_symlink=SHOW_OPTION_YES;
static bool opt_skip_slave_start = 0; // if set, slave is not autostarted
+static bool opt_do_pstack = 0;
static ulong opt_specialflag=SPECIAL_ENGLISH;
static my_socket unix_sock= INVALID_SOCKET,ip_sock= INVALID_SOCKET;
static ulong back_log,connect_timeout,concurrency;
@@ -212,7 +222,8 @@ static bool opt_log,opt_update_log,opt_bin_log,opt_slow_log,opt_noacl,
opt_disable_networking=0, opt_bootstrap=0,opt_skip_show_db=0,
opt_ansi_mode=0,opt_myisam_log=0,
opt_large_files=sizeof(my_off_t) > 4;
-bool opt_sql_bin_update = 0, opt_log_slave_updates = 0, opt_safe_show_db=0;
+bool opt_sql_bin_update = 0, opt_log_slave_updates = 0, opt_safe_show_db=0,
+ opt_show_slave_auth_info = 0;
FILE *bootstrap_file=0;
int segfaulted = 0; // ensure we do not enter SIGSEGV handler twice
extern MASTER_INFO glob_mi;
@@ -234,7 +245,7 @@ static char *opt_ssl_key = 0;
static char *opt_ssl_cert = 0;
static char *opt_ssl_ca = 0;
static char *opt_ssl_capath = 0;
-static VioSSLAcceptorFd* ssl_acceptor_fd = 0;
+struct st_VioSSLAcceptorFd * ssl_acceptor_fd = 0;
#endif /* HAVE_OPENSSL */
@@ -248,7 +259,7 @@ uint32 server_id = 0;
bool server_id_supplied = 0;
uint mysql_port;
-uint test_flags, select_errors=0, dropping_tables=0,ha_open_options=0;
+uint test_flags = 0, select_errors=0, dropping_tables=0,ha_open_options=0;
uint volatile thread_count=0, thread_running=0, kill_cached_threads=0,
wake_thread=0, global_read_lock=0;
ulong thd_startup_options=(OPTION_UPDATE_LOG | OPTION_AUTO_IS_NULL |
@@ -267,9 +278,12 @@ volatile ulong cached_thread_count=0;
// replication parameters, if master_host is not NULL, we are a slave
my_string master_user = (char*) "test", master_password = 0, master_host=0,
master_info_file = (char*) "master.info";
+my_string report_user = 0, report_password = 0, report_host=0;
+
const char *localhost=LOCAL_HOST;
const char *delayed_user="DELAYED";
uint master_port = MYSQL_PORT, master_connect_retry = 60;
+uint report_port = MYSQL_PORT;
ulong max_tmp_tables,max_heap_table_size;
ulong bytes_sent = 0L, bytes_received = 0L;
@@ -331,7 +345,7 @@ pthread_mutex_t LOCK_mysql_create_db, LOCK_Acl, LOCK_open, LOCK_thread_count,
LOCK_delayed_insert, LOCK_delayed_status, LOCK_delayed_create,
LOCK_crypt, LOCK_bytes_sent, LOCK_bytes_received,
LOCK_binlog_update, LOCK_slave, LOCK_server_id,
- LOCK_user_conn;
+ LOCK_user_conn, LOCK_slave_list;
pthread_cond_t COND_refresh,COND_thread_count,COND_binlog_update,
COND_slave_stopped, COND_slave_start;
@@ -509,7 +523,7 @@ static void close_connections(void)
/* Force remaining threads to die by closing the connection to the client */
- (void) my_net_init(&net, (Vio*) 0);
+ (void) my_net_init(&net, (st_vio*) 0);
for (;;)
{
DBUG_PRINT("quit",("Locking LOCK_thread_count"));
@@ -685,6 +699,7 @@ void clean_up(bool print_message)
bitmap_free(&temp_pool);
free_max_user_conn();
end_slave();
+ end_slave_list();
#ifndef __WIN__
if (!opt_bootstrap)
(void) my_delete(pidfile_name,MYF(0)); // This may not always exist
@@ -951,7 +966,7 @@ void yyerror(const char *s)
void close_connection(NET *net,uint errcode,bool lock)
{
- Vio* vio;
+ st_vio* vio;
DBUG_ENTER("close_connection");
DBUG_PRINT("enter",("fd: %s error: '%s'",
net->vio? vio_description(net->vio):"(not connected)",
@@ -1323,6 +1338,13 @@ static void *signal_hand(void *arg __attribute__((unused)))
(void) my_close(pidFile,MYF(0));
}
}
+#ifdef HAVE_STACK_TRACE_ON_SEGV
+ if (opt_do_pstack)
+ {
+ sprintf(pstack_file_name,"mysqld-%lu-%%d-%%d.backtrace", (ulong)getpid());
+ pstack_install_segv_action(pstack_file_name);
+ }
+#endif /* HAVE_STACK_TRACE_ON_SEGV */
// signal to start_signal_handler that we are ready
(void) pthread_mutex_lock(&LOCK_thread_count);
@@ -1357,7 +1379,7 @@ static void *signal_hand(void *arg __attribute__((unused)))
if (!(opt_specialflag & SPECIAL_NO_PRIOR))
my_pthread_attr_setprio(&connection_attrib,INTERRUPT_PRIOR);
if (pthread_create(&tmp,&connection_attrib, kill_server_thread,
- (void*) 0))
+ (void*) sig))
sql_print_error("Error: Can't create thread to kill server");
#else
kill_server((void*) sig); // MIT THREAD has a alarm thread
@@ -1593,8 +1615,9 @@ int main(int argc, char **argv)
#ifdef HAVE_OPENSSL
if (opt_use_ssl)
{
- ssl_acceptor_fd = VioSSLAcceptorFd_new(opt_ssl_key, opt_ssl_cert,
+ ssl_acceptor_fd = new_VioSSLAcceptorFd(opt_ssl_key, opt_ssl_cert,
opt_ssl_ca, opt_ssl_capath);
+ DBUG_PRINT("info",("ssl_acceptor_fd: %p",ssl_acceptor_fd));
if (!ssl_acceptor_fd)
opt_use_ssl=0;
/* having ssl_acceptor_fd!=0 signals the use of SSL */
@@ -1667,7 +1690,8 @@ int main(int argc, char **argv)
randominit(&sql_rand,(ulong) start_time,(ulong) start_time/2);
reset_floating_point_exceptions();
init_thr_lock();
-
+ init_slave_list();
+
/* Fix varibles that are base 1024*1024 */
myisam_max_temp_length= (my_off_t) min(((ulonglong) myisam_max_sort_file_size)*1024*1024, (ulonglong) MAX_FILE_SIZE);
myisam_max_extra_temp_length= (my_off_t) min(((ulonglong) myisam_max_extra_sort_file_size)*1024*1024, (ulonglong) MAX_FILE_SIZE);
@@ -1730,7 +1754,7 @@ The server will not act as a slave.");
LOG_BIN);
using_update_log=1;
}
-
+
if (opt_slow_log)
open_log(&mysql_slow_log, glob_hostname, opt_slow_logname, "-slow.log",
LOG_NORMAL);
@@ -1752,7 +1776,7 @@ The server will not act as a slave.");
}
#else
locked_in_memory=0;
-#endif
+#endif
if (opt_myisam_log)
(void) mi_log( 1 );
@@ -2009,7 +2033,7 @@ static int bootstrap(FILE *file)
int error;
thd->bootstrap=1;
thd->client_capabilities=0;
- my_net_init(&thd->net,(Vio*) 0);
+ my_net_init(&thd->net,(st_vio*) 0);
thd->max_packet_length=thd->net.max_packet;
thd->master_access= ~0;
thd->thread_id=thread_id++;
@@ -2146,7 +2170,7 @@ pthread_handler_decl(handle_connections_sockets,arg __attribute__((unused)))
THD *thd;
struct sockaddr_in cAddr;
int ip_flags=0,socket_flags=0,flags;
- Vio *vio_tmp;
+ st_vio *vio_tmp;
DBUG_ENTER("handle_connections_sockets");
LINT_INIT(new_sock);
@@ -2455,13 +2479,15 @@ enum options {
OPT_INNODB_LOG_ARCH_DIR,
OPT_INNODB_LOG_ARCHIVE,
OPT_INNODB_FLUSH_LOG_AT_TRX_COMMIT,
- OPT_INNODB_UNIX_FILE_FLUSH_METHOD,
+ OPT_INNODB_FLUSH_METHOD,
OPT_SAFE_SHOW_DB,
OPT_GEMINI_SKIP, OPT_INNODB_SKIP,
- OPT_TEMP_POOL, OPT_TX_ISOLATION,
+ OPT_TEMP_POOL, OPT_DO_PSTACK, OPT_TX_ISOLATION,
OPT_GEMINI_FLUSH_LOG, OPT_GEMINI_RECOVER,
OPT_GEMINI_UNBUFFERED_IO, OPT_SKIP_SAFEMALLOC,
- OPT_SKIP_STACK_TRACE, OPT_SKIP_SYMLINKS
+ OPT_SKIP_STACK_TRACE, OPT_SKIP_SYMLINK, OPT_REPORT_HOST,
+ OPT_REPORT_USER, OPT_REPORT_PASSWORD, OPT_REPORT_PORT,
+ OPT_SHOW_SLAVE_AUTH_INFO
};
static struct option long_options[] = {
@@ -2493,6 +2519,8 @@ static struct option long_options[] = {
{"default-table-type", required_argument, 0, (int) OPT_TABLE_TYPE},
{"delay-key-write-for-all-tables",
no_argument, 0, (int) OPT_DELAY_KEY_WRITE},
+ {"do-pstack",
+ no_argument, 0, (int) OPT_DO_PSTACK},
{"enable-locking", no_argument, 0, (int) OPT_ENABLE_LOCK},
{"exit-info", optional_argument, 0, 'T'},
{"flush", no_argument, 0, (int) OPT_FLUSH},
@@ -2517,7 +2545,7 @@ static struct option long_options[] = {
{"innodb_flush_log_at_trx_commit", optional_argument, 0,
OPT_INNODB_FLUSH_LOG_AT_TRX_COMMIT},
{"innodb_flush_method", required_argument, 0,
- OPT_INNODB_UNIX_FILE_FLUSH_METHOD},
+ OPT_INNODB_FLUSH_METHOD},
#endif
{"help", no_argument, 0, '?'},
{"init-file", required_argument, 0, (int) OPT_INIT_FILE},
@@ -2567,11 +2595,19 @@ static struct option long_options[] = {
(int) OPT_REPLICATE_WILD_IGNORE_TABLE},
{"replicate-rewrite-db", required_argument, 0,
(int) OPT_REPLICATE_REWRITE_DB},
+ // In replication, we may need to tell the other servers how to connect
+ // to us
+ {"report-host", required_argument, 0, (int) OPT_REPORT_HOST},
+ {"report-user", required_argument, 0, (int) OPT_REPORT_USER},
+ {"report-password", required_argument, 0, (int) OPT_REPORT_PASSWORD},
+ {"report-port", required_argument, 0, (int) OPT_REPORT_PORT},
{"safe-mode", no_argument, 0, (int) OPT_SAFE},
{"safe-show-database", no_argument, 0, (int) OPT_SAFE_SHOW_DB},
{"socket", required_argument, 0, (int) OPT_SOCKET},
{"server-id", required_argument, 0, (int) OPT_SERVER_ID},
{"set-variable", required_argument, 0, 'O'},
+ {"show-slave-auth-info", no_argument, 0,
+ (int) OPT_SHOW_SLAVE_AUTH_INFO},
{"skip-bdb", no_argument, 0, (int) OPT_BDB_SKIP},
{"skip-innodb", no_argument, 0, (int) OPT_INNODB_SKIP},
{"skip-gemini", no_argument, 0, (int) OPT_GEMINI_SKIP},
@@ -2587,7 +2623,7 @@ static struct option long_options[] = {
{"skip-show-database", no_argument, 0, (int) OPT_SKIP_SHOW_DB},
{"skip-slave-start", no_argument, 0, (int) OPT_SKIP_SLAVE_START},
{"skip-stack-trace", no_argument, 0, (int) OPT_SKIP_STACK_TRACE},
- {"skip-symlink", no_argument, 0, (int) OPT_SKIP_SYMLINKS},
+ {"skip-symlink", no_argument, 0, (int) OPT_SKIP_SYMLINK},
{"skip-thread-priority", no_argument, 0, (int) OPT_SKIP_PRIOR},
{"sql-bin-update-same", no_argument, 0, (int) OPT_SQL_BIN_UPDATE_SAME},
#include "sslopt-longopts.h"
@@ -2598,9 +2634,6 @@ static struct option long_options[] = {
{"temp-pool", no_argument, 0, (int) OPT_TEMP_POOL},
{"tmpdir", required_argument, 0, 't'},
{"use-locking", no_argument, 0, (int) OPT_USE_LOCKING},
-#ifdef USE_SYMDIR
- {"use-symbolic-links", no_argument, 0, 's'},
-#endif
{"user", required_argument, 0, 'u'},
{"version", no_argument, 0, 'V'},
{"warnings", no_argument, 0, 'W'},
@@ -2633,6 +2666,12 @@ CHANGEABLE_VAR changeable_vars[] = {
DELAYED_QUEUE_SIZE, 1, ~0L, 0, 1 },
{ "flush_time", (long*) &flush_time,
FLUSH_TIME, 0, ~0L, 0, 1 },
+ { "ft_min_word_len", (long*) &ft_min_word_len,
+ 4, 1, HA_FT_MAXLEN, 0, 1 },
+ { "ft_max_word_len", (long*) &ft_max_word_len,
+ HA_FT_MAXLEN, 10, HA_FT_MAXLEN, 0, 1 },
+ { "ft_max_word_len_for_sort",(long*) &ft_max_word_len_for_sort,
+ 20, 4, HA_FT_MAXLEN, 0, 1 },
#ifdef HAVE_GEMINI_DB
{ "gemini_buffer_cache", (long*) &gemini_buffer_cache,
128 * 8192, 16, LONG_MAX, 0, 1 },
@@ -2773,6 +2812,9 @@ struct show_var_st init_vars[]= {
{"delayed_queue_size", (char*) &delayed_queue_size, SHOW_LONG},
{"flush", (char*) &myisam_flush, SHOW_MY_BOOL},
{"flush_time", (char*) &flush_time, SHOW_LONG},
+ {"ft_min_word_len", (char*) &ft_min_word_len, SHOW_LONG},
+ {"ft_max_word_len", (char*) &ft_max_word_len, SHOW_LONG},
+ {"ft_max_word_len_for_sort",(char*) &ft_max_word_len_for_sort, SHOW_LONG},
#ifdef HAVE_GEMINI_DB
{"gemini_buffer_cache", (char*) &gemini_buffer_cache, SHOW_LONG},
{"gemini_connection_limit", (char*) &gemini_connection_limit, SHOW_LONG},
@@ -2788,6 +2830,7 @@ struct show_var_st init_vars[]= {
{"have_innodb", (char*) &have_innodb, SHOW_HAVE},
{"have_isam", (char*) &have_isam, SHOW_HAVE},
{"have_raid", (char*) &have_raid, SHOW_HAVE},
+ {"have_symlink", (char*) &have_symlink, SHOW_HAVE},
{"have_ssl", (char*) &have_ssl, SHOW_HAVE},
{"init_file", (char*) &opt_init_file, SHOW_CHAR_PTR},
#ifdef HAVE_INNOBASE_DB
@@ -3007,9 +3050,6 @@ static void usage(void)
puts("\
-O, --set-variable var=option\n\
Give a variable an value. --help lists variables\n\
- -Sg, --skip-grant-tables\n\
- Start without grant tables. This gives all users\n\
- FULL ACCESS to all tables!\n\
--safe-mode Skip some optimize stages (for testing)\n\
--safe-show-database Don't show databases for which the user has no\n\
privileges\n\
@@ -3017,6 +3057,8 @@ static void usage(void)
Don't use concurrent insert with MyISAM\n\
--skip-delay-key-write\n\
Ignore the delay_key_write option for all tables\n\
+ --skip-grant-tables Start without grant tables. This gives all users\n\
+ FULL ACCESS to all tables!\n\
--skip-host-cache Don't cache host names\n\
--skip-locking Don't use system locking. To use isamchk one has\n\
to shut down the server.\n\
@@ -3027,6 +3069,7 @@ static void usage(void)
/* We have to break the string here because of VC++ limits */
puts("\
--skip-stack-trace Don't print a stack trace on failure\n\
+ --skip-symlink Don't allow symlinking of tables\n\
--skip-show-database Don't allow 'SHOW DATABASE' commands\n\
--skip-thread-priority\n\
Don't give threads different priorities.\n\
@@ -3045,9 +3088,6 @@ static void usage(void)
--remove Remove mysqld from the service list (NT)\n\
--standalone Dummy option to start as a standalone program (NT)\
");
-#ifdef USE_SYMDIR
- puts("--use-symbolic-links Enable symbolic link support");
-#endif
puts("");
#endif
#ifdef HAVE_BERKELEY_DB
@@ -3159,7 +3199,7 @@ static void set_options(void)
#endif
#if defined( HAVE_mit_thread ) || defined( __WIN__ ) || defined( HAVE_LINUXTHREADS )
- my_disable_locking = 1;
+ my_disable_locking=myisam_single_user= 1;
#endif
my_bind_addr = htonl( INADDR_ANY );
}
@@ -3171,6 +3211,8 @@ static void get_options(int argc,char **argv)
int c,option_index=0;
myisam_delay_key_write=1; // Allow use of this
+ my_use_symdir=1; // Use internal symbolic links
+
while ((c=getopt_long(argc,argv,"ab:C:h:#::T::?l::L:O:P:sS::t:u:noVvWI?",
long_options, &option_index)) != EOF)
{
@@ -3223,17 +3265,15 @@ static void get_options(int argc,char **argv)
safemalloc_mem_limit = atoi(optarg);
#endif
break;
+ case OPT_SHOW_SLAVE_AUTH_INFO:
+ opt_show_slave_auth_info = 1;
+ break;
case OPT_SOCKET:
mysql_unix_port= optarg;
break;
case 'r':
mysqld_chroot=optarg;
break;
-#ifdef USE_SYMDIR
- case 's':
- my_use_symdir=1; /* Use internal symbolic links */
- break;
-#endif
case 't':
mysql_tmpdir=optarg;
break;
@@ -3255,20 +3295,6 @@ static void get_options(int argc,char **argv)
test_flags= optarg ? (uint) atoi(optarg) : 0;
opt_endinfo=1;
break;
- case 'S':
- if (!optarg)
- opt_specialflag|= SPECIAL_NO_NEW_FUNC | SPECIAL_SAFE_MODE;
- else if (!strcmp(optarg,"l"))
- my_disable_locking=1;
- else if (!strcmp(optarg,"g"))
- opt_noacl=1;
- else
- {
- fprintf(stderr,"%s: Unrecognized option: %s\n",my_progname,optarg);
- use_help();
- exit(1);
- }
- break;
case (int) OPT_BIG_TABLES:
thd_startup_options|=OPTION_BIG_TABLES;
break;
@@ -3429,6 +3455,8 @@ static void get_options(int argc,char **argv)
myisam_concurrent_insert=0;
myisam_recover_options= HA_RECOVER_NONE;
my_disable_symlinks=1;
+ my_use_symdir=0;
+ have_symlink=SHOW_OPTION_DISABLED;
ha_open_options&= ~HA_OPEN_ABORT_IF_CRASHED;
break;
case (int) OPT_SAFE:
@@ -3447,7 +3475,7 @@ static void get_options(int argc,char **argv)
opt_noacl=1;
break;
case (int) OPT_SKIP_LOCK:
- my_disable_locking=1;
+ my_disable_locking=myisam_single_user= 1;
break;
case (int) OPT_SKIP_HOST_CACHE:
opt_specialflag|= SPECIAL_NO_HOST_CACHE;
@@ -3485,8 +3513,10 @@ static void get_options(int argc,char **argv)
case (int) OPT_SKIP_STACK_TRACE:
test_flags|=TEST_NO_STACKTRACE;
break;
- case (int) OPT_SKIP_SYMLINKS:
+ case (int) OPT_SKIP_SYMLINK:
my_disable_symlinks=1;
+ my_use_symdir=0;
+ have_symlink=SHOW_OPTION_DISABLED;
break;
case (int) OPT_BIND_ADDRESS:
if (optarg && isdigit(optarg[0]))
@@ -3673,10 +3703,13 @@ static void get_options(int argc,char **argv)
case OPT_INNODB_FLUSH_LOG_AT_TRX_COMMIT:
innobase_flush_log_at_trx_commit= optarg ? test(atoi(optarg)) : 1;
break;
- case OPT_INNODB_UNIX_FILE_FLUSH_METHOD:
+ case OPT_INNODB_FLUSH_METHOD:
innobase_unix_file_flush_method=optarg;
break;
#endif /* HAVE_INNOBASE_DB */
+ case OPT_DO_PSTACK:
+ opt_do_pstack = 1;
+ break;
case OPT_MYISAM_RECOVER:
{
if (!optarg || !optarg[0])
@@ -3712,6 +3745,18 @@ static void get_options(int argc,char **argv)
case OPT_MASTER_PORT:
master_port= atoi(optarg);
break;
+ case OPT_REPORT_HOST:
+ report_host=optarg;
+ break;
+ case OPT_REPORT_USER:
+ report_user=optarg;
+ break;
+ case OPT_REPORT_PASSWORD:
+ report_password=optarg;
+ break;
+ case OPT_REPORT_PORT:
+ report_port= atoi(optarg);
+ break;
case OPT_MASTER_CONNECT_RETRY:
master_connect_retry= atoi(optarg);
break;
@@ -4016,7 +4061,7 @@ static int get_service_parameters()
}
else if ( lstrcmp(szKeyValueName, TEXT("KeyBufferSize")) == 0 )
{
- SET_CHANGEABLE_VARVAL( "key_buffer" );
+ SET_CHANGEABLE_VARVAL( "key_buffer_size" );
}
else if ( lstrcmp(szKeyValueName, TEXT("LongQueryTime")) == 0 )
{
diff --git a/sql/net_serv.cc b/sql/net_serv.cc
index 4c4642034e1..cde27d4933a 100644
--- a/sql/net_serv.cc
+++ b/sql/net_serv.cc
@@ -1,15 +1,15 @@
/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB
-
+
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
-
+
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
-
+
You should have received a copy of the GNU Library General Public
License along with this library; if not, write to the Free
Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
@@ -22,10 +22,16 @@
** 3 byte length & 1 byte package-number.
*/
+#ifdef EMBEDDED_LIBRARY
+#define net_read_timeout net_read_timeout1
+#define net_write_timeout net_write_timeout1
+#endif
+
#ifdef __WIN__
#include <winsock.h>
#endif
#include <global.h>
+#include <mysql_com.h>
#include <violite.h>
#include <my_sys.h>
#include <m_string.h>
@@ -34,14 +40,23 @@
#include <signal.h>
#include <errno.h>
#include <sys/types.h>
-#include <violite.h>
+#include <assert.h>
+
+extern "C" {
#ifdef MYSQL_SERVER
ulong max_allowed_packet=65536;
extern ulong net_read_timeout,net_write_timeout;
extern uint test_flags;
#else
-ulong max_allowed_packet=16*1024*1024L;
+
+/*
+** Give error if a too big packet is found
+** The server can change this with the -O switch, but because the client
+** can't normally do this the client should have a bigger max_allowed_packet.
+*/
+
+ulong max_allowed_packet=~0L;
ulong net_read_timeout= NET_READ_TIMEOUT;
ulong net_write_timeout= NET_WRITE_TIMEOUT;
#endif
@@ -50,7 +65,7 @@ ulong net_buffer_length=8192; /* Default length. Enlarged if necessary */
#if !defined(__WIN__) && !defined(MSDOS)
#include <sys/socket.h>
#else
-#undef MYSQL_SERVER // Win32 can't handle interrupts
+#undef MYSQL_SERVER /* Win32 can't handle interrupts */
#endif
#if !defined(MSDOS) && !defined(__WIN__) && !defined(HAVE_BROKEN_NETINET_INCLUDES) && !defined(__BEOS__)
#include <netinet/in_systm.h>
@@ -84,28 +99,25 @@ inline int local_thr_alarm(my_bool *A,int B __attribute__((unused)),ALARM *C __a
#endif
#ifdef MYSQL_SERVER
-extern ulong bytes_sent, bytes_received;
+extern ulong bytes_sent, bytes_received;
extern pthread_mutex_t LOCK_bytes_sent , LOCK_bytes_received;
#else
#undef statistic_add
#define statistic_add(A,B,C)
#endif
-/*
-** Give error if a too big packet is found
-** The server can change this with the -O switch, but because the client
-** can't normally do this the client should have a bigger max-buffer.
-*/
-
#define TEST_BLOCKING 8
-static int net_write_buff(NET *net,const char *packet,uint len);
+static int net_write_buff(NET *net,const char *packet,ulong len);
+#define MAX_THREE_BYTES 255L*255L*255L
/* Init with packet info */
int my_net_init(NET *net, Vio* vio)
{
- if (!(net->buff=(uchar*) my_malloc(net_buffer_length,MYF(MY_WME))))
+ if (!(net->buff=(uchar*) my_malloc(net_buffer_length+
+ NET_HEADER_SIZE + COMP_HEADER_SIZE,
+ MYF(MY_WME))))
return 1;
if (net_buffer_length > max_allowed_packet)
max_allowed_packet=net_buffer_length;
@@ -152,8 +164,12 @@ static my_bool net_realloc(NET *net, ulong length)
net->last_errno=ER_NET_PACKET_TOO_LARGE;
return 1;
}
- pkt_length = (length+IO_SIZE-1) & ~(IO_SIZE-1);
- if (!(buff=(uchar*) my_realloc((char*) net->buff, pkt_length, MYF(MY_WME))))
+ pkt_length = (length+IO_SIZE-1) & ~(IO_SIZE-1);
+ /* We must allocate some extra bytes for the end 0 and to be able to
+ read big compressed blocks */
+ if (!(buff=(uchar*) my_realloc((char*) net->buff, pkt_length +
+ NET_HEADER_SIZE + COMP_HEADER_SIZE,
+ MYF(MY_WME))))
{
net->error=1;
#ifdef MYSQL_SERVER
@@ -209,18 +225,34 @@ int net_flush(NET *net)
** Write something to server/client buffer
*****************************************************************************/
-
/*
** Write a logical packet with packet header
** Format: Packet length (3 bytes), packet number(1 byte)
** When compression is used a 3 byte compression length is added
-** NOTE: If compression is used the original package is destroyed!
+** NOTE: If compression is used the original package is modified!
*/
int
my_net_write(NET *net,const char *packet,ulong len)
{
uchar buff[NET_HEADER_SIZE];
+ /*
+ Big packets are handled by splitting them in packets of MAX_THREE_BYTES
+ length. The last packet is always a packet that is < MAX_THREE_BYTES.
+ (The last packet may even have a lengt of 0)
+ */
+ while (len >= MAX_THREE_BYTES)
+ {
+ const ulong z_size = MAX_THREE_BYTES;
+ int3store(buff, z_size);
+ buff[3]= (net->compress) ? 0 : (uchar) (net->pkt_nr++);
+ if (net_write_buff(net, (char*) buff, NET_HEADER_SIZE) ||
+ net_write_buff(net, packet, z_size))
+ return 1;
+ packet += z_size;
+ len-= z_size;
+ }
+ /* Write last packet */
int3store(buff,len);
buff[3]= (net->compress) ? 0 : (uchar) (net->pkt_nr++);
if (net_write_buff(net,(char*) buff,NET_HEADER_SIZE))
@@ -228,23 +260,54 @@ my_net_write(NET *net,const char *packet,ulong len)
return net_write_buff(net,packet,len);
}
+/*
+ Send a command to the server.
+ As the command is part of the first data packet, we have to do some data
+ juggling to put the command in there, without having to create a new
+ packet.
+ This function will split big packets into sub-packets if needed.
+ (Each sub packet can only be 2^24 bytes)
+*/
+
int
net_write_command(NET *net,uchar command,const char *packet,ulong len)
{
- uchar buff[NET_HEADER_SIZE+1];
uint length=len+1; /* 1 extra byte for command */
+ uchar buff[NET_HEADER_SIZE+1];
+ uint header_size=NET_HEADER_SIZE+1;
+ buff[4]=command; /* For first packet */
+ if (length >= MAX_THREE_BYTES)
+ {
+ /* Take into account that we have the command in the first header */
+ len= MAX_THREE_BYTES -1;
+ do
+ {
+ int3store(buff, MAX_THREE_BYTES);
+ buff[3]= (net->compress) ? 0 : (uchar) (net->pkt_nr++);
+ if (net_write_buff(net,(char*) buff, header_size) ||
+ net_write_buff(net,packet,len))
+ return 1;
+ packet+= len;
+ length-= MAX_THREE_BYTES;
+ len=MAX_THREE_BYTES;
+ header_size=NET_HEADER_SIZE;
+ } while (length >= MAX_THREE_BYTES);
+ len=length; /* Data left to be written */
+ }
int3store(buff,length);
buff[3]= (net->compress) ? 0 : (uchar) (net->pkt_nr++);
- buff[4]=command;
- if (net_write_buff(net,(char*) buff,5))
- return 1;
- return test(net_write_buff(net,packet,len) || net_flush(net));
+ return test(net_write_buff(net,(char*) buff,header_size) ||
+ net_write_buff(net,packet,len) || net_flush(net));
}
+/*
+ Caching the data in a local buffer before sending it.
+ One can force the buffer to be flushed with 'net_flush'.
+*/
static int
-net_write_buff(NET *net,const char *packet,uint len)
+net_write_buff(NET *net,const char *packet,ulong len)
{
uint left_length=(uint) (net->buff_end - net->write_pos);
@@ -263,7 +326,11 @@ net_write_buff(NET *net,const char *packet,uint len)
return 0;
}
-/* Read and write using timeouts */
+
+/*
+ Read and write one packet using timeouts.
+ If needed, the packet is compressed before sending.
+*/
int
net_real_write(NET *net,const char *packet,ulong len)
@@ -418,7 +485,7 @@ static void my_net_skip_rest(NET *net, ulong remain, thr_alarm_t *alarmed)
{
if (!thr_alarm(alarmed,net->timeout,&alarm_buff) ||
(!vio_is_blocking(net->vio) && vio_blocking(net->vio,TRUE) < 0))
- return; // Can't setup, abort
+ return; /* Can't setup, abort */
}
while (remain > 0)
{
@@ -426,20 +493,26 @@ static void my_net_skip_rest(NET *net, ulong remain, thr_alarm_t *alarmed)
if ((int) (length=vio_read(net->vio,(char*) net->buff,remain)) <= 0L)
{
my_bool interrupted = vio_should_retry(net->vio);
- if (!thr_got_alarm(alarmed) && interrupted)
- { /* Probably in MIT threads */
+ if (!thr_got_alarm(&alarmed) && interrupted)
+ { /* Probably in MIT threads */
if (retry_count++ < RETRY_COUNT)
continue;
}
return;
}
- remain -=(ulong) length;
- statistic_add(bytes_received,(ulong) length,&LOCK_bytes_received);
+ remain -= length;
+ statistic_add(bytes_received,length,&LOCK_bytes_received);
}
}
#endif /* MYSQL_SERVER */
+/*
+ Reads one packet to net->buff + net->where_b
+ Returns length of packet. Long packets are handled by my_net_read().
+ This function reallocates the net->buff buffer if necessary.
+*/
+
static uint
my_real_read(NET *net, ulong *complen)
{
@@ -576,12 +649,13 @@ my_real_read(NET *net, ulong *complen)
#endif
len=uint3korr(net->buff+net->where_b);
+ if (!len) /* End of big multi-packet */
+ goto end;
helping = max(len,*complen) + net->where_b;
/* The necessary size of net->buff */
if (helping >= net->max_packet)
{
- /* We must allocate one extra byte for the end null */
- if (net_realloc(net,helping+1))
+ if (net_realloc(net,helping))
{
#ifdef MYSQL_SERVER
if (i == 1)
@@ -606,7 +680,21 @@ end:
return(len);
}
-uint
+
+/*
+ Read a packet from the client/server and return it without the internal
+ package header.
+ If the packet is the first packet of a multi-packet packet
+ (which is indicated by the length of the packet = 0xffffff) then
+ all sub packets are read and concatenated.
+ If the packet was compressed, its uncompressed and the length of the
+ uncompressed packet is returned.
+
+ The function returns the length of the found packet or packet_error.
+ net->read_pos points to the read data.
+*/
+
+ulong
my_net_read(NET *net)
{
ulong len,complen;
@@ -615,65 +703,128 @@ my_net_read(NET *net)
if (!net->compress)
{
#endif
- len = my_real_read (net,&complen);
+ len = my_real_read(net,&complen);
+ if (len == MAX_THREE_BYTES)
+ {
+ /* First packet of a multi-packet. Concatenate the packets */
+ int save_pos = net->where_b;
+ ulong total_length=0;
+ do
+ {
+ net->where_b += len;
+ total_length += len;
+ len = my_real_read (net,&complen);
+ } while (len == MAX_THREE_BYTES);
+ if (len != packet_error)
+ len+= total_length;
+ net->where_b = save_pos;
+ }
net->read_pos = net->buff + net->where_b;
if (len != packet_error)
net->read_pos[len]=0; /* Safeguard for mysql_use_result */
return len;
#ifdef HAVE_COMPRESS
}
- if (net->remain_in_buf)
- net->buff[net->buf_length - net->remain_in_buf]=net->save_char;
- for (;;)
+ else
{
+ /* We are using the compressed protocol */
+
+ ulong buf_length= net->buf_length;
+ ulong start_of_packet= net->buf_length - net->remain_in_buf;
+ ulong first_packet_offset=start_of_packet;
+ uint read_length, multi_byte_packet=0;
+
if (net->remain_in_buf)
{
- uchar *pos = net->buff + net->buf_length - net->remain_in_buf;
- if (net->remain_in_buf >= 4)
+ /* Restore the character that was overwritten by the end 0 */
+ net->buff[start_of_packet]=net->save_char;
+ }
+ else
+ {
+ /* reuse buffer, as there is noting in it that we need */
+ buf_length=start_of_packet=first_packet_offset=0;
+ }
+ for (;;)
+ {
+ ulong packet_len;
+
+ if (buf_length - start_of_packet >= NET_HEADER_SIZE)
{
- net->length = uint3korr(pos);
- if (net->length <= net->remain_in_buf - 4)
+ read_length = uint3korr(net->buff+start_of_packet);
+ if (!read_length)
+ {
+ /* End of multi-byte packet */
+ start_of_packet += NET_HEADER_SIZE;
+ break;
+ }
+ if (read_length + NET_HEADER_SIZE <= buf_length - start_of_packet)
{
- /* We have a full packet */
- len=net->length;
- net->remain_in_buf -= net->length + 4;
- net->read_pos=pos + 4;
- break; /* We have a full packet */
+ if (multi_byte_packet)
+ {
+ /* Remove packet header for second packet */
+ memmove(net->buff + first_packet_offset + start_of_packet,
+ net->buff + first_packet_offset + start_of_packet +
+ NET_HEADER_SIZE,
+ buf_length - start_of_packet);
+ start_of_packet += read_length;
+ buf_length -= NET_HEADER_SIZE;
+ }
+ else
+ start_of_packet+= read_length + NET_HEADER_SIZE;
+
+ if (read_length != MAX_THREE_BYTES) /* last package */
+ {
+ multi_byte_packet= 0; // No last zero length packet
+ break;
+ }
+ multi_byte_packet= NET_HEADER_SIZE;
+ /* Move data down to read next data packet after current one */
+ if (first_packet_offset)
+ {
+ memmove(net->buff,net->buff+first_packet_offset,
+ buf_length-first_packet_offset);
+ buf_length-=first_packet_offset;
+ start_of_packet -= first_packet_offset;
+ first_packet_offset=0;
+ }
+ continue;
}
}
/* Move data down to read next data packet after current one */
- if (net->buf_length != net->remain_in_buf)
+ if (first_packet_offset)
{
- memmove(net->buff,pos,net->remain_in_buf);
- net->buf_length=net->remain_in_buf;
+ memmove(net->buff,net->buff+first_packet_offset,
+ buf_length-first_packet_offset);
+ buf_length-=first_packet_offset;
+ start_of_packet -= first_packet_offset;
+ first_packet_offset=0;
}
- net->where_b=net->buf_length;
- }
- else
- {
- net->where_b=0;
- net->buf_length=0;
- }
- if ((len = my_real_read(net,&complen)) == packet_error)
- break;
- if (my_uncompress((byte*) net->buff + net->where_b, &len, &complen))
- {
- len= packet_error;
- net->error=2; /* caller will close socket */
+ net->where_b=buf_length;
+ if ((packet_len = my_real_read(net,&complen)) == packet_error)
+ return packet_error;
+ if (my_uncompress((byte*) net->buff + net->where_b, &packet_len,
+ &complen))
+ {
+ net->error=2; /* caller will close socket */
#ifdef MYSQL_SERVER
- net->last_errno=ER_NET_UNCOMPRESS_ERROR;
+ net->last_errno=ER_NET_UNCOMPRESS_ERROR;
#endif
- break;
+ return packet_error;
+ }
+ buf_length+=packet_len;
}
- net->buf_length+=len;
- net->remain_in_buf+=len;
- }
- if (len != packet_error)
- {
+
+ net->read_pos= net->buff+ first_packet_offset + NET_HEADER_SIZE;
+ net->buf_length= buf_length;
+ net->remain_in_buf= buf_length - start_of_packet;
+ len = ((uint) (start_of_packet - first_packet_offset) - NET_HEADER_SIZE -
+ multi_byte_packet);
net->save_char= net->read_pos[len]; /* Must be saved */
net->read_pos[len]=0; /* Safeguard for mysql_use_result */
}
+#endif /* HAVE_COMPRESS */
return len;
-#endif
+}
+
}
diff --git a/sql/opt_range.cc b/sql/opt_range.cc
index b95b97d670f..cd7c8196c4d 100644
--- a/sql/opt_range.cc
+++ b/sql/opt_range.cc
@@ -33,6 +33,7 @@
#include <m_ctype.h>
#include <nisam.h>
#include "sql_select.h"
+#include <assert.h>
#ifndef EXTRA_DEBUG
@@ -289,7 +290,6 @@ typedef struct st_qsel_param {
max_key[MAX_KEY_LENGTH+MAX_FIELD_WIDTH];
} PARAM;
-
static SEL_TREE * get_mm_parts(PARAM *param,Field *field,
Item_func::Functype type,Item *value,
Item_result cmp_type);
@@ -2455,8 +2455,8 @@ int QUICK_SELECT::get_next()
if ((error=file->index_first(record)))
DBUG_RETURN(error); // Empty table
if (cmp_next(range) == 0)
- DBUG_RETURN(0); // No matching records
- range=0; // To next range
+ DBUG_RETURN(0);
+ range=0; // No matching records; go to next range
continue;
}
if ((result = file->index_read(record,(byte*) range->min_key,
@@ -2516,6 +2516,158 @@ int QUICK_SELECT::cmp_next(QUICK_RANGE *range)
return (range->flag & NEAR_MAX) ? 1 : 0; // Exact match
}
+/*
+ * This is a hack: we inherit from QUICK_SELECT so that we can use the
+ * get_next() interface, but we have to hold a pointer to the original
+ * QUICK_SELECT because its data are used all over the place. What
+ * should be done is to factor out the data that is needed into a base
+ * class (QUICK_SELECT), and then have two subclasses (_ASC and _DESC)
+ * which handle the ranges and implement the get_next() function. But
+ * for now, this seems to work right at least.
+ */
+QUICK_SELECT_DESC::QUICK_SELECT_DESC(QUICK_SELECT *q)
+ : QUICK_SELECT(*q), quick(q), rev_it(rev_ranges)
+{
+ bool not_read_after_key = file->option_flag() & HA_NOT_READ_AFTER_KEY;
+ for (QUICK_RANGE *r = it++; r; r = it++)
+ {
+ rev_ranges.push_front(r);
+ if (not_read_after_key && range_reads_after_key(r))
+ {
+ error = HA_ERR_UNSUPPORTED;
+ break;
+ }
+ }
+}
+
+int QUICK_SELECT_DESC::get_next()
+{
+ DBUG_ENTER("QUICK_SELECT_DESC::get_next");
+
+ /* The max key is handled as follows:
+ * - if there is NO_MAX_RANGE, start at the end and move backwards
+ * - if it is an EQ_RANGE and max key covers the entire key, go directly
+ * to the key and read through it (sorting backwards is
+ * same as sorting forwards)
+ * - if it is NEAR_MAX, go to the key or next, step back once, and
+ * move backwards
+ * - otherwise (not NEAR_MAX == include the key), go after the key,
+ * step back once, and move backwards
+ */
+
+ for (;;)
+ {
+ int result;
+ if (range)
+ { // Already read through key
+ result = ((range->flag & EQ_RANGE) ?
+ file->index_next_same(record, (byte*) range->min_key,
+ range->min_length) :
+ file->index_prev(record));
+ if (!result)
+ {
+ if (cmp_prev(*rev_it.ref()) == 0)
+ DBUG_RETURN(0);
+ }
+ else if (result != HA_ERR_END_OF_FILE)
+ DBUG_RETURN(result);
+ }
+
+ if (!(range=rev_it++))
+ DBUG_RETURN(HA_ERR_END_OF_FILE); // All ranges used
+
+ if (range->flag & NO_MAX_RANGE) // Read last record
+ {
+ int error;
+ if ((error=file->index_last(record)))
+ DBUG_RETURN(error); // Empty table
+ if (cmp_prev(range) == 0)
+ DBUG_RETURN(0);
+ range=0; // No matching records; go to next range
+ continue;
+ }
+
+ if (range->flag & EQ_RANGE &&
+ head->key_info[index].key_length == range->max_length)
+ {
+ result = file->index_read(record, (byte*) range->max_key,
+ range->max_length, HA_READ_KEY_EXACT);
+ }
+ else
+ {
+ dbug_assert(range->flag & NEAR_MAX || range_reads_after_key(range));
+ /* Note: even if max_key is only a prefix, HA_READ_AFTER_KEY will
+ * do the right thing - go past all keys which match the prefix */
+ file->index_read(record, (byte*) range->max_key, range->max_length,
+ ((range->flag & NEAR_MAX) ?
+ HA_READ_KEY_OR_PREV : HA_READ_AFTER_KEY));
+ result = file->index_prev(record);
+ }
+ if (result)
+ {
+ if (result != HA_ERR_KEY_NOT_FOUND)
+ DBUG_RETURN(result);
+ range=0; // Not found, to next range
+ continue;
+ }
+ if (cmp_prev(range) == 0)
+ {
+ if (range->flag == (UNIQUE_RANGE | EQ_RANGE))
+ range = 0; // Stop searching
+ DBUG_RETURN(0); // Found key is in range
+ }
+ range = 0; // To next range
+ }
+ DBUG_RETURN(HA_ERR_END_OF_FILE);
+}
+
+/*
+ * Returns 0 if found key is inside range (found key >= range->min_key).
+ */
+int QUICK_SELECT_DESC::cmp_prev(QUICK_RANGE *range)
+{
+ if (range->flag & NO_MIN_RANGE)
+ return (0); /* key can't be to small */
+
+ KEY_PART *key_part = quick->key_parts;
+ for (char *key = range->min_key, *end = key + range->min_length;
+ key < end;
+ key += key_part++->part_length)
+ {
+ int cmp;
+ if (key_part->null_bit)
+ {
+ // this key part allows null values; NULL is lower than everything else
+ if (*key++)
+ {
+ // the range is expecting a null value
+ if (!key_part->field->is_null())
+ return 0; // not null -- still inside the range
+ continue; // null -- exact match, go to next key part
+ }
+ else if (key_part->field->is_null())
+ return 1; // null -- outside the range
+ }
+ if ((cmp = key_part->field->key_cmp((byte*) key,
+ key_part->part_length)) > 0)
+ return 0;
+ if (cmp < 0)
+ return 1;
+ }
+ return (range->flag & NEAR_MIN) ? 1 : 0; // Exact match
+}
+
+/*
+ * True if this range will require using HA_READ_AFTER_KEY
+ */
+bool QUICK_SELECT_DESC::range_reads_after_key(QUICK_RANGE *range)
+{
+ // See comment in get_next()
+ return range->flag & (NO_MAX_RANGE | NEAR_MAX) ? 1 :
+ (range->flag & EQ_RANGE &&
+ head->key_info[index].key_length == range->max_length) ? 0 : 1;
+}
+
/*****************************************************************************
** Print a quick range for debugging
** TODO:
diff --git a/sql/opt_range.h b/sql/opt_range.h
index 247dd260817..0c8dcf7fed3 100644
--- a/sql/opt_range.h
+++ b/sql/opt_range.h
@@ -80,6 +80,20 @@ public:
bool unique_key_range();
};
+class QUICK_SELECT_DESC: public QUICK_SELECT
+{
+public:
+ QUICK_SELECT_DESC(QUICK_SELECT *q);
+ int get_next();
+private:
+ int cmp_prev(QUICK_RANGE *range);
+ bool range_reads_after_key(QUICK_RANGE *range);
+
+ QUICK_SELECT *quick;
+ List<QUICK_RANGE> rev_ranges;
+ List_iterator<QUICK_RANGE> rev_it;
+};
+
class SQL_SELECT :public Sql_alloc {
public:
QUICK_SELECT *quick; // If quick-select used
diff --git a/sql/share/czech/errmsg.txt b/sql/share/czech/errmsg.txt
index 6d35e913ffd..a8d7c187ad3 100644
--- a/sql/share/czech/errmsg.txt
+++ b/sql/share/czech/errmsg.txt
@@ -220,3 +220,6 @@
"Update locks cannot be acquired during a READ UNCOMMITTED transaction",
"DROP DATABASE not allowed while thread is holding global read lock",
"CREATE DATABASE not allowed while thread is holding global read lock",
+"Error connecting to master: %-.128s",
+"Error running query on master: %-.128s",
+"Error in SHOW BINLOG EVENTS: %-.128s",
diff --git a/sql/share/danish/errmsg.txt b/sql/share/danish/errmsg.txt
index d1e0ea71175..57a6ad3d13f 100644
--- a/sql/share/danish/errmsg.txt
+++ b/sql/share/danish/errmsg.txt
@@ -214,3 +214,6 @@
"Update locks cannot be acquired during a READ UNCOMMITTED transaction",
"DROP DATABASE not allowed while thread is holding global read lock",
"CREATE DATABASE not allowed while thread is holding global read lock",
+"Error connecting to master: %-.128s",
+"Error running query on master: %-.128s",
+"Error in SHOW BINLOG EVENTS: %-.128s",
diff --git a/sql/share/dutch/errmsg.txt b/sql/share/dutch/errmsg.txt
index 7ae6c564283..b886ba43f6f 100644
--- a/sql/share/dutch/errmsg.txt
+++ b/sql/share/dutch/errmsg.txt
@@ -211,3 +211,6 @@
"Update locks cannot be acquired during a READ UNCOMMITTED transaction",
"DROP DATABASE not allowed while thread is holding global read lock",
"CREATE DATABASE not allowed while thread is holding global read lock",
+"Error connecting to master: %-.128s",
+"Error running query on master: %-.128s",
+"Error in SHOW BINLOG EVENTS: %-.128s",
diff --git a/sql/share/english/errmsg.txt b/sql/share/english/errmsg.txt
index 2a6e23b6281..f0887f5b376 100644
--- a/sql/share/english/errmsg.txt
+++ b/sql/share/english/errmsg.txt
@@ -153,7 +153,7 @@
"You have an error in your SQL syntax",
"Delayed insert thread couldn't get requested lock for table %-.64s",
"Too many delayed threads in use",
-"Aborted connection %ld to db: '%-.64s' user: '%-.32s' (%-.64s)",
+"Aborted connection %ld to db: '%-.64s' user: '%-.32s' (%-.64s) - see http://www.mysql.com/doc/C/o/Communication_errors.html",
"Got a packet bigger than 'max_allowed_packet'",
"Got a read error from the connection pipe",
"Got an error from fcntl()",
@@ -185,7 +185,7 @@
"Got error %d during ROLLBACK",
"Got error %d during FLUSH_LOGS",
"Got error %d during CHECKPOINT",
-"Aborted connection %ld to db: '%-.64s' user: '%-.32s' host: `%-.64s' (%-.64s)",
+"Aborted connection %ld to db: '%-.64s' user: '%-.32s' host: `%-.64s' (%-.64s) - see http://www.mysql.com/doc/C/o/Communication_errors.html",
"The handler for the table does not support binary table dump",
"Binlog closed, cannot RESET MASTER",
"Failed rebuilding the index of dumped table '%-.64s'",
@@ -211,3 +211,6 @@
"Update locks cannot be acquired during a READ UNCOMMITTED transaction",
"DROP DATABASE not allowed while thread is holding global read lock",
"CREATE DATABASE not allowed while thread is holding global read lock",
+"Error connecting to master: %-.128s",
+"Error running query on master: %-.128s",
+"Error in SHOW BINLOG EVENTS: %-.128s",
diff --git a/sql/share/estonian/errmsg.txt b/sql/share/estonian/errmsg.txt
index 264badebe38..77a7d2f7841 100644
--- a/sql/share/estonian/errmsg.txt
+++ b/sql/share/estonian/errmsg.txt
@@ -177,19 +177,19 @@
"Can't write, because of unique constraint, to table '%-.64s'",
"BLOB column '%-.64s' used in key specification without a key length",
"All parts of a PRIMARY KEY must be NOT NULL; If you need NULL in a key, use UNIQUE instead",
-"Result consisted of more than one row",
+"Tulemis on rohkem kui üks kirje",
"This table type requires a primary key",
"Antud MySQL ei ole kompileeritud RAID-i toega",
"You are using safe update mode and you tried to update a table without a WHERE that uses a KEY column",
"Key '%-.64s' doesn't exist in table '%-.64s'",
-"Can't open table",
-"The handler for the table doesn't support check/repair",
-"You are not allowed to execute this command in a transaction",
-"Got error %d during COMMIT",
-"Got error %d during ROLLBACK",
-"Got error %d during FLUSH_LOGS",
-"Got error %d during CHECKPOINT",
-"Aborted connection %ld to db: '%-.64s' user: '%-.32s' host: `%-.64s' (%-.64s)",
+"Ei suuda tabelit avada",
+"See tabelitüüp ei toeta käske CHECK/REPAIR",
+"Puudub õigus selle transaktsioonikäsu andmiseks",
+"Sain vea %d COMMIT käsu täitmisel",
+"Sain vea %d ROLLBACK käsu täitmisel",
+"Sain vea %d FLUSH_LOGS käsu täitmisel",
+"Sain vea %d CHECKPOINT käsu täitmisel",
+"Ühendus %ld katkestatud andmebaas: '%-.64s' kasutaja: '%-.32s' masin: `%-.64s' (%-.64s)",
"The handler for the table does not support binary table dump",
"Binlog closed while trying to FLUSH MASTER",
"Failed rebuilding the index of dumped table '%-.64s'",
@@ -198,9 +198,9 @@
"Net error writing to master",
"Can't find FULLTEXT index matching the column list",
"Can't execute the given command because you have active locked tables or an active transaction",
-"Unknown system variable '%-.64'",
-"Table '%-.64s' is marked as crashed and should be repaired",
-"Table '%-.64s' is marked as crashed and last (automatic?) repair failed",
+"Tundmatu süsteemne muutja '%-.64'",
+"Tabel '%-.64s' on märgitud vigaseks ja tuleb parandada",
+"Tabel '%-.64s' on märgitud vigaseks ja viimane (automaatne?) parandamiskatse ebaõnnestus",
"Warning: Some non-transactional changed tables couldn't be rolled back",
"Multi-statement transaction required more than 'max_binlog_cache_size' bytes of storage. Increase this mysqld variable and try again',
"This operation cannot be performed with a running slave, run SLAVE STOP first",
@@ -215,3 +215,6 @@
"Update locks cannot be acquired during a READ UNCOMMITTED transaction",
"DROP DATABASE not allowed while thread is holding global read lock",
"CREATE DATABASE not allowed while thread is holding global read lock",
+"Error connecting to master: %-.128s",
+"Error running query on master: %-.128s",
+"Error in SHOW BINLOG EVENTS: %-.128s",
diff --git a/sql/share/french/errmsg.txt b/sql/share/french/errmsg.txt
index 0da5cf94ed8..2e375bd5e15 100644
--- a/sql/share/french/errmsg.txt
+++ b/sql/share/french/errmsg.txt
@@ -211,3 +211,6 @@
"Update locks cannot be acquired during a READ UNCOMMITTED transaction",
"DROP DATABASE not allowed while thread is holding global read lock",
"CREATE DATABASE not allowed while thread is holding global read lock",
+"Error connecting to master: %-.128s",
+"Error running query on master: %-.128s",
+"Error in SHOW BINLOG EVENTS: %-.128s",
diff --git a/sql/share/german/errmsg.txt b/sql/share/german/errmsg.txt
index 9abbb3a8a2f..85289b46967 100644
--- a/sql/share/german/errmsg.txt
+++ b/sql/share/german/errmsg.txt
@@ -214,3 +214,6 @@
"Update locks cannot be acquired during a READ UNCOMMITTED transaction",
"DROP DATABASE not allowed while thread is holding global read lock",
"CREATE DATABASE not allowed while thread is holding global read lock",
+"Error connecting to master: %-.128s",
+"Error running query on master: %-.128s",
+"Error in SHOW BINLOG EVENTS: %-.128s",
diff --git a/sql/share/greek/errmsg.txt b/sql/share/greek/errmsg.txt
index 8f81fcfda31..bdae260f2f8 100644
--- a/sql/share/greek/errmsg.txt
+++ b/sql/share/greek/errmsg.txt
@@ -211,3 +211,6 @@
"Update locks cannot be acquired during a READ UNCOMMITTED transaction",
"DROP DATABASE not allowed while thread is holding global read lock",
"CREATE DATABASE not allowed while thread is holding global read lock",
+"Error connecting to master: %-.128s",
+"Error running query on master: %-.128s",
+"Error in SHOW BINLOG EVENTS: %-.128s",
diff --git a/sql/share/hungarian/errmsg.txt b/sql/share/hungarian/errmsg.txt
index 84d8c56cd04..f2d45b94b50 100644
--- a/sql/share/hungarian/errmsg.txt
+++ b/sql/share/hungarian/errmsg.txt
@@ -213,3 +213,6 @@
"Update locks cannot be acquired during a READ UNCOMMITTED transaction",
"DROP DATABASE not allowed while thread is holding global read lock",
"CREATE DATABASE not allowed while thread is holding global read lock",
+"Error connecting to master: %-.128s",
+"Error running query on master: %-.128s",
+"Error in SHOW BINLOG EVENTS: %-.128s",
diff --git a/sql/share/italian/errmsg.txt b/sql/share/italian/errmsg.txt
index b85dc03286a..a46e712a6e6 100644
--- a/sql/share/italian/errmsg.txt
+++ b/sql/share/italian/errmsg.txt
@@ -211,3 +211,6 @@
"I lock di aggiornamento non possono essere acquisiti durante una transazione 'READ UNCOMMITTED'",
"DROP DATABASE not allowed while thread is holding global read lock",
"CREATE DATABASE not allowed while thread is holding global read lock",
+"Error connecting to master: %-.128s",
+"Error running query on master: %-.128s",
+"Error in SHOW BINLOG EVENTS: %-.128s",
diff --git a/sql/share/japanese/errmsg.txt b/sql/share/japanese/errmsg.txt
index 49e58079588..6d5ab99f86d 100644
--- a/sql/share/japanese/errmsg.txt
+++ b/sql/share/japanese/errmsg.txt
@@ -213,3 +213,6 @@
"Update locks cannot be acquired during a READ UNCOMMITTED transaction",
"DROP DATABASE not allowed while thread is holding global read lock",
"CREATE DATABASE not allowed while thread is holding global read lock",
+"Error connecting to master: %-.128s",
+"Error running query on master: %-.128s",
+"Error in SHOW BINLOG EVENTS: %-.128s",
diff --git a/sql/share/korean/errmsg.txt b/sql/share/korean/errmsg.txt
index 2e278dbd129..5fa44f581bf 100644
--- a/sql/share/korean/errmsg.txt
+++ b/sql/share/korean/errmsg.txt
@@ -211,3 +211,6 @@
"Update locks cannot be acquired during a READ UNCOMMITTED transaction",
"DROP DATABASE not allowed while thread is holding global read lock",
"CREATE DATABASE not allowed while thread is holding global read lock",
+"Error connecting to master: %-.128s",
+"Error running query on master: %-.128s",
+"Error in SHOW BINLOG EVENTS: %-.128s",
diff --git a/sql/share/norwegian-ny/errmsg.txt b/sql/share/norwegian-ny/errmsg.txt
index df9efbd28a4..f45daa00449 100644
--- a/sql/share/norwegian-ny/errmsg.txt
+++ b/sql/share/norwegian-ny/errmsg.txt
@@ -213,3 +213,6 @@
"Update locks cannot be acquired during a READ UNCOMMITTED transaction",
"DROP DATABASE not allowed while thread is holding global read lock",
"CREATE DATABASE not allowed while thread is holding global read lock",
+"Error connecting to master: %-.128s",
+"Error running query on master: %-.128s",
+"Error in SHOW BINLOG EVENTS: %-.128s",
diff --git a/sql/share/norwegian/errmsg.txt b/sql/share/norwegian/errmsg.txt
index c95669aa016..951631cae75 100644
--- a/sql/share/norwegian/errmsg.txt
+++ b/sql/share/norwegian/errmsg.txt
@@ -213,3 +213,6 @@
"Update locks cannot be acquired during a READ UNCOMMITTED transaction",
"DROP DATABASE not allowed while thread is holding global read lock",
"CREATE DATABASE not allowed while thread is holding global read lock",
+"Error connecting to master: %-.128s",
+"Error running query on master: %-.128s",
+"Error in SHOW BINLOG EVENTS: %-.128s",
diff --git a/sql/share/polish/errmsg.txt b/sql/share/polish/errmsg.txt
index d708bc6fffb..79b420022bf 100644
--- a/sql/share/polish/errmsg.txt
+++ b/sql/share/polish/errmsg.txt
@@ -215,3 +215,6 @@
"Update locks cannot be acquired during a READ UNCOMMITTED transaction",
"DROP DATABASE not allowed while thread is holding global read lock",
"CREATE DATABASE not allowed while thread is holding global read lock",
+"Error connecting to master: %-.128s",
+"Error running query on master: %-.128s",
+"Error in SHOW BINLOG EVENTS: %-.128s",
diff --git a/sql/share/portuguese/errmsg.txt b/sql/share/portuguese/errmsg.txt
index b1cab63c0a0..cd3e948546e 100644
--- a/sql/share/portuguese/errmsg.txt
+++ b/sql/share/portuguese/errmsg.txt
@@ -211,3 +211,6 @@
"Update locks cannot be acquired during a READ UNCOMMITTED transaction",
"DROP DATABASE not allowed while thread is holding global read lock",
"CREATE DATABASE not allowed while thread is holding global read lock",
+"Error connecting to master: %-.128s",
+"Error running query on master: %-.128s",
+"Error in SHOW BINLOG EVENTS: %-.128s",
diff --git a/sql/share/romanian/errmsg.txt b/sql/share/romanian/errmsg.txt
index 8069f9907bb..05362606c44 100644
--- a/sql/share/romanian/errmsg.txt
+++ b/sql/share/romanian/errmsg.txt
@@ -215,3 +215,6 @@
"Update locks cannot be acquired during a READ UNCOMMITTED transaction",
"DROP DATABASE not allowed while thread is holding global read lock",
"CREATE DATABASE not allowed while thread is holding global read lock",
+"Error connecting to master: %-.128s",
+"Error running query on master: %-.128s",
+"Error in SHOW BINLOG EVENTS: %-.128s",
diff --git a/sql/share/russian/errmsg.txt b/sql/share/russian/errmsg.txt
index 6bc845d5599..64adb134c34 100644
--- a/sql/share/russian/errmsg.txt
+++ b/sql/share/russian/errmsg.txt
@@ -214,3 +214,6 @@
"Update locks cannot be acquired during a READ UNCOMMITTED transaction",
"DROP DATABASE not allowed while thread is holding global read lock",
"CREATE DATABASE not allowed while thread is holding global read lock",
+"Error connecting to master: %-.128s",
+"Error running query on master: %-.128s",
+"Error in SHOW BINLOG EVENTS: %-.128s",
diff --git a/sql/share/slovak/errmsg.txt b/sql/share/slovak/errmsg.txt
index 8631ee6bdeb..f951e8f9435 100644
--- a/sql/share/slovak/errmsg.txt
+++ b/sql/share/slovak/errmsg.txt
@@ -219,3 +219,6 @@
"Update locks cannot be acquired during a READ UNCOMMITTED transaction",
"DROP DATABASE not allowed while thread is holding global read lock",
"CREATE DATABASE not allowed while thread is holding global read lock",
+"Error connecting to master: %-.128s",
+"Error running query on master: %-.128s",
+"Error in SHOW BINLOG EVENTS: %-.128s",
diff --git a/sql/share/spanish/errmsg.txt b/sql/share/spanish/errmsg.txt
index ea97a282c83..3088c9b4ee1 100644
--- a/sql/share/spanish/errmsg.txt
+++ b/sql/share/spanish/errmsg.txt
@@ -212,3 +212,6 @@
"Update locks cannot be acquired during a READ UNCOMMITTED transaction",
"DROP DATABASE not allowed while thread is holding global read lock",
"CREATE DATABASE not allowed while thread is holding global read lock",
+"Error connecting to master: %-.128s",
+"Error running query on master: %-.128s",
+"Error in SHOW BINLOG EVENTS: %-.128s",
diff --git a/sql/share/swedish/errmsg.OLD b/sql/share/swedish/errmsg.OLD
index fc26a08e9ee..227a02ac873 100644
--- a/sql/share/swedish/errmsg.OLD
+++ b/sql/share/swedish/errmsg.OLD
@@ -206,6 +206,8 @@
"Kunde inte starta en tråd för replikering",
"Användare '%-.64s' har redan 'max_user_connections' aktiva inloggningar",
"Du kan endast använda konstant-uttryck med SET",
-"Lock wait timeout exceeded",
-"The total number of locks exceeds the lock table size",
-"Update locks cannot be acquired during a READ UNCOMMITTED transaction",
+"Tiden att få ett lås var för lång",
+"Antal lås är större än vad som ryms i lock tabellen",
+"Du kan inte låsa tabeller/poster under READ UNCOMMITTED",
+"Fick fel vid inloggning till master: %-.128s",
+"Fick fel vid exekvering av fråga på master: %-.128s",
diff --git a/sql/share/swedish/errmsg.txt b/sql/share/swedish/errmsg.txt
index 7f43afd04b6..b8ee9e62b03 100644
--- a/sql/share/swedish/errmsg.txt
+++ b/sql/share/swedish/errmsg.txt
@@ -211,3 +211,6 @@
"Update locks cannot be acquired during a READ UNCOMMITTED transaction",
"DROP DATABASE not allowed while thread is holding global read lock",
"CREATE DATABASE not allowed while thread is holding global read lock",
+"Error connecting to master: %-.128s",
+"Error running query on master: %-.128s",
+"Error in SHOW BINLOG EVENTS: %-.128s",
diff --git a/sql/slave.cc b/sql/slave.cc
index a768e8494a0..8ce6b59dd5b 100644
--- a/sql/slave.cc
+++ b/sql/slave.cc
@@ -20,6 +20,7 @@
#include <myisam.h>
#include "mini_client.h"
#include "slave.h"
+#include "sql_repl.h"
#include <thr_alarm.h>
#include <my_dir.h>
@@ -55,7 +56,7 @@ static int init_slave_thread(THD* thd);
static int safe_connect(THD* thd, MYSQL* mysql, MASTER_INFO* mi);
static int safe_reconnect(THD* thd, MYSQL* mysql, MASTER_INFO* mi);
static int safe_sleep(THD* thd, int sec);
-static int request_table_dump(MYSQL* mysql, char* db, char* table);
+static int request_table_dump(MYSQL* mysql, const char* db, const char* table);
static int create_table_from_dump(THD* thd, NET* net, const char* db,
const char* table_name);
inline char* rewrite_db(char* db);
@@ -347,7 +348,7 @@ static int create_table_from_dump(THD* thd, NET* net, const char* db,
thd->proc_info = "Creating table from master dump";
// save old db in case we are creating in a different database
char* save_db = thd->db;
- thd->db = thd->last_nx_db;
+ thd->db = (char*)db;
mysql_parse(thd, thd->query, packet_len); // run create table
thd->db = save_db; // leave things the way the were before
@@ -396,32 +397,42 @@ err:
return error;
}
-int fetch_nx_table(THD* thd, MASTER_INFO* mi)
+int fetch_nx_table(THD* thd, const char* db_name, const char* table_name,
+ MASTER_INFO* mi, MYSQL* mysql)
{
- MYSQL* mysql = mc_mysql_init(NULL);
int error = 1;
int nx_errno = 0;
- if (!mysql)
- {
+ bool called_connected = (mysql != NULL);
+ if (!called_connected && !(mysql = mc_mysql_init(NULL)))
+ {
sql_print_error("fetch_nx_table: Error in mysql_init()");
nx_errno = ER_GET_ERRNO;
goto err;
}
- safe_connect(thd, mysql, mi);
+ if (!called_connected)
+ {
+ if (connect_to_master(thd, mysql, mi))
+ {
+ sql_print_error("Could not connect to master while fetching table\
+ '%-64s.%-64s'", db_name, table_name);
+ nx_errno = ER_CONNECT_TO_MASTER;
+ goto err;
+ }
+ }
if (slave_killed(thd))
goto err;
- if (request_table_dump(mysql, thd->last_nx_db, thd->last_nx_table))
+ if (request_table_dump(mysql, db_name, table_name))
{
nx_errno = ER_GET_ERRNO;
sql_print_error("fetch_nx_table: failed on table dump request ");
goto err;
}
- if (create_table_from_dump(thd, &mysql->net, thd->last_nx_db,
- thd->last_nx_table))
- {
+ if (create_table_from_dump(thd, &mysql->net, db_name,
+ table_name))
+ {
// create_table_from_dump will have sent the error alread
sql_print_error("fetch_nx_table: failed on create table ");
goto err;
@@ -430,7 +441,7 @@ int fetch_nx_table(THD* thd, MASTER_INFO* mi)
error = 0;
err:
- if (mysql)
+ if (mysql && !called_connected)
mc_mysql_close(mysql);
if (nx_errno && thd->net.vio)
send_error(&thd->net, nx_errno, "Error in fetch_nx_table");
@@ -457,7 +468,7 @@ int init_master_info(MASTER_INFO* mi)
MY_STAT stat_area;
char fname[FN_REFLEN+128];
const char *msg;
- fn_format(fname, master_info_file, mysql_data_home, "", 4+16+32);
+ fn_format(fname, master_info_file, mysql_data_home, "", 4+32);
// we need a mutex while we are changing master info parameters to
// keep other threads from reading bogus info
@@ -534,7 +545,9 @@ int init_master_info(MASTER_INFO* mi)
master_password) ||
init_intvar_from_file((int*)&mi->port, &mi->file, master_port) ||
init_intvar_from_file((int*)&mi->connect_retry, &mi->file,
- master_connect_retry))
+ master_connect_retry) ||
+ init_intvar_from_file((int*)&mi->last_log_seq, &mi->file, 0)
+ )
{
msg="Error reading master configuration";
goto error;
@@ -557,6 +570,44 @@ error:
return 1;
}
+int register_slave_on_master(MYSQL* mysql)
+{
+ String packet;
+ uint len;
+ char buf[4];
+
+ if(!report_host)
+ return 0;
+
+ int4store(buf, server_id);
+ packet.append(buf, 4);
+
+ net_store_data(&packet, report_host);
+ if(report_user)
+ net_store_data(&packet, report_user);
+ else
+ packet.append((char)0);
+
+ if(report_password)
+ net_store_data(&packet, report_user);
+ else
+ packet.append((char)0);
+
+ int2store(buf, (uint16)report_port);
+ packet.append(buf, 2);
+
+ if(mc_simple_command(mysql, COM_REGISTER_SLAVE, (char*)packet.ptr(),
+ packet.length(), 0))
+ {
+ sql_print_error("Error on COM_REGISTER_SLAVE: '%s'",
+ mc_mysql_error(mysql));
+ return 1;
+ }
+
+ return 0;
+}
+
+
int show_master_info(THD* thd)
{
DBUG_ENTER("show_master_info");
@@ -576,10 +627,12 @@ int show_master_info(THD* thd)
field_list.push_back(new Item_empty_string("Last_errno", 4));
field_list.push_back(new Item_empty_string("Last_error", 20));
field_list.push_back(new Item_empty_string("Skip_counter", 12));
+ field_list.push_back(new Item_empty_string("Last_log_seq", 12));
if(send_fields(thd, field_list, 1))
DBUG_RETURN(-1);
String* packet = &thd->packet;
+ uint32 last_log_seq;
packet->length(0);
pthread_mutex_lock(&glob_mi.lock);
@@ -588,7 +641,8 @@ int show_master_info(THD* thd)
net_store_data(packet, (uint32) glob_mi.port);
net_store_data(packet, (uint32) glob_mi.connect_retry);
net_store_data(packet, glob_mi.log_file_name);
- net_store_data(packet, (uint32) glob_mi.pos); // QQ: Should be fixed
+ net_store_data(packet, (longlong) glob_mi.pos);
+ last_log_seq = glob_mi.last_log_seq;
pthread_mutex_unlock(&glob_mi.lock);
pthread_mutex_lock(&LOCK_slave);
net_store_data(packet, slave_running ? "Yes":"No");
@@ -598,6 +652,7 @@ int show_master_info(THD* thd)
net_store_data(packet, (uint32)last_slave_errno);
net_store_data(packet, last_slave_error);
net_store_data(packet, slave_skip_counter);
+ net_store_data(packet, last_log_seq);
if (my_net_write(&thd->net, (char*)thd->packet.ptr(), packet->length()))
DBUG_RETURN(-1);
@@ -610,11 +665,13 @@ int flush_master_info(MASTER_INFO* mi)
{
IO_CACHE* file = &mi->file;
char lbuf[22];
+ char lbuf1[22];
my_b_seek(file, 0L);
- my_b_printf(file, "%s\n%s\n%s\n%s\n%s\n%d\n%d\n",
+ my_b_printf(file, "%s\n%s\n%s\n%s\n%s\n%d\n%d\n%d\n",
mi->log_file_name, llstr(mi->pos, lbuf), mi->host, mi->user,
- mi->password, mi->port, mi->connect_retry);
+ mi->password, mi->port, mi->connect_retry,
+ llstr(mi->last_log_seq, lbuf1));
flush_io_cache(file);
return 0;
}
@@ -761,7 +818,7 @@ static int request_dump(MYSQL* mysql, MASTER_INFO* mi)
return 0;
}
-static int request_table_dump(MYSQL* mysql, char* db, char* table)
+static int request_table_dump(MYSQL* mysql, const char* db, const char* table)
{
char buf[1024];
char * p = buf;
@@ -879,7 +936,10 @@ static int exec_event(THD* thd, NET* net, MASTER_INFO* mi, int event_len)
thd->server_id = ev->server_id; // use the original server id for logging
thd->set_time(); // time the query
- if(!ev->when)
+ if(!thd->log_seq)
+ thd->log_seq = ev->log_seq;
+
+ if (!ev->when)
ev->when = time(NULL);
switch(type_code) {
@@ -898,7 +958,6 @@ static int exec_event(THD* thd, NET* net, MASTER_INFO* mi, int event_len)
VOID(pthread_mutex_lock(&LOCK_thread_count));
thd->query_id = query_id++;
VOID(pthread_mutex_unlock(&LOCK_thread_count));
- thd->last_nx_table = thd->last_nx_db = 0;
thd->query_error = 0; // clear error
thd->net.last_errno = 0;
thd->net.last_error[0] = 0;
@@ -960,8 +1019,25 @@ static int exec_event(THD* thd, NET* net, MASTER_INFO* mi, int event_len)
return 1;
}
free_root(&thd->mem_root,0);
+ mi->last_log_seq = ev->log_seq;
delete ev;
+ thd->log_seq = 0;
+ mi->inc_pos(event_len);
+ flush_master_info(mi);
+ break;
+ }
+ case SLAVE_EVENT:
+ {
+ if(mysql_bin_log.is_open())
+ {
+ Slave_log_event *sev = (Slave_log_event*)ev;
+ mysql_bin_log.write(sev);
+ }
+
+ mi->last_log_seq = ev->log_seq;
+ delete ev;
+ thd->log_seq = 0;
mi->inc_pos(event_len);
flush_master_info(mi);
break;
@@ -1074,7 +1150,9 @@ static int exec_event(THD* thd, NET* net, MASTER_INFO* mi, int event_len)
return 1;
}
+ mi->last_log_seq = ev->log_seq;
delete ev;
+ thd->log_seq = 0;
free_root(&thd->mem_root,0);
if(thd->fatal_error)
@@ -1092,8 +1170,10 @@ static int exec_event(THD* thd, NET* net, MASTER_INFO* mi, int event_len)
case START_EVENT:
close_temporary_tables(thd);
mi->inc_pos(event_len);
+ mi->last_log_seq = ev->log_seq;
flush_master_info(mi);
delete ev;
+ thd->log_seq = 0;
break;
case STOP_EVENT:
@@ -1103,24 +1183,56 @@ static int exec_event(THD* thd, NET* net, MASTER_INFO* mi, int event_len)
mi->inc_pos(event_len);
flush_master_info(mi);
}
+ mi->last_log_seq = ev->log_seq;
delete ev;
+ thd->log_seq = 0;
break;
case ROTATE_EVENT:
{
Rotate_log_event* rev = (Rotate_log_event*)ev;
int ident_len = rev->ident_len;
+ bool rotate_binlog = 0, write_slave_event = 0;
+ char* log_name = mi->log_file_name;
pthread_mutex_lock(&mi->lock);
- memcpy(mi->log_file_name, rev->new_log_ident,ident_len );
- mi->log_file_name[ident_len] = 0;
- mi->pos = 4; // skip magic number
+
+ // rotate local binlog only if the name of remote has changed
+ if (!*log_name || !(log_name[ident_len] == 0 &&
+ !memcmp(log_name, rev->new_log_ident, ident_len)))
+ {
+ write_slave_event = (!(rev->flags & LOG_EVENT_FORCED_ROTATE_F)
+ && mysql_bin_log.is_open());
+ rotate_binlog = (*log_name && write_slave_event);
+ memcpy(log_name, rev->new_log_ident,ident_len );
+ log_name[ident_len] = 0;
+ }
+ mi->pos = rev->pos;
+ mi->last_log_seq = ev->log_seq;
pthread_cond_broadcast(&mi->cond);
pthread_mutex_unlock(&mi->lock);
- flush_master_info(mi);
#ifndef DBUG_OFF
- if(abort_slave_event_count)
+ if (abort_slave_event_count)
++events_till_abort;
-#endif
+#endif
+ if (rotate_binlog)
+ {
+ mi->last_log_seq = 0;
+ mysql_bin_log.new_file();
+ }
+ flush_master_info(mi);
+
+ if (write_slave_event)
+ {
+ Slave_log_event s(thd, mi);
+ if (s.master_host)
+ {
+ s.set_log_seq(0, &mysql_bin_log);
+ s.server_id = ::server_id;
+ mysql_bin_log.write(&s);
+ }
+ }
+
delete ev;
+ thd->log_seq = 0;
break;
}
@@ -1140,6 +1252,7 @@ static int exec_event(THD* thd, NET* net, MASTER_INFO* mi, int event_len)
}
mi->inc_pending(event_len);
delete ev;
+ // do not reset log_seq
break;
}
}
@@ -1235,6 +1348,12 @@ pthread_handler_decl(handle_slave,arg __attribute__((unused)))
sql_print_error("Slave thread killed while connecting to master");
goto err;
}
+
+ // register ourselves with the master
+ // if fails, this is not fatal - we just print the error message and go
+ // on with life
+ thd->proc_info = "Registering slave on master";
+ register_slave_on_master(mysql);
while (!slave_killed(thd))
{
diff --git a/sql/slave.h b/sql/slave.h
index 311368a4b82..d9131bb53be 100644
--- a/sql/slave.h
+++ b/sql/slave.h
@@ -1,6 +1,8 @@
#ifndef SLAVE_H
#define SLAVE_H
+#include "mysql.h"
+
typedef struct st_master_info
{
char log_file_name[FN_REFLEN];
@@ -13,11 +15,12 @@ typedef struct st_master_info
char password[HASH_PASSWORD_LENGTH+1];
uint port;
uint connect_retry;
+ uint32 last_log_seq; // log sequence number of last processed event
pthread_mutex_t lock;
pthread_cond_t cond;
bool inited;
- st_master_info():pending(0),fd(-1),inited(0)
+ st_master_info():pending(0),fd(-1),last_log_seq(0),inited(0)
{
host[0] = 0; user[0] = 0; password[0] = 0;
pthread_mutex_init(&lock, MY_MUTEX_INIT_FAST);
@@ -64,12 +67,16 @@ typedef struct st_table_rule_ent
#define TABLE_RULE_ARR_SIZE 16
int flush_master_info(MASTER_INFO* mi);
+int register_slave_on_master(MYSQL* mysql);
-int mysql_table_dump(THD* thd, char* db, char* tbl_name, int fd = -1);
+int mysql_table_dump(THD* thd, const char* db,
+ const char* tbl_name, int fd = -1);
// if fd is -1, dump to NET
-int fetch_nx_table(THD* thd, MASTER_INFO* mi);
+
+int fetch_nx_table(THD* thd, const char* db_name, const char* table_name,
+ MASTER_INFO* mi, MYSQL* mysql);
// retrieve non-exitent table from master
-// the caller must set thd->last_nx_table and thd->last_nx_db first
+
int show_master_info(THD* thd);
int show_binlog_info(THD* thd);
@@ -112,9 +119,9 @@ extern int disconnect_slave_event_count, abort_slave_event_count ;
#endif
// the master variables are defaults read from my.cnf or command line
-extern uint master_port, master_connect_retry;
+extern uint master_port, master_connect_retry, report_port;
extern my_string master_user, master_password, master_host,
- master_info_file;
+ master_info_file, report_user, report_host, report_password;
extern I_List<i_string> replicate_do_db, replicate_ignore_db;
extern I_List<i_string_pair> replicate_rewrite_db;
diff --git a/sql/sql_analyse.cc b/sql/sql_analyse.cc
index de367e8c052..3cc53f1ef49 100644
--- a/sql/sql_analyse.cc
+++ b/sql/sql_analyse.cc
@@ -38,6 +38,37 @@
#define UINT_MAX24 0xffffff
#define UINT_MAX32 0xffffffff
+int sortcmp2(void* cmp_arg __attribute__((unused)),
+ const String *a,const String *b)
+{
+ return sortcmp(a,b);
+}
+
+int stringcmp2(void* cmp_arg __attribute__((unused)),
+ const String *a,const String *b)
+{
+ return stringcmp(a,b);
+}
+
+int compare_double2(void* cmp_arg __attribute__((unused)),
+ const double *s, const double *t)
+{
+ return compare_double(s,t);
+}
+
+int compare_longlong2(void* cmp_arg __attribute__((unused)),
+ const longlong *s, const longlong *t)
+{
+ return compare_longlong(s,t);
+}
+
+int compare_ulonglong2(void* cmp_arg __attribute__((unused)),
+ const ulonglong *s, const ulonglong *t)
+{
+ return compare_ulonglong(s,t);
+}
+
+
Procedure *
proc_analyse_init(THD *thd, ORDER *param, select_result *result,
List<Item> &field_list)
diff --git a/sql/sql_analyse.h b/sql/sql_analyse.h
index ce5c0af6a96..1d26b34dad9 100644
--- a/sql/sql_analyse.h
+++ b/sql/sql_analyse.h
@@ -21,8 +21,6 @@
#pragma interface /* gcc class implementation */
#endif
-#include <my_tree.h>
-
#define DEC_IN_AVG 4
typedef struct st_number_info
@@ -53,8 +51,14 @@ uint check_ulonglong(const char *str, uint length);
bool get_ev_num_info(EV_NUM_INFO *ev_info, NUM_INFO *info, const char *num);
bool test_if_number(NUM_INFO *info, const char *str, uint str_len);
int compare_double(const double *s, const double *t);
+int compare_double2(void* cmp_arg __attribute__((unused)),
+ const double *s, const double *t);
int compare_longlong(const longlong *s, const longlong *t);
+int compare_longlong2(void* cmp_arg __attribute__((unused)),
+ const longlong *s, const longlong *t);
int compare_ulonglong(const ulonglong *s, const ulonglong *t);
+int compare_ulonglong2(void* cmp_arg __attribute__((unused)),
+ const ulonglong *s, const ulonglong *t);
Procedure *proc_analyse_init(THD *thd, ORDER *param, select_result *result,
List<Item> &field_list);
void free_string(String*);
@@ -91,6 +95,11 @@ public:
int collect_string(String *element, element_count count,
TREE_INFO *info);
+int sortcmp2(void* cmp_arg __attribute__((unused)),
+ const String *a,const String *b);
+int stringcmp2(void* cmp_arg __attribute__((unused)),
+ const String *a,const String *b);
+
class field_str :public field_info
{
String min_arg, max_arg;
@@ -106,7 +115,7 @@ public:
must_be_blob(0), was_zero_fill(0),
was_maybe_zerofill(0), can_be_still_num(1)
{ init_tree(&tree, 0, sizeof(String), a->binary ?
- (qsort_cmp) stringcmp : (qsort_cmp) sortcmp,
+ (qsort_cmp2) stringcmp2 : (qsort_cmp2) sortcmp2,
0, (void (*)(void*)) free_string); };
void add();
@@ -146,7 +155,7 @@ public:
field_real(Item* a, analyse* b) :field_info(a,b),
min_arg(0), max_arg(0), sum(0), sum_sqr(0), max_notzero_dec_len(0)
{ init_tree(&tree, 0, sizeof(double),
- (qsort_cmp) compare_double, 0, NULL); }
+ (qsort_cmp2) compare_double2, 0, NULL); }
void add();
void get_opt_type(String*, ha_rows);
@@ -192,7 +201,7 @@ public:
field_longlong(Item* a, analyse* b) :field_info(a,b),
min_arg(0), max_arg(0), sum(0), sum_sqr(0)
{ init_tree(&tree, 0, sizeof(longlong),
- (qsort_cmp) compare_longlong, 0, NULL); }
+ (qsort_cmp2) compare_longlong2, 0, NULL); }
void add();
void get_opt_type(String*, ha_rows);
@@ -237,7 +246,7 @@ public:
field_ulonglong(Item* a, analyse * b) :field_info(a,b),
min_arg(0), max_arg(0), sum(0),sum_sqr(0)
{ init_tree(&tree, 0, sizeof(ulonglong),
- (qsort_cmp) compare_ulonglong, 0, NULL); }
+ (qsort_cmp2) compare_ulonglong2, 0, NULL); }
void add();
void get_opt_type(String*, ha_rows);
String *get_min_arg(String *s) { s->set(min_arg); return s; }
diff --git a/sql/sql_base.cc b/sql/sql_base.cc
index 177df8f1e81..ea3d77c5158 100644
--- a/sql/sql_base.cc
+++ b/sql/sql_base.cc
@@ -34,8 +34,6 @@ HASH open_cache; /* Used by mysql_test */
static int open_unireg_entry(THD *thd,TABLE *entry,const char *db,
const char *name, const char *alias, bool locked);
-static bool insert_fields(THD *thd,TABLE_LIST *tables, const char *db_name,
- const char *table_name, List_iterator<Item> *it);
static void free_cache_entry(TABLE *entry);
static void mysql_rm_tmp_tables(void);
static key_map get_key_map_from_key_list(TABLE *table,
@@ -111,74 +109,71 @@ static void check_unused(void)
#define check_unused()
#endif
-int list_open_tables(THD *thd,List<char> *tables, const char *db,
- const char *wild)
+OPEN_TABLE_LIST *list_open_tables(THD *thd, const char *wild)
{
int result = 0;
uint col_access=thd->col_access;
+ OPEN_TABLE_LIST **start_list, *open_list;
TABLE_LIST table_list;
+ char name[NAME_LEN*2];
DBUG_ENTER("list_open_tables");
+
VOID(pthread_mutex_lock(&LOCK_open));
bzero((char*) &table_list,sizeof(table_list));
+ start_list= &open_list;
+ open_list=0;
for (uint idx=0 ; result == 0 && idx < open_cache.records; idx++)
{
+ OPEN_TABLE_LIST *table;
TABLE *entry=(TABLE*) hash_element(&open_cache,idx);
- if ((!entry->real_name) || strcmp(entry->table_cache_key,db))
- continue;
- if (wild && wild[0] && wild_compare(entry->real_name,wild))
- continue;
- if (db && !(col_access & TABLE_ACLS))
+
+ if ((!entry->real_name))
+ continue; // Shouldn't happen
+ if (wild)
{
- table_list.db= (char*) db;
- table_list.real_name= entry->real_name;/*real name*/
- table_list.grant.privilege=col_access;
- if (check_grant(thd,TABLE_ACLS,&table_list,1))
- continue;
+ strxmov(name,entry->table_cache_key,".",entry->real_name,NullS);
+ if (wild_compare(name,wild))
+ continue;
}
- /* need to check if he have't already listed it */
- List_iterator<char> it(*tables);
- char *table_name;
- int check = 0;
- while (check == 0 && (table_name=it++))
+ /* Check if user has SELECT privilege for any column in the table */
+ table_list.db= (char*) entry->table_cache_key;
+ table_list.real_name= entry->real_name;
+ table_list.grant.privilege=0;
+ if (check_table_access(thd,SELECT_ACL | EXTRA_ACL,&table_list))
+ continue;
+
+ /* need to check if we haven't already listed it */
+ for (table= open_list ; table ; table=table->next)
{
- if (!strcmp(table_name,entry->real_name))
- check++;
+ if (!strcmp(table->table,entry->real_name) &&
+ !strcmp(table->db,entry->table_cache_key))
+ {
+ if (entry->in_use)
+ table->in_use++;
+ if (entry->locked_by_name)
+ table->locked++;
+ break;
+ }
}
- if (check)
+ if (table)
continue;
-
- if (tables->push_back(thd->strdup(entry->real_name)))
+ if (!(*start_list = (OPEN_TABLE_LIST *)
+ sql_alloc(sizeof(OPEN_TABLE_LIST)+entry->key_length)))
{
- result = -1;
+ open_list=0; // Out of memory
+ break;
}
+ (*start_list)->table=(strmov((*start_list)->db=(char*) ((*start_list)+1),
+ entry->table_cache_key)+1,
+ entry->real_name);
+ (*start_list)->in_use= entry->in_use ? 1 : 0;
+ (*start_list)->locked= entry->locked_by_name ? 1 : 0;
+ start_list= &(*start_list)->next;
}
-
VOID(pthread_mutex_unlock(&LOCK_open));
- DBUG_RETURN(result);
-}
-
-char*
-query_table_status(THD *thd,const char *db,const char *table_name)
-{
- int cached = 0, in_use = 0;
- char info[256];
-
- for (uint idx=0 ; idx < open_cache.records; idx++)
- {
- TABLE *entry=(TABLE*) hash_element(&open_cache,idx);
- if (strcmp(entry->table_cache_key,db) ||
- strcmp(entry->real_name,table_name))
- continue;
-
- cached++;
- if (entry->in_use)
- in_use++;
- }
-
- sprintf(info, "cached=%d, in_use=%d", cached, in_use);
- return thd->strdup(info);
+ DBUG_RETURN(open_list);
}
@@ -258,7 +253,7 @@ send_fields(THD *thd,List<Item> &list,uint flag)
if (my_net_write(&thd->net, (char*) packet->ptr(),packet->length()))
break; /* purecov: inspected */
}
- send_eof(&thd->net,(test_flags & TEST_MIT_THREAD) ? 0: 1);
+ send_eof(&thd->net);
return 0;
err:
send_error(&thd->net,ER_OUT_OF_RESOURCES); /* purecov: inspected */
@@ -429,40 +424,9 @@ void close_thread_tables(THD *thd, bool locked)
DBUG_PRINT("info", ("thd->open_tables=%p", thd->open_tables));
- for (table=thd->open_tables ; table ; table=next)
- {
- next=table->next;
- if (table->version != refresh_version ||
- thd->version != refresh_version || !table->db_stat)
- {
- VOID(hash_delete(&open_cache,(byte*) table));
- found_old_table=1;
- }
- else
- {
- if (table->flush_version != flush_version)
- {
- table->flush_version=flush_version;
- table->file->extra(HA_EXTRA_FLUSH);
- }
- else
- {
- // Free memory and reset for next loop
- table->file->extra(HA_EXTRA_RESET);
- }
- table->in_use=0;
- if (unused_tables)
- {
- table->next=unused_tables; /* Link in last */
- table->prev=unused_tables->prev;
- unused_tables->prev=table;
- table->prev->next=table;
- }
- else
- unused_tables=table->next=table->prev=table;
- }
- }
- thd->open_tables=0;
+ while (thd->open_tables)
+ found_old_table|=close_thread_table(thd, &thd->open_tables);
+
/* Free tables to hold down open files */
while (open_cache.records > table_cache_size && unused_tables)
VOID(hash_delete(&open_cache,(byte*) unused_tables)); /* purecov: tested */
@@ -478,6 +442,48 @@ void close_thread_tables(THD *thd, bool locked)
DBUG_VOID_RETURN;
}
+/* move one table to free list */
+
+bool close_thread_table(THD *thd, TABLE **table_ptr)
+{
+ DBUG_ENTER("close_thread_table");
+
+ bool found_old_table=0;
+ TABLE *table=*table_ptr;
+
+ *table_ptr=table->next;
+ if (table->version != refresh_version ||
+ thd->version != refresh_version || !table->db_stat)
+ {
+ VOID(hash_delete(&open_cache,(byte*) table));
+ found_old_table=1;
+ }
+ else
+ {
+ if (table->flush_version != flush_version)
+ {
+ table->flush_version=flush_version;
+ table->file->extra(HA_EXTRA_FLUSH);
+ }
+ else
+ {
+ // Free memory and reset for next loop
+ table->file->extra(HA_EXTRA_RESET);
+ }
+ table->in_use=0;
+ if (unused_tables)
+ {
+ table->next=unused_tables; /* Link in last */
+ table->prev=unused_tables->prev;
+ unused_tables->prev=table;
+ table->prev->next=table;
+ }
+ else
+ unused_tables=table->next=table->prev=table;
+ }
+ DBUG_RETURN(found_old_table);
+}
+
/* Close and delete temporary tables */
void close_temporary(TABLE *table,bool delete_table)
@@ -835,25 +841,6 @@ TABLE *open_table(THD *thd,const char *db,const char *table_name,
!(table->table_cache_key=memdup_root(&table->mem_root,(char*) key,
key_length)))
{
- MEM_ROOT* glob_alloc;
- LINT_INIT(glob_alloc);
-
- if (errno == ENOENT &&
- (glob_alloc = my_pthread_getspecific_ptr(MEM_ROOT*,THR_MALLOC)))
- // Sasha: needed for replication
- // remember the name of the non-existent table
- // so we can try to download it from the master
- {
- int table_name_len = (uint) strlen(table_name);
- int db_len = (uint) strlen(db);
- thd->last_nx_db = alloc_root(glob_alloc,db_len + table_name_len + 2);
- if(thd->last_nx_db)
- {
- thd->last_nx_table = thd->last_nx_db + db_len + 1;
- memcpy(thd->last_nx_table, table_name, table_name_len + 1);
- memcpy(thd->last_nx_db, db, db_len + 1);
- }
- }
table->next=table->prev=table;
free_cache_entry(table);
VOID(pthread_mutex_unlock(&LOCK_open));
@@ -1850,7 +1837,7 @@ static key_map get_key_map_from_key_list(TABLE *table,
** Returns pointer to last inserted field if ok
****************************************************************************/
-static bool
+bool
insert_fields(THD *thd,TABLE_LIST *tables, const char *db_name,
const char *table_name, List_iterator<Item> *it)
{
diff --git a/sql/sql_class.cc b/sql/sql_class.cc
index 9ea3896fd78..f196e4ff852 100644
--- a/sql/sql_class.cc
+++ b/sql/sql_class.cc
@@ -49,6 +49,8 @@ template class List<Alter_drop>;
template class List_iterator<Alter_drop>;
template class List<Alter_column>;
template class List_iterator<Alter_column>;
+template class List<Set_option>;
+template class List_iterator<Set_option>;
#endif
/****************************************************************************
@@ -85,7 +87,8 @@ THD::THD():user_time(0),fatal_error(0),last_insert_id_used(0),
query_length=col_access=0;
query_error=0;
next_insert_id=last_insert_id=0;
- open_tables=temporary_tables=0;
+ open_tables=temporary_tables=handler_tables=0;
+ handler_items=0;
tmp_table=0;
lock=locked_tables=0;
used_tables=0;
@@ -95,7 +98,7 @@ THD::THD():user_time(0),fatal_error(0),last_insert_id_used(0),
current_linfo = 0;
slave_thread = 0;
slave_proxy_id = 0;
- last_nx_table = last_nx_db = 0;
+ log_seq = 0;
cond_count=0;
convert_set=0;
mysys_var=0;
@@ -163,6 +166,11 @@ THD::~THD()
lock=locked_tables; locked_tables=0;
close_thread_tables(this);
}
+ if (handler_tables)
+ {
+ open_tables=handler_tables; handler_tables=0;
+ close_thread_tables(this);
+ }
close_temporary_tables(this);
#ifdef USING_TRANSACTIONS
if (opt_using_transactions)
diff --git a/sql/sql_class.h b/sql/sql_class.h
index 438898ca294..1aa9e65fa74 100644
--- a/sql/sql_class.h
+++ b/sql/sql_class.h
@@ -23,8 +23,10 @@
class Query_log_event;
class Load_log_event;
+class Slave_log_event;
-
+enum enum_enable_or_disable { LEAVE_AS_IS, ENABLE, DISABLE };
+enum enum_ha_read_modes { RFIRST, RNEXT, RPREV, RLAST, RKEY };
enum enum_duplicates { DUP_ERROR, DUP_REPLACE, DUP_IGNORE };
enum enum_log_type { LOG_CLOSED, LOG_NORMAL, LOG_NEW, LOG_BIN };
@@ -61,11 +63,15 @@ class MYSQL_LOG {
char time_buff[20],db[NAME_LEN+1];
char log_file_name[FN_REFLEN],index_file_name[FN_REFLEN];
bool write_error,inited;
+ uint32 log_seq; // current event sequence number
+ // needed this for binlog
bool no_rotate; // for binlog - if log name can never change
// we should not try to rotate it or write any rotation events
// the user should use FLUSH MASTER instead of FLUSH LOGS for
// purging
+ friend class Log_event;
+
public:
MYSQL_LOG();
~MYSQL_LOG();
@@ -82,6 +88,7 @@ public:
time_t query_start=0);
bool write(Query_log_event* event_info); // binary log write
bool write(Load_log_event* event_info);
+ bool write(Slave_log_event* event_info);
bool write(IO_CACHE *cache);
int generate_new_name(char *new_name,const char *old_name);
void make_log_name(char* buf, const char* log_ident);
@@ -234,15 +241,14 @@ public:
const char *proc_info;
uint client_capabilities,max_packet_length;
uint master_access,db_access;
- TABLE *open_tables,*temporary_tables;
+ TABLE *open_tables,*temporary_tables, *handler_tables;
MYSQL_LOCK *lock,*locked_tables;
ULL *ull;
struct st_my_thread_var *mysys_var;
enum enum_server_command command;
uint32 server_id;
+ uint32 log_seq;
const char *where;
- char* last_nx_table; // last non-existent table, we need this for replication
- char* last_nx_db; // database of the last nx table
time_t start_time,time_after_lock,user_time;
time_t connect_time,thr_create_time; // track down slow pthread_create
thr_lock_type update_lock_default;
@@ -256,7 +262,7 @@ public:
#ifdef HAVE_GEMINI_DB
struct st_gemini gemini;
#endif
- Item *free_list;
+ Item *free_list, *handler_items;
CONVERT *convert_set;
Field *dupp_field;
#ifndef __WIN__
@@ -266,7 +272,7 @@ public:
Vio* active_vio;
pthread_mutex_t active_vio_lock;
#endif
- ulonglong next_insert_id,last_insert_id,current_insert_id;
+ ulonglong next_insert_id,last_insert_id,current_insert_id, limit_found_rows;
ha_rows select_limit,offset_limit,default_select_limit,cuted_fields,
max_join_size, sent_row_count, examined_row_count;
table_map used_tables;
@@ -356,6 +362,10 @@ public:
}
return last_insert_id;
}
+ inline ulonglong found_rows(void)
+ {
+ return limit_found_rows;
+ }
inline bool active_transaction()
{
#ifdef USING_TRANSACTIONS
@@ -461,8 +471,6 @@ public:
void send_error(uint errcode,const char *err);
bool send_eof();
};
-
-
class select_insert :public select_result {
protected:
TABLE *table;
@@ -562,3 +570,60 @@ class user_var_entry
Item_result type;
};
+/* Class for unique (removing of duplicates) */
+
+class Unique :public Sql_alloc
+{
+ DYNAMIC_ARRAY file_ptrs;
+ ulong max_elements, max_in_memory_size;
+ IO_CACHE file;
+ TREE tree;
+ char *record_pointers;
+ bool flush();
+
+public:
+ ulong elements;
+ Unique(qsort_cmp2 comp_func, void * comp_func_fixed_arg,
+ uint size, ulong max_in_memory_size_arg);
+ ~Unique();
+ inline bool Unique::unique_add(gptr ptr)
+ {
+ if (tree.elements_in_tree > max_elements && flush())
+ return 1;
+ return !tree_insert(&tree,ptr,0);
+ }
+
+ bool get(TABLE *table);
+
+ friend int unique_write_to_file(gptr key, element_count count, Unique *unique);
+ friend int unique_write_to_ptrs(gptr key, element_count count, Unique *unique);
+};
+
+ class multi_delete : public select_result {
+ TABLE_LIST *delete_tables, *table_being_deleted;
+#ifdef SINISAS_STRIP
+ IO_CACHE **tempfiles;
+ byte *memory_lane;
+#else
+ Unique **tempfiles;
+#endif
+ THD *thd;
+ ha_rows deleted;
+ uint num_of_tables;
+ int error;
+ thr_lock_type lock_option;
+ bool do_delete;
+ public:
+ multi_delete(THD *thd, TABLE_LIST *dt, thr_lock_type lock_option_arg,
+ uint num_of_tables);
+ ~multi_delete();
+ int prepare(List<Item> &list);
+ bool send_fields(List<Item> &list,
+ uint flag) { return 0; }
+ bool send_data(List<Item> &items);
+ void send_error(uint errcode,const char *err);
+ int do_deletes (bool from_send_error);
+ bool send_eof();
+ };
+
+
diff --git a/sql/sql_db.cc b/sql/sql_db.cc
index 7cd892f6079..85d3f0a344c 100644
--- a/sql/sql_db.cc
+++ b/sql/sql_db.cc
@@ -30,11 +30,12 @@ static long mysql_rm_known_files(THD *thd, MY_DIR *dirp, const char *path,
/* db-name is already validated when we come here */
-void mysql_create_db(THD *thd, char *db, uint create_options)
+int mysql_create_db(THD *thd, char *db, uint create_options)
{
char path[FN_REFLEN+16];
MY_DIR *dirp;
long result=1;
+ int error = 0;
DBUG_ENTER("mysql_create_db");
VOID(pthread_mutex_lock(&LOCK_mysql_create_db));
@@ -73,7 +74,9 @@ void mysql_create_db(THD *thd, char *db, uint create_options)
my_dirend(dirp);
if (!(create_options & HA_LEX_CREATE_IF_NOT_EXISTS))
{
- net_printf(&thd->net,ER_DB_CREATE_EXISTS,db);
+ if(thd)
+ net_printf(&thd->net,ER_DB_CREATE_EXISTS,db);
+ error = 1;
goto exit;
}
result = 0;
@@ -83,34 +86,39 @@ void mysql_create_db(THD *thd, char *db, uint create_options)
strend(path)[-1]=0; // Remove last '/' from path
if (my_mkdir(path,0777,MYF(0)) < 0)
{
- net_printf(&thd->net,ER_CANT_CREATE_DB,db,my_errno);
+ if(thd)
+ net_printf(&thd->net,ER_CANT_CREATE_DB,db,my_errno);
+ error = 1;
goto exit;
}
}
- if (!thd->query)
- {
- thd->query = path;
- thd->query_length = (uint) (strxmov(path,"create database ", db, NullS)-
- path);
- }
+
+ if(thd)
{
- mysql_update_log.write(thd,thd->query, thd->query_length);
- if (mysql_bin_log.is_open())
+ if (!thd->query)
{
- Query_log_event qinfo(thd, thd->query);
- mysql_bin_log.write(&qinfo);
+ thd->query = path;
+ thd->query_length = (uint) (strxmov(path,"create database ", db, NullS)-
+ path);
}
+ {
+ mysql_update_log.write(thd,thd->query, thd->query_length);
+ if (mysql_bin_log.is_open())
+ {
+ Query_log_event qinfo(thd, thd->query);
+ mysql_bin_log.write(&qinfo);
+ }
+ }
+ if (thd->query == path)
+ {
+ thd->query = 0; // just in case
+ thd->query_length = 0;
+ }
+ send_ok(&thd->net, result);
}
- if (thd->query == path)
- {
- thd->query = 0; // just in case
- thd->query_length = 0;
- }
- send_ok(&thd->net, result);
-
exit:
VOID(pthread_mutex_unlock(&LOCK_mysql_create_db));
- DBUG_VOID_RETURN;
+ DBUG_RETURN(error);
}
const char *del_exts[]=
@@ -120,10 +128,14 @@ static TYPELIB deletable_extentions=
/* db-name is already validated when we come here */
-
-void mysql_rm_db(THD *thd,char *db,bool if_exists)
+/* If thd == 0, do not write any messages
+ This is useful in replication when we want to remove
+ a stale database before replacing it with the new one
+*/
+int mysql_rm_db(THD *thd,char *db,bool if_exists)
{
long deleted=0;
+ int error = 0;
char path[FN_REFLEN+16];
MY_DIR *dirp;
DBUG_ENTER("mysql_rm_db");
@@ -156,15 +168,19 @@ void mysql_rm_db(THD *thd,char *db,bool if_exists)
/* See if the directory exists */
if (!(dirp = my_dir(path,MYF(MY_WME | MY_DONT_SORT))))
{
- if (!if_exists)
- net_printf(&thd->net,ER_DB_DROP_EXISTS,db);
- else
- send_ok(&thd->net,0);
+ if(thd)
+ {
+ if (!if_exists)
+ net_printf(&thd->net,ER_DB_DROP_EXISTS,db);
+ else
+ send_ok(&thd->net,0);
+ }
+ error = !if_exists;
goto exit;
}
remove_db_from_cache(db);
- if ((deleted=mysql_rm_known_files(thd, dirp, path,0)) >= 0)
+ if ((deleted=mysql_rm_known_files(thd, dirp, path,0)) >= 0 && thd)
{
if (!thd->query)
{
@@ -183,13 +199,14 @@ void mysql_rm_db(THD *thd,char *db,bool if_exists)
thd->query = 0; // just in case
thd->query_length = 0;
}
+
send_ok(&thd->net,(ulong) deleted);
}
exit:
VOID(pthread_mutex_unlock(&LOCK_open));
VOID(pthread_mutex_unlock(&LOCK_mysql_create_db));
- DBUG_VOID_RETURN;
+ DBUG_RETURN(error);
}
/*
@@ -197,6 +214,7 @@ exit:
are 2 digits (raid directories).
*/
+/* This one also needs to work with thd == 0 for replication */
static long mysql_rm_known_files(THD *thd, MY_DIR *dirp, const char *org_path,
uint level)
{
@@ -208,7 +226,7 @@ static long mysql_rm_known_files(THD *thd, MY_DIR *dirp, const char *org_path,
/* remove all files with known extensions */
for (uint idx=2 ;
- idx < (uint) dirp->number_off_files && !thd->killed ;
+ idx < (uint) dirp->number_off_files && (!thd || !thd->killed) ;
idx++)
{
FILEINFO *file=dirp->dir_entry+idx;
@@ -240,9 +258,10 @@ static long mysql_rm_known_files(THD *thd, MY_DIR *dirp, const char *org_path,
}
strxmov(filePath,org_path,"/",file->name,NullS);
unpack_filename(filePath,filePath);
- if (my_delete(filePath,MYF(MY_WME)))
+ if (my_delete_with_symlink(filePath,MYF(MY_WME)))
{
- net_printf(&thd->net,ER_DB_DROP_DELETE,filePath,my_error);
+ if(thd)
+ net_printf(&thd->net,ER_DB_DROP_DELETE,filePath,my_error);
my_dirend(dirp);
DBUG_RETURN(-1);
}
@@ -251,7 +270,7 @@ static long mysql_rm_known_files(THD *thd, MY_DIR *dirp, const char *org_path,
my_dirend(dirp);
- if (thd->killed)
+ if (thd && thd->killed)
{
send_error(&thd->net,ER_SERVER_SHUTDOWN);
DBUG_RETURN(-1);
@@ -275,7 +294,8 @@ static long mysql_rm_known_files(THD *thd, MY_DIR *dirp, const char *org_path,
/* Don't give errors if we can't delete 'RAID' directory */
if (level)
DBUG_RETURN(deleted);
- send_error(&thd->net);
+ if(thd)
+ send_error(&thd->net);
DBUG_RETURN(-1);
}
path=filePath;
@@ -288,7 +308,8 @@ static long mysql_rm_known_files(THD *thd, MY_DIR *dirp, const char *org_path,
/* Don't give errors if we can't delete 'RAID' directory */
if (rmdir(path) < 0 && !level)
{
- net_printf(&thd->net,ER_DB_DROP_RMDIR, path,errno);
+ if(thd)
+ net_printf(&thd->net,ER_DB_DROP_RMDIR, path,errno);
DBUG_RETURN(-1);
}
}
diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc
index 587b205886e..b690ef4b327 100644
--- a/sql/sql_delete.cc
+++ b/sql/sql_delete.cc
@@ -119,8 +119,13 @@ int generate_table(THD *thd, TABLE_LIST *table_list, TABLE *locked_table)
}
-int mysql_delete(THD *thd,TABLE_LIST *table_list,COND *conds,ha_rows limit,
- thr_lock_type lock_type, ulong options)
+int mysql_delete(THD *thd,
+ TABLE_LIST *table_list,
+ COND *conds,
+ ORDER *order,
+ ha_rows limit,
+ thr_lock_type lock_type,
+ ulong options)
{
int error;
TABLE *table;
@@ -146,7 +151,7 @@ int mysql_delete(THD *thd,TABLE_LIST *table_list,COND *conds,ha_rows limit,
#ifdef HAVE_INNOBASE_DB
/* We need to add code to not generate table based on the table type */
if (!innodb_skip)
- use_generate_table=0; // Innodb can't use re-generate table
+ use_generate_table=0; // Innobase can't use re-generate table
#endif
if (use_generate_table && ! thd->open_tables)
{
@@ -181,7 +186,7 @@ int mysql_delete(THD *thd,TABLE_LIST *table_list,COND *conds,ha_rows limit,
/* If running in safe sql mode, don't allow updates without keys */
if (!table->quick_keys)
{
- thd->lex.options|=QUERY_NO_INDEX_USED;
+ thd->lex.select_lex.options|=QUERY_NO_INDEX_USED;
if ((thd->options & OPTION_SAFE_UPDATES) && limit == HA_POS_ERROR)
{
delete select;
@@ -191,8 +196,35 @@ int mysql_delete(THD *thd,TABLE_LIST *table_list,COND *conds,ha_rows limit,
}
(void) table->file->extra(HA_EXTRA_NO_READCHECK);
if (options & OPTION_QUICK)
- (void) table->file->extra(HA_EXTRA_QUICK);
- init_read_record(&info,thd,table,select,-1,1);
+ (void) table->file->extra(HA_EXTRA_QUICK);
+
+ if (order)
+ {
+ uint length;
+ SORT_FIELD *sortorder;
+ TABLE_LIST tables;
+ List<Item> fields;
+ List<Item> all_fields;
+ ha_rows examined_rows;
+
+ bzero((char*) &tables,sizeof(tables));
+ tables.table = table;
+
+ table->io_cache = (IO_CACHE *) my_malloc(sizeof(IO_CACHE),
+ MYF(MY_FAE | MY_ZEROFILL));
+ if (setup_order(thd, &tables, fields, all_fields, order) ||
+ !(sortorder=make_unireg_sortorder(order, &length)) ||
+ (table->found_records = filesort(table, sortorder, length,
+ (SQL_SELECT *) 0, 0L, HA_POS_ERROR,
+ &examined_rows))
+ == HA_POS_ERROR)
+ {
+ delete select;
+ DBUG_RETURN(-1); // This will force out message
+ }
+ }
+
+ init_read_record(&info,thd,table,select,1,1);
ulong deleted=0L;
thd->proc_info="updating";
while (!(error=info.read_record(&info)) && !thd->killed)
@@ -218,9 +250,10 @@ int mysql_delete(THD *thd,TABLE_LIST *table_list,COND *conds,ha_rows limit,
}
thd->proc_info="end";
end_read_record(&info);
+ /* if (order) free_io_cache(table); */ /* QQ Should not be needed */
(void) table->file->extra(HA_EXTRA_READCHECK);
if (options & OPTION_QUICK)
- (void) table->file->extra(HA_EXTRA_NORMAL);
+ (void) table->file->extra(HA_EXTRA_NORMAL);
using_transactions=table->file->has_transactions();
if (deleted && (error <= 0 || !using_transactions))
{
@@ -253,3 +286,635 @@ int mysql_delete(THD *thd,TABLE_LIST *table_list,COND *conds,ha_rows limit,
}
+/***************************************************************************
+** delete multiple tables from join
+***************************************************************************/
+
+#ifndef DBUG_OFF
+#define MEM_STRIP_BUF_SIZE 2048
+#else
+#define MEM_STRIP_BUF_SIZE sortbuffer_size
+#endif
+
+#ifndef SINISAS_STRIP
+int refposcmp2(void* arg, const void *a,const void *b)
+{
+ return memcmp(a,b,(int) arg);
+}
+#endif
+
+multi_delete::multi_delete(THD *thd_arg, TABLE_LIST *dt,
+ thr_lock_type lock_option_arg,
+ uint num_of_tables_arg)
+ : delete_tables (dt), thd(thd_arg), deleted(0),
+ num_of_tables(num_of_tables_arg), error(0), lock_option(lock_option_arg),
+ do_delete(false)
+{
+ uint counter=0;
+#ifdef SINISAS_STRIP
+ tempfiles = (IO_CACHE **) sql_calloc(sizeof(IO_CACHE *)* num_of_tables);
+ memory_lane = (byte *)sql_alloc(MAX_REFLENGTH*MEM_STRIP_BUF_SIZE);
+#else
+ tempfiles = (Unique **) sql_calloc(sizeof(Unique *) * (num_of_tables-1));
+#endif
+
+ (void) dt->table->file->extra(HA_EXTRA_NO_READCHECK);
+ /* Don't use key read with MULTI-TABLE-DELETE */
+ dt->table->used_keys=0;
+ for (dt=dt->next ; dt ; dt=dt->next,counter++)
+ {
+ TABLE *table=dt->table;
+ (void) table->file->extra(HA_EXTRA_NO_READCHECK);
+#ifdef SINISAS_STRIP
+ tempfiles[counter]=(IO_CACHE *) sql_alloc(sizeof(IO_CACHE));
+ if (open_cached_file(tempfiles[counter], mysql_tmpdir,TEMP_PREFIX,
+ DISK_BUFFER_SIZE, MYF(MY_WME)))
+ {
+ my_error(ER_CANT_OPEN_FILE,MYF(0),(tempfiles[counter])->file_name,errno);
+ thd->fatal_error=1;
+ return;
+ }
+#else
+ tempfiles[counter] = new Unique (refposcmp2,
+ (void *) table->file->ref_length,
+ table->file->ref_length,
+ MEM_STRIP_BUF_SIZE);
+#endif
+ }
+}
+
+
+int
+multi_delete::prepare(List<Item> &values)
+{
+ DBUG_ENTER("multi_delete::prepare");
+ do_delete = true;
+ thd->proc_info="deleting from main table";
+
+ if (thd->options & OPTION_SAFE_UPDATES)
+ {
+ TABLE_LIST *table_ref;
+ for (table_ref=delete_tables; table_ref; table_ref=table_ref->next)
+ {
+ TABLE *table=table_ref->table;
+ if ((thd->options & OPTION_SAFE_UPDATES) && !table->quick_keys)
+ {
+ my_error(ER_UPDATE_WITHOUT_KEY_IN_SAFE_MODE,MYF(0));
+ DBUG_RETURN(1);
+ }
+ }
+ }
+ DBUG_RETURN(0);
+}
+
+
+multi_delete::~multi_delete()
+{
+
+ /* Add back EXTRA_READCHECK; In 4.0.1 we shouldn't need this anymore */
+ for (table_being_deleted=delete_tables ;
+ table_being_deleted ;
+ table_being_deleted=table_being_deleted->next)
+ {
+ VOID(table_being_deleted->table->file->extra(HA_EXTRA_READCHECK));
+ }
+ for (uint counter = 0; counter < num_of_tables-1; counter++)
+ {
+ if (tempfiles[counter])
+ {
+#ifdef SINISAS_STRIP
+ end_io_cache(tempfiles[counter]);
+#else
+ delete tempfiles[counter];
+#endif
+ }
+ }
+}
+
+
+bool multi_delete::send_data(List<Item> &values)
+{
+ int secure_counter= -1;
+ for (table_being_deleted=delete_tables ;
+ table_being_deleted ;
+ table_being_deleted=table_being_deleted->next, secure_counter++)
+ {
+ TABLE *table=table_being_deleted->table;
+
+ /* Check if we are using outer join and we didn't find the row */
+ if (table->status & (STATUS_NULL_ROW | STATUS_DELETED))
+ continue;
+
+ table->file->position(table->record[0]);
+ int rl = table->file->ref_length;
+
+ if (secure_counter < 0)
+ {
+ table->status|= STATUS_DELETED;
+ if (!(error=table->file->delete_row(table->record[0])))
+ deleted++;
+ else
+ {
+ table->file->print_error(error,MYF(0));
+ return 1;
+ }
+ }
+ else
+ {
+#ifdef SINISAS_STRIP
+ error=my_b_write(tempfiles[secure_counter],table->file->ref,rl);
+#else
+ error=tempfiles[secure_counter]->unique_add(table->file->ref);
+#endif
+ if (error)
+ {
+ error=-1;
+ return 1;
+ }
+ }
+ }
+ return 0;
+}
+
+
+#ifdef SINISAS_STRIP
+static inline int COMP (byte *ml,uint len,unsigned int left, unsigned int right)
+{
+ return memcmp(ml + left*len,ml + right*len,len);
+}
+
+#define EX(ML,LEN,LLLEFT,RRRIGHT) \
+ptr1 = ML + LLLEFT*LEN;\
+ptr2 = ML + RRRIGHT*LEN;\
+memcpy(tmp,ptr1,LEN);\
+memcpy(ptr1,ptr2,LEN);\
+memcpy(ptr2,tmp,LEN);\
+
+
+
+static void qsort_mem_pieces(byte *ml, uint length, unsigned short pivotP, unsigned int nElem)
+{
+ unsigned int leftP, rightP, pivotEnd, pivotTemp, leftTemp;
+ unsigned int lNum; byte tmp [MAX_REFLENGTH], *ptr1, *ptr2;
+ int retval;
+tailRecursion:
+ if (nElem <= 1) return;
+ if (nElem == 2)
+ {
+ if (COMP(ml,length,pivotP, rightP = pivotP + 1) > 0)
+ {
+ EX(ml,length,pivotP, rightP);
+ }
+ return;
+ }
+
+ rightP = (nElem - 1) + pivotP;
+ leftP = (nElem >> 1) + pivotP;
+
+/* sort the pivot, left, and right elements for "median of 3" */
+
+ if (COMP (ml,length,leftP, rightP) > 0)
+ {
+ EX (ml,length,leftP, rightP);
+ }
+ if (COMP (ml,length,leftP, pivotP) > 0)
+ {
+ EX (ml,length,leftP, pivotP);
+ }
+ else if (COMP (ml,length, pivotP, rightP) > 0)
+ {
+ EX (ml,length,pivotP, rightP);
+ }
+
+ if (nElem == 3) {
+ EX (ml,length,pivotP, leftP);
+ return;
+ }
+
+/* now for the classic Hoare algorithm */
+
+ leftP = pivotEnd = pivotP + 1;
+
+ do {
+ while ((retval = COMP (ml,length, leftP, pivotP)) <= 0)
+ {
+ if (retval == 0) {
+ EX(ml,length,leftP, pivotEnd);
+ pivotEnd++;
+ }
+ if (leftP < rightP)
+ leftP++;
+ else
+ goto qBreak;
+ }
+ while (leftP < rightP) {
+ if ((retval = COMP(ml,length,pivotP, rightP)) < 0)
+ rightP--;
+ else
+ {
+ EX (ml,length,leftP, rightP);
+ if (retval != 0) {
+ leftP++;
+ rightP--;
+ }
+ break;
+ }
+ }
+ } while (leftP < rightP);
+
+qBreak:
+
+ if (COMP(ml,length,leftP, pivotP) <= 0)
+ leftP++;
+
+ leftTemp = leftP - 1; pivotTemp = pivotP;
+
+ while ((pivotTemp < pivotEnd) && (leftTemp >= pivotEnd))
+ {
+ EX(ml,length,pivotTemp, leftTemp);
+ pivotTemp++; leftTemp--;
+ }
+
+ lNum = leftP - pivotEnd; nElem = (nElem + pivotP) - leftP;
+
+ /* Sort smaller partition first to reduce stack usage */
+ if (nElem < lNum)
+ {
+ qsort_mem_pieces(ml,length,leftP, nElem); nElem = lNum;
+ }
+ else
+ {
+ qsort_mem_pieces(ml,length,pivotP, lNum);
+ pivotP = leftP;
+ }
+ goto tailRecursion;
+}
+
+static byte * btree_search(byte *lane, byte *key,register int last, uint length)
+{
+ register int first = 0;
+ if (last == first)
+ {
+ if (!memcmp(lane,key,length)) return lane;
+ return (byte *)0;
+ }
+Recursion_is_too_slow:
+ if (last - first < 3)
+ {
+ if (!memcmp(lane + first*length,key,length)) return lane + first * length;
+ if (last == first + 1) return (byte *)0;
+ if (!memcmp(lane + last*length,key,length)) return lane + last * length;
+ return (byte *)0;
+ }
+ else
+ {
+ int half = first + (last - first)/2;
+ int result = memcmp(lane + half*length,key,length);
+ if (!result) return lane + half*length;
+ if (result < 0)
+ {
+ first = half + 1; goto Recursion_is_too_slow;
+ }
+ else
+ {
+ last = half + 1; goto Recursion_is_too_slow;
+ }
+ }
+}
+
+struct written_block {
+ byte first[MAX_REFLENGTH], last[MAX_REFLENGTH];
+ my_off_t offset;
+ uint how_many;
+};
+
+static IO_CACHE *strip_duplicates_from_temp (byte *memory_lane, IO_CACHE *ptr, uint ref_length, int *written)
+{
+ byte *mem_ptr; my_off_t off = 0;
+ int read_error, write_error, how_many_to_read, total_to_read = *written, pieces_in_memory = 0, mem_count,written_rows;
+ int offset = written_rows=*written=0;
+ int mem_pool_size = MEM_STRIP_BUF_SIZE * MAX_REFLENGTH / ref_length;
+ byte dup_record[MAX_REFLENGTH]; memset(dup_record,'\xFF',MAX_REFLENGTH);
+ if (reinit_io_cache(ptr,READ_CACHE,0L,0,0))
+ return ptr;
+ IO_CACHE *tempptr = (IO_CACHE *) my_malloc(sizeof(IO_CACHE), MYF(MY_FAE | MY_ZEROFILL));
+ if (open_cached_file(tempptr, mysql_tmpdir,TEMP_PREFIX, DISK_BUFFER_SIZE, MYF(MY_WME)))
+ {
+ my_free((gptr) tempptr, MYF (0));
+ return ptr;
+ }
+ DYNAMIC_ARRAY written_blocks;
+ VOID(init_dynamic_array(&written_blocks,sizeof(struct written_block),20,50));
+ for (;pieces_in_memory < total_to_read;)
+ {
+ how_many_to_read = total_to_read - pieces_in_memory; read_error=write_error=0;
+ if (how_many_to_read > mem_pool_size)
+ how_many_to_read = mem_pool_size;
+ if (my_b_read(ptr, memory_lane, (uint) how_many_to_read * ref_length))
+ {
+ read_error = 1;
+ break;
+ }
+ pieces_in_memory += how_many_to_read;
+ qsort_mem_pieces(memory_lane,0, how_many_to_read, ref_length);
+ byte *checking = dup_record, *cursor=NULL, *mem_end = memory_lane + how_many_to_read * ref_length;
+ int opt_unique_pieces, unique_pieces_in_memory=0; write_error=0;
+ for (mem_ptr=memory_lane; mem_ptr < mem_end ; mem_ptr += ref_length)
+ {
+ if (memcmp(mem_ptr,checking, ref_length))
+ {
+ if (cursor)
+ {
+ memmove(cursor,mem_ptr,mem_end - mem_ptr);
+ mem_end -= mem_ptr - cursor;
+ mem_ptr = cursor; cursor = NULL;
+ }
+ unique_pieces_in_memory++;
+ checking = mem_ptr;
+ }
+ else if (!cursor) cursor = mem_ptr;
+ }
+ opt_unique_pieces=unique_pieces_in_memory;
+ if (written_rows)
+ {
+ if (reinit_io_cache(tempptr,READ_CACHE,0L,0,0)) {write_error = -1; break;}
+ for (uint i=0 ; i < written_blocks.elements ; i++)
+ {
+ struct written_block *wbp=dynamic_element(&written_blocks,i,struct written_block*);
+ if ((memcmp(memory_lane,wbp->last,ref_length) == 1) || (memcmp(memory_lane + (unique_pieces_in_memory - 1) * ref_length, wbp->first, ref_length) == -1))
+ continue;
+ else
+ {
+ if (wbp->how_many < 3)
+ {
+ if ((mem_ptr=btree_search(memory_lane,wbp->first,unique_pieces_in_memory-1, ref_length)))
+ {
+ if (!--opt_unique_pieces) goto skip_writting; // nice little optimization
+ memcpy(mem_ptr,dup_record,ref_length);
+ }
+ if (wbp->how_many == 2 && (mem_ptr=btree_search(memory_lane,wbp->last,unique_pieces_in_memory-1, ref_length)))
+ {
+ if (!--opt_unique_pieces) goto skip_writting; // nice little optimization
+ memcpy(mem_ptr,dup_record,ref_length);
+ }
+ }
+ else
+ {
+ byte block[MAX_REFLENGTH * MEM_STRIP_BUF_SIZE]; // 16 K maximum and only temporary !!
+ if (my_b_read(tempptr, block, (uint) wbp->how_many * ref_length))
+ {
+ read_error = 1; goto skip_writting;
+ }
+ if (unique_pieces_in_memory < 3)
+ {
+ if ((mem_ptr=btree_search(block,memory_lane,wbp->how_many - 1, ref_length)))
+ {
+ if (!--opt_unique_pieces) goto skip_writting; // nice little optimization
+ memcpy(memory_lane,dup_record,ref_length);
+ }
+ if (unique_pieces_in_memory == 2 && (mem_ptr=btree_search(block,memory_lane + ref_length,wbp->how_many - 1, ref_length)))
+ {
+ if (!--opt_unique_pieces) goto skip_writting; // nice little optimization
+ memcpy(mem_ptr,dup_record,ref_length);
+ }
+ }
+ else
+ {
+ byte *cursor; bool do_check_past;
+ if (unique_pieces_in_memory < wbp->how_many)
+ {
+ do_check_past = (memcmp(memory_lane + (unique_pieces_in_memory - 1)*ref_length,wbp->last,ref_length) == 1);
+ for (cursor=memory_lane; cursor < memory_lane + unique_pieces_in_memory*ref_length; cursor += ref_length)
+ {
+ if ((mem_ptr=btree_search(block,cursor,wbp->how_many - 1, ref_length)))
+ {
+ if (!--opt_unique_pieces) goto skip_writting; // nice little optimization
+ memcpy(cursor,dup_record,ref_length);
+ }
+ else if (do_check_past && (memcmp(cursor,wbp->last,ref_length) == 1)) break;
+ }
+ }
+ else
+ {
+ do_check_past = (memcmp(memory_lane + (unique_pieces_in_memory - 1)*ref_length,wbp->last,ref_length) == -1);
+ for (cursor=block; cursor < block + wbp->how_many*ref_length;cursor += ref_length)
+ {
+ if ((mem_ptr=btree_search(memory_lane,cursor,unique_pieces_in_memory-1, ref_length)))
+ {
+ if (!--opt_unique_pieces) goto skip_writting; // nice little optimization
+ memcpy(mem_ptr,dup_record,ref_length);
+ }
+ else if (do_check_past && (memcmp(cursor,memory_lane + (unique_pieces_in_memory - 1)*ref_length,ref_length) == 1)) break;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ reinit_io_cache(tempptr, WRITE_CACHE,off,0,0);
+ struct written_block wb; wb.offset = off; wb.how_many=opt_unique_pieces; byte *last;
+ if (opt_unique_pieces < unique_pieces_in_memory)
+ {
+ for (mem_count=0, mem_ptr=memory_lane; mem_count<unique_pieces_in_memory;mem_count++, mem_ptr += ref_length)
+ {
+ if (memcmp(mem_ptr,dup_record,ref_length))
+ {
+ if (my_b_write(tempptr,mem_ptr,ref_length))
+ {
+ if (write_error == 9 || write_error == -1) write_error = 0;
+ if (write_error) break;
+ }
+ if (!mem_count) memcpy(wb.first,mem_ptr,ref_length);
+ last = mem_ptr;
+ written_rows++;
+ }
+ }
+ memcpy(wb.last,last,ref_length);
+ }
+ else
+ {
+ memcpy(wb.first,memory_lane,ref_length); memcpy(wb.last,memory_lane + (unique_pieces_in_memory -1)*ref_length,ref_length);
+ if (my_b_write(tempptr, memory_lane,unique_pieces_in_memory * ref_length))
+ {
+ write_error = 1; break;
+ }
+ written_rows += unique_pieces_in_memory;
+ }
+ off = my_b_tell(tempptr);
+ VOID(push_dynamic(&written_blocks,(gptr) &wb));
+ skip_writting:
+ if (write_error || read_error) break;
+ }
+ delete_dynamic(&written_blocks);
+ if (read_error || write_error)
+ {
+ close_cached_file(tempptr); end_io_cache(tempptr);
+ return ptr;
+ }
+ else
+ {
+ close_cached_file(ptr); *written=written_rows; end_io_cache(ptr);
+ reinit_io_cache(tempptr,READ_CACHE,0L,0,0);
+ return tempptr;
+ }
+}
+
+#endif /* SINISAS_STRIP */
+
+/* Return true if some table is not transaction safe */
+
+static bool some_table_is_not_transaction_safe (TABLE_LIST *tl)
+{
+ for (; tl ; tl=tl->next)
+ {
+ if (!(tl->table->file->has_transactions()))
+ return true;
+ }
+ return false;
+}
+
+
+void multi_delete::send_error(uint errcode,const char *err)
+{
+ /* First send error what ever it is ... */
+ ::send_error(&thd->net,errcode,err);
+ /* If nothing deleted return */
+ if (!deleted)
+ return;
+ /* Below can happen when thread is killed early ... */
+ if (!table_being_deleted)
+ table_being_deleted=delete_tables;
+
+ /*
+ If rows from the first table only has been deleted and it is transactional,
+ just do rollback.
+ The same if all tables are transactional, regardless of where we are.
+ In all other cases do attempt deletes ...
+ */
+ if ((table_being_deleted->table->file->has_transactions() &&
+ table_being_deleted == delete_tables) ||
+ !some_table_is_not_transaction_safe(delete_tables->next))
+ ha_rollback(thd);
+ else if (do_delete)
+ VOID(do_deletes(true));
+}
+
+
+int multi_delete::do_deletes (bool from_send_error)
+{
+ int error = 0, counter = 0, count;
+
+ if (from_send_error)
+ {
+ /* Found out table number for 'table_being_deleted' */
+ for (TABLE_LIST *aux=delete_tables;
+ aux != table_being_deleted;
+ aux=aux->next)
+ counter++;
+ }
+ else
+ table_being_deleted = delete_tables;
+
+ do_delete = false;
+ for (table_being_deleted=table_being_deleted->next;
+ table_being_deleted ;
+ table_being_deleted=table_being_deleted->next, counter++)
+ {
+ TABLE *table = table_being_deleted->table;
+ int rl = table->file->ref_length;
+#ifdef SINISAS_STRIP
+ int num_of_positions = (int)my_b_tell(tempfiles[counter])/rl;
+ if (!num_of_positions) continue;
+ tempfiles[counter] = strip_duplicates_from_temp(memory_lane, tempfiles[counter],rl,&num_of_positions);
+ if (!num_of_positions)
+ {
+ error=1; break;
+ }
+#else
+ if (tempfiles[counter]->get(table))
+ {
+ error=1;
+ break;
+ }
+#endif
+
+#if USE_REGENERATE_TABLE
+ // nice little optimization ....
+ // but Monty has to fix generate_table...
+ // This will not work for transactional tables because for other types
+ // records is not absolute
+ if (num_of_positions == table->file->records)
+ {
+ TABLE_LIST table_list;
+ bzero((char*) &table_list,sizeof(table_list));
+ table_list.name=table->table_name; table_list.real_name=table_being_deleted->real_name;
+ table_list.table=table;
+ table_list.grant=table->grant;
+ table_list.db = table_being_deleted->db;
+ error=generate_table(thd,&table_list,(TABLE *)0);
+ if (error <= 0) {error = 1; break;}
+ deleted += num_of_positions;
+ continue;
+ }
+#endif /* USE_REGENERATE_TABLE */
+
+ READ_RECORD info;
+ error=0;
+#ifdef SINISAS_STRIP
+ SQL_SELECT *select= new SQL_SELECT;
+ select->head=table;
+ select->file=*tempfiles[counter];
+ init_read_record(&info,thd,table,select,0,0);
+#else
+ init_read_record(&info,thd,table,NULL,0,0);
+#endif
+ bool not_trans_safe = some_table_is_not_transaction_safe(delete_tables);
+ while (!(error=info.read_record(&info)) &&
+ (!thd->killed || from_send_error || not_trans_safe))
+ {
+ error=table->file->delete_row(table->record[0]);
+ if (error)
+ {
+ table->file->print_error(error,MYF(0));
+ break;
+ }
+ else
+ deleted++;
+ }
+ end_read_record(&info);
+#ifdef SINISAS_STRIP
+ delete select;
+#endif
+ if (error == -1)
+ error = 0;
+ }
+ return error;
+}
+
+
+bool multi_delete::send_eof()
+{
+ thd->proc_info="deleting from reference tables";
+ int error = do_deletes(false);
+
+ thd->proc_info="end";
+ if (error && error != -1)
+ {
+ ::send_error(&thd->net);
+ return 1;
+ }
+
+ if (deleted &&
+ (error <= 0 || some_table_is_not_transaction_safe(delete_tables)))
+ {
+ mysql_update_log.write(thd,thd->query,thd->query_length);
+ Query_log_event qinfo(thd, thd->query);
+ if (mysql_bin_log.write(&qinfo) &&
+ !some_table_is_not_transaction_safe(delete_tables))
+ error=1; // Rollback
+ VOID(ha_autocommit_or_rollback(thd,error >= 0));
+ }
+ ::send_ok(&thd->net,deleted);
+ return 0;
+}
diff --git a/sql/sql_handler.cc b/sql/sql_handler.cc
new file mode 100644
index 00000000000..a605984aef7
--- /dev/null
+++ b/sql/sql_handler.cc
@@ -0,0 +1,254 @@
+/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+
+/* HANDLER ... commands - direct access to ISAM */
+
+#include <assert.h>
+#include "mysql_priv.h"
+#include "sql_select.h"
+
+/* TODO:
+ HANDLER blabla OPEN [ AS foobar ] [ (column-list) ]
+
+ the most natural (easiest, fastest) way to do it is to
+ compute List<Item> field_list not in mysql_ha_read
+ but in mysql_ha_open, and then store it in TABLE structure.
+
+ The problem here is that mysql_parse calls free_item to free all the
+ items allocated at the end of every query. The workaround would to
+ keep two item lists per THD - normal free_list and handler_items.
+ The second is to be freeed only on thread end. mysql_ha_open should
+ then do { handler_items=concat(handler_items, free_list); free_list=0; }
+
+ But !!! do_cammand calls free_root at the end of every query and frees up
+ all the sql_alloc'ed memory. It's harder to work around...
+ */
+
+#define HANDLER_TABLES_HACK(thd) { \
+ TABLE *tmp=thd->open_tables; \
+ thd->open_tables=thd->handler_tables; \
+ thd->handler_tables=tmp; }
+
+static TABLE **find_table_ptr_by_name(THD *thd, const char *db,
+ const char *table_name);
+
+int mysql_ha_open(THD *thd, TABLE_LIST *tables)
+{
+ HANDLER_TABLES_HACK(thd);
+ int err=open_tables(thd,tables);
+ HANDLER_TABLES_HACK(thd);
+ if (err)
+ return -1;
+
+ send_ok(&thd->net);
+ return 0;
+}
+
+int mysql_ha_close(THD *thd, TABLE_LIST *tables)
+{
+ TABLE **ptr=find_table_ptr_by_name(thd, tables->db, tables->name);
+
+ if (*ptr)
+ close_thread_table(thd, ptr);
+
+ send_ok(&thd->net);
+ return 0;
+}
+
+static enum enum_ha_read_modes rkey_to_rnext[]=
+ { RNEXT, RNEXT, RPREV, RNEXT, RPREV, RNEXT, RPREV };
+
+int mysql_ha_read(THD *thd, TABLE_LIST *tables,
+ enum enum_ha_read_modes mode, char *keyname, List<Item> *key_expr,
+ enum ha_rkey_function ha_rkey_mode, Item *cond,
+ ha_rows select_limit,ha_rows offset_limit)
+{
+ int err, keyno=-1;
+ TABLE *table=*find_table_ptr_by_name(thd, tables->db, tables->name);
+ if (!table)
+ {
+ my_printf_error(ER_UNKNOWN_TABLE,ER(ER_UNKNOWN_TABLE),MYF(0),
+ tables->name,"HANDLER");
+ return -1;
+ }
+ tables->table=table;
+
+ if (cond && cond->fix_fields(thd,tables))
+ return -1;
+
+ if (keyname)
+ {
+ if ((keyno=find_type(keyname, &table->keynames, 1+2)-1)<0)
+ {
+ my_printf_error(ER_KEY_DOES_NOT_EXITS,ER(ER_KEY_DOES_NOT_EXITS),MYF(0),
+ keyname,tables->name);
+ return -1;
+ }
+ }
+
+ List<Item> list;
+ list.push_front(new Item_field(NULL,NULL,"*"));
+ List_iterator<Item> it(list);
+ it++;
+
+ insert_fields(thd,tables,tables->db,tables->name,&it);
+
+ table->file->index_init(keyno);
+
+ select_limit+=offset_limit;
+ send_fields(thd,list,1);
+
+ MYSQL_LOCK *lock=mysql_lock_tables(thd,&tables->table,1);
+
+ for (uint num_rows=0; num_rows < select_limit; )
+ {
+ switch(mode)
+ {
+ case RFIRST:
+ err=keyname ?
+ table->file->index_first(table->record[0]) :
+ table->file->rnd_init(1) ||
+ table->file->rnd_next(table->record[0]);
+ mode=RNEXT;
+ break;
+ case RLAST:
+ dbug_assert(keyname != 0);
+ err=table->file->index_last(table->record[0]);
+ mode=RPREV;
+ break;
+ case RNEXT:
+ err=keyname ?
+ table->file->index_next(table->record[0]) :
+ table->file->rnd_next(table->record[0]);
+ break;
+ case RPREV:
+ dbug_assert(keyname != 0);
+ err=table->file->index_prev(table->record[0]);
+ break;
+ case RKEY:
+ {
+ dbug_assert(keyname != 0);
+ KEY *keyinfo=table->key_info+keyno;
+ KEY_PART_INFO *key_part=keyinfo->key_part;
+ uint key_len;
+ byte *key;
+ if (key_expr->elements > keyinfo->key_parts)
+ {
+ my_printf_error(ER_TOO_MANY_KEY_PARTS,ER(ER_TOO_MANY_KEY_PARTS),
+ MYF(0),keyinfo->key_parts);
+ goto err;
+ }
+ List_iterator<Item> it_ke(*key_expr);
+ Item *item;
+ for (key_len=0 ; (item=it_ke++) ; key_part++)
+ {
+ item->save_in_field(key_part->field);
+ key_len+=key_part->store_length;
+ }
+ if (!(key=sql_calloc(ALIGN_SIZE(key_len))))
+ {
+ send_error(&thd->net,ER_OUTOFMEMORY);
+ goto err;
+ }
+ key_copy(key, table, keyno, key_len);
+ err=table->file->index_read(table->record[0],
+ key,key_len,ha_rkey_mode);
+ mode=rkey_to_rnext[(int)ha_rkey_mode];
+ break;
+ }
+ default:
+ send_error(&thd->net,ER_ILLEGAL_HA);
+ goto err;
+ }
+
+ if (err)
+ {
+ if (err != HA_ERR_KEY_NOT_FOUND && err != HA_ERR_END_OF_FILE)
+ {
+ sql_print_error("mysql_ha_read: Got error %d when reading table",
+ err);
+ table->file->print_error(err,MYF(0));
+ goto err;
+ }
+ goto ok;
+ }
+ if (cond)
+ {
+ err=err;
+ if(!cond->val_int())
+ continue;
+ }
+ if (num_rows>=offset_limit)
+ {
+ if (!err)
+ {
+ String *packet = &thd->packet;
+ Item *item;
+ packet->length(0);
+ it.rewind();
+ while ((item=it++))
+ {
+ if (item->send(packet))
+ {
+ packet->free(); // Free used
+ my_error(ER_OUT_OF_RESOURCES,MYF(0));
+ goto err;
+ }
+ }
+ my_net_write(&thd->net, (char*)packet->ptr(), packet->length());
+ }
+ }
+ num_rows++;
+ }
+ok:
+ mysql_unlock_tables(thd,lock);
+ send_eof(&thd->net);
+ return 0;
+err:
+ mysql_unlock_tables(thd,lock);
+ return -1;
+}
+
+/**************************************************************************
+ 2Monty: It could easily happen, that the following service functions are
+ already defined somewhere in the code, but I failed to find them.
+ If this is the case, just say a word and I'll use old functions here.
+**************************************************************************/
+
+/* Note: this function differs from find_locked_table() because we're looking
+ here for alias, not real table name
+ */
+static TABLE **find_table_ptr_by_name(THD *thd, const char *db,
+ const char *table_name)
+{
+ int dblen;
+ TABLE **ptr;
+
+ if (!db || ! *db)
+ db= thd->db ? thd->db : "";
+ dblen=strlen(db)+1;
+ ptr=&(thd->handler_tables);
+
+ for (TABLE *table=*ptr; table ; table=*ptr)
+ {
+ if (!memcmp(table->table_cache_key, db, dblen) &&
+ !my_strcasecmp(table->table_name,table_name))
+ break;
+ ptr=&(table->next);
+ }
+ return ptr;
+}
diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc
index 14f4a732eac..19dc239c050 100644
--- a/sql/sql_insert.cc
+++ b/sql/sql_insert.cc
@@ -642,7 +642,7 @@ static TABLE *delayed_get_table(THD *thd,TABLE_LIST *table_list)
/* Copy error message and abort */
thd->fatal_error=1;
strmov(thd->net.last_error,tmp->thd.net.last_error);
- thd->net.last_errno=thd->net.last_errno;
+ thd->net.last_errno=tmp->thd.net.last_errno;
}
tmp->unlock();
pthread_mutex_unlock(&LOCK_delayed_create);
diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc
index a78fef62657..3f6c09073e6 100644
--- a/sql/sql_lex.cc
+++ b/sql/sql_lex.cc
@@ -142,11 +142,11 @@ LEX *lex_start(THD *thd, uchar *buf,uint length)
lex->next_state=STATE_START;
lex->end_of_query=(lex->ptr=buf)+length;
lex->yylineno = 1;
- lex->create_refs=lex->in_comment=0;
+ lex->select->create_refs=lex->in_comment=0;
lex->length=0;
- lex->in_sum_expr=0;
- lex->expr_list.empty();
- lex->ftfunc_list.empty();
+ lex->select->in_sum_expr=0;
+ lex->select->expr_list.empty();
+ lex->select->ftfunc_list.empty();
lex->convert_set=(lex->thd=thd)->convert_set;
lex->yacc_yyss=lex->yacc_yyvs=0;
lex->ignore_space=test(thd->client_capabilities & CLIENT_IGNORE_SPACE);
@@ -155,7 +155,7 @@ LEX *lex_start(THD *thd, uchar *buf,uint length)
void lex_end(LEX *lex)
{
- lex->expr_list.delete_elements(); // If error when parsing sql-varargs
+ lex->select->expr_list.delete_elements(); // If error when parsing sql-varargs
x_free(lex->yacc_yyss);
x_free(lex->yacc_yyvs);
}
@@ -196,7 +196,7 @@ static int find_keyword(LEX *lex, uint len, bool function)
/* make a copy of token before ptr and set yytoklen */
-static inline LEX_STRING get_token(LEX *lex,uint length)
+LEX_STRING get_token(LEX *lex,uint length)
{
LEX_STRING tmp;
yyUnget(); // ptr points now after last token char
@@ -509,6 +509,8 @@ int yylex(void *arg)
yySkip(); // next state does a unget
}
yylval->lex_str=get_token(lex,length);
+ if (lex->convert_set)
+ lex->convert_set->convert((char*) yylval->lex_str.str,lex->yytoklen);
return(IDENT);
case STATE_IDENT_SEP: // Found ident and now '.'
@@ -597,6 +599,8 @@ int yylex(void *arg)
case STATE_FOUND_IDENT: // Complete ident
yylval->lex_str=get_token(lex,yyLength());
+ if (lex->convert_set)
+ lex->convert_set->convert((char*) yylval->lex_str.str,lex->yytoklen);
return(IDENT);
case STATE_USER_VARIABLE_DELIMITER:
@@ -604,6 +608,8 @@ int yylex(void *arg)
while ((c=yyGet()) && state_map[c] != STATE_USER_VARIABLE_DELIMITER &&
c != (uchar) NAMES_SEP_CHAR) ;
yylval->lex_str=get_token(lex,yyLength());
+ if (lex->convert_set)
+ lex->convert_set->convert((char*) yylval->lex_str.str,lex->yytoklen);
if (state_map[c] == STATE_USER_VARIABLE_DELIMITER)
yySkip(); // Skipp end `
return(IDENT);
diff --git a/sql/sql_lex.h b/sql/sql_lex.h
index 10a99f920bd..e585ec65191 100644
--- a/sql/sql_lex.h
+++ b/sql/sql_lex.h
@@ -53,7 +53,10 @@ enum enum_sql_command {
SQLCOM_BEGIN, SQLCOM_LOAD_MASTER_TABLE, SQLCOM_CHANGE_MASTER,
SQLCOM_RENAME_TABLE, SQLCOM_BACKUP_TABLE, SQLCOM_RESTORE_TABLE,
SQLCOM_RESET, SQLCOM_PURGE, SQLCOM_SHOW_BINLOGS,
- SQLCOM_SHOW_OPEN_TABLES
+ SQLCOM_SHOW_OPEN_TABLES, SQLCOM_LOAD_MASTER_DATA,
+ SQLCOM_HA_OPEN, SQLCOM_HA_CLOSE, SQLCOM_HA_READ,
+ SQLCOM_SHOW_SLAVE_HOSTS, SQLCOM_MULTI_DELETE, SQLCOM_UNION_SELECT,
+ SQLCOM_SHOW_BINLOG_EVENTS
};
enum lex_states { STATE_START, STATE_CHAR, STATE_IDENT,
@@ -93,39 +96,70 @@ typedef struct st_lex_master_info
ulonglong pos;
} LEX_MASTER_INFO;
+
+enum sub_select_type {UNSPECIFIED_TYPE,UNION_TYPE, INTERSECT_TYPE, EXCEPT_TYPE};
+
+/* The state of the lex parsing for selects */
+
+typedef struct st_select_lex {
+ enum sub_select_type linkage;
+ uint select_number; /* For Item_select */
+ char *db,*db1,*table1,*db2,*table2; /* For outer join using .. */
+ Item *where,*having;
+ ha_rows select_limit,offset_limit;
+ ulong options;
+ List<List_item> expr_list;
+ List<List_item> when_list;
+ SQL_LIST order_list,table_list,group_list;
+ List<Item> item_list;
+ List<String> interval_list,use_index, *use_index_ptr, ignore_index, *ignore_index_ptr;
+ List<Item_func_match> ftfunc_list;
+ uint in_sum_expr, sort_default;
+ bool create_refs;
+ st_select_lex *next;
+} SELECT_LEX;
+
+
+class Set_option :public Sql_alloc {
+public:
+ const char *name;
+ Item *item;
+ uint name_length;
+ bool type; /* 1 if global */
+ Set_option(bool par_type, const char *par_name, uint length,
+ Item *par_item)
+ :name(par_name), item(par_item), name_length(length), type(par_type) {}
+};
+
+
/* The state of the lex parsing. This is saved in the THD struct */
typedef struct st_lex {
uint yylineno,yytoklen; /* Simulate lex */
LEX_YYSTYPE yylval;
+ SELECT_LEX select_lex, *select;
uchar *ptr,*tok_start,*tok_end,*end_of_query;
char *length,*dec,*change,*name;
- char *db,*db1,*table1,*db2,*table2; /* For outer join using .. */
char *backup_dir; /* For RESTORE/BACKUP */
char* to_log; /* For PURGE MASTER LOGS TO */
String *wild;
sql_exchange *exchange;
- ha_rows select_limit,offset_limit;
- List<List_item> expr_list;
- List<List_item> when_list;
- List<List_item> many_values;
List<key_part_spec> col_list;
List<Alter_drop> drop_list;
List<Alter_column> alter_list;
- List<String> interval_list,use_index,*use_index_ptr,
- ignore_index, *ignore_index_ptr;
+ List<String> interval_list;
List<st_lex_user> users_list;
List<LEX_COLUMN> columns;
List<Key> key_list;
List<create_field> create_list;
- List<Item> item_list,*insert_list,field_list,value_list;
- List<Item_func_match> ftfunc_list;
- SQL_LIST order_list,table_list,group_list,proc_list;
+ List<Item> *insert_list,field_list,value_list;
+ List<List_item> many_values;
+ List<Set_option> option_list;
+ SQL_LIST proc_list, auxilliary_table_list;
TYPELIB *interval;
create_field *last_field;
-
- Item *where,*having,*default_value;
+ Item *default_value;
CONVERT *convert_set;
LEX_USER *grant_user;
gptr yacc_yyss,yacc_yyvs;
@@ -135,16 +169,18 @@ typedef struct st_lex {
HA_CREATE_INFO create_info;
LEX_MASTER_INFO mi; // used by CHANGE MASTER
ulong thread_id,type;
- ulong options;
ulong gemini_spin_retries;
enum_sql_command sql_command;
enum lex_states next_state;
enum enum_duplicates duplicates;
enum enum_tx_isolation tx_isolation;
- uint in_sum_expr,grant,grant_tot_col,which_columns, sort_default;
+ enum enum_ha_read_modes ha_read_mode;
+ enum ha_rkey_function ha_rkey_mode;
+ enum enum_enable_or_disable alter_keys_onoff;
+ uint grant,grant_tot_col,which_columns;
thr_lock_type lock_option;
- bool create_refs,drop_primary,drop_if_exists,local_file;
- bool in_comment,ignore_space,verbose;
+ bool drop_primary,drop_if_exists,local_file;
+ bool in_comment,ignore_space,verbose,simple_alter, option_type;
} LEX;
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc
index ef2919c2e6c..928a62a397e 100644
--- a/sql/sql_parse.cc
+++ b/sql/sql_parse.cc
@@ -14,6 +14,10 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+#ifdef EMBEDDED_LIBRARY
+#define net_read_timeout net_read_timeout1
+#define net_write_timeout net_write_timeout1
+#endif
#include "mysql_priv.h"
#include "sql_acl.h"
@@ -26,7 +30,6 @@
#define SCRAMBLE_LENGTH 8
-
extern int yyparse(void);
extern "C" pthread_mutex_t THR_LOCK_keycache;
#ifdef SOLARIS
@@ -36,7 +39,6 @@ extern "C" int gethostname(char *name, int namelen);
static int check_for_max_user_connections(const char *user, int u_length,
const char *host);
static void decrease_user_connections(const char *user, const char *host);
-static bool check_table_access(THD *thd,uint want_access, TABLE_LIST *tables);
static bool check_db_used(THD *thd,TABLE_LIST *tables);
static bool check_merge_table_access(THD *thd, char *db, TABLE_LIST *tables);
static bool check_dup(THD *thd,const char *db,const char *name,
@@ -44,6 +46,8 @@ static bool check_dup(THD *thd,const char *db,const char *name,
static void mysql_init_query(THD *thd);
static void remove_escape(char *name);
static void refresh_status(void);
+static bool append_file_to_dir(char **filename_ptr, char *table_name);
+static int link_in_large_list_and_check_acl(THD *thd,LEX *lex,SQL_LIST *tables);
const char *any_db="*any*"; // Special symbol for check_access
@@ -51,13 +55,13 @@ const char *command_name[]={
"Sleep", "Quit", "Init DB", "Query", "Field List", "Create DB",
"Drop DB", "Refresh", "Shutdown", "Statistics", "Processlist",
"Connect","Kill","Debug","Ping","Time","Delayed_insert","Change user",
- "Binlog Dump","Table Dump", "Connect Out"
+ "Binlog Dump","Table Dump", "Connect Out", "Register Slave"
};
bool volatile abort_slave = 0;
#ifdef HAVE_OPENSSL
-extern VioSSLAcceptorFd* ssl_acceptor_fd;
+extern struct st_VioSSLAcceptorFd * ssl_acceptor_fd;
#endif /* HAVE_OPENSSL */
#ifdef __WIN__
@@ -421,9 +425,7 @@ check_connections(THD *thd)
DBUG_PRINT("info", ("Agreed to change IO layer to SSL") );
/* Do the SSL layering. */
DBUG_PRINT("info", ("IO layer change in progress..."));
- VioSocket* vio_socket = my_reinterpret_cast(VioSocket*)(net->vio);
- VioSSL* vio_ssl = ssl_acceptor_fd->accept(vio_socket);
- net->vio = my_reinterpret_cast(NetVio*) (vio_ssl);
+ net->vio = sslaccept(ssl_acceptor_fd, net->vio);
DBUG_PRINT("info", ("Reading user information over SSL layer"));
if ((pkt_len=my_net_read(net)) == packet_error ||
pkt_len < NORMAL_HANDSHAKE_SIZE)
@@ -702,19 +704,14 @@ err:
}
-
/* Execute one command from socket (query or simple command) */
bool do_command(THD *thd)
{
char *packet;
uint old_timeout,packet_length;
- bool error=0;
NET *net;
enum enum_server_command command;
- // commands which will always take a long time should be marked with
- // this so that they will not get logged to the slow query log
- bool slow_command=FALSE;
DBUG_ENTER("do_command");
net= &thd->net;
@@ -741,7 +738,21 @@ bool do_command(THD *thd)
vio_description(net->vio), command,
command_name[command]));
}
- net->timeout=old_timeout; /* Timeout */
+ net->timeout=old_timeout; // Timeout for writing
+ DBUG_RETURN(dispatch_command(command,thd, packet+1, packet_length));
+}
+
+
+bool dispatch_command(enum enum_server_command command, THD *thd,
+ char* packet, uint packet_length)
+{
+ NET *net= &thd->net;
+ bool error=0;
+ // commands which will always take a long time should be marked with
+ // this so that they will not get logged to the slow query log
+ bool slow_command=FALSE;
+ DBUG_ENTER("dispatch_command");
+
thd->command=command;
VOID(pthread_mutex_lock(&LOCK_thread_count));
thd->query_id=query_id;
@@ -750,23 +761,30 @@ bool do_command(THD *thd)
thread_running++;
VOID(pthread_mutex_unlock(&LOCK_thread_count));
thd->set_time();
- thd->lex.options=0; // We store status here
- switch(command) {
+ thd->lex.select_lex.options=0; // We store status here
+ switch (command) {
case COM_INIT_DB:
- if (!mysql_change_db(thd,packet+1))
+ if (!mysql_change_db(thd,packet))
mysql_log.write(thd,command,"%s",thd->db);
break;
+ case COM_REGISTER_SLAVE:
+ {
+ if(register_slave(thd, (uchar*)packet, packet_length))
+ send_error(&thd->net);
+ else
+ send_ok(&thd->net);
+ break;
+ }
case COM_TABLE_DUMP:
{
slow_command = TRUE;
- char* data = packet + 1;
- uint db_len = *data;
- uint tbl_len = *(data + db_len + 1);
+ uint db_len = *(uchar*)packet;
+ uint tbl_len = *(uchar*)(packet + db_len + 1);
char* db = sql_alloc(db_len + tbl_len + 2);
- memcpy(db, data + 1, db_len);
+ memcpy(db, packet + 1, db_len);
char* tbl_name = db + db_len;
*tbl_name++ = 0;
- memcpy(tbl_name, data + db_len + 2, tbl_len);
+ memcpy(tbl_name, packet + db_len + 2, tbl_len);
tbl_name[tbl_len] = 0;
if(mysql_table_dump(thd, db, tbl_name, -1))
send_error(&thd->net); // dump to NET
@@ -775,7 +793,7 @@ bool do_command(THD *thd)
}
case COM_CHANGE_USER:
{
- char *user= (char*) packet+1;
+ char *user= (char*) packet;
char *passwd= strend(user)+1;
char *db= strend(passwd)+1;
@@ -811,16 +829,16 @@ bool do_command(THD *thd)
case COM_QUERY:
{
- char *pos=packet+packet_length; // Point at end null
+ char *pos=packet-1+packet_length; // Point at end null
/* Remove garage at end of query */
while (packet_length > 0 && pos[-1] == ';')
{
pos--;
packet_length--;
}
- *pos=0;
- if (!(thd->query= (char*) thd->memdup((gptr) (packet+1),packet_length)))
+ if (!(thd->query= (char*) thd->memdup((gptr) (packet),packet_length+1)))
break;
+ thd->query[packet_length]=0;
thd->packet.shrink(net_buffer_length); // Reclaim some memory
if (!(specialflag & SPECIAL_NO_PRIOR))
my_pthread_setprio(pthread_self(),QUERY_PRIOR);
@@ -847,8 +865,8 @@ bool do_command(THD *thd)
break;
}
thd->free_list=0;
- table_list.name=table_list.real_name=thd->strdup(packet+1);
- thd->query=fields=thd->strdup(strend(packet+1)+1);
+ table_list.name=table_list.real_name=thd->strdup(packet);
+ thd->query=fields=thd->strdup(strend(packet)+1);
mysql_log.write(thd,command,"%s %s",table_list.real_name,fields);
remove_escape(table_list.real_name); // This can't have wildcards
@@ -870,7 +888,7 @@ bool do_command(THD *thd)
case COM_CREATE_DB:
{
- char *db=thd->strdup(packet+1);
+ char *db=thd->strdup(packet);
// null test to handle EOM
if (!db || !stripp_sp(db) || check_db_name(db))
{
@@ -879,13 +897,13 @@ bool do_command(THD *thd)
}
if (check_access(thd,CREATE_ACL,db,0,1))
break;
- mysql_log.write(thd,command,packet+1);
+ mysql_log.write(thd,command,packet);
mysql_create_db(thd,db,0);
break;
}
case COM_DROP_DB:
{
- char *db=thd->strdup(packet+1);
+ char *db=thd->strdup(packet);
// null test to handle EOM
if (!db || !stripp_sp(db) || check_db_name(db))
{
@@ -908,13 +926,13 @@ bool do_command(THD *thd)
ulong pos;
ushort flags;
uint32 slave_server_id;
- pos = uint4korr(packet + 1);
- flags = uint2korr(packet + 5);
+ pos = uint4korr(packet);
+ flags = uint2korr(packet + 4);
pthread_mutex_lock(&LOCK_server_id);
- kill_zombie_dump_threads(slave_server_id = uint4korr(packet+7));
+ kill_zombie_dump_threads(slave_server_id = uint4korr(packet+6));
thd->server_id = slave_server_id;
pthread_mutex_unlock(&LOCK_server_id);
- mysql_binlog_send(thd, thd->strdup(packet + 11), pos, flags);
+ mysql_binlog_send(thd, thd->strdup(packet + 10), pos, flags);
// fake COM_QUIT -- if we get here, the thread needs to terminate
error = TRUE;
net->error = 0;
@@ -922,7 +940,7 @@ bool do_command(THD *thd)
}
case COM_REFRESH:
{
- uint options=(uchar) packet[1];
+ uint options=(uchar) packet[0];
if (check_access(thd,RELOAD_ACL,any_db))
break;
mysql_log.write(thd,command,NullS);
@@ -981,7 +999,7 @@ bool do_command(THD *thd)
break;
case COM_PROCESS_KILL:
{
- ulong id=(ulong) uint4korr(packet+1);
+ ulong id=(ulong) uint4korr(packet);
kill_one_thread(thd,id);
break;
}
@@ -1018,7 +1036,7 @@ bool do_command(THD *thd)
thd->proc_info="logging slow query";
if ((ulong) (thd->start_time - thd->time_after_lock) > long_query_time ||
- ((thd->lex.options &
+ ((thd->lex.select_lex.options &
(QUERY_NO_INDEX_USED | QUERY_NO_GOOD_INDEX_USED)) &&
(specialflag & SPECIAL_LONG_LOG_FORMAT)))
{
@@ -1049,7 +1067,8 @@ mysql_execute_command(void)
int res=0;
THD *thd=current_thd;
LEX *lex= &thd->lex;
- TABLE_LIST *tables=(TABLE_LIST*) lex->table_list.first;
+ TABLE_LIST *tables=(TABLE_LIST*) lex->select->table_list.first;
+ SELECT_LEX *select_lex = lex->select;
DBUG_ENTER("mysql_execute_command");
if(table_rules_on && thd->slave_thread && tables && !tables_ok(thd,tables))
@@ -1061,7 +1080,7 @@ mysql_execute_command(void)
case SQLCOM_SELECT:
{
select_result *result;
- if (lex->options & SELECT_DESCRIBE)
+ if (select_lex->options & SELECT_DESCRIBE)
lex->exchange=0;
if (tables)
{
@@ -1079,10 +1098,12 @@ mysql_execute_command(void)
break; // Error message is given
}
- thd->offset_limit=lex->offset_limit;
- thd->select_limit=lex->select_limit+lex->offset_limit;
- if (thd->select_limit < lex->select_limit)
+ thd->offset_limit=select_lex->offset_limit;
+ thd->select_limit=select_lex->select_limit+select_lex->offset_limit;
+ if (thd->select_limit < select_lex->select_limit)
thd->select_limit= HA_POS_ERROR; // no limit
+ if (thd->select_limit == HA_POS_ERROR)
+ select_lex->options&= ~OPTION_FOUND_ROWS;
if (lex->exchange)
{
@@ -1107,8 +1128,8 @@ mysql_execute_command(void)
{
res= -1;
#ifdef DELETE_ITEMS
- delete lex->having;
- delete lex->where;
+ delete select_lex->having;
+ delete select_lex->where;
#endif
break;
}
@@ -1126,22 +1147,22 @@ mysql_execute_command(void)
if (!(res=open_and_lock_tables(thd,tables)))
{
- res=mysql_select(thd,tables,lex->item_list,
- lex->where,
- lex->ftfunc_list,
- (ORDER*) lex->order_list.first,
- (ORDER*) lex->group_list.first,
- lex->having,
+ res=mysql_select(thd,tables,select_lex->item_list,
+ select_lex->where,
+ select_lex->ftfunc_list,
+ (ORDER*) select_lex->order_list.first,
+ (ORDER*) select_lex->group_list.first,
+ select_lex->having,
(ORDER*) lex->proc_list.first,
- lex->options | thd->options,
+ select_lex->options | thd->options,
result);
if (res)
result->abort();
}
delete result;
#ifdef DELETE_ITEMS
- delete lex->having;
- delete lex->where;
+ delete select_lex->having;
+ delete select_lex->where;
#endif
break;
}
@@ -1152,6 +1173,20 @@ mysql_execute_command(void)
res = purge_master_logs(thd, lex->to_log);
break;
}
+ case SQLCOM_SHOW_SLAVE_HOSTS:
+ {
+ if(check_access(thd, FILE_ACL, any_db))
+ goto error;
+ res = show_slave_hosts(thd);
+ break;
+ }
+ case SQLCOM_SHOW_BINLOG_EVENTS:
+ {
+ if(check_access(thd, FILE_ACL, any_db))
+ goto error;
+ res = show_binlog_events(thd);
+ break;
+ }
case SQLCOM_BACKUP_TABLE:
{
if (check_db_used(thd,tables) ||
@@ -1192,6 +1227,13 @@ mysql_execute_command(void)
res = show_binlog_info(thd);
break;
}
+
+ case SQLCOM_LOAD_MASTER_DATA: // sync with master
+ if(check_process_priv(thd))
+ goto error;
+ res = load_master_data(thd);
+ break;
+
case SQLCOM_LOAD_MASTER_TABLE:
if (!tables->db)
@@ -1214,9 +1256,7 @@ mysql_execute_command(void)
break;
}
- thd->last_nx_table = tables->real_name;
- thd->last_nx_db = tables->db;
- if (fetch_nx_table(thd, &glob_mi))
+ if (fetch_nx_table(thd, tables->db, tables->real_name, &glob_mi, 0))
break; // fetch_nx_table did send the error to the client
send_ok(&thd->net);
break;
@@ -1245,7 +1285,14 @@ mysql_execute_command(void)
res=0;
break;
}
- if (lex->item_list.elements) // With select
+ /* Fix names if symlinked tables */
+ if (append_file_to_dir(&lex->create_info.data_file_name, tables->name) ||
+ append_file_to_dir(&lex->create_info.index_file_name, tables->name))
+ {
+ res=-1;
+ break;
+ }
+ if (select_lex->item_list.elements) // With select
{
select_result *result;
@@ -1263,9 +1310,9 @@ mysql_execute_command(void)
for (table = tables->next ; table ; table=table->next)
table->lock_type= lex->lock_option;
}
- thd->offset_limit=lex->offset_limit;
- thd->select_limit=lex->select_limit+lex->offset_limit;
- if (thd->select_limit < lex->select_limit)
+ thd->offset_limit=select_lex->offset_limit;
+ thd->select_limit=select_lex->select_limit+select_lex->offset_limit;
+ if (thd->select_limit < select_lex->select_limit)
thd->select_limit= HA_POS_ERROR; // No limit
if (!(res=open_and_lock_tables(thd,tables->next)))
@@ -1274,16 +1321,16 @@ mysql_execute_command(void)
tables->real_name, &lex->create_info,
lex->create_list,
lex->key_list,
- lex->item_list,lex->duplicates)))
+ select_lex->item_list,lex->duplicates)))
{
- res=mysql_select(thd,tables->next,lex->item_list,
- lex->where,
- lex->ftfunc_list,
- (ORDER*) lex->order_list.first,
- (ORDER*) lex->group_list.first,
- lex->having,
+ res=mysql_select(thd,tables->next,select_lex->item_list,
+ select_lex->where,
+ select_lex->ftfunc_list,
+ (ORDER*) select_lex->order_list.first,
+ (ORDER*) select_lex->group_list.first,
+ select_lex->having,
(ORDER*) lex->proc_list.first,
- lex->options | thd->options,
+ select_lex->options | thd->options,
result);
if (res)
result->abort();
@@ -1338,10 +1385,10 @@ mysql_execute_command(void)
}
if (!tables->db)
tables->db=thd->db;
- if (!lex->db)
- lex->db=tables->db;
+ if (!select_lex->db)
+ select_lex->db=tables->db;
if (check_access(thd,ALTER_ACL,tables->db,&tables->grant.privilege) ||
- check_access(thd,INSERT_ACL | CREATE_ACL,lex->db,&priv) ||
+ check_access(thd,INSERT_ACL | CREATE_ACL,select_lex->db,&priv) ||
check_merge_table_access(thd, tables->db,
(TABLE_LIST *)
lex->create_info.merge_list.first))
@@ -1357,22 +1404,25 @@ mysql_execute_command(void)
TABLE_LIST tmp_table;
bzero((char*) &tmp_table,sizeof(tmp_table));
tmp_table.real_name=lex->name;
- tmp_table.db=lex->db;
+ tmp_table.db=select_lex->db;
tmp_table.grant.privilege=priv;
if (check_grant(thd,INSERT_ACL | CREATE_ACL,tables))
goto error;
}
}
+ /* Don't yet allow changing of symlinks with ALTER TABLE */
+ lex->create_info.data_file_name=lex->create_info.index_file_name=0;
/* ALTER TABLE ends previous transaction */
if (end_active_trans(thd))
res= -1;
else
- res= mysql_alter_table(thd, lex->db, lex->name,
+ res= mysql_alter_table(thd, select_lex->db, lex->name,
&lex->create_info,
tables, lex->create_list,
lex->key_list, lex->drop_list, lex->alter_list,
- (ORDER *) lex->order_list.first,
- lex->drop_primary, lex->duplicates);
+ (ORDER *) select_lex->order_list.first,
+ lex->drop_primary, lex->duplicates,
+ lex->alter_keys_onoff, lex->simple_alter);
break;
}
#endif
@@ -1490,21 +1540,22 @@ mysql_execute_command(void)
goto error;
if (grant_option && check_grant(thd,UPDATE_ACL,tables))
goto error;
- if (lex->item_list.elements != lex->value_list.elements)
+ if (select_lex->item_list.elements != lex->value_list.elements)
{
send_error(&thd->net,ER_WRONG_VALUE_COUNT);
DBUG_VOID_RETURN;
}
res = mysql_update(thd,tables,
- lex->item_list,
+ select_lex->item_list,
lex->value_list,
- lex->where,
- lex->select_limit,
+ select_lex->where,
+ (ORDER *) select_lex->order_list.first,
+ select_lex->select_limit,
lex->duplicates,
lex->lock_option);
#ifdef DELETE_ITEMS
- delete lex->where;
+ delete select_lex->where;
#endif
break;
case SQLCOM_INSERT:
@@ -1548,9 +1599,9 @@ mysql_execute_command(void)
}
select_result *result;
- thd->offset_limit=lex->offset_limit;
- thd->select_limit=lex->select_limit+lex->offset_limit;
- if (thd->select_limit < lex->select_limit)
+ thd->offset_limit=select_lex->offset_limit;
+ thd->select_limit=select_lex->select_limit+select_lex->offset_limit;
+ if (thd->select_limit < select_lex->select_limit)
thd->select_limit= HA_POS_ERROR; // No limit
if (check_dup(thd,tables->db,tables->real_name,tables->next))
@@ -1570,14 +1621,14 @@ mysql_execute_command(void)
lex->sql_command == SQLCOM_REPLACE_SELECT ?
DUP_REPLACE : DUP_IGNORE)))
{
- res=mysql_select(thd,tables->next,lex->item_list,
- lex->where,
- lex->ftfunc_list,
- (ORDER*) lex->order_list.first,
- (ORDER*) lex->group_list.first,
- lex->having,
+ res=mysql_select(thd,tables->next,select_lex->item_list,
+ select_lex->where,
+ select_lex->ftfunc_list,
+ (ORDER*) select_lex->order_list.first,
+ (ORDER*) select_lex->group_list.first,
+ select_lex->having,
(ORDER*) lex->proc_list.first,
- lex->options | thd->options,
+ select_lex->options | thd->options,
result);
delete result;
}
@@ -1585,14 +1636,14 @@ mysql_execute_command(void)
res= -1;
}
#ifdef DELETE_ITEMS
- delete lex->having;
- delete lex->where;
+ delete select_lex->having;
+ delete select_lex->where;
#endif
break;
}
case SQLCOM_TRUNCATE:
- lex->where=0;
- lex->select_limit=HA_POS_ERROR;
+ select_lex->where=0;
+ select_lex->select_limit=HA_POS_ERROR;
/* Fall through */
case SQLCOM_DELETE:
{
@@ -1606,8 +1657,100 @@ mysql_execute_command(void)
if (lex->sql_command == SQLCOM_TRUNCATE && end_active_trans(thd))
res= -1;
else
- res = mysql_delete(thd,tables,lex->where,lex->select_limit,
- lex->lock_option, lex->options);
+ res = mysql_delete(thd,tables, select_lex->where,
+ (ORDER*) select_lex->order_list.first,
+ select_lex->select_limit, lex->lock_option,
+ select_lex->options);
+ break;
+ }
+ case SQLCOM_MULTI_DELETE:
+ {
+ TABLE_LIST *aux_tables=(TABLE_LIST *)thd->lex.auxilliary_table_list.first;
+ TABLE_LIST *auxi;
+ uint table_count=0;
+ multi_delete *result;
+
+ /* sql_yacc guarantees that tables and aux_tables are not zero */
+ if (check_db_used(thd, tables) || check_db_used(thd,aux_tables) ||
+ check_table_access(thd,SELECT_ACL, tables) ||
+ check_table_access(thd,DELETE_ACL, aux_tables))
+ goto error;
+ if ((thd->options & OPTION_SAFE_UPDATES) && !select_lex->where)
+ {
+ send_error(&thd->net,ER_UPDATE_WITHOUT_KEY_IN_SAFE_MODE);
+ goto error;
+ }
+ for (auxi=(TABLE_LIST*) aux_tables ; auxi ; auxi=auxi->next)
+ {
+ table_count++;
+ /* All tables in aux_tables must be found in FROM PART */
+ TABLE_LIST *walk;
+ for (walk=(TABLE_LIST*) tables ; walk ; walk=walk->next)
+ {
+ if (!strcmp(auxi->real_name,walk->real_name) &&
+ !strcmp(walk->db,auxi->db))
+ break;
+ }
+ if (!walk)
+ {
+ net_printf(&thd->net,ER_NONUNIQ_TABLE,auxi->real_name);
+ goto error;
+ }
+ auxi->lock_type=walk->lock_type=TL_WRITE;
+ auxi->table= (TABLE *) walk; // Remember corresponding table
+ }
+ tables->grant.want_privilege=(SELECT_ACL & ~tables->grant.privilege);
+ if (add_item_to_list(new Item_null()))
+ {
+ res= -1;
+ break;
+ }
+ thd->proc_info="init";
+ if ((res=open_and_lock_tables(thd,tables)))
+ break;
+ /* Fix tables-to-be-deleted-from list to point at opened tables */
+ for (auxi=(TABLE_LIST*) aux_tables ; auxi ; auxi=auxi->next)
+ auxi->table= ((TABLE_LIST*) auxi->table)->table;
+ if ((result=new multi_delete(thd,aux_tables,lex->lock_option,
+ table_count)) && ! thd->fatal_error)
+ {
+ res=mysql_select(thd,tables,select_lex->item_list,
+ select_lex->where,select_lex->ftfunc_list,
+ (ORDER *)NULL,(ORDER *)NULL,(Item *)NULL,
+ (ORDER *)NULL,
+ select_lex->options | thd->options |
+ SELECT_NO_JOIN_CACHE,
+ result);
+ }
+ else
+ res= -1; // Error is not sent
+ delete result;
+ close_thread_tables(thd);
+ break;
+ }
+ case SQLCOM_UNION_SELECT:
+ {
+ SQL_LIST *total=(SQL_LIST *) thd->calloc(sizeof(SQL_LIST));
+ if (select_lex->options & SELECT_DESCRIBE)
+ lex->exchange=0;
+ if ((res = link_in_large_list_and_check_acl(thd,lex,total)) == -1)
+ {
+ res=0;
+ break;
+ }
+ if (res &&
+ (res=check_access(thd,
+ lex->exchange ? SELECT_ACL | FILE_ACL : SELECT_ACL,
+ any_db)))
+ {
+ res=0;
+ break;
+ }
+ if (!(res=open_and_lock_tables(thd,(TABLE_LIST *)total->first)))
+ {
+ res=mysql_union(thd,lex, select_lex->select_number+1);
+ if (res==-1) res=0;
+ }
break;
}
case SQLCOM_DROP_TABLE:
@@ -1634,7 +1777,7 @@ mysql_execute_command(void)
break;
case SQLCOM_SHOW_DATABASES:
#if defined(DONT_ALLOW_SHOW_COMMANDS)
- send_error(&thd->net,ER_NOT_ALLOWED_COMMAND); /* purecov: inspected */
+ send_error(&thd->net,ER_NOT_ALLOWED_COMMAND); /* purecov: inspected */
DBUG_VOID_RETURN;
#else
if ((specialflag & SPECIAL_SKIP_SHOW_DB) &&
@@ -1670,13 +1813,12 @@ mysql_execute_command(void)
#endif
case SQLCOM_SHOW_TABLES:
/* FALL THROUGH */
- case SQLCOM_SHOW_OPEN_TABLES:
#ifdef DONT_ALLOW_SHOW_COMMANDS
send_error(&thd->net,ER_NOT_ALLOWED_COMMAND); /* purecov: inspected */
DBUG_VOID_RETURN;
#else
{
- char *db=lex->db ? lex->db : thd->db;
+ char *db=select_lex->db ? select_lex->db : thd->db;
if (!db)
{
send_error(&thd->net,ER_NO_DB_ERROR); /* purecov: inspected */
@@ -1691,18 +1833,18 @@ mysql_execute_command(void)
if (check_access(thd,SELECT_ACL,db,&thd->col_access))
goto error; /* purecov: inspected */
/* grant is checked in mysqld_show_tables */
- if (lex->sql_command == SQLCOM_SHOW_OPEN_TABLES)
- res= mysqld_show_open_tables(thd,db,
- (lex->wild ? lex->wild->ptr() : NullS));
- else if (lex->options & SELECT_DESCRIBE)
+ if (select_lex->options & SELECT_DESCRIBE)
res= mysqld_extend_show_tables(thd,db,
- (lex->wild ? lex->wild->ptr() : NullS));
+ (lex->wild ? lex->wild->ptr() : NullS));
else
res= mysqld_show_tables(thd,db,
(lex->wild ? lex->wild->ptr() : NullS));
break;
}
#endif
+ case SQLCOM_SHOW_OPEN_TABLES:
+ res= mysqld_show_open_tables(thd,(lex->wild ? lex->wild->ptr() : NullS));
+ break;
case SQLCOM_SHOW_FIELDS:
#ifdef DONT_ALLOW_SHOW_COMMANDS
send_error(&thd->net,ER_NOT_ALLOWED_COMMAND); /* purecov: inspected */
@@ -1756,7 +1898,7 @@ mysql_execute_command(void)
}
#endif
case SQLCOM_CHANGE_DB:
- mysql_change_db(thd,lex->db);
+ mysql_change_db(thd,select_lex->db);
break;
case SQLCOM_LOAD:
{
@@ -1780,10 +1922,10 @@ mysql_execute_command(void)
case SQLCOM_SET_OPTION:
{
uint org_options=thd->options;
- thd->options=lex->options;
+ thd->options=select_lex->options;
thd->update_lock_default= ((thd->options & OPTION_LOW_PRIORITY_UPDATES) ?
TL_WRITE_LOW_PRIORITY : TL_WRITE);
- thd->default_select_limit=lex->select_limit;
+ thd->default_select_limit=select_lex->select_limit;
thd->tx_isolation=lex->tx_isolation;
if (thd->gemini_spin_retries != lex->gemini_spin_retries)
{
@@ -1794,7 +1936,7 @@ mysql_execute_command(void)
thd->options,(long) thd->default_select_limit));
/* Check if auto_commit mode changed */
- if ((org_options ^ lex->options) & OPTION_NOT_AUTO_COMMIT)
+ if ((org_options ^ select_lex->options) & OPTION_NOT_AUTO_COMMIT)
{
if ((org_options & OPTION_NOT_AUTO_COMMIT))
{
@@ -1842,6 +1984,8 @@ mysql_execute_command(void)
}
if (check_db_used(thd,tables) || end_active_trans(thd))
goto error;
+ if (grant_option && check_grant(thd,SELECT_ACL | INSERT_ACL | UPDATE_ACL | DELETE_ACL,tables))
+ goto error;
thd->in_lock_tables=1;
if (!(res=open_and_lock_tables(thd,tables)))
{
@@ -1902,7 +2046,7 @@ mysql_execute_command(void)
if (tables && !tables->db)
tables->db=thd->db;
if (check_access(thd, lex->grant | lex->grant_tot_col | GRANT_ACL,
- tables && tables->db ? tables->db : lex->db,
+ tables && tables->db ? tables->db : select_lex->db,
tables ? &tables->grant.privilege : 0,
tables ? 0 : 1))
goto error;
@@ -1954,7 +2098,7 @@ mysql_execute_command(void)
res=1;
}
else
- res = mysql_grant(thd, lex->db, lex->users_list, lex->grant,
+ res = mysql_grant(thd, select_lex->db, lex->users_list, lex->grant,
lex->sql_command == SQLCOM_REVOKE);
if(!res)
{
@@ -1988,6 +2132,24 @@ mysql_execute_command(void)
res = mysql_show_grants(thd,lex->grant_user);
}
break;
+ case SQLCOM_HA_OPEN:
+ if (check_db_used(thd,tables) || check_table_access(thd,SELECT_ACL, tables))
+ goto error;
+ res = mysql_ha_open(thd, tables);
+ break;
+ case SQLCOM_HA_CLOSE:
+ if (check_db_used(thd,tables))
+ goto error;
+ res = mysql_ha_close(thd, tables);
+ break;
+ case SQLCOM_HA_READ:
+ if (check_db_used(thd,tables) || check_table_access(thd,SELECT_ACL, tables))
+ goto error;
+ res = mysql_ha_read(thd, tables, lex->ha_read_mode, lex->backup_dir,
+ lex->insert_list, lex->ha_rkey_mode, select_lex->where,
+ select_lex->select_limit, select_lex->offset_limit);
+ break;
+
case SQLCOM_BEGIN:
if (end_active_trans(thd))
{
@@ -2116,7 +2278,7 @@ bool check_process_priv(THD *thd)
** in the table list for GRANT checking
*/
-static bool
+bool
check_table_access(THD *thd,uint want_access,TABLE_LIST *tables)
{
uint found=0,found_access=0;
@@ -2142,10 +2304,8 @@ check_table_access(THD *thd,uint want_access,TABLE_LIST *tables)
return TRUE; // Access denied
}
if (grant_option)
- {
- want_access &= ~EXTRA_ACL; // Remove SHOW attribute
- return check_grant(thd,want_access,org_tables);
- }
+ return check_grant(thd,want_access & ~EXTRA_ACL,org_tables,
+ test(want_access & EXTRA_ACL));
return FALSE;
}
@@ -2248,13 +2408,13 @@ static void
mysql_init_query(THD *thd)
{
DBUG_ENTER("mysql_init_query");
- thd->lex.item_list.empty();
+ thd->lex.select_lex.item_list.empty();
thd->lex.value_list.empty();
- thd->lex.table_list.elements=0;
+ thd->lex.select_lex.table_list.elements=0;
thd->free_list=0;
-
- thd->lex.table_list.first=0;
- thd->lex.table_list.next= (byte**) &thd->lex.table_list.first;
+ thd->lex.select = &thd->lex.select_lex;
+ thd->lex.select_lex.table_list.first=0;
+ thd->lex.select_lex.table_list.next= (byte**) &thd->lex.select_lex.table_list.first;
thd->fatal_error=0; // Safety
thd->last_insert_id_used=thd->query_start_used=thd->insert_id_used=0;
thd->sent_row_count=thd->examined_row_count=0;
@@ -2264,19 +2424,35 @@ mysql_init_query(THD *thd)
void
mysql_init_select(LEX *lex)
{
- lex->where=lex->having=0;
- lex->select_limit=current_thd->default_select_limit;
- lex->offset_limit=0L;
- lex->options=0;
- lex->exchange = 0;
+ SELECT_LEX *select_lex = lex->select;
+ select_lex->where=select_lex->having=0;
+ select_lex->select_limit=current_thd->default_select_limit;
+ select_lex->offset_limit=0L;
+ select_lex->options=0; select_lex->linkage=UNSPECIFIED_TYPE;
+ select_lex->select_number = 0; lex->exchange = 0;
lex->proc_list.first=0;
- lex->order_list.elements=lex->group_list.elements=0;
- lex->order_list.first=0;
- lex->order_list.next= (byte**) &lex->order_list.first;
- lex->group_list.first=0;
- lex->group_list.next= (byte**) &lex->group_list.first;
+ select_lex->order_list.elements=select_lex->group_list.elements=0;
+ select_lex->order_list.first=0;
+ select_lex->order_list.next= (byte**) &select_lex->order_list.first;
+ select_lex->group_list.first=0;
+ select_lex->group_list.next= (byte**) &select_lex->group_list.first;
+ select_lex->next = (SELECT_LEX *)NULL;
}
+void
+mysql_new_select(LEX *lex)
+{
+ uint select_no=lex->select->select_number;
+ SELECT_LEX *select_lex = (SELECT_LEX *)sql_calloc(sizeof(SELECT_LEX));
+ lex->select->next=select_lex;
+ lex->select=select_lex; lex->select->select_number = ++select_no;
+ lex->select->item_list = lex->select_lex.item_list;
+ lex->select->item_list.empty();
+ lex->select->table_list = lex->select_lex.table_list;
+ lex->select->table_list.elements=0;
+ lex->select->table_list.first=0;
+ lex->select->table_list.next= (byte**) &lex->select->table_list.first;
+}
void
mysql_parse(THD *thd,char *inBuf,uint length)
@@ -2694,7 +2870,7 @@ TABLE_LIST *add_table_to_list(Table_ident *table, LEX_STRING *alias,
if (flags != TL_IGNORE)
{
- for (TABLE_LIST *tables=(TABLE_LIST*) thd->lex.table_list.first ; tables ;
+ for (TABLE_LIST *tables=(TABLE_LIST*) thd->lex.select->table_list.first ; tables ;
tables=tables->next)
{
if (!strcmp(alias_str,tables->name) &&
@@ -2706,10 +2882,46 @@ TABLE_LIST *add_table_to_list(Table_ident *table, LEX_STRING *alias,
}
}
}
- link_in_list(&thd->lex.table_list,(byte*) ptr,(byte**) &ptr->next);
+ link_in_list(&thd->lex.select->table_list,(byte*) ptr,(byte**) &ptr->next);
DBUG_RETURN(ptr);
}
+static int link_in_large_list_and_check_acl(THD *thd,LEX *lex,SQL_LIST *tables)
+{
+ SELECT_LEX *sl; const char *current_db=thd->db ? thd->db : "";
+ for (sl=&lex->select_lex;sl;sl=sl->next)
+ {
+ if ((lex->sql_command == SQLCOM_UNION_SELECT) && (sl->order_list.first != (byte *)NULL) && (sl->next != (st_select_lex *)NULL))
+ {
+ net_printf(&thd->net,ER_ILLEGAL_GRANT_FOR_TABLE); // correct error message will come here; only last SELECt can have ORDER BY
+ return -1;
+ }
+ if (sl->table_list.first == (byte *)NULL) continue;
+ TABLE_LIST *cursor,*aux=(TABLE_LIST*) sl->table_list.first;
+ if (aux)
+ {
+ if (check_table_access(thd, lex->exchange ? SELECT_ACL | FILE_ACL : SELECT_ACL , aux))
+ return -1;
+ for (;aux;aux=aux->next)
+ {
+ if (!aux->db)
+ aux->db=(char *)current_db;
+ for (cursor=(TABLE_LIST *)tables->first;cursor;cursor=cursor->next)
+ if (!strcmp(cursor->db,aux->db) && (!strcmp(cursor->real_name,aux->real_name)))
+ break;
+ if (!cursor || !tables->first)
+ {
+ aux->lock_type= lex->lock_option;
+ if (!tables->next)
+ tables->next= (byte**) &tables->first;
+ link_in_list(tables,(byte*)aux,(byte**) &aux->next);
+ }
+ }
+ }
+ }
+ return (tables->first) ? 0 : 1;
+}
+
void add_join_on(TABLE_LIST *b,Item *expr)
{
if (!b->on_expr)
@@ -2826,3 +3038,29 @@ static void refresh_status(void)
pthread_mutex_unlock(&LOCK_status);
pthread_mutex_unlock(&THR_LOCK_keycache);
}
+
+
+ /* If pointer is not a null pointer, append filename to it */
+
+static bool append_file_to_dir(char **filename_ptr, char *table_name)
+{
+ char buff[FN_REFLEN],*ptr;
+ if (!*filename_ptr)
+ return 0; // nothing to do
+
+ /* Check that the filename is not too long and it's a hard path */
+ if (strlen(*filename_ptr)+strlen(table_name) >= FN_REFLEN-1 ||
+ !test_if_hard_path(*filename_ptr))
+ {
+ my_error(ER_WRONG_TABLE_NAME, MYF(0), *filename_ptr);
+ return 1;
+ }
+ /* Fix is using unix filename format on dos */
+ strmov(buff,*filename_ptr);
+ convert_dirname(buff);
+ if (!(ptr=sql_alloc(strlen(buff)+strlen(table_name)+1)))
+ return 1; // End of memory
+ *filename_ptr=ptr;
+ strxmov(ptr,buff,table_name,NullS);
+ return 0;
+}
diff --git a/sql/sql_repl.cc b/sql/sql_repl.cc
index 7ddf04f1b9a..0c835ee4e57 100644
--- a/sql/sql_repl.cc
+++ b/sql/sql_repl.cc
@@ -21,17 +21,48 @@
#include "sql_repl.h"
#include "sql_acl.h"
#include "log_event.h"
+#include "mini_client.h"
#include <thr_alarm.h>
#include <my_dir.h>
+#define SLAVE_LIST_CHUNK 128
+
extern const char* any_db;
extern pthread_handler_decl(handle_slave,arg);
+HASH slave_list;
+
+static uint32* slave_list_key(SLAVE_INFO* si, uint* len,
+ my_bool not_used __attribute__((unused)))
+{
+ *len = 4;
+ return &si->server_id;
+}
+
+static void slave_info_free(void *s)
+{
+ my_free((byte*)s, MYF(MY_WME));
+}
+
+void init_slave_list()
+{
+ hash_init(&slave_list, SLAVE_LIST_CHUNK, 0, 0,
+ (hash_get_key) slave_list_key, slave_info_free, 0);
+ pthread_mutex_init(&LOCK_slave_list, MY_MUTEX_INIT_FAST);
+}
+
+void end_slave_list()
+{
+ pthread_mutex_lock(&LOCK_slave_list);
+ hash_free(&slave_list);
+ pthread_mutex_unlock(&LOCK_slave_list);
+ pthread_mutex_destroy(&LOCK_slave_list);
+}
static int fake_rotate_event(NET* net, String* packet, char* log_file_name,
const char**errmsg)
{
- char header[LOG_EVENT_HEADER_LEN];
+ char header[LOG_EVENT_HEADER_LEN], buf[ROTATE_HEADER_LEN];
memset(header, 0, 4); // when does not matter
header[EVENT_TYPE_OFFSET] = ROTATE_EVENT;
char* p = strrchr(log_file_name, FN_LIBCHAR);
@@ -42,10 +73,14 @@ static int fake_rotate_event(NET* net, String* packet, char* log_file_name,
p = log_file_name;
uint ident_len = (uint) strlen(p);
- ulong event_len = ident_len + sizeof(header);
- int4store(header + EVENT_TYPE_OFFSET + 1, server_id);
+ ulong event_len = ident_len + ROTATE_EVENT_OVERHEAD;
+ int4store(header + SERVER_ID_OFFSET, server_id);
int4store(header + EVENT_LEN_OFFSET, event_len);
+ int2store(header + FLAGS_OFFSET, 0);
+ int4store(header + LOG_SEQ_OFFSET, 0);
packet->append(header, sizeof(header));
+ int8store(buf, 4); // tell slave to skip magic number
+ packet->append(buf, ROTATE_HEADER_LEN);
packet->append(p,ident_len);
if(my_net_write(net, (char*)packet->ptr(), packet->length()))
{
@@ -55,6 +90,55 @@ static int fake_rotate_event(NET* net, String* packet, char* log_file_name,
return 0;
}
+int register_slave(THD* thd, uchar* packet, uint packet_length)
+{
+ uint len;
+ SLAVE_INFO* si, *old_si;
+ int res = 1;
+ uchar* p = packet, *p_end = packet + packet_length;
+
+ if(check_access(thd, FILE_ACL, any_db))
+ return 1;
+
+ if(!(si = (SLAVE_INFO*)my_malloc(sizeof(SLAVE_INFO), MYF(MY_WME))))
+ goto err;
+
+ si->server_id = uint4korr(p);
+ p += 4;
+ len = (uint)*p++;
+ if(p + len > p_end || len > sizeof(si->host) - 1)
+ goto err;
+ memcpy(si->host, p, len);
+ si->host[len] = 0;
+ p += len;
+ len = *p++;
+ if(p + len > p_end || len > sizeof(si->user) - 1)
+ goto err;
+ memcpy(si->user, p, len);
+ si->user[len] = 0;
+ p += len;
+ len = *p++;
+ if(p + len > p_end || len > sizeof(si->password) - 1)
+ goto err;
+ memcpy(si->password, p, len);
+ si->password[len] = 0;
+ p += len;
+ si->port = uint2korr(p);
+ pthread_mutex_lock(&LOCK_slave_list);
+
+ if((old_si = (SLAVE_INFO*)hash_search(&slave_list,
+ (byte*)&si->server_id, 4)))
+ hash_delete(&slave_list, (byte*)old_si);
+
+ res = hash_insert(&slave_list, (byte*)si);
+ pthread_mutex_unlock(&LOCK_slave_list);
+ return res;
+err:
+ if(si)
+ my_free((byte*)si, MYF(MY_WME));
+ return res;
+}
+
static int send_file(THD *thd)
{
@@ -299,7 +383,8 @@ void mysql_binlog_send(THD* thd, char* log_ident, ulong pos, ushort flags)
if (pos < 4)
{
- errmsg = "Client requested master to start repliction from impossible position.\n";
+ errmsg = "Client requested master to start repliction from \
+impossible position";
goto err;
}
@@ -613,7 +698,7 @@ void reset_slave()
pthread_mutex_unlock(&LOCK_slave);
end_master_info(&glob_mi);
- fn_format(fname, master_info_file, mysql_data_home, "", 4+16+32);
+ fn_format(fname, master_info_file, mysql_data_home, "", 4+32);
if(my_stat(fname, &stat_area, MYF(0)))
if(my_delete(fname, MYF(MY_WME)))
return;
@@ -744,6 +829,149 @@ void reset_master()
}
+
+int show_binlog_events(THD* thd)
+{
+ DBUG_ENTER("show_binlog_events");
+ List<Item> field_list;
+ const char* errmsg = 0;
+ IO_CACHE log;
+ File file = -1;
+
+ Log_event::init_show_field_list(&field_list);
+ if (send_fields(thd, field_list, 1))
+ DBUG_RETURN(-1);
+
+ if (mysql_bin_log.is_open())
+ {
+ LOG_INFO linfo;
+ char search_file_name[FN_REFLEN];
+ LEX_MASTER_INFO* lex_mi = &thd->lex.mi;
+ uint event_count, limit_start, limit_end;
+ const char* log_file_name = lex_mi->log_file_name;
+ Log_event* ev;
+ ulong pos = (ulong) lex_mi->pos;
+
+ limit_start = thd->lex.select->offset_limit;
+ limit_end = thd->lex.select->select_limit + limit_start;
+
+ if (log_file_name)
+ mysql_bin_log.make_log_name(search_file_name, log_file_name);
+ else
+ search_file_name[0] = 0;
+
+ linfo.index_file_offset = 0;
+ thd->current_linfo = &linfo;
+
+ if (mysql_bin_log.find_first_log(&linfo, search_file_name))
+ {
+ errmsg = "Could not find target log";
+ goto err;
+ }
+
+ if ((file=open_binlog(&log, linfo.log_file_name, &errmsg)) < 0)
+ goto err;
+
+ if (pos < 4)
+ {
+ errmsg = "Invalid log position";
+ goto err;
+ }
+
+ pthread_mutex_lock(mysql_bin_log.get_log_lock());
+
+ my_b_seek(&log, pos);
+
+ for (event_count = 0;
+ (ev = Log_event::read_log_event(&log, 0));)
+ {
+ if (event_count >= limit_start &&
+ ev->net_send(thd, linfo.log_file_name, pos))
+ {
+ errmsg = "Net error";
+ delete ev;
+ pthread_mutex_unlock(mysql_bin_log.get_log_lock());
+ goto err;
+ }
+
+ pos = my_b_tell(&log);
+ delete ev;
+
+ if (++event_count >= limit_end)
+ break;
+ }
+
+ if (event_count < limit_end && log.error)
+ {
+ errmsg = "Wrong offset or I/O error";
+ goto err;
+ }
+
+ pthread_mutex_unlock(mysql_bin_log.get_log_lock());
+ }
+
+err:
+ if (file >= 0)
+ {
+ end_io_cache(&log);
+ (void) my_close(file, MYF(MY_WME));
+ }
+
+ if (errmsg)
+ {
+ net_printf(&thd->net, ER_SHOW_BINLOG_EVENTS, errmsg);
+ DBUG_RETURN(1);
+ }
+
+ send_eof(&thd->net);
+ DBUG_RETURN(0);
+}
+
+
+int show_slave_hosts(THD* thd)
+{
+ DBUG_ENTER("show_slave_hosts");
+ List<Item> field_list;
+ field_list.push_back(new Item_empty_string("Server_id", 20));
+ field_list.push_back(new Item_empty_string("Host", 20));
+ if(opt_show_slave_auth_info)
+ {
+ field_list.push_back(new Item_empty_string("User",20));
+ field_list.push_back(new Item_empty_string("Password",20));
+ }
+ field_list.push_back(new Item_empty_string("Port",20));
+
+ if(send_fields(thd, field_list, 1))
+ DBUG_RETURN(-1);
+ String* packet = &thd->packet;
+ uint i;
+ NET* net = &thd->net;
+
+ pthread_mutex_lock(&LOCK_slave_list);
+
+ for(i = 0; i < slave_list.records; ++i)
+ {
+ SLAVE_INFO* si = (SLAVE_INFO*)hash_element(&slave_list, i);
+ packet->length(0);
+ net_store_data(packet, si->server_id);
+ net_store_data(packet, si->host);
+ if(opt_show_slave_auth_info)
+ {
+ net_store_data(packet, si->user);
+ net_store_data(packet, si->password);
+ }
+ net_store_data(packet, (uint)si->port);
+ if(my_net_write(net, (char*)packet->ptr(), packet->length()))
+ {
+ pthread_mutex_unlock(&LOCK_slave_list);
+ DBUG_RETURN(-1);
+ }
+ }
+ pthread_mutex_unlock(&LOCK_slave_list);
+ send_eof(net);
+ DBUG_RETURN(0);
+}
+
int show_binlog_info(THD* thd)
{
DBUG_ENTER("show_binlog_info");
@@ -848,5 +1076,220 @@ err:
return 1;
}
+int connect_to_master(THD *thd, MYSQL* mysql, MASTER_INFO* mi)
+{
+ if(!mc_mysql_connect(mysql, mi->host, mi->user, mi->password, 0,
+ mi->port, 0, 0))
+ {
+ sql_print_error("Connection to master failed: %s",
+ mc_mysql_error(mysql));
+ return 1;
+ }
+ return 0;
+}
+
+static inline void cleanup_mysql_results(MYSQL_RES* db_res,
+ MYSQL_RES** cur, MYSQL_RES** start)
+{
+ for( ; cur >= start; --cur)
+ if(*cur)
+ mc_mysql_free_result(*cur);
+ mc_mysql_free_result(db_res);
+}
+
+static inline int fetch_db_tables(THD* thd, MYSQL* mysql, const char* db,
+ MYSQL_RES* table_res)
+{
+ MYSQL_ROW row;
+
+ for( row = mc_mysql_fetch_row(table_res); row;
+ row = mc_mysql_fetch_row(table_res))
+ {
+ TABLE_LIST table;
+ const char* table_name = row[0];
+ int error;
+ if(table_rules_on)
+ {
+ table.next = 0;
+ table.db = (char*)db;
+ table.real_name = (char*)table_name;
+ if(!tables_ok(thd, &table))
+ continue;
+ }
+
+ if((error = fetch_nx_table(thd, db, table_name, &glob_mi, mysql)))
+ return error;
+ }
+
+ return 0;
+}
+
+int load_master_data(THD* thd)
+{
+ MYSQL mysql;
+ MYSQL_RES* master_status_res = 0;
+ bool slave_was_running = 0;
+ int error = 0;
+
+ mc_mysql_init(&mysql);
+
+ pthread_mutex_lock(&LOCK_slave);
+ // we do not want anyone messing with the slave at all for the entire
+ // duration of the data load;
+
+ // first, kill the slave
+ if((slave_was_running = slave_running))
+ {
+ abort_slave = 1;
+ thr_alarm_kill(slave_real_id);
+ thd->proc_info = "waiting for slave to die";
+ while(slave_running)
+ pthread_cond_wait(&COND_slave_stopped, &LOCK_slave); // wait until done
+ }
+
+
+ if(connect_to_master(thd, &mysql, &glob_mi))
+ {
+ net_printf(&thd->net, error = ER_CONNECT_TO_MASTER,
+ mc_mysql_error(&mysql));
+ goto err;
+ }
+
+ // now that we are connected, get all database and tables in each
+ {
+ MYSQL_RES *db_res, **table_res, **table_res_end, **cur_table_res;
+ uint num_dbs;
+ MYSQL_ROW row;
+
+ if(mc_mysql_query(&mysql, "show databases", 0) ||
+ !(db_res = mc_mysql_store_result(&mysql)))
+ {
+ net_printf(&thd->net, error = ER_QUERY_ON_MASTER,
+ mc_mysql_error(&mysql));
+ goto err;
+ }
+
+ if(!(num_dbs = mc_mysql_num_rows(db_res)))
+ goto err;
+ // in theory, the master could have no databases at all
+ // and run with skip-grant
+
+ if(!(table_res = (MYSQL_RES**)thd->alloc(num_dbs * sizeof(MYSQL_RES*))))
+ {
+ net_printf(&thd->net, error = ER_OUTOFMEMORY);
+ goto err;
+ }
+
+ // this is a temporary solution until we have online backup
+ // capabilities - to be replaced once online backup is working
+ // we wait to issue FLUSH TABLES WITH READ LOCK for as long as we
+ // can to minimize the lock time
+ if(mc_mysql_query(&mysql, "FLUSH TABLES WITH READ LOCK", 0)
+ || mc_mysql_query(&mysql, "SHOW MASTER STATUS",0) ||
+ !(master_status_res = mc_mysql_store_result(&mysql)))
+ {
+ net_printf(&thd->net, error = ER_QUERY_ON_MASTER,
+ mc_mysql_error(&mysql));
+ goto err;
+ }
+
+ // go through every table in every database, and if the replication
+ // rules allow replicating it, get it
+
+ table_res_end = table_res + num_dbs;
+
+ for(cur_table_res = table_res; cur_table_res < table_res_end;
+ ++cur_table_res)
+ {
+ MYSQL_ROW row = mc_mysql_fetch_row(db_res);
+ // since we know how many rows we have, this can never be NULL
+
+ char* db = row[0];
+ int drop_error = 0;
+
+ // do not replicate databases excluded by rules
+ // also skip mysql database - in most cases the user will
+ // mess up and not exclude mysql database with the rules when
+ // he actually means to - in this case, he is up for a surprise if
+ // his priv tables get dropped and downloaded from master
+ // TO DO - add special option, not enabled
+ // by default, to allow inclusion of mysql database into load
+ // data from master
+ if(!db_ok(db, replicate_do_db, replicate_ignore_db) ||
+ !strcmp(db,"mysql"))
+ {
+ *cur_table_res = 0;
+ continue;
+ }
+
+ if((drop_error = mysql_rm_db(0, db, 1)) ||
+ mysql_create_db(0, db, 0))
+ {
+ error = (drop_error) ? ER_DB_DROP_DELETE : ER_CANT_CREATE_DB;
+ net_printf(&thd->net, error, db, my_error);
+ cleanup_mysql_results(db_res, cur_table_res - 1, table_res);
+ goto err;
+ }
+
+ if(mc_mysql_select_db(&mysql, db) ||
+ mc_mysql_query(&mysql, "show tables", 0) ||
+ !(*cur_table_res = mc_mysql_store_result(&mysql)))
+ {
+ net_printf(&thd->net, error = ER_QUERY_ON_MASTER,
+ mc_mysql_error(&mysql));
+ cleanup_mysql_results(db_res, cur_table_res - 1, table_res);
+ goto err;
+ }
+
+ if((error = fetch_db_tables(thd, &mysql, db, *cur_table_res)))
+ {
+ // we do not report the error - fetch_db_tables handles it
+ cleanup_mysql_results(db_res, cur_table_res, table_res);
+ goto err;
+ }
+ }
+
+ cleanup_mysql_results(db_res, cur_table_res - 1, table_res);
+
+ // adjust position in the master
+ if(master_status_res)
+ {
+ MYSQL_ROW row = mc_mysql_fetch_row(master_status_res);
+
+ // we need this check because the master may not be running with
+ // log-bin, but it will still allow us to do all the steps
+ // of LOAD DATA FROM MASTER - no reason to forbid it, really,
+ // although it does not make much sense for the user to do it
+ if(row[0] && row[1])
+ {
+ strmake(glob_mi.log_file_name, row[0], sizeof(glob_mi.log_file_name));
+ glob_mi.pos = atoi(row[1]); // atoi() is ok, since offset is <= 1GB
+ if(glob_mi.pos < 4)
+ glob_mi.pos = 4; // don't hit the magic number
+ glob_mi.pending = 0;
+ flush_master_info(&glob_mi);
+ }
+
+ mc_mysql_free_result(master_status_res);
+ }
+
+ if(mc_mysql_query(&mysql, "UNLOCK TABLES", 0))
+ {
+ net_printf(&thd->net, error = ER_QUERY_ON_MASTER,
+ mc_mysql_error(&mysql));
+ goto err;
+ }
+ }
+err:
+ pthread_mutex_unlock(&LOCK_slave);
+ if(slave_was_running)
+ start_slave(0, 0);
+ mc_mysql_close(&mysql); // safe to call since we always do mc_mysql_init()
+ if(!error)
+ send_ok(&thd->net);
+
+ return error;
+}
+
diff --git a/sql/sql_repl.h b/sql/sql_repl.h
index 68f2b4ba6c4..a3ba11bdfdb 100644
--- a/sql/sql_repl.h
+++ b/sql/sql_repl.h
@@ -3,6 +3,17 @@
#include "slave.h"
+typedef struct st_slave_info
+{
+ uint32 server_id;
+ char host[HOSTNAME_LENGTH+1];
+ char user[USERNAME_LENGTH+1];
+ char password[HASH_PASSWORD_LENGTH+1];
+ uint16 port;
+} SLAVE_INFO;
+
+extern bool opt_show_slave_auth_info;
+extern HASH slave_list;
extern char* master_host;
extern my_string opt_bin_logname, master_info_file;
extern uint32 server_id;
@@ -14,9 +25,16 @@ File open_binlog(IO_CACHE *log, const char *log_file_name,
int start_slave(THD* thd = 0, bool net_report = 1);
int stop_slave(THD* thd = 0, bool net_report = 1);
+int load_master_data(THD* thd);
+int connect_to_master(THD *thd, MYSQL* mysql, MASTER_INFO* mi);
int change_master(THD* thd);
+int show_slave_hosts(THD* thd);
+int show_binlog_events(THD* thd);
void reset_slave();
void reset_master();
+void init_slave_list();
+void end_slave_list();
+int register_slave(THD* thd, uchar* packet, uint packet_length);
int purge_master_logs(THD* thd, const char* to_log);
bool log_in_use(const char* log_name);
void adjust_linfo_offsets(my_off_t purge_offset);
diff --git a/sql/sql_select.cc b/sql/sql_select.cc
index 82b73e5a48f..6a5d3fede15 100644
--- a/sql/sql_select.cc
+++ b/sql/sql_select.cc
@@ -69,7 +69,7 @@ static COND *remove_eq_conds(COND *cond,Item::cond_result *cond_value);
static bool const_expression_in_where(COND *conds,Item *item, Item **comp_item);
static bool open_tmp_table(TABLE *table);
static bool create_myisam_tmp_table(TABLE *table,TMP_TABLE_PARAM *param,
- uint options);
+ ulong options);
static int do_select(JOIN *join,List<Item> *fields,TABLE *tmp_table,
Procedure *proc);
static int sub_select_cache(JOIN *join,JOIN_TAB *join_tab,bool end_of_records);
@@ -105,7 +105,7 @@ static COND *make_cond_for_table(COND *cond,table_map table,
static Item* part_of_refkey(TABLE *form,Field *field);
static uint find_shortest_key(TABLE *table, key_map usable_keys);
static bool test_if_skip_sort_order(JOIN_TAB *tab,ORDER *order,
- ha_rows select_limit);
+ ha_rows select_limit, bool no_changes);
static int create_sort_index(JOIN_TAB *tab,ORDER *order,ha_rows select_limit);
static bool fix_having(JOIN *join, Item **having);
static int remove_duplicates(JOIN *join,TABLE *entry,List<Item> &fields,
@@ -157,7 +157,7 @@ mysql_select(THD *thd,TABLE_LIST *tables,List<Item> &fields,COND *conds,
TABLE *tmp_table;
int error,tmp;
bool need_tmp,hidden_group_fields;
- bool simple_order,simple_group,no_order;
+ bool simple_order,simple_group,no_order, skip_sort_order;
Item::cond_result cond_value;
SQL_SELECT *select;
DYNAMIC_ARRAY keyuse;
@@ -172,7 +172,7 @@ mysql_select(THD *thd,TABLE_LIST *tables,List<Item> &fields,COND *conds,
select_distinct=test(select_options & SELECT_DISTINCT);
tmp_table=0;
select=0;
- no_order=0;
+ no_order=skip_sort_order=0;
bzero((char*) &keyuse,sizeof(keyuse));
thd->proc_info="init";
thd->used_tables=0; // Updated by setup_fields
@@ -276,7 +276,10 @@ mysql_select(THD *thd,TABLE_LIST *tables,List<Item> &fields,COND *conds,
count_field_types(&join.tmp_table_param,all_fields,0);
join.const_tables=0;
join.having=0;
+ join.do_send_rows = 1;
join.group= group != 0;
+ join.row_limit= ((select_distinct || order || group) ? HA_POS_ERROR :
+ thd->select_limit);
#ifdef RESTRICTED_GROUP
if (join.sum_func_count && !group && (join.func_count || join.field_count))
@@ -358,7 +361,7 @@ mysql_select(THD *thd,TABLE_LIST *tables,List<Item> &fields,COND *conds,
result->send_fields(fields,1);
if (!having || having->val_int())
{
- if (result->send_data(fields))
+ if (join.do_send_rows && result->send_data(fields))
{
result->send_error(0,NullS); /* purecov: inspected */
error=1;
@@ -448,7 +451,10 @@ mysql_select(THD *thd,TABLE_LIST *tables,List<Item> &fields,COND *conds,
select_distinct=0;
}
else if (select_distinct && join.tables - join.const_tables == 1 &&
- (order || thd->select_limit == HA_POS_ERROR))
+ (thd->select_limit == HA_POS_ERROR ||
+ (join.select_options & OPTION_FOUND_ROWS) ||
+ order &&
+ !(skip_sort_order=test_if_skip_sort_order(&join.join_tab[join.const_tables], order, thd->select_limit,1))))
{
if ((group=create_distinct_group(order,fields)))
{
@@ -493,8 +499,11 @@ mysql_select(THD *thd,TABLE_LIST *tables,List<Item> &fields,COND *conds,
(group && order) ||
test(select_options & OPTION_BUFFER_RESULT)));
- make_join_readinfo(&join, (select_options & SELECT_DESCRIBE) |
- (ftfuncs.elements ? 0 : SELECT_USE_CACHE)); // No cache for MATCH
+ // No cache for MATCH
+ make_join_readinfo(&join,
+ (select_options & (SELECT_DESCRIBE |
+ SELECT_NO_JOIN_CACHE)) |
+ (ftfuncs.elements ? SELECT_NO_JOIN_CACHE : 0));
/* Need to tell Innobase that to play it safe, it should fetch all
columns of the tables: this is because MySQL
@@ -532,7 +541,7 @@ mysql_select(THD *thd,TABLE_LIST *tables,List<Item> &fields,COND *conds,
if (!(select_options & SELECT_BIG_RESULT) &&
((group && join.const_tables != join.tables &&
!test_if_skip_sort_order(&join.join_tab[join.const_tables], group,
- HA_POS_ERROR)) ||
+ thd->select_limit,0)) ||
select_distinct) &&
join.tmp_table_param.quick_group && !procedure)
{
@@ -546,7 +555,9 @@ mysql_select(THD *thd,TABLE_LIST *tables,List<Item> &fields,COND *conds,
if (order &&
(join.const_tables == join.tables ||
test_if_skip_sort_order(&join.join_tab[join.const_tables], order,
- (group ? HA_POS_ERROR : thd->select_limit))))
+ (join.const_tables != join.tables - 1 ||
+ (join.select_options & OPTION_FOUND_ROWS)) ?
+ HA_POS_ERROR : thd->select_limit,0)))
order=0;
select_describe(&join,need_tmp,
(order != 0 &&
@@ -582,7 +593,8 @@ mysql_select(THD *thd,TABLE_LIST *tables,List<Item> &fields,COND *conds,
group : (ORDER*) 0),
group ? 0 : select_distinct,
group && simple_group,
- order == 0,
+ (order == 0 || skip_sort_order) &&
+ !(join.select_options & OPTION_FOUND_ROWS),
join.select_options)))
goto err; /* purecov: inspected */
@@ -632,6 +644,13 @@ mysql_select(THD *thd,TABLE_LIST *tables,List<Item> &fields,COND *conds,
break;
join_tab->not_used_in_distinct=1;
} while (join_tab-- != join.join_tab);
+ /* Optimize "select distinct b from t1 order by key_part_1 limit #" */
+ if (order && skip_sort_order)
+ {
+ (void) test_if_skip_sort_order(&join.join_tab[join.const_tables],
+ order, thd->select_limit,0);
+ order=0;
+ }
}
/* Copy data to the temporary table */
@@ -770,13 +789,34 @@ mysql_select(THD *thd,TABLE_LIST *tables,List<Item> &fields,COND *conds,
/* If we have already done the group, add HAVING to sorted table */
if (having && ! group && ! join.sort_and_group)
{
- if (fix_having(&join,&having))
- goto err;
+ having->update_used_tables(); // Some tables may have been const
+ JOIN_TAB *table=&join.join_tab[join.const_tables];
+ table_map used_tables= join.const_table_map | table->table->map;
+
+ Item* sort_table_cond=make_cond_for_table(having,used_tables,used_tables);
+ if (sort_table_cond)
+ {
+ if (!table->select)
+ if (!(table->select=new SQL_SELECT))
+ goto err;
+ if (!table->select->cond)
+ table->select->cond=sort_table_cond;
+ else // This should never happen
+ if (!(table->select->cond=new Item_cond_and(table->select->cond,
+ sort_table_cond)))
+ goto err;
+ table->select_cond=table->select->cond;
+ DBUG_EXECUTE("where",print_where(table->select->cond,
+ "select and having"););
+ having=make_cond_for_table(having,~ (table_map) 0,~used_tables);
+ DBUG_EXECUTE("where",print_where(conds,"having after sort"););
+ }
}
if (create_sort_index(&join.join_tab[join.const_tables],
group ? group : order,
(having || group ||
- join.const_tables != join.tables - 1) ?
+ join.const_tables != join.tables - 1 ||
+ (join.select_options & OPTION_FOUND_ROWS)) ?
HA_POS_ERROR : thd->select_limit))
goto err; /* purecov: inspected */
}
@@ -785,7 +825,8 @@ mysql_select(THD *thd,TABLE_LIST *tables,List<Item> &fields,COND *conds,
error=do_select(&join,&fields,NULL,procedure);
err:
- thd->examined_row_count=join.examined_rows;
+ thd->limit_found_rows = join.send_records;
+ thd->examined_row_count = join.examined_rows;
thd->proc_info="end";
join.lock=0; // It's faster to unlock later
join_free(&join);
@@ -806,7 +847,7 @@ err:
*****************************************************************************/
static ha_rows get_quick_record_count(SQL_SELECT *select,TABLE *table,
- key_map keys)
+ key_map keys,ha_rows limit)
{
int error;
DBUG_ENTER("get_quick_record_count");
@@ -814,7 +855,7 @@ static ha_rows get_quick_record_count(SQL_SELECT *select,TABLE *table,
{
select->head=table;
table->reginfo.impossible_range=0;
- if ((error=select->test_quick_select(keys,(table_map) 0,HA_POS_ERROR))
+ if ((error=select->test_quick_select(keys,(table_map) 0,limit))
== 1)
DBUG_RETURN(select->quick->records);
if (error == -1)
@@ -1026,7 +1067,8 @@ make_join_statistics(JOIN *join,TABLE_LIST *tables,COND *conds,
s->read_time=(ha_rows) s->table->file->scan_time();
/* Set a max range of how many seeks we can expect when using keys */
- s->worst_seeks= (double) (s->read_time*2);
+ /* This was (s->read_time*5), but this was too low with small rows */
+ s->worst_seeks= (double) s->found_records / 5;
if (s->worst_seeks < 2.0) // Fix for small tables
s->worst_seeks=2.0;
@@ -1039,7 +1081,8 @@ make_join_statistics(JOIN *join,TABLE_LIST *tables,COND *conds,
select=make_select(s->table,const_table_map,
0,
and_conds(conds,s->on_expr),&error);
- records=get_quick_record_count(select,s->table, s->const_keys);
+ records=get_quick_record_count(select,s->table, s->const_keys,
+ join->row_limit);
s->quick=select->quick;
s->needed_reg=select->needed_reg;
select->quick=0;
@@ -1722,7 +1765,7 @@ find_best(JOIN *join,table_map rest_tables,uint idx,double record_count,
{
/* we can use only index tree */
uint keys_per_block= table->file->block_size/2/
- keyinfo->key_length+1;
+ (keyinfo->key_length+table->file->ref_length)+1;
tmp=(record_count*(records+keys_per_block-1)/
keys_per_block);
}
@@ -1792,7 +1835,7 @@ find_best(JOIN *join,table_map rest_tables,uint idx,double record_count,
{
/* we can use only index tree */
uint keys_per_block= table->file->block_size/2/
- keyinfo->key_length+1;
+ (keyinfo->key_length+table->file->ref_length)+1;
tmp=record_count*(tmp+keys_per_block-1)/keys_per_block;
}
else
@@ -2204,6 +2247,8 @@ make_simple_join(JOIN *join,TABLE *tmp_table)
join->sum_funcs=0;
join->send_records=(ha_rows) 0;
join->group=0;
+ join->do_send_rows = 1;
+ join->row_limit=HA_POS_ERROR;
join_tab->cache.buff=0; /* No cacheing */
join_tab->table=tmp_table;
@@ -2305,15 +2350,19 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond)
*/
if ((tab->keys & ~ tab->const_keys && i > 0) ||
- tab->const_keys && i == join->const_tables &&
- join->thd->select_limit < join->best_positions[i].records_read)
+ (tab->const_keys && i == join->const_tables &&
+ join->thd->select_limit < join->best_positions[i].records_read &&
+ !(join->select_options & OPTION_FOUND_ROWS)))
{
/* Join with outer join condition */
COND *orig_cond=sel->cond;
sel->cond=and_conds(sel->cond,tab->on_expr);
if (sel->test_quick_select(tab->keys,
used_tables & ~ current_map,
- join->thd->select_limit) < 0)
+ (join->select_options &
+ OPTION_FOUND_ROWS ?
+ HA_POS_ERROR :
+ join->thd->select_limit)) < 0)
DBUG_RETURN(1); // Impossible range
sel->cond=orig_cond;
}
@@ -2434,7 +2483,7 @@ make_join_readinfo(JOIN *join,uint options)
** if previous table use cache
*/
table->status=STATUS_NO_RECORD;
- if (i != join->const_tables && (options & SELECT_USE_CACHE) &&
+ if (i != join->const_tables && !(options & SELECT_NO_JOIN_CACHE) &&
tab->use_quick != 2 && !tab->on_expr)
{
if ((options & SELECT_DESCRIBE) ||
@@ -2447,7 +2496,7 @@ make_join_readinfo(JOIN *join,uint options)
/* These init changes read_record */
if (tab->use_quick == 2)
{
- join->thd->lex.options|=QUERY_NO_GOOD_INDEX_USED;
+ join->thd->lex.select_lex.options|=QUERY_NO_GOOD_INDEX_USED;
tab->read_first_record= join_init_quick_read_record;
statistic_increment(select_range_check_count, &LOCK_status);
}
@@ -2462,7 +2511,7 @@ make_join_readinfo(JOIN *join,uint options)
}
else
{
- join->thd->lex.options|=QUERY_NO_INDEX_USED;
+ join->thd->lex.select_lex.options|=QUERY_NO_INDEX_USED;
statistic_increment(select_scan_count, &LOCK_status);
}
}
@@ -2474,7 +2523,7 @@ make_join_readinfo(JOIN *join,uint options)
}
else
{
- join->thd->lex.options|=QUERY_NO_INDEX_USED;
+ join->thd->lex.select_lex.options|=QUERY_NO_INDEX_USED;
statistic_increment(select_full_join_count, &LOCK_status);
}
}
@@ -3291,7 +3340,7 @@ Field *create_tmp_field(TABLE *table,Item *item, Item::Type type,
TABLE *
create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields,
ORDER *group, bool distinct, bool save_sum_fields,
- bool allow_distinct_limit, uint select_options)
+ bool allow_distinct_limit, ulong select_options)
{
TABLE *table;
uint i,field_count,reclength,null_count,null_pack_length,
@@ -3733,7 +3782,7 @@ static bool open_tmp_table(TABLE *table)
static bool create_myisam_tmp_table(TABLE *table,TMP_TABLE_PARAM *param,
- uint options)
+ ulong options)
{
int error;
MI_KEYDEF keydef;
@@ -3894,12 +3943,18 @@ bool create_myisam_from_heap(TABLE *table, TMP_TABLE_PARAM *param, int error,
thd->proc_info="converting HEAP to MyISAM";
if (create_myisam_tmp_table(&new_table,param,
- thd->lex.options | thd->options))
+ thd->lex.select_lex.options | thd->options))
goto err2;
if (open_tmp_table(&new_table))
goto err1;
table->file->index_end();
table->file->rnd_init();
+ if (table->no_rows)
+ {
+ new_table.file->extra(HA_EXTRA_NO_ROWS);
+ new_table.no_rows=1;
+ }
+
/* copy all old rows */
while (!table->file->rnd_next(new_table.record[1]))
{
@@ -4605,14 +4660,23 @@ end_send(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
int error;
if (join->having && join->having->val_int() == 0)
DBUG_RETURN(0); // Didn't match having
+ error=0;
if (join->procedure)
error=join->procedure->send_row(*join->fields);
- else
+ else if (join->do_send_rows)
error=join->result->send_data(*join->fields);
if (error)
DBUG_RETURN(-1); /* purecov: inspected */
- if (++join->send_records >= join->thd->select_limit)
+ if (++join->send_records >= join->thd->select_limit && join->do_send_rows)
+ {
+ if (join->select_options & OPTION_FOUND_ROWS)
+ {
+ join->do_send_rows=0;
+ join->thd->select_limit = HA_POS_ERROR;
+ DBUG_RETURN(0);
+ }
DBUG_RETURN(-3); // Abort nicely
+ }
}
else
{
@@ -4643,9 +4707,10 @@ end_send_group(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
int error;
if (join->procedure)
{
+ error=0;
if (join->having && join->having->val_int() == 0)
error= -1; // Didn't satisfy having
- else
+ else if (join->do_send_rows)
error=join->procedure->send_row(*join->fields) ? 1 : 0;
if (end_of_records && join->procedure->end_of_records())
error= 1; // Fatal error
@@ -4667,8 +4732,14 @@ end_send_group(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
DBUG_RETURN(-1); /* purecov: inspected */
if (end_of_records)
DBUG_RETURN(0);
- if (!error && ++join->send_records >= join->thd->select_limit)
- DBUG_RETURN(-3); /* Abort nicely */
+ if (!error && ++join->send_records >= join->thd->select_limit &&
+ join->do_send_rows)
+ {
+ if (!(join->select_options & OPTION_FOUND_ROWS))
+ DBUG_RETURN(-3); // Abort nicely
+ join->do_send_rows=0;
+ join->thd->select_limit = HA_POS_ERROR;
+ }
}
}
else
@@ -4739,8 +4810,15 @@ end_write(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
if (create_myisam_from_heap(table, &join->tmp_table_param, error,1))
DBUG_RETURN(1); // Not a table_is_full error
table->uniques=0; // To ensure rows are the same
- if (++join->send_records >= join->tmp_table_param.end_write_records)
+ }
+ if (++join->send_records >= join->tmp_table_param.end_write_records &
+ join->do_send_rows)
+ {
+ if (!(join->select_options & OPTION_FOUND_ROWS))
DBUG_RETURN(-3);
+ join->do_send_rows=0;
+ join->thd->select_limit = HA_POS_ERROR;
+ DBUG_RETURN(0);
}
}
}
@@ -5141,7 +5219,8 @@ static uint find_shortest_key(TABLE *table, key_map usable_keys)
/* Return 1 if we don't have to do file sorting */
static bool
-test_if_skip_sort_order(JOIN_TAB *tab,ORDER *order,ha_rows select_limit)
+test_if_skip_sort_order(JOIN_TAB *tab,ORDER *order,ha_rows select_limit,
+ bool no_changes)
{
int ref_key;
TABLE *table=tab->table;
@@ -5169,10 +5248,20 @@ test_if_skip_sort_order(JOIN_TAB *tab,ORDER *order,ha_rows select_limit)
if (ref_key >= 0)
{
+ int order_direction;
/* Check if we get the rows in requested sorted order by using the key */
if ((usable_keys & ((key_map) 1 << ref_key)) &&
- test_if_order_by_key(order,table,ref_key) == 1)
+ (order_direction = test_if_order_by_key(order,table,ref_key)))
+ {
+ if (order_direction == -1 && select && select->quick)
+ {
+ // ORDER BY ref_key DESC
+ select->quick = new QUICK_SELECT_DESC(select->quick);
+ if (select->quick->error)
+ DBUG_RETURN(0);
+ }
DBUG_RETURN(1); /* No need to sort */
+ }
}
else
{
@@ -5196,11 +5285,14 @@ test_if_skip_sort_order(JOIN_TAB *tab,ORDER *order,ha_rows select_limit)
int flag;
if ((flag=test_if_order_by_key(order,table,nr)))
{
- tab->index=nr;
- tab->read_first_record= (flag > 0 ? join_init_read_first_with_key:
- join_init_read_last_with_key);
- table->file->index_init(nr);
- tab->type=JT_NEXT; // Read with index_first(), index_next()
+ if (!no_changes)
+ {
+ tab->index=nr;
+ tab->read_first_record= (flag > 0 ? join_init_read_first_with_key:
+ join_init_read_last_with_key);
+ table->file->index_init(nr);
+ tab->type=JT_NEXT; // Read with index_first(), index_next()
+ }
DBUG_RETURN(1);
}
}
@@ -5219,7 +5311,7 @@ create_sort_index(JOIN_TAB *tab,ORDER *order,ha_rows select_limit)
SQL_SELECT *select=tab->select;
DBUG_ENTER("create_sort_index");
- if (test_if_skip_sort_order(tab,order,select_limit))
+ if (test_if_skip_sort_order(tab,order,select_limit,0))
DBUG_RETURN(0);
if (!(sortorder=make_unireg_sortorder(order,&length)))
goto err; /* purecov: inspected */
@@ -5252,7 +5344,9 @@ create_sort_index(JOIN_TAB *tab,ORDER *order,ha_rows select_limit)
goto err;
}
}
- table->found_records=filesort(&table,sortorder,length,
+ if (table->tmp_table)
+ table->file->info(HA_STATUS_VARIABLE); // Get record count
+ table->found_records=filesort(table,sortorder,length,
select, 0L, select_limit, &examined_rows);
delete select; // filesort did select
tab->select=0;
@@ -6588,7 +6682,7 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
DBUG_ENTER("select_describe");
/* Don't log this into the slow query log */
- join->thd->lex.options&= ~(QUERY_NO_INDEX_USED | QUERY_NO_GOOD_INDEX_USED);
+ join->thd->lex.select_lex.options&= ~(QUERY_NO_INDEX_USED | QUERY_NO_GOOD_INDEX_USED);
field_list.push_back(new Item_empty_string("table",NAME_LEN));
field_list.push_back(new Item_empty_string("type",10));
field_list.push_back(item=new Item_empty_string("possible_keys",
@@ -6747,7 +6841,7 @@ static void describe_info(THD *thd, const char *info)
String *packet= &thd->packet;
/* Don't log this into the slow query log */
- thd->lex.options&= ~(QUERY_NO_INDEX_USED | QUERY_NO_GOOD_INDEX_USED);
+ thd->lex.select_lex.options&= ~(QUERY_NO_INDEX_USED | QUERY_NO_GOOD_INDEX_USED);
field_list.push_back(new Item_empty_string("Comment",80));
if (send_fields(thd,field_list,1))
return; /* purecov: inspected */
diff --git a/sql/sql_select.h b/sql/sql_select.h
index 1bf7d7863eb..0ec1854d641 100644
--- a/sql/sql_select.h
+++ b/sql/sql_select.h
@@ -154,6 +154,7 @@ class JOIN {
uint tables,const_tables;
uint send_group_parts;
bool sort_and_group,first_record,full_join,group, no_field_update;
+ bool do_send_rows;
table_map const_table_map,outer_join;
ha_rows send_records,found_records,examined_rows,row_limit;
POSITION positions[MAX_TABLES+1],best_positions[MAX_TABLES+1];
@@ -183,7 +184,7 @@ void TEST_join(JOIN *join);
bool store_val_in_field(Field *field,Item *val);
TABLE *create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields,
ORDER *group, bool distinct, bool save_sum_fields,
- bool allow_distinct_limit, uint select_options);
+ bool allow_distinct_limit, ulong select_options);
void free_tmp_table(THD *thd, TABLE *entry);
void count_field_types(TMP_TABLE_PARAM *param, List<Item> &fields,
bool reset_with_sum_func);
diff --git a/sql/sql_show.cc b/sql/sql_show.cc
index 6f99495d94d..199d6a764e0 100644
--- a/sql/sql_show.cc
+++ b/sql/sql_show.cc
@@ -81,7 +81,7 @@ mysqld_show_dbs(THD *thd,const char *wild)
(grant_option && !check_grant_db(thd, file_name)))
{
thd->packet.length(0);
- net_store_data(&thd->packet,file_name);
+ net_store_data(&thd->packet, thd->convert_set, file_name);
if (my_net_write(&thd->net, (char*) thd->packet.ptr(),
thd->packet.length()))
DBUG_RETURN(-1);
@@ -95,39 +95,34 @@ mysqld_show_dbs(THD *thd,const char *wild)
** List all open tables in a database
***************************************************************************/
-int mysqld_show_open_tables(THD *thd,const char *db,const char *wild)
+int mysqld_show_open_tables(THD *thd,const char *wild)
{
- Item_string *field=new Item_string("",0);
List<Item> field_list;
- char *end,*table_name;
- List<char> tables;
+ OPEN_TABLE_LIST *open_list;
+ CONVERT *convert=thd->convert_set;
DBUG_ENTER("mysqld_show_open_tables");
- field->name=(char*) thd->alloc(20+(uint) strlen(db)+(wild ? (uint) strlen(wild)+4:0));
- end=strxmov(field->name,"Open_tables_in_",db,NullS);
- if (wild && wild[0])
- strxmov(end," (",wild,")",NullS);
- field->max_length=NAME_LEN;
- field_list.push_back(field);
- field_list.push_back(new Item_empty_string("Comment",80));
+ field_list.push_back(new Item_empty_string("Database",NAME_LEN));
+ field_list.push_back(new Item_empty_string("Table",NAME_LEN));
+ field_list.push_back(new Item_int("In_use",0, 4));
+ field_list.push_back(new Item_int("Name_locked",0, 4));
if (send_fields(thd,field_list,1))
DBUG_RETURN(1);
- if (list_open_tables(thd,&tables,db,wild))
+ if (!(open_list=list_open_tables(thd,wild)))
DBUG_RETURN(-1);
- List_iterator<char> it(tables);
- while ((table_name=it++))
+ for ( ; open_list ; open_list=open_list->next)
{
thd->packet.length(0);
- net_store_data(&thd->packet,table_name);
- net_store_data(&thd->packet,query_table_status(thd,db,table_name));
+ net_store_data(&thd->packet,convert, open_list->db);
+ net_store_data(&thd->packet,convert, open_list->table);
+ net_store_data(&thd->packet,open_list->in_use);
+ net_store_data(&thd->packet,open_list->locked);
if (my_net_write(&thd->net,(char*) thd->packet.ptr(),thd->packet.length()))
DBUG_RETURN(-1);
}
-
-
send_eof(&thd->net);
DBUG_RETURN(0);
}
@@ -162,7 +157,7 @@ int mysqld_show_tables(THD *thd,const char *db,const char *wild)
while ((file_name=it++))
{
thd->packet.length(0);
- net_store_data(&thd->packet,file_name);
+ net_store_data(&thd->packet, thd->convert_set, file_name);
if (my_net_write(&thd->net,(char*) thd->packet.ptr(),thd->packet.length()))
DBUG_RETURN(-1);
}
@@ -248,6 +243,7 @@ int mysqld_extend_show_tables(THD *thd,const char *db,const char *wild)
char *file_name;
TABLE *table;
String *packet= &thd->packet;
+ CONVERT *convert=thd->convert_set;
DBUG_ENTER("mysqld_extend_show_tables");
(void) sprintf(path,"%s/%s",mysql_data_home,db);
@@ -293,14 +289,14 @@ int mysqld_extend_show_tables(THD *thd,const char *db,const char *wild)
TABLE_LIST table_list;
bzero((char*) &table_list,sizeof(table_list));
packet->length(0);
- net_store_data(packet,file_name);
+ net_store_data(packet,convert, file_name);
table_list.db=(char*) db;
table_list.real_name=table_list.name=file_name;
if (!(table = open_ltable(thd, &table_list, TL_READ)))
{
for (uint i=0 ; i < field_list.elements ; i++)
net_store_null(packet);
- net_store_data(packet,thd->net.last_error);
+ net_store_data(packet,convert, thd->net.last_error);
thd->net.last_error[0]=0;
}
else
@@ -308,8 +304,8 @@ int mysqld_extend_show_tables(THD *thd,const char *db,const char *wild)
struct tm tm_tmp;
handler *file=table->file;
file->info(HA_STATUS_VARIABLE | HA_STATUS_TIME | HA_STATUS_NO_LOCK);
- net_store_data(packet, file->table_type());
- net_store_data(packet,
+ net_store_data(packet, convert, file->table_type());
+ net_store_data(packet, convert,
(table->db_options_in_use & HA_OPTION_PACK_RECORD) ?
"Dynamic" :
(table->db_options_in_use & HA_OPTION_COMPRESS_RECORD)
@@ -390,7 +386,7 @@ int mysqld_extend_show_tables(THD *thd,const char *db,const char *wild)
my_raid_type(file->raid_type), file->raid_chunks, file->raid_chunksize/RAID_BLOCK_SIZE);
ptr=strmov(ptr,buff);
}
- net_store_data(packet, option_buff+1,
+ net_store_data(packet, convert, option_buff+1,
(ptr == option_buff ? 0 : (uint) (ptr-option_buff)-1));
}
{
@@ -422,6 +418,7 @@ mysqld_show_fields(THD *thd, TABLE_LIST *table_list,const char *wild,
TABLE *table;
handler *file;
char tmp[MAX_FIELD_WIDTH];
+ CONVERT *convert=thd->convert_set;
DBUG_ENTER("mysqld_show_fields");
DBUG_PRINT("enter",("db: %s table: %s",table_list->db,
table_list->real_name));
@@ -476,18 +473,18 @@ mysqld_show_fields(THD *thd, TABLE_LIST *table_list,const char *wild,
bool null_default_value=0;
packet->length(0);
- net_store_data(packet,field->field_name);
+ net_store_data(packet,convert,field->field_name);
field->sql_type(type);
- net_store_data(packet,type.ptr(),type.length());
+ net_store_data(packet,convert,type.ptr(),type.length());
pos=(byte*) ((flags & NOT_NULL_FLAG) &&
field->type() != FIELD_TYPE_TIMESTAMP ?
"" : "YES");
- net_store_data(packet,(const char*) pos);
+ net_store_data(packet,convert,(const char*) pos);
pos=(byte*) ((field->flags & PRI_KEY_FLAG) ? "PRI" :
(field->flags & UNIQUE_KEY_FLAG) ? "UNI" :
(field->flags & MULTIPLE_KEY_FLAG) ? "MUL":"");
- net_store_data(packet,(char*) pos);
+ net_store_data(packet,convert,(char*) pos);
if (field->type() == FIELD_TYPE_TIMESTAMP ||
field->unireg_check == Field::NEXT_NUMBER)
@@ -496,17 +493,17 @@ mysqld_show_fields(THD *thd, TABLE_LIST *table_list,const char *wild,
{ // Not null by default
type.set(tmp,sizeof(tmp));
field->val_str(&type,&type);
- net_store_data(packet,type.ptr(),type.length());
+ net_store_data(packet,convert,type.ptr(),type.length());
}
else if (field->maybe_null() || null_default_value)
net_store_null(packet); // Null as default
else
- net_store_data(packet,tmp,0);
+ net_store_data(packet,convert,tmp,0);
char *end=tmp;
if (field->unireg_check == Field::NEXT_NUMBER)
end=strmov(tmp,"auto_increment");
- net_store_data(packet,tmp,(uint) (end-tmp));
+ net_store_data(packet,convert,tmp,(uint) (end-tmp));
if (verbose)
{
@@ -521,7 +518,7 @@ mysqld_show_fields(THD *thd, TABLE_LIST *table_list,const char *wild,
end=strmov(end,grant_types.type_names[bitnr]);
}
}
- net_store_data(packet,tmp+1,end == tmp ? 0 : (uint) (end-tmp-1));
+ net_store_data(packet,convert, tmp+1,end == tmp ? 0 : (uint) (end-tmp-1));
}
if (my_net_write(&thd->net,(char*) packet->ptr(),packet->length()))
DBUG_RETURN(1);
@@ -536,6 +533,7 @@ int
mysqld_show_create(THD *thd, TABLE_LIST *table_list)
{
TABLE *table;
+ CONVERT *convert=thd->convert_set;
DBUG_ENTER("mysqld_show_create");
DBUG_PRINT("enter",("db: %s table: %s",table_list->db,
table_list->real_name));
@@ -557,7 +555,7 @@ mysqld_show_create(THD *thd, TABLE_LIST *table_list)
String *packet = &thd->packet;
{
packet->length(0);
- net_store_data(packet, table->table_name);
+ net_store_data(packet,convert, table->table_name);
// a hack - we need to reserve some space for the length before
// we know what it is - let's assume that the length of create table
// statement will fit into 3 bytes ( 16 MB max :-) )
@@ -614,6 +612,7 @@ mysqld_show_keys(THD *thd, TABLE_LIST *table_list)
{
TABLE *table;
char buff[256];
+ CONVERT *convert=thd->convert_set;
DBUG_ENTER("mysqld_show_keys");
DBUG_PRINT("enter",("db: %s table: %s",table_list->db,
table_list->real_name));
@@ -655,16 +654,18 @@ mysqld_show_keys(THD *thd, TABLE_LIST *table_list)
for (uint j=0 ; j < key_info->key_parts ; j++,key_part++)
{
packet->length(0);
- net_store_data(packet,table->table_name);
- net_store_data(packet,((key_info->flags & HA_NOSAME) ? "0" :"1"), 1);
- net_store_data(packet,key_info->name);
+ net_store_data(packet,convert,table->table_name);
+ net_store_data(packet,convert,((key_info->flags & HA_NOSAME) ? "0" :"1"), 1);
+ net_store_data(packet,convert,key_info->name);
end=int10_to_str((long) (j+1),(char*) buff,10);
- net_store_data(packet,buff,(uint) (end-buff));
- net_store_data(packet,key_part->field ? key_part->field->field_name :
+ net_store_data(packet,convert,buff,(uint) (end-buff));
+ net_store_data(packet,convert,
+ key_part->field ? key_part->field->field_name :
"?unknown field?");
if (table->file->option_flag() & HA_READ_ORDER)
- net_store_data(packet,((key_part->key_part_flag & HA_REVERSE_SORT)
- ? "D" : "A"), 1);
+ net_store_data(packet,convert,
+ ((key_part->key_part_flag & HA_REVERSE_SORT) ?
+ "D" : "A"), 1);
else
net_store_null(packet); /* purecov: inspected */
KEY *key=table->key_info+i;
@@ -672,7 +673,7 @@ mysqld_show_keys(THD *thd, TABLE_LIST *table_list)
{
ulong records=(table->file->records / key->rec_per_key[j]);
end=int10_to_str((long) records, buff, 10);
- net_store_data(packet,buff,(uint) (end-buff));
+ net_store_data(packet,convert,buff,(uint) (end-buff));
}
else
net_store_null(packet);
@@ -681,12 +682,13 @@ mysqld_show_keys(THD *thd, TABLE_LIST *table_list)
table->field[key_part->fieldnr-1]->key_length())
{
end=int10_to_str((long) key_part->length, buff,10); /* purecov: inspected */
- net_store_data(packet,buff,(uint) (end-buff)); /* purecov: inspected */
+ net_store_data(packet,convert,buff,(uint) (end-buff)); /* purecov: inspected */
}
else
net_store_null(packet);
net_store_null(packet); // No pack_information yet
- net_store_data(packet,key_info->flags & HA_FULLTEXT ? "FULLTEXT":"");
+ net_store_data(packet,convert,
+ key_info->flags & HA_FULLTEXT ? "FULLTEXT":"");
if (my_net_write(&thd->net,(char*) packet->ptr(),packet->length()))
DBUG_RETURN(1); /* purecov: inspected */
}
@@ -731,15 +733,18 @@ mysqld_list_fields(THD *thd, TABLE_LIST *table_list, const char *wild)
int
mysqld_dump_create_info(THD *thd, TABLE *table, int fd)
{
+ CONVERT *convert=thd->convert_set;
DBUG_ENTER("mysqld_dump_create_info");
DBUG_PRINT("enter",("table: %s",table->real_name));
+
String* packet = &thd->packet;
packet->length(0);
-
- if(store_create_info(thd,table,packet))
+ if (store_create_info(thd,table,packet))
DBUG_RETURN(-1);
- if(fd < 0)
+ if (convert)
+ convert->convert((char*) packet->ptr(), packet->length());
+ if (fd < 0)
{
if(my_net_write(&thd->net, (char*)packet->ptr(), packet->length()))
DBUG_RETURN(-1);
@@ -957,6 +962,7 @@ void mysqld_list_processes(THD *thd,const char *user, bool verbose)
List<Item> field_list;
I_List<thread_info> thread_infos;
ulong max_query_length= verbose ? max_allowed_packet : PROCESS_LIST_WIDTH;
+ CONVERT *convert=thd->convert_set;
DBUG_ENTER("mysqld_list_processes");
field_list.push_back(new Item_int("Id",0,7));
@@ -1040,28 +1046,28 @@ void mysqld_list_processes(THD *thd,const char *user, bool verbose)
char buff[20],*end;
packet->length(0);
end=int10_to_str((long) thd_info->thread_id, buff,10);
- net_store_data(packet,buff,(uint) (end-buff));
- net_store_data(packet,thd_info->user);
- net_store_data(packet,thd_info->host);
+ net_store_data(packet,convert,buff,(uint) (end-buff));
+ net_store_data(packet,convert,thd_info->user);
+ net_store_data(packet,convert,thd_info->host);
if (thd_info->db)
- net_store_data(packet,thd_info->db);
+ net_store_data(packet,convert,thd_info->db);
else
net_store_null(packet);
if (thd_info->proc_info)
- net_store_data(packet,thd_info->proc_info);
+ net_store_data(packet,convert,thd_info->proc_info);
else
- net_store_data(packet,command_name[thd_info->command]);
+ net_store_data(packet,convert,command_name[thd_info->command]);
if (thd_info->start_time)
- net_store_data(packet,(uint32)
- (time((time_t*) 0) - thd_info->start_time));
+ net_store_data(packet,
+ (uint32) (time((time_t*) 0) - thd_info->start_time));
else
net_store_null(packet);
if (thd_info->state_info)
- net_store_data(packet,thd_info->state_info);
+ net_store_data(packet,convert,thd_info->state_info);
else
net_store_null(packet);
if (thd_info->query)
- net_store_data(packet,thd_info->query);
+ net_store_data(packet,convert,thd_info->query);
else
net_store_null(packet);
if (my_net_write(&thd->net,(char*) packet->ptr(),packet->length()))
@@ -1083,6 +1089,7 @@ int mysqld_show(THD *thd, const char *wild, show_var_st *variables)
char buff[8192];
String packet2(buff,sizeof(buff));
List<Item> field_list;
+ CONVERT *convert=thd->convert_set;
DBUG_ENTER("mysqld_show");
field_list.push_back(new Item_empty_string("Variable_name",30));
field_list.push_back(new Item_empty_string("Value",256));
@@ -1096,7 +1103,7 @@ int mysqld_show(THD *thd, const char *wild, show_var_st *variables)
if (!(wild && wild[0] && wild_compare(variables[i].name,wild)))
{
packet2.length(0);
- net_store_data(&packet2,variables[i].name);
+ net_store_data(&packet2,convert,variables[i].name);
switch (variables[i].type){
case SHOW_LONG:
case SHOW_LONG_CONST:
@@ -1123,7 +1130,7 @@ int mysqld_show(THD *thd, const char *wild, show_var_st *variables)
break;
}
case SHOW_CHAR:
- net_store_data(&packet2,variables[i].value);
+ net_store_data(&packet2,convert, variables[i].value);
break;
case SHOW_STARTTIME:
net_store_data(&packet2,(uint32) (thd->query_start() - start_time));
@@ -1137,7 +1144,7 @@ int mysqld_show(THD *thd, const char *wild, show_var_st *variables)
case SHOW_CHAR_PTR:
{
char *value= *(char**) variables[i].value;
- net_store_data(&packet2,value ? value : "");
+ net_store_data(&packet2,convert, value ? value : "");
break;
}
}
diff --git a/sql/sql_sort.h b/sql/sql_sort.h
new file mode 100644
index 00000000000..498a5262b53
--- /dev/null
+++ b/sql/sql_sort.h
@@ -0,0 +1,54 @@
+/* Copyright (C) 2000 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+/* Defines used by filesort and uniques */
+
+#define MERGEBUFF 7
+#define MERGEBUFF2 15
+
+typedef struct st_buffpek { /* Struktur om sorteringsbuffrarna */
+ my_off_t file_pos; /* Where we are in the sort file */
+ uchar *base,*key; /* key pointers */
+ ha_rows count; /* Number of rows in table */
+ ulong mem_count; /* numbers of keys in memory */
+ ulong max_keys; /* Max keys in buffert */
+} BUFFPEK;
+
+
+typedef struct st_sort_param {
+ uint sort_length; /* Length of sort columns */
+ uint keys; /* Max keys / buffert */
+ uint ref_length; /* Length of record ref. */
+ ha_rows max_rows,examined_rows;
+ TABLE *sort_form; /* For quicker make_sortkey */
+ SORT_FIELD *local_sortorder;
+ SORT_FIELD *end;
+ uchar *unique_buff;
+#ifdef USE_STRCOLL
+ char* tmp_buffer;
+#endif
+} SORTPARAM;
+
+
+int merge_many_buff(SORTPARAM *param, uchar *sort_buffer,
+ BUFFPEK *buffpek,
+ uint *maxbuffer, IO_CACHE *t_file);
+uint read_to_buffer(IO_CACHE *fromfile,BUFFPEK *buffpek,
+ uint sort_length);
+int merge_buffers(SORTPARAM *param,IO_CACHE *from_file,
+ IO_CACHE *to_file, uchar *sort_buffer,
+ BUFFPEK *lastbuff,BUFFPEK *Fb,
+ BUFFPEK *Tb,int flag);
diff --git a/sql/sql_table.cc b/sql/sql_table.cc
index 207f9dd324d..b755a4d6327 100644
--- a/sql/sql_table.cc
+++ b/sql/sql_table.cc
@@ -59,9 +59,9 @@ int mysql_rm_table(THD *thd,TABLE_LIST *tables, my_bool if_exists)
VOID(pthread_mutex_lock(&LOCK_open));
pthread_mutex_unlock(&thd->mysys_var->mutex);
- if(global_read_lock)
+ if (global_read_lock)
{
- if(thd->global_read_lock)
+ if (thd->global_read_lock)
{
my_error(ER_TABLE_NOT_LOCKED_FOR_WRITE,MYF(0),
tables->real_name);
@@ -1098,12 +1098,15 @@ int mysql_alter_table(THD *thd,char *new_db, char *new_name,
List<Alter_column> &alter_list,
ORDER *order,
bool drop_primary,
- enum enum_duplicates handle_duplicates)
+ enum enum_duplicates handle_duplicates,
+ enum enum_enable_or_disable keys_onoff,
+ bool simple_alter)
{
TABLE *table,*new_table;
int error;
char tmp_name[80],old_name[32],new_name_buff[FN_REFLEN],
- *table_name,*db;
+ *table_name,*db;
+ char index_file[FN_REFLEN], data_file[FN_REFLEN];
bool use_timestamp=0;
ha_rows copied,deleted;
ulonglong next_insert_id;
@@ -1125,10 +1128,11 @@ int mysql_alter_table(THD *thd,char *new_db, char *new_name,
{
strmov(new_name_buff,new_name);
fn_same(new_name_buff,table_name,3);
+ // Check if name changed
#ifdef FN_LOWER_CASE
- if (!my_strcasecmp(new_name_buff,table_name))// Check if name changed
+ if (!strcmp(db,new_db) && !my_strcasecmp(new_name_buff,table_name))
#else
- if (!strcmp(new_name_buff,table_name)) // Check if name changed
+ if (!strcmp(db,new_db) && !strcmp(new_name_buff,table_name))
#endif
new_name=table_name; // No. Make later check easier
else
@@ -1163,39 +1167,50 @@ int mysql_alter_table(THD *thd,char *new_db, char *new_name,
if (create_info->row_type == ROW_TYPE_DEFAULT)
create_info->row_type=table->row_type;
- /* Check if the user only wants to do a simple RENAME */
+ /* In some simple cases we need not to recreate the table */
thd->proc_info="setup";
- if (new_name != table_name &&
- !fields.elements && !keys.elements && ! drop_list.elements &&
- !alter_list.elements && !drop_primary &&
- new_db_type == old_db_type && create_info->max_rows == 0 &&
- create_info->auto_increment_value == 0 && !table->tmp_table)
+ if (simple_alter)
{
- thd->proc_info="rename";
- VOID(pthread_mutex_lock(&LOCK_open));
- /* Then do a 'simple' rename of the table */
error=0;
- if (!access(new_name_buff,F_OK))
- {
- my_error(ER_TABLE_EXISTS_ERROR,MYF(0),new_name);
- error= -1;
- }
- else
+ if (new_name != table_name)
{
- *fn_ext(new_name)=0;
- close_cached_table(thd,table);
- if (mysql_rename_table(old_db_type,db,table_name,new_db,new_name))
- error= -1;
+ thd->proc_info="rename";
+ VOID(pthread_mutex_lock(&LOCK_open));
+ /* Then do a 'simple' rename of the table */
+ error=0;
+ if (!access(new_name_buff,F_OK))
+ {
+ my_error(ER_TABLE_EXISTS_ERROR,MYF(0),new_name);
+ error= -1;
+ }
+ else
+ {
+ *fn_ext(new_name)=0;
+ close_cached_table(thd,table);
+ if (mysql_rename_table(old_db_type,db,table_name,new_db,new_name))
+ error= -1;
+ }
+ if (!error && (error=ha_commit_rename(thd)))
+ {
+ my_error(ER_GET_ERRNO,MYF(0),error);
+ error=1;
+ }
+
+ VOID(pthread_cond_broadcast(&COND_refresh));
+ VOID(pthread_mutex_unlock(&LOCK_open));
}
- if (!error && (error=ha_commit_rename(thd)))
+ if (!error)
{
- my_error(ER_GET_ERRNO,MYF(0),error);
- error=1;
+ switch (keys_onoff)
+ {
+ case LEAVE_AS_IS: break;
+ case ENABLE: error=table->file->activate_all_index(thd); break;
+ case DISABLE:
+ table->file->deactivate_non_unique_index(table->file->records);
+ break;
+ }
}
-
- VOID(pthread_cond_broadcast(&COND_refresh));
- VOID(pthread_mutex_unlock(&LOCK_open));
if (!error)
{
mysql_update_log.write(thd, thd->query, thd->query_length);
@@ -1206,7 +1221,6 @@ int mysql_alter_table(THD *thd,char *new_db, char *new_name,
}
send_ok(&thd->net);
}
-
DBUG_RETURN(error);
}
@@ -1449,6 +1463,53 @@ int mysql_alter_table(THD *thd,char *new_db, char *new_name,
if (table->tmp_table)
create_info->options|=HA_LEX_CREATE_TMP_TABLE;
+ /*
+ Handling of symlinked tables:
+ If no rename:
+ Create new data file and index file on the same disk as the
+ old data and index files.
+ Copy data.
+ Rename new data file over old data file and new index file over
+ old index file.
+ Symlinks are not changed.
+
+ If rename:
+ Create new data file and index file on the same disk as the
+ old data and index files. Create also symlinks to point at
+ the new tables.
+ Copy data.
+ At end, rename temporary tables and symlinks to temporary table
+ to final table name.
+ Remove old table and old symlinks
+
+ If rename is made to another database:
+ Create new tables in new database.
+ Copy data.
+ Remove old table and symlinks.
+ */
+
+ if (!strcmp(db, new_db)) // Ignore symlink if db changed
+ {
+ if (create_info->index_file_name)
+ {
+ /* Fix index_file_name to have 'tmp_name' as basename */
+ strmov(index_file, tmp_name);
+ create_info->index_file_name=fn_same(index_file,
+ create_info->index_file_name,
+ 1);
+ }
+ if (create_info->data_file_name)
+ {
+ /* Fix data_file_name to have 'tmp_name' as basename */
+ strmov(data_file, tmp_name);
+ create_info->data_file_name=fn_same(data_file,
+ create_info->data_file_name,
+ 1);
+ }
+ }
+ else
+ create_info->data_file_name=create_info->index_file_name=0;
+
if ((error=mysql_create_table(thd, new_db, tmp_name,
create_info,
create_list,key_list,1,1))) // no logging
@@ -1694,8 +1755,8 @@ copy_data_between_tables(TABLE *from,TABLE *to,
if (setup_order(thd, &tables, fields, all_fields, order) ||
!(sortorder=make_unireg_sortorder(order, &length)) ||
- (from->found_records = filesort(&from, sortorder, length,
- (SQL_SELECT *) 0, 0L, HA_POS_ERROR,
+ (from->found_records = filesort(from, sortorder, length,
+ (SQL_SELECT *) 0, 0L, HA_POS_ERROR,
&examined_rows))
== HA_POS_ERROR)
goto err;
diff --git a/sql/sql_test.cc b/sql/sql_test.cc
index 3edfdd3d5ef..d20bc74ecb2 100644
--- a/sql/sql_test.cc
+++ b/sql/sql_test.cc
@@ -96,8 +96,7 @@ void print_cached_tables(void)
}
-void TEST_filesort(TABLE **table,SORT_FIELD *sortorder,uint s_length,
- ha_rows special)
+void TEST_filesort(SORT_FIELD *sortorder,uint s_length, ha_rows special)
{
char buff[256],buff2[256];
String str(buff,sizeof(buff)),out(buff2,sizeof(buff2));
diff --git a/sql/sql_unions.cc b/sql/sql_unions.cc
new file mode 100644
index 00000000000..55aca0f5b68
--- /dev/null
+++ b/sql/sql_unions.cc
@@ -0,0 +1,34 @@
+/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & Monty & Sinisa
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+
+/* Union of selects */
+
+#include "mysql_priv.h"
+
+/*
+ Do a union of selects
+*/
+
+
+int mysql_union(THD *thd,LEX *lex,uint no_of_selects)
+{
+ SELECT_LEX *sl;
+ for (sl=&lex->select_lex;sl;sl=sl->next)
+ {
+ }
+ return 0;
+}
diff --git a/sql/sql_update.cc b/sql/sql_update.cc
index 87a2f5c7b2b..a6ded7cef9c 100644
--- a/sql/sql_update.cc
+++ b/sql/sql_update.cc
@@ -1,15 +1,15 @@
/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB
-
+
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
-
+
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
-
+
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
@@ -40,8 +40,12 @@ static bool compare_record(TABLE *table, ulong query_id)
}
-int mysql_update(THD *thd,TABLE_LIST *table_list,List<Item> &fields,
- List<Item> &values, COND *conds,
+int mysql_update(THD *thd,
+ TABLE_LIST *table_list,
+ List<Item> &fields,
+ List<Item> &values,
+ COND *conds,
+ ORDER *order,
ha_rows limit,
enum enum_duplicates handle_duplicates,
thr_lock_type lock_type)
@@ -124,7 +128,7 @@ int mysql_update(THD *thd,TABLE_LIST *table_list,List<Item> &fields,
/* If running in safe sql mode, don't allow updates without keys */
if (!table->quick_keys)
{
- thd->lex.options|=QUERY_NO_INDEX_USED;
+ thd->lex.select_lex.options|=QUERY_NO_INDEX_USED;
if ((thd->options & OPTION_SAFE_UPDATES) && limit == HA_POS_ERROR)
{
delete select;
@@ -163,6 +167,34 @@ int mysql_update(THD *thd,TABLE_LIST *table_list,List<Item> &fields,
table->key_read=1;
table->file->extra(HA_EXTRA_KEYREAD);
}
+
+ if (order)
+ {
+ uint length;
+ SORT_FIELD *sortorder;
+ TABLE_LIST tables;
+ List<Item> fields;
+ List<Item> all_fields;
+ ha_rows examined_rows;
+
+ bzero((char*) &tables,sizeof(tables));
+ tables.table = table;
+
+ table->io_cache = (IO_CACHE *) my_malloc(sizeof(IO_CACHE),
+ MYF(MY_FAE | MY_ZEROFILL));
+ if (setup_order(thd, &tables, fields, all_fields, order) ||
+ !(sortorder=make_unireg_sortorder(order, &length)) ||
+ (table->found_records = filesort(table, sortorder, length,
+ (SQL_SELECT *) 0, 0L,
+ HA_POS_ERROR, &examined_rows))
+ == HA_POS_ERROR)
+ {
+ delete select;
+ table->time_stamp=save_time_stamp; // Restore timestamp pointer
+ DBUG_RETURN(-1);
+ }
+ }
+
init_read_record(&info,thd,table,select,0,1);
thd->proc_info="searching";
@@ -180,7 +212,7 @@ int mysql_update(THD *thd,TABLE_LIST *table_list,List<Item> &fields,
}
else
{
- if (!(test_flags & 512)) /* For debugging */
+ if (!(test_flags & 512)) /* For debugging */
{
DBUG_DUMP("record",(char*) table->record[0],table->reclength);
}
@@ -202,7 +234,7 @@ int mysql_update(THD *thd,TABLE_LIST *table_list,List<Item> &fields,
select->cond=0;
}
else
- {
+ {
select= new SQL_SELECT;
select->head=table;
}
@@ -213,7 +245,7 @@ int mysql_update(THD *thd,TABLE_LIST *table_list,List<Item> &fields,
{
delete select;
table->time_stamp=save_time_stamp; // Restore timestamp pointer
- DBUG_RETURN(-1);
+ DBUG_RETURN(-1);
}
}
diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy
index c1069d91746..c013ebe1c8c 100644
--- a/sql/sql_yacc.yy
+++ b/sql/sql_yacc.yy
@@ -21,6 +21,7 @@
#define YYINITDEPTH 100
#define YYMAXDEPTH 3200 /* Because of 64K stack */
#define Lex current_lex
+#define Select Lex->select
#include "mysql_priv.h"
#include "slave.h"
#include "sql_acl.h"
@@ -72,6 +73,13 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize);
%token END_OF_INPUT
+%token CLOSE_SYM
+%token HANDLER_SYM
+%token LAST_SYM
+%token NEXT_SYM
+%token PREV_SYM
+%token SQL_CALC_FOUND_ROWS
+
%token EQ
%token EQUAL_SYM
%token GE
@@ -122,6 +130,8 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize);
%token LOAD
%token LOCK_SYM
%token UNLOCK_SYM
+%token BINLOG_SYM
+%token EVENTS_SYM
%token ACTION
%token AGGREGATE_SYM
@@ -154,8 +164,11 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize);
%token DELAY_KEY_WRITE_SYM
%token DESC
%token DESCRIBE
+%token DIRECTORY_SYM
%token DISTINCT
+%token DISABLE_SYM
%token DYNAMIC_SYM
+%token ENABLE_SYM
%token ENCLOSED
%token ESCAPED
%token ESCAPE_SYM
@@ -184,6 +197,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize);
%token IDENT
%token IGNORE_SYM
%token INDEX
+%token INDEXES
%token INFILE
%token INNER_SYM
%token INNOBASE_SYM
@@ -455,12 +469,12 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize);
opt_escape
%type <string>
- text_string
+ text_string
%type <num>
type int_type real_type order_dir opt_field_spec set_option lock_option
udf_type if_exists opt_local opt_table_options table_options
- table_option opt_if_not_exists
+ table_option opt_if_not_exists
%type <ulong_num>
ULONG_NUM raid_types
@@ -517,13 +531,13 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize);
select_item_list select_item values_list no_braces
limit_clause delete_limit_clause fields opt_values values
procedure_list procedure_list2 procedure_item
- when_list2 expr_list2
+ when_list2 expr_list2 handler
opt_precision opt_ignore opt_column opt_restrict
grant revoke set lock unlock string_list field_options field_option
field_opt_list opt_binary table_lock_list table_lock varchar
references opt_on_delete opt_on_delete_list opt_on_delete_item use
opt_delete_options opt_delete_option
- opt_outer table_list table opt_option opt_place opt_low_priority
+ opt_outer table_list table_name opt_option opt_place opt_low_priority
opt_attribute opt_attribute_list attribute column_list column_list_id
opt_column_list grant_privileges opt_table user_list grant_option
grant_privilege grant_privilege_list
@@ -531,7 +545,10 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize);
equal optional_braces opt_key_definition key_usage_list2
opt_mi_check_type opt_to mi_check_types normal_join
table_to_table_list table_to_table opt_table_list opt_as
- END_OF_INPUT
+ handler_rkey_function handler_rkey_mode handler_read_or_scan
+ single_multi table_wild_list table_wild_one opt_wild union union_list
+ precision
+END_OF_INPUT
%type <NONE>
'-' '+' '*' '/' '%' '(' ')'
@@ -580,6 +597,7 @@ verb_clause:
| slave
| show
| truncate
+ | handler
| unlock
| update
| use
@@ -636,7 +654,6 @@ master_def:
}
-
/* create a table */
create:
@@ -660,36 +677,41 @@ create:
| CREATE opt_unique_or_fulltext INDEX ident ON table_ident
{
- Lex->sql_command= SQLCOM_CREATE_INDEX;
+ LEX *lex=Lex;
+ lex->sql_command= SQLCOM_CREATE_INDEX;
if (!add_table_to_list($6,NULL,1))
YYABORT;
- Lex->create_list.empty();
- Lex->key_list.empty();
- Lex->col_list.empty();
- Lex->change=NullS;
+ lex->create_list.empty();
+ lex->key_list.empty();
+ lex->col_list.empty();
+ lex->change=NullS;
}
'(' key_list ')'
{
- Lex->key_list.push_back(new Key($2,$4.str,Lex->col_list));
- Lex->col_list.empty();
+ LEX *lex=Lex;
+ lex->key_list.push_back(new Key($2,$4.str,lex->col_list));
+ lex->col_list.empty();
}
| CREATE DATABASE opt_if_not_exists ident
{
- Lex->sql_command=SQLCOM_CREATE_DB;
- Lex->name=$4.str;
- Lex->create_info.options=$3;
+ LEX *lex=Lex;
+ lex->sql_command=SQLCOM_CREATE_DB;
+ lex->name=$4.str;
+ lex->create_info.options=$3;
}
| CREATE udf_func_type UDF_SYM ident
{
- Lex->sql_command = SQLCOM_CREATE_FUNCTION;
- Lex->udf.name=$4.str;
- Lex->udf.name_length=$4.length;
- Lex->udf.type= $2;
+ LEX *lex=Lex;
+ lex->sql_command = SQLCOM_CREATE_FUNCTION;
+ lex->udf.name=$4.str;
+ lex->udf.name_length=$4.length;
+ lex->udf.type= $2;
}
UDF_RETURNS_SYM udf_type UDF_SONAME_SYM TEXT_STRING
{
- Lex->udf.returns=(Item_result) $7;
- Lex->udf.dl=$9.str;
+ LEX *lex=Lex;
+ lex->udf.returns=(Item_result) $7;
+ lex->udf.dl=$9.str;
}
create2:
@@ -700,8 +722,9 @@ create3:
/* empty */ {}
| opt_duplicate opt_as SELECT_SYM
{
- Lex->lock_option= (using_update_log) ? TL_READ_NO_INSERT : TL_READ;
- mysql_init_select(Lex);
+ LEX *lex=Lex;
+ lex->lock_option= (using_update_log) ? TL_READ_NO_INSERT : TL_READ;
+ mysql_init_select(lex);
}
select_options select_item_list opt_select_from {}
@@ -751,15 +774,17 @@ create_table_option:
{
/* Move the union list to the merge_list */
LEX *lex=Lex;
- TABLE_LIST *table_list= (TABLE_LIST*) lex->table_list.first;
- lex->create_info.merge_list= lex->table_list;
+ TABLE_LIST *table_list= (TABLE_LIST*) lex->select->table_list.first;
+ lex->create_info.merge_list= lex->select->table_list;
lex->create_info.merge_list.elements--;
lex->create_info.merge_list.first= (byte*) (table_list->next);
- lex->table_list.elements=1;
- lex->table_list.next= (byte**) &(table_list->next);
+ lex->select->table_list.elements=1;
+ lex->select->table_list.next= (byte**) &(table_list->next);
table_list->next=0;
lex->create_info.used_fields|= HA_CREATE_USED_UNION;
}
+ | DATA_SYM DIRECTORY_SYM EQ TEXT_STRING { Lex->create_info.data_file_name= $4.str; }
+ | INDEX DIRECTORY_SYM EQ TEXT_STRING { Lex->create_info.index_file_name= $4.str; }
table_types:
ISAM_SYM { $$= DB_TYPE_ISAM; }
@@ -807,8 +832,9 @@ field_list_item:
}
| key_type opt_ident '(' key_list ')'
{
- Lex->key_list.push_back(new Key($1,$2,Lex->col_list));
- Lex->col_list.empty(); /* Alloced by sql_alloc */
+ LEX *lex=Lex;
+ lex->key_list.push_back(new Key($1,$2,lex->col_list));
+ lex->col_list.empty(); /* Alloced by sql_alloc */
}
| opt_constraint FOREIGN KEY_SYM opt_ident '(' key_list ')' references
{
@@ -826,16 +852,18 @@ opt_constraint:
field_spec:
field_ident
{
- Lex->length=Lex->dec=0; Lex->type=0; Lex->interval=0;
- Lex->default_value=0;
+ LEX *lex=Lex;
+ lex->length=lex->dec=0; lex->type=0; lex->interval=0;
+ lex->default_value=0;
}
type opt_attribute
{
+ LEX *lex=Lex;
if (add_field_to_list($1.str,
(enum enum_field_types) $3,
- Lex->length,Lex->dec,Lex->type,
- Lex->default_value,Lex->change,
- Lex->interval))
+ lex->length,lex->dec,lex->type,
+ lex->default_value,lex->change,
+ lex->interval))
YYABORT;
}
@@ -887,12 +915,14 @@ type:
{ $$=FIELD_TYPE_DECIMAL;}
| ENUM {Lex->interval_list.empty();} '(' string_list ')'
{
- Lex->interval=typelib(Lex->interval_list);
+ LEX *lex=Lex;
+ lex->interval=typelib(lex->interval_list);
$$=FIELD_TYPE_ENUM;
}
| SET { Lex->interval_list.empty();} '(' string_list ')'
{
- Lex->interval=typelib(Lex->interval_list);
+ LEX *lex=Lex;
+ lex->interval=typelib(lex->interval_list);
$$=FIELD_TYPE_SET;
}
@@ -924,7 +954,14 @@ real_type:
float_options:
/* empty */ {}
| '(' NUM ')' { Lex->length=$2.str; }
- | '(' NUM ',' NUM ')' { Lex->length=$2.str; Lex->dec=$4.str; }
+ | precision {}
+
+precision:
+ '(' NUM ',' NUM ')'
+ {
+ LEX *lex=Lex;
+ lex->length=$2.str; lex->dec=$4.str;
+ }
field_options:
/* empty */ {}
@@ -944,7 +981,7 @@ opt_len:
opt_precision:
/* empty */ {}
- | '(' NUM ',' NUM ')' { Lex->length=$2.str; Lex->dec=$4.str; }
+ | precision {}
opt_attribute:
/* empty */ {}
@@ -1011,6 +1048,7 @@ key_or_index:
keys_or_index:
KEYS {}
| INDEX {}
+ | INDEXES {}
opt_unique_or_fulltext:
/* empty */ { $$= Key::MULTIPLE; }
@@ -1051,12 +1089,14 @@ alter:
lex->col_list.empty();
lex->drop_list.empty();
lex->alter_list.empty();
- lex->order_list.elements=0;
- lex->order_list.first=0;
- lex->order_list.next= (byte**) &lex->order_list.first;
- lex->db=lex->name=0;
+ lex->select->order_list.elements=0;
+ lex->select->order_list.first=0;
+ lex->select->order_list.next= (byte**) &lex->select->order_list.first;
+ lex->select->db=lex->name=0;
bzero((char*) &lex->create_info,sizeof(lex->create_info));
lex->create_info.db_type= DB_TYPE_DEFAULT;
+ lex->alter_keys_onoff=LEAVE_AS_IS;
+ lex->simple_alter=1;
}
alter_list
@@ -1065,42 +1105,77 @@ alter_list:
| alter_list ',' alter_list_item
add_column:
- ADD opt_column { Lex->change=0;}
+ ADD opt_column { Lex->change=0; }
alter_list_item:
- add_column field_list_item opt_place
- | add_column '(' field_list ')'
- | CHANGE opt_column field_ident { Lex->change= $3.str; } field_spec
+ add_column field_list_item opt_place { Lex->simple_alter=0; }
+ | add_column '(' field_list ')' { Lex->simple_alter=0; }
+ | CHANGE opt_column field_ident
+ {
+ LEX *lex=Lex;
+ lex->change= $3.str; lex->simple_alter=0;
+ }
+ field_spec
| MODIFY_SYM opt_column field_ident
{
- Lex->length=Lex->dec=0; Lex->type=0; Lex->interval=0;
- Lex->default_value=0;
+ LEX *lex=Lex;
+ lex->length=lex->dec=0; lex->type=0; lex->interval=0;
+ lex->default_value=0;
+ lex->simple_alter=0;
}
type opt_attribute
{
+ LEX *lex=Lex;
if (add_field_to_list($3.str,
(enum enum_field_types) $5,
- Lex->length,Lex->dec,Lex->type,
- Lex->default_value, $3.str,
- Lex->interval))
+ lex->length,lex->dec,lex->type,
+ lex->default_value, $3.str,
+ lex->interval))
YYABORT;
+ lex->simple_alter=0;
}
| DROP opt_column field_ident opt_restrict
- { Lex->drop_list.push_back(new Alter_drop(Alter_drop::COLUMN,
- $3.str)); }
- | DROP PRIMARY_SYM KEY_SYM { Lex->drop_primary=1; }
- | DROP FOREIGN KEY_SYM opt_ident {}
+ {
+ LEX *lex=Lex;
+ lex->drop_list.push_back(new Alter_drop(Alter_drop::COLUMN,
+ $3.str)); lex->simple_alter=0;
+ }
+ | DROP PRIMARY_SYM KEY_SYM
+ {
+ LEX *lex=Lex;
+ lex->drop_primary=1; lex->simple_alter=0;
+ }
+ | DROP FOREIGN KEY_SYM opt_ident { Lex->simple_alter=0; }
| DROP key_or_index field_ident
- { Lex->drop_list.push_back(new Alter_drop(Alter_drop::KEY,
- $3.str)); }
+ {
+ LEX *lex=Lex;
+ lex->drop_list.push_back(new Alter_drop(Alter_drop::KEY,
+ $3.str));
+ lex->simple_alter=0;
+ }
+ | DISABLE_SYM KEYS { Lex->alter_keys_onoff=DISABLE; }
+ | ENABLE_SYM KEYS { Lex->alter_keys_onoff=ENABLE; }
| ALTER opt_column field_ident SET DEFAULT literal
- { Lex->alter_list.push_back(new Alter_column($3.str,$6)); }
+ {
+ LEX *lex=Lex;
+ lex->alter_list.push_back(new Alter_column($3.str,$6));
+ lex->simple_alter=0;
+ }
| ALTER opt_column field_ident DROP DEFAULT
- { Lex->alter_list.push_back(new Alter_column($3.str,(Item*) 0)); }
+ {
+ LEX *lex=Lex;
+ lex->alter_list.push_back(new Alter_column($3.str,(Item*) 0));
+ lex->simple_alter=0;
+ }
| RENAME opt_to table_alias table_ident
- { Lex->db=$4->db.str ; Lex->name= $4->table.str; }
- | create_table_options
- | order_clause
+ {
+ LEX *lex=Lex;
+ lex->select->db=$4->db.str;
+ lex->name= $4->table.str;
+ lex->simple_alter=0;
+ }
+ | create_table_options { Lex->simple_alter=0; }
+ | order_clause { Lex->simple_alter=0; }
opt_column:
/* empty */ {}
@@ -1128,14 +1203,16 @@ opt_to:
slave:
SLAVE START_SYM
{
- Lex->sql_command = SQLCOM_SLAVE_START;
- Lex->type = 0;
+ LEX *lex=Lex;
+ lex->sql_command = SQLCOM_SLAVE_START;
+ lex->type = 0;
}
|
SLAVE STOP_SYM
{
- Lex->sql_command = SQLCOM_SLAVE_STOP;
- Lex->type = 0;
+ LEX *lex=Lex;
+ lex->sql_command = SQLCOM_SLAVE_STOP;
+ lex->type = 0;
};
restore:
@@ -1161,8 +1238,9 @@ backup:
repair:
REPAIR table_or_tables
{
- Lex->sql_command = SQLCOM_REPAIR;
- Lex->check_opt.init();
+ LEX *lex=Lex;
+ lex->sql_command = SQLCOM_REPAIR;
+ lex->check_opt.init();
}
table_list opt_mi_check_type
@@ -1186,24 +1264,27 @@ mi_check_type:
analyze:
ANALYZE_SYM table_or_tables
{
- Lex->sql_command = SQLCOM_ANALYZE;
- Lex->check_opt.init();
+ LEX *lex=Lex;
+ lex->sql_command = SQLCOM_ANALYZE;
+ lex->check_opt.init();
}
table_list opt_mi_check_type
check:
CHECK_SYM table_or_tables
{
- Lex->sql_command = SQLCOM_CHECK;
- Lex->check_opt.init();
+ LEX *lex=Lex;
+ lex->sql_command = SQLCOM_CHECK;
+ lex->check_opt.init();
}
table_list opt_mi_check_type
optimize:
OPTIMIZE table_or_tables
{
- Lex->sql_command = SQLCOM_OPTIMIZE;
- Lex->check_opt.init();
+ LEX *lex=Lex;
+ lex->sql_command = SQLCOM_OPTIMIZE;
+ lex->check_opt.init();
}
table_list opt_mi_check_type
@@ -1234,14 +1315,14 @@ select:
SELECT_SYM
{
LEX *lex=Lex;
- lex->sql_command= SQLCOM_SELECT;
+ if (lex->sql_command!=SQLCOM_UNION_SELECT) lex->sql_command= SQLCOM_SELECT;
lex->lock_option=TL_READ;
mysql_init_select(lex);
}
- select_options select_item_list select_into select_lock_type
+ select_options select_item_list select_into select_lock_type union
select_into:
- /* empty */
+ limit_clause {}
| select_from
| opt_into select_from
| select_from opt_into
@@ -1259,19 +1340,20 @@ select_option_list:
| select_option
select_option:
- STRAIGHT_JOIN { Lex->options|= SELECT_STRAIGHT_JOIN; }
+ STRAIGHT_JOIN { Select->options|= SELECT_STRAIGHT_JOIN; }
| HIGH_PRIORITY { Lex->lock_option= TL_READ_HIGH_PRIORITY; }
- | DISTINCT { Lex->options|= SELECT_DISTINCT; }
- | SQL_SMALL_RESULT { Lex->options|= SELECT_SMALL_RESULT; }
- | SQL_BIG_RESULT { Lex->options|= SELECT_BIG_RESULT; }
- | SQL_BUFFER_RESULT { Lex->options|= OPTION_BUFFER_RESULT; }
+ | DISTINCT { Select->options|= SELECT_DISTINCT; }
+ | SQL_SMALL_RESULT { Select->options|= SELECT_SMALL_RESULT; }
+ | SQL_BIG_RESULT { Select->options|= SELECT_BIG_RESULT; }
+ | SQL_BUFFER_RESULT { Select->options|= OPTION_BUFFER_RESULT; }
+ | SQL_CALC_FOUND_ROWS { Select->options|= OPTION_FOUND_ROWS; }
| ALL {}
select_lock_type:
/* empty */
| FOR_SYM UPDATE_SYM
{ Lex->lock_option= TL_WRITE; }
- | LOCK_SYM IN_SYM SHARE_SYM MODE_SYM
+ | IN_SYM SHARE_SYM MODE_SYM
{ Lex->lock_option= TL_READ_WITH_SHARED_LOCKS; }
select_item_list:
@@ -1451,10 +1533,10 @@ simple_expr:
| '(' expr ')' { $$= $2; }
| '{' ident expr '}' { $$= $3; }
| MATCH '(' ident_list ')' AGAINST '(' expr ')'
- { Lex->ftfunc_list.push_back(
+ { Select->ftfunc_list.push_back(
(Item_func_match *)($$=new Item_func_match(*$3,$7))); }
| MATCH ident_list AGAINST '(' expr ')'
- { Lex->ftfunc_list.push_back(
+ { Select->ftfunc_list.push_back(
(Item_func_match *)($$=new Item_func_match(*$2,$5))); }
| BINARY expr %prec NEG { $$= new Item_func_binary($2); }
| CASE_SYM opt_expr WHEN_SYM when_list opt_else END
@@ -1685,30 +1767,30 @@ sum_expr:
{ $$=new Item_sum_sum($3); }
in_sum_expr:
- { Lex->in_sum_expr++ }
+ { Select->in_sum_expr++ }
expr
{
- Lex->in_sum_expr--;
+ Select->in_sum_expr--;
$$=$2;
}
expr_list:
- { Lex->expr_list.push_front(new List<Item>); }
+ { Select->expr_list.push_front(new List<Item>); }
expr_list2
- { $$= Lex->expr_list.pop(); }
+ { $$= Select->expr_list.pop(); }
expr_list2:
- expr { Lex->expr_list.head()->push_back($1); }
- | expr_list2 ',' expr { Lex->expr_list.head()->push_back($3); }
+ expr { Select->expr_list.head()->push_back($1); }
+ | expr_list2 ',' expr { Select->expr_list.head()->push_back($3); }
ident_list:
- { Lex->expr_list.push_front(new List<Item>); }
+ { Select->expr_list.push_front(new List<Item>); }
ident_list2
- { $$= Lex->expr_list.pop(); }
+ { $$= Select->expr_list.pop(); }
ident_list2:
- simple_ident { Lex->expr_list.head()->push_back($1); }
- | ident_list2 ',' simple_ident { Lex->expr_list.head()->push_back($3); }
+ simple_ident { Select->expr_list.head()->push_back($1); }
+ | ident_list2 ',' simple_ident { Select->expr_list.head()->push_back($3); }
opt_expr:
/* empty */ { $$= NULL; }
@@ -1719,20 +1801,22 @@ opt_else:
| ELSE expr { $$= $2; }
when_list:
- { Lex->when_list.push_front(new List<Item>) }
+ { Select->when_list.push_front(new List<Item>) }
when_list2
- { $$= Lex->when_list.pop(); }
+ { $$= Select->when_list.pop(); }
when_list2:
expr THEN_SYM expr
{
- Lex->when_list.head()->push_back($1);
- Lex->when_list.head()->push_back($3);
+ SELECT_LEX *sel=Select;
+ sel->when_list.head()->push_back($1);
+ sel->when_list.head()->push_back($3);
}
| when_list2 WHEN_SYM expr THEN_SYM expr
{
- Lex->when_list.head()->push_back($3);
- Lex->when_list.head()->push_back($5);
+ SELECT_LEX *sel=Select;
+ sel->when_list.head()->push_back($3);
+ sel->when_list.head()->push_back($5);
}
opt_pad:
@@ -1747,15 +1831,21 @@ join_table_list:
| join_table_list INNER_SYM JOIN_SYM join_table ON expr
{ add_join_on($4,$6); $$=$4; }
| join_table_list INNER_SYM JOIN_SYM join_table
- { Lex->db1=$1->db; Lex->table1=$1->name;
- Lex->db2=$4->db; Lex->table2=$4->name; }
+ {
+ SELECT_LEX *sel=Select;
+ sel->db1=$1->db; sel->table1=$1->name;
+ sel->db2=$4->db; sel->table2=$4->name;
+ }
USING '(' using_list ')'
{ add_join_on($4,$8); $$=$4; }
| join_table_list LEFT opt_outer JOIN_SYM join_table ON expr
{ add_join_on($5,$7); $5->outer_join|=JOIN_TYPE_LEFT; $$=$5; }
| join_table_list LEFT opt_outer JOIN_SYM join_table
- { Lex->db1=$1->db; Lex->table1=$1->name;
- Lex->db2=$5->db; Lex->table2=$5->name; }
+ {
+ SELECT_LEX *sel=Select;
+ sel->db1=$1->db; sel->table1=$1->name;
+ sel->db2=$5->db; sel->table2=$5->name;
+ }
USING '(' using_list ')'
{ add_join_on($5,$9); $5->outer_join|=JOIN_TYPE_LEFT; $$=$5; }
| join_table_list NATURAL LEFT opt_outer JOIN_SYM join_table
@@ -1763,8 +1853,11 @@ join_table_list:
| join_table_list RIGHT opt_outer JOIN_SYM join_table ON expr
{ add_join_on($1,$7); $1->outer_join|=JOIN_TYPE_RIGHT; $$=$1; }
| join_table_list RIGHT opt_outer JOIN_SYM join_table
- { Lex->db1=$1->db; Lex->table1=$1->name;
- Lex->db2=$5->db; Lex->table2=$5->name; }
+ {
+ SELECT_LEX *sel=Select;
+ sel->db1=$1->db; sel->table1=$1->name;
+ sel->db2=$5->db; sel->table2=$5->name;
+ }
USING '(' using_list ')'
{ add_join_on($1,$9); $1->outer_join|=JOIN_TYPE_RIGHT; $$=$1; }
| join_table_list NATURAL RIGHT opt_outer JOIN_SYM join_table
@@ -1778,10 +1871,16 @@ normal_join:
| CROSS JOIN_SYM {}
join_table:
- { Lex->use_index_ptr=Lex->ignore_index_ptr=0; }
+ {
+ SELECT_LEX *sel=Select;
+ sel->use_index_ptr=sel->ignore_index_ptr=0;
+ }
table_ident opt_table_alias opt_key_definition
- { if (!($$=add_table_to_list($2,$3,0,TL_UNLOCK, Lex->use_index_ptr,
- Lex->ignore_index_ptr))) YYABORT; }
+ {
+ SELECT_LEX *sel=Select;
+ if (!($$=add_table_to_list($2,$3,0,TL_UNLOCK, sel->use_index_ptr,
+ sel->ignore_index_ptr))) YYABORT;
+ }
| '{' ident join_table LEFT OUTER JOIN_SYM join_table ON expr '}'
{ add_join_on($7,$9); $7->outer_join|=JOIN_TYPE_LEFT; $$=$7; }
@@ -1792,30 +1891,41 @@ opt_outer:
opt_key_definition:
/* empty */ {}
| USE_SYM key_usage_list
- { Lex->use_index= *$2; Lex->use_index_ptr= &Lex->use_index; }
+ {
+ SELECT_LEX *sel=Select;
+ sel->use_index= *$2;
+ sel->use_index_ptr= &sel->use_index;
+ }
| IGNORE_SYM key_usage_list
- { Lex->ignore_index= *$2; Lex->ignore_index_ptr= &Lex->ignore_index;}
+ {
+ SELECT_LEX *sel=Select;
+ sel->ignore_index= *$2;
+ sel->ignore_index_ptr= &sel->ignore_index;
+ }
key_usage_list:
- key_or_index { Lex->interval_list.empty() } '(' key_usage_list2 ')'
- { $$= &Lex->interval_list; }
+ key_or_index { Select->interval_list.empty() } '(' key_usage_list2 ')'
+ { $$= &Select->interval_list; }
key_usage_list2:
key_usage_list2 ',' ident
- { Lex->interval_list.push_back(new String((const char*) $3.str,$3.length)); }
+ { Select->interval_list.push_back(new String((const char*) $3.str,$3.length)); }
| ident
- { Lex->interval_list.push_back(new String((const char*) $1.str,$1.length)); }
+ { Select->interval_list.push_back(new String((const char*) $1.str,$1.length)); }
| PRIMARY_SYM
- { Lex->interval_list.push_back(new String("PRIMARY",7)); }
+ { Select->interval_list.push_back(new String("PRIMARY",7)); }
using_list:
ident
- { if (!($$= new Item_func_eq(new Item_field(Lex->db1,Lex->table1, $1.str), new Item_field(Lex->db2,Lex->table2,$1.str))))
+ {
+ SELECT_LEX *sel=Select;
+ if (!($$= new Item_func_eq(new Item_field(sel->db1,sel->table1, $1.str), new Item_field(sel->db2,sel->table2,$1.str))))
YYABORT;
}
| using_list ',' ident
{
- if (!($$= new Item_cond_and(new Item_func_eq(new Item_field(Lex->db1,Lex->table1,$3.str), new Item_field(Lex->db2,Lex->table2,$3.str)), $1)))
+ SELECT_LEX *sel=Select;
+ if (!($$= new Item_cond_and(new Item_func_eq(new Item_field(sel->db1,sel->table1,$3.str), new Item_field(sel->db2,sel->table2,$3.str)), $1)))
YYABORT;
}
@@ -1846,13 +1956,16 @@ opt_table_alias:
where_clause:
- /* empty */ { Lex->where= 0; }
- | WHERE expr { Lex->where= $2; }
+ /* empty */ { Select->where= 0; }
+ | WHERE expr { Select->where= $2; }
having_clause:
/* empty */
- | HAVING { Lex->create_refs=1; } expr
- { Lex->having= $3; Lex->create_refs=0; }
+ | HAVING { Select->create_refs=1; } expr
+ {
+ SELECT_LEX *sel=Select;
+ sel->having= $3; sel->create_refs=0;
+ }
opt_escape:
ESCAPE_SYM TEXT_STRING { $$= $2.str; }
@@ -1882,7 +1995,7 @@ opt_order_clause:
| order_clause
order_clause:
- ORDER_SYM BY { Lex->sort_default=1; } order_list
+ ORDER_SYM BY { Select->sort_default=1; } order_list
order_list:
order_list ',' order_ident order_dir
@@ -1892,38 +2005,46 @@ order_list:
order_dir:
/* empty */ { $$ = 1; }
- | ASC { $$ = Lex->sort_default=1; }
- | DESC { $$ = Lex->sort_default=0; }
+ | ASC { $$ = Select->sort_default=1; }
+ | DESC { $$ = Select->sort_default=0; }
limit_clause:
/* empty */
{
- Lex->select_limit= current_thd->default_select_limit;
- Lex->offset_limit= 0L;
+ SELECT_LEX *sel=Select;
+ sel->select_limit= (Lex->sql_command == SQLCOM_HA_READ) ?
+ 1 : current_thd->default_select_limit;
+ sel->offset_limit= 0L;
}
| LIMIT ULONG_NUM
- { Lex->select_limit= $2; Lex->offset_limit=0L; }
+ {
+ SELECT_LEX *sel=Select;
+ sel->select_limit= $2; sel->offset_limit=0L;
+ }
| LIMIT ULONG_NUM ',' ULONG_NUM
- { Lex->select_limit= $4; Lex->offset_limit=$2; }
+ {
+ SELECT_LEX *sel=Select;
+ sel->select_limit= $4; sel->offset_limit=$2;
+ }
delete_limit_clause:
/* empty */
{
- Lex->select_limit= HA_POS_ERROR;
+ Select->select_limit= HA_POS_ERROR;
}
| LIMIT ULONGLONG_NUM
- { Lex->select_limit= (ha_rows) $2; }
+ { Select->select_limit= (ha_rows) $2; }
ULONG_NUM:
- NUM { $$= strtoul($1.str,NULL,10); }
- | REAL_NUM { $$= strtoul($1.str,NULL,10); }
+ NUM { $$= strtoul($1.str,NULL,10); }
+ | REAL_NUM { $$= strtoul($1.str,NULL,10); }
| FLOAT_NUM { $$= strtoul($1.str,NULL,10); }
ULONGLONG_NUM:
- NUM { $$= (ulonglong) strtoul($1.str,NULL,10); }
- | LONG_NUM { $$= strtoull($1.str,NULL,10); }
- | REAL_NUM { $$= strtoull($1.str,NULL,10); }
+ NUM { $$= (ulonglong) strtoul($1.str,NULL,10); }
+ | LONG_NUM { $$= strtoull($1.str,NULL,10); }
+ | REAL_NUM { $$= strtoull($1.str,NULL,10); }
| FLOAT_NUM { $$= strtoull($1.str,NULL,10); }
procedure_clause:
@@ -1978,36 +2099,40 @@ opt_into:
drop:
DROP TABLE_SYM if_exists table_list opt_restrict
{
- Lex->sql_command = SQLCOM_DROP_TABLE;
- Lex->drop_if_exists = $3;
+ LEX *lex=Lex;
+ lex->sql_command = SQLCOM_DROP_TABLE;
+ lex->drop_if_exists = $3;
}
| DROP INDEX ident ON table_ident {}
{
- Lex->sql_command= SQLCOM_DROP_INDEX;
- Lex->drop_list.empty();
- Lex->drop_list.push_back(new Alter_drop(Alter_drop::KEY,
+ LEX *lex=Lex;
+ lex->sql_command= SQLCOM_DROP_INDEX;
+ lex->drop_list.empty();
+ lex->drop_list.push_back(new Alter_drop(Alter_drop::KEY,
$3.str));
if (!add_table_to_list($5,NULL, 1))
YYABORT;
}
| DROP DATABASE if_exists ident
{
- Lex->sql_command= SQLCOM_DROP_DB;
- Lex->drop_if_exists=$3;
- Lex->name=$4.str;
+ LEX *lex=Lex;
+ lex->sql_command= SQLCOM_DROP_DB;
+ lex->drop_if_exists=$3;
+ lex->name=$4.str;
}
| DROP UDF_SYM ident
{
- Lex->sql_command = SQLCOM_DROP_FUNCTION;
- Lex->udf.name=$3.str;
+ LEX *lex=Lex;
+ lex->sql_command = SQLCOM_DROP_FUNCTION;
+ lex->udf.name=$3.str;
}
table_list:
- table
- | table_list ',' table
+ table_name
+ | table_list ',' table_name
-table:
+table_name:
table_ident
{ if (!add_table_to_list($1,NULL,1)) YYABORT; }
@@ -2040,19 +2165,21 @@ insert2:
| insert_table {}
insert_table:
- table
+ table_name
{
- Lex->field_list.empty();
- Lex->many_values.empty();
- Lex->insert_list=0;
+ LEX *lex=Lex;
+ lex->field_list.empty();
+ lex->many_values.empty();
+ lex->insert_list=0;
}
insert_field_spec:
opt_field_spec insert_values {}
| SET
{
- if (!(Lex->insert_list = new List_item) ||
- Lex->many_values.push_back(Lex->insert_list))
+ LEX *lex=Lex;
+ if (!(lex->insert_list = new List_item) ||
+ lex->many_values.push_back(lex->insert_list))
YYABORT;
}
ident_eq_list
@@ -2090,8 +2217,9 @@ ident_eq_list:
ident_eq_value:
simple_ident equal expr
{
- if (Lex->field_list.push_back($1) ||
- Lex->insert_list->push_back($3))
+ LEX *lex=Lex;
+ if (lex->field_list.push_back($1) ||
+ lex->insert_list->push_back($3))
YYABORT;
}
@@ -2106,7 +2234,8 @@ no_braces:
}
opt_values ')'
{
- if (Lex->many_values.push_back(Lex->insert_list))
+ LEX *lex=Lex;
+ if (lex->many_values.push_back(lex->insert_list))
YYABORT;
}
@@ -2129,8 +2258,18 @@ values:
/* Update rows in a table */
update:
- UPDATE_SYM opt_low_priority opt_ignore table SET update_list where_clause delete_limit_clause
- { Lex->sql_command = SQLCOM_UPDATE; }
+ UPDATE_SYM opt_low_priority opt_ignore table_name
+ SET update_list
+ where_clause
+ opt_order_clause
+ delete_limit_clause
+ {
+ LEX *lex=Lex;
+ lex->sql_command = SQLCOM_UPDATE;
+ lex->select->order_list.elements=0;
+ lex->select->order_list.first=0;
+ lex->select->order_list.next= (byte**) &lex->select->order_list.first;
+ }
update_list:
update_list ',' simple_ident equal expr
@@ -2152,31 +2291,82 @@ opt_low_priority:
delete:
DELETE_SYM
- {
- Lex->sql_command= SQLCOM_DELETE; Lex->options=0;
- Lex->lock_option= current_thd->update_lock_default;
+ {
+ LEX *lex=Lex;
+ lex->sql_command= SQLCOM_DELETE; lex->select->options=0;
+ lex->lock_option= lex->thd->update_lock_default;
+ lex->select->order_list.elements=0;
+ lex->select->order_list.first=0;
+ lex->select->order_list.next= (byte**) &lex->select->order_list.first;
}
- opt_delete_options FROM table
- where_clause delete_limit_clause
+ opt_delete_options single_multi {}
+
+single_multi:
+ FROM table_name where_clause opt_order_clause delete_limit_clause {}
+ | table_wild_list
+ {
+ LEX *lex=Lex;
+ lex->sql_command = SQLCOM_MULTI_DELETE;
+ mysql_init_select(lex);
+ lex->select->select_limit=HA_POS_ERROR;
+ lex->auxilliary_table_list.elements=0;
+ lex->auxilliary_table_list.first=0;
+ lex->auxilliary_table_list.next= (byte**) &(lex->auxilliary_table_list.first);
+ }
+ FROM
+ {
+ LEX *lex=Lex;
+ lex->auxilliary_table_list=lex->select_lex.table_list;
+ lex->select->table_list.elements=0;
+ lex->select->table_list.first=0;
+ lex->select->table_list.next= (byte**) &(lex->select->table_list.first);
+ } join_table_list where_clause
+
+
+table_wild_list:
+ table_wild_one {}
+ | table_wild_list ',' table_wild_one {}
+
+table_wild_one:
+ ident opt_wild
+ {
+ if (!add_table_to_list(new Table_ident($1),NULL,1,TL_WRITE))
+ YYABORT;
+ }
+ | ident '.' ident opt_wild
+ {
+ if (!add_table_to_list(new Table_ident($1,$3,0),NULL,1,TL_WRITE))
+ YYABORT;
+ }
+
+opt_wild:
+ /* empty */ {}
+ | '.' '*' {}
opt_delete_options:
- /* empty */ {}
+ /* empty */ {}
| opt_delete_option opt_delete_options {}
opt_delete_option:
- QUICK { Lex->options|= OPTION_QUICK; }
+ QUICK { Select->options|= OPTION_QUICK; }
| LOW_PRIORITY { Lex->lock_option= TL_WRITE_LOW_PRIORITY; }
truncate:
- TRUNCATE_SYM opt_table_sym table
- { Lex->sql_command= SQLCOM_TRUNCATE; Lex->options=0;
- Lex->lock_option= current_thd->update_lock_default; }
+ TRUNCATE_SYM opt_table_sym table_name
+ {
+ LEX* lex = Lex;
+ lex->sql_command= SQLCOM_TRUNCATE;
+ lex->select->options=0;
+ lex->select->order_list.elements=0;
+ lex->select->order_list.first=0;
+ lex->select->order_list.next= (byte**) &lex->select->order_list.first;
+ lex->lock_option= current_thd->update_lock_default; }
opt_table_sym:
/* empty */
| TABLE_SYM
-
+
/* Show things */
show: SHOW { Lex->wild=0;} show_param
@@ -2185,18 +2375,26 @@ show_param:
DATABASES wild
{ Lex->sql_command= SQLCOM_SHOW_DATABASES; }
| TABLES opt_db wild
- { Lex->sql_command= SQLCOM_SHOW_TABLES; Lex->db= $2; Lex->options=0;}
+ {
+ LEX *lex=Lex;
+ lex->sql_command= SQLCOM_SHOW_TABLES;
+ lex->select->db= $2; lex->select->options=0;
+ }
| TABLE_SYM STATUS_SYM opt_db wild
- { Lex->sql_command= SQLCOM_SHOW_TABLES;
- Lex->options|= SELECT_DESCRIBE;
- Lex->db= $3;
+ {
+ LEX *lex=Lex;
+ lex->sql_command= SQLCOM_SHOW_TABLES;
+ lex->select->options|= SELECT_DESCRIBE;
+ lex->select->db= $3;
}
| OPEN_SYM TABLES opt_db wild
- { Lex->sql_command= SQLCOM_SHOW_OPEN_TABLES;
- Lex->db= $3;
- Lex->options=0;
+ {
+ LEX *lex=Lex;
+ lex->sql_command= SQLCOM_SHOW_OPEN_TABLES;
+ lex->select->db= $3;
+ lex->select->options=0;
}
- | opt_full COLUMNS FROM table_ident opt_db wild
+ | opt_full COLUMNS from_or_in table_ident opt_db wild
{
Lex->sql_command= SQLCOM_SHOW_FIELDS;
if ($5)
@@ -2207,7 +2405,15 @@ show_param:
| MASTER_SYM LOGS_SYM
{
Lex->sql_command = SQLCOM_SHOW_BINLOGS;
- }
+ }
+ | SLAVE HOSTS_SYM
+ {
+ Lex->sql_command = SQLCOM_SHOW_SLAVE_HOSTS;
+ }
+ | BINLOG_SYM EVENTS_SYM binlog_in binlog_from limit_clause
+ {
+ Lex->sql_command = SQLCOM_SHOW_BINLOG_EVENTS;
+ }
| keys_or_index FROM table_ident opt_db
{
Lex->sql_command= SQLCOM_SHOW_KEYS;
@@ -2225,8 +2431,12 @@ show_param:
| LOGS_SYM
{ Lex->sql_command= SQLCOM_SHOW_LOGS; }
| GRANTS FOR_SYM user
- { Lex->sql_command= SQLCOM_SHOW_GRANTS;
- Lex->grant_user=$3; Lex->grant_user->password.str=NullS; }
+ {
+ LEX *lex=Lex;
+ lex->sql_command= SQLCOM_SHOW_GRANTS;
+ lex->grant_user=$3;
+ lex->grant_user->password.str=NullS;
+ }
| CREATE TABLE_SYM table_ident
{
Lex->sql_command = SQLCOM_SHOW_CREATE;
@@ -2244,7 +2454,7 @@ show_param:
opt_db:
/* empty */ { $$= 0; }
- | FROM ident { $$= $2.str; }
+ | from_or_in ident { $$= $2.str; }
wild:
/* empty */
@@ -2254,18 +2464,32 @@ opt_full:
/* empty */ { Lex->verbose=0; }
| FULL { Lex->verbose=1; }
+from_or_in:
+ FROM
+ | IN_SYM
+
+binlog_in:
+ /* empty */ { Lex->mi.log_file_name = 0; }
+ | IN_SYM TEXT_STRING { Lex->mi.log_file_name = $2.str; }
+
+binlog_from:
+ /* empty */ { Lex->mi.pos = 4; /* skip magic number */ }
+ | FROM ULONGLONG_NUM { Lex->mi.pos = $2; }
+
+
/* A Oracle compatible synonym for show */
describe:
describe_command table_ident
{
- Lex->wild=0;
- Lex->verbose=0;
- Lex->sql_command=SQLCOM_SHOW_FIELDS;
+ LEX *lex=Lex;
+ lex->wild=0;
+ lex->verbose=0;
+ lex->sql_command=SQLCOM_SHOW_FIELDS;
if (!add_table_to_list($2, NULL,0))
YYABORT;
}
opt_describe_column
- | describe_command select { Lex->options|= SELECT_DESCRIBE };
+ | describe_command select { Select->options|= SELECT_DESCRIBE };
describe_command:
@@ -2281,7 +2505,12 @@ opt_describe_column:
/* flush things */
flush:
- FLUSH_SYM {Lex->sql_command= SQLCOM_FLUSH; Lex->type=0; } flush_options
+ FLUSH_SYM
+ {
+ LEX *lex=Lex;
+ lex->sql_command= SQLCOM_FLUSH; lex->type=0;
+ }
+ flush_options
flush_options:
flush_options ',' flush_option
@@ -2302,8 +2531,11 @@ opt_table_list:
| table_list {}
reset:
- RESET_SYM {Lex->sql_command= SQLCOM_RESET; Lex->type=0; } reset_options
-
+ RESET_SYM
+ {
+ LEX *lex=Lex;
+ lex->sql_command= SQLCOM_RESET; lex->type=0;
+ } reset_options
reset_options:
reset_options ',' reset_option
| reset_option
@@ -2313,7 +2545,12 @@ reset_option:
| MASTER_SYM { Lex->type|= REFRESH_MASTER; }
purge:
- PURGE { Lex->sql_command = SQLCOM_PURGE; Lex->type=0;}
+ PURGE
+ {
+ LEX *lex=Lex;
+ lex->sql_command = SQLCOM_PURGE;
+ lex->type=0;
+ }
MASTER_SYM LOGS_SYM TO_SYM TEXT_STRING
{
Lex->to_log = $6.str;
@@ -2324,29 +2561,34 @@ purge:
kill:
KILL_SYM expr
{
- if ($2->fix_fields(current_thd,0))
- {
- send_error(&current_thd->net, ER_SET_CONSTANTS_ONLY);
- YYABORT;
- }
- Lex->sql_command=SQLCOM_KILL;
- Lex->thread_id= (ulong) $2->val_int();
+ LEX *lex=Lex;
+ if ($2->fix_fields(lex->thd,0))
+ {
+ send_error(&lex->thd->net, ER_SET_CONSTANTS_ONLY);
+ YYABORT;
+ }
+ lex->sql_command=SQLCOM_KILL;
+ lex->thread_id= (ulong) $2->val_int();
}
/* change database */
use: USE_SYM ident
- { Lex->sql_command=SQLCOM_CHANGE_DB; Lex->db= $2.str; }
+ {
+ LEX *lex=Lex;
+ lex->sql_command=SQLCOM_CHANGE_DB; lex->select->db= $2.str;
+ }
/* import, export of files */
load: LOAD DATA_SYM load_data_lock opt_local INFILE TEXT_STRING
{
- Lex->sql_command= SQLCOM_LOAD;
- Lex->local_file= $4;
- if (!(Lex->exchange= new sql_exchange($6.str,0)))
+ LEX *lex=Lex;
+ lex->sql_command= SQLCOM_LOAD;
+ lex->local_file= $4;
+ if (!(lex->exchange= new sql_exchange($6.str,0)))
YYABORT;
- Lex->field_list.empty();
+ lex->field_list.empty();
}
opt_duplicate INTO TABLE_SYM table_ident opt_field_term opt_line_term
opt_ignore_lines opt_field_spec
@@ -2362,6 +2604,11 @@ load: LOAD DATA_SYM load_data_lock opt_local INFILE TEXT_STRING
YYABORT;
}
+ |
+ LOAD DATA_SYM FROM MASTER_SYM
+ {
+ Lex->sql_command = SQLCOM_LOAD_MASTER_DATA;
+ }
opt_local:
/* empty */ { $$=0;}
@@ -2389,7 +2636,11 @@ field_term_list:
field_term:
TERMINATED BY text_string { Lex->exchange->field_term= $3;}
| OPTIONALLY ENCLOSED BY text_string
- { Lex->exchange->enclosed= $4; Lex->exchange->opt_enclosed=1;}
+ {
+ LEX *lex=Lex;
+ lex->exchange->enclosed= $4;
+ lex->exchange->opt_enclosed=1;
+ }
| ENCLOSED BY text_string { Lex->exchange->enclosed= $3;}
| ESCAPED BY text_string { Lex->exchange->escaped= $3;}
@@ -2459,13 +2710,25 @@ order_ident:
simple_ident:
ident
- { $$ = !Lex->create_refs || Lex->in_sum_expr > 0 ? (Item*) new Item_field(NullS,NullS,$1.str) : (Item*) new Item_ref(NullS,NullS,$1.str); }
+ {
+ SELECT_LEX *sel=Select;
+ $$ = !sel->create_refs || sel->in_sum_expr > 0 ? (Item*) new Item_field(NullS,NullS,$1.str) : (Item*) new Item_ref(NullS,NullS,$1.str);
+ }
| ident '.' ident
- { $$ = !Lex->create_refs || Lex->in_sum_expr > 0 ? (Item*) new Item_field(NullS,$1.str,$3.str) : (Item*) new Item_ref(NullS,$1.str,$3.str); }
+ {
+ SELECT_LEX *sel=Select;
+ $$ = !sel->create_refs || sel->in_sum_expr > 0 ? (Item*) new Item_field(NullS,$1.str,$3.str) : (Item*) new Item_ref(NullS,$1.str,$3.str);
+ }
| '.' ident '.' ident
- { $$ = !Lex->create_refs || Lex->in_sum_expr > 0 ? (Item*) new Item_field(NullS,$2.str,$4.str) : (Item*) new Item_ref(NullS,$2.str,$4.str); }
+ {
+ SELECT_LEX *sel=Select;
+ $$ = !sel->create_refs || sel->in_sum_expr > 0 ? (Item*) new Item_field(NullS,$2.str,$4.str) : (Item*) new Item_ref(NullS,$2.str,$4.str);
+ }
| ident '.' ident '.' ident
- { $$ = !Lex->create_refs || Lex->in_sum_expr > 0 ? (Item*) new Item_field((current_thd->client_capabilities & CLIENT_NO_SCHEMA ? NullS :$1.str),$3.str,$5.str) : (Item*) new Item_ref((current_thd->client_capabilities & CLIENT_NO_SCHEMA ? NullS :$1.str),$3.str,$5.str); }
+ {
+ SELECT_LEX *sel=Select;
+ $$ = !sel->create_refs || sel->in_sum_expr > 0 ? (Item*) new Item_field((current_thd->client_capabilities & CLIENT_NO_SCHEMA ? NullS :$1.str),$3.str,$5.str) : (Item*) new Item_ref((current_thd->client_capabilities & CLIENT_NO_SCHEMA ? NullS :$1.str),$3.str,$5.str);
+ }
field_ident:
@@ -2482,10 +2745,11 @@ ident:
IDENT { $$=$1; }
| keyword
{
+ LEX *lex;
$$.str=sql_strmake($1.str,$1.length);
$$.length=$1.length;
- if (Lex->next_state != STATE_END)
- Lex->next_state=STATE_OPERATOR_OR_IDENT;
+ if ((lex=Lex)->next_state != STATE_END)
+ lex->next_state=STATE_OPERATOR_OR_IDENT;
}
ident_or_text:
@@ -2526,6 +2790,7 @@ keyword:
| CHANGED {}
| CHECKSUM_SYM {}
| CHECK_SYM {}
+ | CLOSE_SYM {}
| COMMENT_SYM {}
| COMMIT_SYM {}
| COMMITTED_SYM {}
@@ -2535,9 +2800,12 @@ keyword:
| DATETIME {}
| DATE_SYM {}
| DAY_SYM {}
+ | DIRECTORY_SYM {}
| DELAY_KEY_WRITE_SYM {}
+ | DISABLE_SYM {}
| DUMPFILE {}
| DYNAMIC_SYM {}
+ | ENABLE_SYM {}
| END {}
| ENUM {}
| ESCAPE_SYM {}
@@ -2552,12 +2820,15 @@ keyword:
| GEMINI_SYM {}
| GLOBAL_SYM {}
| HEAP_SYM {}
+ | HANDLER_SYM {}
| HOSTS_SYM {}
| HOUR_SYM {}
| IDENTIFIED_SYM {}
+ | INDEXES {}
| ISOLATION {}
| ISAM_SYM {}
| INNOBASE_SYM {}
+ | LAST_SYM {}
| LEVEL_SYM {}
| LOCAL_SYM {}
| LOGS_SYM {}
@@ -2580,10 +2851,12 @@ keyword:
| MYISAM_SYM {}
| NATIONAL_SYM {}
| NCHAR_SYM {}
+ | NEXT_SYM {}
| NO_SYM {}
| OPEN_SYM {}
| PACK_KEYS_SYM {}
| PASSWORD {}
+ | PREV_SYM {}
| PROCESS {}
| PROCESSLIST_SYM {}
| QUICK {}
@@ -2629,12 +2902,14 @@ keyword:
set:
SET opt_option
{
- THD *thd=current_thd;
- Lex->sql_command= SQLCOM_SET_OPTION;
- Lex->options=thd->options;
- Lex->select_limit=thd->default_select_limit;
- Lex->gemini_spin_retries=thd->gemini_spin_retries;
- Lex->tx_isolation=thd->tx_isolation;
+ LEX *lex=Lex;
+ lex->sql_command= SQLCOM_SET_OPTION;
+ lex->select->options=lex->thd->options;
+ lex->select->select_limit=lex->thd->default_select_limit;
+ lex->gemini_spin_retries=lex->thd->gemini_spin_retries;
+ lex->tx_isolation=lex->thd->tx_isolation;
+ lex->option_type=0;
+ lex->option_list.empty()
}
option_value_list
@@ -2644,36 +2919,41 @@ opt_option:
option_value_list:
option_value
+ | GLOBAL_SYM { Lex->option_type=1; } option_value
+ | LOCAL_SYM { Lex->option_type=0; } option_value
| option_value_list ',' option_value
option_value:
set_option equal NUM
{
+ SELECT_LEX *sel=Select;
if (atoi($3.str) == 0)
- Lex->options&= ~$1;
+ sel->options&= ~$1;
else
- Lex->options|= $1;
+ sel->options|= $1;
}
| set_isolation
| AUTOCOMMIT equal NUM
{
+ SELECT_LEX *sel=Select;
if (atoi($3.str) != 0) /* Test NOT AUTOCOMMIT */
- Lex->options&= ~(OPTION_NOT_AUTO_COMMIT);
+ sel->options&= ~(OPTION_NOT_AUTO_COMMIT);
else
- Lex->options|= OPTION_NOT_AUTO_COMMIT;
+ sel->options|= OPTION_NOT_AUTO_COMMIT;
}
| SQL_SELECT_LIMIT equal ULONG_NUM
{
- Lex->select_limit= $3;
+ Select->select_limit= $3;
}
| SQL_SELECT_LIMIT equal DEFAULT
{
- Lex->select_limit= HA_POS_ERROR;
+ Select->select_limit= HA_POS_ERROR;
}
| SQL_MAX_JOIN_SIZE equal ULONG_NUM
{
- current_thd->max_join_size= $3;
- Lex->options&= ~OPTION_BIG_SELECTS;
+ LEX *lex=Lex;
+ lex->thd->max_join_size= $3;
+ lex->select->options&= ~OPTION_BIG_SELECTS;
}
| SQL_MAX_JOIN_SIZE equal DEFAULT
{
@@ -2748,6 +3028,28 @@ option_value:
slave_skip_counter = $3;
pthread_mutex_unlock(&LOCK_slave);
}
+ | ident equal DEFAULT
+ {
+ LEX *lex=Lex;
+ lex->option_list.push_back(new Set_option(lex->option_type,
+ $1.str,$1.length,
+ (Item*) 0));
+ }
+ | ident equal expr
+ {
+ THD *thd=current_thd;
+ Item *item= $3;
+ if (item->fix_fields(current_thd,0))
+ {
+ send_error(&thd->net, ER_SET_CONSTANTS_ONLY);
+ YYABORT;
+ }
+ thd->lex.option_list.
+ push_back(new Set_option(thd->lex.option_type,
+ $1.str,$1.length,
+ item));
+ }
+
text_or_password:
TEXT_STRING { $$=$1.str;}
@@ -2796,7 +3098,10 @@ set_isolation:
default_tx_isolation_name=tx_isolation_typelib.type_names[default_tx_isolation];
}
| SESSION_SYM tx_isolation
- { current_thd->session_tx_isolation= Lex->tx_isolation= $2; }
+ {
+ LEX *lex=Lex;
+ lex->thd->session_tx_isolation= lex->tx_isolation= $2;
+ }
| tx_isolation
{ Lex->tx_isolation= $1; }
@@ -2840,27 +3145,82 @@ unlock:
UNLOCK_SYM table_or_tables { Lex->sql_command=SQLCOM_UNLOCK_TABLES; }
+/*
+** Handler: direct access to ISAM functions
+*/
+
+handler:
+ HANDLER_SYM table_ident OPEN_SYM opt_table_alias
+ {
+ Lex->sql_command = SQLCOM_HA_OPEN;
+ if (!add_table_to_list($2,$4,0))
+ YYABORT;
+ }
+ | HANDLER_SYM table_ident CLOSE_SYM
+ {
+ Lex->sql_command = SQLCOM_HA_CLOSE;
+ if (!add_table_to_list($2,0,0))
+ YYABORT;
+ }
+ | HANDLER_SYM table_ident READ_SYM handler_read_or_scan
+ {
+ Lex->sql_command = SQLCOM_HA_READ;
+ if (!add_table_to_list($2,0,0))
+ YYABORT;
+ }
+ where_clause limit_clause { }
+
+handler_read_or_scan:
+ handler_scan_function { Lex->backup_dir= 0; }
+ | ident handler_rkey_function { Lex->backup_dir= $1.str; }
+
+handler_scan_function:
+ FIRST_SYM { Lex->ha_read_mode = RFIRST; }
+ | NEXT_SYM { Lex->ha_read_mode = RNEXT; }
+
+handler_rkey_function:
+ FIRST_SYM { Lex->ha_read_mode = RFIRST; }
+ | NEXT_SYM { Lex->ha_read_mode = RNEXT; }
+ | PREV_SYM { Lex->ha_read_mode = RPREV; }
+ | LAST_SYM { Lex->ha_read_mode = RLAST; }
+ | handler_rkey_mode
+ {
+ LEX *lex=Lex;
+ lex->ha_read_mode = RKEY;
+ if (!(lex->insert_list = new List_item))
+ YYABORT;
+ } '(' values ')' { }
+
+handler_rkey_mode:
+ EQ { Lex->ha_rkey_mode=HA_READ_KEY_EXACT; }
+ | GE { Lex->ha_rkey_mode=HA_READ_KEY_OR_NEXT; }
+ | LE { Lex->ha_rkey_mode=HA_READ_KEY_OR_PREV; }
+ | GT_SYM { Lex->ha_rkey_mode=HA_READ_AFTER_KEY; }
+ | LT { Lex->ha_rkey_mode=HA_READ_BEFORE_KEY; }
+
/* GRANT / REVOKE */
revoke:
REVOKE
{
- Lex->sql_command = SQLCOM_REVOKE;
- Lex->users_list.empty();
- Lex->columns.empty();
- Lex->grant= Lex->grant_tot_col=0;
- Lex->db=0;
+ LEX *lex=Lex;
+ lex->sql_command = SQLCOM_REVOKE;
+ lex->users_list.empty();
+ lex->columns.empty();
+ lex->grant= lex->grant_tot_col=0;
+ lex->select->db=0;
}
grant_privileges ON opt_table FROM user_list
grant:
GRANT
{
- Lex->sql_command = SQLCOM_GRANT;
- Lex->users_list.empty();
- Lex->columns.empty();
- Lex->grant= Lex->grant_tot_col=0;
- Lex->db=0;
+ LEX *lex=Lex;
+ lex->sql_command = SQLCOM_GRANT;
+ lex->users_list.empty();
+ lex->columns.empty();
+ lex->grant= lex->grant_tot_col=0;
+ lex->select->db=0;
}
grant_privileges ON opt_table TO_SYM user_list
grant_option
@@ -2900,43 +3260,47 @@ grant_privilege:
opt_table:
'*'
{
- Lex->db=current_thd->db;
- if (Lex->grant == UINT_MAX)
- Lex->grant = DB_ACLS & ~GRANT_ACL;
- else if (Lex->columns.elements)
+ LEX *lex=Lex;
+ lex->select->db=lex->thd->db;
+ if (lex->grant == UINT_MAX)
+ lex->grant = DB_ACLS & ~GRANT_ACL;
+ else if (lex->columns.elements)
{
- net_printf(&current_thd->net,ER_ILLEGAL_GRANT_FOR_TABLE);
+ net_printf(&lex->thd->net,ER_ILLEGAL_GRANT_FOR_TABLE);
YYABORT;
- }
+ }
}
| ident '.' '*'
{
- Lex->db = $1.str;
- if (Lex->grant == UINT_MAX)
- Lex->grant = DB_ACLS & ~GRANT_ACL;
- else if (Lex->columns.elements)
+ LEX *lex=Lex;
+ lex->select->db = $1.str;
+ if (lex->grant == UINT_MAX)
+ lex->grant = DB_ACLS & ~GRANT_ACL;
+ else if (lex->columns.elements)
{
- net_printf(&current_thd->net,ER_ILLEGAL_GRANT_FOR_TABLE);
+ net_printf(&lex->thd->net,ER_ILLEGAL_GRANT_FOR_TABLE);
YYABORT;
}
}
| '*' '.' '*'
{
- Lex->db = NULL;
- if (Lex->grant == UINT_MAX)
- Lex->grant = GLOBAL_ACLS & ~GRANT_ACL;
- else if (Lex->columns.elements)
+ LEX *lex=Lex;
+ lex->select->db = NULL;
+ if (lex->grant == UINT_MAX)
+ lex->grant = GLOBAL_ACLS & ~GRANT_ACL;
+ else if (lex->columns.elements)
{
- net_printf(&current_thd->net,ER_ILLEGAL_GRANT_FOR_TABLE);
+ net_printf(&lex->thd->net,ER_ILLEGAL_GRANT_FOR_TABLE);
YYABORT;
}
}
| table_ident
{
+ LEX *lex=Lex;
if (!add_table_to_list($1,NULL,0))
YYABORT;
- if (Lex->grant == UINT_MAX)
- Lex->grant = TABLE_ACLS & ~GRANT_ACL;
+ if (lex->grant == UINT_MAX)
+ lex->grant = TABLE_ACLS & ~GRANT_ACL;
}
@@ -2967,7 +3331,11 @@ grant_user:
opt_column_list:
- /* empty */ { Lex->grant |= Lex->which_columns; }
+ /* empty */
+ {
+ LEX *lex=Lex;
+ lex->grant |= lex->which_columns;
+ }
| '(' column_list ')'
column_list:
@@ -2980,16 +3348,17 @@ column_list_id:
String *new_str = new String((const char*) $1.str,$1.length);
List_iterator <LEX_COLUMN> iter(Lex->columns);
class LEX_COLUMN *point;
+ LEX *lex=Lex;
while ((point=iter++))
{
if (!my_strcasecmp(point->column.ptr(),new_str->ptr()))
break;
}
- Lex->grant_tot_col|= Lex->which_columns;
+ lex->grant_tot_col|= lex->which_columns;
if (point)
- point->rights |= Lex->which_columns;
+ point->rights |= lex->which_columns;
else
- Lex->columns.push_back(new LEX_COLUMN (*new_str,Lex->which_columns));
+ lex->columns.push_back(new LEX_COLUMN (*new_str,lex->which_columns));
}
grant_option:
@@ -3008,3 +3377,23 @@ commit:
rollback:
ROLLBACK_SYM { Lex->sql_command = SQLCOM_ROLLBACK;}
+
+
+/*
+** UNIONS : glue selects together
+*/
+
+
+union:
+ /* empty */ {}
+ | union_list
+
+union_list:
+ UNION_SYM
+ {
+ LEX *lex=Lex;
+ if (lex->exchange) YYABORT; /* Only the last SELECT can have INTO...... */
+ lex->sql_command=SQLCOM_UNION_SELECT;
+ mysql_new_select(lex); lex->select->linkage=UNION_TYPE;
+ }
+ select
diff --git a/sql/structs.h b/sql/structs.h
index 36f503312c0..594432134b2 100644
--- a/sql/structs.h
+++ b/sql/structs.h
@@ -164,3 +164,4 @@ typedef struct st_lex_user {
#define STATUS_NOT_READ 8 /* Record isn't read */
#define STATUS_UPDATED 16 /* Record is updated by formula */
#define STATUS_NULL_ROW 32 /* table->null_row is set */
+#define STATUS_DELETED 64
diff --git a/sql/table.h b/sql/table.h
index b627a158556..b25e2d82132 100644
--- a/sql/table.h
+++ b/sql/table.h
@@ -89,7 +89,7 @@ struct st_table {
my_bool copy_blobs; /* copy_blobs when storing */
my_bool null_row; /* All columns are null */
my_bool maybe_null,outer_join; /* Used with OUTER JOIN */
- my_bool distinct,const_table;
+ my_bool distinct,const_table,no_rows;
my_bool key_read;
my_bool crypted;
my_bool db_low_byte_first; /* Portable row format */
@@ -146,3 +146,10 @@ typedef struct st_table_list {
bool straight; /* optimize with prev table */
bool updating; /* for replicate-do/ignore table */
} TABLE_LIST;
+
+typedef struct st_open_table_list
+{
+ struct st_open_table_list *next;
+ char *db,*table;
+ uint32 in_use,locked;
+} OPEN_TABLE_LIST;
diff --git a/sql/uniques.cc b/sql/uniques.cc
new file mode 100644
index 00000000000..cbb67ecbbe5
--- /dev/null
+++ b/sql/uniques.cc
@@ -0,0 +1,164 @@
+/* Copyright (C) 2001 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+/*
+ Function to handle quick removal of duplicates
+ This code is used when doing multi-table deletes to find the rows in
+ reference tables that needs to be deleted.
+
+ The basic idea is as follows:
+
+ Store first all strings in a binary tree, ignoring duplicates.
+ When the three uses more memory than 'max_heap_table_size',
+ write the tree (in sorted order) out to disk and start with a new tree.
+ When all data has been generated, merge the trees (removing any found
+ duplicates).
+
+ The unique entries will be returned in sort order, to ensure that we do the
+ deletes in disk order.
+*/
+
+#include "mysql_priv.h"
+#include "sql_sort.h"
+
+
+Unique::Unique(qsort_cmp2 comp_func, void * comp_func_fixed_arg,
+ uint size, ulong max_in_memory_size_arg)
+ :max_in_memory_size(max_in_memory_size_arg),elements(0)
+{
+ init_tree(&tree, max_in_memory_size / 16, size, comp_func, 0, 0);
+ tree.cmp_arg=comp_func_fixed_arg;
+ /* If the following fail's the next add will also fail */
+ init_dynamic_array(&file_ptrs, sizeof(BUFFPEK), 16, 16);
+ max_elements= max_in_memory_size / ALIGN_SIZE(sizeof(TREE_ELEMENT)+size);
+ open_cached_file(&file, mysql_tmpdir,TEMP_PREFIX, DISK_BUFFER_SIZE,
+ MYF(MY_WME));
+}
+
+
+Unique::~Unique()
+{
+ close_cached_file(&file);
+ delete_tree(&tree);
+ delete_dynamic(&file_ptrs);
+}
+
+
+ /* Write tree to disk; clear tree */
+bool Unique::flush()
+{
+ BUFFPEK file_ptr;
+ elements+= tree.elements_in_tree;
+ file_ptr.count=tree.elements_in_tree;
+ file_ptr.file_pos=my_b_tell(&file);
+ if (tree_walk(&tree, (tree_walk_action) unique_write_to_file,
+ (void*) this, left_root_right) ||
+ insert_dynamic(&file_ptrs, (gptr) &file_ptr))
+ return 1;
+ delete_tree(&tree);
+ return 0;
+}
+
+
+int unique_write_to_file(gptr key, element_count count, Unique *unique)
+{
+ return my_b_write(&unique->file, key, unique->tree.size_of_element) ? 1 : 0;
+}
+
+int unique_write_to_ptrs(gptr key, element_count count, Unique *unique)
+{
+ memcpy(unique->record_pointers, key, unique->tree.size_of_element);
+ unique->record_pointers+=unique->tree.size_of_element;
+ return 0;
+}
+
+
+/*
+ Modify the TABLE element so that when one calls init_records()
+ the rows will be read in priority order.
+*/
+
+bool Unique::get(TABLE *table)
+{
+ SORTPARAM sort_param;
+ table->found_records=elements+tree.elements_in_tree;
+
+ if (my_b_tell(&file) == 0)
+ {
+ /* Whole tree is in memory; Don't use disk if you don't need to */
+ if ((record_pointers=table->record_pointers= (byte*)
+ my_malloc(tree.size_of_element * tree.elements_in_tree, MYF(0))))
+ {
+ (void) tree_walk(&tree, (tree_walk_action) unique_write_to_ptrs,
+ this, left_root_right);
+ return 0;
+ }
+ }
+ /* Not enough memory; Save the result to file */
+ if (flush())
+ return 1;
+
+ IO_CACHE *outfile=table->io_cache;
+ BUFFPEK *file_ptr= (BUFFPEK*) file_ptrs.buffer;
+ uint maxbuffer= file_ptrs.elements - 1;
+ uchar *sort_buffer;
+ my_off_t save_pos;
+ bool error=1;
+
+ /* Open cached file if it isn't open */
+ outfile=table->io_cache=(IO_CACHE*) my_malloc(sizeof(IO_CACHE),
+ MYF(MY_ZEROFILL));
+
+ if (!outfile || ! my_b_inited(outfile) &&
+ open_cached_file(outfile,mysql_tmpdir,TEMP_PREFIX,READ_RECORD_BUFFER,
+ MYF(MY_WME)))
+ return 1;
+ reinit_io_cache(outfile,WRITE_CACHE,0L,0,0);
+
+ sort_param.max_rows= elements;
+ sort_param.sort_form=table;
+ sort_param.sort_length=sort_param.ref_length=tree.size_of_element;
+ sort_param.keys= max_in_memory_size / sort_param.sort_length;
+
+ if (!(sort_buffer=(uchar*) my_malloc((sort_param.keys+1) *
+ sort_param.sort_length,
+ MYF(0))))
+ return 1;
+ sort_param.unique_buff= sort_buffer+(sort_param.keys*
+ sort_param.sort_length);
+
+ /* Merge the buffers to one file, removing duplicates */
+ if (merge_many_buff(&sort_param,sort_buffer,file_ptr,&maxbuffer,&file))
+ goto err;
+ if (flush_io_cache(&file) ||
+ reinit_io_cache(&file,READ_CACHE,0L,0,0))
+ goto err;
+ if (merge_buffers(&sort_param, &file, outfile, sort_buffer, file_ptr,
+ file_ptr, file_ptr+maxbuffer,0))
+ goto err;
+ error=0;
+err:
+ x_free((gptr) sort_buffer);
+ if (flush_io_cache(outfile))
+ error=1;
+
+ /* Setup io_cache for reading */
+ save_pos=outfile->pos_in_file;
+ if (reinit_io_cache(outfile,READ_CACHE,0L,0,0))
+ error=1;
+ outfile->end_of_file=save_pos;
+ return error;
+}
diff --git a/sql/violite.c b/sql/violite.c
deleted file mode 100644
index 902110ff072..00000000000
--- a/sql/violite.c
+++ /dev/null
@@ -1,430 +0,0 @@
-/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB
-
- This library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Library General Public
- License as published by the Free Software Foundation; either
- version 2 of the License, or (at your option) any later version.
-
- This library is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Library General Public License for more details.
-
- You should have received a copy of the GNU Library General Public
- License along with this library; if not, write to the Free
- Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
- MA 02111-1307, USA */
-
-/*
- Note that we can't have assertion on file descriptors; The reason for
- this is that during mysql shutdown, another thread can close a file
- we are working on. In this case we should just return read errors from
- the file descriptior.
-*/
-
-#include <global.h>
-
-#ifndef HAVE_VIO /* is Vio suppored by the Vio lib ? */
-
-#include <errno.h>
-#include <assert.h>
-#include <violite.h>
-#include <my_sys.h>
-#include <my_net.h>
-#include <m_string.h>
-#ifdef HAVE_POLL
-#include <sys/poll.h>
-#endif
-#ifdef HAVE_SYS_IOCTL_H
-#include <sys/ioctl.h>
-#endif
-
-#if defined(__EMX__)
-#define ioctlsocket ioctl
-#endif /* defined(__EMX__) */
-
-#if defined(MSDOS) || defined(__WIN__)
-#ifdef __WIN__
-#undef errno
-#undef EINTR
-#undef EAGAIN
-#define errno WSAGetLastError()
-#define EINTR WSAEINTR
-#define EAGAIN WSAEINPROGRESS
-#endif /* __WIN__ */
-#define O_NONBLOCK 1 /* For emulation of fcntl() */
-#endif
-#ifndef EWOULDBLOCK
-#define EWOULDBLOCK EAGAIN
-#endif
-
-#ifndef __WIN__
-#define HANDLE void *
-#endif
-
-struct st_vio
-{
- my_socket sd; /* my_socket - real or imaginary */
- HANDLE hPipe;
- my_bool localhost; /* Are we from localhost? */
- int fcntl_mode; /* Buffered fcntl(sd,F_GETFL) */
- struct sockaddr_in local; /* Local internet address */
- struct sockaddr_in remote; /* Remote internet address */
- enum enum_vio_type type; /* Type of connection */
- char desc[30]; /* String description */
-};
-
-typedef void *vio_ptr;
-typedef char *vio_cstring;
-
-/*
- * Helper to fill most of the Vio* with defaults.
- */
-
-static void vio_reset(Vio* vio, enum enum_vio_type type,
- my_socket sd, HANDLE hPipe,
- my_bool localhost)
-{
- bzero((char*) vio, sizeof(*vio));
- vio->type = type;
- vio->sd = sd;
- vio->hPipe = hPipe;
- vio->localhost= localhost;
-}
-
-/* Open the socket or TCP/IP connection and read the fnctl() status */
-
-Vio *vio_new(my_socket sd, enum enum_vio_type type, my_bool localhost)
-{
- Vio *vio;
- DBUG_ENTER("vio_new");
- DBUG_PRINT("enter", ("sd=%d", sd));
- if ((vio = (Vio*) my_malloc(sizeof(*vio),MYF(MY_WME))))
- {
- vio_reset(vio, type, sd, 0, localhost);
- sprintf(vio->desc,
- (vio->type == VIO_TYPE_SOCKET ? "socket (%d)" : "TCP/IP (%d)"),
- vio->sd);
-#if !defined(___WIN__) && !defined(__EMX__)
-#if !defined(NO_FCNTL_NONBLOCK)
- vio->fcntl_mode = fcntl(sd, F_GETFL);
-#elif defined(HAVE_SYS_IOCTL_H) /* hpux */
- /* Non blocking sockets doesn't work good on HPUX 11.0 */
- (void) ioctl(sd,FIOSNBIO,0);
-#endif
-#else /* !defined(__WIN__) && !defined(__EMX__) */
- {
- /* set to blocking mode by default */
- ulong arg=0, r;
- r = ioctlsocket(sd,FIONBIO,(void*) &arg, sizeof(arg));
- }
-#endif
- }
- DBUG_RETURN(vio);
-}
-
-
-#ifdef __WIN__
-
-Vio *vio_new_win32pipe(HANDLE hPipe)
-{
- Vio *vio;
- DBUG_ENTER("vio_new_handle");
- if ((vio = (Vio*) my_malloc(sizeof(Vio),MYF(MY_WME))))
- {
- vio_reset(vio, VIO_TYPE_NAMEDPIPE, 0, hPipe, TRUE);
- strmov(vio->desc, "named pipe");
- }
- DBUG_RETURN(vio);
-}
-
-#endif
-
-void vio_delete(Vio * vio)
-{
- /* It must be safe to delete null pointers. */
- /* This matches the semantics of C++'s delete operator. */
- if (vio)
- {
- if (vio->type != VIO_CLOSED)
- vio_close(vio);
- my_free((gptr) vio,MYF(0));
- }
-}
-
-int vio_errno(Vio *vio __attribute__((unused)))
-{
- return errno; /* On Win32 this mapped to WSAGetLastError() */
-}
-
-
-int vio_read(Vio * vio, gptr buf, int size)
-{
- int r;
- DBUG_ENTER("vio_read");
- DBUG_PRINT("enter", ("sd=%d, buf=%p, size=%d", vio->sd, buf, size));
-#ifdef __WIN__
- if (vio->type == VIO_TYPE_NAMEDPIPE)
- {
- DWORD length;
- if (!ReadFile(vio->hPipe, buf, size, &length, NULL))
- DBUG_RETURN(-1);
- DBUG_RETURN(length);
- }
- r = recv(vio->sd, buf, size,0);
-#else
- errno=0; /* For linux */
- r = read(vio->sd, buf, size);
-#endif /* __WIN__ */
-#ifndef DBUG_OFF
- if (r < 0)
- {
- DBUG_PRINT("vio_error", ("Got error %d during read",errno));
- }
-#endif /* DBUG_OFF */
- DBUG_PRINT("exit", ("%d", r));
- DBUG_RETURN(r);
-}
-
-
-int vio_write(Vio * vio, const gptr buf, int size)
-{
- int r;
- DBUG_ENTER("vio_write");
- DBUG_PRINT("enter", ("sd=%d, buf=%p, size=%d", vio->sd, buf, size));
-#ifdef __WIN__
- if ( vio->type == VIO_TYPE_NAMEDPIPE)
- {
- DWORD length;
- if (!WriteFile(vio->hPipe, (char*) buf, size, &length, NULL))
- DBUG_RETURN(-1);
- DBUG_RETURN(length);
- }
- r = send(vio->sd, buf, size,0);
-#else
- r = write(vio->sd, buf, size);
-#endif /* __WIN__ */
-#ifndef DBUG_OFF
- if (r < 0)
- {
- DBUG_PRINT("vio_error", ("Got error on write: %d",errno));
- }
-#endif /* DBUG_OFF */
- DBUG_PRINT("exit", ("%d", r));
- DBUG_RETURN(r);
-}
-
-
-int vio_blocking(Vio * vio, my_bool set_blocking_mode)
-{
- int r=0;
- DBUG_ENTER("vio_blocking");
- DBUG_PRINT("enter", ("set_blocking_mode: %d", (int) set_blocking_mode));
-
-#if !defined(___WIN__) && !defined(__EMX__)
-#if !defined(NO_FCNTL_NONBLOCK)
-
- if (vio->sd >= 0)
- {
- int old_fcntl=vio->fcntl_mode;
- if (set_blocking_mode)
- vio->fcntl_mode &= ~O_NONBLOCK; /* clear bit */
- else
- vio->fcntl_mode |= O_NONBLOCK; /* set bit */
- if (old_fcntl != vio->fcntl_mode)
- r = fcntl(vio->sd, F_SETFL, vio->fcntl_mode);
- }
-#endif /* !defined(NO_FCNTL_NONBLOCK) */
-#else /* !defined(__WIN__) && !defined(__EMX__) */
-#ifndef __EMX__
- if (vio->type != VIO_TYPE_NAMEDPIPE)
-#endif
- {
- ulong arg;
- int old_fcntl=vio->fcntl_mode;
- if (set_blocking_mode)
- {
- arg = 0;
- vio->fcntl_mode &= ~O_NONBLOCK; /* clear bit */
- }
- else
- {
- arg = 1;
- vio->fcntl_mode |= O_NONBLOCK; /* set bit */
- }
- if (old_fcntl != vio->fcntl_mode)
- r = ioctlsocket(vio->sd,FIONBIO,(void*) &arg, sizeof(arg));
- }
-#endif /* !defined(__WIN__) && !defined(__EMX__) */
- DBUG_RETURN(r);
-}
-
-my_bool
-vio_is_blocking(Vio * vio)
-{
- my_bool r;
- DBUG_ENTER("vio_is_blocking");
- r = !(vio->fcntl_mode & O_NONBLOCK);
- DBUG_PRINT("exit", ("%d", (int) r));
- DBUG_RETURN(r);
-}
-
-
-int vio_fastsend(Vio * vio __attribute__((unused)))
-{
- int r=0;
- DBUG_ENTER("vio_fastsend");
-
-#ifdef IPTOS_THROUGHPUT
- {
-#ifndef __EMX__
- int tos = IPTOS_THROUGHPUT;
- if (!setsockopt(vio->sd, IPPROTO_IP, IP_TOS, (void *) &tos, sizeof(tos)))
-#endif /* !__EMX__ */
- {
- int nodelay = 1;
- if (setsockopt(vio->sd, IPPROTO_TCP, TCP_NODELAY, (void *) &nodelay,
- sizeof(nodelay))) {
- DBUG_PRINT("warning",
- ("Couldn't set socket option for fast send"));
- r= -1;
- }
- }
- }
-#endif /* IPTOS_THROUGHPUT */
- DBUG_PRINT("exit", ("%d", r));
- DBUG_RETURN(r);
-}
-
-int vio_keepalive(Vio* vio, my_bool set_keep_alive)
-{
- int r=0;
- uint opt = 0;
- DBUG_ENTER("vio_keepalive");
- DBUG_PRINT("enter", ("sd=%d, set_keep_alive=%d", vio->sd, (int)
- set_keep_alive));
- if (vio->type != VIO_TYPE_NAMEDPIPE)
- {
- if (set_keep_alive)
- opt = 1;
- r = setsockopt(vio->sd, SOL_SOCKET, SO_KEEPALIVE, (char *) &opt,
- sizeof(opt));
- }
- DBUG_RETURN(r);
-}
-
-
-my_bool
-vio_should_retry(Vio * vio __attribute__((unused)))
-{
- int en = errno;
- return en == EAGAIN || en == EINTR || en == EWOULDBLOCK;
-}
-
-
-int vio_close(Vio * vio)
-{
- int r;
- DBUG_ENTER("vio_close");
-#ifdef __WIN__
- if (vio->type == VIO_TYPE_NAMEDPIPE)
- {
-#if defined(__NT__) && defined(MYSQL_SERVER)
- CancelIo(vio->hPipe);
- DisconnectNamedPipe(vio->hPipe);
-#endif
- r=CloseHandle(vio->hPipe);
- }
- else if (vio->type != VIO_CLOSED)
-#endif /* __WIN__ */
- {
- r=0;
- if (shutdown(vio->sd,2))
- r= -1;
- if (closesocket(vio->sd))
- r= -1;
- }
- if (r)
- {
- DBUG_PRINT("vio_error", ("close() failed, error: %d",errno));
- /* FIXME: error handling (not critical for MySQL) */
- }
- vio->type= VIO_CLOSED;
- vio->sd= -1;
- DBUG_RETURN(r);
-}
-
-
-const char *vio_description(Vio * vio)
-{
- return vio->desc;
-}
-
-enum enum_vio_type vio_type(Vio* vio)
-{
- return vio->type;
-}
-
-my_socket vio_fd(Vio* vio)
-{
- return vio->sd;
-}
-
-
-my_bool vio_peer_addr(Vio * vio, char *buf)
-{
- DBUG_ENTER("vio_peer_addr");
- DBUG_PRINT("enter", ("sd=%d", vio->sd));
- if (vio->localhost)
- {
- strmov(buf,"127.0.0.1");
- }
- else
- {
- size_socket addrLen = sizeof(struct sockaddr);
- if (getpeername(vio->sd, (struct sockaddr *) (& (vio->remote)),
- &addrLen) != 0)
- {
- DBUG_PRINT("exit", ("getpeername, error: %d", errno));
- DBUG_RETURN(1);
- }
- my_inet_ntoa(vio->remote.sin_addr,buf);
- }
- DBUG_PRINT("exit", ("addr=%s", buf));
- DBUG_RETURN(0);
-}
-
-
-void vio_in_addr(Vio *vio, struct in_addr *in)
-{
- DBUG_ENTER("vio_in_addr");
- if (vio->localhost)
- bzero((char*) in, sizeof(*in)); /* This should never be executed */
- else
- *in=vio->remote.sin_addr;
- DBUG_VOID_RETURN;
-}
-
-
-/* Return 0 if there is data to be read */
-
-my_bool vio_poll_read(Vio *vio,uint timeout)
-{
-#ifndef HAVE_POLL
- return 0;
-#else
- struct pollfd fds;
- int res;
- DBUG_ENTER("vio_poll");
- fds.fd=vio->sd;
- fds.events=POLLIN;
- fds.revents=0;
- if ((res=poll(&fds,1,(int) timeout*1000)) <= 0)
- {
- DBUG_RETURN(res < 0 ? 0 : 1); /* Don't return 1 on errors */
- }
- DBUG_RETURN(fds.revents & POLLIN ? 0 : 1);
-#endif
-}
-
-#endif /* HAVE_VIO */