diff options
Diffstat (limited to 'sql')
163 files changed, 23977 insertions, 13646 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 47cee300b09..e1ed9ad8915 100644 --- a/sql/Makefile.am +++ b/sql/Makefile.am @@ -21,27 +21,29 @@ MYSQLDATAdir = $(localstatedir) MYSQLSHAREdir = $(pkgdatadir) MYSQLBASEdir= $(prefix) INCLUDES = @MT_INCLUDES@ \ - @bdb_includes@ @innodb_includes@ @gemini_includes@ \ + @bdb_includes@ @innodb_includes@ \ -I$(srcdir)/../include \ -I$(srcdir)/../regex \ - -I$(srcdir) -I../include -I.. -I. + -I$(srcdir) -I../include -I. $(openssl_includes) WRAPLIBS= @WRAPLIBS@ SUBDIRS = share libexec_PROGRAMS = mysqld noinst_PROGRAMS = gen_lex_hash -gen_lex_hash_LDFLAGS = @NOINST_LDFLAGS@ -LDADD = ../isam/libnisam.a \ - ../merge/libmerge.a \ +gen_lex_hash_LDFLAGS = @NOINST_LDFLAGS@ +LDADD = @isam_libs@ \ ../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@ \ + @innodb_system_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 \ @@ -49,37 +51,37 @@ noinst_HEADERS = item.h item_func.h item_sum.h item_cmpfunc.h \ sql_manager.h sql_map.h sql_string.h unireg.h \ field.h handler.h \ ha_isammrg.h ha_isam.h ha_myisammrg.h\ - ha_heap.h ha_myisam.h ha_berkeley.h ha_innobase.h \ - ha_gemini.h opt_range.h opt_ft.h \ + ha_heap.h ha_myisam.h ha_berkeley.h ha_innodb.h \ + opt_range.h opt_ft.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 \ + lex.h lex_symbol.h sql_acl.h sql_crypt.h \ log_event.h mini_client.h sql_repl.h slave.h \ - stacktrace.h -mysqld_SOURCES = sql_lex.cc \ + stacktrace.h sql_sort.h sql_cache.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_do.cc \ + sql_update.cc sql_delete.cc uniques.cc sql_do.cc \ procedure.cc item_uniq.cc sql_test.cc \ log.cc log_event.cc init.cc derror.cc sql_acl.cc \ - unireg.cc \ + unireg.cc des_key_file.cc \ time.cc opt_range.cc opt_sum.cc opt_ft.cc \ records.cc filesort.cc handler.cc \ ha_heap.cc ha_myisam.cc ha_myisammrg.cc \ - ha_berkeley.cc ha_innobase.cc ha_gemini.cc \ + ha_berkeley.cc ha_innodb.cc \ ha_isam.cc ha_isammrg.cc \ sql_db.cc sql_table.cc sql_rename.cc sql_crypt.cc \ sql_load.cc mf_iocache.cc field_conv.cc sql_show.cc \ sql_udf.cc sql_analyse.cc sql_analyse.h sql_cache.cc \ - slave.cc sql_repl.cc \ + slave.cc sql_repl.cc sql_union.cc \ mini_client.cc mini_client_errors.c \ - md5.c stacktrace.c + stacktrace.c repl_failsafe.h repl_failsafe.cc gen_lex_hash_SOURCES = gen_lex_hash.cc gen_lex_hash_LDADD = $(LDADD) $(CXXLDFLAGS) diff --git a/sql/cache_manager.cc b/sql/cache_manager.cc index 9ea25315f8c..307fe331e5c 100644 --- a/sql/cache_manager.cc +++ b/sql/cache_manager.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 */ @@ -18,7 +18,7 @@ #pragma implementation /* gcc: Class implementation */ #endif -#include <global.h> +#include <my_global.h> #include <my_sys.h> #include "cache_manager.h" @@ -117,7 +117,7 @@ void *cache_manager::alloc(uint size) { void *llist; void *abs_ptr; - + size=ALIGN_SIZE(size+HEADER_LENGTH+SUFFIX_LENGTH); if (!(llist = find_in_llist(size))) { @@ -127,7 +127,7 @@ void *cache_manager::alloc(uint size) } size_of_found_block=int4korr((char*) llist) & ALLOC_MASK; // if (size_of_found_block < SMALLEST_BLOCK) - + abs_ptr = link_into_abs(llist); return abs_ptr; } diff --git a/sql/cache_manager.h b/sql/cache_manager.h index fc3b8f7016a..d422a86ea8e 100644 --- a/sql/cache_manager.h +++ b/sql/cache_manager.h @@ -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 */ @@ -59,7 +59,3 @@ class cache_manager { bool *dealloc(void *); /* Deallocate blocks (with *ptr_arg) */ void clear(void); /* Clear the cache */ }; - - - - diff --git a/sql/convert.cc b/sql/convert.cc index 2c8b775dca2..75af13e0939 100644 --- a/sql/convert.cc +++ b/sql/convert.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 */ @@ -397,19 +397,19 @@ static unsigned char win1251ukr_koi8_ukr[256] = { ****************************************************************************/ -CONVERT conv_cp1251_koi8("cp1251_koi8", cp1251_koi8, koi8_cp1251); +CONVERT conv_cp1251_koi8("cp1251_koi8", cp1251_koi8, koi8_cp1251, 1); #ifdef DEFINE_ALL_CHARACTER_SETS -CONVERT conv_cp1250_latin2("cp1250_latin2", t1250_til2, til2_t1250); -CONVERT conv_kam_latin2("kam_latin2", tkam_til2, til2_tkam); -CONVERT conv_mac_latin2("mac_latin2", tmac_til2, til2_tmac); -CONVERT conv_macce_latin2("macce_latin2", tmacce_til2, til2_tmacce); -CONVERT conv_pc2_latin2("pc2_latin2", tpc2_til2, til2_tpc2); -CONVERT conv_vga_latin2("vga_latin2", tvga_til2, til2_tvga); -CONVERT conv_koi8_cp1251("koi8_cp1251", koi8_cp1251, cp1251_koi8); +CONVERT conv_cp1250_latin2("cp1250_latin2", t1250_til2, til2_t1250, 2); +CONVERT conv_kam_latin2("kam_latin2", tkam_til2, til2_tkam, 3); +CONVERT conv_mac_latin2("mac_latin2", tmac_til2, til2_tmac, 4); +CONVERT conv_macce_latin2("macce_latin2", tmacce_til2, til2_tmacce, 5); +CONVERT conv_pc2_latin2("pc2_latin2", tpc2_til2, til2_tpc2, 6); +CONVERT conv_vga_latin2("vga_latin2", tvga_til2, til2_tvga, 7); +CONVERT conv_koi8_cp1251("koi8_cp1251", koi8_cp1251, cp1251_koi8, 8); CONVERT conv_win1251ukr_koi8_ukr("win1251ukr_koi8_ukr", win1251ukr_koi8_ukr, - koi8_ukr_win1251ukr); + koi8_ukr_win1251ukr, 9); CONVERT conv_koi8_ukr_win1251ukr("koi8_ukr_win1251ukr", koi8_ukr_win1251ukr, - win1251ukr_koi8_ukr); + win1251ukr_koi8_ukr, 10); #endif /* DEFINE_ALL_CHARACTER_SETS */ CONVERT *convert_tables[]= { diff --git a/sql/custom_conf.h b/sql/custom_conf.h index af6012e28ec..19ced12bfbb 100644 --- a/sql/custom_conf.h +++ b/sql/custom_conf.h @@ -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 */ diff --git a/sql/derror.cc b/sql/derror.cc index 62971fde394..7ebe6e4b3c5 100644 --- a/sql/derror.cc +++ b/sql/derror.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 */ @@ -44,13 +44,14 @@ static void read_texts(const char *file_name,const char ***point, uint error_messages) { register uint i; - uint ant,funktpos,length,textant; + uint count,funktpos,length,textcount; File file; char name[FN_REFLEN]; const char *buff; uchar head[32],*pos; DBUG_ENTER("read_texts"); + *point=0; // If something goes wrong LINT_INIT(buff); funktpos=0; if ((file=my_open(fn_format(name,file_name,language,"",4), @@ -63,37 +64,38 @@ static void read_texts(const char *file_name,const char ***point, if (head[0] != (uchar) 254 || head[1] != (uchar) 254 || head[2] != 2 || head[3] != 1) goto err; /* purecov: inspected */ - textant=head[4]; - length=uint2korr(head+6); ant=uint2korr(head+8); + textcount=head[4]; + length=uint2korr(head+6); count=uint2korr(head+8); - if (ant < error_messages) + if (count < error_messages) { - fprintf(stderr,"\n%s: Fatal error: Error message file '%s' had only %d error messages, but it should have at least %d error messages.\n\ -Check that the above file is the right version for this program!\n\n", - my_progname,name,ant,error_messages); + sql_print_error("\ +Error message file '%s' had only %d error messages,\n\ +but it should contain at least %d error messages.\n\ +Check that the above file is the right version for this program!", + name,count,error_messages); VOID(my_close(file,MYF(MY_WME))); - clean_up(0); /* Clean_up frees everything */ - exit(1); /* We can't continue */ + unireg_abort(1); } x_free((gptr) *point); /* Free old language */ if (!(*point= (const char**) - my_malloc((uint) (length+ant*sizeof(char*)),MYF(0)))) + my_malloc((uint) (length+count*sizeof(char*)),MYF(0)))) { funktpos=2; /* purecov: inspected */ goto err; /* purecov: inspected */ } - buff= (char*) (*point + ant); + buff= (char*) (*point + count); - if (my_read(file,(byte*) buff,(uint) ant*2,MYF(MY_NABP))) goto err; - for (i=0, pos= (uchar*) buff ; i< ant ; i++) + if (my_read(file,(byte*) buff,(uint) count*2,MYF(MY_NABP))) goto err; + for (i=0, pos= (uchar*) buff ; i< count ; i++) { (*point)[i]=buff+uint2korr(pos); pos+=2; } if (my_read(file,(byte*) buff,(uint) length,MYF(MY_NABP))) goto err; - for (i=1 ; i < textant ; i++) + for (i=1 ; i < textcount ; i++) { point[i]= *point +uint2korr(head+10+i+i); } @@ -103,20 +105,19 @@ Check that the above file is the right version for this program!\n\n", err: switch (funktpos) { case 2: - buff="\n%s: Fatal error: Not enough memory for messagefile '%s'\n\n"; + buff="Not enough memory for messagefile '%s'"; break; case 1: - buff="\n%s: Fatal error: Can't read from messagefile '%s'\n\n"; + buff="Can't read from messagefile '%s'"; break; default: - buff="\n%s: Fatal error: Can't find messagefile '%s'\n\n"; + buff="Can't find messagefile '%s'"; break; } if (file != FERR) VOID(my_close(file,MYF(MY_WME))); - fprintf(stderr,buff,my_progname,name); - clean_up(0); /* Clean_up frees everything */ - exit(1); /* We can't continue */ + sql_print_error(buff,name); + unireg_abort(1); } /* read_texts */ diff --git a/sql/des_key_file.cc b/sql/des_key_file.cc new file mode 100644 index 00000000000..d9c924b5a3c --- /dev/null +++ b/sql/des_key_file.cc @@ -0,0 +1,107 @@ +/* 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 */ + +#include <mysql_priv.h> +#include <m_ctype.h> + +#ifdef HAVE_OPENSSL + +struct st_des_keyschedule des_keyschedule[10]; +uint des_default_key; +pthread_mutex_t LOCK_des_key_file; +static int initialized; + +/* + Function which loads DES keys from plaintext file into memory on MySQL + server startup and on command FLUSH DES_KEY_FILE. + Blame tonu@spam.ee on bugs ;) + + RETURN + 0 ok + 1 Error +*/ + +bool +load_des_key_file(const char *file_name) +{ + bool result=1; + File file; + IO_CACHE io; + DBUG_ENTER("load_des_key_file"); + DBUG_PRINT("enter",("name: %s",file_name)); + + if (!initialized) + { + initialized=1; + pthread_mutex_init(&LOCK_des_key_file,MY_MUTEX_INIT_FAST); + } + + VOID(pthread_mutex_lock(&LOCK_des_key_file)); + if ((file=my_open(file_name,O_RDONLY | O_BINARY ,MYF(MY_WME))) < 0 || + init_io_cache(&io, file, IO_SIZE*2, READ_CACHE, 0, 0, MYF(MY_WME))) + goto error; + + bzero((char*) des_keyschedule,sizeof(struct st_des_keyschedule) * 10); + des_default_key=15; // Impossible key + for (;;) + { + char *start, *end; + char buf[1024], offset; + st_des_keyblock keyblock; + uint length; + + if (!(length=my_b_gets(&io,buf,sizeof(buf)-1))) + break; // End of file + offset=buf[0]; + if (offset >= '0' && offset <= '9') // If ok key + { + offset=(char) (offset - '0'); + // Remove newline and possible other control characters + for (start=buf+1 ; isspace(*start) ; start++) ; + end=buf+length; + for (end=strend(buf) ; end > start && !isgraph(end[-1]) ; end--) ; + + if (start != end) + { + des_cblock ivec; + bzero((char*) &ivec,sizeof(ivec)); + // We make good 24-byte (168 bit) key from given plaintext key with MD5 + EVP_BytesToKey(EVP_des_ede3_cbc(),EVP_md5(),NULL, + (uchar *) start, (int) (end-start),1, + (uchar *) &keyblock, + ivec); + des_set_key_unchecked(&keyblock.key1,des_keyschedule[(int)offset].ks1); + des_set_key_unchecked(&keyblock.key2,des_keyschedule[(int)offset].ks2); + des_set_key_unchecked(&keyblock.key3,des_keyschedule[(int)offset].ks3); + if (des_default_key == 15) + des_default_key= (uint) offset; // use first as def. + } + } + else if (offset != '#') + sql_print_error("load_des_file: Found wrong key_number: %c",offset); + } + result=0; + +error: + if (file >= 0) + { + my_close(file,MYF(0)); + end_io_cache(&io); + } + VOID(pthread_mutex_unlock(&LOCK_des_key_file)); + DBUG_RETURN(result); +} +#endif /* HAVE_OPENSSL */ diff --git a/sql/field.cc b/sql/field.cc index 246427cc2ac..c6880a29cee 100644 --- a/sql/field.cc +++ b/sql/field.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 */ @@ -63,7 +63,7 @@ const char field_separator=','; *****************************************************************************/ /* - Calculate length of number and it's parts + Calculate length of number and its parts Increment cuted_fields if wrong number */ @@ -215,18 +215,19 @@ static bool test_if_real(const char *str,int length) /**************************************************************************** ** Functions for the base classes -** This is a unpacked number. +** This is an unpacked number. ****************************************************************************/ Field::Field(char *ptr_arg,uint32 length_arg,uchar *null_ptr_arg, - uint null_bit_arg, + uchar null_bit_arg, utype unireg_check_arg, const char *field_name_arg, struct st_table *table_arg) - :ptr(ptr_arg),null_ptr(null_ptr_arg),null_bit(null_bit_arg), - table(table_arg),query_id(0),key_start(0),part_of_key(0),part_of_sortkey(0), - table_name(table_arg ? table_arg->table_name : 0), - field_name(field_name_arg), unireg_check(unireg_check_arg), - field_length(length_arg) + :ptr(ptr_arg),null_ptr(null_ptr_arg), + table(table_arg),table_name(table_arg ? table_arg->table_name : 0), + field_name(field_name_arg), + query_id(0),key_start(0),part_of_key(0),part_of_sortkey(0), + unireg_check(unireg_check_arg), + field_length(length_arg),null_bit(null_bit_arg) { flags=null_ptr ? 0: NOT_NULL_FLAG; } @@ -242,13 +243,13 @@ void Field::copy_from_tmp(int row_offset) memcpy(ptr,ptr+row_offset,pack_length()); if (null_ptr) { - *null_ptr= ((null_ptr[0] & (uchar) ~(uint) null_bit) | - null_ptr[row_offset] & (uchar) null_bit); + *null_ptr= (uchar) ((null_ptr[0] & (uchar) ~(uint) null_bit) | + null_ptr[row_offset] & (uchar) null_bit); } } -bool Field::send(String *packet) +bool Field::send(THD *thd, String *packet) { if (is_null()) return net_store_null(packet); @@ -256,7 +257,7 @@ bool Field::send(String *packet) String tmp(buff,sizeof(buff)); val_str(&tmp,&tmp); CONVERT *convert; - if ((convert=current_thd->convert_set)) + if ((convert=thd->convert_set)) return convert->store(packet,tmp.ptr(),tmp.length()); return net_store_data(packet,tmp.ptr(),tmp.length()); } @@ -361,14 +362,14 @@ void Field::store_time(TIME *ltime,timestamp_type type) } -bool Field::optimize_range() +bool Field::optimize_range(uint idx) { - return test(table->file->option_flag() & HA_READ_NEXT); + return test(table->file->index_flags(idx) & HA_READ_NEXT); } /**************************************************************************** ** Functions for the Field_decimal class -** This is a unpacked number. +** This is an unpacked number. ****************************************************************************/ void @@ -431,7 +432,7 @@ void Field_decimal::store(const char *from,uint len) { fyllchar = '0'; if (from != end) - while (*from == '0' && from != end-1) // Skipp prezero + while (*from == '0' && from != end-1) // Skip prezero from++; } else @@ -490,7 +491,7 @@ void Field_decimal::store(const char *from,uint len) if (tmp_dec--) { *to++ ='.'; - if (decstr.nr_dec) from++; // Skipp '.' + if (decstr.nr_dec) from++; // Skip '.' for (i=(int) min(decstr.nr_dec,tmp_dec) ; i-- > 0 ; ) *to++ = *from++; for (i=(int) (tmp_dec-min(decstr.nr_dec,tmp_dec)) ; i-- > 0 ; ) *to++ = '0'; } @@ -1075,7 +1076,7 @@ void Field_short::sort_string(char *to,uint length __attribute__((unused))) if (unsigned_flag) to[0] = ptr[0]; else - to[0] = ptr[0] ^ 128; /* Revers signbit */ + to[0] = (char) (ptr[0] ^ 128); /* Revers signbit */ to[1] = ptr[1]; } else @@ -1084,7 +1085,7 @@ void Field_short::sort_string(char *to,uint length __attribute__((unused))) if (unsigned_flag) to[0] = ptr[1]; else - to[0] = ptr[1] ^ 128; /* Revers signbit */ + to[0] = (char) (ptr[1] ^ 128); /* Revers signbit */ to[1] = ptr[0]; } } @@ -1155,12 +1156,12 @@ void Field_medium::store(double nr) } else if (nr >= (double) (long) (1L << 24)) { - ulong tmp=(ulong) (1L << 24)-1L; + uint32 tmp=(uint32) (1L << 24)-1L; int3store(ptr,tmp); current_thd->cuted_fields++; } else - int3store(ptr,(ulong) nr); + int3store(ptr,(uint32) nr); } else { @@ -1197,7 +1198,7 @@ void Field_medium::store(longlong nr) current_thd->cuted_fields++; } else - int3store(ptr,(ulong) nr); + int3store(ptr,(uint32) nr); } else { @@ -1475,7 +1476,7 @@ int Field_long::cmp(const char *a_ptr, const char *b_ptr) longget(b,b_ptr); } if (unsigned_flag) - return ((ulong) a < (ulong) b) ? -1 : ((ulong) a > (ulong) b) ? 1 : 0; + return ((uint32) a < (uint32) b) ? -1 : ((uint32) a > (uint32) b) ? 1 : 0; return (a < b) ? -1 : (a > b) ? 1 : 0; } @@ -1487,7 +1488,7 @@ void Field_long::sort_string(char *to,uint length __attribute__((unused))) if (unsigned_flag) to[0] = ptr[0]; else - to[0] = ptr[0] ^ 128; /* Revers signbit */ + to[0] = (char) (ptr[0] ^ 128); /* Revers signbit */ to[1] = ptr[1]; to[2] = ptr[2]; to[3] = ptr[3]; @@ -1498,7 +1499,7 @@ void Field_long::sort_string(char *to,uint length __attribute__((unused))) if (unsigned_flag) to[0] = ptr[3]; else - to[0] = ptr[3] ^ 128; /* Revers signbit */ + to[0] = (char) (ptr[3] ^ 128); /* Revers signbit */ to[1] = ptr[2]; to[2] = ptr[1]; to[3] = ptr[0]; @@ -1686,7 +1687,7 @@ void Field_longlong::sort_string(char *to,uint length __attribute__((unused))) if (unsigned_flag) to[0] = ptr[0]; else - to[0] = ptr[0] ^ 128; /* Revers signbit */ + to[0] = (char) (ptr[0] ^ 128); /* Revers signbit */ to[1] = ptr[1]; to[2] = ptr[2]; to[3] = ptr[3]; @@ -1701,7 +1702,7 @@ void Field_longlong::sort_string(char *to,uint length __attribute__((unused))) if (unsigned_flag) to[0] = ptr[7]; else - to[0] = ptr[7] ^ 128; /* Revers signbit */ + to[0] = (char) (ptr[7] ^ 128); /* Revers signbit */ to[1] = ptr[6]; to[2] = ptr[5]; to[3] = ptr[4]; @@ -1738,6 +1739,11 @@ void Field_float::store(double nr) float j; if (dec < NOT_FIXED_DEC) nr=floor(nr*log_10[dec]+0.5)/log_10[dec]; // To fixed point + if (unsigned_flag && nr < 0) + { + current_thd->cuted_fields++; + nr=0; + } if (nr < -FLT_MAX) { j= -FLT_MAX; @@ -1764,6 +1770,11 @@ void Field_float::store(double nr) void Field_float::store(longlong nr) { float j= (float) nr; + if (unsigned_flag && j < 0) + { + current_thd->cuted_fields++; + j=0; + } #ifdef WORDS_BIGENDIAN if (table->db_low_byte_first) { @@ -1936,7 +1947,7 @@ void Field_float::sort_string(char *to,uint length __attribute__((unused))) { /* make complement */ uint i; for (i=0 ; i < sizeof(nr); i++) - tmp[i]=tmp[i] ^ (uchar) 255; + tmp[i]= (uchar) (tmp[i] ^ (uchar) 255); } else { @@ -1970,6 +1981,11 @@ void Field_double::store(const char *from,uint len) double j= atof(tmp_str.c_ptr()); if (errno || current_thd->count_cuted_fields && !test_if_real(from,len)) current_thd->cuted_fields++; + if (unsigned_flag && j < 0) + { + current_thd->cuted_fields++; + j=0; + } #ifdef WORDS_BIGENDIAN if (table->db_low_byte_first) { @@ -1985,6 +2001,11 @@ void Field_double::store(double nr) { if (dec < NOT_FIXED_DEC) nr=floor(nr*log_10[dec]+0.5)/log_10[dec]; // To fixed point + if (unsigned_flag && nr < 0) + { + current_thd->cuted_fields++; + nr=0; + } #ifdef WORDS_BIGENDIAN if (table->db_low_byte_first) { @@ -1999,6 +2020,11 @@ void Field_double::store(double nr) void Field_double::store(longlong nr) { double j= (double) nr; + if (unsigned_flag && j < 0) + { + current_thd->cuted_fields++; + j=0; + } #ifdef WORDS_BIGENDIAN if (table->db_low_byte_first) { @@ -2304,10 +2330,10 @@ void Field_timestamp::store(longlong nr) { part1=(long) (nr/LL(1000000)); part2=(long) (nr - (longlong) part1*LL(1000000)); - l_time.year= part1/10000L; part1%=10000L; + l_time.year= (int) (part1/10000L); part1%=10000L; l_time.month= (int) part1 / 100; - l_time.day= (int) part1 % 100; - l_time.hour= part2/10000L; part2%=10000L; + l_time.day= (int) part1 % 100; + l_time.hour= (int) (part2/10000L); part2%=10000L; l_time.minute=(int) part2 / 100; l_time.second=(int) part2 % 100; timestamp=my_gmt_sec(&l_time); @@ -2321,7 +2347,7 @@ void Field_timestamp::store(longlong nr) } else #endif - longstore(ptr,(ulong)timestamp); + longstore(ptr,(uint32) timestamp); } @@ -2622,7 +2648,7 @@ void Field_time::store(longlong nr) double Field_time::val_real(void) { - ulong j= (ulong) uint3korr(ptr); + uint32 j= (uint32) uint3korr(ptr); return (double) j; } @@ -2658,19 +2684,19 @@ bool Field_time::get_time(TIME *ltime) ltime->neg= 1; tmp=-tmp; } - ltime->hour=tmp/10000; + ltime->hour= (int) (tmp/10000); tmp-=ltime->hour*10000; - ltime->minute= tmp/100; - ltime->second= tmp % 100; + ltime->minute= (int) tmp/100; + ltime->second= (int) tmp % 100; ltime->second_part=0; return 0; } int Field_time::cmp(const char *a_ptr, const char *b_ptr) { - long a,b; - a=(long) sint3korr(a_ptr); - b=(long) sint3korr(b_ptr); + int32 a,b; + a=(int32) sint3korr(a_ptr); + b=(int32) sint3korr(b_ptr); return (a < b) ? -1 : (a > b) ? 1 : 0; } @@ -2781,14 +2807,14 @@ void Field_year::sql_type(String &res) const ** Stored as a 4 byte unsigned int ****************************************************************************/ -void Field_date::store(const char *from,uint len) +void Field_date::store(const char *from, uint len) { TIME l_time; - ulong tmp; + uint32 tmp; if (str_to_TIME(from,len,&l_time,1) == TIMESTAMP_NONE) tmp=0; else - tmp=(ulong) l_time.year*10000L + (ulong) (l_time.month*100+l_time.day); + tmp=(uint32) l_time.year*10000L + (uint32) (l_time.month*100+l_time.day); #ifdef WORDS_BIGENDIAN if (table->db_low_byte_first) { @@ -2960,7 +2986,7 @@ void Field_newdate::store(double nr) void Field_newdate::store(longlong nr) { - long tmp; + int32 tmp; if (nr >= LL(100000000) && nr <= LL(99991231235959)) nr=nr/LL(1000000); // Timestamp to date if (nr < 0L || nr > 99991231L) @@ -2970,16 +2996,16 @@ void Field_newdate::store(longlong nr) } else { - tmp=(long) nr; + tmp=(int32) nr; if (tmp) { if (tmp < YY_PART_YEAR*10000L) // Fix short dates - tmp+=20000000L; + tmp+= (uint32) 20000000L; else if (tmp < 999999L) - tmp+=19000000L; + tmp+= (uint32) 19000000L; } - uint month=((tmp/100) % 100); - uint day= tmp%100; + uint month= (uint) ((tmp/100) % 100); + uint day= (uint) (tmp%100); if (month > 12 || day > 31) { tmp=0L; // Don't allow date to change @@ -2988,7 +3014,7 @@ void Field_newdate::store(longlong nr) else tmp= day + month*32 + (tmp/10000)*16*32; } - int3store(ptr,tmp); + int3store(ptr,(int32) tmp); } void Field_newdate::store_time(TIME *ltime,timestamp_type type) @@ -3013,7 +3039,7 @@ double Field_newdate::val_real(void) longlong Field_newdate::val_int(void) { - ulong j=uint3korr(ptr); + ulong j= uint3korr(ptr); j= (j % 32L)+(j / 32L % 16L)*100L + (j/(16L*32L))*10000L; return (longlong) j; } @@ -3023,25 +3049,25 @@ String *Field_newdate::val_str(String *val_buffer, { val_buffer->alloc(field_length); val_buffer->length(field_length); - ulong tmp=(ulong) uint3korr(ptr); + uint32 tmp=(uint32) uint3korr(ptr); int part; char *pos=(char*) val_buffer->ptr()+10; /* Open coded to get more speed */ - *pos--=0; + *pos--=0; // End NULL part=(int) (tmp & 31); - *pos--='0'+part%10; - *pos--='0'+part/10; - *pos--='-'; + *pos--= (char) ('0'+part%10); + *pos--= (char) ('0'+part/10); + *pos--= '-'; part=(int) (tmp >> 5 & 15); - *pos--='0'+part%10; - *pos--='0'+part/10; - *pos--='-'; + *pos--= (char) ('0'+part%10); + *pos--= (char) ('0'+part/10); + *pos--= '-'; part=(int) (tmp >> 9); - *pos--='0'+part%10; part/=10; - *pos--='0'+part%10; part/=10; - *pos--='0'+part%10; part/=10; - *pos='0'+part; + *pos--= (char) ('0'+part%10); part/=10; + *pos--= (char) ('0'+part%10); part/=10; + *pos--= (char) ('0'+part%10); part/=10; + *pos= (char) ('0'+part); return val_buffer; } @@ -3049,7 +3075,7 @@ bool Field_newdate::get_date(TIME *ltime,bool fuzzydate) { if (is_null()) return 1; - ulong tmp=(ulong) uint3korr(ptr); + uint32 tmp=(uint32) uint3korr(ptr); bzero((char*) ltime,sizeof(*ltime)); ltime->day= tmp & 31; ltime->month= (tmp >> 5) & 15; @@ -3065,9 +3091,9 @@ bool Field_newdate::get_time(TIME *ltime) int Field_newdate::cmp(const char *a_ptr, const char *b_ptr) { - ulong a,b; - a=(ulong) uint3korr(a_ptr); - b=(ulong) uint3korr(b_ptr); + uint32 a,b; + a=(uint32) uint3korr(a_ptr); + b=(uint32) uint3korr(b_ptr); return (a < b) ? -1 : (a > b) ? 1 : 0; } @@ -3201,44 +3227,44 @@ String *Field_datetime::val_str(String *val_buffer, pos=(char*) val_buffer->ptr()+19; *pos--=0; - *pos--='0'+(char) (part2%10); part2/=10; - *pos--='0'+(char) (part2%10); part3= (int) (part2 / 10); - *pos--=':'; - *pos--='0'+(char) (part3%10); part3/=10; - *pos--='0'+(char) (part3%10); part3/=10; - *pos--=':'; - *pos--='0'+(char) (part3%10); part3/=10; - *pos--='0'+(char) part3; - *pos--=' '; - *pos--='0'+(char) (part1%10); part1/=10; - *pos--='0'+(char) (part1%10); part1/=10; - *pos--='-'; - *pos--='0'+(char) (part1%10); part1/=10; - *pos--='0'+(char) (part1%10); part3= (int) (part1/10); - *pos--='-'; - *pos--='0'+(char) (part3%10); part3/=10; - *pos--='0'+(char) (part3%10); part3/=10; - *pos--='0'+(char) (part3%10); part3/=10; - *pos='0'+(char) part3; + *pos--= (char) ('0'+(char) (part2%10)); part2/=10; + *pos--= (char) ('0'+(char) (part2%10)); part3= (int) (part2 / 10); + *pos--= ':'; + *pos--= (char) ('0'+(char) (part3%10)); part3/=10; + *pos--= (char) ('0'+(char) (part3%10)); part3/=10; + *pos--= ':'; + *pos--= (char) ('0'+(char) (part3%10)); part3/=10; + *pos--= (char) ('0'+(char) part3); + *pos--= ' '; + *pos--= (char) ('0'+(char) (part1%10)); part1/=10; + *pos--= (char) ('0'+(char) (part1%10)); part1/=10; + *pos--= '-'; + *pos--= (char) ('0'+(char) (part1%10)); part1/=10; + *pos--= (char) ('0'+(char) (part1%10)); part3= (int) (part1/10); + *pos--= '-'; + *pos--= (char) ('0'+(char) (part3%10)); part3/=10; + *pos--= (char) ('0'+(char) (part3%10)); part3/=10; + *pos--= (char) ('0'+(char) (part3%10)); part3/=10; + *pos=(char) ('0'+(char) part3); return val_buffer; } bool Field_datetime::get_date(TIME *ltime,bool fuzzydate) { longlong tmp=Field_datetime::val_int(); - long part1,part2; - part1=(long) (tmp/LL(1000000)); - part2=(long) (tmp - (ulonglong) part1*LL(1000000)); + uint32 part1,part2; + part1=(uint32) (tmp/LL(1000000)); + part2=(uint32) (tmp - (ulonglong) part1*LL(1000000)); ltime->time_type= TIMESTAMP_FULL; - ltime->neg=0; - ltime->second_part=0; - ltime->second= part2%100; - ltime->minute= part2/100%100; - ltime->hour= part2/10000; - ltime->day= part1%100; - ltime->month= part1/100%100; - ltime->year= part1/10000; + ltime->neg= 0; + ltime->second_part= 0; + ltime->second= (int) (part2%100); + ltime->minute= (int) (part2/100%100); + ltime->hour= (int) (part2/10000); + ltime->day= (int) (part1%100); + ltime->month= (int) (part1/100%100); + ltime->year= (int) (part1/10000); return (!fuzzydate && (!ltime->month || !ltime->day)) ? 1 : 0; } @@ -3357,7 +3383,7 @@ void Field_string::store(longlong nr) { char buff[22]; char *end=longlong10_to_str(nr,buff,-10); - Field_string::store(buff,end-buff); + Field_string::store(buff,(uint) (end-buff)); } @@ -3548,7 +3574,7 @@ void Field_varstring::store(longlong nr) { char buff[22]; char *end=longlong10_to_str(nr,buff,-10); - Field_varstring::store(buff,end-buff); + Field_varstring::store(buff,(uint) (end-buff)); } @@ -3639,9 +3665,9 @@ char *Field_varstring::pack(char *to, const char *from, uint max_length) uint length=uint2korr(from); if (length > max_length) length=max_length; - *to++= (length & 255); + *to++= (char) (length & 255); if (max_length > 255) - *to++= (uchar) (length >> 8); + *to++= (char) (length >> 8); if (length) memcpy(to, from+2, length); return to+length; @@ -3730,7 +3756,7 @@ uint Field_varstring::max_packed_col_length(uint max_length) ** packlength slot and may be from 1-4. ****************************************************************************/ -Field_blob::Field_blob(char *ptr_arg, uchar *null_ptr_arg, uint null_bit_arg, +Field_blob::Field_blob(char *ptr_arg, uchar *null_ptr_arg, uchar null_bit_arg, enum utype unireg_check_arg, const char *field_name_arg, struct st_table *table_arg,uint blob_pack_length, bool binary_arg) @@ -3747,7 +3773,7 @@ Field_blob::Field_blob(char *ptr_arg, uchar *null_ptr_arg, uint null_bit_arg, } -void Field_blob::store_length(ulong number) +void Field_blob::store_length(uint32 number) { switch (packlength) { case 1: @@ -3774,9 +3800,9 @@ void Field_blob::store_length(ulong number) shortstore(ptr,(unsigned short) number); break; case 3: - if (number > (ulong) (1L << 24)) + if (number > (uint32) (1L << 24)) { - number= (ulong) (1L << 24)-1L; + number= (uint32) (1L << 24)-1L; current_thd->cuted_fields++; } int3store(ptr,number); @@ -3794,11 +3820,11 @@ void Field_blob::store_length(ulong number) } -ulong Field_blob::get_length(const char *pos) +uint32 Field_blob::get_length(const char *pos) { switch (packlength) { case 1: - return (ulong) (uchar) pos[0]; + return (uint32) (uchar) pos[0]; case 2: { uint16 tmp; @@ -3808,10 +3834,10 @@ ulong Field_blob::get_length(const char *pos) else #endif shortget(tmp,pos); - return (ulong) tmp; + return (uint32) tmp; } case 3: - return (ulong) uint3korr(pos); + return (uint32) uint3korr(pos); case 4: { uint32 tmp; @@ -3821,7 +3847,7 @@ ulong Field_blob::get_length(const char *pos) else #endif longget(tmp,pos); - return (ulong) tmp; + return (uint32) tmp; } } return 0; // Impossible @@ -3867,14 +3893,14 @@ void Field_blob::store(const char *from,uint len) void Field_blob::store(double nr) { value.set(nr); - Field_blob::store(value.ptr(),value.length()); + Field_blob::store(value.ptr(),(uint) value.length()); } void Field_blob::store(longlong nr) { value.set(nr); - Field_blob::store(value.ptr(),value.length()); + Field_blob::store(value.ptr(), (uint) value.length()); } @@ -3885,7 +3911,7 @@ double Field_blob::val_real(void) memcpy_fixed(&blob,ptr+packlength,sizeof(char*)); if (!blob) return 0.0; - ulong length=get_length(ptr); + uint32 length=get_length(ptr); char save=blob[length]; // Ok to patch blob in NISAM blob[length]=0; @@ -3901,7 +3927,7 @@ longlong Field_blob::val_int(void) memcpy_fixed(&blob,ptr+packlength,sizeof(char*)); if (!blob) return 0; - ulong length=get_length(ptr); + uint32 length=get_length(ptr); char save=blob[length]; // Ok to patch blob in NISAM blob[length]=0; @@ -3924,8 +3950,8 @@ String *Field_blob::val_str(String *val_buffer __attribute__((unused)), } -int Field_blob::cmp(const char *a,ulong a_length, const char *b, - ulong b_length) +int Field_blob::cmp(const char *a,uint32 a_length, const char *b, + uint32 b_length) { int diff; if (binary_flag) @@ -3959,11 +3985,11 @@ int Field_blob::cmp_binary_offset(uint row_offset) int Field_blob::cmp_binary(const char *a_ptr, const char *b_ptr, - ulong max_length) + uint32 max_length) { char *a,*b; uint diff; - ulong a_length,b_length; + uint32 a_length,b_length; memcpy_fixed(&a,a_ptr+packlength,sizeof(char*)); memcpy_fixed(&b,b_ptr+packlength,sizeof(char*)); a_length=get_length(a_ptr); @@ -3982,9 +4008,9 @@ int Field_blob::cmp_binary(const char *a_ptr, const char *b_ptr, void Field_blob::get_key_image(char *buff,uint length) { length-=HA_KEY_BLOB_LENGTH; - ulong blob_length=get_length(ptr); + uint32 blob_length=get_length(ptr); char *blob; - if ((ulong) length > blob_length) + if ((uint32) length > blob_length) { #ifdef HAVE_purify bzero(buff+2+blob_length, (length-blob_length)); @@ -4078,7 +4104,7 @@ char *Field_blob::pack(char *to, const char *from, uint max_length) { char *save=ptr; ptr=(char*) from; - ulong length=get_length(); // Length of from string + uint32 length=get_length(); // Length of from string if (length > max_length) { ptr=to; @@ -4101,7 +4127,7 @@ char *Field_blob::pack(char *to, const char *from, uint max_length) const char *Field_blob::unpack(char *to, const char *from) { memcpy(to,from,packlength); - ulong length=get_length(from); + uint32 length=get_length(from); from+=packlength; if (length) memcpy_fixed(to+packlength, &from, sizeof(from)); @@ -4110,60 +4136,6 @@ const char *Field_blob::unpack(char *to, const char *from) return from+length; } - -#ifdef HAVE_GEMINI_DB -/* Blobs in Gemini tables are stored separately from the rows which contain -** them (except for tiny blobs, which are stored in the row). For all other -** blob types (blob, mediumblob, longblob), the row contains the length of -** the blob data and a blob id. These methods (pack_id, get_id, and -** unpack_id) handle packing and unpacking blob fields in Gemini rows. -*/ -char *Field_blob::pack_id(char *to, const char *from, ulonglong id, uint max_length) -{ - char *save=ptr; - ptr=(char*) from; - ulong length=get_length(); // Length of from string - if (length > max_length) - { - ptr=to; - length=max_length; - store_length(length); // Store max length - ptr=(char*) from; - } - else - memcpy(to,from,packlength); // Copy length - if (length) - { - int8store(to+packlength, id); - } - ptr=save; // Restore org row pointer - return to+packlength+sizeof(id); -} - - -ulonglong Field_blob::get_id(const char *from) -{ - ulonglong id = 0; - ulong length=get_length(from); - if (length) - id=uint8korr(from+packlength); - return id; -} - - -const char *Field_blob::unpack_id(char *to, const char *from, const char *bdata) -{ - memcpy(to,from,packlength); - ulong length=get_length(from); - from+=packlength; - if (length) - memcpy_fixed(to+packlength, &bdata, sizeof(bdata)); - else - bzero(to+packlength,sizeof(bdata)); - return from+sizeof(ulonglong); -} -#endif /* HAVE_GEMINI_DB */ - /* Keys for blobs are like keys on varchars */ int Field_blob::pack_cmp(const char *a, const char *b, uint key_length) @@ -4220,7 +4192,7 @@ char *Field_blob::pack_key(char *to, const char *from, uint max_length) { char *save=ptr; ptr=(char*) from; - ulong length=get_length(); // Length of from string + uint32 length=get_length(); // Length of from string if (length > max_length) length=max_length; *to++= (uchar) length; @@ -4243,9 +4215,9 @@ char *Field_blob::pack_key_from_key_image(char *to, const char *from, uint length=uint2korr(from); if (length > max_length) length=max_length; - *to++= (length & 255); + *to++= (char) (length & 255); if (max_length > 255) - *to++= (uchar) (length >> 8); + *to++= (char) (length >> 8); if (length) memcpy(to, from+2, length); return to+length; @@ -4358,7 +4330,7 @@ void Field_enum::store(const char *from,uint length) conv=buff; } my_errno=0; - tmp=strtoul(conv,&end,10); + tmp=(uint) strtoul(conv,&end,10); if (my_errno || end != conv+length || tmp > typelib->count) { tmp=0; @@ -4628,7 +4600,7 @@ bool Field_enum::eq_def(Field *field) if (!Field::eq_def(field)) return 0; TYPELIB *from_lib=((Field_enum*) field)->typelib; - + if (typelib->count < from_lib->count) return 0; for (uint i=0 ; i < from_lib->count ; i++) @@ -4638,7 +4610,7 @@ bool Field_enum::eq_def(Field *field) } bool Field_num::eq_def(Field *field) -{ +{ if (!Field::eq_def(field)) return 0; Field_num *from_num= (Field_num*) field; @@ -4704,7 +4676,7 @@ uint pack_length_to_packflag(uint type) Field *make_field(char *ptr, uint32 field_length, - uchar *null_pos, uint null_bit, + uchar *null_pos, uchar null_bit, uint pack_flag, Field::utype unireg_check, TYPELIB *interval, diff --git a/sql/field.h b/sql/field.h index b138eb772d8..df31721186e 100644 --- a/sql/field.h +++ b/sql/field.h @@ -1,23 +1,23 @@ /* 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 */ /* -** Because of the function new_field all field classes that have static -** variables must declare the size_of() member function. + Because of the function new_field() all field classes that have static + variables must declare the size_of() member function. */ #ifdef __GNUC__ @@ -31,27 +31,28 @@ struct st_cache_field; void field_conv(Field *to,Field *from); class Field { - Field(const Item &); /* Prevent use of theese */ + Field(const Item &); /* Prevent use of these */ void operator=(Field &); public: static void *operator new(size_t size) {return (void*) sql_alloc((uint) size); } static void operator delete(void *ptr_arg, size_t size) {} /*lint -e715 */ - enum utype { NONE,DATE,SHIELD,NOEMPTY,CASEUP,PNR,BGNR,PGNR,YES,NO,REL, - CHECK,EMPTY,UNKNOWN_FIELD,CASEDN,NEXT_NUMBER,INTERVAL_FIELD, - BIT_FIELD, TIMESTAMP_FIELD,CAPITALIZE,BLOB_FIELD}; - char *ptr; // Position to field in record + char *ptr; // Position to field in record uchar *null_ptr; // Byte where null_bit is - uint8 null_bit; // And position to it struct st_table *table; // Pointer for table - ulong query_id; // For quick test of used fields - key_map key_start,part_of_key,part_of_sortkey;// Field is part of these keys. - const char *table_name,*field_name; - utype unireg_check; - uint32 field_length; // Length of field - uint16 flags; - - Field(char *ptr_arg,uint32 length_arg,uchar *null_ptr_arg,uint null_bit_arg, + const char *table_name,*field_name; + ulong query_id; // For quick test of used fields + // Field is part of the following keys + key_map key_start,part_of_key,part_of_sortkey; + enum utype { NONE,DATE,SHIELD,NOEMPTY,CASEUP,PNR,BGNR,PGNR,YES,NO,REL, + CHECK,EMPTY,UNKNOWN_FIELD,CASEDN,NEXT_NUMBER,INTERVAL_FIELD, + BIT_FIELD, TIMESTAMP_FIELD,CAPITALIZE,BLOB_FIELD}; + utype unireg_check; + uint32 field_length; // Length of field + uint16 flags; + uchar null_bit; // Bit used to test null bit + + Field(char *ptr_arg,uint32 length_arg,uchar *null_ptr_arg,uchar null_bit_arg, utype unireg_check_arg, const char *field_name_arg, struct st_table *table_arg); virtual ~Field() {} @@ -77,7 +78,7 @@ public: virtual enum_field_types real_type() const { return type(); } inline int cmp(const char *str) { return cmp(ptr,str); } virtual int cmp(const char *,const char *)=0; - virtual int cmp_binary(const char *a,const char *b, ulong max_length=~0L) + virtual int cmp_binary(const char *a,const char *b, uint32 max_length=~0L) { return memcmp(a,b,pack_length()); } virtual int cmp_offset(uint row_offset) { return memcmp(ptr,ptr+row_offset,pack_length()); } @@ -92,7 +93,7 @@ public: // Caller beware: sql_type can change str.Ptr, so check // ptr() to see if it changed if you are using your own buffer // in str and restore it with set() if needed - + virtual uint size_of() const =0; // For new field inline bool is_null(uint row_offset=0) { return null_ptr ? (null_ptr[row_offset] & null_bit ? 1 : 0) : table->null_row; } @@ -101,30 +102,30 @@ public: inline void set_null(int row_offset=0) { if (null_ptr) null_ptr[row_offset]|= null_bit; } inline void set_notnull(int row_offset=0) - { if (null_ptr) null_ptr[row_offset]&= ~null_bit; } + { if (null_ptr) null_ptr[row_offset]&= (uchar) ~null_bit; } inline bool maybe_null(void) { return null_ptr != 0 || table->maybe_null; } inline bool real_maybe_null(void) { return null_ptr != 0; } virtual void make_field(Send_field *)=0; virtual void sort_string(char *buff,uint length)=0; - virtual bool optimize_range(); + virtual bool optimize_range(uint idx); virtual bool store_for_compare() { return 0; } - inline Field *new_field(struct st_table *new_table) - { - Field *tmp= (Field*) sql_memdup((char*) this,size_of()); - if (tmp) - { - tmp->table=new_table; - tmp->key_start=tmp->part_of_key=tmp->part_of_sortkey=0; - tmp->unireg_check=Field::NONE; - tmp->flags&= (NOT_NULL_FLAG | BLOB_FLAG | UNSIGNED_FLAG | ZEROFILL_FLAG | BINARY_FLAG | ENUM_FLAG | SET_FLAG); - tmp->reset_fields(); - } - return tmp; - } - inline void move_field(char *ptr_arg,uchar *null_ptr_arg,uint null_bit_arg) + Field *new_field(MEM_ROOT *root, struct st_table *new_table) + { + Field *tmp= (Field*) memdup_root(root,(char*) this,size_of()); + if (tmp) { - ptr=ptr_arg; null_ptr=null_ptr_arg; null_bit=null_bit_arg; + tmp->table=new_table; + tmp->key_start=tmp->part_of_key=tmp->part_of_sortkey=0; + tmp->unireg_check=Field::NONE; + tmp->flags&= (NOT_NULL_FLAG | BLOB_FLAG | UNSIGNED_FLAG | ZEROFILL_FLAG | BINARY_FLAG | ENUM_FLAG | SET_FLAG); + tmp->reset_fields(); } + return tmp; + } + inline void move_field(char *ptr_arg,uchar *null_ptr_arg,uchar null_bit_arg) + { + ptr=ptr_arg; null_ptr=null_ptr_arg; null_bit=null_bit_arg; + } inline void move_field(char *ptr_arg) { ptr=ptr_arg; } inline void move_field(my_ptrdiff_t ptr_diff) { @@ -154,10 +155,10 @@ public: ptr-=row_offset; return tmp; } - bool send(String *packet); + bool send(THD *thd, String *packet); virtual char *pack(char* to, const char *from, uint max_length=~(uint) 0) { - uint length=pack_length(); + uint32 length=pack_length(); memcpy(to,from,length); return to+length; } @@ -212,10 +213,10 @@ public: const uint8 dec; bool zerofill,unsigned_flag; // Purify cannot handle bit fields Field_num(char *ptr_arg,uint32 len_arg, uchar *null_ptr_arg, - uint null_bit_arg, utype unireg_check_arg, + uchar null_bit_arg, utype unireg_check_arg, const char *field_name_arg, struct st_table *table_arg, - uint dec_arg,bool zero_arg,bool unsigned_arg) + uint8 dec_arg,bool zero_arg,bool unsigned_arg) :Field(ptr_arg, len_arg, null_ptr_arg, null_bit_arg, unireg_check_arg, field_name_arg, table_arg), dec(dec_arg),zerofill(zero_arg),unsigned_flag(unsigned_arg) @@ -230,7 +231,7 @@ public: void add_zerofill_and_unsigned(String &res) const; friend class create_field; void make_field(Send_field *); - uint decimals() const { return dec; } + uint decimals() const { return (uint) dec; } uint size_of() const { return sizeof(*this); } bool eq_def(Field *field); }; @@ -239,7 +240,7 @@ public: class Field_str :public Field { public: Field_str(char *ptr_arg,uint32 len_arg, uchar *null_ptr_arg, - uint null_bit_arg, utype unireg_check_arg, + uchar null_bit_arg, utype unireg_check_arg, const char *field_name_arg, struct st_table *table_arg) :Field(ptr_arg, len_arg, null_ptr_arg, null_bit_arg, @@ -256,10 +257,10 @@ public: class Field_decimal :public Field_num { public: Field_decimal(char *ptr_arg, uint32 len_arg, uchar *null_ptr_arg, - uint null_bit_arg, + uchar null_bit_arg, enum utype unireg_check_arg, const char *field_name_arg, struct st_table *table_arg, - uint dec_arg,bool zero_arg,bool unsigned_arg) + uint8 dec_arg,bool zero_arg,bool unsigned_arg) :Field_num(ptr_arg, len_arg, null_ptr_arg, null_bit_arg, unireg_check_arg, field_name_arg, table_arg, dec_arg, zero_arg,unsigned_arg) @@ -285,7 +286,7 @@ public: class Field_tiny :public Field_num { public: Field_tiny(char *ptr_arg, uint32 len_arg, uchar *null_ptr_arg, - uint null_bit_arg, + uchar null_bit_arg, enum utype unireg_check_arg, const char *field_name_arg, struct st_table *table_arg, bool zero_arg, bool unsigned_arg) @@ -314,7 +315,7 @@ public: class Field_short :public Field_num { public: Field_short(char *ptr_arg, uint32 len_arg, uchar *null_ptr_arg, - uint null_bit_arg, + uchar null_bit_arg, enum utype unireg_check_arg, const char *field_name_arg, struct st_table *table_arg, bool zero_arg, bool unsigned_arg) @@ -343,7 +344,7 @@ public: class Field_medium :public Field_num { public: Field_medium(char *ptr_arg, uint32 len_arg, uchar *null_ptr_arg, - uint null_bit_arg, + uchar null_bit_arg, enum utype unireg_check_arg, const char *field_name_arg, struct st_table *table_arg, bool zero_arg, bool unsigned_arg) @@ -372,7 +373,7 @@ public: class Field_long :public Field_num { public: Field_long(char *ptr_arg, uint32 len_arg, uchar *null_ptr_arg, - uint null_bit_arg, + uchar null_bit_arg, enum utype unireg_check_arg, const char *field_name_arg, struct st_table *table_arg, bool zero_arg, bool unsigned_arg) @@ -407,7 +408,7 @@ public: class Field_longlong :public Field_num { public: Field_longlong(char *ptr_arg, uint32 len_arg, uchar *null_ptr_arg, - uint null_bit_arg, + uchar null_bit_arg, enum utype unireg_check_arg, const char *field_name_arg, struct st_table *table_arg, bool zero_arg, bool unsigned_arg) @@ -415,10 +416,11 @@ public: unireg_check_arg, field_name_arg, table_arg, 0, zero_arg,unsigned_arg) {} - Field_longlong(uint32 len_arg,bool maybe_null_arg, const char *field_name_arg, - struct st_table *table_arg) + Field_longlong(uint32 len_arg,bool maybe_null_arg, + const char *field_name_arg, + struct st_table *table_arg, bool unsigned_arg) :Field_num((char*) 0, len_arg, maybe_null_arg ? (uchar*) "": 0,0, - NONE, field_name_arg, table_arg,0,0,0) + NONE, field_name_arg, table_arg,0,0,unsigned_arg) {} enum Item_result result_type () const { return INT_RESULT; } enum_field_types type() const { return FIELD_TYPE_LONGLONG;} @@ -441,10 +443,10 @@ public: class Field_float :public Field_num { public: Field_float(char *ptr_arg, uint32 len_arg, uchar *null_ptr_arg, - uint null_bit_arg, + uchar null_bit_arg, enum utype unireg_check_arg, const char *field_name_arg, struct st_table *table_arg, - uint dec_arg,bool zero_arg,bool unsigned_arg) + uint8 dec_arg,bool zero_arg,bool unsigned_arg) :Field_num(ptr_arg, len_arg, null_ptr_arg, null_bit_arg, unireg_check_arg, field_name_arg, table_arg, dec_arg, zero_arg,unsigned_arg) @@ -468,16 +470,16 @@ public: class Field_double :public Field_num { public: Field_double(char *ptr_arg, uint32 len_arg, uchar *null_ptr_arg, - uint null_bit_arg, + uchar null_bit_arg, enum utype unireg_check_arg, const char *field_name_arg, struct st_table *table_arg, - uint dec_arg,bool zero_arg,bool unsigned_arg) + uint8 dec_arg,bool zero_arg,bool unsigned_arg) :Field_num(ptr_arg, len_arg, null_ptr_arg, null_bit_arg, unireg_check_arg, field_name_arg, table_arg, dec_arg, zero_arg,unsigned_arg) {} Field_double(uint32 len_arg, bool maybe_null_arg, const char *field_name_arg, - struct st_table *table_arg, uint dec_arg) + struct st_table *table_arg, uint8 dec_arg) :Field_num((char*) 0, len_arg, maybe_null_arg ? (uchar*) "": 0, (uint) 0, NONE, field_name_arg, table_arg,dec_arg,0,0) {} @@ -566,7 +568,7 @@ public: class Field_year :public Field_tiny { public: Field_year(char *ptr_arg, uint32 len_arg, uchar *null_ptr_arg, - uint null_bit_arg, + uchar null_bit_arg, enum utype unireg_check_arg, const char *field_name_arg, struct st_table *table_arg) :Field_tiny(ptr_arg, len_arg, null_ptr_arg, null_bit_arg, @@ -585,12 +587,16 @@ public: class Field_date :public Field_str { public: - Field_date(char *ptr_arg, uchar *null_ptr_arg, uint null_bit_arg, + Field_date(char *ptr_arg, uchar *null_ptr_arg, uchar null_bit_arg, enum utype unireg_check_arg, const char *field_name_arg, struct st_table *table_arg) :Field_str(ptr_arg, 10, null_ptr_arg, null_bit_arg, unireg_check_arg, field_name_arg, table_arg) {} + Field_date(bool maybe_null_arg, const char *field_name_arg, + struct st_table *table_arg) + :Field_str((char*) 0,10, maybe_null_arg ? (uchar*) "": 0,0, + NONE, field_name_arg, table_arg) {} enum_field_types type() const { return FIELD_TYPE_DATE;} enum ha_base_keytype key_type() const { return HA_KEYTYPE_ULONG_INT; } enum Item_result cmp_type () const { return INT_RESULT; } @@ -611,7 +617,7 @@ public: class Field_newdate :public Field_str { public: - Field_newdate(char *ptr_arg, uchar *null_ptr_arg, uint null_bit_arg, + Field_newdate(char *ptr_arg, uchar *null_ptr_arg, uchar null_bit_arg, enum utype unireg_check_arg, const char *field_name_arg, struct st_table *table_arg) :Field_str(ptr_arg, 10, null_ptr_arg, null_bit_arg, @@ -642,12 +648,16 @@ public: class Field_time :public Field_str { public: - Field_time(char *ptr_arg, uchar *null_ptr_arg, uint null_bit_arg, + Field_time(char *ptr_arg, uchar *null_ptr_arg, uchar null_bit_arg, enum utype unireg_check_arg, const char *field_name_arg, struct st_table *table_arg) :Field_str(ptr_arg, 8, null_ptr_arg, null_bit_arg, unireg_check_arg, field_name_arg, table_arg) {} + Field_time(bool maybe_null_arg, const char *field_name_arg, + struct st_table *table_arg) + :Field_str((char*) 0,8, maybe_null_arg ? (uchar*) "": 0,0, + NONE, field_name_arg, table_arg) {} enum_field_types type() const { return FIELD_TYPE_TIME;} enum ha_base_keytype key_type() const { return HA_KEYTYPE_INT24; } enum Item_result cmp_type () const { return INT_RESULT; } @@ -670,12 +680,16 @@ public: class Field_datetime :public Field_str { public: - Field_datetime(char *ptr_arg, uchar *null_ptr_arg, uint null_bit_arg, + Field_datetime(char *ptr_arg, uchar *null_ptr_arg, uchar null_bit_arg, enum utype unireg_check_arg, const char *field_name_arg, struct st_table *table_arg) :Field_str(ptr_arg, 19, null_ptr_arg, null_bit_arg, unireg_check_arg, field_name_arg, table_arg) {} + Field_datetime(bool maybe_null_arg, const char *field_name_arg, + struct st_table *table_arg) + :Field_str((char*) 0,19, maybe_null_arg ? (uchar*) "": 0,0, + NONE, field_name_arg, table_arg) {} enum_field_types type() const { return FIELD_TYPE_DATETIME;} #ifdef HAVE_LONG_LONG enum ha_base_keytype key_type() const { return HA_KEYTYPE_ULONGLONG; } @@ -704,7 +718,7 @@ class Field_string :public Field_str { bool binary_flag; public: Field_string(char *ptr_arg, uint32 len_arg,uchar *null_ptr_arg, - uint null_bit_arg, + uchar null_bit_arg, enum utype unireg_check_arg, const char *field_name_arg, struct st_table *table_arg,bool binary_arg) :Field_str(ptr_arg, len_arg, null_ptr_arg, null_bit_arg, @@ -759,7 +773,7 @@ class Field_varstring :public Field_str { bool binary_flag; public: Field_varstring(char *ptr_arg, uint32 len_arg,uchar *null_ptr_arg, - uint null_bit_arg, + uchar null_bit_arg, enum utype unireg_check_arg, const char *field_name_arg, struct st_table *table_arg,bool binary_arg) :Field_str(ptr_arg, len_arg, null_ptr_arg, null_bit_arg, @@ -812,7 +826,7 @@ class Field_blob :public Field_str { String value; // For temporaries bool binary_flag; public: - Field_blob(char *ptr_arg, uchar *null_ptr_arg, uint null_bit_arg, + Field_blob(char *ptr_arg, uchar *null_ptr_arg, uchar null_bit_arg, enum utype unireg_check_arg, const char *field_name_arg, struct st_table *table_arg,uint blob_pack_length, bool binary_arg); @@ -836,21 +850,22 @@ public: longlong val_int(void); String *val_str(String*,String *); int cmp(const char *,const char*); - int cmp(const char *a, ulong a_length, const char *b, ulong b_length); + int cmp(const char *a, uint32 a_length, const char *b, uint32 b_length); int cmp_offset(uint offset); - int cmp_binary(const char *a,const char *b, ulong max_length=~0L); + int cmp_binary(const char *a,const char *b, uint32 max_length=~0L); int cmp_binary_offset(uint row_offset); int key_cmp(const byte *,const byte*); int key_cmp(const byte *str, uint length); uint32 key_length() const { return 0; } void sort_string(char *buff,uint length); - uint32 pack_length() const { return (uint32) (packlength+table->blob_ptr_size); } - void reset(void) { bzero(ptr,packlength+sizeof(char*)); } + uint32 pack_length() const + { return (uint32) (packlength+table->blob_ptr_size); } + void reset(void) { bzero(ptr, packlength+sizeof(char*)); } void reset_fields() { bzero((char*) &value,sizeof(value)); } - void store_length(ulong number); - inline ulong get_length(uint row_offset=0) + void store_length(uint32 number); + inline uint32 get_length(uint row_offset=0) { return get_length(ptr+row_offset); } - ulong get_length(const char *ptr); + uint32 get_length(const char *ptr); bool binary() const { return binary_flag; } inline void get_ptr(char **str) { @@ -861,7 +876,7 @@ public: memcpy(ptr,length,packlength); memcpy_fixed(ptr+packlength,&data,sizeof(char*)); } - inline void set_ptr(ulong length,char *data) + inline void set_ptr(uint32 length,char *data) { store_length(length); memcpy_fixed(ptr+packlength,&data,sizeof(char*)); @@ -882,21 +897,6 @@ public: } char *pack(char *to, const char *from, uint max_length= ~(uint) 0); const char *unpack(char *to, const char *from); -#ifdef HAVE_GEMINI_DB - char *pack_id(char *to, const char *from, ulonglong id, - uint max_length= ~(uint) 0); - ulonglong get_id(const char *from); - const char *unpack_id(char *to, const char *from, const char *bdata); - inline void get_ptr_from_key_image(char **str,char *key_str) - { - *str = key_str + sizeof(uint16); - } - inline uint get_length_from_key_image(char *key_str) - { - return uint2korr(key_str); - } - enum_field_types blobtype() { return (packlength == 1 ? FIELD_TYPE_TINY_BLOB : FIELD_TYPE_BLOB);} -#endif char *pack_key(char *to, const char *from, uint max_length); char *pack_key_from_key_image(char* to, const char *from, uint max_length); int pack_cmp(const char *a, const char *b, uint key_length); @@ -916,7 +916,7 @@ protected: public: TYPELIB *typelib; Field_enum(char *ptr_arg, uint32 len_arg, uchar *null_ptr_arg, - uint null_bit_arg, + uchar null_bit_arg, enum utype unireg_check_arg, const char *field_name_arg, struct st_table *table_arg,uint packlength_arg, TYPELIB *typelib_arg) @@ -944,7 +944,7 @@ public: uint size_of() const { return sizeof(*this); } enum_field_types real_type() const { return FIELD_TYPE_ENUM; } virtual bool zero_pack() const { return 0; } - bool optimize_range() { return 0; } + bool optimize_range(uint idx) { return 0; } bool binary() const { return 0; } bool eq_def(Field *field); }; @@ -953,7 +953,7 @@ public: class Field_set :public Field_enum { public: Field_set(char *ptr_arg, uint32 len_arg, uchar *null_ptr_arg, - uint null_bit_arg, + uchar null_bit_arg, enum utype unireg_check_arg, const char *field_name_arg, struct st_table *table_arg,uint32 packlength_arg, TYPELIB *typelib_arg) @@ -1036,7 +1036,7 @@ public: Field *make_field(char *ptr, uint32 field_length, - uchar *null_pos, uint null_bit, + uchar *null_pos, uchar null_bit, uint pack_flag, Field::utype unireg_check, TYPELIB *interval, const char *field_name, struct st_table *table); @@ -1079,7 +1079,7 @@ bool test_if_int(const char *str,int length); #define f_is_zerofill(x) ((x) & FIELDFLAG_ZEROFILL) #define f_is_packed(x) ((x) & FIELDFLAG_PACK) #define f_packtype(x) (((x) >> FIELDFLAG_PACK_SHIFT) & 15) -#define f_decimals(x) (((x) >> FIELDFLAG_DEC_SHIFT) & FIELDFLAG_MAX_DEC) +#define f_decimals(x) ((uint8) (((x) >> FIELDFLAG_DEC_SHIFT) & FIELDFLAG_MAX_DEC)) #define f_is_alpha(x) (!f_is_num(x)) #define f_is_binary(x) ((x) & FIELDFLAG_BINARY) #define f_is_enum(x) ((x) & FIELDFLAG_INTERVAL) diff --git a/sql/field_conv.cc b/sql/field_conv.cc index 606edd84c74..c7a6d778953 100644 --- a/sql/field_conv.cc +++ b/sql/field_conv.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 */ diff --git a/sql/filesort.cc b/sql/filesort.cc index 86c95395965..8f3e8b10629 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 +#define SKIP_DBUG_IN_FILESORT #endif - /* static variabels */ - -#define MERGEBUFF 7 -#define MERGEBUFF2 15 /* How to write record_ref. */ @@ -36,85 +34,63 @@ 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); +static BUFFPEK *read_buffpek_from_file(IO_CACHE *buffer_file, uint count); static ha_rows find_all_keys(SORTPARAM *param,SQL_SELECT *select, - uchar * *sort_keys, - BUFFPEK *buffpek,uint *maxbuffer, + uchar * *sort_keys, IO_CACHE *buffer_file, IO_CACHE *tempfile,IO_CACHE *indexfile); static int write_keys(SORTPARAM *param,uchar * *sort_keys, - uint count,BUFFPEK *buffpek, - 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, + uint count, IO_CACHE *buffer_file, IO_CACHE *tempfile); +static void make_sortkey(SORTPARAM *param,uchar *to, byte *ref_pos); +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 + + Before calling filesort, one must have done + table->file->info(HA_STATUS_VARIABLE) -ha_rows filesort(TABLE **table, SORT_FIELD *sortorder, uint s_length, + 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) { int error; - uint memavl,old_memavl,maxbuffer,skr; + ulong memavl; + uint maxbuffer; BUFFPEK *buffpek; ha_rows records; uchar **sort_keys; - IO_CACHE tempfile,*selected_records_file,*outfile; + IO_CACHE tempfile, buffpek_pointers, *selected_records_file, *outfile; SORTPARAM param; DBUG_ENTER("filesort"); - DBUG_EXECUTE("info",TEST_filesort(table,sortorder,s_length,special);); -#ifdef SKIPP_DBUG_IN_FILESORT + DBUG_EXECUTE("info",TEST_filesort(sortorder,s_length,special);); +#ifdef SKIP_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; + my_b_clear(&buffpek_pointers); + buffpek=0; + sort_keys= (uchar **) NULL; + error= 1; + bzero((char*) ¶m,sizeof(param)); + 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; 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) @@ -164,51 +137,34 @@ ha_rows filesort(TABLE **table, SORT_FIELD *sortorder, uint s_length, memavl=sortbuff_size; while (memavl >= MIN_SORT_MEMORY) { - if ((ulonglong) (records+1)*(param.sort_length+sizeof(char*))+sizeof(BUFFPEK)*10 < - (ulonglong) memavl) - param.keys=(uint) records+1; - else - { - maxbuffer=1; - do - { - skr=maxbuffer; - if (memavl < sizeof(BUFFPEK)*maxbuffer) - { - my_error(ER_OUT_OF_SORTMEMORY,MYF(ME_ERROR+ME_WAITTANG)); - goto err; - } - param.keys=(memavl-sizeof(BUFFPEK)*maxbuffer)/ - (param.sort_length+sizeof(char*)); - } - while ((maxbuffer= (uint) (records/param.keys+1)) != skr); - } - if ((sort_keys= (uchar **) make_char_array(param.keys,param.sort_length, + ulong old_memavl; + ulong keys= memavl/(param.sort_length+sizeof(char*)); + param.keys=(uint) min(records+1, keys); + if ((sort_keys= (uchar **) make_char_array(param.keys, param.sort_length, MYF(0)))) - if ((buffpek = (BUFFPEK*) my_malloc((uint) sizeof(BUFFPEK)* - (maxbuffer+10), - MYF(0)))) - break; - else - my_free((gptr) sort_keys,MYF(0)); + break; old_memavl=memavl; if ((memavl=memavl/4*3) < MIN_SORT_MEMORY && old_memavl > MIN_SORT_MEMORY) memavl=MIN_SORT_MEMORY; } - param.keys--; - maxbuffer+=10; /* Some extra range */ - if (memavl < MIN_SORT_MEMORY) { my_error(ER_OUTOFMEMORY,MYF(ME_ERROR+ME_WAITTANG),sortbuff_size); goto err; } - param.sort_form= table[0]; + if (open_cached_file(&buffpek_pointers,mysql_tmpdir,TEMP_PREFIX, + DISK_BUFFER_SIZE, MYF(MY_WME))) + goto err; + + param.keys--; + param.sort_form= table; param.end=(param.local_sortorder=sortorder)+s_length; - if ((records=find_all_keys(¶m,select,sort_keys,buffpek,&maxbuffer, + if ((records=find_all_keys(¶m,select,sort_keys, &buffpek_pointers, &tempfile, selected_records_file)) == HA_POS_ERROR) goto err; + maxbuffer= (uint) (my_b_tell(&buffpek_pointers)/sizeof(*buffpek)); + if (maxbuffer == 0) // The whole set is in memory { if (save_index(¶m,sort_keys,(uint) records)) @@ -216,6 +172,9 @@ ha_rows filesort(TABLE **table, SORT_FIELD *sortorder, uint s_length, } else { + if (!(buffpek=read_buffpek_from_file(&buffpek_pointers, maxbuffer))) + goto err; + close_cached_file(&buffpek_pointers); /* Open cached file if it isn't open */ if (! my_b_inited(outfile) && open_cached_file(outfile,mysql_tmpdir,TEMP_PREFIX,READ_RECORD_BUFFER, @@ -223,14 +182,21 @@ ha_rows filesort(TABLE **table, SORT_FIELD *sortorder, uint s_length, goto err; reinit_io_cache(outfile,WRITE_CACHE,0L,0,0); + /* + Use also the space previously used by string pointers in sort_buffer + for temporary key storage. + */ param.keys=((param.keys*(param.sort_length+sizeof(char*))) / param.sort_length-1); - if (merge_many_buff(¶m,sort_keys,buffpek,&maxbuffer,&tempfile)) + maxbuffer--; // Offset from 0 + if (merge_many_buff(¶m,(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(¶m,sort_keys,buffpek,maxbuffer,&tempfile,outfile)) + if (merge_index(¶m,(uchar*) sort_keys,buffpek,maxbuffer,&tempfile, + outfile)) goto err; } if (records > param.max_rows) @@ -245,6 +211,7 @@ ha_rows filesort(TABLE **table, SORT_FIELD *sortorder, uint s_length, x_free((gptr) sort_keys); x_free((gptr) buffpek); close_cached_file(&tempfile); + close_cached_file(&buffpek_pointers); if (my_b_inited(outfile)) { if (flush_io_cache(outfile)) @@ -262,7 +229,7 @@ ha_rows filesort(TABLE **table, SORT_FIELD *sortorder, uint s_length, else statistic_add(filesort_rows, records, &LOCK_status); *examined_rows= param.examined_rows; -#ifdef SKIPP_DBUG_IN_FILESORT +#ifdef SKIP_DBUG_IN_FILESORT DBUG_POP(); /* Ok to DBUG */ #endif DBUG_PRINT("exit",("records: %ld",records)); @@ -289,11 +256,33 @@ static char **make_char_array(register uint fields, uint length, myf my_flag) } /* make_char_array */ + /* Read all buffer pointers into memory */ + +static BUFFPEK *read_buffpek_from_file(IO_CACHE *buffpek_pointers, uint count) +{ + ulong length; + BUFFPEK *tmp; + DBUG_ENTER("read_buffpek_from_file"); + tmp=(BUFFPEK*) my_malloc(length=sizeof(BUFFPEK)*count, MYF(MY_WME)); + if (tmp) + { + if (reinit_io_cache(buffpek_pointers,READ_CACHE,0L,0,0) || + my_b_read(buffpek_pointers, (byte*) tmp, length)) + { + my_free((char*) tmp, MYF(0)); + tmp=0; + } + } + DBUG_RETURN(tmp); +} + + + /* Search after sort_keys and place them in a temp. file */ static ha_rows find_all_keys(SORTPARAM *param, SQL_SELECT *select, uchar **sort_keys, - BUFFPEK *buffpek, uint *maxbuffer, + IO_CACHE *buffpek_pointers, IO_CACHE *tempfile, IO_CACHE *indexfile) { int error,flag,quick_select; @@ -314,7 +303,7 @@ static ha_rows find_all_keys(SORTPARAM *param, SQL_SELECT *select, ref_pos= ref_buff; quick_select=select && select->quick; record=0; - flag= ((!indexfile && file->option_flag() & HA_REC_NOT_IN_SEQ) + flag= ((!indexfile && file->table_flags() & HA_REC_NOT_IN_SEQ) || quick_select); if (indexfile || flag) ref_pos= &file->ref[0]; @@ -375,9 +364,8 @@ static ha_rows find_all_keys(SORTPARAM *param, SQL_SELECT *select, { if (idx == param->keys) { - if (indexpos >= *maxbuffer || - write_keys(param,sort_keys,idx,buffpek+indexpos,tempfile)) - DBUG_RETURN(HA_POS_ERROR); + if (write_keys(param,sort_keys,idx,buffpek_pointers,tempfile)) + DBUG_RETURN(HA_POS_ERROR); idx=0; indexpos++; if (param->ref_length == param->sort_length && my_b_tell(tempfile)/param->sort_length >= param->max_rows) @@ -399,11 +387,9 @@ static ha_rows find_all_keys(SORTPARAM *param, SQL_SELECT *select, file->print_error(error,MYF(ME_ERROR | ME_WAITTANG)); /* purecov: inspected */ DBUG_RETURN(HA_POS_ERROR); /* purecov: inspected */ } - if (indexpos) - if (indexpos >= *maxbuffer || - write_keys(param,sort_keys,idx,buffpek+indexpos,tempfile)) - DBUG_RETURN(HA_POS_ERROR); /* purecov: inspected */ - *maxbuffer=indexpos; + if (indexpos && + write_keys(param,sort_keys,idx,buffpek_pointers,tempfile)) + DBUG_RETURN(HA_POS_ERROR); /* purecov: inspected */ DBUG_RETURN(my_b_inited(tempfile) ? (ha_rows) (my_b_tell(tempfile)/param->sort_length) : idx); @@ -412,10 +398,13 @@ static ha_rows find_all_keys(SORTPARAM *param, SQL_SELECT *select, /* Skriver en buffert med nycklar till filen */ -static int write_keys(SORTPARAM *param, register uchar **sort_keys, uint count, - BUFFPEK *buffpek, IO_CACHE *tempfile) +static int +write_keys(SORTPARAM *param, register uchar **sort_keys, uint count, + IO_CACHE *buffpek_pointers, IO_CACHE *tempfile) { uint sort_length; + uchar **end; + BUFFPEK buffpek; DBUG_ENTER("write_keys"); sort_length=param->sort_length; @@ -427,15 +416,20 @@ static int write_keys(SORTPARAM *param, register uchar **sort_keys, uint count, if (!my_b_inited(tempfile) && open_cached_file(tempfile,mysql_tmpdir,TEMP_PREFIX,DISK_BUFFER_SIZE, MYF(MY_WME))) - DBUG_RETURN(1); /* purecov: inspected */ - buffpek->file_pos=my_b_tell(tempfile); + goto err; /* purecov: inspected */ + buffpek.file_pos=my_b_tell(tempfile); if ((ha_rows) count > param->max_rows) count=(uint) param->max_rows; /* purecov: inspected */ - buffpek->count=(ha_rows) count; - for (uchar **end=sort_keys+count ; sort_keys != end ; sort_keys++) + buffpek.count=(ha_rows) count; + for (end=sort_keys+count ; sort_keys != end ; sort_keys++) if (my_b_write(tempfile,(byte*) *sort_keys,(uint) sort_length)) - DBUG_RETURN(1); + goto err; + if (my_b_write(buffpek_pointers, (byte*) &buffpek, sizeof(buffpek))) + goto err; DBUG_RETURN(0); + +err: + DBUG_RETURN(1); } /* write_keys */ @@ -458,10 +452,7 @@ static void make_sortkey(register SORTPARAM *param, { if (field->is_null()) { - if (sort_field->reverse) - bfill(to,sort_field->length+1,(char) 255); - else - bzero((char*) to,sort_field->length+1); + bzero((char*) to,sort_field->length+1); to+= sort_field->length+1; continue; } @@ -631,8 +622,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; @@ -654,11 +645,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)) @@ -677,8 +668,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; @@ -699,39 +690,44 @@ 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; + qsort2_cmp cmp; volatile bool *killed= ¤t_thd->killed; + bool not_killable; DBUG_ENTER("merge_buffers"); statistic_increment(filesort_merge_passes, &LOCK_status); + if (param->not_killable) + { + killed= ¬_killable; + not_killable=0; + } - 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, @@ -741,15 +737,46 @@ 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) { - error=1; goto err; /* purecov: inspected */ + error=1; goto err; /* purecov: inspected */ } 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)) @@ -766,6 +793,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) { @@ -798,14 +827,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) @@ -813,6 +856,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, @@ -837,7 +881,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); @@ -847,12 +891,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/frm_crypt.cc b/sql/frm_crypt.cc index 629e4ffab95..8dd70900648 100644 --- a/sql/frm_crypt.cc +++ b/sql/frm_crypt.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 */ diff --git a/sql/gen_lex_hash.cc b/sql/gen_lex_hash.cc index 7bbf08b50f3..7ebdbcd8ba8 100644 --- a/sql/gen_lex_hash.cc +++ b/sql/gen_lex_hash.cc @@ -16,17 +16,18 @@ #define NO_YACC_SYMBOLS -#include <global.h> +#include <my_global.h> #include <my_sys.h> #include <m_string.h> #ifndef __GNU_LIBRARY__ -#define __GNU_LIBRARY__ // Skipp warnings in getopt.h +#define __GNU_LIBRARY__ // Skip warnings in getopt.h #endif #include <getopt.h> #include "mysql_version.h" #include "lex.h" -bool opt_search=0,opt_verbose=0; +bool opt_search=0; +int opt_verbose=0; ulong opt_count=100000; #define max_allowed_array 8000 // Don't generate bigger arrays than this @@ -216,7 +217,7 @@ you have to change 'main' to print out the new function\n"); return(1); } - if (opt_verbose) + if (opt_verbose > 1) fprintf (stderr,"Info: Possible add values: %d\n",found-type_count); for (prime=primes; (function_mod=*prime) ; prime++) @@ -385,7 +386,7 @@ static int get_options(int argc, char **argv) opt_search=1; break; case 'v': - opt_verbose=1; + opt_verbose++; break; case 'V': usage(1); exit(0); case 'I': @@ -481,7 +482,8 @@ int main(int argc,char **argv) int error; MY_INIT(argv[0]); - start_value=6130115L; best_t1=3632784L; best_t2=86437L; best_type=3; /* mode=4229 add=2 type: 0 */ + + start_value=1109118L; best_t1=6657025L; best_t2=6114496L; best_type=1; /* mode=4903 add=3 type: 0 */ if (get_options(argc,(char **) argv)) exit(1); @@ -501,7 +503,7 @@ int main(int argc,char **argv) printf("start_value=%ldL; best_t1=%ldL; best_t2=%ldL; best_type=%d; /* mode=%d add=%d type: %d */\n", start_value, best_t1,best_t2,best_type,best_mod,best_add, best_functype); - + best_start_value=start_value; for (uint i=1 ; i <= opt_count ; i++) { if (i % 10 == 0) @@ -524,6 +526,10 @@ int main(int argc,char **argv) best_start_value,best_t1,best_t2,best_type,best_mod,best_add, best_functype); } + if (opt_verbose && (i % 20000) == 0) + printf("\nstart_value=%ldL; best_t1=%ldL; best_t2=%ldL; best_type=%d; /* mode=%d add=%d type: %d */\n", + best_start_value,best_t1,best_t2,best_type,best_mod,best_add, + best_functype); } } diff --git a/sql/ha_berkeley.cc b/sql/ha_berkeley.cc index f52b99f5a12..614d1b5abf5 100644 --- a/sql/ha_berkeley.cc +++ b/sql/ha_berkeley.cc @@ -1043,9 +1043,9 @@ int ha_berkeley::restore_keys(DB_TXN *trans, key_map changed_keys, break; /* purecov: inspected */ } } - + err: - dbug_assert(error != DB_KEYEXIST); + DBUG_ASSERT(error != DB_KEYEXIST); DBUG_RETURN(error); } @@ -1187,7 +1187,7 @@ int ha_berkeley::remove_key(DB_TXN *trans, uint keynr, const byte *record, ((table->key_info[keynr].flags & (HA_NOSAME | HA_NULL_PART_KEY)) == HA_NOSAME)) { // Unique key - dbug_assert(keynr == primary_key || prim_key->data != key_buff2); + DBUG_ASSERT(keynr == primary_key || prim_key->data != key_buff2); error=key_file[keynr]->del(key_file[keynr], trans, keynr == primary_key ? prim_key : @@ -1201,7 +1201,7 @@ int ha_berkeley::remove_key(DB_TXN *trans, uint keynr, const byte *record, row to find the key to be delete and delete it. We will never come here with keynr = primary_key */ - dbug_assert(keynr != primary_key && prim_key->data != key_buff2); + DBUG_ASSERT(keynr != primary_key && prim_key->data != key_buff2); DBC *tmp_cursor; if (!(error=key_file[keynr]->cursor(key_file[keynr], trans, &tmp_cursor, 0))) @@ -1454,6 +1454,37 @@ int ha_berkeley::index_read(byte * buf, const byte * key, DBUG_RETURN(error); } +/* + Read last key is solved by reading the next key and then reading + the previous key +*/ + +int ha_berkeley::index_read_last(byte * buf, const byte * key, uint key_len) +{ + DBT row; + int error; + KEY *key_info= &table->key_info[active_index]; + DBUG_ENTER("ha_berkeley::index_read"); + + statistic_increment(ha_read_key_count,&LOCK_status); + bzero((char*) &row,sizeof(row)); + + /* read of partial key */ + pack_key(&last_key, active_index, key_buff, key, key_len); + /* Store for compare */ + memcpy(key_buff2, key_buff, (key_len=last_key.size)); + key_info->handler.bdb_return_if_eq= 1; + error=read_row(cursor->c_get(cursor, &last_key, &row, DB_SET_RANGE), + (char*) buf, active_index, &row, (DBT*) 0, 0); + key_info->handler.bdb_return_if_eq= 0; + bzero((char*) &row,sizeof(row)); + if (read_row(cursor->c_get(cursor, &last_key, &row, DB_PREV), + (char*) buf, active_index, &row, &last_key, 1) || + berkeley_key_cmp(table, key_info, key_buff2, key_len)) + error=HA_ERR_KEY_NOT_FOUND; + DBUG_RETURN(error); +} + int ha_berkeley::index_next(byte * buf) { @@ -1565,12 +1596,13 @@ int ha_berkeley::rnd_pos(byte * buf, byte *pos) { DBT db_pos; statistic_increment(ha_read_rnd_count,&LOCK_status); + DBUG_ENTER("ha_berkeley::rnd_pos"); active_index= (uint) -1; // Don't delete via cursor - return read_row(file->get(file, transaction, - get_pos(&db_pos, pos), - ¤t_row, 0), - (char*) buf, primary_key, ¤t_row, (DBT*) 0, 0); + DBUG_RETURN(read_row(file->get(file, transaction, + get_pos(&db_pos, pos), + ¤t_row, 0), + (char*) buf, primary_key, ¤t_row, (DBT*) 0, 0)); } void ha_berkeley::position(const byte *record) @@ -1879,13 +1911,14 @@ int ha_berkeley::delete_table(const char *name) { int error; char name_buff[FN_REFLEN]; + DBUG_ENTER("delete_table"); if ((error=db_create(&file, db_env, 0))) my_errno=error; /* purecov: inspected */ else error=file->remove(file,fn_format(name_buff,name,"",ha_berkeley_ext,2 | 4), NULL,0); file=0; // Safety - return error; + DBUG_RETURN(error); } /* @@ -2175,7 +2208,7 @@ static BDB_SHARE *get_share(const char *table_name, TABLE *table) char *tmp_name; DB **key_file; u_int32_t *key_type; - + if ((share=(BDB_SHARE *) my_multi_malloc(MYF(MY_WME | MY_ZEROFILL), &share, sizeof(*share), diff --git a/sql/ha_berkeley.h b/sql/ha_berkeley.h index ab1ead5a3e9..f56ee5ef1a9 100644 --- a/sql/ha_berkeley.h +++ b/sql/ha_berkeley.h @@ -52,7 +52,7 @@ class ha_berkeley: public handler u_int32_t *key_type; DBC *cursor; BDB_SHARE *share; - ulong int_option_flag; + ulong int_table_flags; ulong alloced_rec_buff_length; ulong changed_rows; uint primary_key,last_dup_key, hidden_primary_key, version; @@ -86,20 +86,20 @@ class ha_berkeley: public handler public: ha_berkeley(TABLE *table): handler(table), alloc_ptr(0),rec_buff(0), file(0), - int_option_flag(HA_READ_NEXT | HA_READ_PREV | - HA_REC_NOT_IN_SEQ | - HA_KEYPOS_TO_RNDPOS | HA_READ_ORDER | HA_LASTKEY_ORDER | - HA_LONGLONG_KEYS | HA_NULL_KEY | HA_HAVE_KEY_READ_ONLY | - HA_BLOB_KEY | HA_NOT_EXACT_COUNT | HA_NO_FULLTEXT_KEY | - HA_PRIMARY_KEY_IN_READ_INDEX | HA_DROP_BEFORE_CREATE | - HA_AUTO_PART_KEY), + int_table_flags(HA_REC_NOT_IN_SEQ | + HA_KEYPOS_TO_RNDPOS | HA_LASTKEY_ORDER | + HA_NULL_KEY | HA_HAVE_KEY_READ_ONLY | + HA_BLOB_KEY | HA_NOT_EXACT_COUNT | + HA_PRIMARY_KEY_IN_READ_INDEX | HA_DROP_BEFORE_CREATE | + HA_AUTO_PART_KEY | HA_TABLE_SCAN_ON_INDEX), changed_rows(0),last_dup_key((uint) -1),version(0),using_ignore(0) { } ~ha_berkeley() {} const char *table_type() const { return "BerkeleyDB"; } + const char *index_type(uint key_number) { return "BTREE"; } const char **bas_ext() const; - ulong option_flag() const { return int_option_flag; } + ulong table_flags(void) const { return int_table_flags; } uint max_record_length() const { return HA_MAX_REC_LENGTH; } uint max_keys() const { return MAX_KEY-1; } uint max_key_parts() const { return MAX_REF_PARTS; } @@ -122,6 +122,7 @@ class ha_berkeley: public handler uint key_len, enum ha_rkey_function find_flag); int index_read_idx(byte * buf, uint index, const byte * key, uint key_len, enum ha_rkey_function find_flag); + int index_read_last(byte * buf, const byte * key, uint key_len); int index_next(byte * buf); int index_next_same(byte * buf, const byte *key, uint keylen); int index_prev(byte * buf); diff --git a/sql/ha_gemini.cc b/sql/ha_gemini.cc deleted file mode 100644 index e8130c55fc7..00000000000 --- a/sql/ha_gemini.cc +++ /dev/null @@ -1,3630 +0,0 @@ -/* Copyright (C) 2000 MySQL AB & NuSphere Corporation - - 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 */ - -/* This file is based on ha_berkeley.cc */ - -#ifdef __GNUC__ -#pragma implementation // gcc: Class implementation -#endif - -#include "mysql_priv.h" -#ifdef HAVE_GEMINI_DB -#include "ha_gemini.h" -#include "dbconfig.h" -#include "dsmpub.h" -#include "recpub.h" -#include "vststat.h" - -#include <m_ctype.h> -#include <myisampack.h> -#include <m_string.h> -#include <assert.h> -#include <hash.h> -#include <stdarg.h> -#include "geminikey.h" - -#define gemini_msg MSGD_CALLBACK - -pthread_mutex_t gem_mutex; - -static HASH gem_open_tables; -static GEM_SHARE *get_share(const char *table_name, TABLE *table); -static int free_share(GEM_SHARE *share, bool mutex_is_locked); -static byte* gem_get_key(GEM_SHARE *share,uint *length, - my_bool not_used __attribute__((unused))); -static void gemini_lock_table_overflow_error(dsmContext_t *pcontext); - -const char *ha_gemini_ext=".gmd"; -const char *ha_gemini_idx_ext=".gmi"; - -bool gemini_skip=0; -long gemini_options = 0; -long gemini_buffer_cache; -long gemini_io_threads; -long gemini_log_cluster_size; -long gemini_locktablesize; -long gemini_lock_wait_timeout; -long gemini_spin_retries; -long gemini_connection_limit; -char *gemini_basedir; - -const char gemini_dbname[] = "gemini"; -dsmContext_t *pfirstContext = NULL; - -ulong gemini_recovery_options = GEMINI_RECOVERY_FULL; -/* bits in gemini_recovery_options */ -const char *gemini_recovery_names[] = -{ "FULL", "NONE", "FORCE" }; -TYPELIB gemini_recovery_typelib= {array_elements(gemini_recovery_names),"", - gemini_recovery_names}; - -const int start_of_name = 2; /* Name passed as ./<db>/<table-name> - and we're not interested in the ./ */ -static const int keyBufSize = MAXKEYSZ + FULLKEYHDRSZ + MAX_REF_PARTS + 16; - -static int gemini_tx_begin(THD *thd); -static void print_msg(THD *thd, const char *table_name, const char *op_name, - const char *msg_type, const char *fmt, ...); - -static int gemini_helper_threads(dsmContext_t *pContext); -pthread_handler_decl(gemini_watchdog,arg ); -pthread_handler_decl(gemini_rl_writer,arg ); -pthread_handler_decl(gemini_apw,arg); - -/* General functions */ - -bool gemini_init(void) -{ - dsmStatus_t rc = 0; - char pmsgsfile[MAXPATHN]; - - DBUG_ENTER("gemini_init"); - - gemini_basedir=mysql_home; - /* If datadir isn't set, bail out */ - if (*mysql_real_data_home == '\0') - { - goto badret; - } - - /* dsmContextCreate and dsmContextSetString(DSM_TAGDB_DBNAME) must - ** be the first DSM calls we make so that we can log any errors which - ** occur in subsequent DSM calls. DO NOT INSERT ANY DSM CALLS IN - ** BETWEEN THIS COMMENT AND THE COMMENT THAT SAYS "END OF CODE..." - */ - /* Gotta connect to the database regardless of the operation */ - rc = dsmContextCreate(&pfirstContext); - if( rc != 0 ) - { - gemini_msg(pfirstContext, "dsmContextCreate failed %l",rc); - goto badret; - } - /* This call will also open the log file */ - rc = dsmContextSetString(pfirstContext, DSM_TAGDB_DBNAME, - strlen(gemini_dbname), (TEXT *)gemini_dbname); - if( rc != 0 ) - { - gemini_msg(pfirstContext, "Dbname tag failed %l", rc); - goto badret; - } - /* END OF CODE NOT TO MESS WITH */ - - fn_format(pmsgsfile, GEM_MSGS_FILE, language, ".db", 2 | 4); - rc = dsmContextSetString(pfirstContext, DSM_TAGDB_MSGS_FILE, - strlen(pmsgsfile), (TEXT *)pmsgsfile); - if( rc != 0 ) - { - gemini_msg(pfirstContext, "MSGS_DIR tag failed %l", rc); - goto badret; - } - - strxmov(pmsgsfile, gemini_basedir, GEM_SYM_FILE, NullS); - rc = dsmContextSetString(pfirstContext, DSM_TAGDB_SYMFILE, - strlen(pmsgsfile), (TEXT *)pmsgsfile); - if( rc != 0 ) - { - gemini_msg(pfirstContext, "SYMFILE tag failed %l", rc); - goto badret; - } - - rc = dsmContextSetLong(pfirstContext,DSM_TAGDB_ACCESS_TYPE,DSM_ACCESS_STARTUP); - if ( rc != 0 ) - { - gemini_msg(pfirstContext, "ACCESS TAG set failed %l",rc); - goto badret; - } - rc = dsmContextSetLong(pfirstContext,DSM_TAGDB_ACCESS_ENV, DSM_SQL_ENGINE); - if( rc != 0 ) - { - gemini_msg(pfirstContext, "ACCESS_ENV set failed %l",rc); - goto badret; - } - - rc = dsmContextSetString(pfirstContext, DSM_TAGDB_DATADIR, - strlen(mysql_real_data_home), - (TEXT *)mysql_real_data_home); - if( rc != 0 ) - { - gemini_msg(pfirstContext, "Datadir tag failed %l", rc); - goto badret; - } - - rc = dsmContextSetLong(pfirstContext, DSM_TAGDB_MAX_USERS, - gemini_connection_limit); - if(rc != 0) - { - gemini_msg(pfirstContext, "MAX_USERS tag set failed %l",rc); - goto badret; - } - - rc = dsmContextSetLong(pfirstContext, DSM_TAGDB_DEFAULT_LOCK_TIMEOUT, - gemini_lock_wait_timeout); - if(rc != 0) - { - gemini_msg(pfirstContext, "MAX_LOCK_ENTRIES tag set failed %l",rc); - goto badret; - } - - rc = dsmContextSetLong(pfirstContext, DSM_TAGDB_MAX_LOCK_ENTRIES, - gemini_locktablesize); - if(rc != 0) - { - gemini_msg(pfirstContext, "MAX_LOCK_ENTRIES tag set failed %l",rc); - goto badret; - } - - rc = dsmContextSetLong(pfirstContext, DSM_TAGDB_SPIN_AMOUNT, - gemini_spin_retries); - if(rc != 0) - { - gemini_msg(pfirstContext, "SPIN_AMOUNT tag set failed %l",rc); - goto badret; - } - - /* blocksize is hardcoded to 8K. Buffer cache is in bytes - need to convert this to 8K blocks */ - gemini_buffer_cache = gemini_buffer_cache / 8192; - rc = dsmContextSetLong(pfirstContext, DSM_TAGDB_DB_BUFFERS, - gemini_buffer_cache); - if(rc != 0) - { - gemini_msg(pfirstContext, "DB_BUFFERS tag set failed %l",rc); - goto badret; - } - - rc = dsmContextSetLong(pfirstContext, DSM_TAGDB_FLUSH_AT_COMMIT, - ((gemini_options & GEMOPT_FLUSH_LOG) ? 0 : 1)); - if(rc != 0) - { - gemini_msg(pfirstContext, "FLush_Log_At_Commit tag set failed %l",rc); - goto badret; - } - rc = dsmContextSetLong(pfirstContext, DSM_TAGDB_DIRECT_IO, - ((gemini_options & GEMOPT_UNBUFFERED_IO) ? 1 : 0)); - if(rc != 0) - { - gemini_msg(pfirstContext, "DIRECT_IO tag set failed %l",rc); - goto badret; - } - - rc = dsmContextSetLong(pfirstContext, DSM_TAGDB_CRASH_PROTECTION, - ((gemini_recovery_options & GEMINI_RECOVERY_FULL) ? 1 : 0)); - if(rc != 0) - { - gemini_msg(pfirstContext, "CRASH_PROTECTION tag set failed %l",rc); - goto badret; - } - - if (gemini_recovery_options & GEMINI_RECOVERY_FORCE) - { - rc = dsmContextSetLong(pfirstContext, DSM_TAGDB_FORCE_ACCESS, 1); - if(rc != 0) - { - printf("CRASH_PROTECTION tag set failed %ld",rc); - goto badret; - } - } - - /* cluster size will come in bytes, need to convert it to - 16 K units. */ - gemini_log_cluster_size = (gemini_log_cluster_size + 16383) / 16384; - rc = dsmContextSetLong(pfirstContext, DSM_TAGDB_BI_CLUSTER_SIZE, - gemini_log_cluster_size); - - if(rc != 0) - { - gemini_msg(pfirstContext, "CRASH_PROTECTION tag set failed %l",rc); - goto badret; - } - - rc = dsmUserConnect(pfirstContext,(TEXT *)"Multi-user", - DSM_DB_OPENDB | DSM_DB_OPENFILE); - if( rc != 0 ) - { - /* Message is output in dbenv() */ - goto badret; - } - /* Set access to shared for subsequent user connects */ - rc = dsmContextSetLong(pfirstContext,DSM_TAGDB_ACCESS_TYPE,DSM_ACCESS_SHARED); - - rc = gemini_helper_threads(pfirstContext); - - - (void) hash_init(&gem_open_tables,32,0,0, - (hash_get_key) gem_get_key,0,0); - pthread_mutex_init(&gem_mutex,NULL); - - - DBUG_RETURN(0); - -badret: - gemini_skip = 1; - DBUG_RETURN(0); -} - -static int gemini_helper_threads(dsmContext_t *pContext) -{ - int rc = 0; - int i; - pthread_attr_t thr_attr; - - pthread_t hThread; - DBUG_ENTER("gemini_helper_threads"); - - (void) pthread_attr_init(&thr_attr); -#if !defined(HAVE_DEC_3_2_THREADS) - pthread_attr_setscope(&thr_attr,PTHREAD_SCOPE_SYSTEM); - (void) pthread_attr_setdetachstate(&thr_attr,PTHREAD_CREATE_DETACHED); - pthread_attr_setstacksize(&thr_attr,32768); -#endif - rc = pthread_create (&hThread, &thr_attr, gemini_watchdog, (void *)pContext); - if (rc) - { - gemini_msg(pContext, "Can't Create gemini watchdog thread"); - goto done; - } - if(!gemini_io_threads) - goto done; - - rc = pthread_create(&hThread, &thr_attr, gemini_rl_writer, (void *)pContext); - if(rc) - { - gemini_msg(pContext, "Can't create Gemini recovery log writer thread"); - goto done; - } - - for(i = gemini_io_threads - 1;i;i--) - { - rc = pthread_create(&hThread, &thr_attr, gemini_apw, (void *)pContext); - if(rc) - { - gemini_msg(pContext, "Can't create Gemini database page writer thread"); - goto done; - } - } -done: - - DBUG_RETURN(rc); -} - -pthread_handler_decl(gemini_watchdog,arg ) -{ - int rc = 0; - dsmContext_t *pcontext = (dsmContext_t *)arg; - dsmContext_t *pmyContext = NULL; - - - rc = dsmContextCopy(pcontext,&pmyContext, DSMCONTEXTDB); - if( rc != 0 ) - { - gemini_msg(pcontext, "dsmContextCopy failed for Gemini watchdog %d",rc); - - return 0; - } - rc = dsmUserConnect(pmyContext,NULL,0); - - if( rc != 0 ) - { - gemini_msg(pcontext, "dsmUserConnect failed for Gemini watchdog %d",rc); - - return 0; - } - - my_thread_init(); - pthread_detach_this_thread(); - - while(rc == 0) - { - rc = dsmDatabaseProcessEvents(pmyContext); - if(!rc) - rc = dsmWatchdog(pmyContext); - sleep(1); - } - rc = dsmUserDisconnect(pmyContext,0); - my_thread_end(); - return 0; -} - -pthread_handler_decl(gemini_rl_writer,arg ) -{ - int rc = 0; - dsmContext_t *pcontext = (dsmContext_t *)arg; - dsmContext_t *pmyContext = NULL; - - - rc = dsmContextCopy(pcontext,&pmyContext, DSMCONTEXTDB); - if( rc != 0 ) - { - gemini_msg(pcontext, "dsmContextCopy failed for Gemini recovery log writer %d",rc); - - return 0; - } - rc = dsmUserConnect(pmyContext,NULL,0); - - if( rc != 0 ) - { - gemini_msg(pcontext, "dsmUserConnect failed for Gemini recovery log writer %d",rc); - - return 0; - } - - my_thread_init(); - pthread_detach_this_thread(); - - while(rc == 0) - { - rc = dsmRLwriter(pmyContext); - } - rc = dsmUserDisconnect(pmyContext,0); - my_thread_end(); - return 0; -} - -pthread_handler_decl(gemini_apw,arg ) -{ - int rc = 0; - dsmContext_t *pcontext = (dsmContext_t *)arg; - dsmContext_t *pmyContext = NULL; - - my_thread_init(); - pthread_detach_this_thread(); - - rc = dsmContextCopy(pcontext,&pmyContext, DSMCONTEXTDB); - if( rc != 0 ) - { - gemini_msg(pcontext, "dsmContextCopy failed for Gemini page writer %d",rc); - my_thread_end(); - return 0; - } - rc = dsmUserConnect(pmyContext,NULL,0); - - if( rc != 0 ) - { - gemini_msg(pcontext, "dsmUserConnect failed for Gemini page writer %d",rc); - my_thread_end(); - return 0; - } - - while(rc == 0) - { - rc = dsmAPW(pmyContext); - } - rc = dsmUserDisconnect(pmyContext,0); - my_thread_end(); - return 0; -} - -int gemini_set_option_long(int optid, long optval) -{ - dsmStatus_t rc = 0; - - switch (optid) - { - case GEM_OPTID_SPIN_RETRIES: - /* If we don't have a context yet, skip the set and just save the - ** value in gemini_spin_retries for a later gemini_init(). This - ** may not ever happen, but we're covered if it does. - */ - if (pfirstContext) - { - rc = dsmContextSetLong(pfirstContext, DSM_TAGDB_SPIN_AMOUNT, - optval); - } - if (rc) - { - gemini_msg(pfirstContext, "SPIN_AMOUNT tag set failed %l",rc); - } - else - { - gemini_spin_retries = optval; - } - break; - } - - return rc; -} - -static int gemini_connect(THD *thd) -{ - DBUG_ENTER("gemini_connect"); - - dsmStatus_t rc; - - rc = dsmContextCopy(pfirstContext,(dsmContext_t **)&thd->gemini.context, - DSMCONTEXTDB); - if( rc != 0 ) - { - gemini_msg(pfirstContext, "dsmContextCopy failed %l",rc); - - return(rc); - } - rc = dsmUserConnect((dsmContext_t *)thd->gemini.context,NULL,0); - - if( rc != 0 ) - { - gemini_msg(pfirstContext, "dsmUserConnect failed %l",rc); - - return(rc); - } - rc = (dsmStatus_t)gemini_tx_begin(thd); - - DBUG_RETURN(rc); -} - -void gemini_disconnect(THD *thd) -{ - dsmStatus_t rc; - - if(thd->gemini.context) - { - rc = dsmUserDisconnect((dsmContext_t *)thd->gemini.context,0); - } - return; -} - -bool gemini_end(void) -{ - dsmStatus_t rc; - THD *thd; - - DBUG_ENTER("gemini_end"); - - hash_free(&gem_open_tables); - pthread_mutex_destroy(&gem_mutex); - if(pfirstContext) - { - rc = dsmShutdownSet(pfirstContext, DSM_SHUTDOWN_NORMAL); - sleep(2); - rc = dsmContextSetLong(pfirstContext,DSM_TAGDB_ACCESS_TYPE,DSM_ACCESS_STARTUP); - rc = dsmShutdown(pfirstContext, DSMNICEBIT,DSMNICEBIT); - } - DBUG_RETURN(0); -} - -bool gemini_flush_logs() -{ - DBUG_ENTER("gemini_flush_logs"); - - DBUG_RETURN(0); -} - -static int gemini_tx_begin(THD *thd) -{ - dsmStatus_t rc; - DBUG_ENTER("gemini_tx_begin"); - - thd->gemini.savepoint = 1; - - rc = dsmTransaction((dsmContext_t *)thd->gemini.context, - &thd->gemini.savepoint,DSMTXN_START,0,NULL); - if(!rc) - thd->gemini.needSavepoint = 1; - - thd->gemini.tx_isolation = thd->tx_isolation; - - DBUG_PRINT("trans",("beginning transaction")); - DBUG_RETURN(rc); -} - -int gemini_commit(THD *thd) -{ - dsmStatus_t rc; - LONG txNumber = 0; - - DBUG_ENTER("gemini_commit"); - - if(!thd->gemini.context) - DBUG_RETURN(0); - - rc = dsmTransaction((dsmContext_t *)thd->gemini.context, - 0,DSMTXN_COMMIT,0,NULL); - if(!rc) - rc = gemini_tx_begin(thd); - - thd->gemini.lock_count = 0; - - DBUG_PRINT("trans",("ending transaction")); - DBUG_RETURN(rc); -} - -int gemini_rollback(THD *thd) -{ - dsmStatus_t rc; - LONG txNumber; - - DBUG_ENTER("gemini_rollback"); - DBUG_PRINT("trans",("aborting transaction")); - - if(!thd->gemini.context) - DBUG_RETURN(0); - - thd->gemini.savepoint = 0; - rc = dsmTransaction((dsmContext_t *)thd->gemini.context, - &thd->gemini.savepoint,DSMTXN_ABORT,0,NULL); - if(!rc) - rc = gemini_tx_begin(thd); - - thd->gemini.lock_count = 0; - - DBUG_RETURN(rc); -} - -int gemini_rollback_to_savepoint(THD *thd) -{ - dsmStatus_t rc = 0; - DBUG_ENTER("gemini_rollback_to_savepoint"); - if(thd->gemini.savepoint > 1) - { - rc = dsmTransaction((dsmContext_t *)thd->gemini.context, - &thd->gemini.savepoint,DSMTXN_UNSAVE,0,NULL); - } - DBUG_RETURN(rc); -} - -int gemini_recovery_logging(THD *thd, bool on) -{ - int error; - int noLogging; - - if(!thd->gemini.context) - return 0; - - if(on) - noLogging = 0; - else - noLogging = 1; - - error = dsmContextSetLong((dsmContext_t *)thd->gemini.context, - DSM_TAGCONTEXT_NO_LOGGING,noLogging); - return error; -} - -/* gemDataType - translates from mysql data type constant to gemini - key services data type contstant */ -int gemDataType ( int mysqlType ) -{ - switch (mysqlType) - { - case FIELD_TYPE_LONG: - case FIELD_TYPE_TINY: - case FIELD_TYPE_SHORT: - case FIELD_TYPE_TIMESTAMP: - case FIELD_TYPE_LONGLONG: - case FIELD_TYPE_INT24: - case FIELD_TYPE_DATE: - case FIELD_TYPE_TIME: - case FIELD_TYPE_DATETIME: - case FIELD_TYPE_YEAR: - case FIELD_TYPE_NEWDATE: - case FIELD_TYPE_ENUM: - case FIELD_TYPE_SET: - return GEM_INT; - case FIELD_TYPE_DECIMAL: - return GEM_DECIMAL; - case FIELD_TYPE_FLOAT: - return GEM_FLOAT; - case FIELD_TYPE_DOUBLE: - return GEM_DOUBLE; - case FIELD_TYPE_TINY_BLOB: - return GEM_TINYBLOB; - case FIELD_TYPE_MEDIUM_BLOB: - return GEM_MEDIUMBLOB; - case FIELD_TYPE_LONG_BLOB: - return GEM_LONGBLOB; - case FIELD_TYPE_BLOB: - return GEM_BLOB; - case FIELD_TYPE_VAR_STRING: - case FIELD_TYPE_STRING: - return GEM_CHAR; - } - return -1; -} - -/***************************************************************************** -** Gemini tables -*****************************************************************************/ - -const char **ha_gemini::bas_ext() const -{ static const char *ext[]= { ha_gemini_ext, ha_gemini_idx_ext, NullS }; - return ext; -} - - -int ha_gemini::open(const char *name, int mode, uint test_if_locked) -{ - dsmObject_t tableId = 0; - THD *thd; - char name_buff[FN_REFLEN]; - char tabname_buff[FN_REFLEN]; - char dbname_buff[FN_REFLEN]; - unsigned i,nameLen; - LONG txNumber; - dsmStatus_t rc; - - DBUG_ENTER("ha_gemini::open"); - - thd = current_thd; - /* Init shared structure */ - if (!(share=get_share(name,table))) - { - DBUG_RETURN(1); /* purecov: inspected */ - } - thr_lock_data_init(&share->lock,&lock,(void*) 0); - - ref_length = sizeof(dsmRecid_t); - - if(thd->gemini.context == NULL) - { - /* Need to get this thread a connection into the database */ - rc = gemini_connect(thd); - if(rc) - return rc; - } - if (!(rec_buff=(byte*)my_malloc(table->rec_buff_length, - MYF(MY_WME)))) - { - DBUG_RETURN(1); - } - - /* separate out the name of the table and the database (a VST must be - ** created in the mysql database) - */ - rc = gemini_parse_table_name(name, dbname_buff, tabname_buff); - if (rc == 0) - { - if (strcmp(dbname_buff, "mysql") == 0) - { - tableId = gemini_is_vst(tabname_buff); - } - } - sprintf(name_buff, "%s.%s", dbname_buff, tabname_buff); - - /* if it's not a VST, get the table number the regular way */ - if (!tableId) - { - rc = dsmObjectNameToNum((dsmContext_t *)thd->gemini.context, - (dsmText_t *)name_buff, - &tableId); - if (rc) - { - gemini_msg((dsmContext_t *)thd->gemini.context, - "Unable to find table number for %s", name_buff); - DBUG_RETURN(rc); - } - } - tableNumber = tableId; - - if(!rc) - rc = index_open(name_buff); - - fixed_length_row=!(table->db_create_options & HA_OPTION_PACK_RECORD); - key_read = 0; - using_ignore = 0; - - /* Get the gemini table status -- we want to know if the table - crashed while being in the midst of a repair operation */ - rc = dsmTableStatus((dsmContext_t *)thd->gemini.context, - tableNumber,&tableStatus); - if(tableStatus == DSM_OBJECT_IN_REPAIR) - tableStatus = HA_ERR_CRASHED; - - pthread_mutex_lock(&share->mutex); - share->use_count++; - pthread_mutex_unlock(&share->mutex); - - if (table->blob_fields) - { - /* Allocate room for the blob ids from an unpacked row. Note that - ** we may not actually need all of this space because tiny blobs - ** are stored in the packed row, not in a separate storage object - ** like larger blobs. But we allocate an entry for all blobs to - ** keep the code simpler. - */ - pBlobDescs = (gemBlobDesc_t *)my_malloc( - table->blob_fields * sizeof(gemBlobDesc_t), - MYF(MY_WME | MY_ZEROFILL)); - } - else - { - pBlobDescs = 0; - } - - get_index_stats(thd); - info(HA_STATUS_CONST); - - DBUG_RETURN (rc); -} - -/* Look up and store the object numbers for the indexes on this table */ -int ha_gemini::index_open(char *tableName) -{ - dsmStatus_t rc = 0; - int nameLen; - - DBUG_ENTER("ha_gemini::index_open"); - if(table->keys) - { - THD *thd = current_thd; - dsmObject_t objectNumber; - if (!(pindexNumbers=(dsmIndex_t *)my_malloc(table->keys*sizeof(dsmIndex_t), - MYF(MY_WME)))) - { - DBUG_RETURN(1); - } - nameLen = strlen(tableName); - tableName[nameLen] = '.'; - nameLen++; - - for( uint i = 0; i < table->keys && !rc; i++) - { - strcpy(&tableName[nameLen],table->key_info[i].name); - rc = dsmObjectNameToNum((dsmContext_t *)thd->gemini.context, - (dsmText_t *)tableName, - &objectNumber); - if (rc) - { - gemini_msg((dsmContext_t *)thd->gemini.context, - "Unable to file Index number for %s", tableName); - DBUG_RETURN(rc); - } - pindexNumbers[i] = objectNumber; - } - } - else - pindexNumbers = 0; - - DBUG_RETURN(rc); -} - -int ha_gemini::close(void) -{ - DBUG_ENTER("ha_gemini::close"); - my_free((char*)rec_buff,MYF(MY_ALLOW_ZERO_PTR)); - rec_buff = 0; - my_free((char *)pindexNumbers,MYF(MY_ALLOW_ZERO_PTR)); - pindexNumbers = 0; - - if (pBlobDescs) - { - for (uint i = 0; i < table->blob_fields; i++) - { - my_free((char*)pBlobDescs[i].pBlob, MYF(MY_ALLOW_ZERO_PTR)); - } - my_free((char *)pBlobDescs, MYF(0)); - pBlobDescs = 0; - } - - DBUG_RETURN(free_share(share, 0)); -} - - -int ha_gemini::write_row(byte * record) -{ - int error = 0; - dsmRecord_t dsmRecord; - THD *thd; - - DBUG_ENTER("write_row"); - - if(tableStatus == HA_ERR_CRASHED) - DBUG_RETURN(tableStatus); - - thd = current_thd; - - statistic_increment(ha_write_count,&LOCK_status); - if (table->time_stamp) - update_timestamp(record+table->time_stamp-1); - - if(thd->gemini.needSavepoint || using_ignore) - { - thd->gemini.savepoint++; - error = dsmTransaction((dsmContext_t *)thd->gemini.context, - &thd->gemini.savepoint, - DSMTXN_SAVE, 0, 0); - if (error) - DBUG_RETURN(error); - thd->gemini.needSavepoint = 0; - } - - if (table->next_number_field && record == table->record[0]) - { - if(thd->next_insert_id) - { - ULONG64 nr; - /* A set insert-id statement so set the auto-increment value if this - value is higher than it's current value */ - error = dsmTableAutoIncrement((dsmContext_t *)thd->gemini.context, - tableNumber, (ULONG64 *)&nr,1); - if(thd->next_insert_id > nr) - { - error = dsmTableAutoIncrementSet((dsmContext_t *)thd->gemini.context, - tableNumber, - (ULONG64)thd->next_insert_id); - } - } - - update_auto_increment(); - } - - dsmRecord.table = tableNumber; - dsmRecord.maxLength = table->rec_buff_length; - - if ((error=pack_row((byte **)&dsmRecord.pbuffer, (int *)&dsmRecord.recLength, - record, FALSE))) - { - DBUG_RETURN(error); - } - - error = dsmRecordCreate((dsmContext_t *)thd->gemini.context, - &dsmRecord,0); - - if(!error) - { - error = handleIndexEntries(record, dsmRecord.recid,KEY_CREATE); - if(error == HA_ERR_FOUND_DUPP_KEY && using_ignore) - { - dsmStatus_t rc; - rc = dsmTransaction((dsmContext_t *)thd->gemini.context, - &thd->gemini.savepoint,DSMTXN_UNSAVE,0,NULL); - thd->gemini.needSavepoint = 1; - } - } - if(error == DSM_S_RQSTREJ) - error = HA_ERR_LOCK_WAIT_TIMEOUT; - - DBUG_RETURN(error); -} - -longlong ha_gemini::get_auto_increment() -{ - longlong nr; - int error; - int update; - THD *thd=current_thd; - - if(thd->lex.sql_command == SQLCOM_SHOW_TABLES) - update = 0; - else - update = 1; - - error = dsmTableAutoIncrement((dsmContext_t *)thd->gemini.context, - tableNumber, (ULONG64 *)&nr, - update); - return nr; -} - -/* Put or delete index entries for a row */ -int ha_gemini::handleIndexEntries(const byte * record, dsmRecid_t recid, - enum_key_string_options option) -{ - dsmStatus_t rc = 0; - - DBUG_ENTER("handleIndexEntries"); - - for (uint i = 0; i < table->keys && rc == 0; i++) - { - rc = handleIndexEntry(record, recid,option, i); - } - DBUG_RETURN(rc); -} - -int ha_gemini::handleIndexEntry(const byte * record, dsmRecid_t recid, - enum_key_string_options option,uint keynr) -{ - dsmStatus_t rc = 0; - KEY *key_info; - int keyStringLen; - bool thereIsAnull; - THD *thd; - - AUTOKEY(theKey,keyBufSize); - - DBUG_ENTER("handleIndexEntry"); - - thd = current_thd; - key_info=table->key_info+keynr; - thereIsAnull = FALSE; - rc = createKeyString(record, key_info, theKey.akey.keystr, - sizeof(theKey.apad),&keyStringLen, - (short)pindexNumbers[keynr], - &thereIsAnull); - if(!rc) - { - theKey.akey.index = pindexNumbers[keynr]; - theKey.akey.keycomps = (COUNT)key_info->key_parts; - - /* We have to subtract three here since cxKeyPrepare - expects that the three lead bytes of the header are - not counted in this length -- But cxKeyPrepare also - expects that these three bytes are present in the keystr */ - theKey.akey.keyLen = (COUNT)keyStringLen - FULLKEYHDRSZ; - theKey.akey.unknown_comp = (dsmBoolean_t)thereIsAnull; - theKey.akey.word_index = 0; - theKey.akey.descending_key =0; - if(option == KEY_CREATE) - { - rc = dsmKeyCreate((dsmContext_t *)thd->gemini.context, &theKey.akey, - (dsmTable_t)tableNumber, recid, NULL); - if(rc == DSM_S_IXDUPKEY) - { - last_dup_key=keynr; - rc = HA_ERR_FOUND_DUPP_KEY; - } - } - else if(option == KEY_DELETE) - { - rc = dsmKeyDelete((dsmContext_t *)thd->gemini.context, &theKey.akey, - (dsmTable_t)tableNumber, recid, 0, NULL); - } - else - { - /* KEY_CHECK */ - dsmCursid_t aCursorId; - int error; - - rc = dsmCursorCreate((dsmContext_t *)thd->gemini.context, - (dsmTable_t)tableNumber, - (dsmIndex_t)pindexNumbers[keynr], - &aCursorId,NULL); - - rc = dsmCursorFind((dsmContext_t *)thd->gemini.context, - &aCursorId,&theKey.akey,NULL,DSMDBKEY, - DSMFINDFIRST,DSM_LK_SHARE,0, - &lastRowid,0); - error = dsmCursorDelete((dsmContext_t *)thd->gemini.context, - &aCursorId, 0); - - } - } - DBUG_RETURN(rc); -} - -int ha_gemini::createKeyString(const byte * record, KEY *pkeyinfo, - unsigned char *pkeyBuf, int bufSize, - int *pkeyStringLen, - short geminiIndexNumber, - bool *thereIsAnull) -{ - dsmStatus_t rc = 0; - int componentLen; - int fieldType; - int isNull; - uint key_part_length; - - KEY_PART_INFO *key_part; - - DBUG_ENTER("createKeyString"); - - rc = gemKeyInit(pkeyBuf,pkeyStringLen, geminiIndexNumber); - - for(uint i = 0; i < pkeyinfo->key_parts && rc == 0; i++) - { - unsigned char *pos; - - key_part = pkeyinfo->key_part + i; - key_part_length = key_part->length; - fieldType = gemDataType(key_part->field->type()); - switch (fieldType) - { - case GEM_CHAR: - { - /* Save the current ptr to the field in case we're building a key - to remove an old key value when an indexed character column - gets updated. */ - char *ptr = key_part->field->ptr; - key_part->field->ptr = (char *)record + key_part->offset; - key_part->field->sort_string((char*)rec_buff, key_part->length); - key_part->field->ptr = ptr; - pos = (unsigned char *)rec_buff; - } - break; - - case GEM_TINYBLOB: - case GEM_BLOB: - case GEM_MEDIUMBLOB: - case GEM_LONGBLOB: - ((Field_blob*)key_part->field)->get_ptr((char**)&pos); - key_part_length = ((Field_blob*)key_part->field)->get_length( - (char*)record + key_part->offset); - break; - - default: - pos = (unsigned char *)record + key_part->offset; - break; - } - - isNull = record[key_part->null_offset] & key_part->null_bit; - if(isNull) - *thereIsAnull = TRUE; - - rc = gemFieldToIdxComponent(pos, - (unsigned long) key_part_length, - fieldType, - isNull , - key_part->field->flags & UNSIGNED_FLAG, - pkeyBuf + *pkeyStringLen, - bufSize, - &componentLen); - *pkeyStringLen += componentLen; - } - DBUG_RETURN(rc); -} - - -int ha_gemini::update_row(const byte * old_record, byte * new_record) -{ - int error = 0; - dsmRecord_t dsmRecord; - unsigned long savepoint; - THD *thd = current_thd; - DBUG_ENTER("update_row"); - - statistic_increment(ha_update_count,&LOCK_status); - if (table->time_stamp) - update_timestamp(new_record+table->time_stamp-1); - - if(thd->gemini.needSavepoint || using_ignore) - { - thd->gemini.savepoint++; - error = dsmTransaction((dsmContext_t *)thd->gemini.context, - &thd->gemini.savepoint, - DSMTXN_SAVE, 0, 0); - if (error) - DBUG_RETURN(error); - thd->gemini.needSavepoint = 0; - } - for (uint keynr=0 ; keynr < table->keys ; keynr++) - { - if(key_cmp(keynr,old_record, new_record,FALSE)) - { - error = handleIndexEntry(old_record,lastRowid,KEY_DELETE,keynr); - if(error) - DBUG_RETURN(error); - error = handleIndexEntry(new_record, lastRowid, KEY_CREATE, keynr); - if(error) - { - if (using_ignore && error == HA_ERR_FOUND_DUPP_KEY) - { - dsmStatus_t rc; - rc = dsmTransaction((dsmContext_t *)thd->gemini.context, - &thd->gemini.savepoint,DSMTXN_UNSAVE,0,NULL); - thd->gemini.needSavepoint = 1; - } - DBUG_RETURN(error); - } - } - } - - dsmRecord.table = tableNumber; - dsmRecord.recid = lastRowid; - dsmRecord.maxLength = table->rec_buff_length; - - if ((error=pack_row((byte **)&dsmRecord.pbuffer, (int *)&dsmRecord.recLength, - new_record, TRUE))) - { - DBUG_RETURN(error); - } - error = dsmRecordUpdate((dsmContext_t *)thd->gemini.context, - &dsmRecord, 0, NULL); - - DBUG_RETURN(error); -} - - -int ha_gemini::delete_row(const byte * record) -{ - int error = 0; - dsmRecord_t dsmRecord; - THD *thd = current_thd; - dsmContext_t *pcontext = (dsmContext_t *)thd->gemini.context; - DBUG_ENTER("delete_row"); - - statistic_increment(ha_delete_count,&LOCK_status); - - if(thd->gemini.needSavepoint) - { - thd->gemini.savepoint++; - error = dsmTransaction(pcontext, &thd->gemini.savepoint, DSMTXN_SAVE, 0, 0); - if (error) - DBUG_RETURN(error); - thd->gemini.needSavepoint = 0; - } - - dsmRecord.table = tableNumber; - dsmRecord.recid = lastRowid; - - error = handleIndexEntries(record, dsmRecord.recid,KEY_DELETE); - if(!error) - { - error = dsmRecordDelete(pcontext, &dsmRecord, 0, NULL); - } - - /* Delete any blobs associated with this row */ - if (table->blob_fields) - { - dsmBlob_t gemBlob; - - gemBlob.areaType = DSMOBJECT_BLOB; - gemBlob.blobObjNo = tableNumber; - for (uint i = 0; i < table->blob_fields; i++) - { - if (pBlobDescs[i].blobId) - { - gemBlob.blobId = pBlobDescs[i].blobId; - my_free((char *)pBlobDescs[i].pBlob, MYF(MY_ALLOW_ZERO_PTR)); - dsmBlobStart(pcontext, &gemBlob); - dsmBlobDelete(pcontext, &gemBlob, NULL); - /* according to DSM doc, no need to call dsmBlobEnd() */ - } - } - } - - DBUG_RETURN(error); -} - -int ha_gemini::index_init(uint keynr) -{ - int error = 0; - THD *thd; - DBUG_ENTER("index_init"); - thd = current_thd; - - lastRowid = 0; - active_index=keynr; - error = dsmCursorCreate((dsmContext_t *)thd->gemini.context, - (dsmTable_t)tableNumber, - (dsmIndex_t)pindexNumbers[keynr], - &cursorId,NULL); - pbracketBase = (dsmKey_t *)my_malloc(sizeof(dsmKey_t) + keyBufSize, - MYF(MY_WME)); - if(!pbracketBase) - DBUG_RETURN(1); - pbracketLimit = (dsmKey_t *)my_malloc(sizeof(dsmKey_t) + keyBufSize,MYF(MY_WME)); - if(!pbracketLimit) - { - my_free((char *)pbracketLimit,MYF(0)); - DBUG_RETURN(1); - } - pbracketBase->index = 0; - pbracketLimit->index = (dsmIndex_t)pindexNumbers[keynr]; - pbracketBase->descending_key = pbracketLimit->descending_key = 0; - pbracketBase->ksubstr = pbracketLimit->ksubstr = 0; - pbracketLimit->keycomps = pbracketBase->keycomps = 1; - - pfoundKey = (dsmKey_t *)my_malloc(sizeof(dsmKey_t) + keyBufSize,MYF(MY_WME)); - if(!pfoundKey) - { - my_free((char *)pbracketLimit,MYF(0)); - my_free((char *)pbracketBase,MYF(0)); - DBUG_RETURN(1); - } - - DBUG_RETURN(error); -} - -int ha_gemini::index_end() -{ - int error = 0; - THD *thd; - DBUG_ENTER("index_end"); - thd = current_thd; - error = dsmCursorDelete((dsmContext_t *)thd->gemini.context, - &cursorId, 0); - if(pbracketLimit) - my_free((char *)pbracketLimit,MYF(0)); - if(pbracketBase) - my_free((char *)pbracketBase,MYF(0)); - if(pfoundKey) - my_free((char *)pfoundKey,MYF(0)); - - pbracketLimit = 0; - pbracketBase = 0; - pfoundKey = 0; - DBUG_RETURN(error); -} - -/* This is only used to read whole keys */ - -int ha_gemini::index_read_idx(byte * buf, uint keynr, const byte * key, - uint key_len, enum ha_rkey_function find_flag) -{ - int error = 0; - DBUG_ENTER("index_read_idx"); - statistic_increment(ha_read_key_count,&LOCK_status); - - error = index_init(keynr); - if (!error) - error = index_read(buf,key,key_len,find_flag); - - if(error == HA_ERR_END_OF_FILE) - error = HA_ERR_KEY_NOT_FOUND; - - table->status = error ? STATUS_NOT_FOUND : 0; - DBUG_RETURN(error); -} - -int ha_gemini::pack_key( uint keynr, dsmKey_t *pkey, - const byte *key_ptr, uint key_length) -{ - KEY *key_info=table->key_info+keynr; - KEY_PART_INFO *key_part=key_info->key_part; - KEY_PART_INFO *end=key_part+key_info->key_parts; - int rc; - int componentLen; - DBUG_ENTER("pack_key"); - - rc = gemKeyInit(pkey->keystr,&componentLen, - (short)pindexNumbers[active_index]); - pkey->keyLen = componentLen; - - for (; key_part != end && (int) key_length > 0 && !rc; key_part++) - { - uint offset=0; - unsigned char *pos; - uint key_part_length = key_part->length; - - int fieldType; - if (key_part->null_bit) - { - offset=1; - if (*key_ptr != 0) // Store 0 if NULL - { - key_length-= key_part->store_length; - key_ptr+= key_part->store_length; - rc = gemFieldToIdxComponent( - (unsigned char *)key_ptr + offset, - (unsigned long) key_part_length, - 0, - 1 , /* Tells it to build a null component */ - key_part->field->flags & UNSIGNED_FLAG, - pkey->keystr + pkey->keyLen, - keyBufSize, - &componentLen); - pkey->keyLen += componentLen; - continue; - } - } - fieldType = gemDataType(key_part->field->type()); - switch (fieldType) - { - case GEM_CHAR: - key_part->field->store((char*)key_ptr + offset, key_part->length); - key_part->field->sort_string((char*)rec_buff, key_part->length); - pos = (unsigned char *)rec_buff; - break; - - case GEM_TINYBLOB: - case GEM_BLOB: - case GEM_MEDIUMBLOB: - case GEM_LONGBLOB: - ((Field_blob*)key_part->field)->get_ptr((char**)&pos); - key_part_length = ((Field_blob*)key_part->field)->get_length( - (char*)key_ptr + offset); - break; - - default: - pos = (unsigned char *)key_ptr + offset; - break; - } - - rc = gemFieldToIdxComponent( - pos, - (unsigned long) key_part_length, - fieldType, - 0 , - key_part->field->flags & UNSIGNED_FLAG, - pkey->keystr + pkey->keyLen, - keyBufSize, - &componentLen); - - key_ptr+=key_part->store_length; - key_length-=key_part->store_length; - pkey->keyLen += componentLen; - } - DBUG_RETURN(rc); -} - -void ha_gemini::unpack_key(char *record, dsmKey_t *key, uint index) -{ - KEY *key_info=table->key_info+index; - KEY_PART_INFO *key_part= key_info->key_part, - *end=key_part+key_info->key_parts; - int fieldIsNull, fieldType; - int rc = 0; - - char unsigned *pos= &key->keystr[FULLKEYHDRSZ+4/* 4 for the index number*/]; - - for ( ; key_part != end; key_part++) - { - fieldType = gemDataType(key_part->field->type()); - if(fieldType == GEM_CHAR) - { - /* Can't get data from character indexes since the sort weights - are in the index and not the characters. */ - key_read = 0; - } - rc = gemIdxComponentToField(pos, fieldType, - (unsigned char *)record + key_part->field->offset(), - //key_part->field->field_length, - key_part->length, - key_part->field->decimals(), - &fieldIsNull); - if(fieldIsNull) - { - record[key_part->null_offset] |= key_part->null_bit; - } - else if (key_part->null_bit) - { - record[key_part->null_offset]&= ~key_part->null_bit; - } - while(*pos++); /* Advance to next field in key by finding */ - /* a null byte */ - } -} - -int ha_gemini::index_read(byte * buf, const byte * key, - uint key_len, enum ha_rkey_function find_flag) -{ - int error = 0; - THD *thd; - int componentLen; - - DBUG_ENTER("index_read"); - statistic_increment(ha_read_key_count,&LOCK_status); - - - pbracketBase->index = (short)pindexNumbers[active_index]; - pbracketBase->keycomps = 1; - - - /* Its a greater than operation so create a base bracket - from the input key data. */ - error = pack_key(active_index, pbracketBase, key, key_len); - if(error) - goto errorReturn; - - if(find_flag == HA_READ_AFTER_KEY) - { - /* A greater than operation */ - error = gemKeyAddLow(pbracketBase->keystr + pbracketBase->keyLen, - &componentLen); - pbracketBase->keyLen += componentLen; - } - if(find_flag == HA_READ_KEY_EXACT) - { - /* Need to set up a high bracket for an equality operator - Which is a copy of the base bracket plus a hi lim term */ - bmove(pbracketLimit,pbracketBase,(size_t)pbracketBase->keyLen + sizeof(dsmKey_t)); - error = gemKeyAddHigh(pbracketLimit->keystr + pbracketLimit->keyLen, - &componentLen); - if(error) - goto errorReturn; - pbracketLimit->keyLen += componentLen; - } - else - { - /* Always add a high range -- except for HA_READ_KEY_EXACT this - is all we need for the upper index bracket */ - error = gemKeyHigh(pbracketLimit->keystr, &componentLen, - pbracketLimit->index); - - pbracketLimit->keyLen = componentLen; - } - /* We have to subtract the header size here since cxKeyPrepare - expects that the three lead bytes of the header are - not counted in this length -- But cxKeyPrepare also - expects that these three bytes are present in the keystr */ - pbracketBase->keyLen -= FULLKEYHDRSZ; - pbracketLimit->keyLen -= FULLKEYHDRSZ; - - thd = current_thd; - - error = findRow(thd, DSMFINDFIRST, buf); - -errorReturn: - if (error == DSM_S_ENDLOOP) - error = HA_ERR_KEY_NOT_FOUND; - - table->status = error ? STATUS_NOT_FOUND : 0; - DBUG_RETURN(error); -} - - -int ha_gemini::index_next(byte * buf) -{ - THD *thd; - int error = 1; - int keyStringLen=0; - dsmMask_t findMode; - DBUG_ENTER("index_next"); - - if(tableStatus == HA_ERR_CRASHED) - DBUG_RETURN(tableStatus); - - thd = current_thd; - - if(pbracketBase->index == 0) - { - error = gemKeyLow(pbracketBase->keystr, &keyStringLen, - pbracketLimit->index); - - pbracketBase->keyLen = (COUNT)keyStringLen - FULLKEYHDRSZ; - pbracketBase->index = pbracketLimit->index; - error = gemKeyHigh(pbracketLimit->keystr, &keyStringLen, - pbracketLimit->index); - pbracketLimit->keyLen = (COUNT)keyStringLen - FULLKEYHDRSZ; - - findMode = DSMFINDFIRST; - } - else - findMode = DSMFINDNEXT; - - error = findRow(thd,findMode,buf); - - if (error == DSM_S_ENDLOOP) - error = HA_ERR_END_OF_FILE; - - table->status = error ? STATUS_NOT_FOUND : 0; - DBUG_RETURN(error); -} - -int ha_gemini::index_next_same(byte * buf, const byte *key, uint keylen) -{ - int error = 0; - DBUG_ENTER("index_next_same"); - statistic_increment(ha_read_next_count,&LOCK_status); - DBUG_RETURN(index_next(buf)); -} - - -int ha_gemini::index_prev(byte * buf) -{ - int error = 0; - THD *thd = current_thd; - - DBUG_ENTER("index_prev"); - statistic_increment(ha_read_prev_count,&LOCK_status); - - error = findRow(thd, DSMFINDPREV, buf); - - if (error == DSM_S_ENDLOOP) - error = HA_ERR_END_OF_FILE; - - - table->status = error ? STATUS_NOT_FOUND : 0; - DBUG_RETURN(error); -} - - -int ha_gemini::index_first(byte * buf) -{ - DBUG_ENTER("index_first"); - statistic_increment(ha_read_first_count,&LOCK_status); - DBUG_RETURN(index_next(buf)); -} - -int ha_gemini::index_last(byte * buf) -{ - int error = 0; - THD *thd; - int keyStringLen; - dsmMask_t findMode; - thd = current_thd; - - DBUG_ENTER("index_last"); - statistic_increment(ha_read_last_count,&LOCK_status); - - error = gemKeyLow(pbracketBase->keystr, &keyStringLen, - pbracketLimit->index); - - pbracketBase->keyLen = (COUNT)keyStringLen - FULLKEYHDRSZ; - pbracketBase->index = pbracketLimit->index; - error = gemKeyHigh(pbracketLimit->keystr, &keyStringLen, - pbracketLimit->index); - pbracketLimit->keyLen = (COUNT)keyStringLen - FULLKEYHDRSZ; - - error = findRow(thd,DSMFINDLAST,buf); - - if (error == DSM_S_ENDLOOP) - error = HA_ERR_END_OF_FILE; - - table->status = error ? STATUS_NOT_FOUND : 0; - DBUG_RETURN(error); -} - -int ha_gemini::rnd_init(bool scan) -{ - THD *thd = current_thd; - - lastRowid = 0; - - return 0; -} - -int ha_gemini::rnd_end() -{ -/* - return gem_scan_end(); -*/ - return 0; -} - -int ha_gemini::rnd_next(byte *buf) -{ - int error = 0; - dsmRecord_t dsmRecord; - THD *thd; - - DBUG_ENTER("rnd_next"); - - if(tableStatus == HA_ERR_CRASHED) - DBUG_RETURN(tableStatus); - - thd = current_thd; - if(thd->gemini.tx_isolation == ISO_READ_COMMITTED && !(lockMode & DSM_LK_EXCL) - && lastRowid) - error = dsmObjectUnlock((dsmContext_t *)thd->gemini.context, - tableNumber, DSMOBJECT_RECORD, lastRowid, - lockMode | DSM_UNLK_FREE, 0); - - statistic_increment(ha_read_rnd_next_count,&LOCK_status); - dsmRecord.table = tableNumber; - dsmRecord.recid = lastRowid; - dsmRecord.pbuffer = (dsmBuffer_t *)rec_buff; - dsmRecord.recLength = table->reclength; - dsmRecord.maxLength = table->rec_buff_length; - - error = dsmTableScan((dsmContext_t *)thd->gemini.context, - &dsmRecord, DSMFINDNEXT, lockMode, 0); - - if(!error) - { - lastRowid = dsmRecord.recid; - error = unpack_row((char *)buf,(char *)dsmRecord.pbuffer); - } - if(!error) - ; - else - { - lastRowid = 0; - if (error == DSM_S_ENDLOOP) - error = HA_ERR_END_OF_FILE; - else if (error == DSM_S_RQSTREJ) - error = HA_ERR_LOCK_WAIT_TIMEOUT; - else if (error == DSM_S_LKTBFULL) - { - error = HA_ERR_LOCK_TABLE_FULL; - gemini_lock_table_overflow_error((dsmContext_t *)thd->gemini.context); - } - } - table->status = error ? STATUS_NOT_FOUND : 0; - DBUG_RETURN(error); -} - - -int ha_gemini::rnd_pos(byte * buf, byte *pos) -{ - int error; - int rc; - - THD *thd; - - statistic_increment(ha_read_rnd_count,&LOCK_status); - thd = current_thd; - memcpy((void *)&lastRowid,pos,ref_length); - if(thd->gemini.tx_isolation == ISO_READ_COMMITTED && !(lockMode & DSM_LK_EXCL)) - { - /* Lock the row */ - - error = dsmObjectLock((dsmContext_t *)thd->gemini.context, - (dsmObject_t)tableNumber,DSMOBJECT_RECORD,lastRowid, - lockMode, 1, 0); - if ( error ) - goto errorReturn; - } - error = fetch_row(thd->gemini.context, buf); - if(thd->gemini.tx_isolation == ISO_READ_COMMITTED && !(lockMode & DSM_LK_EXCL)) - { - /* Unlock the row */ - - rc = dsmObjectUnlock((dsmContext_t *)thd->gemini.context, - (dsmObject_t)tableNumber,DSMOBJECT_RECORD,lastRowid, - lockMode | DSM_UNLK_FREE , 0); - } - if(error == DSM_S_RMNOTFND) - error = HA_ERR_RECORD_DELETED; - - errorReturn: - table->status = error ? STATUS_NOT_FOUND : 0; - return error; -} - -int ha_gemini::fetch_row(void *gemini_context,const byte *buf) -{ - dsmStatus_t rc = 0; - dsmRecord_t dsmRecord; - - DBUG_ENTER("fetch_row"); - dsmRecord.table = tableNumber; - dsmRecord.recid = lastRowid; - dsmRecord.pbuffer = (dsmBuffer_t *)rec_buff; - dsmRecord.recLength = table->reclength; - dsmRecord.maxLength = table->rec_buff_length; - - rc = dsmRecordGet((dsmContext_t *)gemini_context, - &dsmRecord, 0); - - if(!rc) - { - rc = unpack_row((char *)buf,(char *)dsmRecord.pbuffer); - } - - DBUG_RETURN(rc); -} -int ha_gemini::findRow(THD *thd, dsmMask_t findMode, byte *buf) -{ - dsmStatus_t rc; - dsmKey_t *pkey; - - DBUG_ENTER("findRow"); - - if(thd->gemini.tx_isolation == ISO_READ_COMMITTED && !(lockMode & DSM_LK_EXCL) - && lastRowid) - rc = dsmObjectUnlock((dsmContext_t *)thd->gemini.context, - tableNumber, DSMOBJECT_RECORD, lastRowid, - lockMode | DSM_UNLK_FREE, 0); - if( key_read ) - pkey = pfoundKey; - else - pkey = 0; - - rc = dsmCursorFind((dsmContext_t *)thd->gemini.context, - &cursorId, - pbracketBase, - pbracketLimit, - DSMPARTIAL, - findMode, - lockMode, - NULL, - &lastRowid, - pkey); - if( rc ) - goto errorReturn; - - if(key_read) - { - unpack_key((char*)buf, pkey, active_index); - } - if(!key_read) /* unpack_key may have turned off key_read */ - { - rc = fetch_row((dsmContext_t *)thd->gemini.context,buf); - } - -errorReturn: - if(!rc) - ; - else - { - lastRowid = 0; - if(rc == DSM_S_RQSTREJ) - rc = HA_ERR_LOCK_WAIT_TIMEOUT; - else if (rc == DSM_S_LKTBFULL) - { - rc = HA_ERR_LOCK_TABLE_FULL; - gemini_lock_table_overflow_error((dsmContext_t *)thd->gemini.context); - } - } - - DBUG_RETURN(rc); -} - -void ha_gemini::position(const byte *record) -{ - memcpy(ref,&lastRowid,ref_length); -} - - -void ha_gemini::info(uint flag) -{ - DBUG_ENTER("info"); - - if ((flag & HA_STATUS_VARIABLE)) - { - THD *thd = current_thd; - dsmStatus_t error; - ULONG64 rows; - - if(thd->gemini.context == NULL) - { - /* Need to get this thread a connection into the database */ - error = gemini_connect(thd); - if(error) - DBUG_VOID_RETURN; - } - - error = dsmRowCount((dsmContext_t *)thd->gemini.context,tableNumber,&rows); - records = (ha_rows)rows; - deleted = 0; - } - if ((flag & HA_STATUS_CONST)) - { - ha_rows *rec_per_key = share->rec_per_key; - for (uint i = 0; i < table->keys; i++) - for(uint k=0; - k < table->key_info[i].key_parts; k++,rec_per_key++) - table->key_info[i].rec_per_key[k] = *rec_per_key; - } - if ((flag & HA_STATUS_ERRKEY)) - { - errkey=last_dup_key; - } - if ((flag & HA_STATUS_TIME)) - { - ; - } - if ((flag & HA_STATUS_AUTO)) - { - THD *thd = current_thd; - dsmStatus_t error; - - error = dsmTableAutoIncrement((dsmContext_t *)thd->gemini.context, - tableNumber, - (ULONG64 *)&auto_increment_value, - 0); - /* Should return the next auto-increment value that - will be given -- so we need to increment the one dsm - currently reports. */ - auto_increment_value++; - } - - DBUG_VOID_RETURN; -} - - -int ha_gemini::extra(enum ha_extra_function operation) -{ - switch (operation) - { - case HA_EXTRA_RESET: - case HA_EXTRA_RESET_STATE: - key_read=0; - using_ignore=0; - break; - case HA_EXTRA_KEYREAD: - key_read=1; // Query satisfied with key - break; - case HA_EXTRA_NO_KEYREAD: - key_read=0; - break; - case HA_EXTRA_IGNORE_DUP_KEY: - using_ignore=1; - break; - case HA_EXTRA_NO_IGNORE_DUP_KEY: - using_ignore=0; - break; - - default: - break; - } - return 0; -} - - -int ha_gemini::reset(void) -{ - key_read=0; // Reset to state after open - return 0; -} - - -/* - As MySQL will execute an external lock for every new table it uses - we can use this to start the transactions. -*/ - -int ha_gemini::external_lock(THD *thd, int lock_type) -{ - dsmStatus_t rc = 0; - LONG txNumber; - - DBUG_ENTER("ha_gemini::external_lock"); - - if (lock_type != F_UNLCK) - { - if (!thd->gemini.lock_count) - { - thd->gemini.lock_count = 1; - thd->gemini.tx_isolation = thd->tx_isolation; - } - // lockMode has already been set in store_lock - // If the statement about to be executed calls for - // exclusive locks and we're running at read uncommitted - // isolation level then raise an error. - if(thd->gemini.tx_isolation == ISO_READ_UNCOMMITTED) - { - if(lockMode == DSM_LK_EXCL) - { - DBUG_RETURN(HA_ERR_READ_ONLY_TRANSACTION); - } - else - { - lockMode = DSM_LK_NOLOCK; - } - } - - if(thd->gemini.context == NULL) - { - /* Need to get this thread a connection into the database */ - rc = gemini_connect(thd); - if(rc) - return rc; - } - /* Set need savepoint flag */ - thd->gemini.needSavepoint = 1; - - if(rc) - DBUG_RETURN(rc); - - - if( thd->in_lock_tables || thd->gemini.tx_isolation == ISO_SERIALIZABLE ) - { - rc = dsmObjectLock((dsmContext_t *)thd->gemini.context, - (dsmObject_t)tableNumber,DSMOBJECT_TABLE,0, - lockMode, 1, 0); - if(rc == DSM_S_RQSTREJ) - rc = HA_ERR_LOCK_WAIT_TIMEOUT; - } - } - else /* lock_type == F_UNLK */ - { - /* Commit the tx if we're in auto-commit mode */ - if (!(thd->options & OPTION_NOT_AUTO_COMMIT)&& - !(thd->options & OPTION_BEGIN)) - gemini_commit(thd); - } - - DBUG_RETURN(rc); -} - - -THR_LOCK_DATA **ha_gemini::store_lock(THD *thd, THR_LOCK_DATA **to, - enum thr_lock_type lock_type) -{ - if (lock_type != TL_IGNORE && lock.type == TL_UNLOCK) - { - /* If we are not doing a LOCK TABLE, then allow multiple writers */ - if ((lock_type >= TL_WRITE_CONCURRENT_INSERT && - lock_type <= TL_WRITE) && - !thd->in_lock_tables) - lock_type = TL_WRITE_ALLOW_WRITE; - lock.type=lock_type; - } - if(table->reginfo.lock_type > TL_WRITE_ALLOW_READ) - lockMode = DSM_LK_EXCL; - else - lockMode = DSM_LK_SHARE; - - *to++= &lock; - return to; -} - -void ha_gemini::update_create_info(HA_CREATE_INFO *create_info) -{ - table->file->info(HA_STATUS_AUTO | HA_STATUS_CONST); - if (!(create_info->used_fields & HA_CREATE_USED_AUTO)) - { - create_info->auto_increment_value=auto_increment_value; - } -} - -int ha_gemini::create(const char *name, register TABLE *form, - HA_CREATE_INFO *create_info) -{ - THD *thd; - char name_buff[FN_REFLEN]; - char dbname_buff[FN_REFLEN]; - DBUG_ENTER("ha_gemini::create"); - dsmContext_t *pcontext; - dsmStatus_t rc; - dsmArea_t areaNumber; - dsmObject_t tableNumber = 0; - dsmDbkey_t dummy = 0; - unsigned i; - int baseNameLen; - dsmObject_t indexNumber; - - /* separate out the name of the table and the database (a VST must be - ** created in the mysql database) - */ - rc = gemini_parse_table_name(name, dbname_buff, name_buff); - if (rc == 0) - { - /* If the table is a VST, don't create areas or extents */ - if (strcmp(dbname_buff, "mysql") == 0) - { - tableNumber = gemini_is_vst(name_buff); - if (tableNumber) - { - return 0; - } - } - } - - thd = current_thd; - if(thd->gemini.context == NULL) - { - /* Need to get this thread a connection into the database */ - rc = gemini_connect(thd); - if(rc) - return rc; - } - pcontext = (dsmContext_t *)thd->gemini.context; - - if(thd->gemini.needSavepoint || using_ignore) - { - thd->gemini.savepoint++; - rc = dsmTransaction((dsmContext_t *)thd->gemini.context, - &thd->gemini.savepoint, - DSMTXN_SAVE, 0, 0); - if (rc) - DBUG_RETURN(rc); - thd->gemini.needSavepoint = 0; - } - - fn_format(name_buff, name, "", ha_gemini_ext, 2 | 4); - /* Create a storage area */ - rc = dsmAreaNew(pcontext,gemini_blocksize,DSMAREA_TYPE_DATA, - &areaNumber, gemini_recbits, - (dsmText_t *)"gemini_data_area"); - if( rc != 0 ) - { - gemini_msg(pcontext, "dsmAreaNew failed %l",rc); - return(rc); - } - - /* Create an extent */ - /* Don't pass in leading ./ in name_buff */ - rc = dsmExtentCreate(pcontext,areaNumber,1,15,5, - (dsmText_t *)&name_buff[start_of_name]); - if( rc != 0 ) - { - gemini_msg(pcontext, "dsmExtentCreate failed %l",rc); - return(rc); - } - - /* Create the table storage object */ - /* Change slashes in the name to periods */ - for( i = 0; i < strlen(name_buff); i++) - if(name_buff[i] == '/' || name_buff[i] == '\\') - name_buff[i] = '.'; - - /* Get rid of .gmd suffix */ - name_buff[strlen(name_buff) - 4] = '\0'; - - rc = dsmObjectCreate(pcontext, areaNumber, &tableNumber, - DSMOBJECT_MIXTABLE,0,0,0, - (dsmText_t *)&name_buff[start_of_name], - &dummy,&dummy); - - if (rc == 0 && table->blob_fields) - { - /* create a storage object record for blob fields */ - rc = dsmObjectCreate(pcontext, areaNumber, &tableNumber, - DSMOBJECT_BLOB,0,0,0, - (dsmText_t *)&name_buff[start_of_name], - &dummy,&dummy); - if( rc != 0 ) - { - gemini_msg(pcontext, "dsmObjectCreate for blob object failed %l",rc); - return(rc); - } - } - - if(rc == 0 && form->keys) - { - fn_format(name_buff, name, "", ha_gemini_idx_ext, 2 | 4); - /* Create a storage area */ - rc = dsmAreaNew(pcontext,gemini_blocksize,DSMAREA_TYPE_DATA, - &areaNumber, gemini_recbits, - (dsmText_t *)"gemini_index_area"); - if( rc != 0 ) - { - gemini_msg(pcontext, "dsmAreaNew failed %l",rc); - return(rc); - } - /* Create an extent */ - /* Don't pass in leading ./ in name_buff */ - rc = dsmExtentCreate(pcontext,areaNumber,1,15,5, - (dsmText_t *)&name_buff[start_of_name]); - if( rc != 0 ) - { - gemini_msg(pcontext, "dsmExtentCreate failed %l",rc); - return(rc); - } - - /* Change slashes in the name to periods */ - for( i = 0; i < strlen(name_buff); i++) - if(name_buff[i] == '/' || name_buff[i] == '\\') - name_buff[i] = '.'; - - /* Get rid of .gmi suffix */ - name_buff[strlen(name_buff) - 4] = '\0'; - - baseNameLen = strlen(name_buff); - name_buff[baseNameLen] = '.'; - baseNameLen++; - for( i = 0; i < form->keys; i++) - { - dsmObjectAttr_t indexUnique; - - indexNumber = DSMINDEX_INVALID; - /* Create a storage object record for each index */ - /* Add the index name so the object name is in the form - <db>.<table>.<index_name> */ - strcpy(&name_buff[baseNameLen],table->key_info[i].name); - if(table->key_info[i].flags & HA_NOSAME) - indexUnique = 1; - else - indexUnique = 0; - rc = dsmObjectCreate(pcontext, areaNumber, &indexNumber, - DSMOBJECT_MIXINDEX,indexUnique,tableNumber, - DSMOBJECT_MIXTABLE, - (dsmText_t *)&name_buff[start_of_name], - &dummy,&dummy); - - } - } - /* The auto_increment value is the next one to be given - out so give dsm one less than this value */ - if(create_info->auto_increment_value) - rc = dsmTableAutoIncrementSet(pcontext,tableNumber, - create_info->auto_increment_value-1); - - /* Get a table lock on this table in case this table is being - created as part of an alter table statement. We don't want - the alter table statement to abort because of a lock table overflow - */ - if (thd->lex.sql_command == SQLCOM_CREATE_INDEX || - thd->lex.sql_command == SQLCOM_ALTER_TABLE || - thd->lex.sql_command == SQLCOM_DROP_INDEX) - { - rc = dsmObjectLock(pcontext, - (dsmObject_t)tableNumber,DSMOBJECT_TABLE,0, - DSM_LK_EXCL, 1, 0); - /* and don't commit so we won't release the table on the table number - of the table being altered */ - } - else - { - if(!rc) - rc = gemini_commit(thd); - } - - DBUG_RETURN(rc); -} - -int ha_gemini::delete_table(const char *pname) -{ - THD *thd; - dsmStatus_t rc; - dsmContext_t *pcontext; - unsigned i,nameLen; - dsmArea_t indexArea = 0; - dsmArea_t tableArea = 0; - dsmObjectAttr_t objectAttr; - dsmObject_t associate; - dsmObjectType_t associateType; - dsmDbkey_t block, root; - int need_txn = 0; - dsmObject_t tableNum = 0; - char name_buff[FN_REFLEN]; - char dbname_buff[FN_REFLEN]; - DBUG_ENTER("ha_gemini::delete_table"); - - /* separate out the name of the table and the database (a VST must be - ** located in the mysql database) - */ - rc = gemini_parse_table_name(pname, dbname_buff, name_buff); - if (rc == 0) - { - /* If the table is a VST, there are no areas or extents to delete */ - if (strcmp(dbname_buff, "mysql") == 0) - { - tableNum = gemini_is_vst(name_buff); - if (tableNum) - { - return 0; - } - } - } - - thd = current_thd; - if(thd->gemini.context == NULL) - { - /* Need to get this thread a connection into the database */ - rc = gemini_connect(thd); - if(rc) - { - DBUG_RETURN(rc); - } - } - pcontext = (dsmContext_t *)thd->gemini.context; - - - bzero(name_buff, FN_REFLEN); - - nameLen = strlen(pname); - for( i = start_of_name; i < nameLen; i++) - { - if(pname[i] == '/' || pname[i] == '\\') - name_buff[i-start_of_name] = '.'; - else - name_buff[i-start_of_name] = pname[i]; - } - - rc = dsmObjectNameToNum(pcontext, (dsmText_t *)name_buff, - (dsmObject_t *)&tableNum); - if (rc) - { - gemini_msg(pcontext, "Unable to find table number for %s", name_buff); - rc = gemini_rollback(thd); - if (rc) - { - gemini_msg(pcontext, "Error in rollback %l",rc); - } - DBUG_RETURN(rc); - } - - rc = dsmObjectInfo(pcontext, tableNum, DSMOBJECT_MIXTABLE, tableNum, - &tableArea, &objectAttr, &associateType, &block, &root); - if (rc) - { - gemini_msg(pcontext, "Failed to get area number for table %d, %s, return %l", - tableNum, pname, rc); - rc = gemini_rollback(thd); - if (rc) - { - gemini_msg(pcontext, "Error in rollback %l",rc); - } - } - - indexArea = DSMAREA_INVALID; - - /* Delete the indexes and tables storage objects for with the table */ - rc = dsmObjectDeleteAssociate(pcontext, tableNum, &indexArea); - if (rc) - { - gemini_msg(pcontext, "Error deleting storage objects for table number %d, return %l", - (int)tableNum, rc); - - /* roll back txn and return */ - rc = gemini_rollback(thd); - if (rc) - { - gemini_msg(pcontext, "Error in rollback %l",rc); - } - DBUG_RETURN(rc); - } - - if (indexArea != DSMAREA_INVALID) - { - /* Delete the extents for both Index and Table */ - rc = dsmExtentDelete(pcontext, indexArea); - rc = dsmAreaDelete(pcontext, indexArea); - if (rc) - { - gemini_msg(pcontext, "Error deleting Index Area %l, return %l", indexArea, rc); - - /* roll back txn and return */ - rc = gemini_rollback(thd); - if (rc) - { - gemini_msg(pcontext, "Error in rollback %l",rc); - } - DBUG_RETURN(rc); - } - } - - rc = dsmExtentDelete(pcontext, tableArea); - rc = dsmAreaDelete(pcontext, tableArea); - if (rc) - { - gemini_msg(pcontext, "Error deleting table Area %l, name %s, return %l", - tableArea, pname, rc); - /* roll back txn and return */ - rc = gemini_rollback(thd); - if (rc) - { - gemini_msg(pcontext, "Error in rollback %l",rc); - } - DBUG_RETURN(rc); - } - - - /* Commit the transaction */ - rc = gemini_commit(thd); - if (rc) - { - gemini_msg(pcontext, "Failed to commit transaction %l",rc); - } - - - /* now remove all the files that need to be removed and - cause a checkpoint so recovery will work */ - rc = dsmExtentUnlink(pcontext); - - DBUG_RETURN(0); -} - - -int ha_gemini::rename_table(const char *pfrom, const char *pto) -{ - THD *thd; - dsmContext_t *pcontext; - dsmStatus_t rc; - char dbname_buff[FN_REFLEN]; - char name_buff[FN_REFLEN]; - char newname_buff[FN_REFLEN]; - char newextname_buff[FN_REFLEN]; - char newidxextname_buff[FN_REFLEN]; - unsigned i, nameLen; - dsmObject_t tableNum; - dsmArea_t indexArea = 0; - dsmArea_t tableArea = 0; - - DBUG_ENTER("ha_gemini::rename_table"); - - /* don't allow rename of VSTs */ - rc = gemini_parse_table_name(pfrom, dbname_buff, name_buff); - if (rc == 0) - { - /* If the table is a VST, don't create areas or extents */ - if (strcmp(dbname_buff, "mysql") == 0) - { - if (gemini_is_vst(name_buff)) - { - return DSM_S_CANT_RENAME_VST; - } - } - } - - thd = current_thd; - if (thd->gemini.context == NULL) - { - /* Need to get this thread a connection into the database */ - rc = gemini_connect(thd); - if (rc) - { - DBUG_RETURN(rc); - } - } - - pcontext = (dsmContext_t *)thd->gemini.context; - - /* change the slashes to dots in the old and new names */ - nameLen = strlen(pfrom); - for( i = start_of_name; i < nameLen; i++) - { - if(pfrom[i] == '/' || pfrom[i] == '\\') - name_buff[i-start_of_name] = '.'; - else - name_buff[i-start_of_name] = pfrom[i]; - } - name_buff[i-start_of_name] = '\0'; - - nameLen = strlen(pto); - for( i = start_of_name; i < nameLen; i++) - { - if(pto[i] == '/' || pto[i] == '\\') - newname_buff[i-start_of_name] = '.'; - else - newname_buff[i-start_of_name] = pto[i]; - } - newname_buff[i-start_of_name] = '\0'; - - /* generate new extent names (for table and index extents) */ - fn_format(newextname_buff, pto, "", ha_gemini_ext, 2 | 4); - fn_format(newidxextname_buff, pto, "", ha_gemini_idx_ext, 2 | 4); - - rc = dsmObjectNameToNum(pcontext, (dsmText_t *)name_buff, &tableNum); - if (rc) - { - gemini_msg(pcontext, "Unable to file Table number for %s", name_buff); - goto errorReturn; - } - - rc = dsmObjectRename(pcontext, tableNum, - (dsmText_t *)newname_buff, - (dsmText_t *)&newidxextname_buff[start_of_name], - (dsmText_t *)&newextname_buff[start_of_name], - &indexArea, &tableArea); - if (rc) - { - gemini_msg(pcontext, "Failed to rename %s to %s",name_buff,newname_buff); - goto errorReturn; - } - - /* Rename the physical table and index files (if necessary). - ** Close the file, rename it, and reopen it (have to do it this - ** way so rename works on Windows). - */ - if (!(rc = dsmAreaClose(pcontext, tableArea))) - { - if (!(rc = rename_file_ext(pfrom, pto, ha_gemini_ext))) - { - rc = dsmAreaOpen(pcontext, tableArea, 0); - if (rc) - { - gemini_msg(pcontext, "Failed to reopen area %d",tableArea); - } - } - } - - if (!rc && indexArea) - { - if (!(rc = dsmAreaClose(pcontext, indexArea))) - { - if (!(rc = rename_file_ext(pfrom, pto, ha_gemini_idx_ext))) - { - rc = dsmAreaOpen(pcontext, indexArea, 0); - if (rc) - { - gemini_msg(pcontext, "Failed to reopen area %d",tableArea); - } - } - } - } - -errorReturn: - DBUG_RETURN(rc); -} - - -/* - How many seeks it will take to read through the table - This is to be comparable to the number returned by records_in_range so - that we can decide if we should scan the table or use keys. -*/ - -double ha_gemini::scan_time() -{ - return (double)records / - (double)((gemini_blocksize / (double)table->reclength)); -} - -int ha_gemini::analyze(THD* thd, HA_CHECK_OPT* check_opt) -{ - int error; - uint saveIsolation; - dsmMask_t saveLockMode; - - check_opt->quick = TRUE; - check_opt->optimize = TRUE; // Tells check not to get table lock - saveLockMode = lockMode; - saveIsolation = thd->gemini.tx_isolation; - thd->gemini.tx_isolation = ISO_READ_UNCOMMITTED; - lockMode = DSM_LK_NOLOCK; - error = check(thd,check_opt); - lockMode = saveLockMode; - thd->gemini.tx_isolation = saveIsolation; - return (error); -} - -int ha_gemini::check(THD* thd, HA_CHECK_OPT* check_opt) -{ - int error = 0; - int checkStatus = HA_ADMIN_OK; - ha_rows indexCount; - byte *buf = 0, *indexBuf = 0, *prevBuf = 0; - int errorCount = 0; - - info(HA_STATUS_VARIABLE); // Makes sure row count is up to date - - /* Get a shared table lock */ - if(thd->gemini.needSavepoint) - { - /* We don't really need a savepoint here but do it anyway - just to keep the savepoint number correct. */ - thd->gemini.savepoint++; - error = dsmTransaction((dsmContext_t *)thd->gemini.context, - &thd->gemini.savepoint, - DSMTXN_SAVE, 0, 0); - if (error) - return(error); - thd->gemini.needSavepoint = 0; - } - buf = (byte*)my_malloc(table->rec_buff_length,MYF(MY_WME)); - indexBuf = (byte*)my_malloc(table->rec_buff_length,MYF(MY_WME)); - prevBuf = (byte*)my_malloc(table->rec_buff_length,MYF(MY_WME |MY_ZEROFILL )); - - /* Lock the table */ - if (!check_opt->optimize) - error = dsmObjectLock((dsmContext_t *)thd->gemini.context, - (dsmObject_t)tableNumber, - DSMOBJECT_TABLE,0, - DSM_LK_SHARE, 1, 0); - if(error) - { - gemini_msg((dsmContext_t *)thd->gemini.context, - "Failed to lock table %d, error %d",tableNumber, error); - return error; - } - - ha_rows *rec_per_key = share->rec_per_key; - /* If quick option just scan along index converting and counting entries */ - for (uint i = 0; i < table->keys; i++) - { - key_read = 1; // Causes data to be extracted from the keys - indexCount = 0; - // Clear the cardinality stats for this index - memset(table->key_info[i].rec_per_key,0, - sizeof(table->key_info[0].rec_per_key[0]) * - table->key_info[i].key_parts); - error = index_init(i); - error = index_first(indexBuf); - while(!error) - { - indexCount++; - if(!check_opt->quick) - { - /* Fetch row and compare to data produced from key */ - error = fetch_row(thd->gemini.context,buf); - if(!error) - { - if(key_cmp(i,buf,indexBuf,FALSE)) - { - - gemini_msg((dsmContext_t *)thd->gemini.context, - "Check Error! Key does not match row for rowid %d for index %s", - lastRowid,table->key_info[i].name); - print_msg(thd,table->real_name,"check","error", - "Key does not match row for rowid %d for index %s", - lastRowid,table->key_info[i].name); - checkStatus = HA_ADMIN_CORRUPT; - errorCount++; - if(errorCount > 1000) - goto error_return; - } - else if(error == DSM_S_RMNOTFND) - { - errorCount++; - checkStatus = HA_ADMIN_CORRUPT; - gemini_msg((dsmContext_t *)thd->gemini.context, - "Check Error! Key does not have a valid row pointer %d for index %s", - lastRowid,table->key_info[i].name); - print_msg(thd,table->real_name,"check","error", - "Key does not have a valid row pointer %d for index %s", - lastRowid,table->key_info[i].name); - if(errorCount > 1000) - goto error_return; - error = 0; - } - } - } - - key_cmp(i,indexBuf,prevBuf,TRUE); - bcopy((void *)indexBuf,(void *)prevBuf,table->rec_buff_length); - - if(!error) - error = index_next(indexBuf); - } - - for(uint j=1; j < table->key_info[i].key_parts; j++) - { - table->key_info[i].rec_per_key[j] += table->key_info[i].rec_per_key[j-1]; - } - for(uint k=0; k < table->key_info[i].key_parts; k++) - { - if (table->key_info[i].rec_per_key[k]) - table->key_info[i].rec_per_key[k] = - records / table->key_info[i].rec_per_key[k]; - *rec_per_key = table->key_info[i].rec_per_key[k]; - rec_per_key++; - } - - if(error == HA_ERR_END_OF_FILE) - { - /* Check count of rows */ - - if(records != indexCount) - { - /* Number of index entries does not agree with the number of - rows in the index. */ - checkStatus = HA_ADMIN_CORRUPT; - gemini_msg((dsmContext_t *)thd->gemini.context, - "Check Error! Total rows %d does not match total index entries %d for %s", - records, indexCount, - table->key_info[i].name); - print_msg(thd,table->real_name,"check","error", - "Total rows %d does not match total index entries %d for %s", - records, indexCount, - table->key_info[i].name); - } - } - else - { - checkStatus = HA_ADMIN_FAILED; - goto error_return; - } - index_end(); - } - if(!check_opt->quick) - { - /* Now scan the table and for each row generate the keys - and find them in the index */ - error = fullCheck(thd, buf); - if(error) - checkStatus = error; - } - // Store the key distribution information - error = saveKeyStats(thd); - -error_return: - my_free((char*)buf,MYF(MY_ALLOW_ZERO_PTR)); - my_free((char*)indexBuf,MYF(MY_ALLOW_ZERO_PTR)); - my_free((char*)prevBuf,MYF(MY_ALLOW_ZERO_PTR)); - - index_end(); - key_read = 0; - if(!check_opt->optimize) - { - error = dsmObjectUnlock((dsmContext_t *)thd->gemini.context, - (dsmObject_t)tableNumber, - DSMOBJECT_TABLE,0, - DSM_LK_SHARE,0); - if (error) - { - gemini_msg((dsmContext_t *)thd->gemini.context, - "Unable to unlock table %d", tableNumber); - } - } - - return checkStatus; -} - -int ha_gemini::saveKeyStats(THD *thd) -{ - dsmStatus_t rc = 0; - - /* Insert a row in the indexStats table for each column of - each index of the table */ - - for(uint i = 0; i < table->keys; i++) - { - for (uint j = 0; j < table->key_info[i].key_parts && !rc ;j++) - { - rc = dsmIndexStatsPut((dsmContext_t *)thd->gemini.context, - tableNumber, pindexNumbers[i], - j, (LONG64)table->key_info[i].rec_per_key[j]); - if (rc) - { - gemini_msg((dsmContext_t *)thd->gemini.context, - "Failed to update index stats for table %d, index %d", - tableNumber, pindexNumbers[i]); - } - } - } - return rc; -} - -int ha_gemini::fullCheck(THD *thd,byte *buf) -{ - int error; - int errorCount = 0; - int checkStatus = 0; - - lastRowid = 0; - - while(((error = rnd_next( buf)) != HA_ERR_END_OF_FILE) && errorCount <= 1000) - { - if(!error) - { - error = handleIndexEntries(buf,lastRowid,KEY_CHECK); - if(error) - { - /* Error finding an index entry for a row. */ - print_msg(thd,table->real_name,"check","error", - "Unable to find all index entries for row %d", - lastRowid); - errorCount++; - checkStatus = HA_ADMIN_CORRUPT; - error = 0; - } - } - else - { - /* Error reading a row */ - print_msg(thd,table->real_name,"check","error", - "Error reading row %d status = %d", - lastRowid,error); - errorCount++; - checkStatus = HA_ADMIN_CORRUPT; - error = 0; - } - } - - return checkStatus; -} - -int ha_gemini::repair(THD* thd, HA_CHECK_OPT* check_opt) -{ - int error; - dsmRecord_t dsmRecord; - byte *buf; - - if(thd->gemini.needSavepoint) - { - /* We don't really need a savepoint here but do it anyway - just to keep the savepoint number correct. */ - thd->gemini.savepoint++; - error = dsmTransaction((dsmContext_t *)thd->gemini.context, - &thd->gemini.savepoint, - DSMTXN_SAVE, 0, 0); - if (error) - { - gemini_msg((dsmContext_t *)thd->gemini.context, - "Error setting savepoint number %d, error %d", - thd->gemini.savepoint++, error); - return(error); - } - thd->gemini.needSavepoint = 0; - } - - - /* Lock the table */ - error = dsmObjectLock((dsmContext_t *)thd->gemini.context, - (dsmObject_t)tableNumber, - DSMOBJECT_TABLE,0, - DSM_LK_EXCL, 1, 0); - if(error) - { - gemini_msg((dsmContext_t *)thd->gemini.context, - "Failed to lock table %d, error %d",tableNumber, error); - return error; - } - - error = dsmContextSetLong((dsmContext_t *)thd->gemini.context, - DSM_TAGCONTEXT_NO_LOGGING,1); - - error = dsmTableReset((dsmContext_t *)thd->gemini.context, - (dsmTable_t)tableNumber, table->keys, - pindexNumbers); - if (error) - { - gemini_msg((dsmContext_t *)thd->gemini.context, - "dsmTableReset failed for table %d, error %d",tableNumber, error); - } - - buf = (byte*)my_malloc(table->rec_buff_length,MYF(MY_WME)); - dsmRecord.table = tableNumber; - dsmRecord.recid = 0; - dsmRecord.pbuffer = (dsmBuffer_t *)rec_buff; - dsmRecord.recLength = table->reclength; - dsmRecord.maxLength = table->rec_buff_length; - while(!error) - { - error = dsmTableScan((dsmContext_t *)thd->gemini.context, - &dsmRecord, DSMFINDNEXT, DSM_LK_NOLOCK, - 1); - if(!error) - { - if (!(error = unpack_row((char *)buf,(char *)dsmRecord.pbuffer))) - { - error = handleIndexEntries(buf,dsmRecord.recid,KEY_CREATE); - if(error == HA_ERR_FOUND_DUPP_KEY) - { - /* We don't want to stop on duplicate keys -- we're repairing - here so let's get as much repaired as possible. */ - error = 0; - } - } - } - } - error = dsmObjectUnlock((dsmContext_t *)thd->gemini.context, - (dsmObject_t)tableNumber, - DSMOBJECT_TABLE,0, - DSM_LK_EXCL,0); - if (error) - { - gemini_msg((dsmContext_t *)thd->gemini.context, - "Unable to unlock table %d", tableNumber); - } - - my_free((char*)buf,MYF(MY_ALLOW_ZERO_PTR)); - - error = dsmContextSetLong((dsmContext_t *)thd->gemini.context, - DSM_TAGCONTEXT_NO_LOGGING,0); - - return error; -} - - -int ha_gemini::restore(THD* thd, HA_CHECK_OPT *check_opt) -{ - dsmContext_t *pcontext = (dsmContext_t *)thd->gemini.context; - char* backup_dir = thd->lex.backup_dir; - char src_path[FN_REFLEN], dst_path[FN_REFLEN]; - char* table_name = table->real_name; - int error = 0; - int errornum; - const char* errmsg = ""; - dsmArea_t tableArea = 0; - dsmObjectAttr_t objectAttr; - dsmObject_t associate; - dsmObjectType_t associateType; - dsmDbkey_t block, root; - dsmStatus_t rc; - - rc = dsmObjectInfo(pcontext, tableNumber, DSMOBJECT_MIXTABLE, tableNumber, - &tableArea, &objectAttr, &associateType, &block, &root); - if (rc) - { - error = HA_ADMIN_FAILED; - errmsg = "Failed in dsmObjectInfo (.gmd) (Error %d)"; - errornum = rc; - gemini_msg(pcontext, errmsg ,errornum); - goto err; - } - - rc = dsmAreaFlush(pcontext, tableArea, FLUSH_BUFFERS | FLUSH_SYNC); - if (rc) - { - error = HA_ADMIN_FAILED; - errmsg = "Failed in dsmAreaFlush (.gmd) (Error %d)"; - errornum = rc; - gemini_msg(pcontext, errmsg ,errornum); - goto err; - } - - rc = dsmAreaClose(pcontext, tableArea); - if (rc) - { - error = HA_ADMIN_FAILED; - errmsg = "Failed in dsmAreaClose (.gmd) (Error %d)"; - errornum = rc; - gemini_msg(pcontext, errmsg ,errornum); - goto err; - } - - /* Restore the data file */ - if (!fn_format(src_path, table_name, backup_dir, ha_gemini_ext, 4 + 64)) - { - return HA_ADMIN_INVALID; - } - - if (my_copy(src_path, fn_format(dst_path, table->path, "", - ha_gemini_ext, 4), MYF(MY_WME))) - { - error = HA_ADMIN_FAILED; - errmsg = "Failed in my_copy (.gmd) (Error %d)"; - errornum = errno; - gemini_msg(pcontext, errmsg ,errornum); - goto err; - } - - rc = dsmAreaFlush(pcontext, tableArea, FREE_BUFFERS); - if (rc) - { - error = HA_ADMIN_FAILED; - errmsg = "Failed in dsmAreaFlush (.gmd) (Error %d)"; - errornum = rc; - gemini_msg(pcontext, errmsg ,errornum); - goto err; - } - - rc = dsmAreaOpen(pcontext, tableArea, 1); - if (rc) - { - error = HA_ADMIN_FAILED; - errmsg = "Failed in dsmAreaOpen (.gmd) (Error %d)"; - errornum = rc; - gemini_msg(pcontext, errmsg ,errornum); - goto err; - } - -#ifdef GEMINI_BACKUP_IDX - dsmArea_t indexArea = 0; - - rc = dsmObjectInfo(pcontext, tableNumber, DSMOBJECT_MIXINDEX, &indexArea, - &objectAttr, &associate, &associateType, &block, &root); - if (rc) - { - error = HA_ADMIN_FAILED; - errmsg = "Failed in dsmObjectInfo (.gmi) (Error %d)"; - errornum = rc; - gemini_msg(pcontext, errmsg ,errornum); - goto err; - } - - rc = dsmAreaClose(pcontext, indexArea); - if (rc) - { - error = HA_ADMIN_FAILED; - errmsg = "Failed in dsmAreaClose (.gmi) (Error %d)"; - errornum = rc; - gemini_msg(pcontext, errmsg ,errornum); - goto err; - } - - /* Restore the index file */ - if (!fn_format(src_path, table_name, backup_dir, ha_gemini_idx_ext, 4 + 64)) - { - return HA_ADMIN_INVALID; - } - - if (my_copy(src_path, fn_format(dst_path, table->path, "", - ha_gemini_idx_ext, 4), MYF(MY_WME))) - { - error = HA_ADMIN_FAILED; - errmsg = "Failed in my_copy (.gmi) (Error %d)"; - errornum = errno; - gemini_msg(pcontext, errmsg ,errornum); - goto err; - } - - rc = dsmAreaOpen(pcontext, indexArea, 1); - if (rc) - { - error = HA_ADMIN_FAILED; - errmsg = "Failed in dsmAreaOpen (.gmi) (Error %d)"; - errornum = rc; - gemini_msg(pcontext, errmsg ,errornum); - goto err; - } - - return HA_ADMIN_OK; -#else /* #ifdef GEMINI_BACKUP_IDX */ - HA_CHECK_OPT tmp_check_opt; - tmp_check_opt.init(); - /* The following aren't currently implemented in ha_gemini::repair - ** tmp_check_opt.quick = 1; - ** tmp_check_opt.flags |= T_VERY_SILENT; - */ - return (repair(thd, &tmp_check_opt)); -#endif /* #ifdef GEMINI_BACKUP_IDX */ - - err: - { -#if 0 - /* mi_check_print_error is in ha_myisam.cc, so none of the informative - ** error messages above is currently being printed - */ - MI_CHECK param; - myisamchk_init(¶m); - param.thd = thd; - param.op_name = (char*)"restore"; - param.table_name = table->table_name; - param.testflag = 0; - mi_check_print_error(¶m,errmsg, errornum); -#endif - return error; - } -} - - -int ha_gemini::backup(THD* thd, HA_CHECK_OPT *check_opt) -{ - dsmContext_t *pcontext = (dsmContext_t *)thd->gemini.context; - char* backup_dir = thd->lex.backup_dir; - char src_path[FN_REFLEN], dst_path[FN_REFLEN]; - char* table_name = table->real_name; - int error = 0; - int errornum; - const char* errmsg = ""; - dsmArea_t tableArea = 0; - dsmObjectAttr_t objectAttr; - dsmObject_t associate; - dsmObjectType_t associateType; - dsmDbkey_t block, root; - dsmStatus_t rc; - - rc = dsmObjectInfo(pcontext, tableNumber, DSMOBJECT_MIXTABLE, tableNumber, - &tableArea, &objectAttr, &associateType, &block, &root); - if (rc) - { - error = HA_ADMIN_FAILED; - errmsg = "Failed in dsmObjectInfo (.gmd) (Error %d)"; - errornum = rc; - goto err; - } - - /* Flush the buffers before backing up the table */ - dsmAreaFlush((dsmContext_t *)thd->gemini.context, tableArea, - FLUSH_BUFFERS | FLUSH_SYNC); - if (rc) - { - error = HA_ADMIN_FAILED; - errmsg = "Failed in dsmAreaFlush (.gmd) (Error %d)"; - errornum = rc; - gemini_msg(pcontext, errmsg ,errornum); - goto err; - } - - /* Backup the .FRM file */ - if (!fn_format(dst_path, table_name, backup_dir, reg_ext, 4 + 64)) - { - errmsg = "Failed in fn_format() for .frm file: errno = %d"; - error = HA_ADMIN_INVALID; - errornum = errno; - gemini_msg(pcontext, errmsg ,errornum); - goto err; - } - - if (my_copy(fn_format(src_path, table->path,"", reg_ext, 4), - dst_path, - MYF(MY_WME | MY_HOLD_ORIGINAL_MODES ))) - { - error = HA_ADMIN_FAILED; - errmsg = "Failed copying .frm file: errno = %d"; - errornum = errno; - gemini_msg(pcontext, errmsg ,errornum); - goto err; - } - - /* Backup the data file */ - if (!fn_format(dst_path, table_name, backup_dir, ha_gemini_ext, 4 + 64)) - { - errmsg = "Failed in fn_format() for .GMD file: errno = %d"; - error = HA_ADMIN_INVALID; - errornum = errno; - gemini_msg(pcontext, errmsg ,errornum); - goto err; - } - - if (my_copy(fn_format(src_path, table->path,"", ha_gemini_ext, 4), - dst_path, - MYF(MY_WME | MY_HOLD_ORIGINAL_MODES )) ) - { - errmsg = "Failed copying .GMD file: errno = %d"; - error= HA_ADMIN_FAILED; - errornum = errno; - gemini_msg(pcontext, errmsg ,errornum); - goto err; - } - -#ifdef GEMINI_BACKUP_IDX - dsmArea_t indexArea = 0; - - rc = dsmObjectInfo(pcontext, tableNumber, DSMOBJECT_MIXINDEX, &indexArea, - &objectAttr, &associate, &associateType, &block, &root); - if (rc) - { - error = HA_ADMIN_FAILED; - errmsg = "Failed in dsmObjectInfo (.gmi) (Error %d)"; - errornum = rc; - gemini_msg(pcontext, errmsg ,errornum); - goto err; - } - - /* Backup the index file */ - if (!fn_format(dst_path, table_name, backup_dir, ha_gemini_idx_ext, 4 + 64)) - { - errmsg = "Failed in fn_format() for .GMI file: errno = %d"; - error = HA_ADMIN_INVALID; - errornum = errno; - gemini_msg(pcontext, errmsg ,errornum); - goto err; - } - - if (my_copy(fn_format(src_path, table->path,"", ha_gemini_idx_ext, 4), - dst_path, - MYF(MY_WME | MY_HOLD_ORIGINAL_MODES )) ) - { - errmsg = "Failed copying .GMI file: errno = %d"; - error= HA_ADMIN_FAILED; - errornum = errno; - gemini_msg(pcontext, errmsg ,errornum); - goto err; - } -#endif /* #ifdef GEMINI_BACKUP_IDX */ - - return HA_ADMIN_OK; - - err: - { -#if 0 - /* mi_check_print_error is in ha_myisam.cc, so none of the informative - ** error messages above is currently being printed - */ - MI_CHECK param; - myisamchk_init(¶m); - param.thd = thd; - param.op_name = (char*)"backup"; - param.table_name = table->table_name; - param.testflag = 0; - mi_check_print_error(¶m,errmsg, errornum); -#endif - return error; - } -} - - -int ha_gemini::optimize(THD* thd, HA_CHECK_OPT *check_opt) -{ - return HA_ADMIN_ALREADY_DONE; -} - - -ha_rows ha_gemini::records_in_range(int keynr, - const byte *start_key,uint start_key_len, - enum ha_rkey_function start_search_flag, - const byte *end_key,uint end_key_len, - enum ha_rkey_function end_search_flag) -{ - int error; - int componentLen; - float pctInrange; - ha_rows rows = 5; - - DBUG_ENTER("records_in_range"); - - error = index_init(keynr); - if(error) - DBUG_RETURN(rows); - - pbracketBase->index = (short)pindexNumbers[keynr]; - pbracketBase->keycomps = 1; - - if(start_key) - { - error = pack_key(keynr, pbracketBase, start_key, start_key_len); - if(start_search_flag == HA_READ_AFTER_KEY) - { - /* A greater than operation */ - error = gemKeyAddLow(pbracketBase->keystr + pbracketBase->keyLen, - &componentLen); - pbracketBase->keyLen += componentLen; - } - } - else - { - error = gemKeyLow(pbracketBase->keystr, &componentLen, - pbracketBase->index); - pbracketBase->keyLen = componentLen; - - } - pbracketBase->keyLen -= FULLKEYHDRSZ; - - if(end_key) - { - error = pack_key(keynr, pbracketLimit, end_key, end_key_len); - if(!error && end_search_flag == HA_READ_AFTER_KEY) - { - error = gemKeyAddHigh(pbracketLimit->keystr + pbracketLimit->keyLen, - &componentLen); - pbracketLimit->keyLen += componentLen; - } - } - else - { - error = gemKeyHigh(pbracketLimit->keystr,&componentLen, - pbracketLimit->index); - pbracketLimit->keyLen = componentLen; - } - - pbracketLimit->keyLen -= FULLKEYHDRSZ; - error = dsmIndexRowsInRange((dsmContext_t *)current_thd->gemini.context, - pbracketBase,pbracketLimit, - tableNumber, - &pctInrange); - if(pctInrange >= 1) - rows = (ha_rows)pctInrange; - else - { - rows = (ha_rows)(records * pctInrange); - if(!rows && pctInrange > 0) - rows = 1; - } - index_end(); - - DBUG_RETURN(rows); -} - - -/* - Pack a row for storage. If the row is of fixed length, just store the - row 'as is'. - If not, we will generate a packed row suitable for storage. - This will only fail if we don't have enough memory to pack the row, which; - may only happen in rows with blobs, as the default row length is - pre-allocated. -*/ -int ha_gemini::pack_row(byte **pprow, int *ppackedLength, const byte *record, - bool update) -{ - THD *thd = current_thd; - dsmContext_t *pcontext = (dsmContext_t *)thd->gemini.context; - gemBlobDesc_t *pBlobDesc = pBlobDescs; - - if (fixed_length_row) - { - *pprow = (byte *)record; - *ppackedLength=(int)table->reclength; - return 0; - } - /* Copy null bits */ - memcpy(rec_buff, record, table->null_bytes); - byte *ptr=rec_buff + table->null_bytes; - - for (Field **field=table->field ; *field ; field++) - { -#ifdef GEMINI_TINYBLOB_IN_ROW - /* Tiny blobs (255 bytes or less) are stored in the row; larger - ** blobs are stored in a separate storage object (see ha_gemini::create). - */ - if ((*field)->type() == FIELD_TYPE_BLOB && - ((Field_blob*)*field)->blobtype() != FIELD_TYPE_TINY_BLOB) -#else - if ((*field)->type() == FIELD_TYPE_BLOB) -#endif - { - dsmBlob_t gemBlob; - char *blobptr; - - gemBlob.areaType = DSMOBJECT_BLOB; - gemBlob.blobObjNo = tableNumber; - gemBlob.blobId = 0; - gemBlob.totLength = gemBlob.segLength = - ((Field_blob*)*field)->get_length((char*)record + (*field)->offset()); - ((Field_blob*)*field)->get_ptr((char**) &blobptr); - gemBlob.pBuffer = (dsmBuffer_t *)blobptr; - gemBlob.blobContext.blobOffset = 0; - if (gemBlob.totLength) - { - dsmBlobStart(pcontext, &gemBlob); - if (update && pBlobDesc->blobId) - { - gemBlob.blobId = pBlobDesc->blobId; - dsmBlobUpdate(pcontext, &gemBlob, NULL); - } - else - { - dsmBlobPut(pcontext, &gemBlob, NULL); - } - dsmBlobEnd(pcontext, &gemBlob); - } - ptr = (byte*)((Field_blob*)*field)->pack_id((char*) ptr, - (char*)record + (*field)->offset(), (longlong)gemBlob.blobId); - - pBlobDesc++; - } - else - { - ptr=(byte*) (*field)->pack((char*) ptr, (char*)record + (*field)->offset()); - } - } - - *pprow=rec_buff; - *ppackedLength= (ptr - rec_buff); - return 0; -} - -int ha_gemini::unpack_row(char *record, char *prow) -{ - THD *thd = current_thd; - dsmContext_t *pcontext = (dsmContext_t *)thd->gemini.context; - gemBlobDesc_t *pBlobDesc = pBlobDescs; - - if (fixed_length_row) - { - /* If the table is a VST, the row is in Gemini internal format. - ** Convert the fields to MySQL format. - */ - if (RM_IS_VST(tableNumber)) - { - int i = 2; /* VST fields are numbered sequentially starting at 2 */ - long longValue; - char *fld; - unsigned long unknown; - - for (Field **field = table->field; *field; field++, i++) - { - switch ((*field)->type()) - { - case FIELD_TYPE_LONG: - case FIELD_TYPE_TINY: - case FIELD_TYPE_SHORT: - case FIELD_TYPE_TIMESTAMP: - case FIELD_TYPE_LONGLONG: - case FIELD_TYPE_INT24: - case FIELD_TYPE_DATE: - case FIELD_TYPE_TIME: - case FIELD_TYPE_DATETIME: - case FIELD_TYPE_YEAR: - case FIELD_TYPE_NEWDATE: - case FIELD_TYPE_ENUM: - case FIELD_TYPE_SET: - recGetLONG((dsmText_t *)prow, i, 0, &longValue, &unknown); - if (unknown) - { - (*field)->set_null(); - } - else - { - (*field)->set_notnull(); - (*field)->store((longlong)longValue); - } - break; - - case FIELD_TYPE_DECIMAL: - case FIELD_TYPE_DOUBLE: - case FIELD_TYPE_TINY_BLOB: - case FIELD_TYPE_MEDIUM_BLOB: - case FIELD_TYPE_LONG_BLOB: - case FIELD_TYPE_BLOB: - case FIELD_TYPE_VAR_STRING: - break; - - case FIELD_TYPE_STRING: - svcByteString_t stringFld; - - fld = (char *)my_malloc((*field)->field_length, MYF(MY_WME)); - stringFld.pbyte = (TEXT *)fld; - stringFld.size = (*field)->field_length; - recGetBYTES((dsmText_t *)prow, i, 0, &stringFld, &unknown); - if (unknown) - { - (*field)->set_null(); - } - else - { - (*field)->set_notnull(); - (*field)->store(fld, (*field)->field_length); - } - my_free(fld, MYF(MY_ALLOW_ZERO_PTR)); - break; - - default: - break; - } - } - } - else - { - memcpy(record,(char*) prow,table->reclength); - } - } - else - { - /* Copy null bits */ - const char *ptr= (const char*) prow; - memcpy(record, ptr, table->null_bytes); - ptr+=table->null_bytes; - - for (Field **field=table->field ; *field ; field++) - { -#ifdef GEMINI_TINYBLOB_IN_ROW - /* Tiny blobs (255 bytes or less) are stored in the row; larger - ** blobs are stored in a separate storage object (see ha_gemini::create). - */ - if ((*field)->type() == FIELD_TYPE_BLOB && - ((Field_blob*)*field)->blobtype() != FIELD_TYPE_TINY_BLOB) -#else - if ((*field)->type() == FIELD_TYPE_BLOB) -#endif - { - dsmBlob_t gemBlob; - - gemBlob.areaType = DSMOBJECT_BLOB; - gemBlob.blobObjNo = tableNumber; - gemBlob.blobId = (dsmBlobId_t)(((Field_blob*)*field)->get_id(ptr)); - if (gemBlob.blobId) - { - gemBlob.totLength = - gemBlob.segLength = ((Field_blob*)*field)->get_length(ptr); - /* Allocate memory to store the blob. This memory is freed - ** the next time unpack_row is called for this table. - */ - gemBlob.pBuffer = (dsmBuffer_t *)my_malloc(gemBlob.totLength, - MYF(0)); - if (!gemBlob.pBuffer) - { - return HA_ERR_OUT_OF_MEM; - } - gemBlob.blobContext.blobOffset = 0; - dsmBlobStart(pcontext, &gemBlob); - dsmBlobGet(pcontext, &gemBlob, NULL); - dsmBlobEnd(pcontext, &gemBlob); - } - else - { - gemBlob.pBuffer = 0; - } - ptr = ((Field_blob*)*field)->unpack_id(record + (*field)->offset(), - ptr, (char *)gemBlob.pBuffer); - pBlobDesc->blobId = gemBlob.blobId; - my_free((char*)pBlobDesc->pBlob, MYF(MY_ALLOW_ZERO_PTR)); - pBlobDesc->pBlob = gemBlob.pBuffer; - pBlobDesc++; - } - else - { - ptr= (*field)->unpack(record + (*field)->offset(), ptr); - } - } - } - - return 0; -} - -int ha_gemini::key_cmp(uint keynr, const byte * old_row, - const byte * new_row, bool updateStats) -{ - KEY_PART_INFO *key_part=table->key_info[keynr].key_part; - KEY_PART_INFO *end=key_part+table->key_info[keynr].key_parts; - - for ( uint i = 0 ; key_part != end ; key_part++, i++) - { - if (key_part->null_bit) - { - if ((old_row[key_part->null_offset] & key_part->null_bit) != - (new_row[key_part->null_offset] & key_part->null_bit)) - { - if(updateStats) - table->key_info[keynr].rec_per_key[i]++; - return 1; - } - else if((old_row[key_part->null_offset] & key_part->null_bit) && - (new_row[key_part->null_offset] & key_part->null_bit)) - /* Both are null */ - continue; - } - if (key_part->key_part_flag & (HA_BLOB_PART | HA_VAR_LENGTH)) - { - if (key_part->field->cmp_binary((char*)(old_row + key_part->offset), - (char*)(new_row + key_part->offset), - (ulong) key_part->length)) - { - if(updateStats) - table->key_info[keynr].rec_per_key[i]++; - return 1; - } - } - else - { - if (memcmp(old_row+key_part->offset, new_row+key_part->offset, - key_part->length)) - { - /* Check for special case of -0 which causes table check - to find an invalid key when comparing the the index - value of 0 to the -0 stored in the row */ - if(key_part->field->type() == FIELD_TYPE_DECIMAL) - { - double fieldValue; - char *ptr = key_part->field->ptr; - - key_part->field->ptr = (char *)old_row + key_part->offset; - fieldValue = key_part->field->val_real(); - if(fieldValue == 0) - { - key_part->field->ptr = (char *)new_row + key_part->offset; - fieldValue = key_part->field->val_real(); - if(fieldValue == 0) - { - key_part->field->ptr = ptr; - continue; - } - } - key_part->field->ptr = ptr; - } - if(updateStats) - { - table->key_info[keynr].rec_per_key[i]++; - } - return 1; - } - } - } - return 0; -} - -int gemini_parse_table_name(const char *fullname, char *dbname, char *tabname) -{ - char *namestart; - char *nameend; - - /* separate out the name of the table and the database - */ - namestart = (char *)strchr(fullname + start_of_name, '/'); - if (!namestart) - { - /* if on Windows, slashes go the other way */ - namestart = (char *)strchr(fullname + start_of_name, '\\'); - } - nameend = (char *)strchr(fullname + start_of_name, '.'); - /* sometimes fullname has an extension, sometimes it doesn't */ - if (!nameend) - { - nameend = (char *)fullname + strlen(fullname); - } - strncpy(dbname, fullname + start_of_name, - (namestart - fullname) - start_of_name); - dbname[(namestart - fullname) - start_of_name] = '\0'; - strncpy(tabname, namestart + 1, (nameend - namestart) - 1); - tabname[nameend - namestart - 1] = '\0'; - - return 0; -} - -/* PROGRAM: gemini_is_vst - if the name is the name of a VST, return - * its number - * - * RETURNS: Table number if a match is found - * 0 if not a VST - */ -int -gemini_is_vst(const char *pname) /* IN the name */ -{ - int tablenum = 0; - - for (int i = 0; i < vstnumfils; i++) - { - if (strcmp(pname, vstfil[i].filename) == 0) - { - tablenum = vstfil[i].filnum; - break; - } - } - - return tablenum; -} - -static void print_msg(THD *thd, const char *table_name, const char *op_name, - const char *msg_type, const char *fmt, ...) -{ - String* packet = &thd->packet; - packet->length(0); - char msgbuf[256]; - msgbuf[0] = 0; - va_list args; - va_start(args,fmt); - - my_vsnprintf(msgbuf, sizeof(msgbuf), fmt, args); - msgbuf[sizeof(msgbuf) - 1] = 0; // healthy paranoia - - DBUG_PRINT(msg_type,("message: %s",msgbuf)); - - net_store_data(packet, table_name); - net_store_data(packet, op_name); - net_store_data(packet, msg_type); - net_store_data(packet, msgbuf); - if (my_net_write(&thd->net, (char*)thd->packet.ptr(), - thd->packet.length())) - thd->killed=1; -} - -/* Load shared area with rows per key statistics */ -void -ha_gemini::get_index_stats(THD *thd) -{ - dsmStatus_t rc = 0; - ha_rows *rec_per_key = share->rec_per_key; - - for(uint i = 0; i < table->keys && !rc; i++) - { - for (uint j = 0; j < table->key_info[i].key_parts && !rc;j++) - { - LONG64 rows_per_key; - rc = dsmIndexStatsGet((dsmContext_t *)thd->gemini.context, - tableNumber, pindexNumbers[i],(int)j, - &rows_per_key); - if (rc) - { - gemini_msg((dsmContext_t *)thd->gemini.context, - "Index Statistics faild for table %d index %d, error %d", - tableNumber, pindexNumbers[i], rc); - } - *rec_per_key = (ha_rows)rows_per_key; - rec_per_key++; - } - } - return; -} - -/**************************************************************************** - Handling the shared GEM_SHARE structure that is needed to provide - a global in memory storage location of the rec_per_key stats used - by the optimizer. -****************************************************************************/ - -static byte* gem_get_key(GEM_SHARE *share,uint *length, - my_bool not_used __attribute__((unused))) -{ - *length=share->table_name_length; - return (byte*) share->table_name; -} - -static GEM_SHARE *get_share(const char *table_name, TABLE *table) -{ - GEM_SHARE *share; - - pthread_mutex_lock(&gem_mutex); - uint length=(uint) strlen(table_name); - if (!(share=(GEM_SHARE*) hash_search(&gem_open_tables, (byte*) table_name, - length))) - { - ha_rows *rec_per_key; - char *tmp_name; - - if ((share=(GEM_SHARE *) - my_multi_malloc(MYF(MY_WME | MY_ZEROFILL), - &share, sizeof(*share), - &rec_per_key, table->key_parts * sizeof(ha_rows), - &tmp_name, length+1, - NullS))) - { - share->rec_per_key = rec_per_key; - share->table_name = tmp_name; - share->table_name_length=length; - strcpy(share->table_name,table_name); - if (hash_insert(&gem_open_tables, (byte*) share)) - { - pthread_mutex_unlock(&gem_mutex); - my_free((gptr) share,0); - return 0; - } - thr_lock_init(&share->lock); - pthread_mutex_init(&share->mutex,NULL); - } - } - pthread_mutex_unlock(&gem_mutex); - return share; -} - -static int free_share(GEM_SHARE *share, bool mutex_is_locked) -{ - pthread_mutex_lock(&gem_mutex); - if (mutex_is_locked) - pthread_mutex_unlock(&share->mutex); - if (!--share->use_count) - { - hash_delete(&gem_open_tables, (byte*) share); - thr_lock_delete(&share->lock); - pthread_mutex_destroy(&share->mutex); - my_free((gptr) share, MYF(0)); - } - pthread_mutex_unlock(&gem_mutex); - return 0; -} - -static void gemini_lock_table_overflow_error(dsmContext_t *pcontext) -{ - gemini_msg(pcontext, "The total number of locks exceeds the lock table size"); - gemini_msg(pcontext, "Either increase gemini_lock_table_size or use a"); - gemini_msg(pcontext, "different transaction isolation level"); -} - -#endif /* HAVE_GEMINI_DB */ diff --git a/sql/ha_gemini.h b/sql/ha_gemini.h deleted file mode 100644 index 96c0cdd4241..00000000000 --- a/sql/ha_gemini.h +++ /dev/null @@ -1,208 +0,0 @@ -/* Copyright (C) 2000 MySQL AB & NuSphere Corporation - - 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 */ - - -#ifdef __GNUC__ -#pragma interface /* gcc class implementation */ -#endif - -#include "gem_global.h" -#include "dstd.h" -#include "dsmpub.h" - -/* class for the the gemini handler */ - -enum enum_key_string_options{KEY_CREATE,KEY_DELETE,KEY_CHECK}; -typedef struct st_gemini_share { - ha_rows *rec_per_key; - THR_LOCK lock; - pthread_mutex_t mutex; - char *table_name; - uint table_name_length,use_count; -} GEM_SHARE; - -typedef struct gemBlobDesc -{ - dsmBlobId_t blobId; - dsmBuffer_t *pBlob; -} gemBlobDesc_t; - -class ha_gemini: public handler -{ - /* define file as an int for now until we have a real file struct */ - int file; - uint int_option_flag; - int tableNumber; - dsmIndex_t *pindexNumbers; // dsm object numbers for the indexes on this table - dsmRecid_t lastRowid; - uint last_dup_key; - bool fixed_length_row, key_read, using_ignore; - byte *rec_buff; - dsmKey_t *pbracketBase; - dsmKey_t *pbracketLimit; - dsmKey_t *pfoundKey; - dsmMask_t tableStatus; // Crashed/repair status - gemBlobDesc_t *pBlobDescs; - - int index_open(char *tableName); - int pack_row(byte **prow, int *ppackedLength, const byte *record, - bool update); - int unpack_row(char *record, char *prow); - int findRow(THD *thd, dsmMask_t findMode, byte *buf); - int fetch_row(void *gemini_context, const byte *buf); - int handleIndexEntries(const byte * record, dsmRecid_t recid, - enum_key_string_options option); - - int handleIndexEntry(const byte * record, dsmRecid_t recid, - enum_key_string_options option,uint keynr); - - int createKeyString(const byte * record, KEY *pkeyinfo, - unsigned char *pkeyBuf, int bufSize, - int *pkeyStringLen, short geminiIndexNumber, - bool *thereIsAnull); - int fullCheck(THD *thd,byte *buf); - - int pack_key( uint keynr, dsmKey_t *pkey, - const byte *key_ptr, uint key_length); - - void unpack_key(char *record, dsmKey_t *key, uint index); - - int key_cmp(uint keynr, const byte * old_row, - const byte * new_row, bool updateStats); - - int saveKeyStats(THD *thd); - void get_index_stats(THD *thd); - - short cursorId; /* cursorId of active index cursor if any */ - dsmMask_t lockMode; /* Shared or exclusive */ - - /* FIXFIX Don't know why we need this because I don't know what - store_lock method does but we core dump without this */ - THR_LOCK_DATA lock; - GEM_SHARE *share; - - public: - ha_gemini(TABLE *table): handler(table), file(0), - int_option_flag(HA_READ_NEXT | HA_READ_PREV | - HA_REC_NOT_IN_SEQ | - HA_KEYPOS_TO_RNDPOS | HA_READ_ORDER | HA_LASTKEY_ORDER | - HA_LONGLONG_KEYS | HA_NULL_KEY | HA_HAVE_KEY_READ_ONLY | - HA_BLOB_KEY | - HA_NO_TEMP_TABLES | HA_NO_FULLTEXT_KEY | - /*HA_NOT_EXACT_COUNT | */ - /*HA_KEY_READ_WRONG_STR |*/ HA_DROP_BEFORE_CREATE), - pbracketBase(0),pbracketLimit(0),pfoundKey(0), - cursorId(0) - { - } - ~ha_gemini() {} - const char *table_type() const { return "Gemini"; } - const char **bas_ext() const; - ulong option_flag() const { return int_option_flag; } - uint max_record_length() const { return MAXRECSZ; } - uint max_keys() const { return MAX_KEY-1; } - uint max_key_parts() const { return MAX_REF_PARTS; } - uint max_key_length() const { return MAXKEYSZ / 2; } - bool fast_key_read() { return 1;} - bool has_transactions() { return 1;} - - int open(const char *name, int mode, uint test_if_locked); - int close(void); - double scan_time(); - int write_row(byte * buf); - int update_row(const byte * old_data, byte * new_data); - int delete_row(const byte * buf); - int index_init(uint index); - int index_end(); - int index_read(byte * buf, const byte * key, - uint key_len, enum ha_rkey_function find_flag); - int index_read_idx(byte * buf, uint index, const byte * key, - uint key_len, enum ha_rkey_function find_flag); - int index_next(byte * buf); - int index_next_same(byte * buf, const byte *key, uint keylen); - int index_prev(byte * buf); - int index_first(byte * buf); - int index_last(byte * buf); - int rnd_init(bool scan=1); - int rnd_end(); - int rnd_next(byte *buf); - int rnd_pos(byte * buf, byte *pos); - void position(const byte *record); - void info(uint); - int extra(enum ha_extra_function operation); - int reset(void); - int analyze(THD* thd, HA_CHECK_OPT* check_opt); - int check(THD* thd, HA_CHECK_OPT* check_opt); - int repair(THD* thd, HA_CHECK_OPT* check_opt); - int restore(THD* thd, HA_CHECK_OPT* check_opt); - int backup(THD* thd, HA_CHECK_OPT* check_opt); - int optimize(THD* thd, HA_CHECK_OPT* check_opt); - int external_lock(THD *thd, int lock_type); - virtual longlong get_auto_increment(); - void position(byte *record); - ha_rows records_in_range(int inx, - const byte *start_key,uint start_key_len, - enum ha_rkey_function start_search_flag, - const byte *end_key,uint end_key_len, - enum ha_rkey_function end_search_flag); - void update_create_info(HA_CREATE_INFO *create_info); - int create(const char *name, register TABLE *form, - HA_CREATE_INFO *create_info); - int delete_table(const char *name); - int rename_table(const char* from, const char* to); - THR_LOCK_DATA **store_lock(THD *thd, THR_LOCK_DATA **to, - enum thr_lock_type lock_type); -}; - -#define GEMOPT_FLUSH_LOG 0x00000001 -#define GEMOPT_UNBUFFERED_IO 0x00000002 - -#define GEMINI_RECOVERY_FULL 0x00000001 -#define GEMINI_RECOVERY_NONE 0x00000002 -#define GEMINI_RECOVERY_FORCE 0x00000004 - -#define GEM_OPTID_SPIN_RETRIES 1 - -extern bool gemini_skip; -extern SHOW_COMP_OPTION have_gemini; -extern long gemini_options; -extern long gemini_buffer_cache; -extern long gemini_io_threads; -extern long gemini_log_cluster_size; -extern long gemini_locktablesize; -extern long gemini_lock_wait_timeout; -extern long gemini_spin_retries; -extern long gemini_connection_limit; -extern char *gemini_basedir; -extern TYPELIB gemini_recovery_typelib; -extern ulong gemini_recovery_options; - -bool gemini_init(void); -bool gemini_end(void); -bool gemini_flush_logs(void); -int gemini_commit(THD *thd); -int gemini_rollback(THD *thd); -int gemini_recovery_logging(THD *thd, bool on); -void gemini_disconnect(THD *thd); -int gemini_rollback_to_savepoint(THD *thd); -int gemini_parse_table_name(const char *fullname, char *dbname, char *tabname); -int gemini_is_vst(const char *pname); -int gemini_set_option_long(int optid, long optval); - -const int gemini_blocksize = BLKSIZE; -const int gemini_recbits = DEFAULT_RECBITS; - -extern "C" void uttrace(void); diff --git a/sql/ha_hash.h b/sql/ha_hash.h deleted file mode 100644 index 80416611406..00000000000 --- a/sql/ha_hash.h +++ /dev/null @@ -1,31 +0,0 @@ - -int ha_hash::create(my_string name, register TABLE *form, - ulonglong auto_increment_value) -{ - register uint i,j; - char buff[FN_REFLEN]; - KEY *pos; - H_KEYDEF keydef[MAX_KEY]; - DBUG_ENTER("cre_hash"); - - pos=form->key_info; - for (i=0; i < form->keys ; i++, pos++) - { - keydef[i].hk_flag= pos->flags & HA_NOSAME; - for (j=0 ; (int7) j < pos->key_parts ; j++) - { - uint flag=pos->key_part[j].key_type; - if (!f_is_packed(flag) && f_packtype(flag) == (int) FIELD_TYPE_DECIMAL && - !(flag & FIELDFLAG_BINARY)) - keydef[i].hk_keyseg[j].key_type= (int) HA_KEYTYPE_TEXT; - else - keydef[i].hk_keyseg[j].key_type= (int) HA_KEYTYPE_BINARY; - keydef[i].hk_keyseg[j].start= pos->key_part[j].offset; - keydef[i].hk_keyseg[j].length= pos->key_part[j].length; - } - keydef[i].hk_keyseg[j].key_type= 0; - } - DBUG_RETURN(h_create(fn_format(buff,name,"","",2+4+16),i, - keydef,form->reclength,form->max_rows,form->min_rows, - 0)); -} /* cre_hash */ diff --git a/sql/ha_heap.cc b/sql/ha_heap.cc index 13dccc2bf64..5f482bca1e8 100644 --- a/sql/ha_heap.cc +++ b/sql/ha_heap.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 */ @@ -33,7 +33,7 @@ const char **ha_heap::bas_ext() const int ha_heap::open(const char *name, int mode, uint test_if_locked) { - uint key,part,parts,mem_per_row=0; + uint key,parts,mem_per_row=0; ulong max_rows; HP_KEYDEF *keydef; HP_KEYSEG *seg; @@ -48,24 +48,38 @@ int ha_heap::open(const char *name, int mode, uint test_if_locked) for (key=0 ; key < table->keys ; key++) { KEY *pos=table->key_info+key; + KEY_PART_INFO *key_part= pos->key_part; + KEY_PART_INFO *key_part_end= key_part+pos->key_parts; + mem_per_row += (pos->key_length + (sizeof(char*) * 2)); - + keydef[key].keysegs=(uint) pos->key_parts; - keydef[key].flag = (pos->flags & HA_NOSAME); + keydef[key].flag = (pos->flags & (HA_NOSAME | HA_NULL_ARE_EQUAL)); keydef[key].seg=seg; - - for (part=0 ; part < pos->key_parts ; part++) + + for (; key_part != key_part_end ; key_part++, seg++) { - uint flag=pos->key_part[part].key_type; + uint flag=key_part->key_type; + Field *field=key_part->field; if (!f_is_packed(flag) && f_packtype(flag) == (int) FIELD_TYPE_DECIMAL && !(flag & FIELDFLAG_BINARY)) seg->type= (int) HA_KEYTYPE_TEXT; else seg->type= (int) HA_KEYTYPE_BINARY; - seg->start=(uint) pos->key_part[part].offset; - seg->length=(uint) pos->key_part[part].length; - seg++; + seg->start=(uint) key_part->offset; + seg->length=(uint) key_part->length; + if (field->null_ptr) + { + seg->null_bit=field->null_bit; + seg->null_pos= (uint) (field->null_ptr- + (uchar*) table->record[0]); + } + else + { + seg->null_bit=0; + seg->null_pos=0; + } } } mem_per_row += MY_ALIGN(table->reclength+1, sizeof(char*)); @@ -77,7 +91,8 @@ int ha_heap::open(const char *name, int mode, uint test_if_locked) table->max_rows : max_rows), table->min_rows); my_free((gptr) keydef,MYF(0)); - info(HA_STATUS_NO_LOCK | HA_STATUS_CONST | HA_STATUS_VARIABLE); + if (file) + info(HA_STATUS_NO_LOCK | HA_STATUS_CONST | HA_STATUS_VARIABLE); ref_length=sizeof(HEAP_PTR); return (!file ? errno : 0); } @@ -147,7 +162,7 @@ int ha_heap::index_prev(byte * buf) table->status=error ? STATUS_NOT_FOUND: 0; return error; } - + int ha_heap::index_first(byte * buf) { statistic_increment(ha_read_first_count,&LOCK_status); @@ -227,7 +242,7 @@ int ha_heap::delete_all_rows() int ha_heap::external_lock(THD *thd, int lock_type) { return 0; // No external locking -} +} THR_LOCK_DATA **ha_heap::store_lock(THD *thd, THR_LOCK_DATA **to, @@ -247,7 +262,7 @@ THR_LOCK_DATA **ha_heap::store_lock(THD *thd, int ha_heap::delete_table(const char *name) { - int error=heap_delete_all(name); + int error=heap_delete_table(name); return error == ENOENT ? 0 : error; } @@ -272,7 +287,6 @@ ha_rows ha_heap::records_in_range(int inx, return 10; // Good guess } -/* We can just delete the heap on creation */ int ha_heap::create(const char *name, TABLE *form, HA_CREATE_INFO *create_info) diff --git a/sql/ha_heap.h b/sql/ha_heap.h index 6b7e9c6c626..aa675cfffea 100644 --- a/sql/ha_heap.h +++ b/sql/ha_heap.h @@ -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 */ @@ -31,11 +31,24 @@ class ha_heap: public handler ha_heap(TABLE *table): handler(table), file(0) {} ~ha_heap() {} const char *table_type() const { return "HEAP"; } + const char *index_type(uint inx) + { + return ((table->key_info[inx].algorithm == HA_KEY_ALG_BTREE) ? "BTREE" : + "HASH"); + } const char **bas_ext() const; - ulong option_flag() const - { return (HA_READ_RND_SAME | HA_NO_INDEX | HA_ONLY_WHOLE_INDEX | - HA_WRONG_ASCII_ORDER | HA_KEYPOS_TO_RNDPOS | HA_NO_BLOBS | - HA_REC_NOT_IN_SEQ | HA_NO_FULLTEXT_KEY); } + ulong table_flags() const + { + return (HA_READ_RND_SAME | HA_NO_INDEX | HA_KEYPOS_TO_RNDPOS | + HA_NO_BLOBS | HA_NULL_KEY | HA_REC_NOT_IN_SEQ | + HA_NOT_READ_PREFIX_LAST | HA_NO_AUTO_INCREMENT); + } + ulong index_flags(uint inx) const + { + return ((table->key_info[inx].algorithm == HA_KEY_ALG_BTREE) ? + (HA_READ_NEXT | HA_READ_PREV | HA_READ_ORDER) : + (HA_ONLY_WHOLE_INDEX | HA_WRONG_ASCII_ORDER)); + } uint max_record_length() const { return HA_MAX_REC_LENGTH; } uint max_keys() const { return MAX_KEY; } uint max_key_parts() const { return MAX_REF_PARTS; } diff --git a/sql/ha_innobase.cc b/sql/ha_innodb.cc index a741ffefbb5..c85d74ce432 100644 --- a/sql/ha_innobase.cc +++ b/sql/ha_innodb.cc @@ -1,4 +1,4 @@ -/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & InnoDB Oy +/* Copyright (C) 2000 MySQL AB & InnoDB Oy 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 @@ -14,14 +14,14 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -/* This file defines the InnoDB handler: the interface between MySQL and -InnoDB */ - +/* + This file defines the InnoDB handler: the interface between MySQL and InnoDB */ + /* TODO list for the InnoDB handler: - Ask Monty if strings of different languages can exist in the same database. Answer: in near future yes, but not yet. */ - + #ifdef __GNUC__ #pragma implementation // gcc: Class implementation #endif @@ -35,7 +35,7 @@ InnoDB */ #define MAX_ULONG_BIT ((ulong) 1 << (sizeof(ulong)*8-1)) -#include "ha_innobase.h" +#include "ha_innodb.h" /* We must declare this here because we undef SAFE_MUTEX below */ pthread_mutex_t innobase_mutex; @@ -87,11 +87,8 @@ long innobase_mirrored_log_groups, innobase_log_files_in_group, /* The default values for the following char* start-up parameters are determined in innobase_init below: */ - -/* innobase_data_file_path=ibdata:15,idata2:1,... */ - + char* innobase_data_home_dir = NULL; -char* innobase_data_file_path = NULL; char* innobase_log_group_home_dir = NULL; char* innobase_log_arch_dir = NULL; char* innobase_unix_file_flush_method = NULL; @@ -104,6 +101,17 @@ my_bool innobase_log_archive = FALSE; my_bool innobase_use_native_aio = FALSE; my_bool innobase_fast_shutdown = TRUE; +/* + Set default InnoDB data file size to 10 MB and let it be + auto-extending. Thus users can use InnoDB without having to + specify any startup options. +*/ + +/* innobase_data_file_path=ibdata:15,idata2:1,... */ + +char *innobase_data_file_path= (char*) "ibdata1:10M:autoextend"; +static char *internal_innobase_data_file_path=0; + /* The following counter is used to convey information to InnoDB about server activity: in selects it is not sensible to call srv_active_wake_master_thread after each fetch or search, we only do @@ -234,7 +242,7 @@ convert_error_code_to_mysql( return(HA_ERR_TO_BIG_ROW); } else { - dbug_assert(0); + DBUG_ASSERT(0); return(-1); // Unknown error } @@ -274,7 +282,7 @@ innobase_mysql_print_thd( } if (thd->query) { - printf("\n%.100s", thd->query); + printf("\n%-.100s", thd->query); } printf("\n"); @@ -297,7 +305,7 @@ check_trx_exists( trx = (trx_t*) thd->transaction.all.innobase_tid; if (trx == NULL) { - dbug_assert(thd != NULL); + DBUG_ASSERT(thd != NULL); trx = trx_allocate_for_mysql(); trx->mysql_thd = thd; @@ -353,12 +361,27 @@ innobase_init(void) /*===============*/ /* out: TRUE if error */ { - static char current_dir[3]; int err; bool ret; - + char current_lib[3], *default_path; + DBUG_ENTER("innobase_init"); - + + /* + When using the embedded server, the datadirectory is not + in the current directory. + */ + if (mysql_embedded) + default_path=mysql_real_data_home; + else + { + /* It's better to use current lib, to keep path's short */ + current_lib[0] = FN_CURLIB; + current_lib[1] = FN_LIBCHAR; + current_lib[2] = 0; + default_path=current_lib; + } + if (specialflag & SPECIAL_NO_PRIOR) { srv_set_thread_priorities = FALSE; } else { @@ -366,37 +389,22 @@ innobase_init(void) srv_query_thread_priority = QUERY_PRIOR; } - /* Use current_dir if no paths are set */ - current_dir[0] = FN_CURLIB; - current_dir[1] = FN_LIBCHAR; - current_dir[2] = 0; - - /* Set InnoDB initialization parameters according to the values - read from MySQL .cnf file */ + /* + Set InnoDB initialization parameters according to the values + read from MySQL .cnf file + */ - if (!innobase_data_file_path) { - fprintf(stderr, - "Cannot initialize InnoDB as 'innodb_data_file_path' is not set.\n" - "If you do not want to use transactional InnoDB tables, add a line\n" - "skip-innodb\n" - "to the [mysqld] section of init parameters in your my.cnf\n" - "or my.ini. If you want to use InnoDB tables, add for example,\n" - "innodb_data_file_path = ibdata1:30M:autoextend\n" - "But to get good performance you should adjust for your hardware\n" - "the InnoDB startup options listed in section 2 at\n" - "http://www.innodb.com/ibman.html\n"); - - innodb_skip=1; - DBUG_RETURN(FALSE); /* Continue without InnoDB */ - } + // Make a copy of innobase_data_file_path to not modify the original + internal_innobase_data_file_path=my_strdup(innobase_data_file_path, + MYF(MY_WME)); srv_data_home = (innobase_data_home_dir ? innobase_data_home_dir : - current_dir); + default_path); srv_arch_dir = (innobase_log_arch_dir ? innobase_log_arch_dir : - current_dir); + default_path); ret = (bool) - srv_parse_data_file_paths_and_sizes(innobase_data_file_path, + srv_parse_data_file_paths_and_sizes(internal_innobase_data_file_path, &srv_data_file_names, &srv_data_file_sizes, &srv_data_file_is_raw_partition, @@ -404,14 +412,12 @@ innobase_init(void) &srv_auto_extend_last_data_file, &srv_last_file_size_max); if (ret == FALSE) { - fprintf(stderr, - "InnoDB: syntax error in innodb_data_file_path\n"); - DBUG_RETURN(TRUE); + sql_print_error("InnoDB: syntax error in innodb_data_file_path"); + DBUG_RETURN(TRUE); } - if (!innobase_log_group_home_dir) { - innobase_log_group_home_dir = current_dir; - } + if (!innobase_log_group_home_dir) + innobase_log_group_home_dir= default_path; ret = (bool) srv_parse_log_group_home_dirs(innobase_log_group_home_dir, @@ -424,7 +430,6 @@ innobase_init(void) DBUG_RETURN(TRUE); } - srv_unix_file_flush_method_str = (innobase_unix_file_flush_method ? innobase_unix_file_flush_method : (char*)"fdatasync"); @@ -450,12 +455,13 @@ innobase_init(void) srv_fast_shutdown = (ibool) innobase_fast_shutdown; + srv_print_verbose_log = mysql_embedded ? 0 : 1; if (strcmp(default_charset_info->name, "latin1") == 0) { /* Store the character ordering table to InnoDB. For non-latin1 charsets we use the MySQL comparison functions, and consequently we do not need to know the ordering internally in InnoDB. */ - + memcpy(srv_latin1_ordering, default_charset_info->sort_order, 256); } @@ -487,6 +493,7 @@ innobase_end(void) err = innobase_shutdown_for_mysql(); hash_free(&innobase_open_tables); + my_free(internal_innobase_data_file_path,MYF(MY_ALLOW_ZERO_PTR)); if (err != DB_SUCCESS) { @@ -553,7 +560,6 @@ innobase_commit( /* Release possible statement level resources */ innobase_release_stat_resources(trx); - trx_mark_sql_stat_end(trx); #ifndef DBUG_OFF @@ -714,7 +720,7 @@ normalize_table_name( name_ptr = ptr + 1; - dbug_assert(ptr > name); + DBUG_ASSERT(ptr > name); ptr--; @@ -741,7 +747,7 @@ normalize_table_name( } /********************************************************************* -Creates and opens a handle to a table which already exists in an Innnobase +Creates and opens a handle to a table which already exists in an Innobase database. */ int @@ -793,13 +799,13 @@ ha_innobase::open( if (NULL == ib_table) { - fprintf(stderr, -"InnoDB: Error: cannot find table %s from the internal data dictionary\n" -"InnoDB: of InnoDB though the .frm file for the table exists. Maybe you\n" -"InnoDB: have deleted and recreated InnoDB data files but have forgotten\n" -"InnoDB: to delete the corresponding .frm files of InnoDB tables, or you\n" -"InnoDB: have moved .frm files to another database?\n", - norm_name); + sql_print_error("InnoDB error:\n\ +Cannot find table %s from the internal data dictionary\n\ +of InnoDB though the .frm file for the table exists. Maybe you\n\ +have deleted and recreated InnoDB data files but have forgotten\n\ +to delete the corresponding .frm files of InnoDB tables, or you\n\ +have moved .frm files to another database?", + norm_name); free_share(share); my_free((char*) upd_buff, MYF(0)); @@ -827,20 +833,22 @@ ha_innobase::open( primary_key = 0; key_used_on_scan = 0; - /* MySQL allocates the buffer for ref */ + /* + MySQL allocates the buffer for ref. + This includes all keys + one byte for each column + that may be NULL. + The ref_length must be exact as possible as + all reference buffers are allocated based on this. + */ - ref_length = table->key_info->key_length - + table->key_info->key_parts + 10; - - /* One byte per key field is consumed to the SQL NULL - info of the field; we add also 10 bytes of safety margin */ + ref_length = table->key_info->key_length; } else { ((row_prebuilt_t*)innobase_prebuilt) ->clust_index_was_generated = TRUE; - ref_length = DATA_ROW_ID_LEN + 10; - - dbug_assert(key_used_on_scan == MAX_KEY); + ref_length = DATA_ROW_ID_LEN; + + DBUG_ASSERT(key_used_on_scan == MAX_KEY); } auto_inc_counter_for_this_stat = 0; @@ -984,8 +992,8 @@ innobase_mysql_cmp( enum_field_types mysql_tp; int ret; - dbug_assert(a_length != UNIV_SQL_NULL); - dbug_assert(b_length != UNIV_SQL_NULL); + DBUG_ASSERT(a_length != UNIV_SQL_NULL); + DBUG_ASSERT(b_length != UNIV_SQL_NULL); mysql_tp = (enum_field_types) mysql_type; @@ -1023,11 +1031,11 @@ get_innobase_type_from_mysql_type( 8 bits: this is used in ibuf and also when DATA_NOT_NULL is ORed to the type */ - dbug_assert((ulint)FIELD_TYPE_STRING < 256); - dbug_assert((ulint)FIELD_TYPE_VAR_STRING < 256); - dbug_assert((ulint)FIELD_TYPE_DOUBLE < 256); - dbug_assert((ulint)FIELD_TYPE_FLOAT < 256); - dbug_assert((ulint)FIELD_TYPE_DECIMAL < 256); + DBUG_ASSERT((ulint)FIELD_TYPE_STRING < 256); + DBUG_ASSERT((ulint)FIELD_TYPE_VAR_STRING < 256); + DBUG_ASSERT((ulint)FIELD_TYPE_DOUBLE < 256); + DBUG_ASSERT((ulint)FIELD_TYPE_FLOAT < 256); + DBUG_ASSERT((ulint)FIELD_TYPE_DECIMAL < 256); switch (field->type()) { case FIELD_TYPE_VAR_STRING: if (field->flags & BINARY_FLAG) { @@ -1119,7 +1127,12 @@ ha_innobase::store_key_val_for_row( buff += key_part->length; } - DBUG_RETURN(buff - buff_start); + /* + We have to zero-fill the buffer to be able to compare two + keys to see if they are equal + */ + bzero(buff, (ref_length- (uint) (buff - buff_start))); + return ref_length; } /****************************************************************** @@ -1154,7 +1167,7 @@ build_template( if (prebuilt->read_just_key) { /* MySQL has instructed us that it is enough to fetch the columns in the key */ - + fetch_all_in_key = TRUE; } else { /* We are building a temporary table: fetch all @@ -1163,7 +1176,7 @@ build_template( we use below to detect required columns does not reveal that. Actually, it might be enough to fetch only all in the key also in this case! */ - + templ_type = ROW_MYSQL_WHOLE_ROW; } } @@ -1294,7 +1307,7 @@ ha_innobase::write_row( row_prebuilt_t* prebuilt = (row_prebuilt_t*)innobase_prebuilt; int error; longlong auto_inc; - + DBUG_ENTER("ha_innobase::write_row"); statistic_increment(ha_write_count, &LOCK_status); @@ -1316,7 +1329,7 @@ ha_innobase::write_row( /* Fetch the value the user possibly has set in the autoincrement field */ - + auto_inc = table->next_number_field->val_int(); /* In replication and also otherwise the auto-inc column @@ -1344,7 +1357,7 @@ ha_innobase::write_row( auto-inc column */ user_thd->next_insert_id = auto_inc; } - + if (auto_inc != 0) { /* This call will calculate the max of the current value and the value supplied by the user, if @@ -1363,11 +1376,11 @@ ha_innobase::write_row( srv_conc_exit_innodb(prebuilt->trx); if (error != DB_SUCCESS) { - + error = convert_error_code_to_mysql(error); goto func_exit; } - + dict_table_autoinc_update(prebuilt->table, auto_inc); } else { srv_conc_enter_innodb(prebuilt->trx); @@ -1377,8 +1390,8 @@ ha_innobase::write_row( error = row_lock_table_autoinc_for_mysql( prebuilt); if (error != DB_SUCCESS) { - srv_conc_exit_innodb(prebuilt->trx); - + srv_conc_exit_innodb(prebuilt->trx); + error = convert_error_code_to_mysql( error); goto func_exit; @@ -1396,7 +1409,7 @@ ha_innobase::write_row( user_thd->next_insert_id = auto_inc; } } - + update_auto_increment(); if (auto_inc == 0) { @@ -1410,15 +1423,15 @@ ha_innobase::write_row( srv_conc_exit_innodb(prebuilt->trx); if (error != DB_SUCCESS) { - + error = convert_error_code_to_mysql(error); goto func_exit; } - + dict_table_autoinc_initialize(prebuilt->table, auto_inc); } - + /* We have to set sql_stat_start to TRUE because update_auto_increment may have called a select, and has reset that flag; row_insert_for_mysql has to @@ -1878,6 +1891,24 @@ ha_innobase::index_read( DBUG_RETURN(error); } + +/* + The following functions works like index_read, but it find the last + row with the current index prefix. + This code is disabled until Heikki has verified that InnoDB support the + HA_READ_PREFIX_LAST flag and removed the HA_NOT_READ_PREFIX_LAST + flag from ha_innodb.h +*/ + +int +ha_innobase::index_read_last(mysql_byte *buf, + const mysql_byte *key_ptr, + uint key_len) +{ + return index_read(buf, key_ptr, key_len, HA_READ_PREFIX_LAST); +} + + /************************************************************************ Changes the active index of a handle. */ @@ -1889,48 +1920,44 @@ ha_innobase::change_active_index( index, even if it was internally generated by InnoDB */ { - row_prebuilt_t* prebuilt = (row_prebuilt_t*) innobase_prebuilt; - KEY* key=0; - - statistic_increment(ha_read_key_count, &LOCK_status); + row_prebuilt_t* prebuilt = (row_prebuilt_t*) innobase_prebuilt; + KEY* key=0; - DBUG_ENTER("index_read_idx"); + statistic_increment(ha_read_key_count, &LOCK_status); + DBUG_ENTER("change_active_index"); - active_index = keynr; + active_index = keynr; - if (keynr != MAX_KEY && table->keys > 0) { - key = table->key_info + active_index; + if (keynr != MAX_KEY && table->keys > 0) { + key = table->key_info + active_index; - prebuilt->index = dict_table_get_index_noninline( - prebuilt->table, key->name); - } else { - prebuilt->index = dict_table_get_first_index_noninline( - prebuilt->table); - } + prebuilt->index = dict_table_get_index_noninline( + prebuilt->table, + key->name); + } else { + prebuilt->index = dict_table_get_first_index_noninline( + prebuilt->table); + } - if (!prebuilt->index) { - fprintf(stderr, - "InnoDB: Could not find key n:o %u with name %s from dict cache\n" - "InnoDB: for table %s\n", keynr, key ? key->name : "NULL", - prebuilt->table->name); + if (!prebuilt->index) { + sql_print_error("Innodb could not find key n:o %u with name %s from dict cache for table %s", keynr, key ? key->name : "NULL", prebuilt->table->name); + DBUG_RETURN(1); + } - DBUG_RETURN(1); - } - - assert(prebuilt->search_tuple); + assert(prebuilt->search_tuple != 0); - dtuple_set_n_fields(prebuilt->search_tuple, prebuilt->index->n_fields); + dtuple_set_n_fields(prebuilt->search_tuple, prebuilt->index->n_fields); - dict_index_copy_types(prebuilt->search_tuple, prebuilt->index, - prebuilt->index->n_fields); + dict_index_copy_types(prebuilt->search_tuple, prebuilt->index, + prebuilt->index->n_fields); - /* Maybe MySQL changes the active index for a handle also - during some queries, we do not know: then it is safest to build - the template such that all columns will be fetched */ + /* Maybe MySQL changes the active index for a handle also + during some queries, we do not know: then it is safest to build + the template such that all columns will be fetched */ - build_template(prebuilt, user_thd, table, ROW_MYSQL_WHOLE_ROW); + build_template(prebuilt, user_thd, table, ROW_MYSQL_WHOLE_ROW); - DBUG_RETURN(0); + DBUG_RETURN(0); } /************************************************************************** @@ -1981,7 +2008,7 @@ ha_innobase::general_fetch( DBUG_ENTER("general_fetch"); srv_conc_enter_innodb(prebuilt->trx); - + ret = row_search_for_mysql((byte*)buf, 0, prebuilt, match_mode, direction); srv_conc_exit_innodb(prebuilt->trx); @@ -2117,7 +2144,7 @@ ha_innobase::rnd_init( bool scan) /* in: ???????? */ { int err; - + row_prebuilt_t* prebuilt = (row_prebuilt_t*) innobase_prebuilt; if (prebuilt->clust_index_was_generated) { @@ -2186,8 +2213,9 @@ ha_innobase::rnd_pos( row_prebuilt_t* prebuilt = (row_prebuilt_t*) innobase_prebuilt; int error; uint keynr = active_index; - DBUG_ENTER("rnd_pos"); + DBUG_DUMP("key", (char*) pos, ref_stored_len); + statistic_increment(ha_read_rnd_count, &LOCK_status); if (prebuilt->clust_index_was_generated) { @@ -2202,11 +2230,15 @@ ha_innobase::rnd_pos( } if (error) { + DBUG_PRINT("error",("Got error: %ld",error)); DBUG_RETURN(error); } - - error = index_read(buf, pos, ref_stored_len, HA_READ_KEY_EXACT); + error = index_read(buf, pos, ref_stored_len, HA_READ_KEY_EXACT); + if (error) + { + DBUG_PRINT("error",("Got error: %ld",error)); + } change_active_index(keynr); DBUG_RETURN(error); @@ -2241,11 +2273,11 @@ ha_innobase::position( len = store_key_val_for_row(primary_key, (char*) ref, record); } - dbug_assert(len <= ref_length); - + DBUG_ASSERT(len == ref_length); ref_stored_len = len; } + /********************************************************************* Creates a table definition to an InnoDB database. */ static @@ -2333,7 +2365,8 @@ create_index( ind_type = 0; - if (strcmp(key->name, "PRIMARY") == 0) { + if (key_num == form->primary_key) + { ind_type = ind_type | DICT_CLUSTERED; } @@ -2405,8 +2438,7 @@ ha_innobase::create( int error; dict_table_t* innobase_table; trx_t* trx; - int primary_key_no = -1; - KEY* key; + int primary_key_no; uint i; char name2[FN_REFLEN]; char norm_name[FN_REFLEN]; @@ -2434,13 +2466,9 @@ ha_innobase::create( /* Look for a primary key */ - for (i = 0; i < form->keys; i++) { - key = form->key_info + i; - - if (strcmp(key->name, "PRIMARY") == 0) { - primary_key_no = (int) i; - } - } + primary_key_no= (table->primary_key != MAX_KEY ? + (int) table->primary_key : + -1); /* Our function row_get_mysql_key_number_for_index assumes the primary key is always number 0, if it exists */ @@ -2511,12 +2539,12 @@ ha_innobase::create( /* Flush the log to reduce probability that the .frm files and the InnoDB data dictionary get out-of-sync if the user runs with innodb_flush_log_at_trx_commit = 0 */ - + log_flush_up_to(ut_dulint_max, LOG_WAIT_ONE_GROUP); innobase_table = dict_table_get(norm_name, NULL); - assert(innobase_table); + assert(innobase_table != 0); /* Tell the InnoDB server that there might be work for utility threads: */ @@ -2566,7 +2594,7 @@ ha_innobase::delete_table( /* Flush the log to reduce probability that the .frm files and the InnoDB data dictionary get out-of-sync if the user runs with innodb_flush_log_at_trx_commit = 0 */ - + log_flush_up_to(ut_dulint_max, LOG_WAIT_ONE_GROUP); /* Tell the InnoDB server that there might be work for @@ -2600,9 +2628,9 @@ innobase_drop_database( char* ptr; int error; char namebuf[10000]; - + ptr = strend(path) - 2; - + while (ptr >= path && *ptr != '\\' && *ptr != '/') { ptr--; len++; @@ -2613,16 +2641,8 @@ innobase_drop_database( memcpy(namebuf, ptr, len); namebuf[len] = '/'; namebuf[len + 1] = '\0'; - #ifdef __WIN__ - /* Put to lower case */ - - ptr = namebuf; - - while (*ptr != '\0') { - *ptr = tolower(*ptr); - ptr++; - } + casedn_str(namebuf); #endif trx = trx_allocate_for_mysql(); @@ -2631,7 +2651,7 @@ innobase_drop_database( /* Flush the log to reduce probability that the .frm files and the InnoDB data dictionary get out-of-sync if the user runs with innodb_flush_log_at_trx_commit = 0 */ - + log_flush_up_to(ut_dulint_max, LOG_WAIT_ONE_GROUP); /* Tell the InnoDB server that there might be work for @@ -2684,7 +2704,7 @@ ha_innobase::rename_table( /* Flush the log to reduce probability that the .frm files and the InnoDB data dictionary get out-of-sync if the user runs with innodb_flush_log_at_trx_commit = 0 */ - + log_flush_up_to(ut_dulint_max, LOG_WAIT_ONE_GROUP); /* Tell the InnoDB server that there might be work for @@ -2741,7 +2761,7 @@ ha_innobase::records_in_range( /* Warning: since it is not sure that MySQL calls external_lock before calling this function, the trx field in prebuilt can be obsolete! */ - + active_index = keynr; key = table->key_info + active_index; @@ -2793,7 +2813,7 @@ ha_innobase::estimate_number_of_rows(void) dict_index_t* index; ulonglong estimate; ulonglong data_file_length; - + /* Warning: since it is not sure that MySQL calls external_lock before calling this function, the trx field in prebuilt can be obsolete! */ @@ -2801,7 +2821,7 @@ ha_innobase::estimate_number_of_rows(void) DBUG_ENTER("info"); index = dict_table_get_first_index_noninline(prebuilt->table); - + data_file_length = ((ulonglong) index->stat_n_leaf_pages) * UNIV_PAGE_SIZE; @@ -2812,7 +2832,7 @@ ha_innobase::estimate_number_of_rows(void) of the formula below. */ estimate = 2 * data_file_length / dict_index_calc_min_rec_len(index); - + DBUG_RETURN((ha_rows) estimate); } @@ -2857,7 +2877,7 @@ ha_innobase::info( /* Warning: since it is not sure that MySQL calls external_lock before calling this function, the trx field in prebuilt can be obsolete! */ - + ib_table = prebuilt->table; if (flag & HA_STATUS_TIME) { @@ -2914,11 +2934,11 @@ ha_innobase::info( if (rec_per_key == 0) { rec_per_key = 1; } - - table->key_info[i].rec_per_key[j] + + table->key_info[i].rec_per_key[j] = rec_per_key; } - + index = dict_table_get_next_index_noninline(index); } } @@ -2956,7 +2976,7 @@ ha_innobase::check( { row_prebuilt_t* prebuilt = (row_prebuilt_t*) innobase_prebuilt; ulint ret; - + ut_a(prebuilt->trx && prebuilt->trx->magic_n == TRX_MAGIC_N); if (prebuilt->mysql_template == NULL) { @@ -2971,7 +2991,7 @@ ha_innobase::check( if (ret == DB_SUCCESS) { return(HA_ADMIN_OK); } - + return(HA_ADMIN_CORRUPT); } @@ -3011,12 +3031,12 @@ ha_innobase::update_table_comment( (ulong) innobase_get_free_space()); /* We assume 450 - length bytes of space to print info */ - + if (length < 450) { dict_print_info_on_foreign_keys(FALSE, pos, 450 - length, prebuilt->table); } - + return(str); } @@ -3185,6 +3205,7 @@ ha_innobase::external_lock( & (OPTION_NOT_AUTO_COMMIT | OPTION_BEGIN))) { innobase_commit(thd, trx); + thd->transaction.all.innodb_active_trans=0; } } } diff --git a/sql/ha_innobase.h b/sql/ha_innodb.h index 9f752dd2eda..b2aa963f91a 100644 --- a/sql/ha_innobase.h +++ b/sql/ha_innodb.h @@ -1,7 +1,4 @@ -/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB - && Innobase Oy - - -This file is modified from ha_berkeley.h of MySQL distribution- +/* Copyright (C) 2000 MySQL AB && Innobase Oy 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 @@ -17,13 +14,17 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +/* + This file is based on ha_berkeley.h of MySQL distribution + + This file defines the Innodb handler: the interface between MySQL and + Innodb +*/ + #ifdef __GNUC__ #pragma interface /* gcc class implementation */ #endif -/* This file defines the Innobase handler: the interface between MySQL and -Innobase */ - typedef struct st_innobase_share { THR_LOCK lock; pthread_mutex_t mutex; @@ -32,11 +33,11 @@ typedef struct st_innobase_share { } INNOBASE_SHARE; -/* The class defining a handle to an Innobase table */ +/* The class defining a handle to an Innodb table */ class ha_innobase: public handler { void* innobase_prebuilt; /* (row_prebuilt_t*) prebuilt - struct in Innobase, used to save + struct in Innodb, used to save CPU */ THD* user_thd; /* the thread handle of the user currently using the handle; this is @@ -50,10 +51,10 @@ class ha_innobase: public handler byte* upd_buff; /* buffer used in updates */ byte* key_val_buff; /* buffer used in converting search key values from MySQL format - to Innobase format */ + to Innodb format */ uint ref_stored_len; /* length of the key value stored to 'ref' buffer of the handle, if any */ - ulong int_option_flag; + ulong int_table_flags; uint primary_key; uint last_dup_key; ulong start_of_scan; /* this is set to 1 when we are @@ -74,16 +75,16 @@ class ha_innobase: public handler /* Init values for the class: */ public: ha_innobase(TABLE *table): handler(table), - int_option_flag(HA_READ_NEXT | HA_READ_PREV | HA_READ_ORDER | - HA_REC_NOT_IN_SEQ | + int_table_flags(HA_REC_NOT_IN_SEQ | HA_KEYPOS_TO_RNDPOS | HA_LASTKEY_ORDER | - HA_HAVE_KEY_READ_ONLY | HA_READ_NOT_EXACT_KEY | - HA_LONGLONG_KEYS | HA_NULL_KEY | - HA_NOT_EXACT_COUNT | HA_NO_FULLTEXT_KEY | + HA_HAVE_KEY_READ_ONLY | + HA_NULL_KEY | + HA_NOT_EXACT_COUNT | HA_NO_WRITE_DELAYED | HA_PRIMARY_KEY_IN_READ_INDEX | HA_DROP_BEFORE_CREATE | - HA_NO_PREFIX_CHAR_KEYS), + HA_NO_PREFIX_CHAR_KEYS | + HA_TABLE_SCAN_ON_INDEX), last_dup_key((uint) -1), start_of_scan(0) { @@ -91,8 +92,14 @@ class ha_innobase: public handler ~ha_innobase() {} const char* table_type() const { return("InnoDB");} + const char *index_type(uint key_number) { return "BTREE"; } const char** bas_ext() const; - ulong option_flag() const { return int_option_flag; } + ulong table_flags() const { return int_table_flags; } + ulong index_flags(uint idx) const + { + return (HA_READ_NEXT | HA_READ_PREV | HA_READ_ORDER | + HA_NOT_READ_PREFIX_LAST); + } uint max_record_length() const { return HA_MAX_REC_LENGTH; } uint max_keys() const { return MAX_KEY; } uint max_key_parts() const { return MAX_REF_PARTS; } @@ -121,9 +128,10 @@ class ha_innobase: public handler int index_init(uint index); int index_end(); int index_read(byte * buf, const byte * key, - uint key_len, enum ha_rkey_function find_flag); + uint key_len, enum ha_rkey_function find_flag); int index_read_idx(byte * buf, uint index, const byte * key, - uint key_len, enum ha_rkey_function find_flag); + uint key_len, enum ha_rkey_function find_flag); + int index_read_last(byte * buf, const byte * key, uint key_len); int index_next(byte * buf); int index_next_same(byte * buf, const byte *key, uint keylen); int index_prev(byte * buf); diff --git a/sql/ha_isam.cc b/sql/ha_isam.cc index ac37d09e6b4..55d24f5edb9 100644 --- a/sql/ha_isam.cc +++ b/sql/ha_isam.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 */ @@ -20,6 +20,7 @@ #endif #include "mysql_priv.h" +#ifdef HAVE_ISAM #include <m_ctype.h> #include <myisampack.h> #include "ha_isam.h" @@ -51,7 +52,7 @@ int ha_isam::open(const char *name, int mode, uint test_if_locked) if (!(test_if_locked & HA_OPEN_WAIT_IF_LOCKED)) (void) nisam_extra(file,HA_EXTRA_WAIT_LOCK); if (!table->db_record_offset) - int_option_flag|=HA_REC_NOT_IN_SEQ; + int_table_flags|=HA_REC_NOT_IN_SEQ; return (0); } @@ -108,6 +109,15 @@ int ha_isam::index_read_idx(byte * buf, uint index, const byte * key, return !error ? 0 : my_errno ? my_errno : -1; } +int ha_isam::index_read_last(byte * buf, const byte * key, uint key_len) +{ + statistic_increment(ha_read_key_count,&LOCK_status); + int error=nisam_rkey(file, buf, active_index, key, key_len, + HA_READ_PREFIX_LAST); + table->status=error ? STATUS_NOT_FOUND: 0; + return !error ? 0 : my_errno ? my_errno : -1; +} + int ha_isam::index_next(byte * buf) { statistic_increment(ha_read_next_count,&LOCK_status); @@ -123,7 +133,7 @@ int ha_isam::index_prev(byte * buf) table->status=error ? STATUS_NOT_FOUND: 0; return !error ? 0 : my_errno ? my_errno : HA_ERR_END_OF_FILE; } - + int ha_isam::index_first(byte * buf) { statistic_increment(ha_read_first_count,&LOCK_status); @@ -235,7 +245,7 @@ int ha_isam::reset(void) int ha_isam::external_lock(THD *thd, int lock_type) { return nisam_lock_database(file,lock_type); -} +} THR_LOCK_DATA **ha_isam::store_lock(THD *thd, @@ -315,7 +325,7 @@ int ha_isam::create(const char *name, register TABLE *form, { /* skip null fields */ if (!(temp_length= (*field)->pack_length())) - continue; /* Skipp null-fields */ + continue; /* Skip null-fields */ if (! found || fieldpos < minpos || (fieldpos == minpos && temp_length < length)) { @@ -343,15 +353,15 @@ int ha_isam::create(const char *name, register TABLE *form, else if (!(options & HA_OPTION_PACK_RECORD)) recinfo_pos->base.type= (int) FIELD_NORMAL; else if (found->zero_pack()) - recinfo_pos->base.type= (int) FIELD_SKIPP_ZERO; + recinfo_pos->base.type= (int) FIELD_SKIP_ZERO; else recinfo_pos->base.type= (int) ((length <= 3 || (found->flags & ZEROFILL_FLAG)) ? FIELD_NORMAL : found->type() == FIELD_TYPE_STRING || found->type() == FIELD_TYPE_VAR_STRING ? - FIELD_SKIPP_ENDSPACE : - FIELD_SKIPP_PRESPACE); + FIELD_SKIP_ENDSPACE : + FIELD_SKIP_PRESPACE); recinfo_pos++ ->base.length=(uint16) length; recpos=minpos+length; DBUG_PRINT("loop",("length: %d type: %d", @@ -388,3 +398,4 @@ ha_rows ha_isam::records_in_range(int inx, end_key,end_key_len, end_search_flag); } +#endif /* HAVE_ISAM */ diff --git a/sql/ha_isam.h b/sql/ha_isam.h index 5e01edcf889..bae9700f149 100644 --- a/sql/ha_isam.h +++ b/sql/ha_isam.h @@ -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 */ @@ -26,20 +26,21 @@ class ha_isam: public handler { N_INFO *file; - uint int_option_flag; + uint int_table_flags; public: ha_isam(TABLE *table): handler(table), file(0), - int_option_flag(HA_READ_NEXT | HA_READ_PREV | HA_READ_RND_SAME | - HA_KEYPOS_TO_RNDPOS | HA_READ_ORDER | HA_LASTKEY_ORDER | - HA_HAVE_KEY_READ_ONLY | HA_READ_NOT_EXACT_KEY | - HA_LONGLONG_KEYS | HA_KEY_READ_WRONG_STR | HA_DUPP_POS | - HA_NOT_DELETE_WITH_CACHE | HA_NO_FULLTEXT_KEY) + int_table_flags(HA_READ_RND_SAME | + HA_KEYPOS_TO_RNDPOS | HA_LASTKEY_ORDER | + HA_HAVE_KEY_READ_ONLY | + HA_KEY_READ_WRONG_STR | HA_DUPP_POS | + HA_NOT_DELETE_WITH_CACHE) {} ~ha_isam() {} const char *table_type() const { return "ISAM"; } + const char *index_type(uint key_number) { return "BTREE"; } const char **bas_ext() const; - ulong option_flag() const { return int_option_flag; } + ulong table_flags() const { return int_table_flags; } uint max_record_length() const { return HA_MAX_REC_LENGTH; } uint max_keys() const { return N_MAXKEY; } uint max_key_parts() const { return N_MAXKEY_SEG; } @@ -56,6 +57,7 @@ class ha_isam: public handler uint key_len, enum ha_rkey_function find_flag); int index_read_idx(byte * buf, uint idx, const byte * key, uint key_len, enum ha_rkey_function find_flag); + int index_read_last(byte * buf, const byte * key, uint key_len); int index_next(byte * buf); int index_prev(byte * buf); int index_first(byte * buf); @@ -80,9 +82,3 @@ class ha_isam: public handler enum thr_lock_type lock_type); }; - - - - - - diff --git a/sql/ha_isammrg.cc b/sql/ha_isammrg.cc index dd2e4e2f723..b110ffba2f9 100644 --- a/sql/ha_isammrg.cc +++ b/sql/ha_isammrg.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 */ @@ -20,11 +20,12 @@ #endif #include "mysql_priv.h" +#ifdef HAVE_ISAM #include <m_ctype.h> #ifndef MASTER -#include "../srclib/merge/mrgdef.h" +#include "../srclib/merge/mrg_def.h" #else -#include "../merge/mrgdef.h" +#include "../merge/mrg_def.h" #endif #include "ha_isammrg.h" @@ -109,7 +110,7 @@ int ha_isammrg::index_prev(byte * buf) { return (my_errno=HA_ERR_WRONG_COMMAND); } - + int ha_isammrg::index_first(byte * buf) { return (my_errno=HA_ERR_WRONG_COMMAND); @@ -178,7 +179,7 @@ int ha_isammrg::reset(void) int ha_isammrg::external_lock(THD *thd, int lock_type) { return !mrg_lock_database(file,lock_type) ? 0 : my_errno ? my_errno : -1; -} +} uint ha_isammrg::lock_count(void) const { @@ -208,3 +209,4 @@ int ha_isammrg::create(const char *name, register TABLE *form, char buff[FN_REFLEN]; return mrg_create(fn_format(buff,name,"","",2+4+16),0); } +#endif /* HAVE_ISAM */ diff --git a/sql/ha_isammrg.h b/sql/ha_isammrg.h index c8eb7dd9f69..bbe75a74db8 100644 --- a/sql/ha_isammrg.h +++ b/sql/ha_isammrg.h @@ -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 */ @@ -32,8 +32,10 @@ class ha_isammrg: public handler ~ha_isammrg() {} const char *table_type() const { return "MRG_ISAM"; } const char **bas_ext() const; - ulong option_flag() const { return HA_READ_RND_SAME | HA_KEYPOS_TO_RNDPOS - | HA_REC_NOT_IN_SEQ | HA_NO_FULLTEXT_KEY;} + ulong table_flags() const { return (HA_READ_RND_SAME | HA_KEYPOS_TO_RNDPOS | + HA_NOT_READ_PREFIX_LAST | + HA_REC_NOT_IN_SEQ); } + ulong index_flags(uint idx) const { return 0; } uint max_record_length() const { return HA_MAX_REC_LENGTH; } uint max_keys() const { return 0; } uint max_key_parts() const { return 0; } diff --git a/sql/ha_myisam.cc b/sql/ha_myisam.cc index 595c83ff8ef..50088c238e0 100644 --- a/sql/ha_myisam.cc +++ b/sql/ha_myisam.cc @@ -36,7 +36,7 @@ ulong myisam_recover_options= HA_RECOVER_NONE; /* bits in myisam_recover_options */ const char *myisam_recover_names[] = { "DEFAULT", "BACKUP", "FORCE", "QUICK", NullS}; -TYPELIB myisam_recover_typelib= {array_elements(myisam_recover_names),"", +TYPELIB myisam_recover_typelib= {array_elements(myisam_recover_names)-1,"", myisam_recover_names}; @@ -80,9 +80,8 @@ static void mi_check_print_msg(MI_CHECK *param, const char* msg_type, net_store_data(packet, msgbuf); if (my_net_write(&thd->net, (char*)thd->packet.ptr(), thd->packet.length())) - fprintf(stderr, - "Failed on my_net_write, writing to stderr instead: %s\n", - msgbuf); + sql_print_error("Failed on my_net_write, writing to stderr instead: %s\n", + msgbuf); return; } @@ -122,6 +121,13 @@ const char **ha_myisam::bas_ext() const { static const char *ext[]= { ".MYD",".MYI", NullS }; return ext; } +const char *ha_myisam::index_type(uint key_number) +{ + return ((table->key_info[key_number].flags & HA_FULLTEXT) ? + "FULLTEXT" : + "BTREE"); +} + int ha_myisam::net_read_dump(NET* net) { int data_fd = file->dfile; @@ -130,7 +136,7 @@ int ha_myisam::net_read_dump(NET* net) my_seek(data_fd, 0L, MY_SEEK_SET, MYF(MY_WME)); for(;;) { - uint packet_len = my_net_read(net); + ulong packet_len = my_net_read(net); if (!packet_len) break ; // end of file if (packet_len == packet_error) @@ -139,7 +145,7 @@ int ha_myisam::net_read_dump(NET* net) error= -1; goto err; } - if (my_write(data_fd, (byte*)net->read_pos, packet_len, + if (my_write(data_fd, (byte*)net->read_pos, (uint) packet_len, MYF(MY_WME|MY_FNABP))) { error = errno; @@ -217,7 +223,7 @@ int ha_myisam::open(const char *name, int mode, uint test_if_locked) if (!(test_if_locked & HA_OPEN_WAIT_IF_LOCKED)) VOID(mi_extra(file,HA_EXTRA_WAIT_LOCK)); if (!table->db_record_offset) - int_option_flag|=HA_REC_NOT_IN_SEQ; + int_table_flags|=HA_REC_NOT_IN_SEQ; return (0); } @@ -283,7 +289,7 @@ int ha_myisam::check(THD* thd, HA_CHECK_OPT* check_opt) error = chk_key(¶m, file); if (!error) { - if ((!check_opt->quick && + if ((!(param.testflag & T_QUICK) && ((share->options & (HA_OPTION_PACK_RECORD | HA_OPTION_COMPRESS_RECORD)) || (param.testflag & (T_EXTEND | T_MEDIUM)))) || @@ -324,7 +330,6 @@ int ha_myisam::check(THD* thd, HA_CHECK_OPT* check_opt) mi_mark_crashed(file); file->update |= HA_STATE_CHANGED | HA_STATE_ROW_CHANGED; } - check_opt->retry_without_quick=param.retry_without_quick; thd->proc_info=old_proc_info; return error ? HA_ADMIN_CORRUPT : HA_ADMIN_OK; @@ -374,14 +379,14 @@ int ha_myisam::restore(THD* thd, HA_CHECK_OPT *check_opt) char* backup_dir = thd->lex.backup_dir; char src_path[FN_REFLEN], dst_path[FN_REFLEN]; char* table_name = table->real_name; + int error; + const char* errmsg; DBUG_ENTER("restore"); - if (!fn_format(src_path, table_name, backup_dir, MI_NAME_DEXT, 4 + 64)) + if (fn_format_relative_to_data_home(src_path, table_name, backup_dir, + MI_NAME_DEXT)) DBUG_RETURN(HA_ADMIN_INVALID); - int error = 0; - const char* errmsg = ""; - if (my_copy(src_path, fn_format(dst_path, table->path, "", MI_NAME_DEXT, 4), MYF(MY_WME))) { @@ -391,8 +396,7 @@ int ha_myisam::restore(THD* thd, HA_CHECK_OPT *check_opt) } tmp_check_opt.init(); - tmp_check_opt.quick = 1; - tmp_check_opt.flags |= T_VERY_SILENT | T_CALC_CHECKSUM; + tmp_check_opt.flags |= T_VERY_SILENT | T_CALC_CHECKSUM | T_QUICK; DBUG_RETURN(repair(thd, &tmp_check_opt)); err: @@ -404,7 +408,7 @@ int ha_myisam::restore(THD* thd, HA_CHECK_OPT *check_opt) param.db_name = table->table_cache_key; param.table_name = table->table_name; param.testflag = 0; - mi_check_print_error(¶m,errmsg, errno ); + mi_check_print_error(¶m,errmsg, my_errno); DBUG_RETURN(error); } } @@ -415,41 +419,47 @@ int ha_myisam::backup(THD* thd, HA_CHECK_OPT *check_opt) char* backup_dir = thd->lex.backup_dir; char src_path[FN_REFLEN], dst_path[FN_REFLEN]; char* table_name = table->real_name; - int error = 0; - const char* errmsg = ""; - - if (!fn_format(dst_path, table_name, backup_dir, reg_ext, 4 + 64)) - { - errmsg = "Failed in fn_format() for .frm file: errno = %d"; - error = HA_ADMIN_INVALID; - goto err; - } - - if (my_copy(fn_format(src_path, table->path,"", reg_ext, 4), - dst_path, - MYF(MY_WME | MY_HOLD_ORIGINAL_MODES ))) + int error; + const char *errmsg; + DBUG_ENTER("ha_myisam::backup"); + + if (fn_format_relative_to_data_home(dst_path, table_name, backup_dir, + reg_ext)) + { + errmsg = "Failed in fn_format() for .frm file: errno = %d"; + error = HA_ADMIN_INVALID; + goto err; + } + + if (my_copy(fn_format(src_path, table->path,"", reg_ext, MY_UNPACK_FILENAME), + dst_path, + MYF(MY_WME | MY_HOLD_ORIGINAL_MODES))) { error = HA_ADMIN_FAILED; errmsg = "Failed copying .frm file: errno = %d"; goto err; } - if (!fn_format(dst_path, table_name, backup_dir, MI_NAME_DEXT, 4 + 64)) - { - errmsg = "Failed in fn_format() for .MYD file: errno = %d"; - error = HA_ADMIN_INVALID; - goto err; - } + /* Change extension */ + if (!fn_format(dst_path, dst_path, "", MI_NAME_DEXT, + MY_REPLACE_EXT | MY_UNPACK_FILENAME | MY_SAFE_PATH)) + { + errmsg = "Failed in fn_format() for .MYD file: errno = %d"; + error = HA_ADMIN_INVALID; + goto err; + } - if (my_copy(fn_format(src_path, table->path,"", MI_NAME_DEXT, 4), + if (my_copy(fn_format(src_path, table->path,"", MI_NAME_DEXT, + MY_UNPACK_FILENAME), dst_path, - MYF(MY_WME | MY_HOLD_ORIGINAL_MODES )) ) - { - errmsg = "Failed copying .MYD file: errno = %d"; - error= HA_ADMIN_FAILED; - goto err; - } - return HA_ADMIN_OK; + MYF(MY_WME | MY_HOLD_ORIGINAL_MODES))) + { + errmsg = "Failed copying .MYD file: errno = %d"; + error= HA_ADMIN_FAILED; + goto err; + } + DBUG_RETURN(HA_ADMIN_OK); + err: { MI_CHECK param; @@ -459,8 +469,8 @@ int ha_myisam::backup(THD* thd, HA_CHECK_OPT *check_opt) param.db_name = table->table_cache_key; param.table_name = table->table_name; param.testflag = 0; - mi_check_print_error(¶m,errmsg, errno ); - return error; + mi_check_print_error(¶m,errmsg, my_errno); + DBUG_RETURN(error); } } @@ -476,24 +486,23 @@ int ha_myisam::repair(THD* thd, HA_CHECK_OPT *check_opt) myisamchk_init(¶m); param.thd = thd; param.op_name = (char*) "repair"; - param.testflag = ((check_opt->flags & ~(T_EXTEND)) | + param.testflag = ((check_opt->flags & ~(T_EXTEND)) | T_SILENT | T_FORCE_CREATE | (check_opt->flags & T_EXTEND ? T_REP : T_REP_BY_SORT)); - if (check_opt->quick) - param.opt_rep_quick++; param.sort_buffer_length= check_opt->sort_buffer_size; start_records=file->state->records; while ((error=repair(thd,param,0)) && param.retry_repair) { param.retry_repair=0; - if (param.retry_without_quick && param.opt_rep_quick) + if ((param.testflag & T_RETRY_WITHOUT_QUICK) && + (param.testflag & T_QUICK)) { - param.opt_rep_quick=0; + param.testflag&= ~T_RETRY_WITHOUT_QUICK; sql_print_error("Warning: Retrying repair of: '%s' without quick", table->path); continue; } - param.opt_rep_quick=0; // Safety + param.testflag&= ~T_QUICK; if ((param.testflag & T_REP_BY_SORT)) { param.testflag= (param.testflag & ~T_REP_BY_SORT) | T_REP; @@ -525,8 +534,6 @@ int ha_myisam::optimize(THD* thd, HA_CHECK_OPT *check_opt) param.op_name = (char*) "optimize"; param.testflag = (check_opt->flags | T_SILENT | T_FORCE_CREATE | T_REP_BY_SORT | T_STATISTICS | T_SORT_INDEX); - if (check_opt->quick) - param.opt_rep_quick++; param.sort_buffer_length= check_opt->sort_buffer_size; return repair(thd,param,1); } @@ -537,8 +544,8 @@ int ha_myisam::repair(THD *thd, MI_CHECK ¶m, bool optimize) int error=0; uint local_testflag=param.testflag; 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"); @@ -550,8 +557,7 @@ int ha_myisam::repair(THD *thd, MI_CHECK ¶m, 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)) @@ -562,7 +568,7 @@ int ha_myisam::repair(THD *thd, MI_CHECK ¶m, bool optimize) if (!optimize || ((file->state->del || share->state.split != file->state->records) && - (!param.opt_rep_quick || + (!(param.testflag & T_QUICK) || !(share->state.changed & STATE_NOT_OPTIMIZED_KEYS)))) { ulonglong key_map= ((local_testflag & T_CREATE_MISSING_KEYS) ? @@ -576,13 +582,15 @@ int ha_myisam::repair(THD *thd, MI_CHECK ¶m, bool optimize) param.testflag|= T_STATISTICS; // We get this for free thd->proc_info="Repair by sorting"; statistics_done=1; - error = mi_repair_by_sort(¶m, file, fixed_name, param.opt_rep_quick); + error = mi_repair_by_sort(¶m, file, fixed_name, + param.testflag & T_QUICK); } else { thd->proc_info="Repair with keycache"; param.testflag &= ~T_REP_BY_SORT; - error= mi_repair(¶m, file, fixed_name, param.opt_rep_quick); + error= mi_repair(¶m, file, fixed_name, + param.testflag & T_QUICK); } param.testflag=testflag; optimize_done=1; @@ -653,8 +661,21 @@ int ha_myisam::repair(THD *thd, MI_CHECK ¶m, bool optimize) void ha_myisam::deactivate_non_unique_index(ha_rows rows) { - if (!(specialflag & SPECIAL_SAFE_MODE)) - mi_disable_non_unique_index(file,rows); + MYISAM_SHARE* share = file->s; + if (share->state.key_map == ((ulonglong) 1L << share->base.keys)-1) + { + if (!(specialflag & SPECIAL_SAFE_MODE)) + if (rows==HA_POS_ERROR) + mi_extra(file, HA_EXTRA_NO_KEYS); + else + { + mi_disable_non_unique_index(file,rows); + mi_extra(file, HA_EXTRA_BULK_INSERT_BEGIN); + } + enable_activate_all_index=1; + } + else + enable_activate_all_index=0; } @@ -664,21 +685,25 @@ bool ha_myisam::activate_all_index(THD *thd) MI_CHECK param; MYISAM_SHARE* share = file->s; DBUG_ENTER("activate_all_index"); - if (share->state.key_map != set_bits(ulonglong, share->base.keys)) + + mi_extra(file, HA_EXTRA_BULK_INSERT_END); + if (enable_activate_all_index && + share->state.key_map != set_bits(ulonglong, share->base.keys)) { const char *save_proc_info=thd->proc_info; thd->proc_info="Creating index"; myisamchk_init(¶m); param.op_name = (char*) "recreating_index"; - param.testflag = (T_SILENT | T_REP_BY_SORT | + param.testflag = (T_SILENT | T_REP_BY_SORT | T_QUICK | T_CREATE_MISSING_KEYS | T_TRUST_HEADER); param.myf_rw&= ~MY_WAIT_IF_FULL; param.sort_buffer_length= myisam_sort_buffer_size; - param.opt_rep_quick++; // Don't copy data file param.tmpdir=mysql_tmpdir; error=repair(thd,param,0) != HA_ADMIN_OK; thd->proc_info=save_proc_info; } + else + enable_activate_all_index=1; DBUG_RETURN(error); } @@ -694,16 +719,16 @@ bool ha_myisam::check_and_repair(THD *thd) check_opt.flags= T_MEDIUM | T_AUTO_REPAIR; // Don't use quick if deleted rows if (!file->state->del && (myisam_recover_options & HA_RECOVER_QUICK)) - check_opt.quick=1; + check_opt.flags|=T_QUICK; sql_print_error("Warning: Checking table: '%s'",table->path); if ((marked_crashed=mi_is_crashed(file)) || check(thd, &check_opt)) { sql_print_error("Warning: Recovering table: '%s'",table->path); - check_opt.quick= !check_opt.retry_without_quick && !marked_crashed; - check_opt.flags=(((myisam_recover_options & HA_RECOVER_BACKUP) ? - T_BACKUP_DATA : 0) | - (!(myisam_recover_options & HA_RECOVER_FORCE) ? - T_SAFE_REPAIR : 0)) | T_AUTO_REPAIR; + check_opt.flags= + ((myisam_recover_options & HA_RECOVER_BACKUP ? T_BACKUP_DATA : 0) | + (marked_crashed ? 0 : T_QUICK) | + (myisam_recover_options & HA_RECOVER_FORCE ? 0 : T_SAFE_REPAIR) | + T_AUTO_REPAIR); if (repair(thd, &check_opt)) error=1; } @@ -748,6 +773,14 @@ int ha_myisam::index_read_idx(byte * buf, uint index, const byte * key, return error; } +int ha_myisam::index_read_last(byte * buf, const byte * key, uint key_len) +{ + statistic_increment(ha_read_key_count,&LOCK_status); + int error=mi_rkey(file,buf,active_index, key, key_len, HA_READ_PREFIX_LAST); + table->status=error ? STATUS_NOT_FOUND: 0; + return error; +} + int ha_myisam::index_next(byte * buf) { statistic_increment(ha_read_next_count,&LOCK_status); @@ -829,6 +862,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) { @@ -858,6 +893,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) { @@ -875,7 +922,8 @@ int ha_myisam::extra(enum ha_extra_function operation) { if (((specialflag & SPECIAL_SAFE_MODE) || (test_flags & TEST_NO_EXTRA)) && (operation == HA_EXTRA_WRITE_CACHE || - operation == HA_EXTRA_KEYREAD)) + operation == HA_EXTRA_KEYREAD || + operation == HA_EXTRA_BULK_INSERT_BEGIN)) return 0; return mi_extra(file,operation); } @@ -924,10 +972,12 @@ 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; } -int ha_myisam::create(const char *name, register TABLE *form, +int ha_myisam::create(const char *name, register TABLE *table, HA_CREATE_INFO *info) { int error; @@ -939,20 +989,20 @@ int ha_myisam::create(const char *name, register TABLE *form, MI_KEYDEF *keydef; MI_COLUMNDEF *recinfo,*recinfo_pos; MI_KEYSEG *keyseg; - uint options=form->db_options_in_use; + uint options=table->db_options_in_use; DBUG_ENTER("ha_myisam::create"); type=HA_KEYTYPE_BINARY; // Keep compiler happy if (!(my_multi_malloc(MYF(MY_WME), - &recinfo,(form->fields*2+2)*sizeof(MI_COLUMNDEF), - &keydef, form->keys*sizeof(MI_KEYDEF), + &recinfo,(table->fields*2+2)*sizeof(MI_COLUMNDEF), + &keydef, table->keys*sizeof(MI_KEYDEF), &keyseg, - ((form->key_parts + form->keys) * sizeof(MI_KEYSEG)), + ((table->key_parts + table->keys) * sizeof(MI_KEYSEG)), 0))) DBUG_RETURN(1); - pos=form->key_info; - for (i=0; i < form->keys ; i++, pos++) + pos=table->key_info; + for (i=0; i < table->keys ; i++, pos++) { keydef[i].flag= (pos->flags & (HA_NOSAME | HA_FULLTEXT)); keydef[i].seg=keyseg; @@ -995,7 +1045,7 @@ int ha_myisam::create(const char *name, register TABLE *form, { keydef[i].seg[j].null_bit=field->null_bit; keydef[i].seg[j].null_pos= (uint) (field->null_ptr- - (uchar*) form->record[0]); + (uchar*) table->record[0]); } else { @@ -1013,26 +1063,26 @@ int ha_myisam::create(const char *name, register TABLE *form, keydef[i].seg[j].flag|=HA_BLOB_PART; /* save number of bytes used to pack length */ keydef[i].seg[j].bit_start= (uint) (field->pack_length() - - form->blob_ptr_size); + table->blob_ptr_size); } } keyseg+=pos->key_parts; } recpos=0; recinfo_pos=recinfo; - while (recpos < (uint) form->reclength) + while (recpos < (uint) table->reclength) { Field **field,*found=0; - minpos=form->reclength; length=0; + minpos=table->reclength; length=0; - for (field=form->field ; *field ; field++) + for (field=table->field ; *field ; field++) { if ((fieldpos=(*field)->offset()) >= recpos && fieldpos <= minpos) { /* skip null fields */ if (!(temp_length= (*field)->pack_length())) - continue; /* Skipp null-fields */ + continue; /* Skip null-fields */ if (! found || fieldpos < minpos || (fieldpos == minpos && temp_length < length)) { @@ -1058,20 +1108,20 @@ int ha_myisam::create(const char *name, register TABLE *form, else if (!(options & HA_OPTION_PACK_RECORD)) recinfo_pos->type= (int) FIELD_NORMAL; else if (found->zero_pack()) - recinfo_pos->type= (int) FIELD_SKIPP_ZERO; + recinfo_pos->type= (int) FIELD_SKIP_ZERO; else recinfo_pos->type= (int) ((length <= 3 || (found->flags & ZEROFILL_FLAG)) ? FIELD_NORMAL : found->type() == FIELD_TYPE_STRING || found->type() == FIELD_TYPE_VAR_STRING ? - FIELD_SKIPP_ENDSPACE : - FIELD_SKIPP_PRESPACE); + FIELD_SKIP_ENDSPACE : + FIELD_SKIP_PRESPACE); if (found->null_ptr) { recinfo_pos->null_bit=found->null_bit; recinfo_pos->null_pos= (uint) (found->null_ptr- - (uchar*) form->record[0]); + (uchar*) table->record[0]); } else { @@ -1086,18 +1136,23 @@ int ha_myisam::create(const char *name, register TABLE *form, } MI_CREATE_INFO create_info; bzero((char*) &create_info,sizeof(create_info)); - create_info.max_rows=form->max_rows; - create_info.reloc_rows=form->min_rows; + create_info.max_rows=table->max_rows; + create_info.reloc_rows=table->min_rows; create_info.auto_increment=(info->auto_increment_value ? info->auto_increment_value -1 : (ulonglong) 0); - create_info.data_file_length=(ulonglong) form->max_rows*form->avg_row_length; + create_info.data_file_length= ((ulonglong) table->max_rows * + table->avg_row_length); 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; - - error=mi_create(fn_format(buff,name,"","",2+4+16), - form->keys,keydef, + 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), + table->keys,keydef, (uint) (recinfo_pos-recinfo), recinfo, 0, (MI_UNIQUEDEF*) 0, &create_info, @@ -1128,7 +1183,7 @@ longlong ha_myisam::get_auto_increment() longlong nr; int error; - byte key[MAX_KEY_LENGTH]; + byte key[MI_MAX_KEY_LENGTH]; (void) extra(HA_EXTRA_KEYREAD); key_copy(key,table,table->next_number_index, table->next_number_key_offset); @@ -1167,9 +1222,8 @@ int ha_myisam::ft_read(byte * buf) thread_safe_increment(ha_read_next_count,&LOCK_status); // why ? - error=ft_read_next((FT_DOCLIST *) ft_handler,(char*) buf); + error=ft_handler->please->read_next(ft_handler,(char*) buf); table->status=error ? STATUS_NOT_FOUND: 0; return error; } - diff --git a/sql/ha_myisam.h b/sql/ha_myisam.h index 6451e2b80ee..d304085fdd5 100644 --- a/sql/ha_myisam.h +++ b/sql/ha_myisam.h @@ -37,25 +37,28 @@ extern ulong myisam_recover_options; class ha_myisam: public handler { MI_INFO *file; - uint int_option_flag; + uint int_table_flags; + char *data_file_name, *index_file_name; + bool enable_activate_all_index; int repair(THD *thd, MI_CHECK ¶m, bool optimize); public: ha_myisam(TABLE *table): handler(table), file(0), - int_option_flag(HA_READ_NEXT | HA_READ_PREV | HA_READ_RND_SAME | - HA_KEYPOS_TO_RNDPOS | HA_READ_ORDER | HA_LASTKEY_ORDER | - HA_HAVE_KEY_READ_ONLY | HA_READ_NOT_EXACT_KEY | - HA_LONGLONG_KEYS | HA_NULL_KEY | - HA_DUPP_POS | HA_BLOB_KEY | HA_AUTO_PART_KEY) + int_table_flags(HA_READ_RND_SAME | HA_KEYPOS_TO_RNDPOS | HA_LASTKEY_ORDER | + HA_HAVE_KEY_READ_ONLY | + HA_NULL_KEY | HA_CAN_FULLTEXT | HA_CAN_SQL_HANDLER | + HA_DUPP_POS | HA_BLOB_KEY | HA_AUTO_PART_KEY), + enable_activate_all_index(1) {} ~ha_myisam() {} const char *table_type() const { return "MyISAM"; } + const char *index_type(uint key_number); const char **bas_ext() const; - ulong option_flag() const { return int_option_flag; } + ulong table_flags() const { return int_table_flags; } uint max_record_length() const { return HA_MAX_REC_LENGTH; } uint max_keys() const { return MI_MAX_KEY; } uint max_key_parts() const { return MAX_REF_PARTS; } - uint max_key_length() const { return MAX_KEY_LENGTH; } + uint max_key_length() const { return MI_MAX_KEY_LENGTH; } int open(const char *name, int mode, uint test_if_locked); int close(void); @@ -66,6 +69,7 @@ class ha_myisam: public handler uint key_len, enum ha_rkey_function find_flag); int index_read_idx(byte * buf, uint idx, const byte * key, uint key_len, enum ha_rkey_function find_flag); + int index_read_last(byte * buf, const byte * key, uint key_len); int index_next(byte * buf); int index_prev(byte * buf); int index_first(byte * buf); @@ -73,9 +77,15 @@ class ha_myisam: public handler int index_next_same(byte *buf, const byte *key, uint keylen); int index_end() { ft_handler=NULL; return 0; } int ft_init() - { if(!ft_handler) return 1; ft_reinit_search(ft_handler); return 0; } - void *ft_init_ext(uint inx,const byte *key, uint keylen, bool presort) - { return ft_init_search(file,inx,(byte*) key,keylen,presort); } + { + if (!ft_handler) + return 1; + ft_handler->please->reinit_search(ft_handler); + return 0; + } + FT_INFO *ft_init_ext(uint mode, uint inx,const byte *key, uint keylen, + bool presort) + { return ft_init_search(mode, file,inx,(byte*) key,keylen,presort); } int ft_read(byte *buf); int rnd_init(bool scan=1); int rnd_next(byte *buf); diff --git a/sql/ha_myisammrg.cc b/sql/ha_myisammrg.cc index e5fb0310a36..4e09c87f341 100644 --- a/sql/ha_myisammrg.cc +++ b/sql/ha_myisammrg.cc @@ -23,9 +23,9 @@ #include <m_ctype.h> #include "ha_myisammrg.h" #ifndef MASTER -#include "../srclib/myisammrg/mymrgdef.h" +#include "../srclib/myisammrg/myrg_def.h" #else -#include "../myisammrg/mymrgdef.h" +#include "../myisammrg/myrg_def.h" #endif /***************************************************************************** @@ -38,10 +38,15 @@ const char **ha_myisammrg::bas_ext() const int ha_myisammrg::open(const char *name, int mode, uint test_if_locked) { char name_buff[FN_REFLEN]; + DBUG_PRINT("info", ("ha_myisammrg::open")); if (!(file=myrg_open(fn_format(name_buff,name,"","",2 | 4), mode, test_if_locked))) + { + DBUG_PRINT("info", ("ha_myisammrg::open exit %d", my_errno)); return (my_errno ? my_errno : -1); - + } + DBUG_PRINT("info", ("ha_myisammrg::open myrg_extrafunc...")) + myrg_extrafunc(file, query_cache_invalidate_by_MyISAM_filename_ref); if (!(test_if_locked == HA_OPEN_WAIT_IF_LOCKED || test_if_locked == HA_OPEN_ABORT_IF_LOCKED)) myrg_extra(file,HA_EXTRA_NO_WAIT_LOCK); @@ -66,7 +71,13 @@ int ha_myisammrg::close(void) int ha_myisammrg::write_row(byte * buf) { - return (my_errno=HA_ERR_WRONG_COMMAND); + statistic_increment(ha_write_count,&LOCK_status); + if (table->time_stamp) + update_timestamp(buf+table->time_stamp-1); + if (table->next_number_field && buf == table->record[0]) + return (my_errno=HA_ERR_WRONG_COMMAND); + // update_auto_increment(); - [phi] have to check this before allowing it + return myrg_write(file,buf); } int ha_myisammrg::update_row(const byte * old_data, byte * new_data) @@ -101,6 +112,15 @@ int ha_myisammrg::index_read_idx(byte * buf, uint index, const byte * key, return error; } +int ha_myisammrg::index_read_last(byte * buf, const byte * key, uint key_len) +{ + statistic_increment(ha_read_key_count,&LOCK_status); + int error=myrg_rkey(file,buf,active_index, key, key_len, + HA_READ_PREFIX_LAST); + table->status=error ? STATUS_NOT_FOUND: 0; + return error; +} + int ha_myisammrg::index_next(byte * buf) { statistic_increment(ha_read_next_count,&LOCK_status); @@ -185,6 +205,11 @@ void ha_myisammrg::info(uint flag) int ha_myisammrg::extra(enum ha_extra_function operation) { + /* As this is just a mapping, we don't have to force the underlying + tables to be closed */ + if (operation == HA_EXTRA_FORCE_REOPEN || + operation == HA_EXTRA_PREPARE_FOR_DELETE) + return 0; return myrg_extra(file,operation); } @@ -221,6 +246,7 @@ THR_LOCK_DATA **ha_myisammrg::store_lock(THD *thd, void ha_myisammrg::update_create_info(HA_CREATE_INFO *create_info) { + // [phi] auto_increment stuff is missing (but currently not needed) DBUG_ENTER("ha_myisammrg::update_create_info"); if (!(create_info->used_fields & HA_CREATE_USED_UNION)) { @@ -231,7 +257,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)))) @@ -245,6 +271,10 @@ void ha_myisammrg::update_create_info(HA_CREATE_INFO *create_info) } *create_info->merge_list.next=0; } + if (!(create_info->used_fields & HA_CREATE_USED_INSERT_METHOD)) + { + create_info->merge_insert_method = file->merge_insert_method; + } DBUG_VOID_RETURN; err: @@ -267,18 +297,25 @@ int ha_myisammrg::create(const char *name, register TABLE *form, *pos++= tables->real_name; *pos=0; DBUG_RETURN(myrg_create(fn_format(buff,name,"","",2+4+16), - (const char **) table_names, (my_bool) 0)); + (const char **) table_names, + create_info->merge_insert_method, + (my_bool) 0)); } void ha_myisammrg::append_create_info(String *packet) { char buff[FN_REFLEN]; + if (file->merge_insert_method != MERGE_INSERT_DISABLED) + { + packet->append(" INSERT_METHOD=",15); + packet->append(get_type(&merge_insert_method,file->merge_insert_method-1)); + } packet->append(" UNION=(",8); MYRG_TABLE *table,*first; 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/ha_myisammrg.h b/sql/ha_myisammrg.h index b97baa0703c..ed2817f3ca6 100644 --- a/sql/ha_myisammrg.h +++ b/sql/ha_myisammrg.h @@ -32,13 +32,13 @@ class ha_myisammrg: public handler ~ha_myisammrg() {} const char *table_type() const { return "MRG_MyISAM"; } const char **bas_ext() const; - ulong option_flag() const - { return (HA_REC_NOT_IN_SEQ | HA_READ_NEXT | - HA_READ_PREV | HA_READ_RND_SAME | - HA_HAVE_KEY_READ_ONLY | HA_NO_FULLTEXT_KEY | - HA_KEYPOS_TO_RNDPOS | HA_READ_ORDER | - HA_LASTKEY_ORDER | HA_READ_NOT_EXACT_KEY | - HA_LONGLONG_KEYS | HA_NULL_KEY | HA_BLOB_KEY); } + ulong table_flags() const + { + return (HA_REC_NOT_IN_SEQ | HA_READ_RND_SAME | + HA_HAVE_KEY_READ_ONLY | HA_KEYPOS_TO_RNDPOS | + HA_LASTKEY_ORDER | + HA_NULL_KEY | HA_BLOB_KEY); + } uint max_record_length() const { return HA_MAX_REC_LENGTH; } uint max_keys() const { return MI_MAX_KEY; } uint max_key_parts() const { return MAX_REF_PARTS; } @@ -55,6 +55,7 @@ class ha_myisammrg: public handler uint key_len, enum ha_rkey_function find_flag); int index_read_idx(byte * buf, uint idx, const byte * key, uint key_len, enum ha_rkey_function find_flag); + int index_read_last(byte * buf, const byte * key, uint key_len); int index_next(byte * buf); int index_prev(byte * buf); int index_first(byte * buf); @@ -74,4 +75,5 @@ class ha_myisammrg: public handler enum thr_lock_type lock_type); void update_create_info(HA_CREATE_INFO *create_info); void append_create_info(String *packet); + MYRG_INFO *myrg_info() { return file; } }; diff --git a/sql/handler.cc b/sql/handler.cc index 098c3130de2..7947ae5a9f0 100644 --- a/sql/handler.cc +++ b/sql/handler.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 */ @@ -25,7 +25,7 @@ #include "ha_heap.h" #include "ha_myisam.h" #include "ha_myisammrg.h" -#ifndef NO_ISAM +#ifdef HAVE_ISAM #include "ha_isam.h" #include "ha_isammrg.h" #endif @@ -33,10 +33,7 @@ #include "ha_berkeley.h" #endif #ifdef HAVE_INNOBASE_DB -#include "ha_innobase.h" -#endif -#ifdef HAVE_GEMINI_DB -#include "ha_gemini.h" +#include "ha_innodb.h" #endif #include <myisampack.h> #include <errno.h> @@ -48,6 +45,7 @@ static int NEAR_F delete_file(const char *name,const char *ext,int extflag); ulong ha_read_count, ha_write_count, ha_delete_count, ha_update_count, ha_read_key_count, ha_read_next_count, ha_read_prev_count, ha_read_first_count, ha_read_last_count, + ha_commit_count, ha_rollback_count, ha_read_rnd_count, ha_read_rnd_next_count; const char *ha_table_type[] = { @@ -78,21 +76,15 @@ enum db_type ha_checktype(enum db_type database_type) return(berkeley_skip ? DB_TYPE_MYISAM : database_type); #endif #ifdef HAVE_INNOBASE_DB - case DB_TYPE_INNOBASE: + case DB_TYPE_INNODB: return(innodb_skip ? DB_TYPE_MYISAM : database_type); #endif -#ifdef HAVE_GEMINI_DB - case DB_TYPE_GEMINI: - return(gemini_skip ? DB_TYPE_MYISAM : database_type); -#endif #ifndef NO_HASH case DB_TYPE_HASH: #endif -#ifndef NO_MERGE - case DB_TYPE_MRG_ISAM: -#endif -#ifndef NO_ISAM +#ifdef HAVE_ISAM case DB_TYPE_ISAM: + case DB_TYPE_MRG_ISAM: #endif case DB_TYPE_HEAP: case DB_TYPE_MYISAM: @@ -111,11 +103,9 @@ handler *get_new_handler(TABLE *table, enum db_type db_type) #ifndef NO_HASH return new ha_hash(table); #endif -#ifndef NO_MERGE +#ifdef HAVE_ISAM case DB_TYPE_MRG_ISAM: return new ha_isammrg(table); -#endif -#ifndef NO_ISAM case DB_TYPE_ISAM: return new ha_isam(table); #endif @@ -124,13 +114,9 @@ handler *get_new_handler(TABLE *table, enum db_type db_type) return new ha_berkeley(table); #endif #ifdef HAVE_INNOBASE_DB - case DB_TYPE_INNOBASE: + case DB_TYPE_INNODB: return new ha_innobase(table); #endif -#ifdef HAVE_GEMINI_DB - case DB_TYPE_GEMINI: - return new ha_gemini(table); -#endif case DB_TYPE_HEAP: return new ha_heap(table); case DB_TYPE_MYISAM: @@ -166,17 +152,6 @@ int ha_init() have_innodb=SHOW_OPTION_DISABLED; } #endif -#ifdef HAVE_GEMINI_DB - if (!gemini_skip) - { - if (gemini_init()) - return -1; - if (!gemini_skip) // If we couldn't use handler - opt_using_transactions=1; - else - have_gemini=SHOW_OPTION_DISABLED; - } -#endif return 0; } @@ -186,14 +161,14 @@ int ha_init() int ha_panic(enum ha_panic_function flag) { int error=0; -#ifndef NO_MERGE - error|=mrg_panic(flag); -#endif #ifndef NO_HASH error|=h_panic(flag); /* fix hash */ #endif - error|=heap_panic(flag); +#ifdef HAVE_ISAM + error|=mrg_panic(flag); error|=nisam_panic(flag); +#endif + error|=heap_panic(flag); error|=mi_panic(flag); error|=myrg_panic(flag); #ifdef HAVE_BERKELEY_DB @@ -204,10 +179,6 @@ int ha_panic(enum ha_panic_function flag) if (!innodb_skip) error|=innobase_end(); #endif -#ifdef HAVE_GEMINI_DB - if (!gemini_skip) - error|=gemini_end(); -#endif return error; } /* ha_panic */ @@ -225,12 +196,6 @@ void ha_close_connection(THD* thd) if (!innodb_skip) innobase_close_connection(thd); #endif -#ifdef HAVE_GEMINI_DB - if (!gemini_skip && thd->gemini.context) - { - gemini_disconnect(thd); - } -#endif /* HAVE_GEMINI_DB */ } /* @@ -305,6 +270,8 @@ int ha_commit_trans(THD *thd, THD_TRANS* trans) #ifdef USING_TRANSACTIONS if (opt_using_transactions) { + bool operation_done= 0; + bool transaction_commited= 0; /* Update the binary log if we have cached some queries */ if (trans == &thd->transaction.all && mysql_bin_log.is_open() && my_b_tell(&thd->transaction.trans_log)) @@ -322,6 +289,8 @@ int ha_commit_trans(THD *thd, THD_TRANS* trans) my_error(ER_ERROR_DURING_COMMIT, MYF(0), error); error=1; } + else + transaction_commited= 1; trans->bdb_tid=0; } #endif @@ -334,25 +303,20 @@ int ha_commit_trans(THD *thd, THD_TRANS* trans) error=1; } trans->innodb_active_trans=0; + if (trans == &thd->transaction.all) + operation_done= transaction_commited= 1; + } #endif -#ifdef HAVE_GEMINI_DB - /* Commit the transaction in behalf of the commit statement - or if we're in auto-commit mode */ - if((trans == &thd->transaction.all) || - (!(thd->options & (OPTION_NOT_AUTO_COMMIT | OPTION_BEGIN)))) - { - error=gemini_commit(thd); - if (error) - { - my_error(ER_ERROR_DURING_COMMIT, MYF(0), error); - error=1; - } - } -#endif +#ifdef HAVE_QUERY_CACHE + if (transaction_commited) + query_cache.invalidate(thd->transaction.changed_tables); +#endif /*HAVE_QUERY_CACHE*/ if (error && trans == &thd->transaction.all && mysql_bin_log.is_open()) sql_print_error("Error: Got error during commit; Binlog is not up to date!"); thd->tx_isolation=thd->session_tx_isolation; + if (operation_done) + statistic_increment(ha_commit_count,&LOCK_status); } #endif // using transactions DBUG_RETURN(error); @@ -366,6 +330,7 @@ int ha_rollback_trans(THD *thd, THD_TRANS *trans) #ifdef USING_TRANSACTIONS if (opt_using_transactions) { + bool operation_done=0; #ifdef HAVE_BERKELEY_DB if (trans->bdb_tid) { @@ -375,6 +340,7 @@ int ha_rollback_trans(THD *thd, THD_TRANS *trans) error=1; } trans->bdb_tid=0; + operation_done=1; } #endif #ifdef HAVE_INNOBASE_DB @@ -386,18 +352,7 @@ int ha_rollback_trans(THD *thd, THD_TRANS *trans) error=1; } trans->innodb_active_trans=0; - } -#endif -#ifdef HAVE_GEMINI_DB - if((trans == &thd->transaction.stmt) && - (thd->options & (OPTION_NOT_AUTO_COMMIT | OPTION_BEGIN))) - error = gemini_rollback_to_savepoint(thd); - else - error=gemini_rollback(thd); - if (error) - { - my_error(ER_ERROR_DURING_ROLLBACK, MYF(0), error); - error=1; + operation_done=1; } #endif if (trans == &thd->transaction.all) @@ -405,22 +360,13 @@ int ha_rollback_trans(THD *thd, THD_TRANS *trans) WRITE_CACHE, (my_off_t) 0, 0, 1); thd->transaction.trans_log.end_of_file= max_binlog_cache_size; thd->tx_isolation=thd->session_tx_isolation; + if (operation_done) + statistic_increment(ha_rollback_count,&LOCK_status); } #endif /* USING_TRANSACTIONS */ DBUG_RETURN(error); } -void ha_set_spin_retries(uint retries) -{ -#ifdef HAVE_GEMINI_DB - if (!gemini_skip) - { - gemini_set_option_long(GEM_OPTID_SPIN_RETRIES, retries); - } -#endif /* HAVE_GEMINI_DB */ -} - - bool ha_flush_logs() { bool result=0; @@ -449,7 +395,7 @@ int ha_delete_table(enum db_type table_type, const char *path) delete file; return error; } - + void ha_store_ptr(byte *buff, uint pack_length, my_off_t pos) { switch (pack_length) { @@ -536,9 +482,8 @@ int handler::ha_open(const char *name, int mode, int test_if_locked) { if (table->db_options_in_use & HA_OPTION_READ_ONLY_DATA) table->db_stat|=HA_READ_ONLY; - } - if (!error) - { + (void) extra(HA_EXTRA_NO_READCHECK); // Not needed in SQL + if (!alloc_root_inited(&table->mem_root)) // If temporary table ref=(byte*) sql_alloc(ALIGN_SIZE(ref_length)*2); else @@ -584,17 +529,37 @@ int handler::analyze(THD* thd, HA_CHECK_OPT* check_opt) return HA_ADMIN_NOT_IMPLEMENTED; } - /* Read first row from a table */ +/* + Read first row (only) from a table + This is never called for InnoDB or BDB tables, as these table types + has the HA_NOT_EXACT_COUNT set. +*/ -int handler::rnd_first(byte * buf) +int handler::read_first_row(byte * buf, uint primary_key) { register int error; - DBUG_ENTER("handler::rnd_first"); + DBUG_ENTER("handler::read_first_row"); statistic_increment(ha_read_first_count,&LOCK_status); - (void) rnd_init(); - while ((error= rnd_next(buf)) == HA_ERR_RECORD_DELETED) ; - (void) rnd_end(); + + /* + If there is very few deleted rows in the table, find the first row by + scanning the table. + */ + if (deleted < 10 || primary_key >= MAX_KEY || + !(index_flags(primary_key) & HA_READ_ORDER)) + { + (void) rnd_init(); + while ((error= rnd_next(buf)) == HA_ERR_RECORD_DELETED) ; + (void) rnd_end(); + } + else + { + /* Find the first row through the primary key */ + (void) index_init(primary_key); + error=index_first(buf); + (void) index_end(); + } DBUG_RETURN(error); } @@ -610,7 +575,7 @@ int handler::restart_rnd_next(byte *buf, byte *pos) } - /* Set a timestamp in record */ +/* Set a timestamp in record */ void handler::update_timestamp(byte *record) { @@ -626,9 +591,10 @@ void handler::update_timestamp(byte *record) return; } - /* Updates field with field_type NEXT_NUMBER according to following: - ** if field = 0 change field to the next free key in database. - */ +/* + Updates field with field_type NEXT_NUMBER according to following: + if field = 0 change field to the next free key in database. +*/ void handler::update_auto_increment() { @@ -811,22 +777,6 @@ int handler::rename_table(const char * from, const char * to) DBUG_RETURN(0); } -int ha_commit_rename(THD *thd) -{ - int error=0; -#ifdef HAVE_GEMINI_DB - /* Gemini needs to commit the rename; otherwise a rollback will change - ** the table names back internally but the physical files will still - ** have the new names. - */ - if (ha_commit_stmt(thd)) - error= -1; - if (ha_commit(thd)) - error= -1; -#endif - return error; -} - /* Tell the handler to turn on or off logging to the handler's recovery log */ @@ -835,14 +785,6 @@ int ha_recovery_logging(THD *thd, bool on) int error=0; DBUG_ENTER("ha_recovery_logging"); -#ifdef USING_TRANSACTIONS - if (opt_using_transactions) - { -#ifdef HAVE_GEMINI_DB - error = gemini_recovery_logging(thd, on); -#endif - } -#endif DBUG_RETURN(error); } @@ -862,8 +804,10 @@ int handler::index_next_same(byte *buf, const byte *key, uint keylen) /* - The following is only needed if we would like to use the database - for internal temporary tables + This is called to delete all rows in a table + If the handler don't support this, then this function will + return HA_ERR_WRONG_COMMAND and MySQL will delete the rows one + by one. */ int handler::delete_all_rows() @@ -891,19 +835,21 @@ int ha_create_table(const char *name, HA_CREATE_INFO *create_info, if (update_create_info) { update_create_info_from_table(create_info, &table); - if (table.file->option_flag() & HA_DROP_BEFORE_CREATE) + if (table.file->table_flags() & HA_DROP_BEFORE_CREATE) table.file->delete_table(name); // Needed for BDB tables } error=table.file->create(name,&table,create_info); VOID(closefrm(&table)); - if (error) { - if (table.db_type == DB_TYPE_INNOBASE) { + if (error) + { + if (table.db_type == DB_TYPE_INNODB) + { /* Creation of InnoDB table cannot fail because of an OS error: put error as the number */ my_error(ER_CANT_CREATE_TABLE,MYF(ME_BELL+ME_WAITTANG),name,error); - } else { - my_error(ER_CANT_CREATE_TABLE,MYF(ME_BELL+ME_WAITTANG),name,my_errno); } + else + my_error(ER_CANT_CREATE_TABLE,MYF(ME_BELL+ME_WAITTANG),name,my_errno); } DBUG_RETURN(error != 0); } @@ -921,5 +867,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 89c19993238..668453f8905 100644 --- a/sql/handler.h +++ b/sql/handler.h @@ -21,11 +21,13 @@ #pragma interface /* gcc class implementation */ #endif +#include <ft_global.h> + #ifndef NO_HASH #define NO_HASH /* Not yet implemented */ #endif -#if defined(HAVE_BERKELEY_DB) || defined(HAVE_INNOBASE_DB) || defined(HAVE_GEMINI_DB) +#if defined(HAVE_BERKELEY_DB) || defined(HAVE_INNOBASE_DB) #define USING_TRANSACTIONS #endif @@ -39,30 +41,19 @@ #define HA_ADMIN_INTERNAL_ERROR -4 #define HA_ADMIN_INVALID -5 -/* Bits in bas_flag to show what database can do */ - -#define HA_READ_NEXT 1 /* Read next record with same key */ -#define HA_READ_PREV 2 /* Read prev. record with same key */ -#define HA_READ_ORDER 4 /* Read through record-keys in order */ -#define HA_READ_RND_SAME 8 /* Read RND-record to KEY-record +/* Bits in table_flags() to show what database can do */ +#define HA_READ_RND_SAME 1 /* Read RND-record to KEY-record (To update with RND-read) */ -#define HA_KEYPOS_TO_RNDPOS 16 /* ha_info gives pos to record */ -#define HA_LASTKEY_ORDER 32 /* Next record gives next record - according last record read (even - if database is updated after read) */ -#define HA_REC_NOT_IN_SEQ 64 /* ha_info don't return recnumber; +#define HA_KEYPOS_TO_RNDPOS 2 /* ha_info gives pos to record */ +#define HA_TABLE_SCAN_ON_INDEX 4 /* No separate data/index file */ +#define HA_REC_NOT_IN_SEQ 8 /* ha_info don't return recnumber; It returns a position to ha_r_rnd */ -#define HA_ONLY_WHOLE_INDEX 128 /* Can't use part key searches */ -#define HA_RSAME_NO_INDEX 256 /* RSAME can't restore index */ -#define HA_WRONG_ASCII_ORDER 512 /* Can't use sorting through key */ -#define HA_HAVE_KEY_READ_ONLY 1024 /* Can read only keys (no record) */ -#define HA_READ_NOT_EXACT_KEY 2048 /* Can read record after/before key */ -#define HA_NO_INDEX 4096 /* No index needed for next/prev */ -#define HA_LONGLONG_KEYS 8192 /* Can have longlong as key */ -#define HA_KEY_READ_WRONG_STR 16384 /* keyread returns converted strings */ -#define HA_NULL_KEY 32768 /* One can have keys with NULL */ -#define HA_DUPP_POS 65536 /* ha_position() gives dupp row */ -#define HA_NO_BLOBS 131072 /* Doesn't support blobs */ +#define HA_HAVE_KEY_READ_ONLY 16 /* Can read only keys (no record) */ +#define HA_NO_INDEX 32 /* No index needed for next/prev */ +#define HA_KEY_READ_WRONG_STR 64 /* keyread returns converted strings */ +#define HA_NULL_KEY 128 /* One can have keys with NULL */ +#define HA_DUPP_POS 256 /* ha_position() gives dupp row */ +#define HA_NO_BLOBS 512 /* Doesn't support blobs */ #define HA_BLOB_KEY (HA_NO_BLOBS*2) /* key on blob */ #define HA_AUTO_PART_KEY (HA_BLOB_KEY*2) #define HA_REQUIRE_PRIMARY_KEY (HA_AUTO_PART_KEY*2) @@ -74,26 +65,44 @@ #define HA_NOT_DELETE_WITH_CACHE (HA_NOT_READ_AFTER_KEY*2) #define HA_NO_TEMP_TABLES (HA_NOT_DELETE_WITH_CACHE*2) #define HA_NO_PREFIX_CHAR_KEYS (HA_NO_TEMP_TABLES*2) -#define HA_NO_FULLTEXT_KEY (HA_NO_PREFIX_CHAR_KEYS*2) - - /* Parameters for open() (in register form->filestat) */ - /* HA_GET_INFO does an implicit HA_ABORT_IF_LOCKED */ +#define HA_CAN_FULLTEXT (HA_NO_PREFIX_CHAR_KEYS*2) +#define HA_CAN_SQL_HANDLER (HA_CAN_FULLTEXT*2) +#define HA_NO_AUTO_INCREMENT (HA_CAN_SQL_HANDLER*2) + +/* + Next record gives next record according last record read (even + if database is updated after read). Not used at this point. +*/ +#define HA_LASTKEY_ORDER (HA_NO_AUTO_INCREMENT*2) + + +/* bits in index_flags(index_number) for what you can do with index */ +#define HA_WRONG_ASCII_ORDER 1 /* Can't use sorting through key */ +#define HA_READ_NEXT 2 /* Read next record with same key */ +#define HA_READ_PREV 4 /* Read prev. record with same key */ +#define HA_READ_ORDER 8 /* Read through record-keys in order */ +#define HA_ONLY_WHOLE_INDEX 16 /* Can't use part key searches */ +#define HA_NOT_READ_PREFIX_LAST 32 + +/* + Parameters for open() (in register form->filestat) + HA_GET_INFO does an implicit HA_ABORT_IF_LOCKED +*/ #define HA_OPEN_KEYFILE 1 #define HA_OPEN_RNDFILE 2 #define HA_GET_INDEX 4 #define HA_GET_INFO 8 /* do a ha_info() after open */ #define HA_READ_ONLY 16 /* File opened as readonly */ -#define HA_TRY_READ_ONLY 32 /* Try readonly if can't */ - /* open with read and write */ +/* Try readonly if can't open with read and write */ +#define HA_TRY_READ_ONLY 32 #define HA_WAIT_IF_LOCKED 64 /* Wait if locked on open */ #define HA_ABORT_IF_LOCKED 128 /* skip if locked on open.*/ #define HA_BLOCK_LOCK 256 /* unlock when reading some records */ #define HA_OPEN_TEMPORARY 512 - /* Error on write which is recoverable (Key exist) */ - -#define HA_WRITE_SKIPP 121 /* Duplicate key on write */ + /* Errors on write which is recoverable (Key exist) */ +#define HA_WRITE_SKIP 121 /* Duplicate key on write */ #define HA_READ_CHECK 123 /* Update with is recoverable */ #define HA_CANT_DO_THAT 131 /* Databasehandler can't do it */ @@ -111,23 +120,27 @@ enum db_type { DB_TYPE_UNKNOWN=0,DB_TYPE_DIAB_ISAM=1, DB_TYPE_HASH,DB_TYPE_MISAM,DB_TYPE_PISAM, DB_TYPE_RMS_ISAM, DB_TYPE_HEAP, DB_TYPE_ISAM, DB_TYPE_MRG_ISAM, DB_TYPE_MYISAM, DB_TYPE_MRG_MYISAM, - DB_TYPE_BERKELEY_DB, DB_TYPE_INNOBASE, DB_TYPE_GEMINI, + DB_TYPE_BERKELEY_DB, DB_TYPE_INNODB, DB_TYPE_GEMINI, DB_TYPE_DEFAULT }; -enum row_type { ROW_TYPE_DEFAULT, ROW_TYPE_FIXED, ROW_TYPE_DYNAMIC, - ROW_TYPE_COMPRESSED }; +enum row_type { ROW_TYPE_NOT_USED=-1, ROW_TYPE_DEFAULT, ROW_TYPE_FIXED, + ROW_TYPE_DYNAMIC, ROW_TYPE_COMPRESSED}; /* struct to hold information about the table that should be created */ /* Bits in used_fields */ -#define HA_CREATE_USED_AUTO 1 -#define HA_CREATE_USED_RAID 2 -#define HA_CREATE_USED_UNION 4 +#define HA_CREATE_USED_AUTO 1 +#define HA_CREATE_USED_RAID 2 +#define HA_CREATE_USED_UNION 4 +#define HA_CREATE_USED_INSERT_METHOD 8 +#define HA_CREATE_USED_MIN_ROWS 16 +#define HA_CREATE_USED_MAX_ROWS 32 +#define HA_CREATE_USED_AVG_ROW_LENGTH 64 +#define HA_CREATE_USED_PACK_KEYS 128 typedef struct st_thd_trans { void *bdb_tid; void *innobase_tid; - void *gemini_tid; bool innodb_active_trans; } THD_TRANS; @@ -143,6 +156,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; char *create_statement; uint options; /* OR of HA_CREATE_ options */ uint raid_type,raid_chunks; @@ -150,6 +164,7 @@ typedef struct st_ha_create_information bool if_not_exists; ulong used_fields; SQL_LIST merge_list; + uint merge_insert_method; } HA_CREATE_INFO; @@ -162,14 +177,11 @@ extern ulong myisam_sort_buffer_size; typedef struct st_ha_check_opt { ulong sort_buffer_size; - uint flags; - bool quick; - bool changed_files; - bool optimize; - bool retry_without_quick; + uint flags; /* isam layer flags (e.g. for myisamchk) */ + uint sql_flags; /* sql layer flags - for something myisamchk cannot do */ inline void init() { - flags= 0; quick= optimize= retry_without_quick=0; + flags= sql_flags= 0; sort_buffer_size = myisam_sort_buffer_size; } } HA_CHECK_OPT; @@ -201,7 +213,7 @@ public: time_t check_time; time_t update_time; ulong mean_rec_length; /* physical reclength */ - void *ft_handler; + FT_INFO *ft_handler; bool auto_increment_column_changed; handler(TABLE *table_arg) : table(table_arg),active_index(MAX_REF_PARTS), @@ -227,6 +239,7 @@ public: virtual bool has_transactions(){ return 0;} virtual uint extra_rec_buf_length() { return 0; } virtual ha_rows estimate_number_of_rows() { return records+EXTRA_RECORDS; } + virtual const char *index_type(uint key_number) { return "";} virtual int index_init(uint idx) { active_index=idx; return 0;} virtual int index_end() {return 0; } @@ -238,7 +251,7 @@ public: virtual int update_row(const byte * old_data, byte * new_data)=0; virtual int delete_row(const byte * buf)=0; virtual int index_read(byte * buf, const byte * key, - uint key_len, enum ha_rkey_function find_flag)=0; + uint key_len, enum ha_rkey_function find_flag)=0; virtual int index_read_idx(byte * buf, uint index, const byte * key, uint key_len, enum ha_rkey_function find_flag)=0; virtual int index_next(byte * buf)=0; @@ -246,17 +259,21 @@ public: virtual int index_first(byte * buf)=0; virtual int index_last(byte * buf)=0; virtual int index_next_same(byte *buf, const byte *key, uint keylen); + virtual int index_read_last(byte * buf, const byte * key, uint key_len) + { + return (my_errno=HA_ERR_WRONG_COMMAND); + } virtual int ft_init() { return -1; } - virtual void *ft_init_ext(uint inx,const byte *key, uint keylen, + virtual FT_INFO *ft_init_ext(uint mode,uint inx,const byte *key, uint keylen, bool presort) - { return (void *)NULL; } + { return NULL; } virtual int ft_read(byte *buf) { return -1; } virtual int rnd_init(bool scan=1)=0; virtual int rnd_end() { return 0; } virtual int rnd_next(byte *buf)=0; virtual int rnd_pos(byte * buf, byte *pos)=0; - virtual int rnd_first(byte *buf); + virtual int read_first_row(byte *buf, uint primary_key); virtual int restart_rnd_next(byte *buf, byte *pos); virtual ha_rows records_in_range(int inx, const byte *start_key,uint start_key_len, @@ -281,10 +298,11 @@ public: virtual int optimize(THD* thd,HA_CHECK_OPT* check_opt); virtual int analyze(THD* thd, HA_CHECK_OPT* check_opt); virtual int backup(THD* thd, HA_CHECK_OPT* check_opt); + /* + restore assumes .frm file must exist, and that generate_table() has been + called; It will just copy the data file and run repair. + */ virtual int restore(THD* thd, HA_CHECK_OPT* check_opt); - // assumes .frm file must exist, and you must have already called - // generate_table() - it will just copy the data file and run repair - virtual int dump(THD* thd, int fd = -1) { return ER_DUMP_NOT_IMPLEMENTED; } virtual void deactivate_non_unique_index(ha_rows rows) {} virtual bool activate_all_index(THD *thd) {return 0;} @@ -300,7 +318,11 @@ public: /* The following can be called without an open handler */ virtual const char *table_type() const =0; virtual const char **bas_ext() const =0; - virtual ulong option_flag() const =0; + virtual ulong table_flags(void) const =0; + virtual ulong index_flags(uint idx) const + { + return (HA_READ_NEXT | HA_READ_PREV | HA_READ_ORDER); + } virtual uint max_record_length() const =0; virtual uint max_keys() const =0; virtual uint max_key_parts() const =0; @@ -320,17 +342,6 @@ public: enum thr_lock_type lock_type)=0; }; -#ifdef HAVE_GEMINI_DB -struct st_gemini -{ - void *context; - unsigned long savepoint; - bool needSavepoint; - uint tx_isolation; - uint lock_count; -}; -#endif - /* Some extern variables used with handlers */ extern const char *ha_row_type[]; @@ -342,6 +353,8 @@ extern TYPELIB ha_table_typelib, tx_isolation_typelib; #define ha_commit(thd) (ha_commit_trans((thd), &((thd)->transaction.all))) #define ha_rollback(thd) (ha_rollback_trans((thd), &((thd)->transaction.all))) +#define ha_supports_generate(T) (T != DB_TYPE_INNODB) + handler *get_new_handler(TABLE *table, enum db_type db_type); my_off_t ha_get_ptr(byte *ptr, uint pack_length); void ha_store_ptr(byte *buff, uint pack_length, my_off_t pos); @@ -355,14 +368,11 @@ int ha_delete_table(enum db_type db_type, const char *path); void ha_drop_database(char* path); void ha_key_cache(void); int ha_start_stmt(THD *thd); -int ha_report_binlog_offset_and_commit( - THD *thd, - char *log_file_name, - my_off_t end_offset); +int ha_report_binlog_offset_and_commit(THD *thd, char *log_file_name, + my_off_t end_offset); int ha_commit_trans(THD *thd, THD_TRANS *trans); int ha_rollback_trans(THD *thd, THD_TRANS *trans); int ha_autocommit_or_rollback(THD *thd, int error); void ha_set_spin_retries(uint retries); bool ha_flush_logs(void); -int ha_commit_rename(THD *thd); int ha_recovery_logging(THD *thd, bool on); diff --git a/sql/hash_filo.cc b/sql/hash_filo.cc index 990d2d662d6..b85f8054f10 100644 --- a/sql/hash_filo.cc +++ b/sql/hash_filo.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 */ diff --git a/sql/hash_filo.h b/sql/hash_filo.h index 157c2739add..b8d45f0d3be 100644 --- a/sql/hash_filo.h +++ b/sql/hash_filo.h @@ -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 */ @@ -24,7 +24,7 @@ #define HASH_FILO_H #ifdef __GNUC__ -#pragma interface /* gcc class implementation */ +#pragma interface /* gcc class interface */ #endif class hash_filo_element diff --git a/sql/hostname.cc b/sql/hostname.cc index 21dbd5a2bbe..5359f876522 100644 --- a/sql/hostname.cc +++ b/sql/hostname.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 */ diff --git a/sql/init.cc b/sql/init.cc index e6606b82b7c..df06ddd41ef 100644 --- a/sql/init.cc +++ b/sql/init.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 */ diff --git a/sql/item.cc b/sql/item.cc index 0ce37cdd593..dac10eafafb 100644 --- a/sql/item.cc +++ b/sql/item.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 */ @@ -37,7 +37,7 @@ void item_init(void) Item::Item() { marker=0; - binary=maybe_null=null_value=with_sum_func=0; + binary=maybe_null=null_value=with_sum_func=unsigned_flag=0; name=0; decimals=0; max_length=0; next=current_thd->free_list; // Put in free list @@ -132,6 +132,7 @@ void Item_field::set_field(Field *field_par) table_name=field_par->table_name; field_name=field_par->field_name; binary=field_par->binary(); + unsigned_flag=test(field_par->flags & UNSIGNED_FLAG); } const char *Item_ident::full_name() const @@ -247,6 +248,22 @@ void Item_int::print(String *str) str->append(name); } +String *Item_uint::val_str(String *str) +{ + str->set((ulonglong) value); + return str; +} + +void Item_uint::print(String *str) +{ + if (!name) + { + str_value.set((ulonglong) value); + name=str_value.c_ptr(); + } + str->append(name); +} + String *Item_real::val_str(String *str) { @@ -299,13 +316,21 @@ bool Item::fix_fields(THD *thd, bool Item_field::fix_fields(THD *thd,TABLE_LIST *tables) { - if (!field) + if (!field) // If field is not checked { Field *tmp; if (!(tmp=find_field_in_tables(thd,this,tables))) return 1; set_field(tmp); } + else if (thd && thd->set_query_id && field->query_id != thd->query_id) + { + /* We only come here in unions */ + TABLE *table=field->table; + field->query_id=thd->query_id; + table->used_fields++; + table->used_keys&=field->part_of_key; + } return 0; } @@ -319,6 +344,8 @@ void Item::init_make_field(Send_field *tmp_field, tmp_field->type=field_type; tmp_field->length=max_length; tmp_field->decimals=decimals; + if (unsigned_flag) + tmp_field->flags |= UNSIGNED_FLAG; } /* ARGSUSED */ @@ -334,6 +361,13 @@ void Item_int::make_field(Send_field *tmp_field) init_make_field(tmp_field,FIELD_TYPE_LONGLONG); } +void Item_uint::make_field(Send_field *tmp_field) +{ + init_make_field(tmp_field,FIELD_TYPE_LONGLONG); + tmp_field->flags|= UNSIGNED_FLAG; + unsigned_flag=1; +} + void Item_real::make_field(Send_field *tmp_field) { init_make_field(tmp_field,FIELD_TYPE_DOUBLE); @@ -552,19 +586,19 @@ void Item_varbinary::make_field(Send_field *tmp_field) ** pack data in buffer for sending */ -bool Item::send(String *packet) +bool Item::send(THD *thd, String *packet) { char buff[MAX_FIELD_WIDTH]; + CONVERT *convert; String s(buff,sizeof(buff)),*res; if (!(res=val_str(&s))) return net_store_null(packet); - CONVERT *convert; - if ((convert=current_thd->convert_set)) + if ((convert=thd->convert_set)) return convert->store(packet,res->ptr(),res->length()); return net_store_data(packet,res->ptr(),res->length()); } -bool Item_null::send(String *packet) +bool Item_null::send(THD *thd, String *packet) { return net_store_null(packet); } @@ -578,7 +612,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; @@ -694,5 +728,6 @@ bool field_is_equal_to_item(Field *field,Item *item) #ifdef __GNUC__ template class List<Item>; template class List_iterator<Item>; +template class List_iterator_fast<Item>; template class List<List_item>; #endif diff --git a/sql/item.h b/sql/item.h index b8903756027..1ce29748d2e 100644 --- a/sql/item.h +++ b/sql/item.h @@ -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 */ @@ -23,7 +23,7 @@ struct st_table_list; void item_init(void); /* Init item functions */ class Item { - Item(const Item &); /* Prevent use of theese */ + Item(const Item &); /* Prevent use of these */ void operator=(Item &); public: static void *operator new(size_t size) {return (void*) sql_alloc((uint) size); } @@ -43,6 +43,7 @@ public: my_bool maybe_null; /* If item may be null */ my_bool null_value; /* if item is null */ my_bool binary; + my_bool unsigned_flag; my_bool with_sum_func; @@ -55,7 +56,7 @@ public: virtual bool save_in_field(Field *field); virtual void save_org_in_field(Field *field) { (void) save_in_field(field); } - virtual bool send(String *str); + virtual bool send(THD *thd, String *str); virtual bool eq(const Item *, bool binary_cmp) const; virtual Item_result result_type () const { return REAL_RESULT; } virtual enum Type type() const =0; @@ -63,7 +64,7 @@ public: virtual longlong val_int()=0; virtual String *val_str(String*)=0; virtual void make_field(Send_field *field)=0; - virtual Field *tmp_table_field() { return 0; } + virtual Field *tmp_table_field(TABLE *t_arg=(TABLE *)0) { return 0; } virtual const char *full_name() const { return name ? name : "???"; } virtual double val_result() { return val(); } virtual longlong val_int_result() { return val_int(); } @@ -80,6 +81,7 @@ public: virtual void split_sum_func(List<Item> &fields) {} virtual bool get_date(TIME *ltime,bool fuzzydate); virtual bool get_time(TIME *ltime); + virtual bool is_null() { return 0; } }; @@ -116,7 +118,7 @@ public: double val_result(); longlong val_int_result(); String *str_result(String* tmp); - bool send(String *str_arg) { return result_field->send(str_arg); } + bool send(THD *thd, String *str_arg) { return result_field->send(thd,str_arg); } void make_field(Send_field *field); bool fix_fields(THD *,struct st_table_list *); bool save_in_field(Field *field); @@ -126,9 +128,10 @@ public: { return field->result_type(); } - Field *tmp_table_field() { return result_field; } + Field *tmp_table_field(TABLE *t_arg=(TABLE *)0) { return result_field; } bool get_date(TIME *ltime,bool fuzzydate); bool get_time(TIME *ltime); + bool is_null() { return field->is_null(); } }; @@ -146,9 +149,10 @@ public: bool save_in_field(Field *field); enum Item_result result_type () const { return STRING_RESULT; } - bool send(String *str); + bool send(THD *thd, String *str); bool basic_const_item() const { return 1; } Item *new_item() { return new Item_null(name); } + bool is_null() { return 1; } }; @@ -181,6 +185,19 @@ public: }; +class Item_uint :public Item_int +{ +public: + Item_uint(const char *str_arg, uint length) : + Item_int(str_arg, (longlong) strtoull(str_arg,(char**) 0,10), length) {} + double val() { return ulonglong2double(value); } + String *val_str(String*); + void make_field(Send_field *field); + Item *new_item() { return new Item_uint(name,max_length); } + void print(String *str); +}; + + class Item_real :public Item { public: @@ -189,14 +206,14 @@ public: Item_real(const char *str_arg,uint length) :value(atof(str_arg)) { name=(char*) str_arg; - decimals=nr_of_decimals(str_arg); + decimals=(uint8) nr_of_decimals(str_arg); max_length=length; } Item_real(const char *str,double val_arg,uint decimal_par,uint length) :value(val_arg) { name=(char*) str; - decimals=decimal_par; + decimals=(uint8) decimal_par; max_length=length; } Item_real(double value_par) :value(value_par) {} @@ -292,7 +309,7 @@ public: Field *result_field; /* Save result here */ Item_result_field() :result_field(0) {} ~Item_result_field() {} /* Required with gcc 2.95 */ - Field *tmp_table_field() { return result_field; } + Field *tmp_table_field(TABLE *t_arg=(TABLE *)0) { return result_field; } table_map used_tables() const { return 1; } virtual void fix_length_and_dec()=0; }; @@ -328,11 +345,16 @@ public: null_value=(*ref)->null_value; return tmp; } + bool is_null() + { + (void) (*ref)->val_int_result(); + return (*ref)->null_value; + } bool get_date(TIME *ltime,bool fuzzydate) { return (null_value=(*ref)->get_date(ltime,fuzzydate)); } - bool send(String *tmp) { return (*ref)->send(tmp); } + bool send(THD *thd, String *tmp) { return (*ref)->send(thd, tmp); } void make_field(Send_field *field) { (*ref)->make_field(field); } bool fix_fields(THD *,struct st_table_list *); bool save_in_field(Field *field) { return (*ref)->save_in_field(field); } @@ -391,6 +413,7 @@ public: void copy(); table_map used_tables() const { return (table_map) 1L; } bool const_item() const { return 0; } + bool is_null() { return null_value; } }; diff --git a/sql/item_buff.cc b/sql/item_buff.cc index 61e1f5498a9..b55a4dc66a0 100644 --- a/sql/item_buff.cc +++ b/sql/item_buff.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 */ diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index ae50090fea1..09c2bdc593a 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.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 */ @@ -256,7 +256,7 @@ longlong Item_func_strcmp::val_int() null_value=1; return 0; } - int value=stringcmp(a,b); + int value= binary ? stringcmp(a,b) : sortcmp(a,b); null_value=0; return !value ? 0 : (value < 0 ? (longlong) -1 : (longlong) 1); } @@ -797,8 +797,9 @@ String *Item_func_coalesce::val_str(String *str) null_value=0; for (uint i=0 ; i < arg_count ; i++) { - if (args[i]->val_str(str) != NULL) - return args[i]->val_str(str); + String *res; + if ((res=args[i]->val_str(str))) + return res; } null_value=1; return 0; @@ -962,7 +963,7 @@ void Item_func_in::fix_length_and_dec() for (uint i=0 ; i < arg_count ; i++) { array->set(j,args[i]); - if (!args[i]->null_value) // Skipp NULL values + if (!args[i]->null_value) // Skip NULL values j++; } if ((array->used_count=j)) @@ -1136,7 +1137,7 @@ void Item_cond::update_used_tables() { used_tables_cache=0; const_item_cache=1; - List_iterator<Item> li(list); + List_iterator_fast<Item> li(list); Item *item; while ((item=li++)) { @@ -1150,7 +1151,7 @@ void Item_cond::update_used_tables() void Item_cond::print(String *str) { str->append('('); - List_iterator<Item> li(list); + List_iterator_fast<Item> li(list); Item *item; if ((item=li++)) item->print(str); @@ -1167,7 +1168,7 @@ void Item_cond::print(String *str) longlong Item_cond_and::val_int() { - List_iterator<Item> li(list); + List_iterator_fast<Item> li(list); Item *item; while ((item=li++)) { @@ -1188,7 +1189,7 @@ longlong Item_cond_and::val_int() longlong Item_cond_or::val_int() { - List_iterator<Item> li(list); + List_iterator_fast<Item> li(list); Item *item; null_value=0; while ((item=li++)) @@ -1212,14 +1213,12 @@ longlong Item_func_isnull::val_int() */ if (!used_tables_cache) return cached_value; - (void) args[0]->val(); - return (args[0]->null_value) ? 1 : 0; + return args[0]->is_null() ? 1: 0; } longlong Item_func_isnotnull::val_int() { - (void) args[0]->val(); - return !(args[0]->null_value) ? 1 : 0; + return args[0]->is_null() ? 0 : 1; } diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h index e7c598808e8..2048c4baea8 100644 --- a/sql/item_cmpfunc.h +++ b/sql/item_cmpfunc.h @@ -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 */ @@ -46,6 +46,7 @@ public: virtual enum Functype rev_functype() const { return UNKNOWN_FUNC; } bool have_rev_func() const { return rev_functype() != UNKNOWN_FUNC; } void print(String *str) { Item_func::print_op(str); } + bool is_null() { return test(args[0]->is_null() || args[1]->is_null()); } }; @@ -466,7 +467,10 @@ public: Item_func_isnotnull(Item *a) :Item_bool_func(a) {} longlong val_int(); enum Functype functype() const { return ISNOTNULL_FUNC; } - void fix_length_and_dec() { decimals=0; max_length=1; maybe_null=0; } + void fix_length_and_dec() + { + decimals=0; max_length=1; maybe_null=0; + } const char *func_name() const { return "isnotnull"; } optimize_type select_optimize() const { return OPTIMIZE_NULL; } }; diff --git a/sql/item_create.cc b/sql/item_create.cc index ef9f5f2d38b..6f64e9517ba 100644 --- a/sql/item_create.cc +++ b/sql/item_create.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 */ @@ -65,7 +65,9 @@ Item *create_func_ceiling(Item* a) Item *create_func_connection_id(void) { - return new Item_int("CONNECTION_ID()",(longlong) current_thd->thread_id,10); + THD *thd=current_thd; + thd->safe_to_cache_query=0; + return new Item_int("CONNECTION_ID()",(longlong) thd->thread_id,10); } Item *create_func_conv(Item* a, Item *b, Item *c) @@ -129,6 +131,13 @@ Item *create_func_floor(Item* a) return new Item_func_floor(a); } +Item *create_func_found_rows(void) +{ + THD *thd=current_thd; + thd->safe_to_cache_query=0; + return new Item_int("FOUND_ROWS()",(longlong) thd->found_rows(),21); +} + Item *create_func_from_days(Item* a) { return new Item_func_from_days(a); @@ -136,13 +145,13 @@ Item *create_func_from_days(Item* a) Item *create_func_get_lock(Item* a, Item *b) { + current_thd->safe_to_cache_query=0; return new Item_func_get_lock(a, b); } Item *create_func_hex(Item *a) { - return new Item_func_conv(a,new Item_int((int32) 10,2), - new Item_int((int32) 16,2)); + return new Item_func_hex(a); } Item *create_func_inet_ntoa(Item* a) @@ -191,6 +200,11 @@ Item *create_func_length(Item* a) return new Item_func_length(a); } +Item *create_func_bit_length(Item* a) +{ + return new Item_func_bit_length(a); +} + Item *create_func_char_length(Item* a) { return new Item_func_char_length(a); @@ -274,6 +288,7 @@ Item *create_func_radians(Item *a) Item *create_func_release_lock(Item* a) { + current_thd->safe_to_cache_query=0; return new Item_func_release_lock(a); } @@ -374,10 +389,27 @@ Item *create_func_year(Item* a) Item *create_load_file(Item* a) { + current_thd->safe_to_cache_query=0; return new Item_load_file(a); } Item *create_wait_for_master_pos(Item* a, Item* b) { + current_thd->safe_to_cache_query=0; return new Item_master_pos_wait(a, b); } + +Item *create_func_cast(Item *a, Item_cast cast_type) +{ + Item *res; + LINT_INIT(res); + switch (cast_type) { + case ITEM_CAST_BINARY: res= new Item_func_binary(a); break; + case ITEM_CAST_SIGNED_INT: res= new Item_func_signed(a); break; + case ITEM_CAST_UNSIGNED_INT: res= new Item_func_unsigned(a); break; + case ITEM_CAST_DATE: res= new Item_date_typecast(a); break; + case ITEM_CAST_TIME: res= new Item_time_typecast(a); break; + case ITEM_CAST_DATETIME: res= new Item_datetime_typecast(a); break; + } + return res; +} diff --git a/sql/item_create.h b/sql/item_create.h index cc7497b0183..580596505da 100644 --- a/sql/item_create.h +++ b/sql/item_create.h @@ -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 */ @@ -22,6 +22,7 @@ Item *create_func_ascii(Item* a); Item *create_func_asin(Item* a); Item *create_func_bin(Item* a); Item *create_func_bit_count(Item* a); +Item *create_func_bit_length(Item* a); Item *create_func_ceiling(Item* a); Item *create_func_char_length(Item* a); Item *create_func_connection_id(void); @@ -37,6 +38,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_func.cc b/sql/item_func.cc index 9180cccabcf..cffa92919ba 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -43,7 +43,7 @@ Item_func::Item_func(List<Item> &list) if ((args=(Item**) sql_alloc(sizeof(Item*)*arg_count))) { uint i=0; - List_iterator<Item> li(list); + List_iterator_fast<Item> li(list); Item *item; while ((item=li++)) @@ -184,8 +184,10 @@ String *Item_num_func::val_str(String *str) longlong nr=val_int(); if (null_value) return 0; /* purecov: inspected */ - else + else if (!unsigned_flag) str->set(nr); + else + str->set((ulonglong) nr); } else { @@ -207,24 +209,31 @@ void Item_func::fix_num_length_and_dec() max_length=float_length(decimals); } - String *Item_int_func::val_str(String *str) { longlong nr=val_int(); if (null_value) return 0; - else + else if (!unsigned_flag) str->set(nr); + else + str->set((ulonglong) nr); return str; } -/* Change from REAL_RESULT (default) to INT_RESULT if both arguments are integers */ +/* + Change from REAL_RESULT (default) to INT_RESULT if both arguments are + integers +*/ void Item_num_op::find_num_type(void) { if (args[0]->result_type() == INT_RESULT && args[1]->result_type() == INT_RESULT) + { hybrid_type=INT_RESULT; + unsigned_flag=args[0]->unsigned_flag | args[1]->unsigned_flag; + } } String *Item_num_op::val_str(String *str) @@ -234,8 +243,10 @@ String *Item_num_op::val_str(String *str) longlong nr=val_int(); if (null_value) return 0; /* purecov: inspected */ - else + else if (!unsigned_flag) str->set(nr); + else + str->set((ulonglong) nr); } else { @@ -269,6 +280,21 @@ longlong Item_func_plus::val_int() return (longlong) Item_func_plus::val(); } + +/* + The following function is here to allow the user to force + subtraction of UNSIGNED BIGINT to return negative values. +*/ + +void Item_func_minus::fix_length_and_dec() +{ + Item_num_op::fix_length_and_dec(); + if (unsigned_flag && + (current_thd->sql_mode & MODE_NO_UNSIGNED_SUBTRACTION)) + unsigned_flag=0; +} + + double Item_func_minus::val() { double value=args[0]->val() - args[1]->val(); @@ -610,8 +636,9 @@ double Item_func_rand::val() { if (arg_count) { // Only use argument once in query - ulong tmp=((ulong) args[0]->val_int())+55555555L; - randominit(¤t_thd->rand,tmp,tmp/2); + uint32 tmp= (uint32) (args[0]->val_int()); + randominit(¤t_thd->rand,(uint32) (tmp*0x10001L+55555555L), + (uint32) (tmp*0x10000001L)); #ifdef DELETE_ITEMS delete args[0]; #endif @@ -667,8 +694,10 @@ String *Item_func_min_max::val_str(String *str) longlong nr=val_int(); if (null_value) return 0; - else + else if (!unsigned_flag) str->set(nr); + else + str->set((ulonglong) nr); return str; } case REAL_RESULT: @@ -784,9 +813,7 @@ longlong Item_func_locate::val_int() { String *a=args[0]->val_str(&value1); String *b=args[1]->val_str(&value2); -#ifdef USE_MB bool binary_str = args[0]->binary || args[1]->binary; -#endif if (!a || !b) { null_value=1; @@ -840,7 +867,8 @@ longlong Item_func_locate::val_int() return 0; } #endif /* USE_MB */ - return (longlong) (a->strstr(*b,start)+1) ; + return (longlong) (binary ? a->strstr(*b,start) : + (a->strstr_case(*b,start)))+1; } @@ -1050,7 +1078,8 @@ udf_handler::~udf_handler() } free_udf(u_d); } - delete [] buffers; + if (buffers) // Because of bug in ecc + delete [] buffers; } @@ -1306,8 +1335,10 @@ String *Item_func_udf_int::val_str(String *str) longlong nr=val_int(); if (null_value) return 0; - else + else if (!unsigned_flag) str->set(nr); + else + str->set((ulonglong) nr); return str; } @@ -1402,19 +1433,19 @@ void item_user_lock_release(ULL *ull) if (mysql_bin_log.is_open()) { THD *thd = current_thd; - int save_errno; + uint save_query_length; char buf[256]; String tmp(buf,sizeof(buf)); tmp.length(0); tmp.append("DO RELEASE_LOCK(\""); tmp.append(ull->key,ull->key_length); tmp.append("\")"); - save_errno=thd->net.last_errno; - thd->net.last_errno=0; + save_query_length=thd->query_length; thd->query_length=tmp.length(); Query_log_event qev(thd,tmp.ptr()); + qev.error_code=0; // this query is always safe to run on slave mysql_bin_log.write(&qev); - thd->net.last_errno=save_errno; + thd->query_length=save_query_length; } if (--ull->count) pthread_cond_signal(&ull->cond); @@ -1432,7 +1463,7 @@ longlong Item_master_pos_wait::val_int() THD* thd = current_thd; String *log_name = args[0]->val_str(&value); int event_count; - + null_value=0; if (thd->slave_thread || !log_name || !log_name->length()) { @@ -1440,14 +1471,83 @@ longlong Item_master_pos_wait::val_int() return 0; } ulong pos = (ulong)args[1]->val_int(); - if ((event_count = glob_mi.wait_for_pos(thd, log_name, pos)) == -1) + LOCK_ACTIVE_MI; + if ((event_count = active_mi->rli.wait_for_pos(thd, log_name, pos)) == -1) { null_value = 1; event_count=0; } + UNLOCK_ACTIVE_MI; return event_count; } +#ifdef EXTRA_DEBUG +void debug_sync_point(const char* lock_name, uint lock_timeout) +{ + THD* thd=current_thd; + ULL* ull; + struct timespec abstime; + int lock_name_len,error=0; + lock_name_len=strlen(lock_name); + pthread_mutex_lock(&LOCK_user_locks); + + if (thd->ull) + { + item_user_lock_release(thd->ull); + thd->ull=0; + } + + /* if the lock has not been aquired by some client, we do not want to + create an entry for it, since we immediately release the lock. In + this case, we will not be waiting, but rather, just waste CPU and + memory on the whole deal + */ + if (!(ull= ((ULL*) hash_search(&hash_user_locks,lock_name, + lock_name_len)))) + { + pthread_mutex_unlock(&LOCK_user_locks); + return; + } + ull->count++; + + /* structure is now initialized. Try to get the lock */ + /* Set up control struct to allow others to abort locks */ + thd->proc_info="User lock"; + thd->mysys_var->current_mutex= &LOCK_user_locks; + thd->mysys_var->current_cond= &ull->cond; + + set_timespec(abstime,lock_timeout); + while (!thd->killed && + (error=pthread_cond_timedwait(&ull->cond,&LOCK_user_locks,&abstime)) + != ETIME && error != ETIMEDOUT && ull->locked) ; + if (ull->locked) + { + if (!--ull->count) + delete ull; // Should never happen + } + else + { + ull->locked=1; + ull->thread=thd->real_id; + thd->ull=ull; + } + pthread_mutex_unlock(&LOCK_user_locks); + pthread_mutex_lock(&thd->mysys_var->mutex); + thd->proc_info=0; + thd->mysys_var->current_mutex= 0; + thd->mysys_var->current_cond= 0; + pthread_mutex_unlock(&thd->mysys_var->mutex); + pthread_mutex_lock(&LOCK_user_locks); + if (thd->ull) + { + item_user_lock_release(thd->ull); + thd->ull=0; + } + pthread_mutex_unlock(&LOCK_user_locks); +} + +#endif + /* Get a user level lock. If the thread has an old lock this is first released. Returns 1: Got lock @@ -1504,14 +1604,7 @@ longlong Item_func_get_lock::val_int() thd->mysys_var->current_mutex= &LOCK_user_locks; thd->mysys_var->current_cond= &ull->cond; -#ifdef HAVE_TIMESPEC_TS_SEC - abstime.ts_sec=time((time_t*) 0)+(time_t) timeout; - abstime.ts_nsec=0; -#else - abstime.tv_sec=time((time_t*) 0)+(time_t) timeout; - abstime.tv_nsec=0; -#endif - + set_timespec(abstime,timeout); while (!thd->killed && (error=pthread_cond_timedwait(&ull->cond,&LOCK_user_locks,&abstime)) != ETIME && error != ETIMEDOUT && error != EINVAL && ull->locked) ; @@ -1941,49 +2034,14 @@ err: return 0; } -double Item_func_match::val() -{ - if (ft_handler==NULL) - return -1.0; - - if (join_key) - { - if (table->file->ft_handler) - return ft_get_relevance(ft_handler); - - join_key=0; // Magic here ! See ha_myisam::ft_read() - } - - /* we'll have to find ft_relevance manually in ft_handler array */ - - int a,b,c; - FT_DOC *docs=ft_handler->doc; - my_off_t docid=table->file->row_position(); - - if ((null_value=(docid==HA_OFFSET_ERROR))) - return 0.0; - - // Assuming docs[] is sorted by dpos... - - for (a=0, b=ft_handler->ndocs, c=(a+b)/2; b-a>1; c=(a+b)/2) - { - if (docs[c].dpos > docid) - b=c; - else - a=c; - } - if (docs[a].dpos == docid) - return docs[a].weight; - else - return 0.0; - -} - void Item_func_match::init_search(bool no_order) { if (ft_handler) return; + if (key == NO_SUCH_KEY) + concat=new Item_func_concat_ws (new Item_string(" ",1), fields); + if (master) { join_key=master->join_key=join_key|master->join_key; @@ -1997,16 +2055,17 @@ void Item_func_match::init_search(bool no_order) char tmp1[FT_QUERY_MAXLEN]; String tmp2(tmp1,sizeof(tmp1)); - // MATCH ... AGAINST (NULL) is meaningless, but possible + // MATCH ... AGAINST (NULL) is meaningless, but possible if (!(ft_tmp=key_item()->val_str(&tmp2))) { ft_tmp=&tmp2; tmp2.set("",0); } - ft_handler=(FT_DOCLIST *) - table->file->ft_init_ext(key, (byte*) ft_tmp->ptr(), ft_tmp->length(), - join_key && !no_order); + ft_handler=table->file->ft_init_ext(mode, key, + (byte*) ft_tmp->ptr(), + ft_tmp->length(), + join_key && !no_order); if (join_key) { @@ -2023,12 +2082,11 @@ bool Item_func_match::fix_fields(THD *thd,struct st_table_list *tlist) maybe_null=1; join_key=0; - /* Serg: - I'd rather say now that const_item is assumed in quite a bit of - places, so it would be difficult to remove; If it would ever to be - removed, this should include modifications to find_best and auto_close - as complement to auto_init code above. - */ + /* const_item is assumed in quite a bit of places, so it would be difficult + to remove; If it would ever to be removed, this should include + modifications to find_best and auto_close as complement to auto_init code + above. + */ if (Item_func::fix_fields(thd,tlist) || !const_item()) { my_error(ER_WRONG_ARGUMENTS,MYF(0),"AGAINST"); @@ -2042,30 +2100,34 @@ bool Item_func_match::fix_fields(THD *thd,struct st_table_list *tlist) if (item->type() == Item::REF_ITEM) li.replace(item= *((Item_ref *)item)->ref); if (item->type() != Item::FIELD_ITEM || !item->used_tables()) - { - my_error(ER_WRONG_ARGUMENTS,MYF(0),"MATCH"); - return 1; - } + key=NO_SUCH_KEY; used_tables_cache|=item->used_tables(); } /* check that all columns come from the same table */ if (count_bits(used_tables_cache) != 1) + key=NO_SUCH_KEY; + const_item_cache=0; + table=((Item_field *)fields.head())->field->table; + table->fulltext_searched=1; + record=table->record[0]; + if (key == NO_SUCH_KEY && mode != FT_BOOL) { my_error(ER_WRONG_ARGUMENTS,MYF(0),"MATCH"); return 1; } - const_item_cache=0; - table=((Item_field *)fields.head())->field->table; - table->fulltext_searched=1; + return 0; } - bool Item_func_match::fix_index() { - List_iterator<Item> li(fields); + List_iterator_fast<Item> li(fields); Item_field *item; uint ft_to_key[MAX_KEY], ft_cnt[MAX_KEY], fts=0, key; + uint max_cnt=0, mkeys=0; + + if (this->key == NO_SUCH_KEY) + return 0; for (key=0 ; key<table->keys ; key++) { @@ -2079,11 +2141,7 @@ bool Item_func_match::fix_index() } if (!fts) - { - my_printf_error(ER_FT_MATCHING_KEY_NOT_FOUND, - ER(ER_FT_MATCHING_KEY_NOT_FOUND),MYF(0)); - return 1; - } + goto err; while ((item=(Item_field*)(li++))) { @@ -2100,7 +2158,6 @@ bool Item_func_match::fix_index() } } - uint max_cnt=0, mkeys=0; for (key=0 ; key<fts ; key++) { if (ft_cnt[key] > max_cnt) @@ -2131,6 +2188,12 @@ bool Item_func_match::fix_index() return 0; } +err: + if (mode == FT_BOOL) + { + this->key=NO_SUCH_KEY; + return 0; + } my_printf_error(ER_FT_MATCHING_KEY_NOT_FOUND, ER(ER_FT_MATCHING_KEY_NOT_FOUND),MYF(0)); return 1; @@ -2153,6 +2216,30 @@ bool Item_func_match::eq(const Item *item, bool binary_cmp) const return 0; } +double Item_func_match::val() +{ + if (ft_handler == NULL) + return -1.0; + + if (join_key) + { + if (table->file->ft_handler) + return ft_handler->please->get_relevance(ft_handler); + + join_key=0; + } + + if (key == NO_SUCH_KEY) + { + String *a=concat->val_str(&value); + if ((null_value= (a==0))) + return 0; + return ft_handler->please->find_relevance(ft_handler, + (byte *)a->ptr(), a->length()); + } + else + return ft_handler->please->find_relevance(ft_handler, record, 0); +} /*************************************************************************** System variables diff --git a/sql/item_func.h b/sql/item_func.h index 1d85973055b..c3e437712ee 100644 --- a/sql/item_func.h +++ b/sql/item_func.h @@ -119,6 +119,7 @@ public: { return (null_value=args[0]->get_time(ltime)); } + bool is_null() { (void) val_int(); return null_value; } friend class udf_handler; }; @@ -134,6 +135,11 @@ public: longlong val_int() { return (longlong) val(); } enum Item_result result_type () const { return REAL_RESULT; } void fix_length_and_dec() { decimals=NOT_FIXED_DEC; max_length=float_length(decimals); } + Field *tmp_table_field(TABLE *t_arg) + { + if (!t_arg) return result_field; + return new Field_double(max_length, maybe_null, name,t_arg,decimals); + } }; class Item_num_func :public Item_func @@ -147,6 +153,7 @@ public: longlong val_int() { return (longlong) val(); } enum Item_result result_type () const { return hybrid_type; } void fix_length_and_dec() { fix_num_length_and_dec(); } + bool is_null() { (void) val(); return null_value; } }; @@ -161,23 +168,55 @@ class Item_num_op :public Item_func enum Item_result result_type () const { return hybrid_type; } void fix_length_and_dec() { fix_num_length_and_dec(); find_num_type(); } void find_num_type(void); + bool is_null() { (void) val(); return null_value; } + Field *tmp_table_field(TABLE *t_arg) + { + if (!t_arg) return result_field; + return args[0]->result_type() == INT_RESULT ? ((max_length > 11) ? (Field *)new Field_longlong(max_length,maybe_null,name, t_arg,unsigned_flag) : (Field *)new Field_long(max_length,maybe_null,name, t_arg,unsigned_flag)) : (Field *) new Field_double(max_length, maybe_null, name,t_arg,decimals); + } }; class Item_int_func :public Item_func { public: - Item_int_func() :Item_func() {} - Item_int_func(Item *a) :Item_func(a) {} - Item_int_func(Item *a,Item *b) :Item_func(a,b) {} - Item_int_func(Item *a,Item *b,Item *c) :Item_func(a,b,c) {} - Item_int_func(List<Item> &list) :Item_func(list) {} + Item_int_func() :Item_func() { max_length=21; } + Item_int_func(Item *a) :Item_func(a) { max_length=21; } + Item_int_func(Item *a,Item *b) :Item_func(a,b) { max_length=21; } + Item_int_func(Item *a,Item *b,Item *c) :Item_func(a,b,c) { max_length=21; } + Item_int_func(List<Item> &list) :Item_func(list) { max_length=21; } double val() { return (double) val_int(); } String *val_str(String*str); enum Item_result result_type () const { return INT_RESULT; } - void fix_length_and_dec() { decimals=0; max_length=21; } + void fix_length_and_dec() {} + Field *tmp_table_field(TABLE *t_arg) + { + if (!t_arg) return result_field; + return (max_length > 11) ? (Field *)new Field_longlong(max_length,maybe_null,name, t_arg,unsigned_flag) : (Field *)new Field_long(max_length,maybe_null,name, t_arg,unsigned_flag); + } +}; + +class Item_func_signed :public Item_int_func +{ +public: + Item_func_signed(Item *a) :Item_int_func(a) {} + double val() { return args[0]->val(); } + longlong val_int() { return args[0]->val_int(); } + void fix_length_and_dec() + { max_length=args[0]->max_length; unsigned_flag=0; } +}; + +class Item_func_unsigned :public Item_int_func +{ +public: + Item_func_unsigned(Item *a) :Item_int_func(a) {} + double val() { return args[0]->val(); } + longlong val_int() { return args[0]->val_int(); } + void fix_length_and_dec() + { max_length=args[0]->max_length; unsigned_flag=1; } }; + class Item_func_plus :public Item_num_op { public: @@ -194,8 +233,10 @@ public: const char *func_name() const { return "-"; } double val(); longlong val_int(); + void fix_length_and_dec(); }; + class Item_func_mul :public Item_num_op { public: @@ -483,6 +524,14 @@ public: void fix_length_and_dec() { max_length=10; } }; +class Item_func_bit_length :public Item_func_length +{ +public: + Item_func_bit_length(Item *a) :Item_func_length(a) {} + longlong val_int() { return Item_func_length::val_int()*8; } + const char *func_name() const { return "bit_length"; } +}; + class Item_func_char_length :public Item_int_func { String value; @@ -551,7 +600,6 @@ public: Item_func_ord(Item *a) :Item_int_func(a) {} longlong val_int(); const char *func_name() const { return "ord"; } - void fix_length_and_dec() { max_length=21; } }; class Item_func_find_in_set :public Item_int_func @@ -573,7 +621,7 @@ public: Item_func_bit_or(Item *a,Item *b) :Item_int_func(a,b) {} longlong val_int(); const char *func_name() const { return "|"; } - void fix_length_and_dec() { decimals=0; max_length=21; } + void fix_length_and_dec() { unsigned_flag=1; } }; class Item_func_bit_and :public Item_int_func @@ -582,7 +630,7 @@ public: Item_func_bit_and(Item *a,Item *b) :Item_int_func(a,b) {} longlong val_int(); const char *func_name() const { return "&"; } - void fix_length_and_dec() { decimals=0; max_length=21; } + void fix_length_and_dec() { unsigned_flag=1; } }; class Item_func_bit_count :public Item_int_func @@ -591,7 +639,7 @@ public: Item_func_bit_count(Item *a) :Item_int_func(a) {} longlong val_int(); const char *func_name() const { return "bit_count"; } - void fix_length_and_dec() { decimals=0; max_length=2; } + void fix_length_and_dec() { max_length=2; } }; class Item_func_shift_left :public Item_int_func @@ -600,7 +648,7 @@ public: Item_func_shift_left(Item *a,Item *b) :Item_int_func(a,b) {} longlong val_int(); const char *func_name() const { return "<<"; } - void fix_length_and_dec() { decimals=0; max_length=21; } + void fix_length_and_dec() { unsigned_flag=1; } }; class Item_func_shift_right :public Item_int_func @@ -609,7 +657,6 @@ public: Item_func_shift_right(Item *a,Item *b) :Item_int_func(a,b) {} longlong val_int(); const char *func_name() const { return ">>"; } - void fix_length_and_dec() { decimals=0; max_length=21; } }; class Item_func_bit_neg :public Item_int_func @@ -618,7 +665,7 @@ public: Item_func_bit_neg(Item *a) :Item_int_func(a) {} longlong val_int(); const char *func_name() const { return "~"; } - void fix_length_and_dec() { decimals=0; max_length=21; } + void fix_length_and_dec() { unsigned_flag=1; } }; class Item_func_set_last_insert_id :public Item_int_func @@ -627,7 +674,7 @@ public: Item_func_set_last_insert_id(Item *a) :Item_int_func(a) {} longlong val_int(); const char *func_name() const { return "last_insert_id"; } - void fix_length_and_dec() { decimals=0; max_length=args[0]->max_length; } + void fix_length_and_dec() { max_length=args[0]->max_length; } }; class Item_func_benchmark :public Item_int_func @@ -639,7 +686,7 @@ class Item_func_benchmark :public Item_int_func {} longlong val_int(); const char *func_name() const { return "benchmark"; } - void fix_length_and_dec() { decimals=0; max_length=1; maybe_null=0; } + void fix_length_and_dec() { max_length=1; maybe_null=0; } }; @@ -771,7 +818,7 @@ class Item_func_get_lock :public Item_int_func Item_func_get_lock(Item *a,Item *b) :Item_int_func(a,b) {} longlong val_int(); const char *func_name() const { return "get_lock"; } - void fix_length_and_dec() { decimals=0; max_length=1; maybe_null=1;} + void fix_length_and_dec() { max_length=1; maybe_null=1;} }; class Item_func_release_lock :public Item_int_func @@ -781,7 +828,7 @@ class Item_func_release_lock :public Item_int_func Item_func_release_lock(Item *a) :Item_int_func(a) {} longlong val_int(); const char *func_name() const { return "release_lock"; } - void fix_length_and_dec() { decimals=0; max_length=1; maybe_null=1;} + void fix_length_and_dec() { max_length=1; maybe_null=1;} }; /* replication functions */ @@ -793,7 +840,7 @@ class Item_master_pos_wait :public Item_int_func Item_master_pos_wait(Item *a,Item *b) :Item_int_func(a,b) {} longlong val_int(); const char *func_name() const { return "master_pos_wait"; } - void fix_length_and_dec() { decimals=0; max_length=1; maybe_null=1;} + void fix_length_and_dec() { max_length=1; maybe_null=1;} }; @@ -863,35 +910,63 @@ class Item_func_match :public Item_real_func { public: List<Item> fields; + String value; TABLE *table; - uint key; - bool join_key; Item_func_match *master; - FT_DOCLIST *ft_handler; + FT_INFO * ft_handler; + Item *concat; + byte *record; + uint key, mode; + bool join_key; Item_func_match(List<Item> &a, Item *b): Item_real_func(b), - fields(a), table(0), join_key(0), master(0), ft_handler(0) {} + fields(a), table(0), master(0), ft_handler(0), + concat(0), key(0), join_key(0) {} ~Item_func_match() { - if (!master) + if (!master && ft_handler) { - if (ft_handler) - { - ft_close_search(ft_handler); - if(join_key) - table->file->ft_handler=0; - table->fulltext_searched=0; - } + ft_handler->please->close_search(ft_handler); + ft_handler=0; + if(join_key) + table->file->ft_handler=0; + table->fulltext_searched=0; } + if (concat) delete concat; } - const char *func_name() const { return "match"; } enum Functype functype() const { return FT_FUNC; } void update_used_tables() {} bool fix_fields(THD *thd,struct st_table_list *tlist); bool eq(const Item *, bool binary_cmp) const; - double val(); longlong val_int() { return val()!=0.0; } + double val(); bool fix_index(); void init_search(bool no_order); }; + +class Item_func_match_nl :public Item_func_match +{ +public: + Item_func_match_nl(List<Item> &a, Item *b): + Item_func_match(a,b) { mode=FT_NL; } + const char *func_name() const { return "match_nl"; } +}; + +class Item_func_match_bool :public Item_func_match +{ +public: + Item_func_match_bool(List<Item> &a, Item *b): + Item_func_match(a,b) { mode=FT_BOOL; } + const char *func_name() const { return "match_bool"; } +}; + +/* For type casts */ + +enum Item_cast +{ + ITEM_CAST_BINARY, ITEM_CAST_SIGNED_INT, ITEM_CAST_UNSIGNED_INT, + ITEM_CAST_DATE, ITEM_CAST_TIME, ITEM_CAST_DATETIME +}; + +Item *create_func_cast(Item *a, Item_cast cast_type); diff --git a/sql/item_strfunc.cc b/sql/item_strfunc.cc index 0321d37c0fe..9d24ca19b4c 100644 --- a/sql/item_strfunc.cc +++ b/sql/item_strfunc.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 */ @@ -17,7 +17,7 @@ /* This file defines all string functions ** Warning: Some string functions doesn't always put and end-null on a String -** (This shouldn't be neaded) +** (This shouldn't be needed) */ #ifdef __GNUC__ @@ -30,7 +30,9 @@ #ifdef HAVE_CRYPT_H #include <crypt.h> #endif - +#ifdef HAVE_OPENSSL +#include <openssl/des.h> +#endif /* HAVE_OPENSSL */ #include "md5.h" String empty_string(""); @@ -66,14 +68,18 @@ 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); - str->alloc(32); // Ensure that memory is free + my_MD5Init (&context); + my_MD5Update (&context,(unsigned char *) sptr->ptr(), sptr->length()); + my_MD5Final (digest, &context); + if (str->alloc(32)) // Ensure that memory is free + { + null_value=1; + return 0; + } sprintf((char *) str->ptr(), "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", digest[0], digest[1], digest[2], digest[3], @@ -199,11 +205,167 @@ void Item_func_concat::fix_length_and_dec() } } +/* + Function des_encrypt() by tonu@spam.ee & monty + Works only if compiled with OpenSSL library support. + This returns a binary string where first character is + CHAR(128 | key-number). + If one uses a string key key_number is 127. + Encryption result is longer than original by formula: + new_length= org_length + (8-(org_length % 8))+1 +*/ + +String *Item_func_des_encrypt::val_str(String *str) +{ +#ifdef HAVE_OPENSSL + des_cblock ivec; + struct st_des_keyblock keyblock; + struct st_des_keyschedule keyschedule; + const char *append_str="********"; + uint key_number, res_length, tail; + String *res= args[0]->val_str(str); + + if ((null_value=args[0]->null_value)) + return 0; + if ((res_length=res->length()) == 0) + return &empty_string; + + if (arg_count == 1) + { + /* Protect against someone doing FLUSH DES_KEY_FILE */ + VOID(pthread_mutex_lock(&LOCK_des_key_file)); + keyschedule= des_keyschedule[key_number=des_default_key]; + VOID(pthread_mutex_unlock(&LOCK_des_key_file)); + } + else if (args[1]->result_type() == INT_RESULT) + { + key_number= (uint) args[1]->val_int(); + if (key_number > 9) + goto error; + VOID(pthread_mutex_lock(&LOCK_des_key_file)); + keyschedule= des_keyschedule[key_number]; + VOID(pthread_mutex_unlock(&LOCK_des_key_file)); + } + else + { + String *keystr=args[1]->val_str(&tmp_value); + if (!keystr) + goto error; + key_number=127; // User key string + + /* We make good 24-byte (168 bit) key from given plaintext key with MD5 */ + bzero((char*) &ivec,sizeof(ivec)); + EVP_BytesToKey(EVP_des_ede3_cbc(),EVP_md5(),NULL, + (uchar*) keystr->ptr(), (int) keystr->length(), + 1, (uchar*) &keyblock,ivec); + des_set_key_unchecked(&keyblock.key1,keyschedule.ks1); + des_set_key_unchecked(&keyblock.key2,keyschedule.ks2); + des_set_key_unchecked(&keyblock.key3,keyschedule.ks3); + } + + /* + The problem: DES algorithm requires original data to be in 8-bytes + chunks. Missing bytes get filled with '*'s and result of encryption + can be up to 8 bytes longer than original string. When decrypted, + we do not know the size of original string :( + We add one byte with value 0x1..0x8 as the last byte of the padded + string marking change of string length. + */ + + tail= (8-(res_length) % 8); // 1..8 marking extra length + res_length+=tail; + if (tail && res->append(append_str, tail) || tmp_value.alloc(res_length+1)) + goto error; + (*res)[res_length-1]=tail; // save extra length + tmp_value.length(res_length+1); + tmp_value[0]=(char) (128 | key_number); + // Real encryption + bzero((char*) &ivec,sizeof(ivec)); + des_ede3_cbc_encrypt((const uchar*) (res->ptr()), + (uchar*) (tmp_value.ptr()+1), + res_length, + keyschedule.ks1, + keyschedule.ks2, + keyschedule.ks3, + &ivec, TRUE); + return &tmp_value; + +error: +#endif /* HAVE_OPENSSL */ + null_value=1; + return 0; +} + + +String *Item_func_des_decrypt::val_str(String *str) +{ +#ifdef HAVE_OPENSSL + des_key_schedule ks1, ks2, ks3; + des_cblock ivec; + struct st_des_keyblock keyblock; + struct st_des_keyschedule keyschedule; + String *res= args[0]->val_str(str); + uint length=res->length(),tail; + + if ((null_value=args[0]->null_value)) + return 0; + length=res->length(); + if (length < 9 || (length % 8) != 1 || !((*res)[0] & 128)) + return res; // Skip decryption if not encrypted + + if (arg_count == 1) // If automatic uncompression + { + uint key_number=(uint) (*res)[0] & 127; + // Check if automatic key and that we have privilege to uncompress using it + if (!(current_thd->master_access & PROCESS_ACL) || key_number > 9) + goto error; + VOID(pthread_mutex_lock(&LOCK_des_key_file)); + keyschedule= des_keyschedule[key_number]; + VOID(pthread_mutex_unlock(&LOCK_des_key_file)); + } + else + { + // We make good 24-byte (168 bit) key from given plaintext key with MD5 + String *keystr=args[1]->val_str(&tmp_value); + if (!keystr) + goto error; + + bzero((char*) &ivec,sizeof(ivec)); + EVP_BytesToKey(EVP_des_ede3_cbc(),EVP_md5(),NULL, + (uchar*) keystr->ptr(),(int) keystr->length(), + 1,(uchar*) &keyblock,ivec); + // Here we set all 64-bit keys (56 effective) one by one + des_set_key_unchecked(&keyblock.key1,keyschedule.ks1); + des_set_key_unchecked(&keyblock.key2,keyschedule.ks2); + des_set_key_unchecked(&keyblock.key3,keyschedule.ks3); + } + if (tmp_value.alloc(length-1)) + goto error; + + bzero((char*) &ivec,sizeof(ivec)); + des_ede3_cbc_encrypt((const uchar*) res->ptr()+1, + (uchar*) (tmp_value.ptr()), + length-1, + keyschedule.ks1, + keyschedule.ks2, + keyschedule.ks3, + &ivec, FALSE); + /* Restore old length of key */ + if ((tail=(uint) (uchar) tmp_value[length-2]) > 8) + goto error; // Wrong key + tmp_value.length(length-1-tail); + return &tmp_value; + +error: +#endif /* HAVE_OPENSSL */ + null_value=1; + return 0; +} /* -** concat with separator. First arg is the separator -** concat_ws takes at least two arguments. + concat with separator. First arg is the separator + concat_ws takes at least two arguments. */ String *Item_func_concat_ws::val_str(String *str) @@ -232,7 +394,7 @@ String *Item_func_concat_ws::val_str(String *str) for (i++; i < arg_count ; i++) { if (!(res2= args[i]->val_str(use_as_buff)) || !res2->length()) - continue; // Skipp NULL and empty string + continue; // Skip NULL and empty string if (res->length() + sep_str->length() + res2->length() > max_allowed_packet) @@ -385,7 +547,7 @@ void Item_func_reverse::fix_length_and_dec() /* ** Replace all occurences of string2 in string1 with string3. -** Don't reallocate val_str() if not neaded +** Don't reallocate val_str() if not needed */ /* TODO: Fix that this works with binary strings when using USE_MB */ @@ -521,7 +683,7 @@ String *Item_func_insert::val_str(String *str) } #endif if (start > res->length()+1) - return res; // Wrong param; skipp insert + return res; // Wrong param; skip insert if (length > res->length()-start) length=res->length()-start; if (res->length() - length + res2->length() > max_allowed_packet) @@ -1098,7 +1260,7 @@ void Item_func_soundex::fix_length_and_dec() /* If alpha, map input letter to soundex code. - If not alpha and remove_garbage is set then skipp to next char + If not alpha and remove_garbage is set then skip to next char else return 0 */ @@ -1125,12 +1287,12 @@ String *Item_func_soundex::val_str(String *str) if ((null_value=args[0]->null_value)) return 0; /* purecov: inspected */ - if (str_value.alloc(max(res->length(),4))) + if (tmp_value.alloc(max(res->length(),4))) return str; /* purecov: inspected */ - char *to= (char *) str_value.ptr(); + char *to= (char *) tmp_value.ptr(); char *from= (char *) res->ptr(), *end=from+res->length(); - while (from != end && isspace(*from)) // Skipp pre-space + while (from != end && isspace(*from)) // Skip pre-space from++; /* purecov: inspected */ if (from == end) return &empty_string; // No alpha characters. @@ -1151,11 +1313,11 @@ String *Item_func_soundex::val_str(String *str) last_ch = ch; // save code of last input letter } // for next double-letter check } - for (end=(char*) str_value.ptr()+4 ; to < end ; to++) + for (end=(char*) tmp_value.ptr()+4 ; to < end ; to++) *to = '0'; *to=0; // end string - str_value.length((uint) (to-str_value.ptr())); - return &str_value; + tmp_value.length((uint) (to-tmp_value.ptr())); + return &tmp_value; } @@ -1305,7 +1467,7 @@ String *Item_func_make_set::val_str(String *str) if (bits & 1) { String *res= (*ptr)->val_str(str); - if (res) // Skipp nulls + if (res) // Skip nulls { if (!first_found) { // First argument @@ -1610,6 +1772,45 @@ String *Item_func_conv::val_str(String *str) return str; } + +String *Item_func_hex::val_str(String *str) +{ + if (args[0]->result_type() != STRING_RESULT) + { + /* Return hex of unsigned longlong value */ + longlong dec= args[0]->val_int(); + char ans[65],*ptr; + if ((null_value= args[0]->null_value)) + return 0; + ptr= longlong2str(dec,ans,16); + if (str->copy(ans,(uint32) (ptr-ans))) + return &empty_string; // End of memory + return str; + } + + /* Convert given string to a hex string, character by character */ + String *res= args[0]->val_str(str); + const char *from, *end; + char *to; + if (!res || tmp_value.alloc(res->length()*2)) + { + null_value=1; + return 0; + } + null_value=0; + tmp_value.length(res->length()*2); + for (from=res->ptr(), end=from+res->length(), to= (char*) tmp_value.ptr(); + from != end ; + from++, to+=2) + { + uint tmp=(uint) (uchar) *from; + to[0]=_dig_vec[tmp >> 4]; + to[1]=_dig_vec[tmp & 15]; + } + return &tmp_value; +} + + #include <my_dir.h> // For my_stat String *Item_load_file::val_str(String *str) @@ -1720,24 +1921,23 @@ String* Item_func_inet_ntoa::val_str(String* str) uchar buf[8], *p; ulonglong n = (ulonglong) args[0]->val_int(); char num[4]; + /* - we do not know if args[0] is NULL until we have called + We do not know if args[0] is NULL until we have called some val function on it if args[0] is not a constant! + + Also return null if n > 255.255.255.255 */ - if ((null_value=args[0]->null_value)) + if ((null_value= (args[0]->null_value || n > (ulonglong) LL(4294967295)))) return 0; // Null value str->length(0); - int8store(buf,n); + int4store(buf,n); - /* - Now we can assume little endian. - We handle the possibility of an 8-byte IP address however, we do - not want to confuse those who are just using 4 byte ones - */ - for (p= buf + 8; p > buf+4 && p[-1] == 0 ; p-- ) ; + /* Now we can assume little endian. */ + num[3]='.'; - while (p-- > buf) + for (p=buf+4 ; p-- > buf ; ) { uint c = *p; uint n1,n2; // Try to avoid divisions diff --git a/sql/item_strfunc.h b/sql/item_strfunc.h index 1b829b19439..1279a5099d5 100644 --- a/sql/item_strfunc.h +++ b/sql/item_strfunc.h @@ -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 */ @@ -35,6 +35,11 @@ public: double val(); enum Item_result result_type () const { return STRING_RESULT; } void left_right_max_length(); + Field *tmp_table_field(TABLE *t_arg) + { + if (!t_arg) return result_field; + return (max_length > 255) ? (Field *)new Field_blob(max_length,maybe_null, name,t_arg, binary) : (Field *) new Field_string(max_length,maybe_null, name,t_arg, binary); + } }; class Item_func_md5 :public Item_str_func @@ -222,6 +227,29 @@ public: const char *func_name() const { return "password"; } }; +class Item_func_des_encrypt :public Item_str_func +{ + String tmp_value; +public: + Item_func_des_encrypt(Item *a) :Item_str_func(a) {} + Item_func_des_encrypt(Item *a, Item *b): Item_str_func(a,b) {} + String *val_str(String *); + void fix_length_and_dec() + { maybe_null=1; max_length = args[0]->max_length+8; } + const char *func_name() const { return "des_encrypt"; } +}; + +class Item_func_des_decrypt :public Item_str_func +{ + String tmp_value; +public: + Item_func_des_decrypt(Item *a) :Item_str_func(a) {} + Item_func_des_decrypt(Item *a, Item *b): Item_str_func(a,b) {} + String *val_str(String *); + void fix_length_and_dec() { maybe_null=1; max_length = args[0]->max_length; } + const char *func_name() const { return "des_decrypt"; } +}; + class Item_func_encrypt :public Item_str_func { String tmp_value; @@ -274,6 +302,7 @@ public: class Item_func_soundex :public Item_str_func { + String tmp_value; public: Item_func_soundex(Item *a) :Item_str_func(a) {} String *val_str(String *); @@ -389,12 +418,25 @@ public: void fix_length_and_dec() { decimals=0; max_length=64; } }; + +class Item_func_hex :public Item_str_func +{ + String tmp_value; +public: + Item_func_hex(Item *a) :Item_str_func(a) {} + const char *func_name() const { return "hex"; } + String *val_str(String *); + void fix_length_and_dec() { decimals=0; max_length=args[0]->max_length*2; } +}; + + class Item_func_binary :public Item_str_func { public: Item_func_binary(Item *a) :Item_str_func(a) {} const char *func_name() const { return "binary"; } - String *val_str(String *a) { return (args[0]->val_str(a)); } + String *val_str(String *a) + { a=args[0]->val_str(a); null_value=args[0]->null_value; return a; } void fix_length_and_dec() { binary=1; max_length=args[0]->max_length; } void print(String *str) { print_op(str); } }; diff --git a/sql/item_sum.cc b/sql/item_sum.cc index 5b24a1eda90..e8f16e3ed56 100644 --- a/sql/item_sum.cc +++ b/sql/item_sum.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 */ @@ -30,7 +30,7 @@ Item_sum::Item_sum(List<Item> &list) if ((args=(Item**) sql_alloc(sizeof(Item*)*arg_count))) { uint i=0; - List_iterator<Item> li(list); + List_iterator_fast<Item> li(list); Item *item; while ((item=li++)) @@ -52,6 +52,8 @@ void Item_sum::make_field(Send_field *tmp_field) tmp_field->flags=0; if (!maybe_null) tmp_field->flags|= NOT_NULL_FLAG; + if (unsigned_flag) + tmp_field->flags |= UNSIGNED_FLAG; tmp_field->length=max_length; tmp_field->decimals=decimals; tmp_field->type=(result_type() == INT_RESULT ? FIELD_TYPE_LONG : @@ -150,7 +152,7 @@ Item_sum_hybrid::fix_fields(THD *thd,TABLE_LIST *tables) return 1; hybrid_type=item->result_type(); if (hybrid_type == INT_RESULT) - max_length=21; + max_length=20; else if (hybrid_type == REAL_RESULT) max_length=float_length(decimals); else @@ -158,6 +160,7 @@ Item_sum_hybrid::fix_fields(THD *thd,TABLE_LIST *tables) decimals=item->decimals; maybe_null=item->maybe_null; binary=item->binary; + unsigned_flag=item->unsigned_flag; result_field=0; null_value=1; fix_length_and_dec(); @@ -323,12 +326,27 @@ double Item_sum_hybrid::val() { if (null_value) return 0.0; - if (hybrid_type == STRING_RESULT) - { + switch (hybrid_type) { + case STRING_RESULT: String *res; res=val_str(&str_value); return res ? atof(res->c_ptr()) : 0.0; + case INT_RESULT: + if (unsigned_flag) + return ulonglong2double(sum_int); + return (double) sum_int; + case REAL_RESULT: + return sum; } - return sum; + return 0; // Keep compiler happy +} + +longlong Item_sum_hybrid::val_int() +{ + if (null_value) + return 0; + if (hybrid_type == INT_RESULT) + return sum_int; + return (longlong) Item_sum_hybrid::val(); } @@ -337,25 +355,26 @@ Item_sum_hybrid::val_str(String *str) { if (null_value) return 0; - if (hybrid_type == STRING_RESULT) + switch (hybrid_type) { + case STRING_RESULT: return &value; - str->set(sum,decimals); - return str; + case REAL_RESULT: + str->set(sum,decimals); + break; + case INT_RESULT: + if (unsigned_flag) + str->set((ulonglong) sum_int); + else + str->set((longlong) sum_int); + break; + } + return str; // Keep compiler happy } - bool Item_sum_min::add() { - if (hybrid_type != STRING_RESULT) - { - double nr=args[0]->val(); - if (!args[0]->null_value && (null_value || nr < sum)) - { - sum=nr; - null_value=0; - } - } - else + switch (hybrid_type) { + case STRING_RESULT: { String *result=args[0]->val_str(&tmp_value); if (!args[0]->null_value && @@ -366,22 +385,39 @@ bool Item_sum_min::add() null_value=0; } } - return 0; -} - - -bool Item_sum_max::add() -{ - if (hybrid_type != STRING_RESULT) + break; + case INT_RESULT: + { + longlong nr=args[0]->val_int(); + if (!args[0]->null_value && (null_value || + (unsigned_flag && + (ulonglong) nr < (ulonglong) sum_int) || + (!unsigned_flag && nr < sum_int))) + { + sum_int=nr; + null_value=0; + } + } + break; + case REAL_RESULT: { double nr=args[0]->val(); - if (!args[0]->null_value && (null_value || nr > sum)) + if (!args[0]->null_value && (null_value || nr < sum)) { sum=nr; null_value=0; } } - else + break; + } + return 0; +} + + +bool Item_sum_max::add() +{ + switch (hybrid_type) { + case STRING_RESULT: { String *result=args[0]->val_str(&tmp_value); if (!args[0]->null_value && @@ -392,6 +428,31 @@ bool Item_sum_max::add() null_value=0; } } + break; + case INT_RESULT: + { + longlong nr=args[0]->val_int(); + if (!args[0]->null_value && (null_value || + (unsigned_flag && + (ulonglong) nr > (ulonglong) sum_int) || + (!unsigned_flag && nr > sum_int))) + { + sum_int=nr; + null_value=0; + } + } + break; + case REAL_RESULT: + { + double nr=args[0]->val(); + if (!args[0]->null_value && (null_value || nr > sum)) + { + sum=nr; + null_value=0; + } + } + break; + } return 0; } @@ -676,9 +737,17 @@ Item_sum_hybrid::min_max_update_int_field(int offset) nr=args[0]->val_int(); if (!args[0]->null_value) { - if (result_field->is_null(offset) || - (cmp_sign > 0 ? old_nr > nr : old_nr < nr)) + if (result_field->is_null(offset)) old_nr=nr; + else + { + bool res=(unsigned_flag ? + (ulonglong) old_nr > (ulonglong) nr : + old_nr > nr); + /* (cmp_sign > 0 && res) || (!(cmp_sign > 0) && !res) */ + if ((cmp_sign > 0) ^ (!res)) + old_nr=nr; + } result_field->set_notnull(); } else if (result_field->is_null(offset)) @@ -788,11 +857,74 @@ 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, *(uint*) arg); +} + +static int simple_str_key_cmp(void* arg, byte* key1, byte* key2) +{ + return my_sortcmp((char*) key1, (char*) key2, *(uint*) 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 **field_end= field + item->table->fields; + uint32 *lengths=item->field_lengths; + for (; field < field_end; ++field) + { + Field* f = *field; + int len = *lengths++; + int res = f->key_cmp(key1, key2); + 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) +{ + byte* 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 +961,125 @@ 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; + + + // no blobs, otherwise it would be MyISAM + if (table->db_type == DB_TYPE_HEAP) + { + qsort_cmp2 compare_key; + void* cmp_arg; + + // 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; + } + key_length = field->pack_length(); + cmp_arg = (void*) &key_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; + uint32 *lengths; + if (!(field_lengths= + (uint32*) thd->alloc(sizeof(uint32) * table->fields))) + return 1; + + for (key_length = 0, lengths=field_lengths; field < field_end; ++field) + { + uint32 length= (*field)->pack_length(); + key_length += length; + *lengths++ = length; + if (!(*field)->binary()) + all_binary = 0; // Can't break loop here + } + rec_offset = table->reclength - key_length; + if (all_binary) + { + compare_key = (qsort_cmp2)simple_raw_key_cmp; + cmp_arg = (void*) &key_length; + } + else + { + compare_key = (qsort_cmp2) composite_key_cmp ; + cmp_arg = (void*) this; + } + } + + init_tree(&tree, min(max_heap_table_size, sortbuff_size/16), 0, + key_length, compare_key, 0, NULL, cmp_arg); + use_tree = 1; + + /* + The only time key_length 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_length) ? max_heap_table_size/key_length : + 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 +1094,21 @@ 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 +1124,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; } @@ -897,7 +1148,7 @@ void Item_udf_sum::reset() bool Item_udf_sum::add() { - DBUG_ENTER("Item_udf_sum::reset"); + DBUG_ENTER("Item_udf_sum::add"); udf.add(&null_value); DBUG_RETURN(0); } diff --git a/sql/item_sum.h b/sql/item_sum.h index f68dfee1b61..a963799b6a7 100644 --- a/sql/item_sum.h +++ b/sql/item_sum.h @@ -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 */ @@ -21,6 +21,8 @@ #pragma interface /* gcc class implementation */ #endif +#include <my_tree.h> + class Item_sum :public Item_result_field { public: @@ -62,6 +64,7 @@ public: { return new Item_field(field);} table_map used_tables() const { return ~(table_map) 0; } /* Not used */ bool const_item() const { return 0; } + bool is_null() { return null_value; } void update_used_tables() { } void make_field(Send_field *field); void print(String *str); @@ -144,13 +147,36 @@ class Item_sum_count_distinct :public Item_sum_int TABLE *table; table_map used_table_cache; bool fix_fields(THD *thd,TABLE_LIST *tables); + uint32 *field_lengths; TMP_TABLE_PARAM *tmp_table_param; - bool always_null; + TREE tree; + uint key_length; + + // 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; } @@ -177,6 +203,7 @@ public: enum Type type() const { return FIELD_AVG_ITEM; } double val(); longlong val_int() { return (longlong) val(); } + bool is_null() { (void) val_int(); return null_value; } String *val_str(String*); void make_field(Send_field *field); void fix_length_and_dec() {} @@ -214,6 +241,7 @@ public: double val(); longlong val_int() { return (longlong) val(); } String *val_str(String*); + bool is_null() { (void) val_int(); return null_value; } void make_field(Send_field *field); void fix_length_and_dec() {} }; @@ -246,6 +274,7 @@ class Item_sum_hybrid :public Item_sum protected: String value,tmp_value; double sum; + longlong sum_int; Item_result hybrid_type; int cmp_sign; table_map used_table_cache; @@ -261,12 +290,13 @@ class Item_sum_hybrid :public Item_sum void reset() { sum=0.0; + sum_int=0; value.length(0); null_value=1; add(); } double val(); - longlong val_int() { return (longlong) val(); } /* Real as default */ + longlong val_int(); void reset_field(); String *val_str(String *); void make_const() { used_table_cache=0; } diff --git a/sql/item_timefunc.cc b/sql/item_timefunc.cc index eb9b1423c78..9a003b79609 100644 --- a/sql/item_timefunc.cc +++ b/sql/item_timefunc.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 */ @@ -672,7 +672,7 @@ String *Item_func_date_format::val_str(String *str) else size=format_length(format); if (format == str) - str=&value; // Save result here + str= &value; // Save result here if (str->alloc(size)) { null_value=1; @@ -1123,3 +1123,13 @@ longlong Item_extract::val_int() } return 0; // Impossible } + + +void Item_typecast::print(String *str) +{ + str->append("CAST("); + args[0]->print(str); + str->append(" AS "); + str->append(func_name()); + str->append(')'); +} diff --git a/sql/item_timefunc.h b/sql/item_timefunc.h index 720f8ba2882..cc5902b4920 100644 --- a/sql/item_timefunc.h +++ b/sql/item_timefunc.h @@ -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 */ @@ -229,6 +229,33 @@ public: const char *func_name() const { return "date"; } void fix_length_and_dec() { decimals=0; max_length=10; } bool save_in_field(Field *to); + void make_field(Send_field *tmp_field) + { + init_make_field(tmp_field,FIELD_TYPE_DATE); + } + Field *tmp_table_field(TABLE *t_arg) + { + if (!t_arg) return result_field; + return new Field_date(maybe_null, name, t_arg); + } +}; + + +class Item_date_func :public Item_str_func +{ +public: + Item_date_func() :Item_str_func() {} + Item_date_func(Item *a) :Item_str_func(a) {} + Item_date_func(Item *a,Item *b) :Item_str_func(a,b) {} + void make_field(Send_field *tmp_field) + { + init_make_field(tmp_field,FIELD_TYPE_DATETIME); + } + Field *tmp_table_field(TABLE *t_arg) + { + if (!t_arg) return result_field; + return new Field_datetime(maybe_null, name, t_arg); + } }; @@ -247,6 +274,15 @@ public: { str_value.set(buff,buff_length); return &str_value; } const char *func_name() const { return "curtime"; } void fix_length_and_dec(); + void make_field(Send_field *tmp_field) + { + init_make_field(tmp_field,FIELD_TYPE_TIME); + } + Field *tmp_table_field(TABLE *t_arg) + { + if (!t_arg) return result_field; + return new Field_time(maybe_null, name, t_arg); + } }; @@ -263,15 +299,15 @@ public: }; -class Item_func_now :public Item_func +class Item_func_now :public Item_date_func { longlong value; char buff[20]; uint buff_length; TIME ltime; public: - Item_func_now() :Item_func() {} - Item_func_now(Item *a) :Item_func(a) {} + Item_func_now() :Item_date_func() {} + Item_func_now(Item *a) :Item_date_func(a) {} enum Item_result result_type () const { return STRING_RESULT; } double val() { return (double) value; } longlong val_int() { return value; } @@ -308,16 +344,16 @@ public: }; -class Item_func_from_unixtime :public Item_func +class Item_func_from_unixtime :public Item_date_func { public: - Item_func_from_unixtime(Item *a) :Item_func(a) {} + Item_func_from_unixtime(Item *a) :Item_date_func(a) {} double val() { return (double) Item_func_from_unixtime::val_int(); } longlong val_int(); String *val_str(String *str); const char *func_name() const { return "from_unixtime"; } void fix_length_and_dec() { decimals=0; max_length=19; } - enum Item_result result_type () const { return STRING_RESULT; } +// enum Item_result result_type () const { return STRING_RESULT; } bool get_date(TIME *res,bool fuzzy_date); }; @@ -331,6 +367,15 @@ public: String *val_str(String *); void fix_length_and_dec() { maybe_null=1; max_length=13; } const char *func_name() const { return "sec_to_time"; } + void make_field(Send_field *tmp_field) + { + init_make_field(tmp_field,FIELD_TYPE_TIME); + } + Field *tmp_table_field(TABLE *t_arg) + { + if (!t_arg) return result_field; + return new Field_time(maybe_null, name, t_arg); + } }; enum interval_type { INTERVAL_YEAR, INTERVAL_MONTH, INTERVAL_DAY, @@ -340,7 +385,7 @@ enum interval_type { INTERVAL_YEAR, INTERVAL_MONTH, INTERVAL_DAY, INTERVAL_HOUR_MINUTE, INTERVAL_HOUR_SECOND, INTERVAL_MINUTE_SECOND}; -class Item_date_add_interval :public Item_str_func +class Item_date_add_interval :public Item_date_func { const interval_type int_type; String value; @@ -348,7 +393,7 @@ class Item_date_add_interval :public Item_str_func public: Item_date_add_interval(Item *a,Item *b,interval_type type_arg,bool neg_arg) - :Item_str_func(a,b),int_type(type_arg), date_sub_interval(neg_arg) {} + :Item_date_func(a,b),int_type(type_arg), date_sub_interval(neg_arg) {} String *val_str(String *); const char *func_name() const { return "date_add_interval"; } void fix_length_and_dec() { maybe_null=1; max_length=19; value.alloc(32);} @@ -369,3 +414,62 @@ class Item_extract :public Item_int_func const char *func_name() const { return "extract"; } void fix_length_and_dec(); }; + +class Item_typecast :public Item_str_func +{ +public: + Item_typecast(Item *a) :Item_str_func(a) {} + String *val_str(String *a) + { a=args[0]->val_str(a); null_value=args[0]->null_value; return a; } + void fix_length_and_dec() { max_length=args[0]->max_length; } + void print(String *str); +}; + + +class Item_date_typecast :public Item_typecast +{ +public: + Item_date_typecast(Item *a) :Item_typecast(a) {} + const char *func_name() const { return "date"; } + void make_field(Send_field *tmp_field) + { + init_make_field(tmp_field,FIELD_TYPE_DATE); + } + Field *tmp_table_field(TABLE *t_arg) + { + if (!t_arg) return result_field; + return new Field_date(maybe_null, name, t_arg); + } +}; + +class Item_time_typecast :public Item_typecast +{ +public: + Item_time_typecast(Item *a) :Item_typecast(a) {} + const char *func_name() const { return "time"; } + void make_field(Send_field *tmp_field) + { + init_make_field(tmp_field,FIELD_TYPE_TIME); + } + Field *tmp_table_field(TABLE *t_arg) + { + if (!t_arg) return result_field; + return new Field_time(maybe_null, name, t_arg); + } +}; + +class Item_datetime_typecast :public Item_typecast +{ +public: + Item_datetime_typecast(Item *a) :Item_typecast(a) {} + const char *func_name() const { return "datetime"; } + void make_field(Send_field *tmp_field) + { + init_make_field(tmp_field,FIELD_TYPE_DATETIME); + } + Field *tmp_table_field(TABLE *t_arg) + { + if (!t_arg) return result_field; + return new Field_datetime(maybe_null, name, t_arg); + } +}; diff --git a/sql/item_uniq.cc b/sql/item_uniq.cc index 80ed6433fd8..88e0cbbc0e6 100644 --- a/sql/item_uniq.cc +++ b/sql/item_uniq.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 */ diff --git a/sql/item_uniq.h b/sql/item_uniq.h index ff11222e2ee..4be64ecc74a 100644 --- a/sql/item_uniq.h +++ b/sql/item_uniq.h @@ -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 */ diff --git a/sql/key.cc b/sql/key.cc index 80a33bc45d3..d2f483e3d73 100644 --- a/sql/key.cc +++ b/sql/key.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 */ @@ -96,7 +96,7 @@ void key_copy(byte *key,TABLE *table,uint idx,uint key_length) length=min(key_length,key_part->length); set_if_smaller(blob_length,length); int2store(key,(uint) blob_length); - key+=2; // Skipp length info + key+=2; // Skip length info memcpy(key,pos,blob_length); } else @@ -250,7 +250,7 @@ void key_unpack(String *to,TABLE *table,uint idx) bool check_if_key_used(TABLE *table, uint idx, List<Item> &fields) { - List_iterator<Item> f(fields); + List_iterator_fast<Item> f(fields); KEY_PART_INFO *key_part,*key_part_end; for (key_part=table->key_info[idx].key_part,key_part_end=key_part+ table->key_info[idx].key_parts ; @@ -258,7 +258,7 @@ bool check_if_key_used(TABLE *table, uint idx, List<Item> &fields) key_part++) { Item_field *field; - + if (key_part->field == table->timestamp_field) return 1; // Can't be used for update diff --git a/sql/lex.h b/sql/lex.h index 30fbf46e354..e03c4db6479 100644 --- a/sql/lex.h +++ b/sql/lex.h @@ -62,18 +62,21 @@ static SYMBOL symbols[] = { { "AVG_ROW_LENGTH", SYM(AVG_ROW_LENGTH),0,0}, { "AUTO_INCREMENT", SYM(AUTO_INC),0,0}, { "AUTOCOMMIT", SYM(AUTOCOMMIT),0,0}, - { "BACKUP", SYM(BACKUP_SYM),0,0}, - { "BEGIN", SYM(BEGIN_SYM),0,0}, + { "BACKUP", SYM(BACKUP_SYM),0,0}, + { "BEGIN", SYM(BEGIN_SYM),0,0}, { "BERKELEYDB", SYM(BERKELEY_DB_SYM),0,0}, { "BDB", SYM(BERKELEY_DB_SYM),0,0}, { "BETWEEN", SYM(BETWEEN_SYM),0,0}, { "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}, + { "BOOLEAN", SYM(BOOLEAN_SYM),0,0}, { "BOTH", SYM(BOTH),0,0}, { "BY", SYM(BY),0,0}, + { "CACHE", SYM(CACHE_SYM),0,0}, { "CASCADE", SYM(CASCADE),0,0}, { "CASE", SYM(CASE_SYM),0,0}, { "CHAR", SYM(CHAR_SYM),0,0}, @@ -82,6 +85,8 @@ static SYMBOL symbols[] = { { "CHANGED", SYM(CHANGED),0,0}, { "CHECK", SYM(CHECK_SYM),0,0}, { "CHECKSUM", SYM(CHECKSUM_SYM),0,0}, + { "CIPHER", SYM(CIPHER_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}, @@ -106,12 +111,16 @@ static SYMBOL symbols[] = { { "DAY_SECOND", SYM(DAY_SECOND_SYM),0,0}, { "DEC", SYM(DECIMAL_SYM),0,0}, { "DECIMAL", SYM(DECIMAL_SYM),0,0}, + { "DES_KEY_FILE", SYM(DES_KEY_FILE),0,0}, { "DEFAULT", SYM(DEFAULT),0,0}, { "DELAYED", SYM(DELAYED_SYM),0,0}, { "DELAY_KEY_WRITE", SYM(DELAY_KEY_WRITE_SYM),0,0}, { "DELETE", SYM(DELETE_SYM),0,0}, + { "DEMAND", SYM(DEMAND_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 */ { "DO", SYM(DO_SYM),0,0}, @@ -123,8 +132,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}, @@ -147,13 +158,12 @@ static SYMBOL symbols[] = { { "FULL", SYM(FULL),0,0}, { "FULLTEXT", SYM(FULLTEXT_SYM),0,0}, { "FUNCTION", SYM(UDF_SYM),0,0}, - { "GEMINI", SYM(GEMINI_SYM),0,0}, - { "GEMINI_SPIN_RETRIES", SYM(GEMINI_SPIN_RETRIES),0,0}, { "GLOBAL", SYM(GLOBAL_SYM),0,0}, { "GRANT", SYM(GRANT),0,0}, { "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}, @@ -164,12 +174,14 @@ 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}, { "INNODB", SYM(INNOBASE_SYM),0,0}, { "INSERT", SYM(INSERT),0,0}, { "INSERT_ID", SYM(INSERT_ID),0,0}, + { "INSERT_METHOD", SYM(INSERT_METHOD),0,0}, { "INT", SYM(INT_SYM),0,0}, { "INTEGER", SYM(INT_SYM),0,0}, { "INTERVAL", SYM(INTERVAL_SYM),0,0}, @@ -179,14 +191,17 @@ static SYMBOL symbols[] = { { "INT4", SYM(INT_SYM),0,0}, { "INT8", SYM(BIGINT),0,0}, { "INTO", SYM(INTO),0,0}, + { "IO_THREAD", SYM(IO_THREAD),0,0}, { "IF", SYM(IF),0,0}, { "IS", SYM(IS),0,0}, { "ISOLATION", SYM(ISOLATION),0,0}, { "ISAM", SYM(ISAM_SYM),0,0}, + { "ISSUER", SYM(ISSUER_SYM),0,0}, { "JOIN", SYM(JOIN_SYM),0,0}, { "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}, @@ -210,8 +225,12 @@ static SYMBOL symbols[] = { { "MASTER_LOG_POS", SYM(MASTER_LOG_POS_SYM),0,0}, { "MASTER_PASSWORD", SYM(MASTER_PASSWORD_SYM),0,0}, { "MASTER_PORT", SYM(MASTER_PORT_SYM),0,0}, + { "MASTER_SERVER_ID", SYM(MASTER_SERVER_ID_SYM),0,0}, { "MASTER_USER", SYM(MASTER_USER_SYM),0,0}, { "MAX_ROWS", SYM(MAX_ROWS),0,0}, + { "MAX_QUERIES_PER_HOUR", SYM(MAX_QUERIES_PER_HOUR), 0,0}, + { "MAX_UPDATES_PER_HOUR", SYM(MAX_UPDATES_PER_HOUR), 0,0}, + { "MAX_CONNECTIONS_PER_HOUR", SYM(MAX_CONNECTIONS_PER_HOUR), 0,0}, { "MATCH", SYM(MATCH),0,0}, { "MEDIUMBLOB", SYM(MEDIUMBLOB),0,0}, { "MEDIUMTEXT", SYM(MEDIUMTEXT),0,0}, @@ -229,11 +248,14 @@ 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}, + { "NEW", SYM(NEW_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}, + { "OFF", SYM(OFF),0,0}, { "ON", SYM(ON),0,0}, { "OPEN", SYM(OPEN_SYM),0,0}, { "OPTIMIZE", SYM(OPTIMIZE),0,0}, @@ -248,23 +270,29 @@ 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}, { "PROCESSLIST", SYM(PROCESSLIST_SYM),0,0}, { "PRIVILEGES", SYM(PRIVILEGES),0,0}, + { "QUERY", SYM(QUERY_SYM),0,0}, { "QUICK", SYM(QUICK),0,0}, { "RAID0", SYM(RAID_0_SYM),0,0}, { "READ", SYM(READ_SYM),0,0}, { "REAL", SYM(REAL),0,0}, { "REFERENCES", SYM(REFERENCES),0,0}, + { "RELAY_LOG_FILE", SYM(RELAY_LOG_FILE_SYM),0,0}, + { "RELAY_LOG_POS", SYM(RELAY_LOG_POS_SYM),0,0}, { "RELOAD", SYM(RELOAD),0,0}, { "REGEXP", SYM(REGEXP),0,0}, { "RENAME", SYM(RENAME),0,0}, { "REPAIR", SYM(REPAIR),0,0}, { "REPLACE", SYM(REPLACE),0,0}, { "REPEATABLE", SYM(REPEATABLE_SYM),0,0}, + { "REQUIRE", SYM(REQUIRE_SYM),0,0}, { "RESET", SYM(RESET_SYM),0,0}, + { "USER_RESOURCES", SYM(RESOURCES),0,0}, { "RESTORE", SYM(RESTORE_SYM),0,0}, { "RESTRICT", SYM(RESTRICT),0,0}, { "RETURNS", SYM(UDF_RETURNS_SYM),0,0}, @@ -279,6 +307,7 @@ static SYMBOL symbols[] = { { "SERIALIZABLE", SYM(SERIALIZABLE_SYM),0,0}, { "SESSION", SYM(SESSION_SYM),0,0}, { "SET", SYM(SET),0,0}, + { "SIGNED", SYM(SIGNED_SYM),0,0}, { "SHARE", SYM(SHARE_SYM),0,0}, { "SHOW", SYM(SHOW),0,0}, { "SHUTDOWN", SYM(SHUTDOWN),0,0}, @@ -290,17 +319,23 @@ 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_CACHE", SYM(SQL_CACHE_SYM), 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}, { "SQL_LOW_PRIORITY_UPDATES", SYM(SQL_LOW_PRIORITY_UPDATES),0,0}, { "SQL_MAX_JOIN_SIZE",SYM(SQL_MAX_JOIN_SIZE), 0, 0}, + { "SQL_NO_CACHE", SYM(SQL_NO_CACHE_SYM), 0, 0}, + { "SQL_QUERY_CACHE_TYPE",SYM(SQL_QUERY_CACHE_TYPE_SYM), 0, 0}, { "SQL_QUOTE_SHOW_CREATE",SYM(SQL_QUOTE_SHOW_CREATE), 0, 0}, { "SQL_SAFE_UPDATES", SYM(SQL_SAFE_UPDATES),0,0}, { "SQL_SELECT_LIMIT", SYM(SQL_SELECT_LIMIT),0,0}, { "SQL_SLAVE_SKIP_COUNTER", SYM(SQL_SLAVE_SKIP_COUNTER),0,0}, { "SQL_SMALL_RESULT", SYM(SQL_SMALL_RESULT),0,0}, + { "SQL_THREAD", SYM(SQL_THREAD),0,0}, { "SQL_WARNINGS", SYM(SQL_WARNINGS),0,0}, + { "SSL", SYM(SSL_SYM),0,0}, { "STRAIGHT_JOIN", SYM(STRAIGHT_JOIN),0,0}, { "START", SYM(START_SYM),0,0}, { "STARTING", SYM(STARTING),0,0}, @@ -308,6 +343,7 @@ static SYMBOL symbols[] = { { "STRING", SYM(STRING_SYM),0,0}, { "STOP", SYM(STOP_SYM),0,0}, { "STRIPED", SYM(RAID_STRIPED_SYM),0,0}, + { "SUBJECT", SYM(SUBJECT_SYM),0,0}, { "TABLE", SYM(TABLE_SYM),0,0}, { "TABLES", SYM(TABLES),0,0}, { "TEMPORARY", SYM(TEMPORARY),0,0}, @@ -330,6 +366,7 @@ static SYMBOL symbols[] = { { "UNLOCK", SYM(UNLOCK_SYM),0,0}, { "UNSIGNED", SYM(UNSIGNED),0,0}, { "USE", SYM(USE_SYM),0,0}, + { "USE_FRM", SYM(USE_FRM),0,0}, { "USING", SYM(USING),0,0}, { "UPDATE", SYM(UPDATE_SYM),0,0}, { "USAGE", SYM(USAGE),0,0}, @@ -343,6 +380,7 @@ static SYMBOL symbols[] = { { "WRITE", SYM(WRITE_SYM),0,0}, { "WHEN", SYM(WHEN_SYM),0,0}, { "WHERE", SYM(WHERE),0,0}, + { "X509", SYM(X509_SYM),0,0}, { "YEAR", SYM(YEAR_SYM),0,0}, { "YEAR_MONTH", SYM(YEAR_MONTH_SYM),0,0}, { "ZEROFILL", SYM(ZEROFILL),0,0}, @@ -363,7 +401,9 @@ static SYMBOL sql_functions[] = { { "BIT_COUNT", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_bit_count)}, { "BIT_OR", SYM(BIT_OR),0,0}, { "BIT_AND", SYM(BIT_AND),0,0}, + { "CAST", SYM(CAST_SYM),0,0}, { "CEILING", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_ceiling)}, + { "BIT_LENGTH", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_bit_length)}, { "CHAR_LENGTH", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_char_length)}, { "CHARACTER_LENGTH", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_char_length)}, { "COALESCE", SYM(COALESCE),0,0}, @@ -371,6 +411,7 @@ static SYMBOL sql_functions[] = { { "CONCAT_WS", SYM(CONCAT_WS),0,0}, { "CONNECTION_ID", SYM(FUNC_ARG0),0,CREATE_FUNC(create_func_connection_id)}, { "CONV", SYM(FUNC_ARG3),0,CREATE_FUNC(create_func_conv)}, + { "CONVERT", SYM(CONVERT_SYM),0,0}, { "COUNT", SYM(COUNT_SYM),0,0}, { "COS", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_cos)}, { "COT", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_cot)}, @@ -385,6 +426,8 @@ static SYMBOL sql_functions[] = { { "DAYOFYEAR", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_dayofyear)}, { "DECODE", SYM(DECODE_SYM),0,0}, { "DEGREES", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_degrees)}, + { "DES_ENCRYPT", SYM(DES_ENCRYPT_SYM),0,0}, + { "DES_DECRYPT", SYM(DES_DECRYPT_SYM),0,0}, { "ELT", SYM(ELT_FUNC),0,0}, { "ENCODE", SYM(ENCODE_SYM),0,0}, { "ENCRYPT", SYM(ENCRYPT),0,0}, @@ -395,6 +438,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/lex_symbol.h b/sql/lex_symbol.h index a011e27b59e..9fff1751b1b 100644 --- a/sql/lex_symbol.h +++ b/sql/lex_symbol.h @@ -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 */ diff --git a/sql/lock.cc b/sql/lock.cc index a9054b99186..db849757741 100644 --- a/sql/lock.cc +++ b/sql/lock.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 */ @@ -55,33 +55,13 @@ MYSQL_LOCK *mysql_lock_tables(THD *thd,TABLE **tables,uint count) Someone has issued LOCK ALL TABLES FOR READ and we want a write lock Wait until the lock is gone */ - if (thd->global_read_lock) // This thread had the read locks + if (wait_if_global_read_lock(thd, 1)) { - my_error(ER_TABLE_NOT_LOCKED_FOR_WRITE,MYF(0), - write_lock_used->table_name); my_free((gptr) sql_lock,MYF(0)); sql_lock=0; break; } - - pthread_mutex_lock(&LOCK_open); - thd->mysys_var->current_mutex= &LOCK_open; - thd->mysys_var->current_cond= &COND_refresh; - thd->proc_info="Waiting for table"; - - while (global_read_lock && ! thd->killed && - thd->version == refresh_version) - { - (void) pthread_cond_wait(&COND_refresh,&LOCK_open); - } - pthread_mutex_unlock(&LOCK_open); - pthread_mutex_lock(&thd->mysys_var->mutex); - thd->mysys_var->current_mutex= 0; - thd->mysys_var->current_cond= 0; - thd->proc_info= 0; - pthread_mutex_unlock(&thd->mysys_var->mutex); - - if (thd->version != refresh_version || thd->killed) + if (thd->version != refresh_version) { my_free((gptr) sql_lock,MYF(0)); goto retry; @@ -401,6 +381,36 @@ static MYSQL_LOCK *get_lock_data(THD *thd, TABLE **table_ptr, uint count, *****************************************************************************/ /* + Lock and wait for the named lock. + Returns 0 on ok +*/ + +int lock_and_wait_for_table_name(THD *thd, TABLE_LIST *table_list) +{ + int lock_retcode; + int error= -1; + DBUG_ENTER("lock_and_wait_for_table_name"); + + if (wait_if_global_read_lock(thd,0)) + DBUG_RETURN(1); + VOID(pthread_mutex_lock(&LOCK_open)); + if ((lock_retcode = lock_table_name(thd, table_list)) < 0) + goto end; + if (lock_retcode && wait_for_locked_table_names(thd, table_list)) + { + unlock_table_name(thd, table_list); + goto end; + } + error=0; + +end: + pthread_mutex_unlock(&LOCK_open); + start_waiting_global_read_lock(thd); + DBUG_RETURN(error); +} + + +/* Put a not open table with an old refresh version in the table cache. This will force any other threads that uses the table to release it as soon as possible. @@ -411,7 +421,6 @@ static MYSQL_LOCK *get_lock_data(THD *thd, TABLE **table_ptr, uint count, > 0 table locked, but someone is using it */ - int lock_table_name(THD *thd, TABLE_LIST *table_list) { TABLE *table; @@ -429,10 +438,11 @@ int lock_table_name(THD *thd, TABLE_LIST *table_list) if (table->in_use == thd) DBUG_RETURN(0); - /* Create a table entry with the right key and with an old refresh version */ - /* Note that we must use my_malloc() here as this is freed by the table - cache */ - + /* + Create a table entry with the right key and with an old refresh version + Note that we must use my_malloc() here as this is freed by the table + cache + */ if (!(table= (TABLE*) my_malloc(sizeof(*table)+key_length, MYF(MY_WME | MY_ZEROFILL)))) DBUG_RETURN(-1); @@ -510,3 +520,102 @@ static void print_lock_error(int error) DBUG_VOID_RETURN; } + +/**************************************************************************** + Handling of global read locks + + The global locks are handled through the global variables: + global_read_lock + waiting_for_read_lock + protect_against_global_read_lock +****************************************************************************/ + +volatile uint global_read_lock=0; +static volatile uint protect_against_global_read_lock=0; +static volatile uint waiting_for_read_lock=0; + +bool lock_global_read_lock(THD *thd) +{ + DBUG_ENTER("lock_global_read_lock"); + + if (!thd->global_read_lock) + { + (void) pthread_mutex_lock(&LOCK_open); + const char *old_message=thd->enter_cond(&COND_refresh, &LOCK_open, + "Waiting to get readlock"); + DBUG_PRINT("info", + ("waiting_for: %d protect_against: %d", + waiting_for_read_lock, protect_against_global_read_lock)); + + waiting_for_read_lock++; + while (protect_against_global_read_lock && !thd->killed) + pthread_cond_wait(&COND_refresh, &LOCK_open); + waiting_for_read_lock--; + thd->exit_cond(old_message); + if (thd->killed) + { + (void) pthread_mutex_unlock(&LOCK_open); + DBUG_RETURN(1); + } + thd->global_read_lock=1; + global_read_lock++; + (void) pthread_mutex_unlock(&LOCK_open); + } + DBUG_RETURN(0); +} + +void unlock_global_read_lock(THD *thd) +{ + uint tmp; + thd->global_read_lock=0; + pthread_mutex_lock(&LOCK_open); + tmp= --global_read_lock; + pthread_mutex_unlock(&LOCK_open); + /* Send the signal outside the mutex to avoid a context switch */ + if (!tmp) + pthread_cond_broadcast(&COND_refresh); +} + + +bool wait_if_global_read_lock(THD *thd, bool abort_on_refresh) +{ + const char *old_message; + bool result=0; + DBUG_ENTER("wait_if_global_read_lock"); + + (void) pthread_mutex_lock(&LOCK_open); + if (global_read_lock) + { + if (thd->global_read_lock) // This thread had the read locks + { + my_error(ER_CANT_UPDATE_WITH_READLOCK,MYF(0)); + (void) pthread_mutex_unlock(&LOCK_open); + DBUG_RETURN(1); + } + old_message=thd->enter_cond(&COND_refresh, &LOCK_open, + "Waiting for release of readlock"); + while (global_read_lock && ! thd->killed && + (!abort_on_refresh || thd->version == refresh_version)) + (void) pthread_cond_wait(&COND_refresh,&LOCK_open); + if (thd->killed) + result=1; + thd->exit_cond(old_message); + } + if (!abort_on_refresh && !result) + protect_against_global_read_lock++; + pthread_mutex_unlock(&LOCK_open); + DBUG_RETURN(result); +} + + +void start_waiting_global_read_lock(THD *thd) +{ + bool tmp; + DBUG_ENTER("start_waiting_global_read_lock"); + (void) pthread_mutex_lock(&LOCK_open); + tmp= (!--protect_against_global_read_lock && waiting_for_read_lock); + (void) pthread_mutex_unlock(&LOCK_open); + if (tmp) + pthread_cond_broadcast(&COND_refresh); + DBUG_VOID_RETURN; +} diff --git a/sql/log.cc b/sql/log.cc index f4284ac6bad..f0012a94f5d 100644 --- a/sql/log.cc +++ b/sql/log.cc @@ -29,6 +29,7 @@ #include <my_dir.h> #include <stdarg.h> #include <m_ctype.h> // For test_if_number +#include <assert.h> MYSQL_LOG mysql_log,mysql_update_log,mysql_slow_log,mysql_bin_log; extern I_List<i_string> binlog_do_db, binlog_ignore_db; @@ -81,7 +82,8 @@ 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), file_id(1),no_rotate(0), + need_start_event(1),bytes_written(0) { /* We don't want to intialize LOCK_Log here as the thread system may @@ -97,6 +99,7 @@ MYSQL_LOG::~MYSQL_LOG() { (void) pthread_mutex_destroy(&LOCK_log); (void) pthread_mutex_destroy(&LOCK_index); + (void) pthread_cond_destroy(&update_cond); } } @@ -136,14 +139,19 @@ bool MYSQL_LOG::open_index( int options) MYF(MY_WME))) < 0); } -void MYSQL_LOG::init(enum_log_type log_type_arg) +void MYSQL_LOG::init(enum_log_type log_type_arg, + enum cache_type io_cache_type_arg, + bool no_auto_events_arg) { log_type = log_type_arg; + io_cache_type = io_cache_type_arg; + no_auto_events = no_auto_events_arg; if (!inited) { inited=1; (void) pthread_mutex_init(&LOCK_log,MY_MUTEX_INIT_SLOW); (void) pthread_mutex_init(&LOCK_index, MY_MUTEX_INIT_SLOW); + (void) pthread_cond_init(&update_cond, 0); } } @@ -157,16 +165,17 @@ void MYSQL_LOG::close_index() } void MYSQL_LOG::open(const char *log_name, enum_log_type log_type_arg, - const char *new_name) + const char *new_name, enum cache_type io_cache_type_arg, + bool no_auto_events_arg) { MY_STAT tmp_stat; char buff[512]; File file= -1; bool do_magic; - + int open_flags = O_CREAT | O_APPEND | O_BINARY; if (!inited && log_type_arg == LOG_BIN && *fn_ext(log_name)) no_rotate = 1; - init(log_type_arg); + init(log_type_arg,io_cache_type_arg,no_auto_events_arg); if (!(name=my_strdup(log_name,MYF(MY_WME)))) goto err; @@ -174,7 +183,12 @@ void MYSQL_LOG::open(const char *log_name, enum_log_type log_type_arg, strmov(log_file_name,new_name); else if (generate_new_name(log_file_name, name)) goto err; - + + if (io_cache_type == SEQ_READ_APPEND) + open_flags |= O_RDWR; + else + open_flags |= O_WRONLY; + if (log_type == LOG_BIN && !index_file_name[0]) fn_format(index_file_name, name, mysql_data_home, ".index", 6); @@ -182,9 +196,9 @@ void MYSQL_LOG::open(const char *log_name, enum_log_type log_type_arg, do_magic = ((log_type == LOG_BIN) && !my_stat(log_file_name, &tmp_stat, MYF(0))); - if ((file=my_open(log_file_name,O_CREAT | O_APPEND | O_WRONLY | O_BINARY, + if ((file=my_open(log_file_name,open_flags, MYF(MY_WME | ME_WAITTANG))) < 0 || - init_io_cache(&log_file, file, IO_SIZE, WRITE_CACHE, + init_io_cache(&log_file, file, IO_SIZE, io_cache_type, my_tell(file,MYF(MY_WME)), 0, MYF(MY_WME | MY_NABP))) goto err; @@ -220,19 +234,22 @@ void MYSQL_LOG::open(const char *log_name, enum_log_type log_type_arg, } else if (log_type == LOG_BIN) { - /* - Explanation of the boolean black magic: - if we are supposed to write magic number try write - clean up if failed - then if index_file has not been previously opened, try to open it - clean up if failed - */ - if ((do_magic && my_b_write(&log_file, (byte*) BINLOG_MAGIC, 4)) || - open_index(O_APPEND | O_RDWR | O_CREAT)) - goto err; - Start_log_event s; bool error; - s.write(&log_file); + if (do_magic) + { + if (my_b_write(&log_file, (byte*) BINLOG_MAGIC, 4) || + open_index(O_APPEND | O_RDWR | O_CREAT)) + goto err; + bytes_written += 4; + } + + if (need_start_event && !no_auto_events) + { + Start_log_event s; + s.set_log_pos(this); + s.write(&log_file); + need_start_event=0; + } flush_io_cache(&log_file); pthread_mutex_lock(&LOCK_index); error=(my_write(index_file, (byte*) log_file_name, strlen(log_file_name), @@ -254,9 +271,7 @@ err: end_io_cache(&log_file); x_free(name); name=0; log_type=LOG_CLOSED; - return; - } int MYSQL_LOG::get_current_log(LOG_INFO* linfo) @@ -269,7 +284,8 @@ int MYSQL_LOG::get_current_log(LOG_INFO* linfo) } // if log_name is "" we stop at the first entry -int MYSQL_LOG::find_first_log(LOG_INFO* linfo, const char* log_name) +int MYSQL_LOG::find_first_log(LOG_INFO* linfo, const char* log_name, + bool need_mutex) { if (index_file < 0) return LOG_INFO_INVALID; @@ -280,7 +296,8 @@ int MYSQL_LOG::find_first_log(LOG_INFO* linfo, const char* log_name) // mutex needed because we need to make sure the file pointer does not move // from under our feet - pthread_mutex_lock(&LOCK_index); + if (need_mutex) + pthread_mutex_lock(&LOCK_index); if (init_io_cache(&io_cache, index_file, IO_SIZE, READ_CACHE, (my_off_t) 0, 0, MYF(MY_WME))) { @@ -309,14 +326,15 @@ int MYSQL_LOG::find_first_log(LOG_INFO* linfo, const char* log_name) error = 0; err: - pthread_mutex_unlock(&LOCK_index); + if (need_mutex) + pthread_mutex_unlock(&LOCK_index); end_io_cache(&io_cache); return error; } -int MYSQL_LOG::find_next_log(LOG_INFO* linfo) +int MYSQL_LOG::find_next_log(LOG_INFO* linfo, bool need_lock) { // mutex needed because we need to make sure the file pointer does not move // from under our feet @@ -325,8 +343,8 @@ int MYSQL_LOG::find_next_log(LOG_INFO* linfo) char* fname = linfo->log_file_name; IO_CACHE io_cache; uint length; - - pthread_mutex_lock(&LOCK_index); + if (need_lock) + pthread_mutex_lock(&LOCK_index); if (init_io_cache(&io_cache, index_file, IO_SIZE, READ_CACHE, (my_off_t) linfo->index_file_offset, 0, MYF(MY_WME))) @@ -344,16 +362,153 @@ int MYSQL_LOG::find_next_log(LOG_INFO* linfo) error = 0; err: - pthread_mutex_unlock(&LOCK_index); + if (need_lock) + pthread_mutex_unlock(&LOCK_index); end_io_cache(&io_cache); return error; } - + +int MYSQL_LOG::reset_logs(THD* thd) +{ + LOG_INFO linfo; + int error=0; + const char* save_name; + enum_log_type save_log_type; + pthread_mutex_lock(&LOCK_log); + if (find_first_log(&linfo,"")) + { + error=1; + goto err; + } + + for(;;) + { + my_delete(linfo.log_file_name, MYF(MY_WME)); + if (find_next_log(&linfo)) + break; + } + save_name=name; + name=0; + save_log_type=log_type; + close(1); + my_delete(index_file_name, MYF(MY_WME)); + if (thd && !thd->slave_thread) + need_start_event=1; + open(save_name,save_log_type,0,io_cache_type,no_auto_events); + my_free((gptr)save_name,MYF(0)); +err: + pthread_mutex_unlock(&LOCK_log); + return error; +} + + +int MYSQL_LOG::purge_first_log(struct st_relay_log_info* rli) +{ + // pre-conditions + DBUG_ASSERT(is_open()); + DBUG_ASSERT(index_file >= 0); + DBUG_ASSERT(rli->slave_running == 1); + DBUG_ASSERT(!strcmp(rli->linfo.log_file_name,rli->relay_log_name)); + /* + Assume that we have previously read the first log and + stored it in rli->relay_log_name + */ + DBUG_ASSERT(rli->linfo.index_file_offset == + strlen(rli->relay_log_name) + 1); + int tmp_fd; + char* fname, *io_buf; + int error = 0; + + if (!(fname= (char*) my_malloc(IO_SIZE+FN_REFLEN, MYF(MY_WME)))) + return 1; + pthread_mutex_lock(&LOCK_index); + my_seek(index_file,rli->linfo.index_file_offset, + MY_SEEK_SET, MYF(MY_WME)); + io_buf = fname + FN_REFLEN; + strxmov(fname,rli->relay_log_name,".tmp",NullS); + + if ((tmp_fd = my_open(fname,O_CREAT|O_BINARY|O_RDWR, MYF(MY_WME))) < 0) + { + error = 1; + goto err; + } + for (;;) + { + int bytes_read; + bytes_read = my_read(index_file, (byte*) io_buf, IO_SIZE, MYF(0)); + if (bytes_read < 0) // error + { + error=1; + goto err; + } + if (!bytes_read) + break; // end of file + // otherwise, we've read something and need to write it out + if (my_write(tmp_fd, (byte*) io_buf, bytes_read, MYF(MY_WME|MY_NABP))) + { + error=1; + goto err; + } + } + +err: + if (tmp_fd) + my_close(tmp_fd, MYF(MY_WME)); + if (error) + my_delete(fname, MYF(0)); // do not report error if the file is not there + else + { + MY_STAT s; + my_close(index_file, MYF(MY_WME)); + if (!my_stat(rli->relay_log_name,&s,MYF(0))) + { + sql_print_error("The first log %s failed to stat during purge", + rli->relay_log_name); + error=1; + goto err; + } + if (my_rename(fname,index_file_name,MYF(MY_WME)) || + (index_file=my_open(index_file_name,O_BINARY|O_RDWR|O_APPEND, + MYF(MY_WME)))<0 || + my_delete(rli->relay_log_name, MYF(MY_WME))) + error=1; + + pthread_mutex_lock(&rli->log_space_lock); + rli->log_space_total -= s.st_size; + pthread_mutex_unlock(&rli->log_space_lock); + // ok to broadcast after the critical region as there is no risk of + // the mutex being destroyed by this thread later - this helps save + // context switches + pthread_cond_broadcast(&rli->log_space_cond); + + if ((error=find_first_log(&rli->linfo,"",0/*no mutex*/))) + { + char buff[22]; + sql_print_error("next log error=%d,offset=%s,log=%s",error, + llstr(rli->linfo.index_file_offset,buff), + rli->linfo.log_file_name); + goto err2; + } + rli->relay_log_pos = 4; + strnmov(rli->relay_log_name,rli->linfo.log_file_name, + sizeof(rli->relay_log_name)); + flush_relay_log_info(rli); + } + /* + No need to free io_buf because we allocated both fname and io_buf in + one malloc() + */ + +err2: + pthread_mutex_unlock(&LOCK_index); + my_free(fname, MYF(MY_WME)); + return error; +} + + int MYSQL_LOG::purge_logs(THD* thd, const char* to_log) { - if (index_file < 0) return LOG_INFO_INVALID; - if (no_rotate) return LOG_INFO_PURGE_NO_ROTATE; int error; char fname[FN_REFLEN]; char *p; @@ -364,6 +519,10 @@ int MYSQL_LOG::purge_logs(THD* thd, const char* to_log) LINT_INIT(purge_offset); IO_CACHE io_cache; + if (index_file < 0) + return LOG_INFO_INVALID; + if (no_rotate) + return LOG_INFO_PURGE_NO_ROTATE; pthread_mutex_lock(&LOCK_index); if (init_io_cache(&io_cache,index_file, IO_SIZE*2, READ_CACHE, (my_off_t) 0, @@ -385,9 +544,8 @@ int MYSQL_LOG::purge_logs(THD* thd, const char* to_log) goto err; } logs_to_keep_inited = 1; - - for(;;) + for (;;) { my_off_t init_purge_offset= my_b_tell(&io_cache); if (!(fname_len=my_b_gets(&io_cache, fname, FN_REFLEN))) @@ -399,14 +557,14 @@ int MYSQL_LOG::purge_logs(THD* thd, const char* to_log) } fname[--fname_len]=0; // kill \n - if(!memcmp(fname, to_log, fname_len + 1 )) + if (!memcmp(fname, to_log, fname_len + 1 )) { found_log = 1; purge_offset = init_purge_offset; } // if one of the logs before the target is in use - if(!found_log && log_in_use(fname)) + if (!found_log && log_in_use(fname)) { error = LOG_INFO_IN_USE; goto err; @@ -422,13 +580,13 @@ int MYSQL_LOG::purge_logs(THD* thd, const char* to_log) } end_io_cache(&io_cache); - if(!found_log) + if (!found_log) { error = LOG_INFO_EOF; goto err; } - for(i = 0; i < logs_to_purge.elements; i++) + for (i = 0; i < logs_to_purge.elements; i++) { char* l; get_dynamic(&logs_to_purge, (gptr)&l, i); @@ -436,9 +594,10 @@ int MYSQL_LOG::purge_logs(THD* thd, const char* to_log) sql_print_error("Error deleting %s during purge", l); } - // if we get killed -9 here, the sysadmin would have to do a small - // vi job on the log index file after restart - otherwise, this should - // be safe + /* + If we get killed -9 here, the sysadmin would have to edit + the log index file after restart - otherwise, this should be safe + */ #ifdef HAVE_FTRUNCATE if (ftruncate(index_file,0)) { @@ -451,9 +610,9 @@ during log purge for write"); #else my_close(index_file, MYF(MY_WME)); my_delete(index_file_name, MYF(MY_WME)); - if(!(index_file = my_open(index_file_name, + if ((index_file = my_open(index_file_name, O_CREAT | O_BINARY | O_RDWR | O_APPEND, - MYF(MY_WME)))) + MYF(MY_WME)))<0) { sql_print_error("Could not re-open the binlog index file \ during log purge for write"); @@ -462,7 +621,7 @@ during log purge for write"); } #endif - for(i = 0; i < logs_to_keep.elements; i++) + for (i = 0; i < logs_to_keep.elements; i++) { char* l; get_dynamic(&logs_to_keep, (gptr)&l, i); @@ -480,15 +639,14 @@ during log purge for write"); err: pthread_mutex_unlock(&LOCK_index); - if(logs_to_purge_inited) + if (logs_to_purge_inited) delete_dynamic(&logs_to_purge); - if(logs_to_keep_inited) + if (logs_to_keep_inited) delete_dynamic(&logs_to_keep); end_io_cache(&io_cache); return error; } - // we assume that buf has at least FN_REFLEN bytes alloced void MYSQL_LOG::make_log_name(char* buf, const char* log_ident) { @@ -533,20 +691,37 @@ void MYSQL_LOG::new_file(bool inside_mutex) } if (log_type == LOG_BIN) { - /* - We log the whole file name for log file as the user may decide - to change base names at some point. - */ - Rotate_log_event r(new_name+dirname_length(new_name)); - r.write(&log_file); - VOID(pthread_cond_broadcast(&COND_binlog_update)); + if (!no_auto_events) + { + /* + We log the whole file name for log file as the user may decide + to change base names at some point. + */ + THD* thd = current_thd; + Rotate_log_event r(thd,new_name+dirname_length(new_name)); + r.set_log_pos(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 && thd->slave_thread) + r.flags |= LOG_EVENT_FORCED_ROTATE_F; + r.write(&log_file); + bytes_written += r.get_event_len(); + } + // update needs to be signaled even if there is no rotate event + // log rotation should give the waiting thread a signal to + // discover EOF and move on to the next log + signal_update(); } + else + strmov(new_name, old_name); // Reopen old file name } - else - strmov(new_name, old_name); // Reopen old file name name=0; close(); - open(old_name, log_type, new_name); + open(old_name, log_type, new_name, io_cache_type, no_auto_events); my_free(old_name,MYF(0)); last_time=query_start=0; write_error=0; @@ -555,6 +730,60 @@ void MYSQL_LOG::new_file(bool inside_mutex) } } +bool MYSQL_LOG::append(Log_event* ev) +{ + bool error = 0; + pthread_mutex_lock(&LOCK_log); + + DBUG_ASSERT(log_file.type == SEQ_READ_APPEND); + // Log_event::write() is smart enough to use my_b_write() or + // my_b_append() depending on the kind of cache we have + if (ev->write(&log_file)) + { + error=1; + goto err; + } + bytes_written += ev->get_event_len(); + if ((uint)my_b_append_tell(&log_file) > max_binlog_size) + { + new_file(1); + } + signal_update(); +err: + pthread_mutex_unlock(&LOCK_log); + return error; +} + +bool MYSQL_LOG::appendv(const char* buf, uint len,...) +{ + bool error = 0; + va_list(args); + va_start(args,len); + + DBUG_ASSERT(log_file.type == SEQ_READ_APPEND); + + pthread_mutex_lock(&LOCK_log); + do + { + if (my_b_append(&log_file,(byte*) buf,len)) + { + error = 1; + break; + } + bytes_written += len; + } while ((buf=va_arg(args,const char*)) && (len=va_arg(args,uint))); + + if ((uint) my_b_append_tell(&log_file) > max_binlog_size) + { + new_file(1); + } + + if (!error) + signal_update(); + pthread_mutex_unlock(&LOCK_log); + return error; +} + bool MYSQL_LOG::write(THD *thd,enum enum_server_command command, const char *format,...) @@ -635,9 +864,8 @@ bool MYSQL_LOG::write(THD *thd,enum enum_server_command command, return 0; } -/* Write to binary log in a format to be used for replication */ -bool MYSQL_LOG::write(Query_log_event* event_info) +bool MYSQL_LOG::write(Log_event* event_info) { /* In most cases this is only called if 'is_open()' is true */ bool error=0; @@ -649,35 +877,46 @@ bool MYSQL_LOG::write(Query_log_event* event_info) if (is_open()) { THD *thd=event_info->thd; - IO_CACHE *file = (event_info->cache_stmt ? &thd->transaction.trans_log : + const char* db = event_info->get_db(); +#ifdef USING_TRANSACTIONS + IO_CACHE *file = ((event_info->get_cache_stmt() && thd) ? + &thd->transaction.trans_log : &log_file); - if ((!(thd->options & OPTION_BIN_LOG) && +#else + IO_CACHE *file = &log_file; +#endif + if ((thd && !(thd->options & OPTION_BIN_LOG) && (thd->master_access & PROCESS_ACL)) || - !db_ok(event_info->db, binlog_do_db, binlog_ignore_db)) + (db && !db_ok(db, binlog_do_db, binlog_ignore_db))) { VOID(pthread_mutex_unlock(&LOCK_log)); return 0; } error=1; - - if (thd->last_insert_id_used) + /* + No check for auto events flag here - this write method should + never be called if auto-events are enabled + */ + if (thd && 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; + Intvar_log_event e(thd,(uchar)LAST_INSERT_ID_EVENT,thd->last_insert_id); + e.set_log_pos(this); + if (thd->server_id) + e.server_id = thd->server_id; if (e.write(file)) goto err; } - if (thd->insert_id_used) + if (thd && 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; + Intvar_log_event e(thd,(uchar)INSERT_ID_EVENT,thd->last_insert_id); + e.set_log_pos(this); + if (thd->server_id) + e.server_id = thd->server_id; if (e.write(file)) goto err; } - if (thd->convert_set) + if (thd && thd->convert_set) { char buf[1024] = "SET CHARACTER SET "; char* p = strend(buf); @@ -686,27 +925,32 @@ 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_pos(this); if (e.write(file)) goto err; thd->query_length = save_query_length; // clean up } + event_info->set_log_pos(this); if (event_info->write(file) || file == &log_file && flush_io_cache(file)) goto err; error=0; - should_rotate = (file == &log_file && my_b_tell(file) >= max_binlog_size); - /* Tell for transactional table handlers up to which position in the - binlog file we wrote. The table handler can store this info, and - after crash recovery print for the user the offset of the last - transactions which were recovered. Actually, we must also call - the table handler commit here, protected by the LOCK_log mutex, - because otherwise the transactions may end up in a different order - in the table handler log! */ + /* + Tell for transactional table handlers up to which position in the + binlog file we wrote. The table handler can store this info, and + after crash recovery print for the user the offset of the last + transactions which were recovered. Actually, we must also call + the table handler commit here, protected by the LOCK_log mutex, + because otherwise the transactions may end up in a different order + in the table handler log! + */ - if (file == &log_file) { + if (file == &log_file) + { error = ha_report_binlog_offset_and_commit(thd, log_file_name, - file->pos_in_file); + file->pos_in_file); + should_rotate= (my_b_tell(file) >= (my_off_t) max_binlog_size); } err: @@ -719,7 +963,7 @@ err: write_error=1; } if (file == &log_file) - VOID(pthread_cond_broadcast(&COND_binlog_update)); + signal_update(); } if (should_rotate) new_file(1); // inside mutex @@ -727,6 +971,15 @@ err: return error; } +uint MYSQL_LOG::next_file_id() +{ + uint res; + pthread_mutex_lock(&LOCK_log); + res = file_id++; + pthread_mutex_unlock(&LOCK_log); + return res; +} + /* Write a cached log entry to the binary log We only come here if there is something in the cache. @@ -741,7 +994,7 @@ bool MYSQL_LOG::write(THD *thd, IO_CACHE *cache) if (is_open()) { uint length; - + //QQ: this looks like a bug - why READ_CACHE? if (reinit_io_cache(cache, READ_CACHE, 0, 0, 0)) { sql_print_error(ER(ER_ERROR_ON_WRITE), cache->file_name, errno); @@ -750,13 +1003,13 @@ bool MYSQL_LOG::write(THD *thd, IO_CACHE *cache) length=my_b_bytes_in_cache(cache); do { - if (my_b_write(&log_file, cache->rc_pos, length)) + if (my_b_write(&log_file, cache->read_pos, length)) { if (!write_error) sql_print_error(ER(ER_ERROR_ON_WRITE), name, errno); goto err; } - cache->rc_pos=cache->rc_end; // Mark buffer used up + cache->read_pos=cache->read_end; // Mark buffer used up } while ((length=my_b_fill(cache))); if (flush_io_cache(&log_file)) { @@ -780,7 +1033,7 @@ err: if (error) write_error=1; else - VOID(pthread_cond_broadcast(&COND_binlog_update)); + signal_update(); VOID(pthread_mutex_unlock(&LOCK_log)); @@ -788,42 +1041,6 @@ err: } -bool MYSQL_LOG::write(Load_log_event* event_info) -{ - bool error=0; - bool should_rotate = 0; - - if (inited) - { - VOID(pthread_mutex_lock(&LOCK_log)); - if (is_open()) - { - THD *thd=event_info->thd; - if ((thd->options & OPTION_BIN_LOG) || - !(thd->master_access & PROCESS_ACL)) - { - if (event_info->write(&log_file) || flush_io_cache(&log_file)) - { - if (!write_error) - sql_print_error(ER(ER_ERROR_ON_WRITE), name, errno); - error=write_error=1; - } - should_rotate = (my_b_tell(&log_file) >= max_binlog_size); - VOID(pthread_cond_broadcast(&COND_binlog_update)); - } - } - - if(should_rotate) - new_file(1); // inside mutex - - VOID(pthread_mutex_unlock(&LOCK_log)); - } - - - return error; -} - - /* Write update log in a format suitable for incremental backup */ bool MYSQL_LOG::write(THD *thd,const char *query, uint query_length, @@ -946,20 +1163,37 @@ bool MYSQL_LOG::write(THD *thd,const char *query, uint query_length, return error; } +void MYSQL_LOG:: wait_for_update(THD* thd) +{ + const char* old_msg = thd->enter_cond(&update_cond, &LOCK_log, + "Slave: waiting for binlog update"); + pthread_cond_wait(&update_cond, &LOCK_log); + // this is not a bug - we unlock the mutex for the caller, and expect him + // to lock it and then not unlock it upon return. This is a rather odd + // way of doing things, but this is the cleanest way I could think of to + // solve the race deadlock caused by THD::awake() first acquiring mysys_var + // mutex and then the current mutex, while wait_for_update being called with + // the current mutex already aquired and THD::exit_cond() trying to acquire + // mysys_var mutex. We do need the mutex to be acquired prior to the + // invocation of wait_for_update in all cases, so mutex acquisition inside + // wait_for_update() is not an option + pthread_mutex_unlock(&LOCK_log); + thd->exit_cond(old_msg); +} void MYSQL_LOG::close(bool exiting) { // One can't set log_type here! if (is_open()) { - File file=log_file.file; - if (log_type == LOG_BIN) + if (log_type == LOG_BIN && !no_auto_events) { Stop_log_event s; + s.set_log_pos(this); s.write(&log_file); - VOID(pthread_cond_broadcast(&COND_binlog_update)); + signal_update(); } end_io_cache(&log_file); - if (my_close(file,MYF(0)) < 0 && ! write_error) + if (my_close(log_file.file,MYF(0)) < 0 && ! write_error) { write_error=1; sql_print_error(ER(ER_ERROR_ON_WRITE),name,errno); diff --git a/sql/log_event.cc b/sql/log_event.cc index c30d03adaf5..fd04f8dbbaa 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -20,25 +20,377 @@ #pragma implementation // gcc: Class implementation #endif #include "mysql_priv.h" +#include "slave.h" +#include <my_dir.h> #endif /* MYSQL_CLIENT */ +#include <assert.h> -static void pretty_print_char(FILE* file, int c) +inline int my_b_safe_write(IO_CACHE* file, const byte *buf, + int len) { + /* + Sasha: We are not writing this with the ? operator to avoid hitting + a possible compiler bug. At least gcc 2.95 cannot deal with + several layers of ternary operators that evaluated comma(,) operator + expressions inside - I do have a test case if somebody wants it + */ + if (file->type == SEQ_READ_APPEND) + return my_b_append(file, buf,len); + return my_b_write(file, buf,len); +} + +#ifdef MYSQL_CLIENT +static void pretty_print_str(FILE* file, char* str, int len) +{ + char* end = str + len; fputc('\'', file); - switch(c) { - case '\n': fprintf(file, "\\n"); break; - case '\r': fprintf(file, "\\r"); break; - case '\\': fprintf(file, "\\\\"); break; - case '\b': fprintf(file, "\\b"); break; - case '\'': fprintf(file, "\\'"); break; - case 0 : fprintf(file, "\\0"); break; - default: - fputc(c, file); - break; + while (str < end) + { + char c; + switch ((c=*str++)) { + case '\n': fprintf(file, "\\n"); break; + 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: + fputc(c, file); + break; + } } fputc('\'', file); } +#endif + +#ifndef MYSQL_CLIENT + +inline int ignored_error_code(int err_code) +{ + return use_slave_mask && bitmap_is_set(&slave_error_mask, err_code); +} + + +static void pretty_print_str(String* packet, char* str, int len) +{ + char* end = str + len; + packet->append('\''); + while (str < end) + { + char c; + switch((c=*str++)) { + 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('\''); +} + + +static inline char* slave_load_file_stem(char*buf, uint file_id, + int event_server_id) +{ + fn_format(buf,"SQL_LOAD-",slave_load_tmpdir,"",4+32); + buf = strend(buf); + buf = int10_to_str(::server_id, buf, 10); + *buf++ = '-'; + buf = int10_to_str(event_server_id, buf, 10); + *buf++ = '-'; + return int10_to_str(file_id, buf, 10); +} + +#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 NEW_LOAD_EVENT: return "New_load"; + case SLAVE_EVENT: return "Slave"; + case CREATE_FILE_EVENT: return "Create_file"; + case APPEND_BLOCK_EVENT: return "Append_block"; + case DELETE_FILE_EVENT: return "Delete_file"; + case EXEC_LOAD_EVENT: return "Exec_load"; + default: /* impossible */ return "Unknown"; + } +} + +#ifndef MYSQL_CLIENT +Log_event::Log_event(THD* thd_arg, uint16 flags_arg): + exec_time(0), + flags(flags_arg),cached_event_len(0), + temp_buf(0),thd(thd_arg) +{ + if (thd) + { + server_id = thd->server_id; + when = thd->start_time; + log_pos = thd->log_pos; + } + else + { + server_id = ::server_id; + when = time(NULL); + log_pos=0; + } +} + +static void cleanup_load_tmpdir() +{ + MY_DIR *dirp; + FILEINFO *file; + uint i; + if (!(dirp=my_dir(slave_load_tmpdir,MYF(MY_WME)))) + return; + + for (i=0;i<(uint)dirp->number_off_files;i++) + { + file=dirp->dir_entry+i; + if (!memcmp(file->name,"SQL_LOAD-",9)) + my_delete(file->name,MYF(MY_WME)); + } + + my_dirend(dirp); +} + +#endif + +Log_event::Log_event(const char* buf, bool old_format): + cached_event_len(0),temp_buf(0) +{ + when = uint4korr(buf); + server_id = uint4korr(buf + SERVER_ID_OFFSET); + if (old_format) + { + log_pos=0; + flags=0; + } + else + { + log_pos = uint4korr(buf + LOG_POS_OFFSET); + flags = uint2korr(buf + FLAGS_OFFSET); + } +#ifndef MYSQL_CLIENT + thd = 0; +#endif +} + + +#ifndef MYSQL_CLIENT + +int Log_event::exec_event(struct st_relay_log_info* rli) +{ + if (rli) + { + rli->inc_pos(get_event_len(),log_pos); + DBUG_ASSERT(rli->sql_thd != 0); + flush_relay_log_info(rli); + } + return 0; +} + +void Log_event::pack_info(String* packet) +{ + net_store_data(packet, "", 0); +} + +void Query_log_event::pack_info(String* packet) +{ + char buf[256]; + String tmp(buf, sizeof(buf)); + tmp.length(0); + 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) +{ + char buf1[256]; + String tmp(buf1, sizeof(buf1)); + tmp.length(0); + 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) +{ + char buf[256]; + String tmp(buf, sizeof(buf)); + tmp.length(0); + if(db && db_len) + { + tmp.append("use "); + tmp.append(db, db_len); + tmp.append("; ", 2); + } + + tmp.append("LOAD DATA INFILE '"); + tmp.append(fname, fname_len); + 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.field_term_len) + { + tmp.append(" FIELDS TERMINATED BY "); + pretty_print_str(&tmp, sql_ex.field_term, sql_ex.field_term_len); + } + + if (sql_ex.enclosed_len) + { + if (sql_ex.opt_flags && OPT_ENCLOSED_FLAG ) + tmp.append(" OPTIONALLY "); + tmp.append( " ENCLOSED BY "); + pretty_print_str(&tmp, sql_ex.enclosed, sql_ex.enclosed_len); + } + + if (sql_ex.escaped_len) + { + tmp.append( " ESCAPED BY "); + pretty_print_str(&tmp, sql_ex.escaped, sql_ex.escaped_len); + } + + if (sql_ex.line_term_len) + { + tmp.append(" LINES TERMINATED BY "); + pretty_print_str(&tmp, sql_ex.line_term, sql_ex.line_term_len); + } + + if (sql_ex.line_start_len) + { + tmp.append(" LINES STARTING BY "); + pretty_print_str(&tmp, sql_ex.line_start, sql_ex.line_start_len); + } + + 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) +{ + char buf1[256]; + String tmp(buf1, sizeof(buf1)); + tmp.length(0); + 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) +{ + char buf1[256]; + String tmp(buf1, sizeof(buf1)); + tmp.length(0); + 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) +{ + char buf1[256]; + String tmp(buf1, sizeof(buf1)); + tmp.length(0); + 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("Orig_log_pos", 20)); + field_list->push_back(new Item_empty_string("Info", 20)); +} + +int Log_event::net_send(THD* thd, const char* log_name, my_off_t 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, (longlong) log_pos); + pack_info(packet); + return my_net_write(&thd->net, (char*) packet->ptr(), packet->length()); +} + +#endif int Query_log_event::write(IO_CACHE* file) { @@ -52,7 +404,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, (ulong) when); // timestamp @@ -63,7 +414,11 @@ int Log_event::write_header(IO_CACHE* file) long tmp=get_data_size() + LOG_EVENT_HEADER_LEN; int4store(pos, tmp); pos += 4; - return (my_b_write(file, (byte*) buf, (uint) (pos - buf))); + int4store(pos, log_pos); + pos += 4; + int2store(pos, flags); + pos += 2; + return (my_b_safe_write(file, (byte*) buf, (uint) (pos - buf))); } #ifndef MYSQL_CLIENT @@ -117,157 +472,144 @@ int Log_event::read_log_event(IO_CACHE* file, String* packet, #define UNLOCK_MUTEX #endif +#ifndef MYSQL_CLIENT +#define LOCK_MUTEX if(log_lock) pthread_mutex_lock(log_lock); +#else +#define LOCK_MUTEX +#endif + // allocates memory - the caller is responsible for clean-up #ifndef MYSQL_CLIENT -Log_event* Log_event::read_log_event(IO_CACHE* file, pthread_mutex_t* log_lock) +Log_event* Log_event::read_log_event(IO_CACHE* file, + pthread_mutex_t* log_lock, + bool old_format) #else -Log_event* Log_event::read_log_event(IO_CACHE* file) +Log_event* Log_event::read_log_event(IO_CACHE* file, bool old_format) #endif { - time_t timestamp; - uint32 server_id; - - char buf[LOG_EVENT_HEADER_LEN-4]; -#ifndef MYSQL_CLIENT - if(log_lock) pthread_mutex_lock(log_lock); -#endif - if (my_b_read(file, (byte *) buf, sizeof(buf))) + char head[LOG_EVENT_HEADER_LEN]; + uint header_size = old_format ? OLD_HEADER_LEN : + LOG_EVENT_HEADER_LEN; + LOCK_MUTEX; + if (my_b_read(file, (byte *) head, header_size )) { - UNLOCK_MUTEX - return NULL; + UNLOCK_MUTEX; + return 0; } - timestamp = uint4korr(buf); - server_id = uint4korr(buf + 5); - - switch(buf[EVENT_TYPE_OFFSET]) - { - case QUERY_EVENT: + + uint data_len = uint4korr(head + EVENT_LEN_OFFSET); + char* buf = 0; + const char* error = 0; + Log_event* res = 0; + + if (data_len > max_allowed_packet) { - Query_log_event* q = new Query_log_event(file, timestamp, server_id); - UNLOCK_MUTEX - if (!q->query) - { - delete q; - q=NULL; - } - return q; + error = "Event too big"; + goto err; } - - case LOAD_EVENT: + + if (data_len < header_size) { - Load_log_event* l = new Load_log_event(file, timestamp, server_id); - UNLOCK_MUTEX - if (!l->table_name) - { - delete l; - l=NULL; - } - return l; + error = "Event too small"; + goto err; } - - case ROTATE_EVENT: + // some events use the extra byte to null-terminate strings + if (!(buf = my_malloc(data_len+1, MYF(MY_WME)))) { - Rotate_log_event* r = new Rotate_log_event(file, timestamp, server_id); - UNLOCK_MUTEX - if (!r->new_log_ident) - { - delete r; - r=NULL; - } - return r; + error = "Out of memory"; + goto err; } - - case INTVAR_EVENT: + buf[data_len] = 0; + memcpy(buf, head, header_size); + if (my_b_read(file, (byte*) buf + header_size, + data_len - header_size)) { - Intvar_log_event* e = new Intvar_log_event(file, timestamp, server_id); - UNLOCK_MUTEX - if (e->type == INVALID_INT_EVENT) - { - delete e; - e=NULL; - } - return e; + error = "read error"; + goto err; } - - case START_EVENT: - { - Start_log_event* e = new Start_log_event(file, timestamp, server_id); - UNLOCK_MUTEX - return e; - } - case STOP_EVENT: - { - Stop_log_event* e = new Stop_log_event(file, timestamp, server_id); - UNLOCK_MUTEX - return e; - } - default: - break; + if ((res = read_log_event(buf, data_len, &error, old_format))) + res->register_temp_buf(buf); +err: + UNLOCK_MUTEX; + if (error) + { + sql_print_error("Error in Log_event::read_log_event(): '%s', \ +data_len=%d,event_type=%d",error,data_len,head[EVENT_TYPE_OFFSET]); + my_free(buf, MYF(MY_ALLOW_ZERO_PTR)); } - - // default - UNLOCK_MUTEX - return NULL; + return res; } -Log_event* Log_event::read_log_event(const char* buf, int event_len) +Log_event* Log_event::read_log_event(const char* buf, int event_len, + const char **error, bool old_format) { - if(event_len < EVENT_LEN_OFFSET || - (uint)event_len != uint4korr(buf+EVENT_LEN_OFFSET)) + if (event_len < EVENT_LEN_OFFSET || + (uint)event_len != uint4korr(buf+EVENT_LEN_OFFSET)) return NULL; // general sanity check - will fail on a partial read + Log_event* ev = NULL; + switch(buf[EVENT_TYPE_OFFSET]) { case QUERY_EVENT: - { - Query_log_event* q = new Query_log_event(buf, event_len); - if (!q->query) - { - delete q; - return NULL; - } - - return q; - } - + ev = new Query_log_event(buf, event_len, old_format); + break; case LOAD_EVENT: - { - Load_log_event* l = new Load_log_event(buf, event_len); - if (!l->table_name) - { - delete l; - return NULL; - } - - return l; - } - + ev = new Create_file_log_event(buf, event_len, old_format); + break; + case NEW_LOAD_EVENT: + ev = new Load_log_event(buf, event_len, old_format); + break; case ROTATE_EVENT: - { - Rotate_log_event* r = new Rotate_log_event(buf, event_len); - if (!r->new_log_ident) - { - delete r; - return NULL; - } - - return r; - } - 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); + ev = new Rotate_log_event(buf, event_len, old_format); + break; + case SLAVE_EVENT: + ev = new Slave_log_event(buf, event_len); + break; + case CREATE_FILE_EVENT: + ev = new Create_file_log_event(buf, event_len, old_format); + break; + case APPEND_BLOCK_EVENT: + ev = new Append_block_log_event(buf, event_len); + break; + case DELETE_FILE_EVENT: + ev = new Delete_file_log_event(buf, event_len); + break; + case EXEC_LOAD_EVENT: + ev = new Execute_load_log_event(buf, event_len); + break; + case START_EVENT: + ev = new Start_log_event(buf, old_format); + break; + case STOP_EVENT: + ev = new Stop_log_event(buf, old_format); + break; + case INTVAR_EVENT: + ev = new Intvar_log_event(buf, old_format); + break; default: break; } - return NULL; // default value + if (!ev) return 0; + if (!ev->is_valid()) + { + *error= "Found invalid event in binary log"; + delete ev; + return 0; + } + ev->cached_event_len = event_len; + return ev; } +#ifdef MYSQL_CLIENT void Log_event::print_header(FILE* file) { + char llbuff[22]; fputc('#', file); print_timestamp(file); - fprintf(file, " server id %d ", server_id); + fprintf(file, " server id %d log_pos %s ", server_id, + llstr(log_pos,llbuff)); } void Log_event::print_timestamp(FILE* file, time_t* ts) @@ -321,6 +663,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; @@ -329,64 +672,55 @@ 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; -} +#endif /* #ifdef MYSQL_CLIENT */ -Start_log_event::Start_log_event(const char* buf) :Log_event(buf) +Start_log_event::Start_log_event(const char* buf, + bool old_format) :Log_event(buf, old_format) { - 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)); + buf += (old_format) ? OLD_HEADER_LEN : LOG_EVENT_HEADER_LEN; + binlog_version = uint2korr(buf+ST_BINLOG_VER_OFFSET); + memcpy(server_version, buf+ST_SERVER_VER_OFFSET, + ST_SERVER_VER_LEN); + created = uint4korr(buf+ST_CREATED_OFFSET); } 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); - return (my_b_write(file, (byte*) buff, sizeof(buff)) ? -1 : 0); + 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_safe_write(file, (byte*) buff, sizeof(buff)) ? -1 : 0); } -Rotate_log_event::Rotate_log_event(const char* buf, int event_len): - Log_event(buf),new_log_ident(NULL),alloced(0) +Rotate_log_event::Rotate_log_event(const char* buf, int event_len, + bool old_format): + Log_event(buf, old_format),new_log_ident(NULL),alloced(0) { // the caller will ensure that event_len is what we have at // EVENT_LEN_OFFSET - if(event_len < ROTATE_EVENT_OVERHEAD) + int header_size = (old_format) ? OLD_HEADER_LEN : LOG_EVENT_HEADER_LEN; + uint ident_offset; + if(event_len < header_size) return; - - ident_len = (uchar)(event_len - ROTATE_EVENT_OVERHEAD); - if (!(new_log_ident = (char*) my_memdup((byte*) buf + LOG_EVENT_HEADER_LEN, + buf += header_size; + if (old_format) + { + ident_len = (uchar)(event_len - OLD_HEADER_LEN); + pos = 4; + ident_offset = 0; + } + else + { + ident_len = (uchar)(event_len - ROTATE_EVENT_OVERHEAD); + pos = uint8korr(buf + R_POS_OFFSET); + ident_offset = ROTATE_HEADER_LEN; + } + if (!(new_log_ident = (char*) my_memdup((byte*) buf + ident_offset, (uint) ident_len, MYF(MY_WME)))) return; @@ -395,68 +729,66 @@ 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; + char buf[ROTATE_HEADER_LEN]; + int8store(buf, pos + R_POS_OFFSET); + return my_b_safe_write(file, (byte*)buf, ROTATE_HEADER_LEN) || + my_b_safe_write(file, (byte*)new_log_ident, (uint) ident_len); } -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)) +#ifndef MYSQL_CLIENT +Query_log_event::Query_log_event(THD* thd_arg, const char* query_arg, + bool using_trans): + Log_event(thd_arg), 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), + cache_stmt(using_trans && + (thd_arg->options & (OPTION_NOT_AUTO_COMMIT | OPTION_BEGIN))) { - my_free((gptr) data_buf, MYF(0)); - data_buf = 0; - return; + time_t end_time; + time(&end_time); + exec_time = (ulong) (end_time - thd->start_time); + db_len = (db) ? (uint32) strlen(db) : 0; } +#endif - 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 -} - -Query_log_event::Query_log_event(const char* buf, int event_len): - Log_event(buf),data_buf(0), query(NULL), db(NULL) +Query_log_event::Query_log_event(const char* buf, int event_len, + bool old_format): + Log_event(buf, old_format),data_buf(0), query(NULL), db(NULL) { - if ((uint)event_len < QUERY_EVENT_OVERHEAD) - return; ulong data_len; - buf += EVENT_LEN_OFFSET; - data_len = event_len - QUERY_EVENT_OVERHEAD; + if (old_format) + { + if ((uint)event_len < OLD_HEADER_LEN + QUERY_HEADER_LEN) + return; + data_len = event_len - (QUERY_HEADER_LEN + OLD_HEADER_LEN); + buf += OLD_HEADER_LEN; + } + else + { + if ((uint)event_len < QUERY_EVENT_OVERHEAD) + return; + data_len = event_len - QUERY_EVENT_OVERHEAD; + buf += LOG_EVENT_HEADER_LEN; + } - exec_time = uint4korr(buf + 8); - error_code = uint2korr(buf + 13); + exec_time = uint4korr(buf + Q_EXEC_TIME_OFFSET); + error_code = uint2korr(buf + 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 + Q_DATA_OFFSET, data_len); + thread_id = uint4korr(buf + Q_THREAD_ID_OFFSET); db = data_buf; - db_len = (uint)buf[12]; + db_len = (uint)buf[Q_DB_LEN_OFFSET]; query=data_buf + db_len + 1; q_len = data_len - 1 - db_len; *((char*)query+q_len) = 0; } +#ifdef MYSQL_CLIENT + void Query_log_event::print(FILE* file, bool short_form, char* last_db) { char buff[40],*end; // Enough for SET TIMESTAMP @@ -485,52 +817,50 @@ void Query_log_event::print(FILE* file, bool short_form, char* last_db) fprintf(file, ";\n"); } +#endif + 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; - - return (my_b_write(file, (byte*) buf, (uint)(pos - buf)) || - my_b_write(file, (db) ? (byte*) db : (byte*)"", db_len + 1) || - my_b_write(file, (byte*) query, q_len)) ? -1 : 0; + 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_safe_write(file, (byte*) buf, QUERY_HEADER_LEN) || + my_b_safe_write(file, (db) ? (byte*) db : (byte*)"", db_len + 1) || + my_b_safe_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, bool old_format): + Log_event(buf, old_format) { - char buf[9+4]; - if (!my_b_read(file, (byte*) buf, sizeof(buf))) - { - type = buf[4]; - val = uint8korr(buf+1+4); - } + buf += (old_format) ? OLD_HEADER_LEN : 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); - return my_b_write(file, (byte*) buf, sizeof(buf)); + buf[I_TYPE_OFFSET] = type; + int8store(buf + I_VAL_OFFSET, val); + return my_b_safe_write(file, (byte*) buf, sizeof(buf)); } +#ifdef MYSQL_CLIENT void Intvar_log_event::print(FILE* file, bool short_form, char* last_db) { char llbuff[22]; @@ -554,100 +884,255 @@ void Intvar_log_event::print(FILE* file, bool short_form, char* last_db) fflush(file); } +#endif -int Load_log_event::write_data(IO_CACHE* file) +int Load_log_event::write_data_header(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); - - if(my_b_write(file, (byte*)buf, sizeof(buf)) || - my_b_write(file, (byte*)&sql_ex, sizeof(sql_ex))) - return 1; + 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); + return my_b_safe_write(file, (byte*)buf, LOAD_HEADER_LEN); +} +int Load_log_event::write_data_body(IO_CACHE* file) +{ + if (sql_ex.write_data(file)) return 1; if (num_fields && fields && field_lens) { - if(my_b_write(file, (byte*)field_lens, num_fields) || - my_b_write(file, (byte*)fields, field_block_len)) + if (my_b_safe_write(file, (byte*)field_lens, num_fields) || + my_b_safe_write(file, (byte*)fields, field_block_len)) return 1; } - if(my_b_write(file, (byte*)table_name, table_name_len + 1) || - my_b_write(file, (byte*)db, db_len + 1) || - my_b_write(file, (byte*)fname, fname_len)) + return (my_b_safe_write(file, (byte*)table_name, table_name_len + 1) || + my_b_safe_write(file, (byte*)db, db_len + 1) || + my_b_safe_write(file, (byte*)fname, fname_len)); +} + + +static bool write_str(IO_CACHE *file, char *str, byte length) +{ + return (my_b_safe_write(file, &length, 1) || + my_b_safe_write(file, (byte*) str, (int) length)); +} + +int sql_ex_info::write_data(IO_CACHE* file) +{ + if (new_format()) + { + return (write_str(file, field_term, field_term_len) || + write_str(file, enclosed, enclosed_len) || + write_str(file, line_term, line_term_len) || + write_str(file, line_start, line_start_len) || + write_str(file, escaped, escaped_len) || + my_b_safe_write(file,(byte*) &opt_flags,1)); + } + else + { + old_sql_ex old_ex; + old_ex.field_term= *field_term; + old_ex.enclosed= *enclosed; + old_ex.line_term= *line_term; + old_ex.line_start= *line_start; + old_ex.escaped= *escaped; + old_ex.opt_flags= opt_flags; + old_ex.empty_flags=empty_flags; + return my_b_safe_write(file, (byte*) &old_ex, sizeof(old_ex)); + } +} + +static inline int read_str(char * &buf, char *buf_end, char * &str, + uint8 &len) +{ + if (buf + (uint) (uchar) *buf >= buf_end) return 1; + len = (uint8) *buf; + str= buf+1; + buf+= (uint) len+1; 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* sql_ex_info::init(char* buf,char* buf_end,bool use_new_format) { - 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); + cached_new_format = use_new_format; + if (use_new_format) + { + empty_flags=0; + /* the code below assumes that buf will not disappear from + under our feet during the lifetime of the event. This assumption + holds true in the slave thread if the log is in new format, but is not + the case when we have old format because we will be reusing net buffer + to read the actual file before we write out the Create_file event + */ + if (read_str(buf, buf_end, field_term, field_term_len) || + read_str(buf, buf_end, enclosed, enclosed_len) || + read_str(buf, buf_end, line_term, line_term_len) || + read_str(buf, buf_end, line_start, line_start_len) || + read_str(buf, buf_end, escaped, escaped_len)) + return 0; + opt_flags = *buf++; + } + else + { + field_term_len= enclosed_len= line_term_len= line_start_len= escaped_len=1; + field_term = buf++; + enclosed= buf++; + line_term= buf++; + line_start= buf++; + escaped= buf++; + opt_flags = *buf++; + empty_flags=*buf++; + if (empty_flags & FIELD_TERM_EMPTY) + field_term_len=0; + if (empty_flags & ENCLOSED_EMPTY) + enclosed_len=0; + if (empty_flags & LINE_TERM_EMPTY) + line_term_len=0; + if (empty_flags & LINE_START_EMPTY) + line_start_len=0; + if (empty_flags & ESCAPED_EMPTY) + escaped_len=0; + } + return buf; } -Load_log_event::Load_log_event(const char* buf, int event_len): - Log_event(buf),data_buf(0),num_fields(0),fields(0), + +#ifndef MYSQL_CLIENT +Load_log_event::Load_log_event(THD* thd, sql_exchange* ex, + const char* db_arg, const char* table_name_arg, + List<Item>& fields_arg, enum enum_duplicates handle_dup): + Log_event(thd),thread_id(thd->thread_id), + num_fields(0),fields(0),field_lens(0),field_block_len(0), + table_name(table_name_arg), + db(db_arg), + fname(ex->file_name) + { + time_t end_time; + time(&end_time); + exec_time = (ulong) (end_time - thd->start_time); + db_len = (db) ? (uint32) strlen(db) : 0; + table_name_len = (table_name) ? (uint32) strlen(table_name) : 0; + fname_len = (fname) ? (uint) strlen(fname) : 0; + sql_ex.field_term = (char*) ex->field_term->ptr(); + sql_ex.field_term_len = (uint8) ex->field_term->length(); + sql_ex.enclosed = (char*) ex->enclosed->ptr(); + sql_ex.enclosed_len = (uint8) ex->enclosed->length(); + sql_ex.line_term = (char*) ex->line_term->ptr(); + sql_ex.line_term_len = (uint8) ex->line_term->length(); + sql_ex.line_start = (char*) ex->line_start->ptr(); + sql_ex.line_start_len = (uint8) ex->line_start->length(); + sql_ex.escaped = (char*) ex->escaped->ptr(); + sql_ex.escaped_len = (uint8) ex->escaped->length(); + sql_ex.opt_flags = 0; + sql_ex.cached_new_format = -1; + + if(ex->dumpfile) + sql_ex.opt_flags |= DUMPFILE_FLAG; + if(ex->opt_enclosed) + sql_ex.opt_flags |= OPT_ENCLOSED_FLAG; + + sql_ex.empty_flags = 0; + + switch(handle_dup) + { + case DUP_IGNORE: sql_ex.opt_flags |= IGNORE_FLAG; break; + case DUP_REPLACE: sql_ex.opt_flags |= REPLACE_FLAG; break; + case DUP_ERROR: break; + } + + + if(!ex->field_term->length()) + sql_ex.empty_flags |= FIELD_TERM_EMPTY; + if(!ex->enclosed->length()) + sql_ex.empty_flags |= ENCLOSED_EMPTY; + if(!ex->line_term->length()) + sql_ex.empty_flags |= LINE_TERM_EMPTY; + if(!ex->line_start->length()) + sql_ex.empty_flags |= LINE_START_EMPTY; + if(!ex->escaped->length()) + sql_ex.empty_flags |= ESCAPED_EMPTY; + + skip_lines = ex->skip_lines; + + List_iterator<Item> li(fields_arg); + field_lens_buf.length(0); + fields_buf.length(0); + Item* item; + while((item = li++)) + { + num_fields++; + uchar len = (uchar) strlen(item->name); + field_block_len += len + 1; + fields_buf.append(item->name, len + 1); + field_lens_buf.append((char*)&len, 1); + } + + field_lens = (const uchar*)field_lens_buf.ptr(); + fields = fields_buf.ptr(); + } + +#endif + +// the caller must do buf[event_len] = 0 before he starts using the +// constructed event +Load_log_event::Load_log_event(const char* buf, int event_len, + bool old_format): + Log_event(buf, old_format),num_fields(0),fields(0), field_lens(0),field_block_len(0), table_name(0),db(0),fname(0) { - ulong data_len; - - if((uint)event_len < (LOAD_EVENT_OVERHEAD + LOG_EVENT_HEADER_LEN)) + if (!event_len) // derived class, will call copy_log_event() itself return; - buf += EVENT_LEN_OFFSET; - memcpy(&sql_ex, buf + LOAD_HEADER_LEN + 4, sizeof(sql_ex)); - data_len = event_len; - - if(!(data_buf = (char*)my_malloc(data_len + 1, MYF(MY_WME)))) - return; - memcpy(data_buf, buf + 22 + sizeof(sql_ex), data_len); - copy_log_event(buf, data_len); + copy_log_event(buf, event_len, old_format); } -void Load_log_event::copy_log_event(const char *buf, ulong data_len) +int Load_log_event::copy_log_event(const char *buf, ulong event_len, + bool old_format) { - 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); + uint data_len; + char* buf_end = (char*)buf + event_len; + const char* data_head = buf + ((old_format) ? + OLD_HEADER_LEN : LOG_EVENT_HEADER_LEN); + thread_id = uint4korr(data_head + L_THREAD_ID_OFFSET); + exec_time = uint4korr(data_head + L_EXEC_TIME_OFFSET); + skip_lines = uint4korr(data_head + L_SKIP_LINES_OFFSET); + table_name_len = (uint)data_head[L_TBL_LEN_OFFSET]; + db_len = (uint)data_head[L_DB_LEN_OFFSET]; + num_fields = uint4korr(data_head + L_NUM_FIELDS_OFFSET); + int body_offset = (buf[EVENT_TYPE_OFFSET] == LOAD_EVENT) ? + LOAD_HEADER_LEN + OLD_HEADER_LEN : get_data_body_offset(); + + if ((int) event_len < body_offset) + return 1; + //sql_ex.init() on success returns the pointer to the first byte after + //the sql_ex structure, which is the start of field lengths array + if (!(field_lens=(uchar*)sql_ex.init((char*)buf + body_offset, + buf_end, + buf[EVENT_TYPE_OFFSET] != LOAD_EVENT))) + return 1; + + data_len = event_len - body_offset; if (num_fields > data_len) // simple sanity check against corruption - return; - - field_lens = (uchar*) data_buf; + return 1; uint i; for (i = 0; i < num_fields; i++) { field_block_len += (uint)field_lens[i] + 1; } fields = (char*)field_lens + num_fields; - - *((char*)data_buf+data_len) = 0; table_name = fields + field_block_len; db = table_name + table_name_len + 1; fname = db + db_len + 1; - fname_len = data_len - 2 - db_len - table_name_len - num_fields - - field_block_len; + fname_len = strlen(fname); + // null termination is accomplished by the caller doing buf[event_len]=0 + return 0; } +#ifdef MYSQL_CLIENT void Load_log_event::print(FILE* file, bool short_form, char* last_db) { @@ -669,7 +1154,7 @@ void Load_log_event::print(FILE* file, bool short_form, char* last_db) if(db && db[0] && !same_db) fprintf(file, "use %s;\n", db); - fprintf(file, "LOAD DATA INFILE '%s' ", fname); + fprintf(file, "LOAD DATA INFILE '%-*s' ", fname_len, fname); if(sql_ex.opt_flags && REPLACE_FLAG ) fprintf(file," REPLACE "); @@ -677,36 +1162,36 @@ void Load_log_event::print(FILE* file, bool short_form, char* last_db) fprintf(file," IGNORE "); fprintf(file, "INTO TABLE %s ", table_name); - if(!(sql_ex.empty_flags & FIELD_TERM_EMPTY)) + if(sql_ex.field_term) { fprintf(file, " FIELDS TERMINATED BY "); - pretty_print_char(file, sql_ex.field_term); + pretty_print_str(file, sql_ex.field_term, sql_ex.field_term_len); } - if(!(sql_ex.empty_flags & ENCLOSED_EMPTY)) + if(sql_ex.enclosed) { if(sql_ex.opt_flags && OPT_ENCLOSED_FLAG ) fprintf(file," OPTIONALLY "); fprintf(file, " ENCLOSED BY "); - pretty_print_char(file, sql_ex.enclosed); + pretty_print_str(file, sql_ex.enclosed, sql_ex.enclosed_len); } - if(!(sql_ex.empty_flags & ESCAPED_EMPTY)) + if (sql_ex.escaped) { fprintf(file, " ESCAPED BY "); - pretty_print_char(file, sql_ex.escaped); + pretty_print_str(file, sql_ex.escaped, sql_ex.escaped_len); } - if(!(sql_ex.empty_flags & LINE_TERM_EMPTY)) + if (sql_ex.line_term) { fprintf(file," LINES TERMINATED BY "); - pretty_print_char(file, sql_ex.line_term); + pretty_print_str(file, sql_ex.line_term, sql_ex.line_term_len); } - if(!(sql_ex.empty_flags & LINE_START_EMPTY)) + if (sql_ex.line_start) { fprintf(file," LINES STARTING BY "); - pretty_print_char(file, sql_ex.line_start); + pretty_print_str(file, sql_ex.line_start, sql_ex.line_start_len); } if((int)skip_lines > 0) @@ -731,18 +1216,796 @@ void Load_log_event::print(FILE* file, bool short_form, char* last_db) fprintf(file, ";\n"); } +#endif /* #ifdef MYSQL_CLIENT */ + #ifndef MYSQL_CLIENT +void Log_event::set_log_pos(MYSQL_LOG* log) + { + if (!log_pos) + log_pos = my_b_tell(&log->log_file); + } + void Load_log_event::set_fields(List<Item> &fields) { uint i; const char* field = this->fields; - for(i = 0; i < num_fields; i++) + for (i = 0; i < num_fields; i++) + { + fields.push_back(new Item_field(db, table_name, field)); + field += field_lens[i] + 1; + } +} + + +Slave_log_event::Slave_log_event(THD* thd_arg, + struct st_relay_log_info* rli): + Log_event(thd_arg),mem_pool(0),master_host(0) +{ + if (!rli->inited) + return; + + MASTER_INFO* mi = rli->mi; + // TODO: re-write this better without holding both locks at the same time + pthread_mutex_lock(&mi->data_lock); + pthread_mutex_lock(&rli->data_lock); + master_host_len = strlen(mi->host); + master_log_len = strlen(rli->master_log_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, rli->master_log_name, master_log_len + 1); + master_port = mi->port; + master_pos = rli->master_log_pos; + } + else + sql_print_error("Out of memory while recording slave event"); + pthread_mutex_unlock(&rli->data_lock); + pthread_mutex_unlock(&mi->data_lock); +} + + +#endif + + +Slave_log_event::~Slave_log_event() +{ + my_free(mem_pool, MYF(MY_ALLOW_ZERO_PTR)); +} + +#ifdef MYSQL_CLIENT + +void Slave_log_event::print(FILE* file, bool short_form, char* last_db) +{ + 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)); +} + +#endif + +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_safe_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,0),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); +} + +#ifndef MYSQL_CLIENT +Create_file_log_event::Create_file_log_event(THD* thd_arg, sql_exchange* ex, + const char* db_arg, const char* table_name_arg, + List<Item>& fields_arg, enum enum_duplicates handle_dup, + char* block_arg, uint block_len_arg): + Load_log_event(thd_arg,ex,db_arg,table_name_arg,fields_arg,handle_dup), + fake_base(0),block(block_arg),block_len(block_len_arg), + file_id(thd_arg->file_id = mysql_bin_log.next_file_id()) +{ + sql_ex.force_new_format(); +} +#endif + +int Create_file_log_event::write_data_body(IO_CACHE* file) +{ + int res; + if ((res = Load_log_event::write_data_body(file)) || fake_base) + return res; + return (my_b_safe_write(file, (byte*) "", 1) || + my_b_safe_write(file, (byte*) block, block_len)); +} + +int Create_file_log_event::write_data_header(IO_CACHE* file) +{ + int res; + if ((res = Load_log_event::write_data_header(file)) || fake_base) + return res; + byte buf[CREATE_FILE_HEADER_LEN]; + int4store(buf + CF_FILE_ID_OFFSET, file_id); + return my_b_safe_write(file, buf, CREATE_FILE_HEADER_LEN); +} + +int Create_file_log_event::write_base(IO_CACHE* file) +{ + int res; + fake_base = 1; // pretend we are Load event + res = write(file); + fake_base = 0; + return res; +} + +Create_file_log_event::Create_file_log_event(const char* buf, int len, + bool old_format): + Load_log_event(buf,0,old_format),fake_base(0),block(0),inited_from_old(0) +{ + int block_offset; + if (copy_log_event(buf,len,old_format)) + return; + if (!old_format) + { + file_id = uint4korr(buf + LOG_EVENT_HEADER_LEN + + + LOAD_HEADER_LEN + CF_FILE_ID_OFFSET); + block_offset = LOG_EVENT_HEADER_LEN + Load_log_event::get_data_size() + + CREATE_FILE_HEADER_LEN + 1; // 1 for \0 terminating fname + if (len < block_offset) + return; + block = (char*)buf + block_offset; + block_len = len - block_offset; + } + else + { + sql_ex.force_new_format(); + inited_from_old = 1; + } +} + + +#ifdef MYSQL_CLIENT +void Create_file_log_event::print(FILE* file, bool short_form, + char* last_db) +{ + if (short_form) + return; + Load_log_event::print(file, 1, last_db); + fprintf(file, " file_id=%d, block_len=%d\n", file_id, block_len); +} +#endif + +#ifndef MYSQL_CLIENT +void Create_file_log_event::pack_info(String* packet) +{ + char buf1[256]; + String tmp(buf1, sizeof(buf1)); + tmp.length(0); + char buf[22]; + tmp.append("db="); + tmp.append(db, db_len); + tmp.append(";table="); + tmp.append(table_name, table_name_len); + tmp.append(";file_id="); + tmp.append(llstr(file_id,buf)); + tmp.append(";block_len="); + tmp.append(llstr(block_len,buf)); + net_store_data(packet, (char*)tmp.ptr(), tmp.length()); +} +#endif + +#ifndef MYSQL_CLIENT +Append_block_log_event::Append_block_log_event(THD* thd_arg, char* block_arg, + uint block_len_arg): + Log_event(thd_arg), block(block_arg),block_len(block_len_arg), + file_id(thd_arg->file_id) +{ +} +#endif + +Append_block_log_event::Append_block_log_event(const char* buf, int len): + Log_event(buf, 0),block(0) +{ + if((uint)len < APPEND_BLOCK_EVENT_OVERHEAD) + return; + file_id = uint4korr(buf + LOG_EVENT_HEADER_LEN + AB_FILE_ID_OFFSET); + block = (char*)buf + APPEND_BLOCK_EVENT_OVERHEAD; + block_len = len - APPEND_BLOCK_EVENT_OVERHEAD; +} + +int Append_block_log_event::write_data(IO_CACHE* file) +{ + byte buf[APPEND_BLOCK_HEADER_LEN]; + int4store(buf + AB_FILE_ID_OFFSET, file_id); + return (my_b_safe_write(file, buf, APPEND_BLOCK_HEADER_LEN) || + my_b_safe_write(file, (byte*) block, block_len)); +} + +#ifdef MYSQL_CLIENT +void Append_block_log_event::print(FILE* file, bool short_form, + char* last_db) +{ + if (short_form) + return; + print_header(file); + fputc('\n', file); + fprintf(file, "#Append_block: file_id=%d, block_len=%d\n", + file_id, block_len); +} +#endif +#ifndef MYSQL_CLIENT +void Append_block_log_event::pack_info(String* packet) +{ + char buf1[256]; + String tmp(buf1, sizeof(buf1)); + tmp.length(0); + char buf[22]; + tmp.append(";file_id="); + tmp.append(llstr(file_id,buf)); + tmp.append(";block_len="); + tmp.append(llstr(block_len,buf)); + net_store_data(packet, (char*)tmp.ptr(), tmp.length()); +} +#endif + +#ifndef MYSQL_CLIENT +Delete_file_log_event::Delete_file_log_event(THD* thd_arg): + Log_event(thd_arg),file_id(thd_arg->file_id) +{ +} +#endif + +Delete_file_log_event::Delete_file_log_event(const char* buf, int len): + Log_event(buf, 0),file_id(0) +{ + if((uint)len < DELETE_FILE_EVENT_OVERHEAD) + return; + file_id = uint4korr(buf + LOG_EVENT_HEADER_LEN + AB_FILE_ID_OFFSET); +} + +int Delete_file_log_event::write_data(IO_CACHE* file) +{ + byte buf[DELETE_FILE_HEADER_LEN]; + int4store(buf + DF_FILE_ID_OFFSET, file_id); + return my_b_safe_write(file, buf, DELETE_FILE_HEADER_LEN); +} + +#ifdef MYSQL_CLIENT +void Delete_file_log_event::print(FILE* file, bool short_form, + char* last_db) +{ + if (short_form) + return; + print_header(file); + fputc('\n', file); + fprintf(file, "#Delete_file: file_id=%d\n", + file_id); +} +#endif +#ifndef MYSQL_CLIENT +void Delete_file_log_event::pack_info(String* packet) +{ + char buf1[256]; + String tmp(buf1, sizeof(buf1)); + tmp.length(0); + char buf[22]; + tmp.append(";file_id="); + tmp.append(llstr(file_id,buf)); + net_store_data(packet, (char*)tmp.ptr(), tmp.length()); +} +#endif + +#ifndef MYSQL_CLIENT +Execute_load_log_event::Execute_load_log_event(THD* thd_arg): + Log_event(thd_arg),file_id(thd_arg->file_id) +{ +} +#endif + +Execute_load_log_event::Execute_load_log_event(const char* buf,int len): + Log_event(buf, 0),file_id(0) +{ + if((uint)len < EXEC_LOAD_EVENT_OVERHEAD) + return; + file_id = uint4korr(buf + LOG_EVENT_HEADER_LEN + EL_FILE_ID_OFFSET); +} + +int Execute_load_log_event::write_data(IO_CACHE* file) +{ + byte buf[EXEC_LOAD_HEADER_LEN]; + int4store(buf + EL_FILE_ID_OFFSET, file_id); + return my_b_safe_write(file, buf, EXEC_LOAD_HEADER_LEN); +} + +#ifdef MYSQL_CLIENT +void Execute_load_log_event::print(FILE* file, bool short_form, + char* last_db) +{ + if (short_form) + return; + print_header(file); + fputc('\n', file); + fprintf(file, "#Exec_load: file_id=%d\n", + file_id); +} +#endif +#ifndef MYSQL_CLIENT +void Execute_load_log_event::pack_info(String* packet) +{ + char buf1[256]; + String tmp(buf1, sizeof(buf1)); + tmp.length(0); + char buf[22]; + tmp.append(";file_id="); + tmp.append(llstr(file_id,buf)); + net_store_data(packet, (char*)tmp.ptr(), tmp.length()); +} +#endif + +#ifndef MYSQL_CLIENT +int Query_log_event::exec_event(struct st_relay_log_info* rli) +{ + int expected_error,actual_error = 0; + init_sql_alloc(&thd->mem_root, 8192,0); + thd->db = rewrite_db((char*)db); + DBUG_ASSERT(q_len == strlen(query)); + if (db_ok(thd->db, replicate_do_db, replicate_ignore_db)) + { + thd->query = (char*)query; + thd->set_time((time_t)when); + thd->current_tablenr = 0; + VOID(pthread_mutex_lock(&LOCK_thread_count)); + thd->query_id = query_id++; + VOID(pthread_mutex_unlock(&LOCK_thread_count)); + thd->query_error = 0; // clear error + thd->net.last_errno = 0; + thd->net.last_error[0] = 0; + thd->slave_proxy_id = thread_id; // for temp tables + + // sanity check to make sure the master did not get a really bad + // error on the query + if (ignored_error_code((expected_error = error_code)) || + !check_expected_error(thd,rli,expected_error)) { - fields.push_back(new Item_field(db, table_name, field)); - field += field_lens[i] + 1; + mysql_parse(thd, thd->query, q_len); + if (expected_error != + (actual_error = thd->net.last_errno) && expected_error && + !ignored_error_code(actual_error) && + !ignored_error_code(expected_error)) + { + const char* errmsg = "Slave: did not get the expected error\ + running query from master - expected: '%s' (%d), got '%s' (%d)"; + sql_print_error(errmsg, ER_SAFE(expected_error), + expected_error, + actual_error ? thd->net.last_error: "no error", + actual_error); + thd->query_error = 1; + } + else if (expected_error == actual_error + || ignored_error_code(actual_error)) + { + thd->query_error = 0; + *rli->last_slave_error = 0; + rli->last_slave_errno = 0; + } } + else + { + // master could be inconsistent, abort and tell DBA to check/fix it + thd->db = thd->query = 0; + thd->convert_set = 0; + close_thread_tables(thd); + free_root(&thd->mem_root,0); + return 1; + } + } + thd->db = 0; // prevent db from being freed + thd->query = 0; // just to be sure + // assume no convert for next query unless set explictly + thd->convert_set = 0; + close_thread_tables(thd); + + if (thd->query_error || thd->fatal_error) + { + slave_print_error(rli,actual_error, "error '%s' on query '%s'", + actual_error ? thd->net.last_error : + "unexpected success or fatal error", query); + free_root(&thd->mem_root,0); + return 1; + } + free_root(&thd->mem_root,0); + return Log_event::exec_event(rli); +} + +int Load_log_event::exec_event(NET* net, struct st_relay_log_info* rli) +{ + init_sql_alloc(&thd->mem_root, 8192,0); + thd->db = rewrite_db((char*)db); + thd->query = 0; + thd->query_error = 0; + + if(db_ok(thd->db, replicate_do_db, replicate_ignore_db)) + { + thd->set_time((time_t)when); + thd->current_tablenr = 0; + VOID(pthread_mutex_lock(&LOCK_thread_count)); + thd->query_id = query_id++; + VOID(pthread_mutex_unlock(&LOCK_thread_count)); + + TABLE_LIST tables; + bzero((char*) &tables,sizeof(tables)); + tables.db = thd->db; + tables.name = tables.real_name = (char*)table_name; + tables.lock_type = TL_WRITE; + // the table will be opened in mysql_load + if(table_rules_on && !tables_ok(thd, &tables)) + { + // TODO: this is a bug - this needs to be moved to the I/O thread + if (net) + skip_load_data_infile(net); + } + else + { + char llbuff[22]; + enum enum_duplicates handle_dup = DUP_IGNORE; + if (sql_ex.opt_flags && REPLACE_FLAG) + handle_dup = DUP_REPLACE; + sql_exchange ex((char*)fname, sql_ex.opt_flags && + DUMPFILE_FLAG ); + String field_term(sql_ex.field_term,sql_ex.field_term_len); + String enclosed(sql_ex.enclosed,sql_ex.enclosed_len); + String line_term(sql_ex.line_term,sql_ex.line_term_len); + String line_start(sql_ex.line_start,sql_ex.line_start_len); + String escaped(sql_ex.escaped,sql_ex.escaped_len); + + ex.opt_enclosed = (sql_ex.opt_flags & OPT_ENCLOSED_FLAG); + if (sql_ex.empty_flags & FIELD_TERM_EMPTY) + ex.field_term->length(0); + + ex.skip_lines = skip_lines; + List<Item> fields; + set_fields(fields); + thd->slave_proxy_id = thd->thread_id; + if (net) + { + // mysql_load will use thd->net to read the file + thd->net.vio = net->vio; + // make sure the client does not get confused + // about the packet sequence + thd->net.pkt_nr = net->pkt_nr; + } + if(mysql_load(thd, &ex, &tables, fields, handle_dup, net != 0, + TL_WRITE)) + thd->query_error = 1; + if(thd->cuted_fields) + sql_print_error("Slave: load data infile at position %s in log \ +'%s' produced %d warning(s)", llstr(rli->master_log_pos,llbuff), RPL_LOG_NAME, + thd->cuted_fields ); + if(net) + net->pkt_nr = thd->net.pkt_nr; + } + } + else + { + // we will just ask the master to send us /dev/null if we do not + // want to load the data + // TODO: this a bug - needs to be done in I/O thread + if (net) + skip_load_data_infile(net); + } + + thd->net.vio = 0; + thd->db = 0;// prevent db from being freed + close_thread_tables(thd); + if(thd->query_error) + { + int sql_error = thd->net.last_errno; + if (!sql_error) + sql_error = ER_UNKNOWN_ERROR; + + slave_print_error(rli,sql_error, + "Slave: Error '%s' running load data infile ", + ER_SAFE(sql_error)); + free_root(&thd->mem_root,0); + return 1; + } + free_root(&thd->mem_root,0); + + if(thd->fatal_error) + { + sql_print_error("Slave: Fatal error running LOAD DATA INFILE "); + return 1; + } + + return Log_event::exec_event(rli); +} + +int Start_log_event::exec_event(struct st_relay_log_info* rli) +{ + close_temporary_tables(thd); + // if we have old format, load_tmpdir is cleaned up by the I/O thread + // TODO: cleanup_load_tmpdir() needs to remove only the files associated + // with the server id that has just started + if (!rli->mi->old_format) + cleanup_load_tmpdir(); + return Log_event::exec_event(rli); +} + +int Stop_log_event::exec_event(struct st_relay_log_info* rli) +{ + // do not clean up immediately after rotate event + if (rli->master_log_pos > 4) + { + close_temporary_tables(thd); + cleanup_load_tmpdir(); + } + /* + We do not want to update master_log pos because we get a rotate event + before stop, so by now master_log_name is set to the next log + if we updated it, we will have incorrect master coordinates and this + could give false triggers in MASTER_POS_WAIT() that we have reached + the targed position when in fact we have not + */ + rli->inc_pos(get_event_len(), 0); + flush_relay_log_info(rli); + return 0; +} + +int Rotate_log_event::exec_event(struct st_relay_log_info* rli) +{ + bool rotate_binlog = 0, write_slave_event = 0; + char* log_name = rli->master_log_name; + pthread_mutex_lock(&rli->data_lock); + // TODO: probably needs re-write + // rotate local binlog only if the name of remote has changed + if (!*log_name || !(log_name[ident_len] == 0 && + !memcmp(log_name, new_log_ident, ident_len))) + { + write_slave_event = (!(flags & LOG_EVENT_FORCED_ROTATE_F) + && mysql_bin_log.is_open()); + rotate_binlog = (*log_name && write_slave_event); + if (ident_len >= sizeof(rli->master_log_name)) + return 1; + memcpy(log_name, new_log_ident,ident_len); + log_name[ident_len] = 0; + } + rli->master_log_pos = pos; + rli->relay_log_pos += get_event_len(); + if (rotate_binlog) + { + mysql_bin_log.new_file(); + rli->master_log_pos = 4; + } + pthread_cond_broadcast(&rli->data_cond); + pthread_mutex_unlock(&rli->data_lock); + flush_relay_log_info(rli); + + if (write_slave_event) + { + Slave_log_event s(thd, rli); + if (s.master_host) + { + s.set_log_pos(&mysql_bin_log); + s.server_id = ::server_id; + mysql_bin_log.write(&s); + } + } + return 0; +} + +int Intvar_log_event::exec_event(struct st_relay_log_info* rli) +{ + switch (type) + { + case LAST_INSERT_ID_EVENT: + thd->last_insert_id_used = 1; + thd->last_insert_id = val; + break; + case INSERT_ID_EVENT: + thd->next_insert_id = val; + break; + } + rli->inc_pending(get_event_len()); + return 0; +} + +int Slave_log_event::exec_event(struct st_relay_log_info* rli) +{ + if(mysql_bin_log.is_open()) + mysql_bin_log.write(this); + return Log_event::exec_event(rli); +} + +int Create_file_log_event::exec_event(struct st_relay_log_info* rli) +{ + char fname_buf[FN_REFLEN+10]; + char *p; + int fd = -1; + IO_CACHE file; + int error = 1; + + bzero((char*)&file, sizeof(file)); + p = slave_load_file_stem(fname_buf, file_id, server_id); + strmov(p, ".info"); // strmov takes less code than memcpy + if ((fd = my_open(fname_buf, O_WRONLY|O_CREAT|O_BINARY|O_TRUNC, + MYF(MY_WME))) < 0 || + init_io_cache(&file, fd, IO_SIZE, WRITE_CACHE, (my_off_t)0, 0, + MYF(MY_WME|MY_NABP))) + { + slave_print_error(rli,my_errno, "Could not open file '%s'", fname_buf); + goto err; + } + // a trick to avoid allocating another buffer + strmov(p, ".data"); + fname = fname_buf; + fname_len = (uint)(p-fname) + 5; + if (write_base(&file)) + { + strmov(p, ".info"); // to have it right in the error message + slave_print_error(rli,my_errno, "Could not write to file '%s'", fname_buf); + goto err; + } + end_io_cache(&file); + my_close(fd, MYF(0)); + + // fname_buf now already has .data, not .info, because we did our trick + if ((fd = my_open(fname_buf, O_WRONLY|O_CREAT|O_BINARY|O_TRUNC, + MYF(MY_WME))) < 0) + { + slave_print_error(rli,my_errno, "Could not open file '%s'", fname_buf); + goto err; + } + if (my_write(fd, (byte*) block, block_len, MYF(MY_WME+MY_NABP))) + { + slave_print_error(rli,my_errno, "Write to '%s' failed", fname_buf); + goto err; + } + if (mysql_bin_log.is_open()) + mysql_bin_log.write(this); + error=0; +err: + if (error) + end_io_cache(&file); + if (fd >= 0) + my_close(fd, MYF(0)); + return error ? 1 : Log_event::exec_event(rli); +} + +int Delete_file_log_event::exec_event(struct st_relay_log_info* rli) +{ + char fname[FN_REFLEN+10]; + char* p; + p = slave_load_file_stem(fname, file_id, server_id); + memcpy(p, ".data", 6); + (void)my_delete(fname, MYF(MY_WME)); + memcpy(p, ".info", 6); + (void)my_delete(fname, MYF(MY_WME)); + if (mysql_bin_log.is_open()) + mysql_bin_log.write(this); + return Log_event::exec_event(rli); } +int Append_block_log_event::exec_event(struct st_relay_log_info* rli) +{ + char fname[FN_REFLEN+10]; + char* p; + int fd = -1; + int error = 1; + p = slave_load_file_stem(fname, file_id, server_id); + memcpy(p, ".data", 6); + if ((fd = my_open(fname, O_WRONLY|O_APPEND|O_BINARY, MYF(MY_WME))) < 0) + { + slave_print_error(rli,my_errno, "Could not open file '%s'", fname); + goto err; + } + if (my_write(fd, (byte*) block, block_len, MYF(MY_WME+MY_NABP))) + { + slave_print_error(rli,my_errno, "Write to '%s' failed", fname); + goto err; + } + if (mysql_bin_log.is_open()) + mysql_bin_log.write(this); + error=0; +err: + if (fd >= 0) + my_close(fd, MYF(0)); + return error ? error : Log_event::exec_event(rli); +} + +int Execute_load_log_event::exec_event(struct st_relay_log_info* rli) +{ + char fname[FN_REFLEN+10]; + char* p; + int fd = -1; + int error = 1; + ulong save_options; + IO_CACHE file; + Load_log_event* lev = 0; + p = slave_load_file_stem(fname, file_id, server_id); + memcpy(p, ".info", 6); + bzero((char*)&file, sizeof(file)); + if ((fd = my_open(fname, O_RDONLY|O_BINARY, MYF(MY_WME))) < 0 || + init_io_cache(&file, fd, IO_SIZE, READ_CACHE, (my_off_t)0, 0, + MYF(MY_WME|MY_NABP))) + { + slave_print_error(rli,my_errno, "Could not open file '%s'", fname); + goto err; + } + if (!(lev = (Load_log_event*)Log_event::read_log_event(&file, + (pthread_mutex_t*)0, + (bool)0)) + || lev->get_type_code() != NEW_LOAD_EVENT) + { + slave_print_error(rli,0, "File '%s' appears corrupted", fname); + goto err; + } + // we want to disable binary logging in slave thread + // because we need the file events to appear in the same order + // as they do on the master relative to other events, so that we + // can preserve ascending order of log sequence numbers - needed + // to handle failover + save_options = thd->options; + thd->options &= ~ (ulong) (OPTION_BIN_LOG); + lev->thd = thd; + if (lev->exec_event(0,0)) + { + slave_print_error(rli,my_errno, "Failed executing load from '%s'", fname); + thd->options = save_options; + goto err; + } + thd->options = save_options; + (void)my_delete(fname, MYF(MY_WME)); + memcpy(p, ".data", 6); + (void)my_delete(fname, MYF(MY_WME)); + if (mysql_bin_log.is_open()) + mysql_bin_log.write(this); + error = 0; +err: + delete lev; + end_io_cache(&file); + if (fd >= 0) + my_close(fd, MYF(0)); + return error ? error : Log_event::exec_event(rli); +} + + #endif diff --git a/sql/log_event.h b/sql/log_event.h index a31c698fae9..a29c3952d46 100644 --- a/sql/log_event.h +++ b/sql/log_event.h @@ -34,40 +34,195 @@ #define LOG_READ_TOO_LARGE -7 #define LOG_EVENT_OFFSET 4 -#define BINLOG_VERSION 1 +#define BINLOG_VERSION 3 + +/* 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 + +#define DUMPFILE_FLAG 0x1 +#define OPT_ENCLOSED_FLAG 0x2 +#define REPLACE_FLAG 0x4 +#define IGNORE_FLAG 0x8 + +#define FIELD_TERM_EMPTY 0x1 +#define ENCLOSED_EMPTY 0x2 +#define LINE_TERM_EMPTY 0x4 +#define LINE_START_EMPTY 0x8 +#define ESCAPED_EMPTY 0x10 + +struct old_sql_ex + { + char field_term; + char enclosed; + char line_term; + char line_start; + char escaped; + char opt_flags; + char empty_flags; + }; + +#define NUM_LOAD_DELIM_STRS 5 + + +struct sql_ex_info + { + char* field_term; + char* enclosed; + char* line_term; + char* line_start; + char* escaped; + uint8 field_term_len,enclosed_len,line_term_len,line_start_len, + escaped_len; + char opt_flags; + char empty_flags; + int cached_new_format; + + // store in new format even if old is possible + void force_new_format() { cached_new_format = 1;} + int data_size() { return new_format() ? + field_term_len + enclosed_len + line_term_len + + line_start_len + escaped_len + 6 : 7;} + int write_data(IO_CACHE* file); + char* init(char* buf,char* buf_end,bool use_new_format); + bool new_format() + { + return (cached_new_format != -1) ? cached_new_format : + (cached_new_format=(field_term_len > 1 || + enclosed_len > 1 || + line_term_len > 1 || line_start_len > 1 || + escaped_len > 1)); + } + } ; + +/* 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 OLD_HEADER_LEN 13 +#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 +#define CREATE_FILE_HEADER_LEN 4 +#define APPEND_BLOCK_HEADER_LEN 4 +#define EXEC_LOAD_HEADER_LEN 4 +#define DELETE_FILE_HEADER_LEN 4 + +/* 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_POS_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_TBL_LEN_OFFSET 12 +#define L_DB_LEN_OFFSET 13 +#define L_NUM_FIELDS_OFFSET 14 +#define L_SQL_EX_OFFSET 18 +#define L_DATA_OFFSET LOAD_HEADER_LEN + +/* Rotate event post-header */ + +#define R_POS_OFFSET 0 +#define R_IDENT_OFFSET 8 + +#define CF_FILE_ID_OFFSET 0 +#define CF_DATA_OFFSET CREATE_FILE_HEADER_LEN + +#define AB_FILE_ID_OFFSET 0 +#define AB_DATA_OFFSET APPEND_BLOCK_HEADER_LEN + +#define EL_FILE_ID_OFFSET 0 + +#define DF_FILE_ID_OFFSET 0 + #define QUERY_EVENT_OVERHEAD (LOG_EVENT_HEADER_LEN+QUERY_HEADER_LEN) -#define ROTATE_EVENT_OVERHEAD LOG_EVENT_HEADER_LEN -#define LOAD_EVENT_OVERHEAD (LOG_EVENT_HEADER_LEN+LOAD_HEADER_LEN+sizeof(sql_ex_info)) +#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) +#define CREATE_FILE_EVENT_OVERHEAD (LOG_EVENT_HEADER_LEN+\ + +LOAD_HEADER_LEN+CREATE_FILE_HEADER_LEN) +#define DELETE_FILE_EVENT_OVERHEAD (LOG_EVENT_HEADER_LEN+DELETE_FILE_HEADER_LEN) +#define EXEC_LOAD_EVENT_OVERHEAD (LOG_EVENT_HEADER_LEN+EXEC_LOAD_HEADER_LEN) +#define APPEND_BLOCK_EVENT_OVERHEAD (LOG_EVENT_HEADER_LEN+APPEND_BLOCK_HEADER_LEN) + #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, CREATE_FILE_EVENT=8, + APPEND_BLOCK_EVENT=9, EXEC_LOAD_EVENT=10, DELETE_FILE_EVENT=11, + NEW_LOAD_EVENT=12}; 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_relay_log_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; + my_off_t log_pos; + uint16 flags; + int cached_event_len; + char* temp_buf; +#ifndef MYSQL_CLIENT + THD* thd; +#endif static void *operator new(size_t size) { @@ -81,41 +236,63 @@ public: int write(IO_CACHE* file); int write_header(IO_CACHE* file); - virtual int write_data(IO_CACHE* file __attribute__((unused))) { return 0; } + virtual int write_data(IO_CACHE* file) + { return write_data_header(file) || write_data_body(file); } + virtual int write_data_header(IO_CACHE* file __attribute__((unused))) + { return 0; } + virtual int write_data_body(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): - when(when_arg), exec_time(exec_time_arg), - valid_exec_time(valid_exec_time_arg) - { - server_id = server_id_arg ? server_id_arg : (::server_id); - } - - Log_event(const char* buf): valid_exec_time(0) - { - when = uint4korr(buf); - server_id = uint4korr(buf + 5); - } - - virtual ~Log_event() {} - + virtual bool is_valid() = 0; + virtual bool get_cache_stmt() { return 0; } + Log_event(const char* buf, bool old_format); +#ifndef MYSQL_CLIENT + Log_event(THD* thd_arg, uint16 flags_arg = 0); +#endif + virtual ~Log_event() { free_temp_buf();} + void register_temp_buf(char* buf) { temp_buf = buf; } + void free_temp_buf() + { + if (temp_buf) + { + my_free(temp_buf, MYF(0)); + temp_buf = 0; + } + } virtual int get_data_size() { return 0;} + virtual int get_data_body_offset() { return 0; } + int get_event_len() { return cached_event_len ? cached_event_len : + (cached_event_len = LOG_EVENT_HEADER_LEN + get_data_size()); } +#ifdef MYSQL_CLIENT virtual void print(FILE* file, bool short_form = 0, char* last_db = 0) = 0; - void print_timestamp(FILE* file, time_t *ts = 0); void print_header(FILE* file); - +#endif + #ifndef MYSQL_CLIENT // 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(IO_CACHE* file, + pthread_mutex_t* log_lock, + bool old_format); #else // avoid having to link mysqlbinlog against libpthread - static Log_event* read_log_event(IO_CACHE* file); + static Log_event* read_log_event(IO_CACHE* file, bool old_format); #endif - static Log_event* read_log_event(const char* buf, int event_len); + static Log_event* read_log_event(const char* buf, int event_len, + const char **error, bool old_format); + 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_pos(MYSQL_LOG* log); + virtual void pack_info(String* packet); + int net_send(THD* thd, const char* log_name, my_off_t pos); + static void init_show_field_list(List<Item>* field_list); + virtual int exec_event(struct st_relay_log_info* rli); + virtual const char* get_db() + { + return thd ? thd->db : 0; + } #endif }; @@ -135,28 +312,17 @@ public: uint16 error_code; ulong thread_id; #if !defined(MYSQL_CLIENT) - 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), - 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), - cache_stmt(using_trans && - (thd_arg->options & (OPTION_NOT_AUTO_COMMIT | OPTION_BEGIN))) - { - time_t end_time; - time(&end_time); - exec_time = (ulong) (end_time - thd->start_time); - db_len = (db) ? (uint32) strlen(db) : 0; - // do not log stray system errors such as EE_WRITE - if (error_code < ERRMOD) - error_code = 0; - } + + Query_log_event(THD* thd_arg, const char* query_arg, + bool using_trans=0); + const char* get_db() { return db; } + void pack_info(String* packet); + int exec_event(struct st_relay_log_info* rli); + bool get_cache_stmt() { return cache_stmt; } #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(const char* buf, int event_len, bool old_format); ~Query_log_event() { if (data_buf) @@ -167,46 +333,55 @@ public: Log_event_type get_type_code() { return QUERY_EVENT; } int write(IO_CACHE* file); int write_data(IO_CACHE* file); // returns 0 on success, -1 on error + bool is_valid() { return query != 0; } int get_data_size() { return q_len + db_len + 2 + - sizeof(uint32) // thread_id - + sizeof(uint32) // exec_time - + sizeof(uint16) // error_code + 4 // thread_id + + 4 // exec_time + + 2 // error_code ; } - +#ifdef MYSQL_CLIENT void print(FILE* file, bool short_form = 0, char* last_db = 0); +#endif }; -#define DUMPFILE_FLAG 0x1 -#define OPT_ENCLOSED_FLAG 0x2 -#define REPLACE_FLAG 0x4 -#define IGNORE_FLAG 0x8 - -#define FIELD_TERM_EMPTY 0x1 -#define ENCLOSED_EMPTY 0x2 -#define LINE_TERM_EMPTY 0x4 -#define LINE_START_EMPTY 0x8 -#define ESCAPED_EMPTY 0x10 +class Slave_log_event: public Log_event +{ +protected: + char* mem_pool; + void init_from_mem_pool(int data_size); +public: + my_off_t master_pos; + char* master_host; + char* master_log; + int master_host_len; + int master_log_len; + uint16 master_port; +#ifndef MYSQL_CLIENT + Slave_log_event(THD* thd_arg, struct st_relay_log_info* rli); + void pack_info(String* packet); + int exec_event(struct st_relay_log_info* rli); +#endif + + Slave_log_event(const char* buf, int event_len); + ~Slave_log_event(); + int get_data_size(); + bool is_valid() { return master_host != 0; } + Log_event_type get_type_code() { return SLAVE_EVENT; } +#ifdef MYSQL_CLIENT + void print(FILE* file, bool short_form = 0, char* last_db = 0); +#endif + int write_data(IO_CACHE* file ); -struct sql_ex_info - { - char field_term; - char enclosed; - char line_term; - char line_start; - char escaped; - char opt_flags; // flags for the options - char empty_flags; // flags to indicate which of the terminating charact - } ; +}; class Load_log_event: public Log_event { protected: - char* data_buf; - void copy_log_event(const char *buf, ulong data_len); + int copy_log_event(const char *buf, ulong event_len, bool old_format); public: ulong thread_id; @@ -217,7 +392,6 @@ public: const char* fields; const uchar* field_lens; uint32 field_block_len; - const char* table_name; const char* db; @@ -226,89 +400,31 @@ public: sql_ex_info sql_ex; #if !defined(MYSQL_CLIENT) - THD* thd; String field_lens_buf; String fields_buf; - Load_log_event(THD* thd, sql_exchange* ex, - const char *db_arg, const char* table_name_arg, - List<Item>& fields_arg, enum enum_duplicates handle_dup ): - Log_event(thd->start_time),data_buf(0),thread_id(thd->thread_id), - num_fields(0),fields(0),field_lens(0),field_block_len(0), - table_name(table_name_arg), - db(db_arg), - fname(ex->file_name), - thd(thd) - { - 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; - sql_ex.field_term = (*ex->field_term)[0]; - sql_ex.enclosed = (*ex->enclosed)[0]; - sql_ex.line_term = (*ex->line_term)[0]; - sql_ex.line_start = (*ex->line_start)[0]; - sql_ex.escaped = (*ex->escaped)[0]; - sql_ex.opt_flags = 0; - if(ex->dumpfile) - sql_ex.opt_flags |= DUMPFILE_FLAG; - if(ex->opt_enclosed) - sql_ex.opt_flags |= OPT_ENCLOSED_FLAG; - - sql_ex.empty_flags = 0; - - switch(handle_dup) - { - case DUP_IGNORE: sql_ex.opt_flags |= IGNORE_FLAG; break; - case DUP_REPLACE: sql_ex.opt_flags |= REPLACE_FLAG; break; - case DUP_ERROR: break; - } - - if(!ex->field_term->length()) - sql_ex.empty_flags |= FIELD_TERM_EMPTY; - if(!ex->enclosed->length()) - sql_ex.empty_flags |= ENCLOSED_EMPTY; - if(!ex->line_term->length()) - sql_ex.empty_flags |= LINE_TERM_EMPTY; - if(!ex->line_start->length()) - sql_ex.empty_flags |= LINE_START_EMPTY; - if(!ex->escaped->length()) - sql_ex.empty_flags |= ESCAPED_EMPTY; - - skip_lines = ex->skip_lines; - - List_iterator<Item> li(fields_arg); - field_lens_buf.length(0); - fields_buf.length(0); - Item* item; - while((item = li++)) - { - num_fields++; - uchar len = (uchar) strlen(item->name); - field_block_len += len + 1; - fields_buf.append(item->name, len + 1); - field_lens_buf.append((char*)&len, 1); - } - - field_lens = (const uchar*)field_lens_buf.ptr(); - fields = fields_buf.ptr(); - } + + Load_log_event(THD* thd, sql_exchange* ex, const char* db_arg, + const char* table_name_arg, + List<Item>& fields_arg, enum enum_duplicates handle_dup); void set_fields(List<Item> &fields_arg); + void pack_info(String* packet); + const char* get_db() { return db; } + int exec_event(struct st_relay_log_info* rli) + { + return exec_event(thd->slave_net,rli); + } + int exec_event(NET* net, struct st_relay_log_info* rli); #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(const char* buf, int event_len, bool old_format); ~Load_log_event() { - if (data_buf) - { - my_free((gptr) data_buf, MYF(0)); - } } - Log_event_type get_type_code() { return LOAD_EVENT; } - int write_data(IO_CACHE* file); // returns 0 on success, -1 on error + Log_event_type get_type_code() { return sql_ex.new_format() ? + NEW_LOAD_EVENT: LOAD_EVENT; } + int write_data_header(IO_CACHE* file); + int write_data_body(IO_CACHE* file); + bool is_valid() { return table_name != 0; } int get_data_size() { return table_name_len + 2 + db_len + 2 + fname_len @@ -316,11 +432,13 @@ public: + 4 // exec_time + 4 // skip_lines + 4 // field block len - + sizeof(sql_ex) + field_block_len + num_fields*sizeof(uchar) ; + + sql_ex.data_size() + field_block_len + num_fields; ; } - + int get_data_body_offset() { return LOAD_EVENT_OVERHEAD; } +#ifdef MYSQL_CLIENT void print(FILE* file, bool short_form = 0, char* last_db = 0); +#endif }; extern char server_version[SERVER_VERSION_LENGTH]; @@ -330,35 +448,30 @@ class Start_log_event: public Log_event public: uint32 created; uint16 binlog_version; - char server_version[50]; - - Start_log_event() :Log_event(time(NULL)),binlog_version(BINLOG_VERSION) + char server_version[ST_SERVER_VER_LEN]; +#ifndef MYSQL_CLIENT + Start_log_event() :Log_event((THD*)0),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); - +#endif + Start_log_event(const char* buf, bool old_format); ~Start_log_event() {} Log_event_type get_type_code() { return START_EVENT;} int write_data(IO_CACHE* file); + bool is_valid() { return 1; } 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); + int exec_event(struct st_relay_log_info* rli); +#endif +#ifdef MYSQL_CLIENT void print(FILE* file, bool short_form = 0, char* last_db = 0); +#endif }; class Intvar_log_event: public Log_event @@ -366,37 +479,48 @@ class Intvar_log_event: public Log_event public: ulonglong val; uchar type; - Intvar_log_event(uchar type_arg, ulonglong val_arg) - :Log_event(time(NULL)),val(val_arg),type(type_arg) +#ifndef MYSQL_CLIENT + Intvar_log_event(THD* thd_arg,uchar type_arg, ulonglong val_arg) + :Log_event(thd_arg),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); +#endif + Intvar_log_event(const char* buf, bool old_format); ~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); + bool is_valid() { return 1; } +#ifndef MYSQL_CLIENT + void pack_info(String* packet); + int exec_event(struct st_relay_log_info* rli); +#endif - +#ifdef MYSQL_CLIENT void print(FILE* file, bool short_form = 0, char* last_db = 0); +#endif }; class Stop_log_event: public Log_event { public: - Stop_log_event() :Log_event(time(NULL)) +#ifndef MYSQL_CLIENT + Stop_log_event() :Log_event((THD*)0) {} - 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) +#endif + Stop_log_event(const char* buf, bool old_format):Log_event(buf, + old_format) { } ~Stop_log_event() {} Log_event_type get_type_code() { return STOP_EVENT;} + bool is_valid() { return 1; } +#ifdef MYSQL_CLIENT void print(FILE* file, bool short_form = 0, char* last_db = 0); +#endif +#ifndef MYSQL_CLIENT + int exec_event(struct st_relay_log_info* rli); +#endif }; class Rotate_log_event: public Log_event @@ -404,27 +528,172 @@ 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) : - Log_event(time(NULL)), +#ifndef MYSQL_CLIENT + Rotate_log_event(THD* thd_arg, const char* new_log_ident_arg, + uint ident_len_arg = 0,ulonglong pos_arg = 4) : + Log_event(thd_arg), 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); +#endif + Rotate_log_event(const char* buf, int event_len, bool old_format); ~Rotate_log_event() { if (alloced) 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;} + bool is_valid() { return new_log_ident != 0; } + int write_data(IO_CACHE* file); +#ifdef MYSQL_CLIENT + void print(FILE* file, bool short_form = 0, char* last_db = 0); +#endif +#ifndef MYSQL_CLIENT + void pack_info(String* packet); + int exec_event(struct st_relay_log_info* rli); +#endif +}; + +/* the classes below are for the new LOAD DATA INFILE logging */ + +class Create_file_log_event: public Load_log_event +{ +protected: + // pretend we are Load event, so we can write out just + // our Load part - used on the slave when writing event out to + // SQL_LOAD-*.info file + bool fake_base; +public: + char* block; + uint block_len; + uint file_id; + bool inited_from_old; +#ifndef MYSQL_CLIENT + Create_file_log_event(THD* thd, sql_exchange* ex, const char* db_arg, + const char* table_name_arg, + List<Item>& fields_arg, + enum enum_duplicates handle_dup, + char* block_arg, uint block_len_arg); +#endif + + Create_file_log_event(const char* buf, int event_len, bool old_format); + ~Create_file_log_event() + { + } + Log_event_type get_type_code() + { + return fake_base ? Load_log_event::get_type_code() : CREATE_FILE_EVENT; + } + int get_data_size() { return fake_base ? Load_log_event::get_data_size() : + Load_log_event::get_data_size() + + 4 + 1 + block_len;} + int get_data_body_offset() { return fake_base ? LOAD_EVENT_OVERHEAD: + LOAD_EVENT_OVERHEAD + CREATE_FILE_HEADER_LEN; } + bool is_valid() { return inited_from_old || block != 0; } + int write_data_header(IO_CACHE* file); + int write_data_body(IO_CACHE* file); + int write_base(IO_CACHE* file); // cut out Create_file extentions and + // write it as Load event - used on the slave + +#ifdef MYSQL_CLIENT + void print(FILE* file, bool short_form = 0, char* last_db = 0); +#endif +#ifndef MYSQL_CLIENT + void pack_info(String* packet); + int exec_event(struct st_relay_log_info* rli); +#endif +}; + +class Append_block_log_event: public Log_event +{ +public: + char* block; + uint block_len; + uint file_id; + +#ifndef MYSQL_CLIENT + Append_block_log_event(THD* thd, char* block_arg, + uint block_len_arg); + int exec_event(struct st_relay_log_info* rli); +#endif + + Append_block_log_event(const char* buf, int event_len); + ~Append_block_log_event() + { + } + Log_event_type get_type_code() { return APPEND_BLOCK_EVENT;} + int get_data_size() { return block_len + APPEND_BLOCK_HEADER_LEN ;} + bool is_valid() { return block != 0; } int write_data(IO_CACHE* file); + +#ifdef MYSQL_CLIENT + void print(FILE* file, bool short_form = 0, char* last_db = 0); +#endif +#ifndef MYSQL_CLIENT + void pack_info(String* packet); +#endif +}; + +class Delete_file_log_event: public Log_event +{ +public: + uint file_id; +#ifndef MYSQL_CLIENT + Delete_file_log_event(THD* thd); +#endif + + Delete_file_log_event(const char* buf, int event_len); + ~Delete_file_log_event() + { + } + Log_event_type get_type_code() { return DELETE_FILE_EVENT;} + int get_data_size() { return DELETE_FILE_HEADER_LEN ;} + bool is_valid() { return file_id != 0; } + int write_data(IO_CACHE* file); + +#ifdef MYSQL_CLIENT + void print(FILE* file, bool short_form = 0, char* last_db = 0); +#endif +#ifndef MYSQL_CLIENT + void pack_info(String* packet); + int exec_event(struct st_relay_log_info* rli); +#endif +}; + +class Execute_load_log_event: public Log_event +{ +public: + uint file_id; + +#ifndef MYSQL_CLIENT + Execute_load_log_event(THD* thd); +#endif + + Execute_load_log_event(const char* buf, int event_len); + ~Execute_load_log_event() + { + } + Log_event_type get_type_code() { return EXEC_LOAD_EVENT;} + int get_data_size() { return EXEC_LOAD_HEADER_LEN ;} + bool is_valid() { return file_id != 0; } + int write_data(IO_CACHE* file); + +#ifdef MYSQL_CLIENT void print(FILE* file, bool short_form = 0, char* last_db = 0); +#endif +#ifndef MYSQL_CLIENT + void pack_info(String* packet); + int exec_event(struct st_relay_log_info* rli); +#endif }; #endif + + + diff --git a/sql/matherr.c b/sql/matherr.c index 8523a78ce94..ea0c15d2feb 100644 --- a/sql/matherr.c +++ b/sql/matherr.c @@ -1,22 +1,22 @@ /* 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 */ /* Fix that we got POSTFIX_ERROR when doing unreasonable math (not core) */ -#include <global.h> +#include <my_global.h> #include <errno.h> /* Fix that we gets POSTFIX_ERROR when error in math */ diff --git a/sql/md5.c b/sql/md5.c deleted file mode 100644 index 4c2e80107b8..00000000000 --- a/sql/md5.c +++ /dev/null @@ -1,351 +0,0 @@ -/* MD5C.C - RSA Data Security, Inc., MD5 message-digest algorithm - */ - -/* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All -rights reserved. - -License to copy and use this software is granted provided that it -is identified as the "RSA Data Security, Inc. MD5 Message-Digest -Algorithm" in all material mentioning or referencing this software -or this function. - -License is also granted to make and use derivative works provided -that such works are identified as "derived from the RSA Data -Security, Inc. MD5 Message-Digest Algorithm" in all material -mentioning or referencing the derived work. - -RSA Data Security, Inc. makes no representations concerning either -the merchantability of this software or the suitability of this -software for any particular purpose. It is provided "as is" -without express or implied warranty of any kind. - -These notices must be retained in any copies of any part of this -documentation and/or software. -*/ - -/* - Changes by Monty: - Replace of MD5_memset and MD5_memcpy with memset & memcpy -*/ - -#include <global.h> -#include <m_string.h> -#include "md5.h" - -/* Constants for MD5Transform routine. */ - -#define S11 7 -#define S12 12 -#define S13 17 -#define S14 22 -#define S21 5 -#define S22 9 -#define S23 14 -#define S24 20 -#define S31 4 -#define S32 11 -#define S33 16 -#define S34 23 -#define S41 6 -#define S42 10 -#define S43 15 -#define S44 21 - - -static void MD5Transform PROTO_LIST ((UINT4 [4], unsigned char [64])); -static void Encode PROTO_LIST - ((unsigned char *, UINT4 *, unsigned int)); -static void Decode PROTO_LIST - ((UINT4 *, unsigned char *, unsigned int)); -#ifdef OLD_CODE -static void MD5_memcpy PROTO_LIST ((POINTER, POINTER, unsigned int)); -static void MD5_memset PROTO_LIST ((POINTER, int, unsigned int)); -#else -#define MD5_memcpy(A,B,C) memcpy((char*) (A),(char*) (B), (C)) -#define MD5_memset(A,B,C) memset((char*) (A),(B), (C)) -#endif - -static unsigned char PADDING[64] = { - 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 -}; - -/* F, G, H and I are basic MD5 functions. - */ -#define F(x, y, z) (((x) & (y)) | ((~x) & (z))) -#define G(x, y, z) (((x) & (z)) | ((y) & (~z))) -#define H(x, y, z) ((x) ^ (y) ^ (z)) -#define I(x, y, z) ((y) ^ ((x) | (~z))) - -/* ROTATE_LEFT rotates x left n bits. - */ -#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n)))) - -/* FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4. -Rotation is separate from addition to prevent recomputation. - */ -#define FF(a, b, c, d, x, s, ac) { \ - (a) += F ((b), (c), (d)) + (x) + (UINT4)(ac); \ - (a) = ROTATE_LEFT ((a), (s)); \ - (a) += (b); \ - } -#define GG(a, b, c, d, x, s, ac) { \ - (a) += G ((b), (c), (d)) + (x) + (UINT4)(ac); \ - (a) = ROTATE_LEFT ((a), (s)); \ - (a) += (b); \ - } -#define HH(a, b, c, d, x, s, ac) { \ - (a) += H ((b), (c), (d)) + (x) + (UINT4)(ac); \ - (a) = ROTATE_LEFT ((a), (s)); \ - (a) += (b); \ - } -#define II(a, b, c, d, x, s, ac) { \ - (a) += I ((b), (c), (d)) + (x) + (UINT4)(ac); \ - (a) = ROTATE_LEFT ((a), (s)); \ - (a) += (b); \ - } - -/* MD5 initialization. Begins an MD5 operation, writing a new context. - */ -void MD5Init (MD5_CTX *context) /* context */ -{ - context->count[0] = context->count[1] = 0; - /* Load magic initialization constants. -*/ - context->state[0] = 0x67452301; - context->state[1] = 0xefcdab89; - context->state[2] = 0x98badcfe; - context->state[3] = 0x10325476; -} - -/* MD5 block update operation. Continues an MD5 message-digest - operation, processing another message block, and updating the - context. - */ -void MD5Update ( -MD5_CTX *context, /* context */ -unsigned char *input, /* input block */ -unsigned int inputLen) /* length of input block */ -{ - unsigned int i, idx, partLen; - - /* Compute number of bytes mod 64 */ - idx = (unsigned int)((context->count[0] >> 3) & 0x3F); - - - /* Update number of bits */ - if ((context->count[0] += ((UINT4)inputLen << 3)) - < ((UINT4)inputLen << 3)) - context->count[1]++; - context->count[1] += ((UINT4)inputLen >> 29); - - partLen = 64 - idx; - - /* Transform as many times as possible. -*/ - if (inputLen >= partLen) { - MD5_memcpy((POINTER)&context->buffer[idx], (POINTER)input, partLen); - MD5Transform(context->state, context->buffer); - - for (i = partLen; i + 63 < inputLen; i += 64) - MD5Transform (context->state, &input[i]); - - idx = 0; - } - else - i = 0; - - /* Buffer remaining input */ - MD5_memcpy((POINTER)&context->buffer[idx], (POINTER)&input[i], - inputLen-i); -} - -/* MD5 finalization. Ends an MD5 message-digest operation, writing the - the message digest and zeroizing the context. - */ -void MD5Final ( -unsigned char digest[16], /* message digest */ -MD5_CTX *context) /* context */ -{ - unsigned char bits[8]; - unsigned int idx, padLen; - - /* Save number of bits */ - Encode (bits, context->count, 8); - - /* Pad out to 56 mod 64. -*/ - idx = (unsigned int)((context->count[0] >> 3) & 0x3f); - padLen = (idx < 56) ? (56 - idx) : (120 - idx); - MD5Update (context, PADDING, padLen); - - /* Append length (before padding) */ - MD5Update (context, bits, 8); - - /* Store state in digest */ - Encode (digest, context->state, 16); - - /* Zeroize sensitive information. -*/ - MD5_memset ((POINTER)context, 0, sizeof (*context)); -} - -/* MD5 basic transformation. Transforms state based on block. - */ -static void MD5Transform ( -UINT4 state[4], -unsigned char block[64]) -{ - UINT4 a = state[0], b = state[1], c = state[2], d = state[3], x[16]; - - Decode (x, block, 64); - - /* Round 1 */ - FF (a, b, c, d, x[ 0], S11, 0xd76aa478); /* 1 */ - FF (d, a, b, c, x[ 1], S12, 0xe8c7b756); /* 2 */ - FF (c, d, a, b, x[ 2], S13, 0x242070db); /* 3 */ - FF (b, c, d, a, x[ 3], S14, 0xc1bdceee); /* 4 */ - FF (a, b, c, d, x[ 4], S11, 0xf57c0faf); /* 5 */ - FF (d, a, b, c, x[ 5], S12, 0x4787c62a); /* 6 */ - FF (c, d, a, b, x[ 6], S13, 0xa8304613); /* 7 */ - FF (b, c, d, a, x[ 7], S14, 0xfd469501); /* 8 */ - FF (a, b, c, d, x[ 8], S11, 0x698098d8); /* 9 */ - FF (d, a, b, c, x[ 9], S12, 0x8b44f7af); /* 10 */ - FF (c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */ - FF (b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */ - FF (a, b, c, d, x[12], S11, 0x6b901122); /* 13 */ - FF (d, a, b, c, x[13], S12, 0xfd987193); /* 14 */ - FF (c, d, a, b, x[14], S13, 0xa679438e); /* 15 */ - FF (b, c, d, a, x[15], S14, 0x49b40821); /* 16 */ - - /* Round 2 */ - GG (a, b, c, d, x[ 1], S21, 0xf61e2562); /* 17 */ - GG (d, a, b, c, x[ 6], S22, 0xc040b340); /* 18 */ - GG (c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */ - GG (b, c, d, a, x[ 0], S24, 0xe9b6c7aa); /* 20 */ - GG (a, b, c, d, x[ 5], S21, 0xd62f105d); /* 21 */ - GG (d, a, b, c, x[10], S22, 0x2441453); /* 22 */ - GG (c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */ - GG (b, c, d, a, x[ 4], S24, 0xe7d3fbc8); /* 24 */ - GG (a, b, c, d, x[ 9], S21, 0x21e1cde6); /* 25 */ - GG (d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */ - GG (c, d, a, b, x[ 3], S23, 0xf4d50d87); /* 27 */ - GG (b, c, d, a, x[ 8], S24, 0x455a14ed); /* 28 */ - GG (a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */ - GG (d, a, b, c, x[ 2], S22, 0xfcefa3f8); /* 30 */ - GG (c, d, a, b, x[ 7], S23, 0x676f02d9); /* 31 */ - GG (b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */ - - /* Round 3 */ - HH (a, b, c, d, x[ 5], S31, 0xfffa3942); /* 33 */ - HH (d, a, b, c, x[ 8], S32, 0x8771f681); /* 34 */ - HH (c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */ - HH (b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */ - HH (a, b, c, d, x[ 1], S31, 0xa4beea44); /* 37 */ - HH (d, a, b, c, x[ 4], S32, 0x4bdecfa9); /* 38 */ - HH (c, d, a, b, x[ 7], S33, 0xf6bb4b60); /* 39 */ - HH (b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */ - HH (a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */ - HH (d, a, b, c, x[ 0], S32, 0xeaa127fa); /* 42 */ - HH (c, d, a, b, x[ 3], S33, 0xd4ef3085); /* 43 */ - HH (b, c, d, a, x[ 6], S34, 0x4881d05); /* 44 */ - HH (a, b, c, d, x[ 9], S31, 0xd9d4d039); /* 45 */ - HH (d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */ - HH (c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */ - HH (b, c, d, a, x[ 2], S34, 0xc4ac5665); /* 48 */ - - /* Round 4 */ - II (a, b, c, d, x[ 0], S41, 0xf4292244); /* 49 */ - II (d, a, b, c, x[ 7], S42, 0x432aff97); /* 50 */ - II (c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */ - II (b, c, d, a, x[ 5], S44, 0xfc93a039); /* 52 */ - II (a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */ - II (d, a, b, c, x[ 3], S42, 0x8f0ccc92); /* 54 */ - II (c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */ - II (b, c, d, a, x[ 1], S44, 0x85845dd1); /* 56 */ - II (a, b, c, d, x[ 8], S41, 0x6fa87e4f); /* 57 */ - II (d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */ - II (c, d, a, b, x[ 6], S43, 0xa3014314); /* 59 */ - II (b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */ - II (a, b, c, d, x[ 4], S41, 0xf7537e82); /* 61 */ - II (d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */ - II (c, d, a, b, x[ 2], S43, 0x2ad7d2bb); /* 63 */ - II (b, c, d, a, x[ 9], S44, 0xeb86d391); /* 64 */ - - - state[0] += a; - state[1] += b; - state[2] += c; - state[3] += d; - - /* Zeroize sensitive information. -*/ - MD5_memset ((POINTER)x, 0, sizeof (x)); -} - -/* Encodes input (UINT4) into output (unsigned char). Assumes len is - a multiple of 4. - */ -static void Encode ( -unsigned char *output, -UINT4 *input, -unsigned int len) -{ - unsigned int i, j; - - for (i = 0, j = 0; j < len; i++, j += 4) { - output[j] = (unsigned char)(input[i] & 0xff); - output[j+1] = (unsigned char)((input[i] >> 8) & 0xff); - output[j+2] = (unsigned char)((input[i] >> 16) & 0xff); - output[j+3] = (unsigned char)((input[i] >> 24) & 0xff); - } -} - - -/* Decodes input (unsigned char) into output (UINT4). Assumes len is - a multiple of 4. - */ -static void Decode ( -UINT4 *output, -unsigned char *input, -unsigned int len) -{ - unsigned int i, j; - - for (i = 0, j = 0; j < len; i++, j += 4) - output[i] = ((UINT4)input[j]) | (((UINT4)input[j+1]) << 8) | - (((UINT4)input[j+2]) << 16) | (((UINT4)input[j+3]) << 24); -} - -/* Note: Replace "for loop" with standard memcpy if possible. - */ - -#ifndef MD5_memcpy -static void MD5_memcpy (output, input, len) -POINTER output; -POINTER input; -unsigned int len; -{ - unsigned int i; - - for (i = 0; i < len; i++) - output[i] = input[i]; -} -#endif - -/* Note: Replace "for loop" with standard memset if possible. - */ - -#ifndef MD5_memset -static void MD5_memset (output, value, len) -POINTER output; -int value; -unsigned int len; -{ - unsigned int i; - - for (i = 0; i < len; i++) - ((char *)output)[i] = (char)value; -} -#endif diff --git a/sql/md5.h b/sql/md5.h deleted file mode 100644 index 862129391f1..00000000000 --- a/sql/md5.h +++ /dev/null @@ -1,80 +0,0 @@ - -/* MD5.H - header file for MD5C.C - */ - -/* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All -rights reserved. - -License to copy and use this software is granted provided that it -is identified as the "RSA Data Security, Inc. MD5 Message-Digest -Algorithm" in all material mentioning or referencing this software -or this function. - -License is also granted to make and use derivative works provided -that such works are identified as "derived from the RSA Data -Security, Inc. MD5 Message-Digest Algorithm" in all material -mentioning or referencing the derived work. - -RSA Data Security, Inc. makes no representations concerning either -the merchantability of this software or the suitability of this -software for any particular purpose. It is provided "as is" -without express or implied warranty of any kind. - -These notices must be retained in any copies of any part of this -documentation and/or software. - */ - -/* GLOBAL.H - RSAREF types and constants - */ - -/* PROTOTYPES should be set to one if and only if the compiler supports - function argument prototyping. -The following makes PROTOTYPES default to 0 if it has not already - been defined with C compiler flags. - */ - -/* egcs 1.1.2 under linux didn't defined it.... :( */ - -#ifndef PROTOTYPES -#define PROTOTYPES 1 /* Assume prototypes */ -#endif - -/* POINTER defines a generic pointer type */ -typedef unsigned char *POINTER; - -/* UINT2 defines a two byte word */ -typedef uint16 UINT2; /* Fix for MySQL / Alpha */ - -/* UINT4 defines a four byte word */ -typedef uint32 UINT4; /* Fix for MySQL / Alpha */ - -/* PROTO_LIST is defined depending on how PROTOTYPES is defined above. -If using PROTOTYPES, then PROTO_LIST returns the list, otherwise it - returns an empty list. - */ -#if PROTOTYPES -#define PROTO_LIST(list) list -#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; - -#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 *)); - -#ifdef __cplusplus -} -#endif - diff --git a/sql/mf_iocache.cc b/sql/mf_iocache.cc index cddacaa820f..1bc65eebd23 100644 --- a/sql/mf_iocache.cc +++ b/sql/mf_iocache.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 */ @@ -42,293 +42,6 @@ static void my_aiowait(my_aio_result *result); extern "C" { /* - ** if cachesize == 0 then use default cachesize (from s-file) - ** if file == -1 then real_open_cached_file() will be called. - ** returns 0 if ok - */ - -int init_io_cache(IO_CACHE *info, File file, uint cachesize, - enum cache_type type, my_off_t seek_offset, - pbool use_async_io, myf cache_myflags) -{ - uint min_cache; - DBUG_ENTER("init_io_cache"); - DBUG_PRINT("enter",("type: %d pos: %ld",(int) type, (ulong) seek_offset)); - - /* There is no file in net_reading */ - info->file= file; - if (!cachesize) - if (! (cachesize= my_default_record_cache_size)) - DBUG_RETURN(1); /* No cache requested */ - min_cache=use_async_io ? IO_SIZE*4 : IO_SIZE*2; - if (type == READ_CACHE) - { /* Assume file isn't growing */ - if (cache_myflags & MY_DONT_CHECK_FILESIZE) - { - cache_myflags &= ~MY_DONT_CHECK_FILESIZE; - } - else - { - my_off_t file_pos,end_of_file; - if ((file_pos=my_tell(file,MYF(0)) == MY_FILEPOS_ERROR)) - DBUG_RETURN(1); - end_of_file=my_seek(file,0L,MY_SEEK_END,MYF(0)); - if (end_of_file < seek_offset) - end_of_file=seek_offset; - VOID(my_seek(file,file_pos,MY_SEEK_SET,MYF(0))); - if ((my_off_t) cachesize > end_of_file-seek_offset+IO_SIZE*2-1) - { - cachesize=(uint) (end_of_file-seek_offset)+IO_SIZE*2-1; - use_async_io=0; /* No nead to use async */ - } - } - } - if ((int) type < (int) READ_NET) - { - for (;;) - { - cachesize=(uint) ((ulong) (cachesize + min_cache-1) & - (ulong) ~(min_cache-1)); - if (cachesize < min_cache) - cachesize = min_cache; - if ((info->buffer= - (byte*) my_malloc(cachesize, - MYF((cache_myflags & ~ MY_WME) | - (cachesize == min_cache ? MY_WME : 0)))) != 0) - break; /* Enough memory found */ - if (cachesize == min_cache) - DBUG_RETURN(2); /* Can't alloc cache */ - cachesize= (uint) ((long) cachesize*3/4); /* Try with less memory */ - } - } - else - info->buffer=0; - DBUG_PRINT("info",("init_io_cache: cachesize = %u",cachesize)); - info->pos_in_file= seek_offset; - info->read_length=info->buffer_length=cachesize; - info->seek_not_done= test(file >= 0 && type != READ_FIFO && - type != READ_NET); - info->myflags=cache_myflags & ~(MY_NABP | MY_FNABP); - info->rc_request_pos=info->rc_pos=info->buffer; - - if (type == READ_CACHE || type == READ_NET || type == READ_FIFO) - { - info->rc_end=info->buffer; /* Nothing in cache */ - } - else /* type == WRITE_CACHE */ - { - info->rc_end=info->buffer+info->buffer_length- (seek_offset & (IO_SIZE-1)); - } - /* end_of_file may be changed by user later */ - info->end_of_file= ((type == READ_NET || type == READ_FIFO ) ? 0 - : ~(my_off_t) 0); - info->type=type; - info->error=0; - info->read_function=(type == READ_NET) ? _my_b_net_read : _my_b_read; /* net | file */ -#ifdef HAVE_AIOWAIT - if (use_async_io && ! my_disable_async_io) - { - DBUG_PRINT("info",("Using async io")); - info->read_length/=2; - info->read_function=_my_b_async_read; - } - info->inited=info->aio_result.pending=0; -#endif - DBUG_RETURN(0); -} /* init_io_cache */ - - - /* Wait until current request is ready */ - -#ifdef HAVE_AIOWAIT -static void my_aiowait(my_aio_result *result) -{ - if (result->pending) - { - struct aio_result_t *tmp; - for (;;) - { - if ((int) (tmp=aiowait((struct timeval *) 0)) == -1) - { - if (errno == EINTR) - continue; - DBUG_PRINT("error",("No aio request, error: %d",errno)); - result->pending=0; /* Assume everythings is ok */ - break; - } - ((my_aio_result*) tmp)->pending=0; - if ((my_aio_result*) tmp == result) - break; - } - } - return; -} -#endif - - /* Use this to reset cache to start or other type */ - /* Some simple optimizing is done when reinit in current buffer */ - -my_bool reinit_io_cache(IO_CACHE *info, enum cache_type type, - my_off_t seek_offset, - pbool use_async_io __attribute__((unused)), - pbool clear_cache) -{ - DBUG_ENTER("reinit_io_cache"); - - info->seek_not_done= test(info->file >= 0); /* Seek not done */ - - /* If the whole file is in memory, avoid flushing to disk */ - if (! clear_cache && - seek_offset >= info->pos_in_file && - seek_offset <= info->pos_in_file + - (uint) (info->rc_end - info->rc_request_pos)) - { /* use current buffer */ - if (info->type == WRITE_CACHE && type == READ_CACHE) - { - info->rc_end=info->rc_pos; - info->end_of_file=my_b_tell(info); - } - else if (type == WRITE_CACHE) - { - if (info->type == READ_CACHE) - info->rc_end=info->buffer+info->buffer_length; - info->end_of_file = ~(my_off_t) 0; - } - info->rc_pos=info->rc_request_pos+(seek_offset-info->pos_in_file); -#ifdef HAVE_AIOWAIT - my_aiowait(&info->aio_result); /* Wait for outstanding req */ -#endif - } - else - { - /* - If we change from WRITE_CACHE to READ_CACHE, assume that everything - after the current positions should be ignored - */ - if (info->type == WRITE_CACHE && type == READ_CACHE) - info->end_of_file=my_b_tell(info); - /* No need to flush cache if we want to reuse it */ - if ((type != WRITE_CACHE || !clear_cache) && flush_io_cache(info)) - DBUG_RETURN(1); - if (info->pos_in_file != seek_offset) - { - info->pos_in_file=seek_offset; - info->seek_not_done=1; - } - info->rc_request_pos=info->rc_pos=info->buffer; - if (type == READ_CACHE || type == READ_NET || type == READ_FIFO) - { - info->rc_end=info->buffer; /* Nothing in cache */ - } - else - { - info->rc_end=info->buffer+info->buffer_length- - (seek_offset & (IO_SIZE-1)); - info->end_of_file= ((type == READ_NET || type == READ_FIFO) ? 0 : - ~(my_off_t) 0); - } - } - info->type=type; - info->error=0; - info->read_function=(type == READ_NET) ? _my_b_net_read : _my_b_read; -#ifdef HAVE_AIOWAIT - if (type != READ_NET) - { - if (use_async_io && ! my_disable_async_io && - ((ulong) info->buffer_length < - (ulong) (info->end_of_file - seek_offset))) - { - info->read_length=info->buffer_length/2; - info->read_function=_my_b_async_read; - } - } - info->inited=0; -#endif - DBUG_RETURN(0); -} /* init_io_cache */ - - - - /* - Read buffered. Returns 1 if can't read requested characters - This function is only called from the my_b_read() macro - when there isn't enough characters in the buffer to - satisfy the request. - Returns 0 we succeeded in reading all data - */ - -int _my_b_read(register IO_CACHE *info, byte *Buffer, uint Count) -{ - uint length,diff_length,left_length; - my_off_t max_length, pos_in_file; - - if ((left_length=(uint) (info->rc_end-info->rc_pos))) - { - dbug_assert(Count >= left_length); /* User is not using my_b_read() */ - memcpy(Buffer,info->rc_pos, (size_t) (left_length)); - Buffer+=left_length; - Count-=left_length; - } - /* pos_in_file always point on where info->buffer was read */ - pos_in_file=info->pos_in_file+(uint) (info->rc_end - info->buffer); - if (info->seek_not_done) - { /* File touched, do seek */ - VOID(my_seek(info->file,pos_in_file,MY_SEEK_SET,MYF(0))); - info->seek_not_done=0; - } - diff_length=(uint) (pos_in_file & (IO_SIZE-1)); - if (Count >= (uint) (IO_SIZE+(IO_SIZE-diff_length))) - { /* Fill first intern buffer */ - uint read_length; - if (info->end_of_file == pos_in_file) - { /* End of file */ - info->error=(int) left_length; - return 1; - } - length=(Count & (uint) ~(IO_SIZE-1))-diff_length; - if ((read_length=my_read(info->file,Buffer,(uint) length,info->myflags)) - != (uint) length) - { - info->error= read_length == (uint) -1 ? -1 : - (int) (read_length+left_length); - return 1; - } - Count-=length; - Buffer+=length; - pos_in_file+=length; - left_length+=length; - diff_length=0; - } - max_length=info->read_length-diff_length; - if (info->type != READ_FIFO && - (info->end_of_file - pos_in_file) < max_length) - max_length = info->end_of_file - pos_in_file; - if (!max_length) - { - if (Count) - { - info->error= left_length; /* We only got this many char */ - return 1; - } - length=0; /* Didn't read any chars */ - } - else if ((length=my_read(info->file,info->buffer,(uint) max_length, - info->myflags)) < Count || - length == (uint) -1) - { - if (length != (uint) -1) - memcpy(Buffer,info->buffer,(size_t) length); - info->error= length == (uint) -1 ? -1 : (int) (length+left_length); - return 1; - } - info->rc_pos=info->buffer+Count; - info->rc_end=info->buffer+length; - info->pos_in_file=pos_in_file; - memcpy(Buffer,info->buffer,(size_t) Count); - return 0; -} - - /* ** Read buffered from the net. ** Returns 1 if can't read requested characters ** Returns 0 if record read @@ -337,353 +50,34 @@ int _my_b_read(register IO_CACHE *info, byte *Buffer, uint Count) int _my_b_net_read(register IO_CACHE *info, byte *Buffer, uint Count __attribute__((unused))) { - int read_length; + ulong read_length; NET *net= &(current_thd)->net; + DBUG_ENTER("_my_b_net_read"); - if (info->end_of_file) - return 1; /* because my_b_get (no _) takes 1 byte at a time */ + if (!info->end_of_file) + DBUG_RETURN(1); /* because my_b_get (no _) takes 1 byte at a time */ read_length=my_net_read(net); - if (read_length == (int) packet_error) + if (read_length == packet_error) { info->error= -1; - return 1; + DBUG_RETURN(1); } if (read_length == 0) { - /* End of file from client */ - info->end_of_file = 1; return 1; + info->end_of_file= 0; /* End of file from client */ + DBUG_RETURN(1); } /* to set up stuff for my_b_get (no _) */ - info->rc_end = (info->rc_pos = (byte*) net->read_pos) + read_length; - Buffer[0] = info->rc_pos[0]; /* length is always 1 */ - info->rc_pos++; - return 0; -} - -#ifdef HAVE_AIOWAIT - -int _my_b_async_read(register IO_CACHE *info, byte *Buffer, uint Count) -{ - uint length,read_length,diff_length,left_length,use_length,org_Count; - my_off_t max_length; - my_off_t next_pos_in_file; - byte *read_buffer; - - memcpy(Buffer,info->rc_pos, - (size_t) (left_length=(uint) (info->rc_end-info->rc_pos))); - Buffer+=left_length; - org_Count=Count; - Count-=left_length; - - if (info->inited) - { /* wait for read block */ - info->inited=0; /* No more block to read */ - my_aiowait(&info->aio_result); /* Wait for outstanding req */ - if (info->aio_result.result.aio_errno) - { - if (info->myflags & MY_WME) - my_error(EE_READ, MYF(ME_BELL+ME_WAITTANG), - my_filename(info->file), - info->aio_result.result.aio_errno); - my_errno=info->aio_result.result.aio_errno; - info->error= -1; - return(1); - } - if (! (read_length = (uint) info->aio_result.result.aio_return) || - read_length == (uint) -1) - { - my_errno=0; /* For testing */ - info->error= (read_length == (uint) -1 ? -1 : - (int) (read_length+left_length)); - return(1); - } - info->pos_in_file+=(uint) (info->rc_end - info->rc_request_pos); - - if (info->rc_request_pos != info->buffer) - info->rc_request_pos=info->buffer; - else - info->rc_request_pos=info->buffer+info->read_length; - info->rc_pos=info->rc_request_pos; - next_pos_in_file=info->aio_read_pos+read_length; - - /* Check if pos_in_file is changed - (_ni_read_cache may have skipped some bytes) */ - - if (info->aio_read_pos < info->pos_in_file) - { /* Fix if skipped bytes */ - if (info->aio_read_pos + read_length < info->pos_in_file) - { - read_length=0; /* Skipp block */ - next_pos_in_file=info->pos_in_file; - } - else - { - my_off_t offset= (info->pos_in_file - info->aio_read_pos); - info->pos_in_file=info->aio_read_pos; /* Whe are here */ - info->rc_pos=info->rc_request_pos+offset; - read_length-=offset; /* Bytes left from rc_pos */ - } - } -#ifndef DBUG_OFF - if (info->aio_read_pos > info->pos_in_file) - { - my_errno=EINVAL; - return(info->read_length= -1); - } -#endif - /* Copy found bytes to buffer */ - length=min(Count,read_length); - memcpy(Buffer,info->rc_pos,(size_t) length); - Buffer+=length; - Count-=length; - left_length+=length; - info->rc_end=info->rc_pos+read_length; - info->rc_pos+=length; - } - else - next_pos_in_file=(info->pos_in_file+ (uint) - (info->rc_end - info->rc_request_pos)); - - /* If reading large blocks, or first read or read with skipp */ - if (Count) - { - if (next_pos_in_file == info->end_of_file) - { - info->error=(int) (read_length+left_length); - return 1; - } - VOID(my_seek(info->file,next_pos_in_file,MY_SEEK_SET,MYF(0))); - read_length=IO_SIZE*2- (uint) (next_pos_in_file & (IO_SIZE-1)); - if (Count < read_length) - { /* Small block, read to cache */ - if ((read_length=my_read(info->file,info->rc_request_pos, - read_length, info->myflags)) == (uint) -1) - return info->error= -1; - use_length=min(Count,read_length); - memcpy(Buffer,info->rc_request_pos,(size_t) use_length); - info->rc_pos=info->rc_request_pos+Count; - info->rc_end=info->rc_request_pos+read_length; - info->pos_in_file=next_pos_in_file; /* Start of block in cache */ - next_pos_in_file+=read_length; - - if (Count != use_length) - { /* Didn't find hole block */ - if (info->myflags & (MY_WME | MY_FAE | MY_FNABP) && Count != org_Count) - my_error(EE_EOFERR, MYF(ME_BELL+ME_WAITTANG), - my_filename(info->file),my_errno); - info->error=(int) (read_length+left_length); - return 1; - } - } - else - { /* Big block, don't cache it */ - if ((read_length=my_read(info->file,Buffer,(uint) Count,info->myflags)) - != Count) - { - info->error= read_length == (uint) -1 ? -1 : read_length+left_length; - return 1; - } - info->rc_pos=info->rc_end=info->rc_request_pos; - info->pos_in_file=(next_pos_in_file+=Count); - } - } - - /* Read next block with asyncronic io */ - max_length=info->end_of_file - next_pos_in_file; - diff_length=(next_pos_in_file & (IO_SIZE-1)); - - if (max_length > (my_off_t) info->read_length - diff_length) - max_length= (my_off_t) info->read_length - diff_length; - if (info->rc_request_pos != info->buffer) - read_buffer=info->buffer; - else - read_buffer=info->buffer+info->read_length; - info->aio_read_pos=next_pos_in_file; - if (max_length) - { - info->aio_result.result.aio_errno=AIO_INPROGRESS; /* Marker for test */ - DBUG_PRINT("aioread",("filepos: %ld length: %ld", - (ulong) next_pos_in_file,(ulong) max_length)); - if (aioread(info->file,read_buffer,(int) max_length, - (my_off_t) next_pos_in_file,MY_SEEK_SET, - &info->aio_result.result)) - { /* Skipp async io */ - my_errno=errno; - DBUG_PRINT("error",("got error: %d, aio_result: %d from aioread, async skipped", - errno, info->aio_result.result.aio_errno)); - if (info->rc_request_pos != info->buffer) - { - bmove(info->buffer,info->rc_request_pos, - (uint) (info->rc_end - info->rc_pos)); - info->rc_request_pos=info->buffer; - info->rc_pos-=info->read_length; - info->rc_end-=info->read_length; - } - info->read_length=info->buffer_length; /* Use hole buffer */ - info->read_function=_my_b_read; /* Use normal IO_READ next */ - } - else - info->inited=info->aio_result.pending=1; - } - return 0; /* Block read, async in use */ -} /* _my_b_async_read */ -#endif - - -/* Read one byte when buffer is empty */ - -int _my_b_get(IO_CACHE *info) -{ - byte buff; - if ((*(info)->read_function)(info,&buff,1)) - return my_b_EOF; - return (int) (uchar) buff; -} - - /* Returns != 0 if error on write */ - -int _my_b_write(register IO_CACHE *info, const byte *Buffer, uint Count) -{ - uint rest_length,length; - - rest_length=(uint) (info->rc_end - info->rc_pos); - memcpy(info->rc_pos,Buffer,(size_t) rest_length); - Buffer+=rest_length; - Count-=rest_length; - info->rc_pos+=rest_length; - if (info->pos_in_file+info->buffer_length > info->end_of_file) - { - my_errno=errno=EFBIG; - return info->error = -1; - } - if (flush_io_cache(info)) - return 1; - if (Count >= IO_SIZE) - { /* Fill first intern buffer */ - length=Count & (uint) ~(IO_SIZE-1); - if (info->seek_not_done) - { /* File touched, do seek */ - VOID(my_seek(info->file,info->pos_in_file,MY_SEEK_SET,MYF(0))); - info->seek_not_done=0; - } - if (my_write(info->file,Buffer,(uint) length,info->myflags | MY_NABP)) - return info->error= -1; - Count-=length; - Buffer+=length; - info->pos_in_file+=length; - } - memcpy(info->rc_pos,Buffer,(size_t) Count); - info->rc_pos+=Count; - return 0; -} - - -/* - Write a block to disk where part of the data may be inside the record - buffer. As all write calls to the data goes through the cache, - we will never get a seek over the end of the buffer -*/ - -int my_block_write(register IO_CACHE *info, const byte *Buffer, uint Count, - my_off_t pos) -{ - uint length; - int error=0; - - if (pos < info->pos_in_file) - { - /* Of no overlap, write everything without buffering */ - if (pos + Count <= info->pos_in_file) - return my_pwrite(info->file, Buffer, Count, pos, - info->myflags | MY_NABP); - /* Write the part of the block that is before buffer */ - length= (uint) (info->pos_in_file - pos); - if (my_pwrite(info->file, Buffer, length, pos, info->myflags | MY_NABP)) - info->error=error=-1; - Buffer+=length; - pos+= length; - Count-= length; - } - - /* Check if we want to write inside the used part of the buffer.*/ - length= (uint) (info->rc_end - info->buffer); - if (pos < info->pos_in_file + length) - { - uint offset= (uint) (pos - info->pos_in_file); - length-=offset; - if (length > Count) - length=Count; - memcpy(info->buffer+offset, Buffer, length); - Buffer+=length; - Count-= length; - /* Fix length of buffer if the new data was larger */ - if (info->buffer+length > info->rc_pos) - info->rc_pos=info->buffer+length; - if (!Count) - return (error); - } - /* Write at the end of the current buffer; This is the normal case */ - if (_my_b_write(info, Buffer, Count)) - error= -1; - return error; -} - - /* Flush write cache */ - -int flush_io_cache(IO_CACHE *info) -{ - uint length; - DBUG_ENTER("flush_io_cache"); - - if (info->type == WRITE_CACHE) - { - if (info->file == -1) - { - if (real_open_cached_file(info)) - DBUG_RETURN((info->error= -1)); - } - if (info->rc_pos != info->buffer) - { - length=(uint) (info->rc_pos - info->buffer); - if (info->seek_not_done) - { /* File touched, do seek */ - if (my_seek(info->file,info->pos_in_file,MY_SEEK_SET,MYF(0)) == - MY_FILEPOS_ERROR) - DBUG_RETURN((info->error= -1)); - info->seek_not_done=0; - } - info->rc_pos=info->buffer; - info->pos_in_file+=length; - info->rc_end=(info->buffer+info->buffer_length- - (info->pos_in_file & (IO_SIZE-1))); - if (my_write(info->file,info->buffer,length,info->myflags | MY_NABP)) - DBUG_RETURN((info->error= -1)); - DBUG_RETURN(0); - } - } -#ifdef HAVE_AIOWAIT - else if (info->type != READ_NET) - { - my_aiowait(&info->aio_result); /* Wait for outstanding req */ - info->inited=0; - } -#endif + info->read_end = (info->read_pos = (byte*) net->read_pos) + read_length; + Buffer[0] = info->read_pos[0]; /* length is always 1 */ + info->read_pos++; + + /* + info->request_pos is used by log_loaded_block() to know the size + of the current block + */ + info->request_pos=info->read_pos; DBUG_RETURN(0); } - -int end_io_cache(IO_CACHE *info) -{ - int error=0; - DBUG_ENTER("end_io_cache"); - if (info->buffer) - { - if (info->file != -1) /* File doesn't exist */ - error=flush_io_cache(info); - my_free((gptr) info->buffer,MYF(MY_WME)); - info->buffer=info->rc_pos=(byte*) 0; - } - DBUG_RETURN(error); -} /* end_io_cache */ - } /* extern "C" */ diff --git a/sql/mini_client.cc b/sql/mini_client.cc index 8f703b80e3a..ffb5d659238 100644 --- a/sql/mini_client.cc +++ b/sql/mini_client.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 */ @@ -22,25 +22,21 @@ in case we decide to make them external at some point */ -#define DONT_USE_RAID +#ifdef EMBEDDED_LIBRARY +#define net_read_timeout net_read_timeout1 +#define net_write_timeout net_write_timeout1 +#endif + #if defined(__WIN__) #include <winsock.h> -#include <odbcinst.h> -/* Disable alarms */ -typedef my_bool ALARM; -#define thr_alarm_init(A) (*(A))=0 -#define thr_alarm_in_use(A) (*(A)) -#define thr_end_alarm(A) -#define thr_alarm(A,B,C) local_thr_alarm((A),(B),(C)) -inline int local_thr_alarm(my_bool *A,int B __attribute__((unused)),ALARM *C __attribute__((unused))) -{ - *A=1; - return 0; -} -#define thr_got_alarm(A) 0 +#include <odbcinst.h> /* QQ: Is this really needed ? */ +#define DONT_USE_THR_ALARM #endif -#include <global.h> +#include <my_global.h> +#include <mysql_embed.h> +#include <mysql_com.h> +#include <violite.h> #include <my_sys.h> #include <mysys_err.h> #include <m_string.h> @@ -50,9 +46,8 @@ inline int local_thr_alarm(my_bool *A,int B __attribute__((unused)),ALARM *C __a #include "mysql_version.h" #include "mysqld_error.h" #include "errmsg.h" -#include <violite.h> -#if defined( OS2) && defined( MYSQL_SERVER) +#if defined( OS2) && defined(MYSQL_SERVER) #undef ER #define ER CER #endif @@ -76,23 +71,35 @@ extern "C" { // Because of SCO 3.2V4.2 #ifdef HAVE_SYS_SELECT_H #include <sys/select.h> #endif -#endif +#endif /*!defined(MSDOS) && !defined(__WIN__) */ #ifdef HAVE_SYS_UN_H # include <sys/un.h> #endif #if defined(THREAD) #include <my_pthread.h> /* because of signal() */ -#include <thr_alarm.h> #endif +#include <thr_alarm.h> #ifndef INADDR_NONE #define INADDR_NONE -1 #endif - } -static void mc_end_server(MYSQL *mysql); +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); + +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) @@ -187,8 +194,7 @@ HANDLE create_named_pipe(NET *net, uint connect_timeout, char **arg_host, ** Init MySQL structure or allocate one ****************************************************************************/ -MYSQL * STDCALL -mc_mysql_init(MYSQL *mysql) +MYSQL *mc_mysql_init(MYSQL *mysql) { init_client_errs(); if (!mysql) @@ -210,7 +216,7 @@ mc_mysql_init(MYSQL *mysql) ** Shut down connection **************************************************************************/ -static void +void mc_end_server(MYSQL *mysql) { DBUG_ENTER("mc_end_server"); @@ -332,11 +338,11 @@ static int mc_sock_connect(my_socket s, const struct sockaddr *name, ** or packet is an error message *****************************************************************************/ -uint STDCALL +ulong mc_net_safe_read(MYSQL *mysql) { NET *net= &mysql->net; - uint len=0; + ulong len=0; if (net->vio != 0) len=my_net_read(net); @@ -393,17 +399,17 @@ max_allowed_packet on this server"); } -char * STDCALL mc_mysql_error(MYSQL *mysql) +char *mc_mysql_error(MYSQL *mysql) { return (mysql)->net.last_error; } -int STDCALL mc_mysql_errno(MYSQL *mysql) +int mc_mysql_errno(MYSQL *mysql) { return (mysql)->net.last_errno; } -my_bool STDCALL mc_mysql_reconnect(MYSQL *mysql) +my_bool mc_mysql_reconnect(MYSQL *mysql) { MYSQL tmp_mysql; DBUG_ENTER("mc_mysql_reconnect"); @@ -433,7 +439,7 @@ my_bool STDCALL mc_mysql_reconnect(MYSQL *mysql) -int STDCALL +int mc_simple_command(MYSQL *mysql,enum enum_server_command command, const char *arg, uint length, my_bool skipp_check) { @@ -486,7 +492,7 @@ mc_simple_command(MYSQL *mysql,enum enum_server_command command, } -MYSQL * STDCALL +MYSQL * mc_mysql_connect(MYSQL *mysql,const char *host, const char *user, const char *passwd, const char *db, uint port, const char *unix_socket,uint client_flag) @@ -495,7 +501,7 @@ mc_mysql_connect(MYSQL *mysql,const char *host, const char *user, my_socket sock; ulong ip_addr; struct sockaddr_in sock_addr; - uint pkt_length; + ulong pkt_length; NET *net= &mysql->net; thr_alarm_t alarmed; ALARM alarm_buff; @@ -750,6 +756,20 @@ mc_mysql_connect(MYSQL *mysql,const char *host, const char *user, mysql->client_flag=client_flag; #ifdef HAVE_OPENSSL + if ((mysql->server_capabilities & CLIENT_SSL) && + (mysql->options.use_ssl || (client_flag & CLIENT_SSL))) + { + DBUG_PRINT("info", ("Changing IO layer to SSL")); + client_flag |= CLIENT_SSL; + } + else + { + if (client_flag & CLIENT_SSL) + { + DBUG_PRINT("info", ("Leaving IO layer intact because server doesn't support SSL")); + } + client_flag &= ~CLIENT_SSL; + } /* Oops.. are we careful enough to not send ANY information */ /* without encryption? */ if (client_flag & CLIENT_SSL) @@ -758,15 +778,14 @@ mc_mysql_connect(MYSQL *mysql,const char *host, const char *user, goto error; /* Do the SSL layering. */ 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); + DBUG_PRINT("info", ("IO context %p",((struct st_VioSSLConnectorFd*)mysql->connector_fd)->ssl_context_)); + sslconnect((struct st_VioSSLConnectorFd*)(mysql->connector_fd),mysql->net.vio,60L); + DBUG_PRINT("info", ("IO layer change done!")); } #endif /* HAVE_OPENSSL */ - int3store(buff+2,max_allowed_packet); + + if (user && user[0]) strmake(buff+5,user,32); else @@ -805,12 +824,38 @@ error: DBUG_RETURN(0); } + +#ifdef HAVE_OPENSSL +/* +************************************************************************** +** Free strings in the SSL structure and clear 'use_ssl' flag. +** NB! Errors are not reported until you do mysql_real_connect. +************************************************************************** +*/ +int +mysql_ssl_clear(MYSQL *mysql) +{ + my_free(mysql->options.ssl_key, MYF(MY_ALLOW_ZERO_PTR)); + my_free(mysql->options.ssl_cert, MYF(MY_ALLOW_ZERO_PTR)); + my_free(mysql->options.ssl_ca, MYF(MY_ALLOW_ZERO_PTR)); + my_free(mysql->options.ssl_capath, MYF(MY_ALLOW_ZERO_PTR)); + mysql->options.ssl_key = 0; + mysql->options.ssl_cert = 0; + mysql->options.ssl_ca = 0; + mysql->options.ssl_capath = 0; + mysql->options.use_ssl = FALSE; + my_free(mysql->connector_fd,MYF(MY_ALLOW_ZERO_PTR)); + mysql->connector_fd = 0; + return 0; +} +#endif /* HAVE_OPENSSL */ + /************************************************************************* ** Send a QUIT to the server and close the connection ** If handle is alloced by mysql connect free it. *************************************************************************/ -void STDCALL +void mc_mysql_close(MYSQL *mysql) { DBUG_ENTER("mysql_close"); @@ -833,11 +878,502 @@ 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; + mysql_ssl_clear(mysql); #endif /* HAVE_OPENSSL */ if (mysql->free_me) my_free((gptr) mysql,MYF(0)); } DBUG_VOID_RETURN; } + +void 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 (;;) + { + ulong pkt_len; + if ((pkt_len=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 +mc_mysql_send_query(MYSQL* mysql, const char* query, uint length) +{ + return mc_simple_command(mysql, COM_QUERY, query, length, 1); +} + +int mc_mysql_read_query_result(MYSQL *mysql) +{ + uchar *pos; + ulong field_count; + MYSQL_DATA *fields; + ulong 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 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, result= -1; + uint packet_length=MY_ALIGN(mysql->net.max_packet-16,IO_SIZE); + char *buf, tmp_name[FN_REFLEN]; + DBUG_ENTER("send_file_to_server"); + + if (!(buf=my_malloc(packet_length,MYF(0)))) + { + strmov(mysql->net.last_error, ER(mysql->net.last_errno=CR_OUT_OF_MEMORY)); + DBUG_RETURN(-1); + } + + fn_format(tmp_name,filename,"","",4); /* Convert to client format */ + if ((fd = my_open(tmp_name,O_RDONLY, MYF(0))) < 0) + { + my_net_write(&mysql->net,"",0); // Server needs one packet + net_flush(&mysql->net); + mysql->net.last_errno=EE_FILENOTFOUND; + my_snprintf(mysql->net.last_error,sizeof(mysql->net.last_error)-1, + EE(mysql->net.last_errno),tmp_name, errno); + goto err; + } + + while ((readcount = (int) my_read(fd,(byte*) buf,packet_length,MYF(0))) > 0) + { + if (my_net_write(&mysql->net,buf,readcount)) + { + DBUG_PRINT("error",("Lost connection to MySQL server during LOAD DATA of local file")); + mysql->net.last_errno=CR_SERVER_LOST; + strmov(mysql->net.last_error,ER(mysql->net.last_errno)); + goto err; + } + } + /* 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); + goto err; + } + if (readcount < 0) + { + mysql->net.last_errno=EE_READ; /* the errmsg for not entire file read */ + my_snprintf(mysql->net.last_error,sizeof(mysql->net.last_error)-1, + tmp_name,errno); + goto err; + } + result=0; // Ok + +err: + if (fd >= 0) + (void) my_close(fd,MYF(0)); + my_free(buf,MYF(0)); + DBUG_RETURN(result); +} + + +/* 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; + ulong 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=mc_net_safe_read(mysql)) == packet_error) + DBUG_RETURN(0); + if (!(result=(MYSQL_DATA*) my_malloc(sizeof(MYSQL_DATA), + MYF(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=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 mc_mysql_num_rows(MYSQL_RES *res) +{ + return res->row_count; +} + +unsigned int mc_mysql_num_fields(MYSQL_RES *res) +{ + return res->field_count; +} + +void 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 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 *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_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..6721b072080 100644 --- a/sql/mini_client.h +++ b/sql/mini_client.h @@ -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 */ @@ -18,30 +18,34 @@ #define _MINI_CLIENT_H -MYSQL* STDCALL -mc_mysql_connect(MYSQL *mysql,const char *host, const char *user, +MYSQL* mc_mysql_connect(MYSQL *mysql,const char *host, const char *user, const char *passwd, const char *db, uint port, const char *unix_socket,uint client_flag); -int STDCALL -mc_simple_command(MYSQL *mysql,enum enum_server_command command, const char *arg, +int mc_simple_command(MYSQL *mysql,enum enum_server_command command, const char *arg, uint length, my_bool skipp_check); -void STDCALL -mc_mysql_close(MYSQL *mysql); +void mc_mysql_close(MYSQL *mysql); -MYSQL * STDCALL -mc_mysql_init(MYSQL *mysql); +MYSQL * mc_mysql_init(MYSQL *mysql); -void STDCALL -mc_mysql_debug(const char *debug); +void mc_mysql_debug(const char *debug); -uint STDCALL -mc_net_safe_read(MYSQL *mysql); +ulong mc_net_safe_read(MYSQL *mysql); -char * STDCALL mc_mysql_error(MYSQL *mysql); -int STDCALL mc_mysql_errno(MYSQL *mysql); -my_bool STDCALL mc_mysql_reconnect(MYSQL* mysql); +char * mc_mysql_error(MYSQL *mysql); +int mc_mysql_errno(MYSQL *mysql); +my_bool mc_mysql_reconnect(MYSQL* mysql); +int mc_mysql_send_query(MYSQL* mysql, const char* query, uint length); +int mc_mysql_read_query_result(MYSQL *mysql); +int mc_mysql_query(MYSQL *mysql, const char *query, uint length); +MYSQL_RES * mc_mysql_store_result(MYSQL *mysql); +void mc_mysql_free_result(MYSQL_RES *result); +void mc_mysql_data_seek(MYSQL_RES *result, my_ulonglong row); +my_ulonglong mc_mysql_num_rows(MYSQL_RES *res); +unsigned int mc_mysql_num_fields(MYSQL_RES *res); +MYSQL_ROW STDCALL mc_mysql_fetch_row(MYSQL_RES *res); +int mc_mysql_select_db(MYSQL *mysql, const char *db); +void mc_end_server(MYSQL *mysql); #endif - diff --git a/sql/my_lock.c b/sql/my_lock.c index 647c07a03c3..4d451fcff22 100644 --- a/sql/my_lock.c +++ b/sql/my_lock.c @@ -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 */ @@ -19,7 +19,7 @@ #else #undef MAP_TO_USE_RAID /* Avoid RAID mappings */ -#include <global.h> +#include <my_global.h> #include <my_sys.h> #include <mysys_err.h> #include <my_pthread.h> diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index 4cee7d04ef0..d1d538dd3c9 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -17,16 +17,16 @@ #ifndef _MYSQL_PRIV_H #define _MYSQL_PRIV_H -#include <global.h> +#include <my_global.h> +#include "mysql_embed.h" #include <my_sys.h> #include <m_string.h> -#include "mysql_version.h" +#include <mysql_version.h> #include <hash.h> #include <signal.h> #include <thr_lock.h> #include <my_base.h> /* Needed by field.h */ #include <my_bitmap.h> -#include <violite.h> #ifdef __EMX__ #undef write // remove pthread.h macro definition for EMX @@ -37,6 +37,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); @@ -47,6 +48,8 @@ char *sql_strmake(const char *str,uint len); gptr sql_memdup(const void * ptr,unsigned size); void sql_element_free(void *ptr); void kill_one_thread(THD *thd, ulong id); +int net_request_file(NET* net, const char* fname); +char* query_table_status(THD *thd,const char *db,const char *table_name); #define x_free(A) { my_free((gptr) (A),MYF(MY_WME | MY_FAE | MY_ALLOW_ZERO_PTR)); } #define safeFree(x) { if(x) { my_free((gptr) x,MYF(0)); x = NULL; } } @@ -69,7 +72,6 @@ void kill_one_thread(THD *thd, ulong id); #define HASH_PASSWORD_LENGTH 16 #define HOST_CACHE_SIZE 128 #define MAX_ACCEPT_RETRY 10 // Test accept this many times -#define MAX_BLOB_WIDTH 8192 // Default width for blob #define MAX_FIELDS_BEFORE_HASH 32 #define USER_VARS_HASH_SIZE 16 #define STACK_MIN_SIZE 8192 // Abort if less stack during eval. @@ -155,9 +157,9 @@ void kill_one_thread(THD *thd, ulong id); #define SELECT_DESCRIBE 4 #define SELECT_SMALL_RESULT 8 #define SELECT_BIG_RESULT 16 -#define SELECT_HIGH_PRIORITY 64 /* Intern */ -#define SELECT_USE_CACHE 256 /* Intern */ -#define SELECT_COUNT_DISTINCT 512 /* Intern */ +#define OPTION_FOUND_ROWS 32 +#define OPTION_TO_QUERY_CACHE 64 +#define SELECT_NO_JOIN_CACHE 256 /* Intern */ #define OPTION_BIG_TABLES 512 /* for SQL OPTION */ #define OPTION_BIG_SELECTS 1024 /* for SQL OPTION */ @@ -184,16 +186,33 @@ void kill_one_thread(THD *thd, ulong id); #define QUERY_NO_INDEX_USED OPTION_STATUS_NO_TRANS_UPDATE*2 #define QUERY_NO_GOOD_INDEX_USED QUERY_NO_INDEX_USED*2 +#define SELECT_NO_UNLOCK (QUERY_NO_GOOD_INDEX_USED*2) +#define TMP_TABLE_ALL_COLUMNS (SELECT_NO_UNLOCK*2) + /* Bits for different SQL modes modes (including ANSI mode) */ -#define MODE_REAL_AS_FLOAT 1 -#define MODE_PIPES_AS_CONCAT 2 -#define MODE_ANSI_QUOTES 4 -#define MODE_IGNORE_SPACE 8 -#define MODE_SERIALIZABLE 16 -#define MODE_ONLY_FULL_GROUP_BY 32 +#define MODE_REAL_AS_FLOAT 1 +#define MODE_PIPES_AS_CONCAT 2 +#define MODE_ANSI_QUOTES 4 +#define MODE_IGNORE_SPACE 8 +#define MODE_SERIALIZABLE 16 +#define MODE_ONLY_FULL_GROUP_BY 32 +#define MODE_NO_UNSIGNED_SUBTRACTION 64 #define RAID_BLOCK_SIZE 1024 +// Sync points allow us to force the server to reach a certain line of code +// and block there until the client tells the server it is ok to go on. +// The client tells the server to block with SELECT GET_LOCK() +// and unblocks it with SELECT RELEASE_LOCK(). Used for debugging difficult +// concurrency problems +#ifdef EXTRA_DEBUG +#define DBUG_SYNC_POINT(lock_name,lock_timeout) \ + debug_sync_point(lock_name,lock_timeout) +void debug_sync_point(const char* lock_name, uint lock_timeout); +#else +#define DBUG_SYNC_POINT(lock_name,lock_timeout) +#endif + /* BINLOG_DUMP options */ #define BINLOG_DUMP_NON_BLOCK 1 @@ -241,16 +260,47 @@ inline THD *_current_thd(void) #include "sql_class.h" #include "opt_range.h" - -void mysql_create_db(THD *thd, char *db, uint create_info); +#ifdef HAVE_QUERY_CACHE +#include "sql_cache.h" +#define query_cache_store_query(A, B) query_cache.store_query(A, B) +#define query_cache_destroy() query_cache.destroy() +#define query_cache_result_size_limit(A) query_cache.result_size_limit(A) +#define query_cache_resize(A) query_cache.resize(A) +#define query_cache_invalidate3(A, B, C) query_cache.invalidate(A, B, C) +#define query_cache_invalidate1(A) query_cache.invalidate(A) +#define query_cache_send_result_to_client(A, B, C) \ + query_cache.send_result_to_client(A, B, C) +#define query_cache_invalidate_by_MyISAM_filename_ref \ + &query_cache_invalidate_by_MyISAM_filename +#else +#define query_cache_store_query(A, B) +#define query_cache_destroy() +#define query_cache_result_size_limit(A) +#define query_cache_resize(A) +#define query_cache_invalidate3(A, B, C) +#define query_cache_invalidate1(A) +#define query_cache_send_result_to_client(A, B, C) 0 +#define query_cache_invalidate_by_MyISAM_filename_ref NULL + +#define query_cache_abort(A) +#define query_cache_end_of_result(A) +#define query_cache_invalidate_by_MyISAM_filename_ref NULL +#endif /*HAVE_QUERY_CACHE*/ + +int mysql_create_db(THD *thd, char *db, uint create_info, bool silent); +int mysql_rm_db(THD *thd,char *db,bool if_exists, bool silent); 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 mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists, + bool log_query); int quick_rm_table(enum db_type base,const char *db, const char *table_name); 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); +bool mysql_new_select(LEX *lex); +void mysql_init_multi_delete(LEX *lex); void init_max_user_conn(void); void free_max_user_conn(void); pthread_handler_decl(handle_one_connection,arg); @@ -260,23 +310,21 @@ 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); +bool reload_acl_and_cache(THD *thd, ulong options, TABLE_LIST *tables); void table_cache_init(void); void table_cache_free(void); uint cached_tables(void); 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 no_grant=0, bool no_errors=0); +bool check_table_access(THD *thd,uint want_access, TABLE_LIST *tables, + bool no_errors=0); bool check_process_priv(THD *thd=0); -int generate_table(THD *thd, TABLE_LIST *table_list, - TABLE *locked_table); - - int mysql_backup_table(THD* thd, TABLE_LIST* table_list); int mysql_restore_table(THD* thd, TABLE_LIST* table_list); @@ -290,7 +338,6 @@ int mysql_optimize_table(THD* thd, TABLE_LIST* table_list, HA_CHECK_OPT* check_opt); /* net_pkg.c */ -void send_error(NET *net,uint sql_errno=0, const char *err=0); void send_warning(NET *net, uint sql_errno, const char *err=0); void net_printf(NET *net,uint sql_errno, ...); void send_ok(NET *net,ha_rows affected_rows=0L,ulonglong id=0L, @@ -317,10 +364,12 @@ SORT_FIELD * make_unireg_sortorder(ORDER *order, uint *length); int setup_order(THD *thd,TABLE_LIST *tables, List<Item> &fields, List <Item> &all_fields, ORDER *order); +int handle_select(THD *thd, LEX *lex, select_result *result); int mysql_select(THD *thd,TABLE_LIST *tables,List<Item> &list,COND *conds, ORDER *order, ORDER *group,Item *having,ORDER *proc_param, - uint select_type,select_result *result); -Field *create_tmp_field(TABLE *table,Item *item, Item::Type type, + ulong select_type,select_result *result); +int mysql_union(THD *thd,LEX *lex,select_result *result); +Field *create_tmp_field(THD *thd, TABLE *table,Item *item, Item::Type type, Item_result_field ***copy_func, Field **from_field, bool group,bool modify_item); int mysql_create_table(THD *thd,const char *db, const char *table_name, @@ -344,7 +393,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, @@ -355,15 +406,17 @@ 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); +int mysql_truncate(THD *thd, TABLE_LIST *table_list, bool dont_send_ok=0); 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); @@ -381,13 +434,28 @@ void abort_locked_tables(THD *thd,const char *db, const char *table_name); Field *find_field_in_tables(THD *thd,Item_field *item,TABLE_LIST *tables); Field *find_field_in_table(THD *thd,TABLE *table,const char *name,uint length, bool check_grant,bool allow_rowid); +#ifdef HAVE_OPENSSL +struct st_des_keyblock +{ + des_cblock key1, key2, key3; +}; +struct st_des_keyschedule +{ + des_key_schedule ks1, ks2, ks3; +}; +extern char *des_key_file; +extern struct st_des_keyschedule des_keyschedule[10]; +extern uint des_default_key; +extern pthread_mutex_t LOCK_des_key_file; +bool load_des_key_file(const char *file_name); +#endif /* HAVE_OPENSSL */ /* sql_do.cc */ int mysql_do(THD *thd, List<Item> &values); /* 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, @@ -403,6 +471,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, bool dont_send_ok=0); +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, @@ -424,9 +498,13 @@ 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); + bool set_query_id,List<Item> *sum_func_list, + bool allow_sum_func); int setup_conds(THD *thd,TABLE_LIST *tables,COND **conds); int setup_ftfuncs(THD *thd); int init_ftfuncs(THD *thd, bool no_order); @@ -441,6 +519,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); @@ -455,8 +534,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); @@ -471,7 +549,7 @@ int write_record(TABLE *table,COPY_INFO *info); /* bits set in manager_status */ #define MANAGER_BERKELEY_LOG_CLEANUP (1L << 0) extern ulong volatile manager_status; -extern bool volatile manager_thread_in_use; +extern bool volatile manager_thread_in_use, mqh_used; extern pthread_t manager_thread; extern pthread_mutex_t LOCK_manager; extern pthread_cond_t COND_manager; @@ -481,8 +559,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 */ @@ -497,11 +574,17 @@ void init_errmessage(void); void sql_perror(const char *message); void sql_print_error(const char *format,...) __attribute__ ((format (printf, 1, 2))); +bool fn_format_relative_to_data_home(my_string to, const char *name, + const char *dir, const char *extension); +void open_log(MYSQL_LOG *log, const char *hostname, + const char *opt_name, const char *extension, + enum_log_type type, bool read_append = 0, + bool no_auto_events = 0); extern uint32 server_id; -extern char mysql_data_home[2],server_version[SERVER_VERSION_LENGTH], +extern char *mysql_data_home,server_version[SERVER_VERSION_LENGTH], max_sort_char, mysql_real_data_home[]; -extern my_string mysql_unix_port,mysql_tmpdir; +extern my_string mysql_tmpdir; extern const char *first_keyword, *localhost, *delayed_user; extern ulong refresh_version,flush_version, thread_id,query_id,opened_tables, created_tmp_tables, created_tmp_disk_tables, @@ -515,8 +598,9 @@ extern ulong filesort_merge_passes; extern ulong select_range_check_count, select_range_count, select_scan_count; extern ulong select_full_range_join_count,select_full_join_count, slave_open_temp_tables; -extern uint test_flags,select_errors,mysql_port,ha_open_options; +extern uint test_flags,select_errors,ha_open_options; extern ulong thd_startup_options, slow_launch_threads, slow_launch_time; +extern ulong query_cache_startup_type; extern time_t start_time; extern const char *command_name[]; extern I_List<THD> threads; @@ -528,17 +612,18 @@ 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; -extern pthread_cond_t COND_refresh,COND_thread_count, COND_binlog_update, - COND_slave_stopped, COND_slave_start; + LOCK_server_id, LOCK_slave_list, LOCK_active_mi; +extern pthread_cond_t COND_refresh,COND_thread_count; extern pthread_attr_t connection_attrib; extern bool opt_endinfo, using_udf_functions, locked_in_memory, - opt_using_transactions, use_temp_pool, opt_local_infile; + opt_using_transactions, use_temp_pool, mysql_embedded; +extern bool opt_local_infile; extern char f_fyllchar; extern ulong ha_read_count, ha_write_count, ha_delete_count, ha_update_count, ha_read_key_count, ha_read_next_count, ha_read_prev_count, ha_read_first_count, ha_read_last_count, - ha_read_rnd_count, ha_read_rnd_next_count; + ha_read_rnd_count, ha_read_rnd_next_count, + ha_commit_count, ha_rollback_count; extern MY_BITMAP temp_pool; extern uchar *days_in_month; extern DATE_FORMAT dayord; @@ -550,14 +635,15 @@ extern ulong keybuff_size,sortbuff_size,max_item_sort_length,table_cache_size, max_insert_delayed_threads, max_user_connections, long_query_count,net_wait_timeout,net_interactive_timeout, net_read_timeout,net_write_timeout, - what_to_log,flush_time, opt_sql_mode, + what_to_log,flush_time,opt_sql_mode, max_tmp_tables,max_heap_table_size,query_buff_size, lower_case_table_names,thread_stack,thread_stack_min, binlog_cache_size, max_binlog_cache_size, record_rnd_cache_size; extern ulong com_stat[(uint) SQLCOM_END], com_other; extern ulong specialflag, current_pid; -extern bool low_priority_updates, using_update_log,opt_warnings; -extern bool opt_sql_bin_update, opt_safe_show_db, opt_safe_user_create; +extern bool low_priority_updates, using_update_log; +extern bool opt_sql_bin_update, opt_safe_show_db, opt_warnings, + opt_safe_user_create, opt_no_mix_types; extern char language[LIBLEN],reg_ext[FN_EXTLEN],blob_newline; extern const char **errmesg; /* Error messages */ extern const char *default_tx_isolation_name; @@ -566,6 +652,7 @@ extern struct show_var_st init_vars[]; extern struct show_var_st status_vars[]; extern enum db_type default_table_type; extern enum enum_tx_isolation default_tx_isolation; +extern char glob_hostname[FN_REFLEN]; #ifndef __WIN__ extern pthread_t signal_thread; @@ -581,8 +668,13 @@ void mysql_unlock_some_tables(THD *thd, TABLE **table,uint count); void mysql_lock_remove(THD *thd, MYSQL_LOCK *locked,TABLE *table); void mysql_lock_abort(THD *thd, TABLE *table); MYSQL_LOCK *mysql_lock_merge(MYSQL_LOCK *a,MYSQL_LOCK *b); +bool lock_global_read_lock(THD *thd); +void unlock_global_read_lock(THD *thd); +bool wait_if_global_read_lock(THD *thd, bool abort_on_refresh); +void start_waiting_global_read_lock(THD *thd); /* Lock based on name */ +int lock_and_wait_for_table_name(THD *thd, TABLE_LIST *table_list); int lock_table_name(THD *thd, TABLE_LIST *table_list); void unlock_table_name(THD *thd, TABLE_LIST *table_list); bool wait_for_locked_table_names(THD *thd, TABLE_LIST *table_list); @@ -625,7 +717,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); @@ -667,7 +759,6 @@ void hostname_cache_refresh(void); bool get_interval_info(const char *str,uint length,uint count, long *values); /* sql_cache */ - extern bool sql_cache_init(); extern void sql_cache_free(); extern int sql_cache_hit(THD *thd, char *inBuf, uint length); @@ -676,7 +767,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) { @@ -684,11 +775,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 9ac6ea6fff8..db500879058 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -21,15 +21,13 @@ #include "sql_acl.h" #include "slave.h" #include "sql_repl.h" +#include "repl_failsafe.h" #include "stacktrace.h" #ifdef HAVE_BERKELEY_DB #include "ha_berkeley.h" #endif #ifdef HAVE_INNOBASE_DB -#include "ha_innobase.h" -#endif -#ifdef HAVE_GEMINI_DB -#include "ha_gemini.h" +#include "ha_innodb.h" #endif #include "ha_myisam.h" #include <nisam.h> @@ -40,10 +38,26 @@ #define ONE_THREAD #endif +#ifdef SAFEMALLOC +#define SHUTDOWN_THD shutdown_th=pthread_self(); +#define MAIN_THD main_th=pthread_self(); +#define SIGNAL_THD signal_th=pthread_self(); +#else +#define SHUTDOWN_THD +#define MAIN_THD +#define SIGNAL_THD +#endif + +/* 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__ */ + #if defined(HAVE_DEC_3_2_THREADS) || defined(SIGNALS_DONT_BREAK_READ) #define HAVE_CLOSE_SERVER_SOCK 1 -void close_server_sock(); -#endif +#endif extern "C" { // Because of SCO 3.2V4.2 #include <errno.h> @@ -51,7 +65,7 @@ extern "C" { // Because of SCO 3.2V4.2 #ifndef __GNU_LIBRARY__ #define __GNU_LIBRARY__ // Skip warnings in getopt.h #endif -#include <getopt.h> +#include <my_getopt.h> #ifdef HAVE_SYSENT_H #include <sysent.h> #endif @@ -77,9 +91,7 @@ extern "C" { // Because of SCO 3.2V4.2 #include <sys/select.h> #endif #include <sys/utsname.h> -#else -#include <windows.h> -#endif // __WIN__ +#endif /* __WIN__ */ #ifdef HAVE_LIBWRAP #include <tcpd.h> @@ -197,17 +209,12 @@ SHOW_COMP_OPTION have_berkeley_db=SHOW_OPTION_YES; #else SHOW_COMP_OPTION have_berkeley_db=SHOW_OPTION_NO; #endif -#ifdef HAVE_GEMINI_DB -SHOW_COMP_OPTION have_gemini=SHOW_OPTION_YES; -#else -SHOW_COMP_OPTION have_gemini=SHOW_OPTION_NO; -#endif #ifdef HAVE_INNOBASE_DB SHOW_COMP_OPTION have_innodb=SHOW_OPTION_YES; #else SHOW_COMP_OPTION have_innodb=SHOW_OPTION_NO; #endif -#ifndef NO_ISAM +#ifdef HAVE_ISAM SHOW_COMP_OPTION have_isam=SHOW_OPTION_YES; #else SHOW_COMP_OPTION have_isam=SHOW_OPTION_NO; @@ -218,51 +225,63 @@ SHOW_COMP_OPTION have_raid=SHOW_OPTION_YES; SHOW_COMP_OPTION have_raid=SHOW_OPTION_NO; #endif #ifdef HAVE_OPENSSL -SHOW_COMP_OPTION have_ssl=SHOW_OPTION_YES; +SHOW_COMP_OPTION have_openssl=SHOW_OPTION_YES; +#else +SHOW_COMP_OPTION have_openssl=SHOW_OPTION_NO; +#endif +SHOW_COMP_OPTION have_symlink=SHOW_OPTION_YES; +#ifdef HAVE_QUERY_CACHE +SHOW_COMP_OPTION have_query_cache=SHOW_OPTION_YES; #else -SHOW_COMP_OPTION have_ssl=SHOW_OPTION_NO; +SHOW_COMP_OPTION have_query_cache=SHOW_OPTION_NO; #endif +bool opt_skip_slave_start = 0; // If set, slave is not autostarted -static bool opt_skip_slave_start = 0; // if set, slave is not autostarted +/* if set, some standard measures to enforce + slave data intergity will not be performed + */ +bool opt_reckless_slave = 0; +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; +static ulong opt_myisam_block_size; +static my_socket unix_sock= INVALID_SOCKET,ip_sock= INVALID_SOCKET; static my_string opt_logname=0,opt_update_logname=0, opt_binlog_index_name = 0,opt_slow_logname=0; static char mysql_home[FN_REFLEN],pidfile_name[FN_REFLEN]; +static char* mysql_home_ptr= mysql_home; +static char* pidfile_name_ptr= pidfile_name; static pthread_t select_thread; 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_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, - opt_safe_user_create=0; + opt_show_slave_auth_info = 0, opt_old_rpl_compat = 0, + opt_safe_user_create = 0, opt_no_mix_types = 0; +volatile bool mqh_used = 0; FILE *bootstrap_file=0; int segfaulted = 0; // ensure we do not enter SIGSEGV handler twice -extern MASTER_INFO glob_mi; -extern int init_master_info(MASTER_INFO* mi); -// if sql_bin_update is true, SQL_LOG_UPDATE and SQL_LOG_BIN are kept in sync, -// and are treated as aliases for each other +/* + If sql_bin_update is true, SQL_LOG_UPDATE and SQL_LOG_BIN are kept in sync, + and are treated as aliases for each other +*/ static bool kill_in_progress=FALSE; static struct rand_struct sql_rand; static int cleanup_done; static char **defaults_argv,time_zone[30]; static const char *default_table_type_name; -static char glob_hostname[FN_REFLEN]; +char glob_hostname[FN_REFLEN]; +#include "sslopt-vars.h" #ifdef HAVE_OPENSSL -static bool opt_use_ssl = FALSE; -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; +char *des_key_file = 0; +struct st_VioSSLAcceptorFd * ssl_acceptor_fd = 0; #endif /* HAVE_OPENSSL */ - I_List <i_string_pair> replicate_rewrite_db; I_List<i_string> replicate_do_db, replicate_ignore_db; // allow the user to tell us which db to replicate and which to ignore @@ -273,9 +292,9 @@ 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; + wake_thread=0; ulong thd_startup_options=(OPTION_UPDATE_LOG | OPTION_AUTO_IS_NULL | OPTION_BIN_LOG | OPTION_QUOTE_SHOW_CREATE ); uint protocol_version=PROTOCOL_VERSION; @@ -289,14 +308,26 @@ ulong keybuff_size,sortbuff_size,max_item_sort_length,table_cache_size, ulong com_stat[(uint) SQLCOM_END], com_other; ulong slave_net_timeout; ulong thread_cache_size=0, binlog_cache_size=0, max_binlog_cache_size=0; +ulong query_cache_size=0; +#ifdef HAVE_QUERY_CACHE +ulong query_cache_limit=0, query_cache_startup_type=1; +Query_cache query_cache; +#endif + 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"; + master_info_file = (char*) "master.info", + relay_log_info_file = (char*) "relay-log.info", + master_ssl_key=0, master_ssl_cert=0; +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; +bool master_ssl = 0; ulong max_tmp_tables,max_heap_table_size,master_retry_count=0; ulong bytes_sent = 0L, bytes_received = 0L; @@ -324,11 +355,22 @@ ulong slow_launch_threads = 0; ulong myisam_max_sort_file_size, myisam_max_extra_sort_file_size; char mysql_real_data_home[FN_REFLEN], - mysql_data_home[2],language[LIBLEN],reg_ext[FN_EXTLEN], + language[LIBLEN],reg_ext[FN_EXTLEN], default_charset[LIBLEN],mysql_charsets_dir[FN_REFLEN], *charsets_list, blob_newline,f_fyllchar,max_sort_char,*mysqld_user,*mysqld_chroot, *opt_init_file; +char *language_ptr= language; +char *default_charset_ptr= default_charset; +#ifndef EMBEDDED_LIBRARY +char mysql_data_home_buff[2], *mysql_data_home=mysql_data_home_buff; +bool mysql_embedded=0; +#else +char *mysql_data_home=mysql_real_data_home; +bool mysql_embedded=1; +#endif + char *opt_bin_logname = 0; // this one needs to be seen in sql_parse.cc +char *opt_relay_logname = 0, *opt_relaylog_index_name=0; char server_version[SERVER_VERSION_LENGTH]=MYSQL_SERVER_VERSION; const char *first_keyword="first"; const char **errmesg; /* Error messages */ @@ -337,10 +379,9 @@ const char *sql_mode_str="OFF"; const char *default_tx_isolation_name; enum_tx_isolation default_tx_isolation=ISO_READ_COMMITTED; -#ifdef HAVE_GEMINI_DB -const char *gemini_recovery_options_str="FULL"; -#endif -my_string mysql_unix_port=NULL,mysql_tmpdir=NULL; +uint rpl_recovery_rank=0; + +my_string mysql_unix_port=NULL, mysql_tmpdir=NULL, allocated_mysql_tmpdir=NULL; ulong my_bind_addr; /* the address we bind to */ DATE_FORMAT dayord; double log_10[32]; /* 10 potences */ @@ -350,8 +391,8 @@ time_t start_time; ulong opt_sql_mode = 0L; const char *sql_mode_names[] = { "REAL_AS_FLOAT", "PIPES_AS_CONCAT", "ANSI_QUOTES", "IGNORE_SPACE", - "SERIALIZE","ONLY_FULL_GROUP_BY", NullS }; -TYPELIB sql_mode_typelib= {array_elements(sql_mode_names),"", + "SERIALIZE","ONLY_FULL_GROUP_BY", "NO_UNSIGNED_SUBTRACTION",NullS }; +TYPELIB sql_mode_typelib= {array_elements(sql_mode_names)-1,"", sql_mode_names}; MY_BITMAP temp_pool; @@ -365,8 +406,8 @@ pthread_mutex_t LOCK_mysql_create_db, LOCK_Acl, LOCK_open, LOCK_thread_count, LOCK_error_log, 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_server_id, + LOCK_user_conn, LOCK_slave_list, LOCK_active_mi; pthread_cond_t COND_refresh,COND_thread_count,COND_binlog_update, COND_slave_stopped, COND_slave_start; @@ -378,10 +419,13 @@ enum db_type default_table_type=DB_TYPE_MYISAM; #ifdef __WIN__ #undef getpid #include <process.h> +#if !defined(EMBEDDED_LIBRARY) HANDLE hEventShutdown; +static char *event_name; #include "nt_servc.h" static NTService Service; // Service object for WinNT #endif +#endif #ifdef OS2 pthread_cond_t eventShutdown; @@ -396,6 +440,7 @@ static void fix_paths(void); static pthread_handler_decl(handle_connections_sockets,arg); static pthread_handler_decl(kill_server_thread,arg); static int bootstrap(FILE *file); +static void close_server_sock(); static bool read_init_file(char *file_name); #ifdef __NT__ static pthread_handler_decl(handle_connections_namedpipes,arg); @@ -445,15 +490,7 @@ static void close_connections(void) if (pthread_kill(select_thread,THR_CLIENT_ALARM)) break; // allready dead #endif -#ifdef HAVE_TIMESPEC_TS_SEC - abstime.ts_sec=time(NULL)+2; // Bsd 2.1 - abstime.ts_nsec=0; -#else - struct timeval tv; - gettimeofday(&tv,0); - abstime.tv_sec=tv.tv_sec+2; - abstime.tv_nsec=tv.tv_usec*1000; -#endif + set_timespec(abstime, 2); for (uint tmp=0 ; tmp < 10 ; tmp++) { error=pthread_cond_timedwait(&COND_thread_count,&LOCK_thread_count, @@ -465,9 +502,7 @@ static void close_connections(void) if (error != 0 && !count++) sql_print_error("Got error %d from pthread_cond_timedwait",error); #endif -#if defined(HAVE_DEC_3_2_THREADS) || defined(SIGNALS_DONT_BREAK_READ) close_server_sock(); -#endif } (void) pthread_mutex_unlock(&LOCK_thread_count); #endif /* __WIN__ */ @@ -485,28 +520,28 @@ static void close_connections(void) } } #ifdef __NT__ -if (hPipe != INVALID_HANDLE_VALUE && opt_enable_named_pipe) -{ - HANDLE temp; - DBUG_PRINT( "quit", ("Closing named pipes") ); - - /* Create connection to the handle named pipe handler to break the loop */ - if ((temp = CreateFile(szPipeName, - GENERIC_READ | GENERIC_WRITE, - 0, - NULL, - OPEN_EXISTING, - 0, - NULL )) != INVALID_HANDLE_VALUE) + if (hPipe != INVALID_HANDLE_VALUE && opt_enable_named_pipe) { - WaitNamedPipe(szPipeName, 1000); - DWORD dwMode = PIPE_READMODE_BYTE | PIPE_WAIT; - SetNamedPipeHandleState(temp, &dwMode, NULL, NULL); - CancelIo(temp); - DisconnectNamedPipe(temp); - CloseHandle(temp); + HANDLE temp; + DBUG_PRINT( "quit", ("Closing named pipes") ); + + /* Create connection to the handle named pipe handler to break the loop */ + if ((temp = CreateFile(szPipeName, + GENERIC_READ | GENERIC_WRITE, + 0, + NULL, + OPEN_EXISTING, + 0, + NULL )) != INVALID_HANDLE_VALUE) + { + WaitNamedPipe(szPipeName, 1000); + DWORD dwMode = PIPE_READMODE_BYTE | PIPE_WAIT; + SetNamedPipeHandleState(temp, &dwMode, NULL, NULL); + CancelIo(temp); + DisconnectNamedPipe(temp); + CloseHandle(temp); + } } - } #endif #ifdef HAVE_SYS_UN_H if (unix_sock != INVALID_SOCKET) @@ -553,7 +588,7 @@ if (hPipe != INVALID_HANDLE_VALUE && opt_enable_named_pipe) /* 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")); @@ -594,59 +629,68 @@ if (hPipe != INVALID_HANDLE_VALUE && opt_enable_named_pipe) DBUG_VOID_RETURN; } -#ifdef HAVE_CLOSE_SERVER_SOCK -void close_server_sock() +static void close_server_sock() { +#ifdef HAVE_CLOSE_SERVER_SOCK DBUG_ENTER("close_server_sock"); - if (ip_sock != INVALID_SOCKET) + my_socket tmp_sock; + tmp_sock=ip_sock; + if (tmp_sock != INVALID_SOCKET) { - DBUG_PRINT("info",("closing TCP/IP socket")); - VOID(shutdown(ip_sock,2)); - VOID(closesocket(ip_sock)); ip_sock=INVALID_SOCKET; + DBUG_PRINT("info",("closing TCP/IP socket")); + VOID(shutdown(tmp_sock,2)); + VOID(closesocket(tmp_sock)); } - if (unix_sock != INVALID_SOCKET) + tmp_sock=unix_sock; + if (tmp_sock != INVALID_SOCKET) { + unix_sock=INVALID_SOCKET; DBUG_PRINT("info",("closing Unix socket")); - VOID(shutdown(unix_sock,2)); - VOID(closesocket(unix_sock)); + VOID(shutdown(tmp_sock,2)); + VOID(closesocket(tmp_sock)); VOID(unlink(mysql_unix_port)); - unix_sock=INVALID_SOCKET; } DBUG_VOID_RETURN; -} #endif +} void kill_mysql(void) { DBUG_ENTER("kill_mysql"); + #ifdef SIGNALS_DONT_BREAK_READ close_server_sock(); /* force accept to wake up */ -#endif +#endif + #if defined(__WIN__) +#if !defined(EMBEDDED_LIBRARY) { if (!SetEvent(hEventShutdown)) { DBUG_PRINT("error",("Got error: %ld from SetEvent",GetLastError())); } - // or: - // HANDLE hEvent=OpenEvent(0, FALSE, "MySqlShutdown"); - // SetEvent(hEventShutdown); - // CloseHandle(hEvent); + /* + or: + HANDLE hEvent=OpenEvent(0, FALSE, "MySqlShutdown"); + SetEvent(hEventShutdown); + CloseHandle(hEvent); + */ } +#endif #elif defined(OS2) pthread_cond_signal( &eventShutdown); // post semaphore #elif defined(HAVE_PTHREAD_KILL) - if (pthread_kill(signal_thread,MYSQL_KILL_SIGNAL))// End everything nicely + if (pthread_kill(signal_thread,SIGTERM)) /* End everything nicely */ { DBUG_PRINT("error",("Got error %d from pthread_kill",errno)); /* purecov: inspected */ } #elif !defined(SIGNALS_DONT_BREAK_READ) - kill(current_pid,MYSQL_KILL_SIGNAL); + kill(current_pid,SIGTERM); #endif DBUG_PRINT("quit",("After pthread_kill")); shutdown_in_progress=1; // Safety if kill didn't work -#ifdef SIGNALS_DONT_BREAK_READ +#ifdef SIGNALS_DONT_BREAK_READ if (!abort_loop) { pthread_t tmp; @@ -676,8 +720,7 @@ static void __cdecl kill_server(int sig_ptr) int sig=(int) (long) sig_ptr; // This is passed a int DBUG_ENTER("kill_server"); - // if there is a signal during the kill in progress, we do not need - // another one + // if there is a signal during the kill in progress, ignore the other if (kill_in_progress) // Safety RETURN_FROM_KILL_SERVER; kill_in_progress=TRUE; @@ -689,6 +732,7 @@ static void __cdecl kill_server(int sig_ptr) sql_print_error(ER(ER_GOT_SIGNAL),my_progname,sig); /* purecov: inspected */ #if defined(USE_ONE_SIGNAL_HAND) && !defined(__WIN__) && !defined(OS2) + SHUTDOWN_THD; my_thread_init(); // If this is a new thread #endif close_connections(); @@ -702,8 +746,9 @@ static void __cdecl kill_server(int sig_ptr) #ifdef USE_ONE_SIGNAL_HAND -pthread_handler_decl(kill_server_thread,arg __attribute__((unused))) +static pthread_handler_decl(kill_server_thread,arg __attribute__((unused))) { + SHUTDOWN_THD; my_thread_init(); // Initialize new thread kill_server(0); my_thread_end(); // Normally never reached @@ -711,6 +756,11 @@ pthread_handler_decl(kill_server_thread,arg __attribute__((unused))) } #endif +#if defined(__amiga__) +#undef sigset +#define sigset signal +#endif + static sig_handler print_signal_warning(int sig) { if (opt_warnings) @@ -729,15 +779,19 @@ static sig_handler print_signal_warning(int sig) void unireg_end(int signal_number __attribute__((unused))) { clean_up(); + my_thread_end(); pthread_exit(0); // Exit is in main thread } void unireg_abort(int exit_code) { + DBUG_ENTER("unireg_abort"); if (exit_code) sql_print_error("Aborting\n"); clean_up(); /* purecov: inspected */ + DBUG_PRINT("quit",("done with cleanup in unireg_abort")); + my_thread_end(); exit(exit_code); /* purecov: inspected */ } @@ -751,7 +805,7 @@ void clean_up(bool print_message) bitmap_free(&slave_error_mask); acl_free(1); grant_free(); - sql_cache_free(); + query_cache_destroy(); table_cache_free(); hostname_cache_free(); item_user_lock_free(); @@ -765,27 +819,42 @@ void clean_up(bool print_message) #ifdef USE_RAID end_raid(); #endif +#ifdef HAVE_OPENSSL + my_free(opt_ssl_key,MYF(MY_ALLOW_ZERO_PTR)); + my_free(opt_ssl_cert,MYF(MY_ALLOW_ZERO_PTR)); + my_free(opt_ssl_ca,MYF(MY_ALLOW_ZERO_PTR)); + my_free(opt_ssl_capath,MYF(MY_ALLOW_ZERO_PTR)); + my_free(opt_ssl_cipher,MYF(MY_ALLOW_ZERO_PTR)); + my_free((gptr) ssl_acceptor_fd, MYF(MY_ALLOW_ZERO_PTR)); + opt_ssl_key=opt_ssl_cert=opt_ssl_ca=opt_ssl_capath=0; +#endif /* HAVE_OPENSSL */ + free_defaults(defaults_argv); my_free(charsets_list, MYF(MY_ALLOW_ZERO_PTR)); - my_free(mysql_tmpdir,MYF(0)); + my_free(allocated_mysql_tmpdir,MYF(MY_ALLOW_ZERO_PTR)); + my_free(slave_load_tmpdir,MYF(MY_ALLOW_ZERO_PTR)); x_free(opt_bin_logname); + x_free(opt_relay_logname); bitmap_free(&temp_pool); free_max_user_conn(); -#ifndef __WIN__ + end_slave_list(); + +#if !defined(__WIN__) && !defined(EMBEDDED_LIBRARY) if (!opt_bootstrap) (void) my_delete(pidfile_name,MYF(0)); // This may not always exist #endif - if (print_message) + if (print_message && errmesg) sql_print_error(ER(ER_SHUTDOWN_COMPLETE),my_progname); x_free((gptr) my_errmsg[ERRMAPP]); /* Free messages */ - my_thread_end(); - + DBUG_PRINT("quit", ("Error messages freed")); /* Tell main we are ready */ (void) pthread_mutex_lock(&LOCK_thread_count); + DBUG_PRINT("quit", ("got thread count lock")); ready_to_exit=1; /* do the broadcast inside the lock to ensure that my_end() is not called */ (void) pthread_cond_broadcast(&COND_thread_count); (void) pthread_mutex_unlock(&LOCK_thread_count); + DBUG_PRINT("quit", ("done with cleanup")); } /* clean_up */ @@ -845,20 +914,33 @@ static void set_user(const char *user) if (!strcmp(user,"root")) return; // Avoid problem with dynamic libraries + uid_t uid; if (!(ent = getpwnam(user))) { - fprintf(stderr,"Fatal error: Can't change to run as user '%s' ; Please check that the user exists!\n",user); - unireg_abort(1); + // allow a numeric uid to be used + const char *pos; + for (pos=user; isdigit(*pos); pos++) ; + if (*pos) // Not numeric id + { + fprintf(stderr,"Fatal error: Can't change to run as user '%s' ; Please check that the user exists!\n",user); + unireg_abort(1); + } + uid=atoi(user); // Use numberic uid } + else + { #ifdef HAVE_INITGROUPS - initgroups((char*) user,ent->pw_gid); + initgroups((char*) user,ent->pw_gid); #endif - if (setgid(ent->pw_gid) == -1) - { - sql_perror("setgid"); - unireg_abort(1); + if (setgid(ent->pw_gid) == -1) + { + sql_perror("setgid"); + unireg_abort(1); + } + uid=ent->pw_uid; } - if (setuid(ent->pw_uid) == -1) + + if (setuid(uid) == -1) { sql_perror("setuid"); unireg_abort(1); @@ -928,15 +1010,14 @@ static void server_init(void) if (listen(ip_sock,(int) back_log) < 0) { sql_perror("Can't start server: listen() on TCP/IP port"); - sql_print_error("Warning: listen() on TCP/IP failed with error %d", + sql_print_error("Error: listen() on TCP/IP failed with error %d", socket_errno); unireg_abort(1); } } if (mysqld_chroot) set_root(mysqld_chroot); - - set_user(mysqld_user); // set_user now takes care of mysqld_user==NULL + set_user(mysqld_user); // Works also with mysqld_user==NULL #ifdef __NT__ /* create named pipe */ @@ -1039,7 +1120,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)", @@ -1104,6 +1185,8 @@ void end_thread(THD *thd, bool put_in_cache) DBUG_PRINT("info", ("sending a broadcast")) /* Tell main we are ready */ + // TODO: explain why we broadcast outside of the lock or + // fix the bug - Sasha (void) pthread_mutex_unlock(&LOCK_thread_count); (void) pthread_cond_broadcast(&COND_thread_count); DBUG_PRINT("info", ("unlocked thread_count mutex")) @@ -1117,20 +1200,6 @@ void end_thread(THD *thd, bool put_in_cache) DBUG_VOID_RETURN; } -#ifdef SIGNALS_DONT_BREAK_READ -inline void kill_broken_server() -{ - /* hack to get around signals ignored in syscalls for problem OS's */ - if (unix_sock == INVALID_SOCKET || ip_sock ==INVALID_SOCKET) - { - select_thread_in_use = 0; - kill_server((void*)MYSQL_KILL_SIGNAL); /* never returns */ - } -} -#define MAYBE_BROKEN_SYSCALL kill_broken_server(); -#else -#define MAYBE_BROKEN_SYSCALL -#endif /* Start a cached thread. LOCK_thread_count is locked on entry */ @@ -1198,19 +1267,6 @@ static void start_signal_handler(void) } #elif defined(__EMX__) -static void init_signals(void) -{ - signal(SIGQUIT, sig_kill); - signal(SIGKILL, sig_kill); - signal(SIGTERM, sig_kill); - signal(SIGINT, sig_kill); - signal(SIGHUP, sig_reload); // Flush everything - signal(SIGALRM, SIG_IGN); - signal(SIGBREAK,SIG_IGN); - signal_thread = pthread_self(); -} - - static void sig_reload(int signo) { reload_acl_and_cache((THD*) 0,REFRESH_LOG, (TABLE_LIST*) 0); // Flush everything @@ -1227,10 +1283,23 @@ static void sig_kill(int signo) signal(signo, SIG_ACK); } +static void init_signals(void) +{ + signal(SIGQUIT, sig_kill); + signal(SIGKILL, sig_kill); + signal(SIGTERM, sig_kill); + signal(SIGINT, sig_kill); + signal(SIGHUP, sig_reload); // Flush everything + signal(SIGALRM, SIG_IGN); + signal(SIGBREAK,SIG_IGN); + signal_thread = pthread_self(); + SIGNAL_THD; +} static void start_signal_handler(void) { } + #else /* if ! __WIN__ && ! __EMX__ */ #ifdef HAVE_LINUXTHREADS @@ -1241,10 +1310,10 @@ static sig_handler handle_segfault(int sig) { THD *thd=current_thd; /* - Strictly speaking, we should need a mutex here + Strictly speaking, one needs a mutex here but since we have got SIGSEGV already, things are a mess so not having the mutex is not as bad as possibly using a buggy - mutex - so we keep things simple. + mutex - so we keep things simple */ if (segfaulted) { @@ -1256,13 +1325,13 @@ static sig_handler handle_segfault(int sig) fprintf(stderr,"\ mysqld got signal %d;\n\ This could be because you hit a bug. It is also possible that this binary\n\ -or one of the libraries it was linked agaist is corrupt, improperly built,\n\ +or one of the libraries it was linked against is corrupt, improperly built,\n\ or misconfigured. This error can also be caused by malfunctioning hardware.\n", sig); fprintf(stderr, "\ We will try our best to scrape up some info that will hopefully help diagnose\n\ the problem, but since we have already crashed, something is definitely wrong\n\ -and this may fail\n\n"); +and this may fail.\n\n"); fprintf(stderr, "key_buffer_size=%ld\n", keybuff_size); fprintf(stderr, "record_buffer=%ld\n", my_default_record_cache_size); fprintf(stderr, "sort_buffer=%ld\n", sortbuff_size); @@ -1273,15 +1342,15 @@ and this may fail\n\n"); key_buffer_size + (record_buffer + sort_buffer)*max_connections = %ld K\n\ bytes of memory\n", (keybuff_size + (my_default_record_cache_size + sortbuff_size) * max_connections)/ 1024); - fprintf(stderr, "Hope that's ok, if not, decrease some variables in the equation\n\n"); + fprintf(stderr, "Hope that's ok; if not, decrease some variables in the equation.\n\n"); #if defined(HAVE_LINUXTHREADS) if (sizeof(char*) == 4 && thread_count > UNSAFE_DEFAULT_LINUX_THREADS) { fprintf(stderr, "\ You seem to be running 32-bit Linux and have %d concurrent connections.\n\ -If you have not changed STACK_SIZE in LinuxThreads and build the binary \n\ -yourself, LinuxThreads is quite likely to steal a part of global heap for\n\ +If you have not changed STACK_SIZE in LinuxThreads and built the binary \n\ +yourself, LinuxThreads is quite likely to steal a part of the global heap for\n\ the thread stack. Please read http://www.mysql.com/doc/L/i/Linux.html\n\n", thread_count); } @@ -1290,9 +1359,7 @@ the thread stack. Please read http://www.mysql.com/doc/L/i/Linux.html\n\n", #ifdef HAVE_STACKTRACE if(!(test_flags & TEST_NO_STACKTRACE)) { -#ifdef HAVE_GEMINI_DB - utrace(); -#endif + fprintf(stderr,"thd=%p\n",thd); print_stacktrace(thd ? (gptr) thd->thread_stack : (gptr) 0, thread_stack); } @@ -1305,12 +1372,12 @@ Some pointers may be invalid and cause the dump to abort...\n"); fprintf(stderr, "\n\ Successfully dumped variables, if you ran with --log, take a look at the\n\ details of what thread %ld did to cause the crash. In some cases of really\n\ -bad corruption, the values shown above may be invalid\n\n", +bad corruption, the values shown above may be invalid.\n\n", thd->thread_id); } fprintf(stderr, "\ The manual page at http://www.mysql.com/doc/C/r/Crashing.html contains\n\ -information that should help you find out what is causing the crash\n"); +information that should help you find out what is causing the crash.\n"); fflush(stderr); #endif /* HAVE_STACKTRACE */ @@ -1342,12 +1409,17 @@ static void init_signals(void) sigprocmask(SIG_SETMASK,&sa.sa_mask,NULL); init_stacktrace(); +#if defined(__amiga__) + sa.sa_handler=(void(*)())handle_segfault; +#else sa.sa_handler=handle_segfault; +#endif sigaction(SIGSEGV, &sa, NULL); #ifdef SIGBUS sigaction(SIGBUS, &sa, NULL); #endif sigaction(SIGILL, &sa, NULL); + sigaction(SIGFPE, &sa, NULL); } (void) sigemptyset(&set); #ifdef THREAD_SPECIFIC_SIGPIPE @@ -1361,8 +1433,8 @@ static void init_signals(void) sigaddset(&set,SIGQUIT); sigaddset(&set,SIGTERM); sigaddset(&set,SIGHUP); - sigset(SIGTERM,print_signal_warning); // If it's blocked by parent - signal(SIGHUP,print_signal_warning); // If it's blocked by parent + signal(SIGTERM,SIG_DFL); // If it's blocked by parent + signal(SIGHUP,SIG_DFL); // If it's blocked by parent #ifdef SIGTSTP sigaddset(&set,SIGTSTP); #endif @@ -1415,7 +1487,7 @@ static void *signal_hand(void *arg __attribute__((unused))) int sig; my_thread_init(); // Init new thread DBUG_ENTER("signal_hand"); - + SIGNAL_THD; /* Setup alarm handler */ init_thr_alarm(max_connections+max_insert_delayed_threads); #if SIGINT != THR_KILL_SIGNAL @@ -1446,6 +1518,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); @@ -1457,13 +1536,16 @@ static void *signal_hand(void *arg __attribute__((unused))) int error; // Used when debugging if (shutdown_in_progress && !abort_loop) { - sig= MYSQL_KILL_SIGNAL; + sig=SIGTERM; error=0; } else while ((error=my_sigwait(&set,&sig)) == EINTR) ; if (cleanup_done) + { + my_thread_end(); pthread_exit(0); // Safety + } switch (sig) { case SIGTERM: case SIGQUIT: @@ -1480,15 +1562,18 @@ 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 + kill_server((void*) sig); // MIT THREAD has a alarm thread #endif } break; case SIGHUP: - reload_acl_and_cache((THD*) 0,REFRESH_LOG, + reload_acl_and_cache((THD*) 0, + (REFRESH_LOG | REFRESH_TABLES | REFRESH_FAST | + REFRESH_STATUS | REFRESH_GRANT | REFRESH_THREADS | + REFRESH_HOSTS), (TABLE_LIST*) 0); // Flush logs mysql_print_status((THD*) 0); // Send debug some info break; @@ -1554,12 +1639,14 @@ int uname(struct utsname *a) pthread_handler_decl(handle_shutdown,arg) { MSG msg; + SHUTDOWN_THD; my_thread_init(); /* this call should create the message queue for this thread */ PeekMessage(&msg, NULL, 1, 65534,PM_NOREMOVE); - +#if !defined(EMBEDDED_LIBRARY) if (WaitForSingleObject(hEventShutdown,INFINITE)==WAIT_OBJECT_0) +#endif kill_server(MYSQL_KILL_SIGNAL); return 0; } @@ -1579,6 +1666,7 @@ int __stdcall handle_kill(ulong ctrl_type) #ifdef OS2 pthread_handler_decl(handle_shutdown,arg) { + SHUTDOWN_THD; my_thread_init(); // wait semaphore @@ -1587,8 +1675,10 @@ pthread_handler_decl(handle_shutdown,arg) // close semaphore and kill server pthread_cond_destroy( &eventShutdown); - // exit main loop on main thread, so kill will be done from - // main thread (this is thread 2) + /* + Exit main loop on main thread, so kill will be done from + main thread (this is thread 2) + */ abort_loop = 1; // unblock select() @@ -1605,15 +1695,17 @@ const char *load_default_groups[]= { "mysqld","server",0 }; char *libwrapName=NULL; #endif -static void open_log(MYSQL_LOG *log, const char *hostname, +void open_log(MYSQL_LOG *log, const char *hostname, const char *opt_name, const char *extension, - enum_log_type type) + enum_log_type type, bool read_append, + bool no_auto_events) { char tmp[FN_REFLEN]; if (!opt_name || !opt_name[0]) { - /* TODO: The following should be using fn_format(); We just need to - first change fn_format() to cut the file name if it's too long. + /* + TODO: The following should be using fn_format(); We just need to + first change fn_format() to cut the file name if it's too long. */ strmake(tmp,hostname,FN_REFLEN-5); strmov(strcend(tmp,'.'),extension); @@ -1630,7 +1722,8 @@ static void open_log(MYSQL_LOG *log, const char *hostname, opt_name=tmp; } } - log->open(opt_name,type); + log->open(opt_name,type,0,(read_append) ? SEQ_READ_APPEND : WRITE_CACHE, + no_auto_events); } @@ -1645,6 +1738,13 @@ int main(int argc, char **argv) my_umask=0660; // Default umask for new files my_umask_dir=0700; // Default umask for new directories + MAIN_THD; + /* initialize signal_th and shutdown_th to main_th for default value + as we need to initialize them to something safe. They are used + when compiled with safemalloc + */ + SIGNAL_THD; + SHUTDOWN_THD; MY_INIT(argv[0]); // init my_sys library & pthreads tzset(); // Set tzname @@ -1726,33 +1826,35 @@ int main(int argc, char **argv) (void) pthread_mutex_init(&LOCK_bytes_sent,MY_MUTEX_INIT_FAST); (void) pthread_mutex_init(&LOCK_bytes_received,MY_MUTEX_INIT_FAST); (void) pthread_mutex_init(&LOCK_timezone,MY_MUTEX_INIT_FAST); - (void) pthread_mutex_init(&LOCK_binlog_update, MY_MUTEX_INIT_FAST); // QQ NOT USED - (void) pthread_mutex_init(&LOCK_slave, MY_MUTEX_INIT_FAST); (void) pthread_mutex_init(&LOCK_server_id, MY_MUTEX_INIT_FAST); (void) pthread_mutex_init(&LOCK_user_conn, MY_MUTEX_INIT_FAST); + (void) pthread_mutex_init(&LOCK_rpl_status, MY_MUTEX_INIT_FAST); + (void) pthread_mutex_init(&LOCK_active_mi, MY_MUTEX_INIT_FAST); (void) pthread_cond_init(&COND_thread_count,NULL); (void) pthread_cond_init(&COND_refresh,NULL); (void) pthread_cond_init(&COND_thread_cache,NULL); (void) pthread_cond_init(&COND_flush_thread_cache,NULL); (void) pthread_cond_init(&COND_manager,NULL); - (void) pthread_cond_init(&COND_binlog_update, NULL); - (void) pthread_cond_init(&COND_slave_stopped, NULL); - (void) pthread_cond_init(&COND_slave_start, NULL); + (void) pthread_cond_init(&COND_rpl_status, NULL); init_signals(); if (set_default_charset_by_name(default_charset, MYF(MY_WME))) - unireg_abort(1); + exit( 1 ); charsets_list = list_charsets(MYF(MY_COMPILED_SETS|MY_CONFIG_SETS)); #ifdef HAVE_OPENSSL if (opt_use_ssl) { - ssl_acceptor_fd = VioSSLAcceptorFd_new(opt_ssl_key, opt_ssl_cert, - opt_ssl_ca, opt_ssl_capath); + ssl_acceptor_fd = new_VioSSLAcceptorFd(opt_ssl_key, opt_ssl_cert, + opt_ssl_ca, opt_ssl_capath, + opt_ssl_cipher); + 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 */ + opt_use_ssl = 0; + /* having ssl_acceptor_fd != 0 signals the use of SSL */ } + if (des_key_file) + load_des_key_file(des_key_file); #endif /* HAVE_OPENSSL */ #ifdef HAVE_LIBWRAP @@ -1773,7 +1875,7 @@ int main(int argc, char **argv) pthread_attr_setscope(&connection_attrib, PTHREAD_SCOPE_SYSTEM); #if defined( SET_RLIMIT_NOFILE) || defined( OS2) - /* connections and databases neads lots of files */ + /* connections and databases needs lots of files */ { uint wanted_files=10+(uint) max(max_connections*5, max_connections+table_cache_size*2); @@ -1817,14 +1919,12 @@ int main(int argc, char **argv) server_init(); table_cache_init(); hostname_cache_init(); - sql_cache_init(); + query_cache_result_size_limit(query_cache_limit); + query_cache_resize(query_cache_size); randominit(&sql_rand,(ulong) start_time,(ulong) start_time/2); reset_floating_point_exceptions(); init_thr_lock(); - - /* 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); + init_slave_list(); /* Setup log files */ if (opt_log) @@ -1835,56 +1935,7 @@ int main(int argc, char **argv) LOG_NEW); using_update_log=1; } - - //make sure slave thread gets started - // if server_id is set, valid master.info is present, and master_host has - // not been specified - if(server_id && !master_host) - { - char fname[FN_REFLEN+128]; - MY_STAT stat_area; - fn_format(fname, master_info_file, mysql_data_home, "", 4+16+32); - if(my_stat(fname, &stat_area, MYF(0)) && !init_master_info(&glob_mi)) - master_host = glob_mi.host; - } - - if (opt_bin_log && !server_id) - { - server_id= !master_host ? 1 : 2; - switch (server_id) { -#ifdef EXTRA_DEBUG - case 1: - sql_print_error("\ -Warning: You have enabled the binary log, but you haven't set server-id:\n\ -Updates will be logged to the binary log, but connections to slaves will\n\ -not be accepted."); - break; -#endif - case 2: - sql_print_error("\ -Warning: You should set server-id to a non-0 value if master_host is set.\n\ -The server will not act as a slave."); - break; - } - } - if (opt_bin_log) - { - if (!opt_bin_logname) - { - char tmp[FN_REFLEN]; - /* TODO: The following should be using fn_format(); We just need to - first change fn_format() to cut the file name if it's too long. - */ - strmake(tmp,glob_hostname,FN_REFLEN-5); - strmov(strcend(tmp,'.'),"-bin"); - opt_bin_logname=my_strdup(tmp,MYF(MY_WME)); - } - mysql_bin_log.set_index_file_name(opt_binlog_index_name); - open_log(&mysql_bin_log, glob_hostname, opt_bin_logname, "-bin", - LOG_BIN); - using_update_log=1; - } - + if (opt_slow_log) open_log(&mysql_slow_log, glob_hostname, opt_slow_logname, "-slow.log", LOG_NORMAL); @@ -1914,15 +1965,15 @@ The server will not act as a slave."); } #else locked_in_memory=0; -#endif +#endif if (opt_myisam_log) (void) mi_log( 1 ); - ft_init_stopwords(ft_precompiled_stopwords); /* SerG */ + ft_init_stopwords(ft_precompiled_stopwords); #ifdef __WIN__ if (!opt_console) - FreeConsole(); // Remove window + FreeConsole(); // Remove window #endif /* @@ -1943,19 +1994,58 @@ The server will not act as a slave."); (void) pthread_kill(signal_thread,MYSQL_KILL_SIGNAL); #ifndef __WIN__ if (!opt_bootstrap) - (void) my_delete(pidfile_name,MYF(MY_WME)); // Not neaded anymore + (void) my_delete(pidfile_name,MYF(MY_WME)); // Not needed anymore #endif exit(1); } if (!opt_noacl) (void) grant_init(); - if (max_user_connections) - init_max_user_conn(); + init_max_user_conn(); #ifdef HAVE_DLOPEN if (!opt_noacl) udf_init(); #endif + /* init_slave() must be called after the thread keys are created */ + init_slave(); + + if (opt_bin_log && !server_id) + { + server_id= !master_host ? 1 : 2; + switch (server_id) { +#ifdef EXTRA_DEBUG + case 1: + sql_print_error("\ +Warning: You have enabled the binary log, but you haven't set server-id:\n\ +Updates will be logged to the binary log, but connections to slaves will\n\ +not be accepted."); + break; +#endif + case 2: + sql_print_error("\ +Warning: You should set server-id to a non-0 value if master_host is set.\n\ +The server will not act as a slave."); + break; + } + } + if (opt_bin_log) + { + if (!opt_bin_logname) + { + char tmp[FN_REFLEN]; + /* TODO: The following should be using fn_format(); We just need to + first change fn_format() to cut the file name if it's too long. + */ + strmake(tmp,glob_hostname,FN_REFLEN-5); + strmov(strcend(tmp,'.'),"-bin"); + opt_bin_logname=my_strdup(tmp,MYF(MY_WME)); + } + mysql_bin_log.set_index_file_name(opt_binlog_index_name); + open_log(&mysql_bin_log, glob_hostname, opt_bin_logname, "-bin", + LOG_BIN); + using_update_log=1; + } + if (opt_bootstrap) { @@ -1972,9 +2062,9 @@ The server will not act as a slave."); } } (void) thr_setconcurrency(concurrency); // 10 by default -#ifdef __WIN__ //IRENA +#if defined(__WIN__) && !defined(EMBEDDED_LIBRARY) //IRENA { - hEventShutdown=CreateEvent(0, FALSE, FALSE, "MySqlShutdown"); + hEventShutdown=CreateEvent(0, FALSE, FALSE, event_name); pthread_t hThread; if (pthread_create(&hThread,&connection_attrib,handle_shutdown,0)) sql_print_error("Warning: Can't create thread to handle shutdown requests"); @@ -2003,17 +2093,6 @@ The server will not act as a slave."); sql_print_error("Warning: Can't create thread to manage maintenance"); } - // slave thread - if (master_host) - { - pthread_t hThread; - if (!opt_skip_slave_start && - pthread_create(&hThread, &connection_attrib, handle_slave, 0)) - sql_print_error("Warning: Can't create thread to handle slave"); - else if(opt_skip_slave_start) - init_master_info(&glob_mi); - } - printf(ER(ER_READY),my_progname,server_version,""); fflush(stdout); @@ -2060,7 +2139,7 @@ The server will not act as a slave."); } #else handle_connections_sockets(0); -#ifdef EXTRA_DEBUG +#ifdef EXTRA_DEBUG2 sql_print_error("Exiting main thread"); #endif #endif /* __NT__ */ @@ -2070,17 +2149,18 @@ The server will not act as a slave."); DBUG_PRINT("quit",("Exiting main thread")); #ifndef __WIN__ -#ifdef EXTRA_DEBUG +#ifdef EXTRA_DEBUG2 sql_print_error("Before Lock_thread_count"); #endif (void) pthread_mutex_lock(&LOCK_thread_count); + DBUG_PRINT("quit", ("Got thread_count mutex")); select_thread_in_use=0; // For close_connections (void) pthread_cond_broadcast(&COND_thread_count); (void) pthread_mutex_unlock(&LOCK_thread_count); -#ifdef EXTRA_DEBUG +#ifdef EXTRA_DEBUG2 sql_print_error("After lock_thread_count"); #endif -#endif +#endif /* __WIN__ */ /* Wait until cleanup is done */ (void) pthread_mutex_lock(&LOCK_thread_count); @@ -2089,22 +2169,16 @@ The server will not act as a slave."); pthread_cond_wait(&COND_thread_count,&LOCK_thread_count); } (void) pthread_mutex_unlock(&LOCK_thread_count); -#ifdef __WIN__ - if (Service.IsNT()) - { - if(start_mode) - Service.Stop(); - else - { - Service.SetShutdownEvent(0); - if(hEventShutdown) CloseHandle(hEventShutdown); - } - } - else - { + +#if defined(__WIN__) && !defined(EMBEDDED_LIBRARY) + if (Service.IsNT() && start_mode) + Service.Stop(); + else + { Service.SetShutdownEvent(0); - if(hEventShutdown) CloseHandle(hEventShutdown); - } + if (hEventShutdown) + CloseHandle(hEventShutdown); + } #endif my_end(opt_endinfo ? MY_CHECK_ERROR | MY_GIVE_INFO : 0); exit(0); @@ -2112,7 +2186,7 @@ The server will not act as a slave."); } -#ifdef __WIN__ +#if defined(__WIN__) && !defined(EMBEDDED_LIBRARY) /* ------------------------------------------------------------------------ main and thread entry function for Win32 (all this is needed only to run mysqld as a service on WinNT) @@ -2125,44 +2199,70 @@ int mysql_service(void *p) int main(int argc, char **argv) { - // check environment variable OS - if (Service.GetOS()) // "OS" defined; Should be NT + if (Service.GetOS()) /* true NT family */ { + char file_path[FN_REFLEN]; + my_path(file_path, argv[0], ""); /* Find name in path */ + fn_format(file_path,argv[0],file_path,"",1+4+16); /* Force use of full path */ + if (argc == 2) + { + if (Service.got_service_option(argv, "install")) + { + Service.Install(1, MYSQL_SERVICENAME, MYSQL_SERVICENAME, file_path); + return 0; + } + else if (Service.got_service_option(argv, "install-manual")) + { + Service.Install(0, MYSQL_SERVICENAME, MYSQL_SERVICENAME, file_path); + return 0; + } + else if (Service.got_service_option(argv, "remove")) + { + Service.Remove(MYSQL_SERVICENAME); + return 0; + } + else if (Service.IsService(argv[1])) + { + /* start an optional service */ + load_default_groups[0]= argv[1]; + event_name= argv[1]; + start_mode= 1; + Service.Init(event_name, mysql_service ); + return 0; + } + } + else if (argc == 3) /* install or remove any optional service */ { - char path[FN_REFLEN]; - my_path(path, argv[0], ""); // Find name in path - fn_format(path,argv[0],path,"",1+4+16); // Force use of full path - - if (!strcmp(argv[1],"-install") || !strcmp(argv[1],"--install")) + uint length=strlen(file_path); + file_path[sizeof(file_path)-1]=0; + strxnmov(file_path + length, sizeof(file_path)-2, " ", argv[2], NullS); + if (Service.got_service_option(argv, "install")) { - Service.Install(1,MYSQL_SERVICENAME,MYSQL_SERVICENAME,path); - return 0; + Service.Install(1, argv[2], argv[2], file_path); + return 0; } - else if (!strcmp(argv[1],"-install-manual") || !strcmp(argv[1],"--install-manual")) + else if (Service.got_service_option(argv, "install-manual")) { - Service.Install(0,MYSQL_SERVICENAME,MYSQL_SERVICENAME,path); - return 0; + Service.Install(0, argv[2], argv[2], file_path); + return 0; } - else if (!strcmp(argv[1],"-remove") || !strcmp(argv[1],"--remove")) + else if (Service.got_service_option(argv, "remove")) { - Service.Remove(MYSQL_SERVICENAME); - return 0; + Service.Remove(argv[2]); + return 0; } } - else if (argc == 1) // No arguments; start as a service + else if (argc == 1 && Service.IsService(MYSQL_SERVICENAME)) { - // init service - start_mode = 1; - long tmp=Service.Init(MYSQL_SERVICENAME,mysql_service); + /* start the default service */ + start_mode= 1; + event_name= "MySqlShutdown"; + Service.Init(MYSQL_SERVICENAME, mysql_service); return 0; } } - - // This is a WIN95 machine or a start of mysqld as a standalone program - // we have to pass the arguments, in case of NT-service this will be done - // by ServiceMain() - + /* Start as standalone server */ Service.my_argc=argc; Service.my_argv=argv; mysql_service(NULL); @@ -2178,7 +2278,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++; @@ -2303,6 +2403,20 @@ static void create_new_thread(THD *thd) DBUG_VOID_RETURN; } +#ifdef SIGNALS_DONT_BREAK_READ +inline void kill_broken_server() +{ + /* hack to get around signals ignored in syscalls for problem OS's */ + if (unix_sock == INVALID_SOCKET || ip_sock ==INVALID_SOCKET) + { + select_thread_in_use = 0; + kill_server((void*)MYSQL_KILL_SIGNAL); /* never returns */ + } +} +#define MAYBE_BROKEN_SYSCALL kill_broken_server(); +#else +#define MAYBE_BROKEN_SYSCALL +#endif /* Handle new connections and spawn new process to handle them */ @@ -2315,7 +2429,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); @@ -2353,13 +2467,15 @@ pthread_handler_decl(handle_connections_sockets,arg __attribute__((unused))) if (!select_errors++ && !abort_loop) /* purecov: inspected */ sql_print_error("mysqld: Got error %d from select",socket_errno); /* purecov: inspected */ } - MAYBE_BROKEN_SYSCALL; + MAYBE_BROKEN_SYSCALL continue; } #endif /* HPUX */ if (abort_loop) + { + MAYBE_BROKEN_SYSCALL; break; - + } /* ** Is this a new connection request */ @@ -2472,7 +2588,8 @@ pthread_handler_decl(handle_connections_sockets,arg __attribute__((unused))) if (!(thd= new THD)) { - (void) shutdown(new_sock,2); VOID(closesocket(new_sock)); + (void) shutdown(new_sock,2); + VOID(closesocket(new_sock)); continue; } if (!(vio_tmp=vio_new(new_sock, @@ -2615,7 +2732,7 @@ enum options { OPT_BOOTSTRAP, OPT_SKIP_SHOW_DB, OPT_TABLE_TYPE, OPT_INIT_FILE, OPT_DELAY_KEY_WRITE, OPT_SLOW_QUERY_LOG, - OPT_SKIP_DELAY_KEY_WRITE, OPT_CHARSETS_DIR, + OPT_USE_DELAY_KEY_WRITE, OPT_CHARSETS_DIR, OPT_BDB_HOME, OPT_BDB_LOG, OPT_BDB_TMP, OPT_BDB_NOSYNC, OPT_BDB_LOCK, OPT_BDB_SKIP, @@ -2624,10 +2741,12 @@ enum options { OPT_MASTER_PASSWORD, OPT_MASTER_PORT, OPT_MASTER_INFO_FILE, OPT_MASTER_CONNECT_RETRY, OPT_MASTER_RETRY_COUNT, + OPT_MASTER_SSL, OPT_MASTER_SSL_KEY, + OPT_MASTER_SSL_CERT, OPT_SQL_BIN_UPDATE_SAME, OPT_REPLICATE_DO_DB, OPT_REPLICATE_IGNORE_DB, OPT_LOG_SLAVE_UPDATES, OPT_BINLOG_DO_DB, OPT_BINLOG_IGNORE_DB, - OPT_WANT_CORE, OPT_SKIP_CONCURRENT_INSERT, + OPT_WANT_CORE, OPT_CONCURRENT_INSERT, OPT_MEMLOCK, OPT_MYISAM_RECOVER, OPT_REPLICATE_REWRITE_DB, OPT_SERVER_ID, OPT_SKIP_SLAVE_START, OPT_SKIP_INNOBASE, @@ -2642,326 +2761,784 @@ enum options { OPT_INNODB_LOG_ARCH_DIR, OPT_INNODB_LOG_ARCHIVE, OPT_INNODB_FLUSH_LOG_AT_TRX_COMMIT, + OPT_INNODB_FLUSH_METHOD, OPT_INNODB_FAST_SHUTDOWN, - OPT_INNODB_UNIX_FILE_FLUSH_METHOD, OPT_SAFE_SHOW_DB, - OPT_GEMINI_SKIP, OPT_INNODB_SKIP, + OPT_INNODB_SKIP, OPT_SKIP_SAFEMALLOC, OPT_TEMP_POOL, 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_MAX_BINLOG_DUMP_EVENTS, OPT_SPORADIC_BINLOG_DUMP_FAIL, OPT_SAFE_USER_CREATE, OPT_SQL_MODE, OPT_HAVE_NAMED_PIPE, - OPT_SLAVE_SKIP_ERRORS, OPT_LOCAL_INFILE + OPT_DO_PSTACK, OPT_REPORT_HOST, + OPT_REPORT_USER, OPT_REPORT_PASSWORD, OPT_REPORT_PORT, + OPT_SHOW_SLAVE_AUTH_INFO, OPT_OLD_RPL_COMPAT, + OPT_SLAVE_LOAD_TMPDIR, OPT_NO_MIX_TYPE, + OPT_RPL_RECOVERY_RANK,OPT_INIT_RPL_ROLE, + OPT_RELAY_LOG, OPT_RELAY_LOG_INDEX, OPT_RELAY_LOG_INFO_FILE, + OPT_SLAVE_SKIP_ERRORS, OPT_DES_KEY_FILE, OPT_LOCAL_INFILE, + OPT_RECKLESS_SLAVE, + OPT_SSL_SSL, OPT_SSL_KEY, OPT_SSL_CERT, OPT_SSL_CA, + OPT_SSL_CAPATH, OPT_SSL_CIPHER, + OPT_BACK_LOG, OPT_BINLOG_CACHE_SIZE, + OPT_CONNECT_TIMEOUT, OPT_DELAYED_INSERT_TIMEOUT, + OPT_DELAYED_INSERT_LIMIT, OPT_DELAYED_QUEUE_SIZE, + OPT_FLUSH_TIME, OPT_FT_MIN_WORD_LEN, + OPT_FT_MAX_WORD_LEN, OPT_FT_MAX_WORD_LEN_FOR_SORT, + OPT_INTERACTIVE_TIMEOUT, OPT_JOIN_BUFF_SIZE, + OPT_KEY_BUFFER_SIZE, OPT_LONG_QUERY_TIME, + OPT_LOWER_CASE_TABLE_NAMES, OPT_MAX_ALLOWED_PACKET, + OPT_MAX_BINLOG_CACHE_SIZE, OPT_MAX_BINLOG_SIZE, + OPT_MAX_CONNECTIONS, OPT_MAX_CONNECT_ERRORS, + OPT_MAX_DELAYED_THREADS, OPT_MAX_HEP_TABLE_SIZE, + OPT_MAX_JOIN_SIZE, OPT_MAX_SORT_LENGTH, + OPT_MAX_TMP_TABLES, OPT_MAX_USER_CONNECTIONS, + OPT_MAX_WRITE_LOCK_COUNT, OPT_MYISAM_BULK_INSERT_TREE_SIZE, + OPT_MYISAM_BLOCK_SIZE, OPT_MYISAM_MAX_EXTRA_SORT_FILE_SIZE, + OPT_MYISAM_MAX_SORT_FILE_SIZE, OPT_MYISAM_SORT_BUFFER_SIZE, + OPT_NET_BUFFER_LENGTH, OPT_NET_RETRY_COUNT, + OPT_NET_READ_TIMEOUT, OPT_NET_WRITE_TIMEOUT, + OPT_OPEN_FILES_LIMIT, OPT_QUERY_BUFFER_SIZE, + OPT_QUERY_CACHE_LIMIT, OPT_QUERY_CACHE_SIZE, + OPT_QUERY_CACHE_STARTUP_TYPE, OPT_RECORD_BUFFER, + OPT_RECORD_RND_BUFFER, OPT_RELAY_LOG_SPACE_LIMIT, + OPT_SLAVE_NET_TIMEOUT, OPT_SLOW_LAUNCH_TIME, + OPT_SORT_BUFFER, OPT_TABLE_CACHE, + OPT_THREAD_CONCURRENCY, OPT_THREAD_CACHE_SIZE, + OPT_TMP_TABLE_SIZE, OPT_THREAD_STACK, + OPT_WAIT_TIMEOUT, + OPT_INNODB_MIRRORED_LOG_GROUPS, + OPT_INNODB_LOG_FILES_IN_GROUP, + OPT_INNODB_LOG_FILE_SIZE, + OPT_INNODB_LOG_BUFFER_SIZE, + OPT_INNODB_BUFFER_POOL_SIZE, + OPT_INNODB_ADDITIONAL_MEM_POOL_SIZE, + OPT_INNODB_FILE_IO_THREADS, + OPT_INNODB_LOCK_WAIT_TIMEOUT, + OPT_INNODB_THREAD_CONCURRENCY, + OPT_INNODB_FORCE_RECOVERY, + OPT_BDB_CACHE_SIZE, + OPT_BDB_LOG_BUFFER_SIZE, + OPT_BDB_MAX_LOCK }; -static struct option long_options[] = { - {"ansi", no_argument, 0, 'a'}, - {"basedir", required_argument, 0, 'b'}, + +#define LONG_TIMEOUT ((ulong) 3600L*24L*365L) + +static struct my_option my_long_options[] = +{ + {"ansi", 'a', "Use ANSI SQL syntax instead of MySQL syntax", 0, 0, 0, + GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, + {"basedir", 'b', + "Path to installation directory. All paths are usually resolved relative to this.", + (gptr*) &mysql_home_ptr, (gptr*) &mysql_home_ptr, 0, GET_STR, REQUIRED_ARG, + 0, 0, 0, 0, 0, 0}, #ifdef HAVE_BERKELEY_DB - {"bdb-home", required_argument, 0, (int) OPT_BDB_HOME}, - {"bdb-lock-detect", required_argument, 0, (int) OPT_BDB_LOCK}, - {"bdb-logdir", required_argument, 0, (int) OPT_BDB_LOG}, - {"bdb-no-recover", no_argument, 0, (int) OPT_BDB_NO_RECOVER}, - {"bdb-no-sync", no_argument, 0, (int) OPT_BDB_NOSYNC}, - {"bdb-shared-data", no_argument, 0, (int) OPT_BDB_SHARED}, - {"bdb-tmpdir", required_argument, 0, (int) OPT_BDB_TMP}, -#endif - {"big-tables", no_argument, 0, (int) OPT_BIG_TABLES}, - {"binlog-do-db", required_argument, 0, (int) OPT_BINLOG_DO_DB}, - {"binlog-ignore-db", required_argument, 0, (int) OPT_BINLOG_IGNORE_DB}, - {"bind-address", required_argument, 0, (int) OPT_BIND_ADDRESS}, - {"bootstrap", no_argument, 0, (int) OPT_BOOTSTRAP}, + {"bdb-home", OPT_BDB_HOME, "Berkeley home directory", (gptr*) &berkeley_home, + (gptr*) &berkeley_home, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, + {"bdb-lock-detect", OPT_BDB_LOCK, + "Berkeley lock detect (DEFAULT, OLDEST, RANDOM or YOUNGEST, # sec)", + 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, + {"bdb-logdir", OPT_BDB_LOG, "Berkeley DB log file directory", + (gptr*) &berkeley_logdir, (gptr*) &berkeley_logdir, 0, GET_STR, + REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, + {"bdb-no-recover", OPT_BDB_NO_RECOVER, + "Don't try to recover Berkeley DB tables on start", 0, 0, 0, GET_NO_ARG, + NO_ARG, 0, 0, 0, 0, 0, 0}, + {"bdb-no-sync", OPT_BDB_NOSYNC, "Don't synchronously flush logs", 0, 0, 0, + GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, + {"bdb-shared-data", OPT_BDB_SHARED, + "Start Berkeley DB in multi-process mode", 0, 0, 0, GET_NO_ARG, NO_ARG, 0, + 0, 0, 0, 0, 0}, + {"bdb-tmpdir", OPT_BDB_TMP, "Berkeley DB tempfile name", + (gptr*) &berkeley_tmpdir, (gptr*) &berkeley_tmpdir, 0, GET_STR, + REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, +#endif /* HAVE_BERKELEY_DB */ + {"skip-bdb", OPT_BDB_SKIP, "Don't use berkeley db (will save memory)", + 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, + {"big-tables", OPT_BIG_TABLES, + "Allow big result sets by saving all temporary sets on file (Solves most 'table full' errors)", + 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, + {"binlog-do-db", OPT_BINLOG_DO_DB, + "Tells the master it should log updates for the specified database, and exclude all others not explicitly mentioned.", + 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, + {"binlog-ignore-db", OPT_BINLOG_IGNORE_DB, + "Tells the master that updates to the given database should not be logged tothe binary log", + 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, + {"bind-address", OPT_BIND_ADDRESS, "Ip address to bind to", + (gptr*) &my_bind_addr, (gptr*) &my_bind_addr, 0, GET_ULONG, REQUIRED_ARG, 0, + 0, 0, 0, 0, 0}, + {"bootstrap", OPT_BOOTSTRAP, "Used by mysql installation scripts", 0, 0, 0, + GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, #ifdef __WIN__ - {"console", no_argument, 0, (int) OPT_CONSOLE}, -#endif - {"core-file", no_argument, 0, (int) OPT_WANT_CORE}, - {"chroot", required_argument, 0, 'r'}, - {"character-sets-dir", required_argument, 0, (int) OPT_CHARSETS_DIR}, - {"datadir", required_argument, 0, 'h'}, - {"debug", optional_argument, 0, '#'}, - {"default-character-set", required_argument, 0, 'C'}, - {"default-table-type", required_argument, 0, (int) OPT_TABLE_TYPE}, - {"delay-key-write-for-all-tables", - no_argument, 0, (int) OPT_DELAY_KEY_WRITE}, - {"enable-locking", no_argument, 0, (int) OPT_ENABLE_LOCK}, - {"enable-named-pipe", no_argument, 0, (int) OPT_HAVE_NAMED_PIPE}, - {"exit-info", optional_argument, 0, 'T'}, - {"flush", no_argument, 0, (int) OPT_FLUSH}, -#ifdef HAVE_GEMINI_DB - {"gemini-flush-log-at-commit",no_argument, 0, (int) OPT_GEMINI_FLUSH_LOG}, - {"gemini-recovery", required_argument, 0, (int) OPT_GEMINI_RECOVER}, - {"gemini-unbuffered-io", no_argument, 0, (int) OPT_GEMINI_UNBUFFERED_IO}, -#endif - /* We must always support this option to make scripts like mysqltest easier - to do */ - {"innodb_data_file_path", required_argument, 0, - OPT_INNODB_DATA_FILE_PATH}, + {"console", OPT_CONSOLE, "Don't remove the console window", + (gptr*) &opt_console, (gptr*) &opt_console, 0, GET_BOOL, NO_ARG, 0, 0, 0, + 0, 0, 0}, + {"standalone", OPT_STANDALONE, + "Dummy option to start as a standalone program (NT)", 0, 0, 0, GET_NO_ARG, + NO_ARG, 0, 0, 0, 0, 0, 0}, +#endif + {"core-file", OPT_WANT_CORE, "Write core on errors", 0, 0, 0, GET_NO_ARG, + NO_ARG, 0, 0, 0, 0, 0, 0}, + {"chroot", 'r', "Chroot mysqld daemon during startup.", + (gptr*) &mysqld_chroot, (gptr*) &mysqld_chroot, 0, GET_STR, REQUIRED_ARG, + 0, 0, 0, 0, 0, 0}, + {"character-sets-dir", OPT_CHARSETS_DIR, + "Directory where character sets are", (gptr*) &charsets_dir, + (gptr*) &charsets_dir, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, + {"datadir", 'h', "Path to the database root", (gptr*) &mysql_data_home, + (gptr*) &mysql_data_home, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, +#ifndef DBUG_OFF + {"debug", '#', "Debug log.", (gptr*) &default_dbug_option, + (gptr*) &default_dbug_option, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0}, +#ifdef SAFEMALLOC + {"skip-safemalloc", OPT_SKIP_SAFEMALLOC, + "Don't use the memory allocation checking", 0, 0, 0, GET_NO_ARG, NO_ARG, + 0, 0, 0, 0, 0, 0}, +#endif +#endif +#ifdef HAVE_OPENSSL + {"des-key-file", OPT_DES_KEY_FILE, + "Load keys for des_encrypt() and des_encrypt from given file", + (gptr*) &des_key_file, (gptr*) &des_key_file, 0, GET_STR, REQUIRED_ARG, + 0, 0, 0, 0, 0, 0}, +#endif /* HAVE_OPENSSL */ + {"default-character-set", 'C', "Set the default character set", + (gptr*) &default_charset_ptr, (gptr*) &default_charset_ptr, 0, GET_STR, + REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, + {"default-table-type", OPT_TABLE_TYPE, + "Set the default table type for tables", (gptr*) &default_table_type_name, + (gptr*) &default_table_type_name, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, + 0}, + {"delay-key-write-for-all-tables", OPT_DELAY_KEY_WRITE, + "Don't flush key buffers between writes for any MyISAM table", 0, 0, 0, + GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, + {"enable-locking", OPT_ENABLE_LOCK, "Enable system locking", 0, 0, 0, + GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, +#ifdef __NT__ + {"enable-named-pipe", OPT_HAVE_NAMED_PIPE, "Enable the named pipe (NT)", + (gptr*) &opt_enable_named_pipe, (gptr*) &opt_enable_named_pipe, 0, GET_BOOL, + NO_ARG, 0, 0, 0, 0, 0, 0}, +#endif + {"enable-pstack", OPT_DO_PSTACK, "Print a symbolic stack trace on failure", + (gptr*) &opt_do_pstack, (gptr*) &opt_do_pstack, 0, GET_BOOL, NO_ARG, 0, 0, + 0, 0, 0, 0}, + {"exit-info", 'T', "Used for debugging; Use at your own risk!", 0, 0, 0, + GET_LONG, OPT_ARG, 0, 0, 0, 0, 0, 0}, + {"flush", OPT_FLUSH, "Flush tables to disk between SQL commands", 0, 0, 0, + GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, + /* We must always support the next option to make scripts like mysqltest + easier to do */ + {"init-rpl-role", OPT_INIT_RPL_ROLE, "Set the replication role", 0, 0, 0, + GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, + {"innodb_data_file_path", OPT_INNODB_DATA_FILE_PATH, + "Path to individual files and their sizes", + 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, #ifdef HAVE_INNOBASE_DB - {"innodb_data_home_dir", required_argument, 0, - OPT_INNODB_DATA_HOME_DIR}, - {"innodb_log_group_home_dir", required_argument, 0, - OPT_INNODB_LOG_GROUP_HOME_DIR}, - {"innodb_log_arch_dir", required_argument, 0, - OPT_INNODB_LOG_ARCH_DIR}, - {"innodb_log_archive", optional_argument, 0, - OPT_INNODB_LOG_ARCHIVE}, - {"innodb_flush_log_at_trx_commit", optional_argument, 0, - OPT_INNODB_FLUSH_LOG_AT_TRX_COMMIT}, - {"innodb_fast_shutdown", optional_argument, 0, - OPT_INNODB_FAST_SHUTDOWN}, - {"innodb_flush_method", required_argument, 0, - OPT_INNODB_UNIX_FILE_FLUSH_METHOD}, -#endif - {"help", no_argument, 0, '?'}, - {"init-file", required_argument, 0, (int) OPT_INIT_FILE}, - {"log", optional_argument, 0, 'l'}, - {"language", required_argument, 0, 'L'}, - {"local-infile", optional_argument, 0, (int) OPT_LOCAL_INFILE}, - {"log-bin", optional_argument, 0, (int) OPT_BIN_LOG}, - {"log-bin-index", required_argument, 0, (int) OPT_BIN_LOG_INDEX}, - {"log-isam", optional_argument, 0, (int) OPT_ISAM_LOG}, - {"log-update", optional_argument, 0, (int) OPT_UPDATE_LOG}, - {"log-slow-queries", optional_argument, 0, (int) OPT_SLOW_QUERY_LOG}, - {"log-long-format", no_argument, 0, (int) OPT_LONG_FORMAT}, - {"log-slave-updates", no_argument, 0, (int) OPT_LOG_SLAVE_UPDATES}, - {"low-priority-updates", no_argument, 0, (int) OPT_LOW_PRIORITY_UPDATES}, - {"master-host", required_argument, 0, (int) OPT_MASTER_HOST}, - {"master-user", required_argument, 0, (int) OPT_MASTER_USER}, - {"master-password", required_argument, 0, (int) OPT_MASTER_PASSWORD}, - {"master-port", required_argument, 0, (int) OPT_MASTER_PORT}, - {"master-connect-retry", required_argument, 0, (int) OPT_MASTER_CONNECT_RETRY}, - {"master-retry-count", required_argument, 0, (int) OPT_MASTER_RETRY_COUNT}, - {"master-info-file", required_argument, 0, (int) OPT_MASTER_INFO_FILE}, - {"myisam-recover", optional_argument, 0, (int) OPT_MYISAM_RECOVER}, - {"memlock", no_argument, 0, (int) OPT_MEMLOCK}, - // needs to be available for the test case to pass in non-debugging mode - // is a no-op - {"disconnect-slave-event-count", required_argument, 0, - (int) OPT_DISCONNECT_SLAVE_EVENT_COUNT}, - {"abort-slave-event-count", required_argument, 0, - (int) OPT_ABORT_SLAVE_EVENT_COUNT}, - {"max-binlog-dump-events", required_argument, 0, - (int) OPT_MAX_BINLOG_DUMP_EVENTS}, - {"sporadic-binlog-dump-fail", no_argument, 0, - (int) OPT_SPORADIC_BINLOG_DUMP_FAIL}, - {"safemalloc-mem-limit", required_argument, 0, (int) - OPT_SAFEMALLOC_MEM_LIMIT}, - {"new", no_argument, 0, 'n'}, - {"old-protocol", no_argument, 0, 'o'}, + {"innodb_data_home_dir", OPT_INNODB_DATA_HOME_DIR, + "The common part for Innodb table spaces", (gptr*) &innobase_data_home_dir, + (gptr*) &innobase_data_home_dir, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, + 0}, + {"innodb_log_group_home_dir", OPT_INNODB_LOG_GROUP_HOME_DIR, + "Path to innodb log files.", (gptr*) &innobase_log_group_home_dir, + (gptr*) &innobase_log_group_home_dir, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, + 0, 0}, + {"innodb_log_arch_dir", OPT_INNODB_LOG_ARCH_DIR, + "Where full logs should be archived", (gptr*) &innobase_log_arch_dir, + (gptr*) &innobase_log_arch_dir, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, + {"innodb_log_archive", OPT_INNODB_LOG_ARCHIVE, + "Set to 1 if you want to have logs archived", 0, 0, 0, GET_LONG, OPT_ARG, + 0, 0, 0, 0, 0, 0}, + {"innodb_flush_log_at_trx_commit", OPT_INNODB_FLUSH_LOG_AT_TRX_COMMIT, + "Set to 0 if you don't want to flush logs", 0, 0, 0, GET_LONG, OPT_ARG, + 0, 0, 0, 0, 0, 0}, + {"innodb_flush_method", OPT_INNODB_FLUSH_METHOD, + "With which method to flush data", (gptr*) &innobase_unix_file_flush_method, + (gptr*) &innobase_unix_file_flush_method, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, + 0, 0, 0}, + {"innodb_fast_shutdown", OPT_INNODB_FAST_SHUTDOWN, + "Speeds up server shutdown process", (gptr*) &innobase_fast_shutdown, + (gptr*) &innobase_fast_shutdown, 0, GET_BOOL, NO_ARG, 1, 0, 0, 0, 0, 0}, +#endif /* End HAVE_INNOBASE_DB */ + {"help", '?', "Display this help and exit", 0, 0, 0, GET_NO_ARG, NO_ARG, 0, + 0, 0, 0, 0, 0}, + {"init-file", OPT_INIT_FILE, "Read SQL commands from this file at startup", + (gptr*) &opt_init_file, (gptr*) &opt_init_file, 0, GET_STR, REQUIRED_ARG, + 0, 0, 0, 0, 0, 0}, + {"log", 'l', "Log connections and queries to file", (gptr*) &opt_logname, + (gptr*) &opt_logname, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0}, + {"language", 'L', + "Client error messages in given language. May be given as a full path", + (gptr*) &language_ptr, (gptr*) &language_ptr, 0, GET_STR, REQUIRED_ARG, + 0, 0, 0, 0, 0, 0}, + {"local-infile", OPT_LOCAL_INFILE, + "Enable/disable LOAD DATA LOCAL INFILE (takes values 1|0)", + (gptr*) &opt_local_infile, (gptr*) &opt_local_infile, 0, GET_BOOL, OPT_ARG, + 0, 0, 0, 0, 0, 0}, + {"log-bin", OPT_BIN_LOG, + "Log queries in new binary format (for replication)", + (gptr*) &opt_bin_logname, (gptr*) &opt_bin_logname, 0, GET_STR_ALLOC, + OPT_ARG, 0, 0, 0, 0, 0, 0}, + {"log-bin-index", OPT_BIN_LOG_INDEX, + "File that holds the names for last binary log files", + (gptr*) &opt_binlog_index_name, (gptr*) &opt_binlog_index_name, 0, GET_STR, + REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, + {"log-isam", OPT_ISAM_LOG, "Log all MyISAM changes to file", + (gptr*) &myisam_log_filename, (gptr*) &myisam_log_filename, 0, GET_STR, + OPT_ARG, 0, 0, 0, 0, 0, 0}, + {"log-update", OPT_UPDATE_LOG, + "Log updates to file.# where # is a unique number if not given.", + (gptr*) &opt_update_logname, (gptr*) &opt_update_logname, 0, GET_STR, + OPT_ARG, 0, 0, 0, 0, 0, 0}, + {"log-slow-queries", OPT_SLOW_QUERY_LOG, + "Log slow queries to this log file. Defaults logging to hostname-slow.log", + (gptr*) &opt_slow_logname, (gptr*) &opt_slow_logname, 0, GET_STR, OPT_ARG, + 0, 0, 0, 0, 0, 0}, + {"log-long-format", OPT_LONG_FORMAT, + "Log some extra information to update log", 0, 0, 0, GET_NO_ARG, NO_ARG, + 0, 0, 0, 0, 0, 0}, + {"log-slave-updates", OPT_LOG_SLAVE_UPDATES, + "Tells the slave to log the updates from the slave thread to the binary log. Off by default. You will need to turn it on if you plan to daisy-chain the slaves.", + (gptr*) &opt_log_slave_updates, (gptr*) &opt_log_slave_updates, 0, GET_BOOL, + NO_ARG, 0, 0, 0, 0, 0, 0}, + {"low-priority-updates", OPT_LOW_PRIORITY_UPDATES, + "INSERT/DELETE/UPDATE has lower priority than selects", 0, 0, 0, GET_NO_ARG, + NO_ARG, 0, 0, 0, 0, 0, 0}, + {"master-host", OPT_MASTER_HOST, + "Master hostname or IP address for replication. If not set, the slave thread will not be started. Note that the setting of master-host will be ignored if there exists a valid master.info file.", + (gptr*) &master_host, (gptr*) &master_host, 0, GET_STR, REQUIRED_ARG, 0, 0, + 0, 0, 0, 0}, + {"master-user", OPT_MASTER_USER, + "The username the slave thread will use for authentication when connecting to the master. The user must have FILE privilege. If the master user is not set, user test is assumed. The value in master.info will take precedence if it can be read.", + (gptr*) &master_user, (gptr*) &master_user, 0, GET_STR, REQUIRED_ARG, 0, 0, + 0, 0, 0, 0}, + {"master-password", OPT_MASTER_PASSWORD, + "The password the slave thread will authenticate with when connecting to the master. If not set, an empty password is assumed.The value in master.info will take precedence if it can be read.", + 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, + {"master-port", OPT_MASTER_PORT, + "The port the master is listening on. If not set, the compiled setting of MYSQL_PORT is assumed. If you have not tinkered with configure options, this should be 3306. The value in master.info will take precedence if it can be read", + (gptr*) &master_port, (gptr*) &master_port, 0, GET_UINT, REQUIRED_ARG, + MYSQL_PORT, 0, 0, 0, 0, 0}, + {"master-connect-retry", OPT_MASTER_CONNECT_RETRY, + "The number of seconds the slave thread will sleep before retrying to connect to the master in case the master goes down or the connection is lost.", + (gptr*) &master_connect_retry, (gptr*) &master_connect_retry, 0, GET_UINT, + REQUIRED_ARG, 60, 0, 0, 0, 0, 0}, + {"master-retry-count", OPT_MASTER_RETRY_COUNT, + "The number of tries the slave will make to connect to the master before giving up.", + (gptr*) &master_retry_count, (gptr*) &master_retry_count, 0, GET_ULONG, + REQUIRED_ARG, 60, 0, 0, 0, 0, 0}, + {"master-info-file", OPT_MASTER_INFO_FILE, + "The location of the file that remembers where we left off on the master during the replication process. The default is `master.info' in the data directory. You should not need to change this.", + (gptr*) &master_info_file, (gptr*) &master_info_file, 0, GET_STR, + REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, + {"master-ssl", OPT_MASTER_SSL, + "Turn SSL on for replication. Be warned that is this is a relatively new feature.", + (gptr*) &master_ssl, (gptr*) &master_ssl, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, + 0, 0}, + {"master-ssl-key", OPT_MASTER_SSL_KEY, + "Master SSL keyfile name. Only applies if you have enabled master-ssl.", + (gptr*) &master_ssl_key, (gptr*) &master_ssl_key, 0, GET_STR, OPT_ARG, + 0, 0, 0, 0, 0, 0}, + {"master-ssl-cert", OPT_MASTER_SSL_CERT, + "Master SSL certificate file name. Only applies if you have enabled master-ssl.", + (gptr*) &master_ssl_cert, (gptr*) &master_ssl_cert, 0, GET_STR, OPT_ARG, + 0, 0, 0, 0, 0, 0}, + {"myisam-recover", OPT_MYISAM_RECOVER, + "Syntax: myisam-recover[=option[,option...]], where option can be DEFAULT, BACKUP or FORCE.", + (gptr*) &myisam_recover_options_str, (gptr*) &myisam_recover_options_str, 0, + GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0}, + /* + Option needs to be available for the test case to pass in non-debugging + mode. is a no-op. + */ + {"memlock", OPT_MEMLOCK, "Lock mysqld in memory", (gptr*) &locked_in_memory, + (gptr*) &locked_in_memory, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, +#ifndef DBUG_OFF + {"disconnect-slave-event-count", OPT_DISCONNECT_SLAVE_EVENT_COUNT, + "Undocumented: Meant for debugging and testing of replication", + (gptr*) &disconnect_slave_event_count, + (gptr*) &disconnect_slave_event_count, 0, GET_INT, REQUIRED_ARG, 0, 0, 0, + 0, 0, 0}, + {"abort-slave-event-count", OPT_ABORT_SLAVE_EVENT_COUNT, + "Undocumented: Meant for debugging and testing of replication", + (gptr*) &abort_slave_event_count, (gptr*) &abort_slave_event_count, + 0, GET_INT, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, + {"max-binlog-dump-events", OPT_MAX_BINLOG_DUMP_EVENTS, "Undocumented", + (gptr*) &max_binlog_dump_events, (gptr*) &max_binlog_dump_events, 0, + GET_INT, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, + {"sporadic-binlog-dump-fail", OPT_SPORADIC_BINLOG_DUMP_FAIL, "Undocumented", + (gptr*) &opt_sporadic_binlog_dump_fail, + (gptr*) &opt_sporadic_binlog_dump_fail, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, + 0}, +#endif + {"safemalloc-mem-limit", OPT_SAFEMALLOC_MEM_LIMIT, + "Simulate memory shortage when compiled with the --with-debug=full option", + 0, 0, 0, GET_ULL, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, + {"new", 'n', "Use very new possible 'unsafe' functions", 0, 0, 0, GET_NO_ARG, + NO_ARG, 0, 0, 0, 0, 0, 0}, +#ifdef NOT_YET + {"no-mix-table-types", OPT_NO_MIX_TYPE, "Undocumented", + (gptr*) &opt_no_mix_types, (gptr*) &opt_no_mix_types, 0, GET_BOOL, NO_ARG, + 0, 0, 0, 0, 0, 0}, +#endif + {"old-protocol", 'o', "Use the old (3.20) protocol", + (gptr*) &protocol_version, (gptr*) &protocol_version, 0, GET_UINT, NO_ARG, + PROTOCOL_VERSION, 0, 0, 0, 0, 0}, + {"old-rpl-compat", OPT_OLD_RPL_COMPAT, "Undocumented", + (gptr*) &opt_old_rpl_compat, (gptr*) &opt_old_rpl_compat, 0, GET_BOOL, + NO_ARG, 0, 0, 0, 0, 0, 0}, #ifdef ONE_THREAD - {"one-thread", no_argument, 0, (int) OPT_ONE_THREAD}, -#endif - {"pid-file", required_argument, 0, (int) OPT_PID_FILE}, - {"port", required_argument, 0, 'P'}, - {"replicate-do-db", required_argument, 0, (int) OPT_REPLICATE_DO_DB}, - {"replicate-do-table", required_argument, 0, - (int) OPT_REPLICATE_DO_TABLE}, - {"replicate-wild-do-table", required_argument, 0, - (int) OPT_REPLICATE_WILD_DO_TABLE}, - {"replicate-ignore-db", required_argument, 0, - (int) OPT_REPLICATE_IGNORE_DB}, - {"replicate-ignore-table", required_argument, 0, - (int) OPT_REPLICATE_IGNORE_TABLE}, - {"replicate-wild-ignore-table", required_argument, 0, - (int) OPT_REPLICATE_WILD_IGNORE_TABLE}, - {"replicate-rewrite-db", required_argument, 0, - (int) OPT_REPLICATE_REWRITE_DB}, - {"safe-mode", no_argument, 0, (int) OPT_SAFE}, - {"safe-show-database", no_argument, 0, (int) OPT_SAFE_SHOW_DB}, - {"safe-user-create", no_argument, 0, (int) OPT_SAFE_USER_CREATE}, - {"server-id", required_argument, 0, (int) OPT_SERVER_ID}, - {"set-variable", required_argument, 0, 'O'}, - {"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}, - {"skip-concurrent-insert", no_argument, 0, (int) OPT_SKIP_CONCURRENT_INSERT}, - {"skip-delay-key-write", no_argument, 0, (int) OPT_SKIP_DELAY_KEY_WRITE}, - {"skip-grant-tables", no_argument, 0, (int) OPT_SKIP_GRANT}, - {"skip-locking", no_argument, 0, (int) OPT_SKIP_LOCK}, - {"skip-host-cache", no_argument, 0, (int) OPT_SKIP_HOST_CACHE}, - {"skip-name-resolve", no_argument, 0, (int) OPT_SKIP_RESOLVE}, - {"skip-networking", no_argument, 0, (int) OPT_SKIP_NETWORKING}, - {"skip-new", no_argument, 0, (int) OPT_SKIP_NEW}, - {"skip-safemalloc", no_argument, 0, (int) OPT_SKIP_SAFEMALLOC}, - {"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-thread-priority", no_argument, 0, (int) OPT_SKIP_PRIOR}, - {"slave-skip-errors", required_argument,0, - (int) OPT_SLAVE_SKIP_ERRORS}, - {"socket", required_argument, 0, (int) OPT_SOCKET}, - {"sql-bin-update-same", no_argument, 0, (int) OPT_SQL_BIN_UPDATE_SAME}, - {"sql-mode", required_argument, 0, (int) OPT_SQL_MODE}, + {"one-thread", OPT_ONE_THREAD, + "Only use one thread (for debugging under Linux)", 0, 0, 0, GET_NO_ARG, + NO_ARG, 0, 0, 0, 0, 0, 0}, +#endif + {"pid-file", OPT_PID_FILE, "Pid file used by safe_mysqld", + (gptr*) &pidfile_name_ptr, (gptr*) &pidfile_name_ptr, 0, GET_STR, + REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, + {"port", 'P', "Port number to use for connection.", (gptr*) &mysql_port, + (gptr*) &mysql_port, 0, GET_UINT, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, + {"reckless-slave", OPT_RECKLESS_SLAVE, "Undocumented", 0, 0, 0, GET_NO_ARG, + NO_ARG, 0, 0, 0, 0, 0, 0}, + {"replicate-do-db", OPT_REPLICATE_DO_DB, + "Tells the slave thread to restrict replication to the specified database. To specify more than one database, use the directive multiple times, once for each database. Note that this will only work if you do not use cross-database queries such as UPDATE some_db.some_table SET foo='bar' while having selected a different or no database. If you need cross database updates to work, make sure you have 3.23.28 or later, and use replicate-wild-do-table=db_name.%.", + 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, + {"replicate-do-table", OPT_REPLICATE_DO_TABLE, + "Tells the slave thread to restrict replication to the specified table. To specify more than one table, use the directive multiple times, once for each table. This will work for cross-database updates, in contrast to replicate-do-db.", + 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, + {"replicate-wild-do-table", OPT_REPLICATE_WILD_DO_TABLE, + "Tells the slave thread to restrict replication to the tables that match the specified wildcard pattern. To specify more than one table, use the directive multiple times, once for each table. This will work for cross-database updates. Example: replicate-wild-do-table=foo%.bar% will replicate only updates to tables in all databases that start with foo and whose table names start with bar", + 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, + {"replicate-ignore-db", OPT_REPLICATE_IGNORE_DB, + "Tells the slave thread to not replicate to the specified database. To specify more than one database to ignore, use the directive multiple times, once for each database. This option will not work if you use cross database updates. If you need cross database updates to work, make sure you have 3.23.28 or later, and use replicate-wild-ignore-table=db_name.%. ", + 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, + {"replicate-ignore-table", OPT_REPLICATE_IGNORE_TABLE, + "Tells the slave thread to not replicate to the specified table. To specify more than one table to ignore, use the directive multiple times, once for each table. This will work for cross-datbase updates, in contrast to replicate-ignore-db.", + 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, + {"replicate-wild-ignore-table", OPT_REPLICATE_WILD_IGNORE_TABLE, + "Tells the slave thread to not replicate to the tables that match the given wildcard pattern. To specify more than one table to ignore, use the directive multiple times, once for each table. This will work for cross-database updates. Example: replicate-wild-ignore-table=foo%.bar% will not do updates to tables in databases that start with foo and whose table names start with bar.", + 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, + {"replicate-rewrite-db", OPT_REPLICATE_REWRITE_DB, + "Updates to a database with a different name than the original. Example: replicate-rewrite-db=master_db_name->slave_db_name", + 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, + // In replication, we may need to tell the other servers how to connect + {"report-host", OPT_REPORT_HOST, + "Hostname or IP of the slave to be reported to to the master during slave registration. Will appear in the output of SHOW SLAVE HOSTS. Leave unset if you do not want the slave to register itself with the master. Note that it is not sufficient for the master to simply read the IP of the slave off the socket once the slave connects. Due to NAT and other routing issues, that IP may not be valid for connecting to the slave from the master or other hosts.", + (gptr*) &report_host, (gptr*) &report_host, 0, GET_STR, REQUIRED_ARG, 0, 0, + 0, 0, 0, 0}, + {"report-user", OPT_REPORT_USER, "Undocumented", (gptr*) &report_user, + (gptr*) &report_user, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, + {"report-password", OPT_REPORT_PASSWORD, "Undocumented", + (gptr*) &report_password, (gptr*) &report_password, 0, GET_STR, + REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, + {"report-port", OPT_REPORT_PORT, + "Port for connecting to slave reported to the master during slave registration. Set it only if the slave is listening on a non-default port or if you have a special tunnel from the master or other clients to the slave. If not sure, leave this option unset.", + (gptr*) &report_port, (gptr*) &report_port, 0, GET_UINT, REQUIRED_ARG, + MYSQL_PORT, 0, 0, 0, 0, 0}, + {"rpl-recovery-rank", OPT_RPL_RECOVERY_RANK, "Undocumented", + (gptr*) &rpl_recovery_rank, (gptr*) &rpl_recovery_rank, 0, GET_UINT, + REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, + {"relay-log", OPT_RELAY_LOG, "Undocumented", + (gptr*) &opt_relay_logname, (gptr*) &opt_relay_logname, 0, + GET_STR_ALLOC, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, + {"relay-log-index", OPT_RELAY_LOG_INDEX, "Undocumented", + (gptr*) &opt_relaylog_index_name, (gptr*) &opt_relaylog_index_name, 0, + GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, + {"safe-mode", OPT_SAFE, "Skip some optimize stages (for testing).", + 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, + {"safe-show-database", OPT_SAFE_SHOW_DB, + "Don't show databases for which the user has no privileges", + (gptr*) &opt_safe_show_db, (gptr*) &opt_safe_show_db, 0, GET_BOOL, NO_ARG, + 0, 0, 0, 0, 0, 0}, + {"safe-user-create", OPT_SAFE_USER_CREATE, + "Don't allow new user creation by the user who has no write privileges to the mysql.user table", + (gptr*) &opt_safe_user_create, (gptr*) &opt_safe_user_create, 0, GET_BOOL, + NO_ARG, 0, 0, 0, 0, 0, 0}, + {"server-id", OPT_SERVER_ID, + "Uniquely identifies the server instance in the community of replication partners", + (gptr*) &server_id, (gptr*) &server_id, 0, GET_UINT, REQUIRED_ARG, 0, 0, 0, + 0, 0, 0}, + {"set-variable", 'O', + "Change the value of a variable. Please note that this option is deprecated;you can set variables directly with --variable-name=value.", + 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, + {"show-slave-auth-info", OPT_SHOW_SLAVE_AUTH_INFO, "Undocumented", + (gptr*) &opt_show_slave_auth_info, (gptr*) &opt_show_slave_auth_info, 0, + GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, + {"concurrent-insert", OPT_CONCURRENT_INSERT, + "Use concurrent insert with MyISAM. Disable with prefix --skip-", + (gptr*) &myisam_concurrent_insert, (gptr*) &myisam_concurrent_insert, + 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, + {"delay-key-write", OPT_USE_DELAY_KEY_WRITE, + "Use delay_key_write option for all tables. Disable with prefix --skip-", + (gptr*) &myisam_delay_key_write, (gptr*) &myisam_delay_key_write, 0, + GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, + {"skip-grant-tables", OPT_SKIP_GRANT, + "Start without grant tables. This gives all users FULL ACCESS to all tables!", + (gptr*) &opt_noacl, (gptr*) &opt_noacl, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, + 0}, + {"skip-innodb", OPT_INNODB_SKIP, "Don't use Innodb (will save memory)", + 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, + {"skip-locking", OPT_SKIP_LOCK, + "Don't use system locking. To use isamchk one has to shut down the server.", + 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, + {"skip-host-cache", OPT_SKIP_HOST_CACHE, "Don't cache host names", 0, 0, 0, + GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, + {"skip-name-resolve", OPT_SKIP_RESOLVE, + "Don't resolve hostnames. All hostnames are IP's or 'localhost'", + 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, + {"skip-networking", OPT_SKIP_NETWORKING, + "Don't allow connection with TCP/IP.", 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, + 0, 0, 0}, + {"skip-new", OPT_SKIP_NEW, "Don't use new, possible wrong routines.", + 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, + {"skip-show-database", OPT_SKIP_SHOW_DB, + "Don't allow 'SHOW DATABASE' commands", 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, + 0, 0, 0, 0}, + {"skip-slave-start", OPT_SKIP_SLAVE_START, + "If set, slave is not autostarted.", (gptr*) &opt_skip_slave_start, + (gptr*) &opt_skip_slave_start, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, + {"skip-stack-trace", OPT_SKIP_STACK_TRACE, + "Don't print a stack trace on failure", 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, + 0, 0, 0, 0}, + {"skip-symlink", OPT_SKIP_SYMLINKS, "Don't allow symlinking of tables", + 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, + {"skip-thread-priority", OPT_SKIP_PRIOR, + "Don't give threads different priorities.", 0, 0, 0, GET_NO_ARG, NO_ARG, 0, + 0, 0, 0, 0, 0}, + {"relay-log-info-file", OPT_RELAY_LOG_INFO_FILE, "Undocumented", + (gptr*) &relay_log_info_file, (gptr*) &relay_log_info_file, 0, GET_STR, + REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, + {"slave-load-tmpdir", OPT_SLAVE_LOAD_TMPDIR, "Undocumented", + (gptr*) &slave_load_tmpdir, (gptr*) &slave_load_tmpdir, 0, GET_STR_ALLOC, + REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, + {"slave-skip-errors", OPT_SLAVE_SKIP_ERRORS, + "Tells the slave thread to continue replication when a query returns an error from the provided list", + 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, + {"socket", OPT_SOCKET, "Socket file to use for connection", + (gptr*) &mysql_unix_port, (gptr*) &mysql_unix_port, 0, GET_STR, + REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, + {"sql-bin-update-same", OPT_SQL_BIN_UPDATE_SAME, + "If set, setting SQL_LOG_BIN to a value will automatically set SQL_LOG_UPDATE to the same value and vice versa.", + (gptr*) &opt_sql_bin_update, (gptr*) &opt_sql_bin_update, 0, GET_BOOL, + NO_ARG, 0, 0, 0, 0, 0, 0}, + {"sql-mode", OPT_SQL_MODE, + "Syntax: sql-mode=option[,option[,option...]] where option can be one of: REAL_AS_FLOAT, PIPES_AS_CONCAT, ANSI_QUOTES, IGNORE_SPACE, SERIALIZE, ONLY_FULL_GROUP_BY, NO_UNSIGNED_SUBTRACTION.", + (gptr*) &sql_mode_str, (gptr*) &sql_mode_str, 0, GET_STR, REQUIRED_ARG, 0, + 0, 0, 0, 0, 0}, +#ifdef HAVE_OPENSSL #include "sslopt-longopts.h" -#ifdef __WIN__ - {"standalone", no_argument, 0, (int) OPT_STANDALONE}, #endif - {"transaction-isolation", required_argument, 0, (int) OPT_TX_ISOLATION}, - {"temp-pool", no_argument, 0, (int) OPT_TEMP_POOL}, - {"tmpdir", required_argument, 0, 't'}, - {"use-locking", no_argument, 0, (int) OPT_USE_LOCKING}, + {"temp-pool", OPT_TEMP_POOL, + "Using this option will cause most temporary files created to use a small set of names, rather than a unique name for each new file.", + (gptr*) &use_temp_pool, (gptr*) &use_temp_pool, 0, GET_BOOL, NO_ARG, 0, 0, + 0, 0, 0, 0}, + {"tmpdir", 't', "Path for temporary files", (gptr*) &mysql_tmpdir, + (gptr*) &mysql_tmpdir, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, + {"transaction-isolation", OPT_TX_ISOLATION, + "Default transaction isolation level", (gptr*) &default_tx_isolation_name, + (gptr*) &default_tx_isolation_name, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, + 0, 0}, + {"use-locking", OPT_USE_LOCKING, "Use system (external) locking", + 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, #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'}, - {0, 0, 0, 0} -}; - -#define LONG_TIMEOUT ((ulong) 3600L*24L*365L) - -CHANGEABLE_VAR changeable_vars[] = { - { "back_log", (long*) &back_log, - 50, 1, 65535, 0, 1 }, + {"use-symbolic-links", 's', "Enable symbolic link support", + (gptr*) &my_use_symdir, (gptr*) &my_use_symdir, 0, GET_BOOL, NO_ARG, 0, 0, + 0, 0, 0, 0}, +#endif + {"user", 'u', "Run mysqld daemon as user", (gptr*) &mysqld_user, + (gptr*) &mysqld_user, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, + {"version", 'V', "Output version information and exit", 0, 0, 0, GET_NO_ARG, + NO_ARG, 0, 0, 0, 0, 0, 0}, + {"version", 'v', "Synonym for option -v", 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, + 0, 0, 0, 0}, + {"warnings", 'W', "Log some not critical warnings to the log file", + (gptr*) &opt_warnings, (gptr*) &opt_warnings, 0, GET_BOOL, NO_ARG, 0, 0, 0, + 0, 0, 0}, + { "back_log", OPT_BACK_LOG, + "The number of outstanding connection requests MySQL can have. This comes into play when the main MySQL thread gets very many connection requests in a very short time.", (gptr*) &back_log, (gptr*) &back_log, 0, GET_ULONG, + REQUIRED_ARG, 50, 1, 65535, 0, 1, 0 }, #ifdef HAVE_BERKELEY_DB - { "bdb_cache_size", (long*) &berkeley_cache_size, - KEY_CACHE_SIZE, 20*1024, (long) ~0, 0, IO_SIZE }, - {"bdb_log_buffer_size", (long*) &berkeley_log_buffer_size, 0, 256*1024L, - ~0L, 0, 1024}, - { "bdb_max_lock", (long*) &berkeley_max_lock, - 10000, 0, (long) ~0, 0, 1 }, - /* QQ: The following should be removed soon! */ - { "bdb_lock_max", (long*) &berkeley_max_lock, - 10000, 0, (long) ~0, 0, 1 }, -#endif - { "binlog_cache_size", (long*) &binlog_cache_size, - 32*1024L, IO_SIZE, ~0L, 0, IO_SIZE }, - { "connect_timeout", (long*) &connect_timeout, - CONNECT_TIMEOUT, 2, LONG_TIMEOUT, 0, 1 }, - { "delayed_insert_timeout", (long*) &delayed_insert_timeout, - DELAYED_WAIT_TIMEOUT, 1, LONG_TIMEOUT, 0, 1 }, - { "delayed_insert_limit", (long*) &delayed_insert_limit, - DELAYED_LIMIT, 1, ~0L, 0, 1 }, - { "delayed_queue_size", (long*) &delayed_queue_size, - DELAYED_QUEUE_SIZE, 1, ~0L, 0, 1 }, - { "flush_time", (long*) &flush_time, - FLUSH_TIME, 0, LONG_TIMEOUT, 0, 1 }, -#ifdef HAVE_GEMINI_DB - { "gemini_buffer_cache", (long*) &gemini_buffer_cache, - 128 * 8192, 16, LONG_MAX, 0, 1 }, - { "gemini_connection_limit", (long*) &gemini_connection_limit, - 100, 10, LONG_MAX, 0, 1 }, - { "gemini_io_threads", (long*) &gemini_io_threads, - 2, 0, 256, 0, 1 }, - { "gemini_log_cluster_size", (long*) &gemini_log_cluster_size, - 256 * 1024, 16 * 1024, LONG_MAX, 0, 1 }, - { "gemini_lock_table_size", (long*) &gemini_locktablesize, - 4096, 1024, LONG_MAX, 0, 1 }, - { "gemini_lock_wait_timeout",(long*) &gemini_lock_wait_timeout, - 10, 1, LONG_MAX, 0, 1 }, - { "gemini_spin_retries", (long*) &gemini_spin_retries, - 1, 0, LONG_MAX, 0, 1 }, -#endif + { "bdb_cache_size", OPT_BDB_CACHE_SIZE, + "The buffer that is allocated to cache index and rows for BDB tables.", + (gptr*) &berkeley_cache_size, (gptr*) &berkeley_cache_size, 0, GET_ULONG, + REQUIRED_ARG, KEY_CACHE_SIZE, 20*1024, (long) ~0, 0, IO_SIZE, 0}, + {"bdb_log_buffer_size", OPT_BDB_LOG_BUFFER_SIZE, + "The buffer that is allocated to cache index and rows for BDB tables.", + (gptr*) &berkeley_log_buffer_size, (gptr*) &berkeley_log_buffer_size, 0, + GET_ULONG, REQUIRED_ARG, 0, 256*1024L, ~0L, 0, 1024, 0}, + {"bdb_max_lock", OPT_BDB_MAX_LOCK, + "The maximum number of locks you can have active on a BDB table.", + (gptr*) &berkeley_max_lock, (gptr*) &berkeley_max_lock, 0, GET_ULONG, + REQUIRED_ARG, 10000, 0, (long) ~0, 0, 1, 0}, + /* QQ: The following should be removed soon! */ + {"bdb_lock_max", OPT_BDB_MAX_LOCK, "Synonym for bdb_max_lock", + (gptr*) &berkeley_max_lock, (gptr*) &berkeley_max_lock, 0, GET_ULONG, + REQUIRED_ARG, 10000, 0, (long) ~0, 0, 1, 0}, +#endif /* HAVE_BERKELEY_DB */ + {"binlog_cache_size", OPT_BINLOG_CACHE_SIZE, + "The size of the cache to hold the SQL statements for the binary log during a transaction. If you often use big, multi-statement transactions you can increase this to get more performance.", + (gptr*) &binlog_cache_size, (gptr*) &binlog_cache_size, 0, GET_ULONG, + REQUIRED_ARG, 32*1024L, IO_SIZE, ~0L, 0, IO_SIZE, 0}, + {"connect_timeout", OPT_CONNECT_TIMEOUT, + "The number of seconds the mysqld server is waiting for a connect packet before responding with Bad handshake", + (gptr*) &connect_timeout, (gptr*) &connect_timeout, 0, GET_ULONG, + REQUIRED_ARG, CONNECT_TIMEOUT, 2, LONG_TIMEOUT, 0, 1, 0 }, + {"delayed_insert_timeout", OPT_DELAYED_INSERT_TIMEOUT, + "How long a INSERT DELAYED thread should wait for INSERT statements before terminating.", + (gptr*) &delayed_insert_timeout, (gptr*) &delayed_insert_timeout, 0, + GET_ULONG, REQUIRED_ARG, DELAYED_WAIT_TIMEOUT, 1, LONG_TIMEOUT, 0, 1, 0}, + {"delayed_insert_limit", OPT_DELAYED_INSERT_LIMIT, + "After inserting delayed_insert_limit rows, the INSERT DELAYED handler will check if there are any SELECT statements pending. If so, it allows these to execute before continuing.", + (gptr*) &delayed_insert_limit, (gptr*) &delayed_insert_limit, 0, GET_ULONG, + REQUIRED_ARG, DELAYED_LIMIT, 1, ~0L, 0, 1, 0}, + { "delayed_queue_size", OPT_DELAYED_QUEUE_SIZE, + "What size queue (in rows) should be allocated for handling INSERT DELAYED. If the queue becomes full, any client that does INSERT DELAYED will wait until there is room in the queue again.", + (gptr*) &delayed_queue_size, (gptr*) &delayed_queue_size, 0, GET_ULONG, + REQUIRED_ARG, DELAYED_QUEUE_SIZE, 1, ~0L, 0, 1, 0}, + { "flush_time", OPT_FLUSH_TIME, + "A dedicated thread is created to flush all tables at the given interval.", + (gptr*) &flush_time, (gptr*) &flush_time, 0, GET_ULONG, REQUIRED_ARG, + FLUSH_TIME, 0, LONG_TIMEOUT, 0, 1, 0}, + { "ft_min_word_len", OPT_FT_MIN_WORD_LEN, + "The minimum length of the word to be included in a FULLTEXT index. Note: FULLTEXT indexes must be rebuilt after changing this variable.", + (gptr*) &ft_min_word_len, (gptr*) &ft_min_word_len, 0, GET_ULONG, + REQUIRED_ARG, 4, 1, HA_FT_MAXLEN, 0, 1, 0}, + { "ft_max_word_len", OPT_FT_MAX_WORD_LEN, + "The maximum length of the word to be included in a FULLTEXT index. Note: FULLTEXT indexes must be rebuilt after changing this variable.", + (gptr*) &ft_max_word_len, (gptr*) &ft_max_word_len, 0, GET_ULONG, + REQUIRED_ARG, HA_FT_MAXLEN, 10, HA_FT_MAXLEN, 0, 1, 0}, + { "ft_max_word_len_for_sort", OPT_FT_MAX_WORD_LEN_FOR_SORT, + "Undocumented", (gptr*) &ft_max_word_len_for_sort, + (gptr*) &ft_max_word_len_for_sort, 0, GET_ULONG, REQUIRED_ARG, 20, 4, + HA_FT_MAXLEN, 0, 1, 0}, #ifdef HAVE_INNOBASE_DB - {"innodb_mirrored_log_groups", - (long*) &innobase_mirrored_log_groups, 1, 1, 10, 0, 1}, - {"innodb_log_files_in_group", - (long*) &innobase_log_files_in_group, 2, 2, 100, 0, 1}, - {"innodb_log_file_size", - (long*) &innobase_log_file_size, 5*1024*1024L, 1*1024*1024L, - ~0L, 0, 1024*1024L}, - {"innodb_log_buffer_size", - (long*) &innobase_log_buffer_size, 1024*1024L, 256*1024L, - ~0L, 0, 1024}, - {"innodb_buffer_pool_size", - (long*) &innobase_buffer_pool_size, 8*1024*1024L, 1024*1024L, - ~0L, 0, 1024*1024L}, - {"innodb_additional_mem_pool_size", - (long*) &innobase_additional_mem_pool_size, 1*1024*1024L, 512*1024L, - ~0L, 0, 1024}, - {"innodb_file_io_threads", - (long*) &innobase_file_io_threads, 4, 4, 64, 0, 1}, - {"innodb_lock_wait_timeout", - (long*) &innobase_lock_wait_timeout, 50, 1, - 1024 * 1024 * 1024, 0, 1}, - {"innodb_thread_concurrency", - (long*) &innobase_thread_concurrency, 8, 1, 1000, 0, 1}, - {"innodb_force_recovery", - (long*) &innobase_force_recovery, 0, 0, 6, 0, 1}, -#endif - { "interactive_timeout", (long*) &net_interactive_timeout, - NET_WAIT_TIMEOUT, 1, LONG_TIMEOUT, 0, 1 }, - { "join_buffer_size", (long*) &join_buff_size, - 128*1024L, IO_SIZE*2+MALLOC_OVERHEAD, ~0L, MALLOC_OVERHEAD, IO_SIZE }, - { "key_buffer_size", (long*) &keybuff_size, - KEY_CACHE_SIZE, MALLOC_OVERHEAD, (long) ~0, MALLOC_OVERHEAD, IO_SIZE }, - { "long_query_time", (long*) &long_query_time, - 10, 1, LONG_TIMEOUT, 0, 1 }, - { "lower_case_table_names", (long*) &lower_case_table_names, - IF_WIN(1,0), 0, 1, 0, 1 }, - { "max_allowed_packet", (long*) &max_allowed_packet, - 1024*1024L, 80, 64*1024*1024L, MALLOC_OVERHEAD, 1024 }, - { "max_binlog_cache_size", (long*) &max_binlog_cache_size, - ~0L, IO_SIZE, ~0L, 0, IO_SIZE }, - { "max_binlog_size", (long*) &max_binlog_size, - 1024*1024L*1024L, 1024, 1024*1024L*1024L, 0, 1 }, - { "max_connections", (long*) &max_connections, - 100, 1, 16384, 0, 1 }, - { "max_connect_errors", (long*) &max_connect_errors, - MAX_CONNECT_ERRORS, 1, ~0L, 0, 1 }, - { "max_delayed_threads", (long*) &max_insert_delayed_threads, - 20, 1, 16384, 0, 1 }, - { "max_heap_table_size", (long*) &max_heap_table_size, - 16*1024*1024L, 16384, ~0L, MALLOC_OVERHEAD, 1024 }, - { "max_join_size", (long*) &max_join_size, - ~0L, 1, ~0L, 0, 1 }, - { "max_sort_length", (long*) &max_item_sort_length, - 1024, 4, 8192*1024L, 0, 1 }, - { "max_tmp_tables", (long*) &max_tmp_tables, - 32, 1, ~0L, 0, 1 }, - { "max_user_connections", (long*) &max_user_connections, - 0, 1, ~0L, 0, 1 }, - { "max_write_lock_count", (long*) &max_write_lock_count, - ~0L, 1, ~0L, 0, 1 }, - { "myisam_max_extra_sort_file_size", - (long*) &myisam_max_extra_sort_file_size, - (long) (MI_MAX_TEMP_LENGTH/(1024L*1024L)), 0, ~0L, 0, 1 }, - { "myisam_max_sort_file_size", (long*) &myisam_max_sort_file_size, - (long) (LONG_MAX/(1024L*1024L)), 0, ~0L, 0, 1 }, - { "myisam_sort_buffer_size", (long*) &myisam_sort_buffer_size, - 8192*1024, 4, ~0L, 0, 1 }, - { "net_buffer_length", (long*) &net_buffer_length, - 16384, 1024, 1024*1024L, MALLOC_OVERHEAD, 1024 }, - { "net_retry_count", (long*) &mysqld_net_retry_count, - MYSQLD_NET_RETRY_COUNT, 1, ~0L, 0, 1 }, - { "net_read_timeout", (long*) &net_read_timeout, - NET_READ_TIMEOUT, 1, LONG_TIMEOUT, 0, 1 }, - { "net_write_timeout", (long*) &net_write_timeout, - NET_WRITE_TIMEOUT, 1, LONG_TIMEOUT, 0, 1 }, - { "open_files_limit", (long*) &open_files_limit, - 0, 0, 65535, 0, 1}, - { "query_buffer_size", (long*) &query_buff_size, - 0, MALLOC_OVERHEAD, (long) ~0, MALLOC_OVERHEAD, IO_SIZE }, - { "record_buffer", (long*) &my_default_record_cache_size, - 128*1024L, IO_SIZE*2+MALLOC_OVERHEAD, ~0L, MALLOC_OVERHEAD, IO_SIZE }, - { "record_rnd_buffer", (long*) &record_rnd_cache_size, - 0, IO_SIZE*2+MALLOC_OVERHEAD, ~0L, MALLOC_OVERHEAD, IO_SIZE }, - { "slave_net_timeout", (long*) &slave_net_timeout, - SLAVE_NET_TIMEOUT, 1, LONG_TIMEOUT, 0, 1 }, - { "slow_launch_time", (long*) &slow_launch_time, - 2L, 0L, LONG_TIMEOUT, 0, 1 }, - { "sort_buffer", (long*) &sortbuff_size, - MAX_SORT_MEMORY, MIN_SORT_MEMORY+MALLOC_OVERHEAD*2, ~0L, MALLOC_OVERHEAD, 1 }, - { "table_cache", (long*) &table_cache_size, - 64, 1, 16384, 0, 1 }, - { "thread_concurrency", (long*) &concurrency, - DEFAULT_CONCURRENCY, 1, 512, 0, 1 }, - { "thread_cache_size", (long*) &thread_cache_size, - 0, 0, 16384, 0, 1 }, - { "tmp_table_size", (long*) &tmp_table_size, - 32*1024*1024L, 1024, ~0L, 0, 1 }, - { "thread_stack", (long*) &thread_stack, - DEFAULT_THREAD_STACK, 1024*32, ~0L, 0, 1024 }, - { "wait_timeout", (long*) &net_wait_timeout, - NET_WAIT_TIMEOUT, 1, LONG_TIMEOUT, 0, 1 }, - { NullS, (long*) 0, 0, 0, 0, 0, 0} + {"innodb_mirrored_log_groups", OPT_INNODB_MIRRORED_LOG_GROUPS, + "Number of identical copies of log groups we keep for the database. Currently this should be set to 1.", + (gptr*) &innobase_mirrored_log_groups, + (gptr*) &innobase_mirrored_log_groups, 0, GET_LONG, REQUIRED_ARG, 1, 1, 10, + 0, 1, 0}, + {"innodb_log_files_in_group", OPT_INNODB_LOG_FILES_IN_GROUP, + "Number of log files in the log group. InnoDB writes to the files in a circular fashion. Value 3 is recommended here.", + (gptr*) &innobase_log_files_in_group, (gptr*) &innobase_log_files_in_group, + 0, GET_LONG, REQUIRED_ARG, 2, 2, 100, 0, 1, 0}, + {"innodb_log_file_size", OPT_INNODB_LOG_FILE_SIZE, + "Size of each log file in a log group in megabytes.", + (gptr*) &innobase_log_file_size, (gptr*) &innobase_log_file_size, 0, + GET_LONG, REQUIRED_ARG, 5*1024*1024L, 1*1024*1024L, ~0L, 0, 1024*1024L, 0}, + {"innodb_log_buffer_size", OPT_INNODB_LOG_BUFFER_SIZE, + "The size of the buffer which InnoDB uses to write log to the log files on disk.", + (gptr*) &innobase_log_buffer_size, (gptr*) &innobase_log_buffer_size, 0, + GET_LONG, REQUIRED_ARG, 1024*1024L, 256*1024L, ~0L, 0, 1024, 0}, + {"innodb_buffer_pool_size", OPT_INNODB_BUFFER_POOL_SIZE, + "The size of the memory buffer InnoDB uses to cache data and indexes of its tables.", + (gptr*) &innobase_buffer_pool_size, (gptr*) &innobase_buffer_pool_size, 0, + GET_LONG, REQUIRED_ARG, 8*1024*1024L, 1024*1024L, ~0L, 0, 1024*1024L, 0}, + {"innodb_additional_mem_pool_size", OPT_INNODB_ADDITIONAL_MEM_POOL_SIZE, + "Size of a memory pool InnoDB uses to store data dictionary information and other internal data structures.", + (gptr*) &innobase_additional_mem_pool_size, + (gptr*) &innobase_additional_mem_pool_size, 0, GET_LONG, REQUIRED_ARG, + 1*1024*1024L, 512*1024L, ~0L, 0, 1024, 0}, + {"innodb_file_io_threads", OPT_INNODB_FILE_IO_THREADS, + "Number of file I/O threads in InnoDB.", (gptr*) &innobase_file_io_threads, + (gptr*) &innobase_file_io_threads, 0, GET_LONG, REQUIRED_ARG, 4, 4, 64, 0, + 1, 0}, + {"innodb_lock_wait_timeout", OPT_INNODB_LOCK_WAIT_TIMEOUT, + "Timeout in seconds an InnoDB transaction may wait for a lock before being rolled back.", + (gptr*) &innobase_lock_wait_timeout, (gptr*) &innobase_lock_wait_timeout, + 0, GET_LONG, REQUIRED_ARG, 50, 1, 1024 * 1024 * 1024, 0, 1, 0}, + {"innodb_thread_concurrency", OPT_INNODB_THREAD_CONCURRENCY, + "Helps in performance tuning in heavily concurrent environments.", + (gptr*) &innobase_thread_concurrency, (gptr*) &innobase_thread_concurrency, + 0, GET_LONG, REQUIRED_ARG, 8, 1, 1000, 0, 1, 0}, + {"innodb_force_recovery", OPT_INNODB_FORCE_RECOVERY, + "Helps to save your data in case the disk image of the database becomes corrupt.", + (gptr*) &innobase_force_recovery, (gptr*) &innobase_force_recovery, 0, + GET_LONG, REQUIRED_ARG, 0, 0, 6, 0, 1, 0}, +#endif /* HAVE_INNOBASE_DB */ + {"interactive_timeout", OPT_INTERACTIVE_TIMEOUT, + "The number of seconds the server waits for activity on an interactive connection before closing it.", + (gptr*) &net_interactive_timeout, (gptr*) &net_interactive_timeout, 0, + GET_ULONG, REQUIRED_ARG, NET_WAIT_TIMEOUT, 1, LONG_TIMEOUT, 0, 1, 0}, + {"join_buffer_size", OPT_JOIN_BUFF_SIZE, + "The size of the buffer that is used for full joins.", + (gptr*) &join_buff_size, (gptr*) &join_buff_size, 0, GET_ULONG, + REQUIRED_ARG, 128*1024L, IO_SIZE*2+MALLOC_OVERHEAD, ~0L, MALLOC_OVERHEAD, + IO_SIZE, 0}, + {"key_buffer_size", OPT_KEY_BUFFER_SIZE, + "The size of the buffer used for index blocks. Increase this to get better index handling (for all reads and multiple writes) to as much as you can afford; 64M on a 256M machine that mainly runs MySQL is quite common.", + (gptr*) &keybuff_size, (gptr*) &keybuff_size, 0, GET_ULONG, REQUIRED_ARG, + KEY_CACHE_SIZE, MALLOC_OVERHEAD, (long) ~0, MALLOC_OVERHEAD, IO_SIZE, 0}, + {"long_query_time", OPT_LONG_QUERY_TIME, + "Log all queries that have taken more than long_query_time seconds to execute to file.", + (gptr*) &long_query_time, (gptr*) &long_query_time, 0, GET_ULONG, + REQUIRED_ARG, 10, 1, LONG_TIMEOUT, 0, 1, 0}, + {"lower_case_table_names", OPT_LOWER_CASE_TABLE_NAMES, + "If set to 1 table names are stored in lowercase on disk and table names will be case-insensitive.", + (gptr*) &lower_case_table_names, (gptr*) &lower_case_table_names, 0, + GET_ULONG, REQUIRED_ARG, IF_WIN(1,0), 0, 1, 0, 1, 0}, + {"max_allowed_packet", OPT_MAX_ALLOWED_PACKET, + "Max packetlength to send/receive from to server.", + (gptr*) &max_allowed_packet, (gptr*) &max_allowed_packet, 0, GET_ULONG, + REQUIRED_ARG, 1024*1024L, 80, 64*1024*1024L, MALLOC_OVERHEAD, 1024, 0}, + {"max_binlog_cache_size", OPT_MAX_BINLOG_CACHE_SIZE, + "Can be used to restrict the total size used to cache a multi-transaction query.", + (gptr*) &max_binlog_cache_size, (gptr*) &max_binlog_cache_size, 0, + GET_ULONG, REQUIRED_ARG, ~0L, IO_SIZE, ~0L, 0, IO_SIZE, 0}, + {"max_binlog_size", OPT_MAX_BINLOG_SIZE, + "Binary log will be rotated automatically when the size crosses the limit.", + (gptr*) &max_binlog_size, (gptr*) &max_binlog_size, 0, GET_ULONG, + REQUIRED_ARG, 1024*1024L*1024L, 1024, 1024*1024L*1024L, 0, 1, 0}, + {"max_connections", OPT_MAX_CONNECTIONS, + "The number of simultaneous clients allowed.", (gptr*) &max_connections, + (gptr*) &max_connections, 0, GET_ULONG, REQUIRED_ARG, 100, 1, 16384, 0, 1, + 0}, + {"max_connect_errors", OPT_MAX_CONNECT_ERRORS, + "If there is more than this number of interrupted connections from a host this host will be blocked from further connections.", + (gptr*) &max_connect_errors, (gptr*) &max_connect_errors, 0, GET_ULONG, + REQUIRED_ARG, MAX_CONNECT_ERRORS, 1, ~0L, 0, 1, 0}, + {"max_delayed_threads", OPT_MAX_DELAYED_THREADS, + "Don't start more than this number of threads to handle INSERT DELAYED statements.", + (gptr*) &max_insert_delayed_threads, (gptr*) &max_insert_delayed_threads, + 0, GET_ULONG, REQUIRED_ARG, 20, 1, 16384, 0, 1, 0}, + {"max_heap_table_size", OPT_MAX_HEP_TABLE_SIZE, + "Don't allow creation of heap tables bigger than this.", + (gptr*) &max_heap_table_size, (gptr*) &max_heap_table_size, 0, GET_ULONG, + REQUIRED_ARG, 16*1024*1024L, 16384, ~0L, MALLOC_OVERHEAD, 1024, 0}, + {"max_join_size", OPT_MAX_JOIN_SIZE, + "Joins that are probably going to read more than max_join_size records return an error.", + (gptr*) &max_join_size, (gptr*) &max_join_size, 0, GET_ULONG, REQUIRED_ARG, + ~0L, 1, ~0L, 0, 1, 0}, + {"max_sort_length", OPT_MAX_SORT_LENGTH, + "The number of bytes to use when sorting BLOB or TEXT values (only the first max_sort_length bytes of each value are used; the rest are ignored).", + (gptr*) &max_item_sort_length, (gptr*) &max_item_sort_length, 0, GET_ULONG, + REQUIRED_ARG, 1024, 4, 8192*1024L, 0, 1, 0}, + {"max_tmp_tables", OPT_MAX_TMP_TABLES, + "Maximum number of temporary tables a client can keep open at a time.", + (gptr*) &max_tmp_tables, (gptr*) &max_tmp_tables, 0, GET_ULONG, + REQUIRED_ARG, 32, 1, ~0L, 0, 1, 0}, + {"max_user_connections", OPT_MAX_USER_CONNECTIONS, + "The maximum number of active connections for a single user (0 = no limit).", + (gptr*) &max_user_connections, (gptr*) &max_user_connections, 0, GET_ULONG, + REQUIRED_ARG, 0, 1, ~0L, 0, 1, 0}, + {"max_write_lock_count", OPT_MAX_WRITE_LOCK_COUNT, + "After this many write locks, allow some read locks to run in between.", + (gptr*) &max_write_lock_count, (gptr*) &max_write_lock_count, 0, GET_ULONG, + REQUIRED_ARG, ~0L, 1, ~0L, 0, 1, 0}, + {"myisam_bulk_insert_tree_size", OPT_MYISAM_BULK_INSERT_TREE_SIZE, + "Size of tree cache used in bulk insert optimisation. Note that this is a limit per thread!", + (gptr*) &myisam_bulk_insert_tree_size, + (gptr*) &myisam_bulk_insert_tree_size, 0, GET_ULONG, REQUIRED_ARG, + 8192*1024, 0, ~0L, 0, 1, 0}, + {"myisam_block_size", OPT_MYISAM_BLOCK_SIZE, + "Undocumented", (gptr*) &opt_myisam_block_size, + (gptr*) &opt_myisam_block_size, 0, GET_ULONG, REQUIRED_ARG, + MI_KEY_BLOCK_LENGTH, MI_MIN_KEY_BLOCK_LENGTH, MI_MAX_KEY_BLOCK_LENGTH, + 0, MI_MIN_KEY_BLOCK_LENGTH, 0}, + {"myisam_max_extra_sort_file_size", OPT_MYISAM_MAX_EXTRA_SORT_FILE_SIZE, + "Used to help MySQL to decide when to use the slow but safe key cache index create method. Note that this parameter is given in megabytes!", + (gptr*) &myisam_max_extra_sort_file_size, + (gptr*) &myisam_max_extra_sort_file_size, 0, GET_ULONG, REQUIRED_ARG, + (long) (MI_MAX_TEMP_LENGTH/(1024L*1024L)), 0, ~0L, 0, 1, 0}, + {"myisam_max_sort_file_size", OPT_MYISAM_MAX_SORT_FILE_SIZE, + "Don't use the fast sort index method to created index if the temporary file would get bigger than this. Note that this paramter is given in megabytes!", + (gptr*) &myisam_max_sort_file_size, (gptr*) &myisam_max_sort_file_size, 0, + GET_ULONG, REQUIRED_ARG, (long) (LONG_MAX/(1024L*1024L)), 0, ~0L, 0, 1, 0}, + {"myisam_sort_buffer_size", OPT_MYISAM_SORT_BUFFER_SIZE, + "The buffer that is allocated when sorting the index when doing a REPAIR or when creating indexes with CREATE INDEX or ALTER TABLE.", + (gptr*) &myisam_sort_buffer_size, (gptr*) &myisam_sort_buffer_size, 0, + GET_ULONG, REQUIRED_ARG, 8192*1024, 4, ~0L, 0, 1, 0}, + {"net_buffer_length", OPT_NET_BUFFER_LENGTH, + "Buffer for TCP/IP and socket communication.", (gptr*) &net_buffer_length, + (gptr*) &net_buffer_length, 0, GET_ULONG, REQUIRED_ARG, 16384, 1024, + 1024*1024L, MALLOC_OVERHEAD, 1024, 0}, + {"net_retry_count", OPT_NET_RETRY_COUNT, + "If a read on a communication port is interrupted, retry this many times before giving up.", + (gptr*) &mysqld_net_retry_count, (gptr*) &mysqld_net_retry_count, 0, + GET_ULONG, REQUIRED_ARG, MYSQLD_NET_RETRY_COUNT, 1, ~0L, 0, 1, 0}, + {"net_read_timeout", OPT_NET_READ_TIMEOUT, + "Number of seconds to wait for more data from a connection before aborting the read.", + (gptr*) &net_read_timeout, (gptr*) &net_read_timeout, 0, GET_ULONG, + REQUIRED_ARG, NET_READ_TIMEOUT, 1, LONG_TIMEOUT, 0, 1, 0}, + {"net_write_timeout", OPT_NET_WRITE_TIMEOUT, + "Number of seconds to wait for a block to be written to a connection before aborting the write.", + (gptr*) &net_write_timeout, (gptr*) &net_write_timeout, 0, GET_ULONG, + REQUIRED_ARG, NET_WRITE_TIMEOUT, 1, LONG_TIMEOUT, 0, 1, 0}, + {"open_files_limit", OPT_OPEN_FILES_LIMIT, + "If this is not 0, then mysqld will use this value to reserve file descriptors to use with setrlimit(). If this value is 0 then mysqld will reserve max_connections*5 or max_connections + table_cache*2 (whichever is larger) number of files.", + (gptr*) &open_files_limit, (gptr*) &open_files_limit, 0, GET_ULONG, + REQUIRED_ARG, 0, 0, 65535, 0, 1, 0}, + {"query_buffer_size", OPT_QUERY_BUFFER_SIZE, + "The initial allocation of the query buffer.", (gptr*) &query_buff_size, + (gptr*) &query_buff_size, 0, GET_ULONG, REQUIRED_ARG, 0, MALLOC_OVERHEAD, + (long) ~0, MALLOC_OVERHEAD, IO_SIZE, 0}, +#ifdef HAVE_QUERY_CACHE + {"query_cache_limit", OPT_QUERY_CACHE_LIMIT, + "Don't cache results that are bigger than this.", + (gptr*) &query_cache_limit, (gptr*) &query_cache_limit, 0, GET_ULONG, + REQUIRED_ARG, 1024*1024L, 0, ULONG_MAX, 0, 1, 0}, +#endif /*HAVE_QUERY_CACHE*/ + {"query_cache_size", OPT_QUERY_CACHE_SIZE, + "The memory allocated to store results from old queries.", + (gptr*) &query_cache_size, (gptr*) &query_cache_size, 0, GET_ULONG, + REQUIRED_ARG, 0, 0, ULONG_MAX, 0, 1, 0}, +#ifdef HAVE_QUERY_CACHE + {"query_cache_startup_type", OPT_QUERY_CACHE_STARTUP_TYPE, + "0 = OFF = Don't cache or retrieve results. 1 = ON = Cache all results except SELECT SQL_NO_CACHE ... queries. 2 = DEMAND = Cache only SELECT SQL_CACHE ... queries.", + (gptr*) &query_cache_startup_type, (gptr*) &query_cache_startup_type, 0, + GET_ULONG, REQUIRED_ARG, 1, 0, 2, 0, 1, 0}, +#endif /*HAVE_QUERY_CACHE*/ + {"record_buffer", OPT_RECORD_BUFFER, + "Each thread that does a sequential scan allocates a buffer of this size for each table it scans. If you do many sequential scans, you may want to increase this value.", + (gptr*) &my_default_record_cache_size, + (gptr*) &my_default_record_cache_size, 0, GET_ULONG, REQUIRED_ARG, + 128*1024L, IO_SIZE*2+MALLOC_OVERHEAD, ~0L, MALLOC_OVERHEAD, IO_SIZE, 0}, + {"record_rnd_buffer", OPT_RECORD_RND_BUFFER, + "When reading rows in sorted order after a sort, the rows are read through this buffer to avoid a disk seeks. If not set, then it's set to the value of record_buffer.", + (gptr*) &record_rnd_cache_size, (gptr*) &record_rnd_cache_size, 0, + GET_ULONG, REQUIRED_ARG, 0, IO_SIZE*2+MALLOC_OVERHEAD, + ~0L, MALLOC_OVERHEAD, IO_SIZE, 0}, + {"relay_log_space_limit", OPT_RELAY_LOG_SPACE_LIMIT, + "Undocumented", (gptr*) &relay_log_space_limit, + (gptr*) &relay_log_space_limit, 0, GET_ULONG, REQUIRED_ARG, 0L, 0L, + ULONG_MAX, 0, 1, 0}, + {"slave_net_timeout", OPT_SLAVE_NET_TIMEOUT, + "Undocumented", (gptr*) &slave_net_timeout, (gptr*) &slave_net_timeout, 0, + GET_ULONG, REQUIRED_ARG, SLAVE_NET_TIMEOUT, 1, LONG_TIMEOUT, 0, 1, 0}, + {"slow_launch_time", OPT_SLOW_LAUNCH_TIME, + "If creating the thread takes longer than this value (in seconds), the Slow_launch_threads counter will be incremented.", + (gptr*) &slow_launch_time, (gptr*) &slow_launch_time, 0, GET_ULONG, + REQUIRED_ARG, 2L, 0L, LONG_TIMEOUT, 0, 1, 0}, + {"sort_buffer", OPT_SORT_BUFFER, + "Each thread that needs to do a sort allocates a buffer of this size.", + (gptr*) &sortbuff_size, (gptr*) &sortbuff_size, 0, GET_ULONG, REQUIRED_ARG, + MAX_SORT_MEMORY, MIN_SORT_MEMORY+MALLOC_OVERHEAD*2, ~0L, MALLOC_OVERHEAD, + 1, 0}, + {"table_cache", OPT_TABLE_CACHE, + "The number of open tables for all threads.", (gptr*) &table_cache_size, + (gptr*) &table_cache_size, 0, GET_ULONG, REQUIRED_ARG, 64, 1, 16384, 0, 1, + 0}, + {"thread_concurrency", OPT_THREAD_CONCURRENCY, + "Permits the application to give the threads system a hint for the desired number of threads that should be run at the same time.", + (gptr*) &concurrency, (gptr*) &concurrency, 0, GET_ULONG, REQUIRED_ARG, + DEFAULT_CONCURRENCY, 1, 512, 0, 1, 0}, + {"thread_cache_size", OPT_THREAD_CACHE_SIZE, + "How many threads we should keep in a cache for reuse.", + (gptr*) &thread_cache_size, (gptr*) &thread_cache_size, 0, GET_ULONG, + REQUIRED_ARG, 0, 0, 16384, 0, 1, 0}, + {"tmp_table_size", OPT_TMP_TABLE_SIZE, + "If an in-memory temporary table exceeds this size, MySQL will automatically convert it to an on-disk MyISAM table.", + (gptr*) &tmp_table_size, (gptr*) &tmp_table_size, 0, GET_ULONG, + REQUIRED_ARG, 32*1024*1024L, 1024, ~0L, 0, 1, 0}, + {"thread_stack", OPT_THREAD_STACK, + "The stack size for each thread.", (gptr*) &thread_stack, + (gptr*) &thread_stack, 0, GET_ULONG, REQUIRED_ARG,DEFAULT_THREAD_STACK, + 1024*32, ~0L, 0, 1024, 0}, + {"wait_timeout", OPT_WAIT_TIMEOUT, + "The number of seconds the server waits for activity on a connection before closing it", + (gptr*) &net_wait_timeout, (gptr*) &net_wait_timeout, 0, GET_ULONG, + REQUIRED_ARG, NET_WAIT_TIMEOUT, 1, LONG_TIMEOUT, 0, 1, 0}, + {0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0} }; - struct show_var_st init_vars[]= { {"back_log", (char*) &back_log, SHOW_LONG}, {"basedir", mysql_home, SHOW_CHAR}, @@ -2987,22 +3564,17 @@ 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}, -#ifdef HAVE_GEMINI_DB - {"gemini_buffer_cache", (char*) &gemini_buffer_cache, SHOW_LONG}, - {"gemini_connection_limit", (char*) &gemini_connection_limit, SHOW_LONG}, - {"gemini_io_threads", (char*) &gemini_io_threads, SHOW_LONG}, - {"gemini_log_cluster_size", (char*) &gemini_log_cluster_size, SHOW_LONG}, - {"gemini_lock_table_size", (char*) &gemini_locktablesize, SHOW_LONG}, - {"gemini_lock_wait_timeout",(char*) &gemini_lock_wait_timeout, SHOW_LONG}, - {"gemini_recovery_options", (char*) &gemini_recovery_options_str, SHOW_CHAR_PTR}, - {"gemini_spin_retries", (char*) &gemini_spin_retries, SHOW_LONG}, -#endif + {"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}, + {"ft_boolean_syntax", (char*) ft_boolean_syntax, SHOW_CHAR}, {"have_bdb", (char*) &have_berkeley_db, SHOW_HAVE}, - {"have_gemini", (char*) &have_gemini, SHOW_HAVE}, {"have_innodb", (char*) &have_innodb, SHOW_HAVE}, {"have_isam", (char*) &have_isam, SHOW_HAVE}, {"have_raid", (char*) &have_raid, SHOW_HAVE}, - {"have_openssl", (char*) &have_ssl, SHOW_HAVE}, + {"have_symlink", (char*) &have_symlink, SHOW_HAVE}, + {"have_openssl", (char*) &have_openssl, SHOW_HAVE}, + {"have_query_cache", (char*) &have_query_cache, SHOW_HAVE}, {"init_file", (char*) &opt_init_file, SHOW_CHAR_PTR}, #ifdef HAVE_INNOBASE_DB {"innodb_additional_mem_pool_size", (char*) &innobase_additional_mem_pool_size, SHOW_LONG }, @@ -3036,7 +3608,7 @@ struct show_var_st init_vars[]= { {"log_update", (char*) &opt_update_log, SHOW_BOOL}, {"log_bin", (char*) &opt_bin_log, SHOW_BOOL}, {"log_slave_updates", (char*) &opt_log_slave_updates, SHOW_BOOL}, - {"log_long_queries", (char*) &opt_slow_log, SHOW_BOOL}, + {"log_slow_queries", (char*) &opt_slow_log, SHOW_BOOL}, {"long_query_time", (char*) &long_query_time, SHOW_LONG}, {"low_priority_updates", (char*) &low_priority_updates, SHOW_BOOL}, {"lower_case_table_names", (char*) &lower_case_table_names, SHOW_LONG}, @@ -3052,10 +3624,11 @@ struct show_var_st init_vars[]= { {"max_user_connections", (char*) &max_user_connections, SHOW_LONG}, {"max_tmp_tables", (char*) &max_tmp_tables, SHOW_LONG}, {"max_write_lock_count", (char*) &max_write_lock_count, SHOW_LONG}, + {"myisam_bulk_insert_tree_size", (char*) &myisam_bulk_insert_tree_size, SHOW_INT}, {"myisam_max_extra_sort_file_size", (char*) &myisam_max_extra_sort_file_size, SHOW_LONG}, {"myisam_max_sort_file_size",(char*) &myisam_max_sort_file_size, SHOW_LONG}, - {"myisam_recover_options", (char*) &myisam_recover_options, SHOW_LONG}, + {"myisam_recover_options", (char*) &myisam_recover_options_str, SHOW_CHAR_PTR}, {"myisam_sort_buffer_size", (char*) &myisam_sort_buffer_size, SHOW_LONG}, #ifdef __NT__ {"named_pipe", (char*) &opt_enable_named_pipe, SHOW_BOOL}, @@ -3070,7 +3643,13 @@ struct show_var_st init_vars[]= { {"protocol_version", (char*) &protocol_version, SHOW_INT}, {"record_buffer", (char*) &my_default_record_cache_size,SHOW_LONG}, {"record_rnd_buffer", (char*) &record_rnd_cache_size, SHOW_LONG}, + {"rpl_recovery_rank", (char*) &rpl_recovery_rank, SHOW_LONG}, {"query_buffer_size", (char*) &query_buff_size, SHOW_LONG}, +#ifdef HAVE_QUERY_CACHE + {"query_cache_limit", (char*) &query_cache.query_cache_limit, SHOW_LONG}, + {"query_cache_size", (char*) &query_cache.query_cache_size, SHOW_LONG}, + {"query_cache_startup_type",(char*) &query_cache_startup_type, SHOW_LONG}, +#endif /*HAVE_QUERY_CACHE*/ {"safe_show_database", (char*) &opt_safe_show_db, SHOW_BOOL}, {"server_id", (char*) &server_id, SHOW_LONG}, {"slave_net_timeout", (char*) &slave_net_timeout, SHOW_LONG}, @@ -3118,16 +3697,21 @@ struct show_var_st status_vars[]= { {"Com_create_index", (char*) (com_stat+(uint) SQLCOM_CREATE_INDEX),SHOW_LONG}, {"Com_create_table", (char*) (com_stat+(uint) SQLCOM_CREATE_TABLE),SHOW_LONG}, {"Com_delete", (char*) (com_stat+(uint) SQLCOM_DELETE),SHOW_LONG}, + {"Com_delete_multi", (char*) (com_stat+(uint) SQLCOM_DELETE_MULTI),SHOW_LONG}, {"Com_drop_db", (char*) (com_stat+(uint) SQLCOM_DROP_DB),SHOW_LONG}, {"Com_drop_function", (char*) (com_stat+(uint) SQLCOM_DROP_FUNCTION),SHOW_LONG}, {"Com_drop_index", (char*) (com_stat+(uint) SQLCOM_DROP_INDEX),SHOW_LONG}, {"Com_drop_table", (char*) (com_stat+(uint) SQLCOM_DROP_TABLE),SHOW_LONG}, {"Com_flush", (char*) (com_stat+(uint) SQLCOM_FLUSH),SHOW_LONG}, {"Com_grant", (char*) (com_stat+(uint) SQLCOM_GRANT),SHOW_LONG}, + {"Com_ha_close", (char*) (com_stat+(uint) SQLCOM_HA_CLOSE),SHOW_LONG}, + {"Com_ha_open", (char*) (com_stat+(uint) SQLCOM_HA_OPEN),SHOW_LONG}, + {"Com_ha_read", (char*) (com_stat+(uint) SQLCOM_HA_READ),SHOW_LONG}, {"Com_insert", (char*) (com_stat+(uint) SQLCOM_INSERT),SHOW_LONG}, {"Com_insert_select", (char*) (com_stat+(uint) SQLCOM_INSERT_SELECT),SHOW_LONG}, {"Com_kill", (char*) (com_stat+(uint) SQLCOM_KILL),SHOW_LONG}, {"Com_load", (char*) (com_stat+(uint) SQLCOM_LOAD),SHOW_LONG}, + {"Com_load_master_data", (char*) (com_stat+(uint) SQLCOM_LOAD_MASTER_DATA),SHOW_LONG}, {"Com_load_master_table", (char*) (com_stat+(uint) SQLCOM_LOAD_MASTER_TABLE),SHOW_LONG}, {"Com_lock_tables", (char*) (com_stat+(uint) SQLCOM_LOCK_TABLES),SHOW_LONG}, {"Com_optimize", (char*) (com_stat+(uint) SQLCOM_OPTIMIZE),SHOW_LONG}, @@ -3142,6 +3726,7 @@ struct show_var_st status_vars[]= { {"Com_rollback", (char*) (com_stat+(uint) SQLCOM_ROLLBACK),SHOW_LONG}, {"Com_select", (char*) (com_stat+(uint) SQLCOM_SELECT),SHOW_LONG}, {"Com_set_option", (char*) (com_stat+(uint) SQLCOM_SET_OPTION),SHOW_LONG}, + {"Com_show_binlog_events", (char*) (com_stat+(uint) SQLCOM_SHOW_BINLOG_EVENTS),SHOW_LONG}, {"Com_show_binlogs", (char*) (com_stat+(uint) SQLCOM_SHOW_BINLOGS),SHOW_LONG}, {"Com_show_create", (char*) (com_stat+(uint) SQLCOM_SHOW_CREATE),SHOW_LONG}, {"Com_show_databases", (char*) (com_stat+(uint) SQLCOM_SHOW_DATABASES),SHOW_LONG}, @@ -3150,8 +3735,10 @@ struct show_var_st status_vars[]= { {"Com_show_keys", (char*) (com_stat+(uint) SQLCOM_SHOW_KEYS),SHOW_LONG}, {"Com_show_logs", (char*) (com_stat+(uint) SQLCOM_SHOW_LOGS),SHOW_LONG}, {"Com_show_master_status", (char*) (com_stat+(uint) SQLCOM_SHOW_MASTER_STAT),SHOW_LONG}, + {"Com_show_new_master", (char*) (com_stat+(uint) SQLCOM_SHOW_NEW_MASTER),SHOW_LONG}, {"Com_show_open_tables", (char*) (com_stat+(uint) SQLCOM_SHOW_OPEN_TABLES),SHOW_LONG}, {"Com_show_processlist", (char*) (com_stat+(uint) SQLCOM_SHOW_PROCESSLIST),SHOW_LONG}, + {"Com_show_slave_hosts", (char*) (com_stat+(uint) SQLCOM_SHOW_SLAVE_HOSTS),SHOW_LONG}, {"Com_show_slave_status", (char*) (com_stat+(uint) SQLCOM_SHOW_SLAVE_STAT),SHOW_LONG}, {"Com_show_status", (char*) (com_stat+(uint) SQLCOM_SHOW_STATUS),SHOW_LONG}, {"Com_show_tables", (char*) (com_stat+(uint) SQLCOM_SHOW_TABLES),SHOW_LONG}, @@ -3169,6 +3756,7 @@ struct show_var_st status_vars[]= { {"Delayed_writes", (char*) &delayed_insert_writes, SHOW_LONG}, {"Delayed_errors", (char*) &delayed_insert_errors, SHOW_LONG}, {"Flush_commands", (char*) &refresh_version, SHOW_LONG_CONST}, + {"Handler_commit", (char*) &ha_commit_count, SHOW_LONG}, {"Handler_delete", (char*) &ha_delete_count, SHOW_LONG}, {"Handler_read_first", (char*) &ha_read_first_count, SHOW_LONG}, {"Handler_read_key", (char*) &ha_read_key_count, SHOW_LONG}, @@ -3176,6 +3764,7 @@ struct show_var_st status_vars[]= { {"Handler_read_prev", (char*) &ha_read_prev_count, SHOW_LONG}, {"Handler_read_rnd", (char*) &ha_read_rnd_count, SHOW_LONG}, {"Handler_read_rnd_next", (char*) &ha_read_rnd_next_count, SHOW_LONG}, + {"Handler_rollback", (char*) &ha_rollback_count, SHOW_LONG}, {"Handler_update", (char*) &ha_update_count, SHOW_LONG}, {"Handler_write", (char*) &ha_write_count, SHOW_LONG}, {"Key_blocks_used", (char*) &_my_blocks_used, SHOW_LONG_CONST}, @@ -3191,19 +3780,57 @@ struct show_var_st status_vars[]= { {"Open_streams", (char*) &my_stream_opened, SHOW_INT_CONST}, {"Opened_tables", (char*) &opened_tables, SHOW_LONG}, {"Questions", (char*) 0, SHOW_QUESTION}, +#ifdef HAVE_QUERY_CACHE + {"Qcache_queries_in_cache", (char*) &query_cache.queries_in_cache, SHOW_LONG_CONST}, + {"Qcache_inserts", (char*) &query_cache.inserts, SHOW_LONG}, + {"Qcache_hits", (char*) &query_cache.hits, SHOW_LONG}, + {"Qcache_not_cached", (char*) &query_cache.refused, SHOW_LONG}, + {"Qcache_free_memory", (char*) &query_cache.free_memory, + SHOW_LONG_CONST}, + {"Qcache_free_blocks", (char*) &query_cache.free_memory_blocks, + SHOW_LONG_CONST}, + {"Qcache_total_blocks", (char*) &query_cache.total_blocks, + SHOW_LONG_CONST}, +#endif /*HAVE_QUERY_CACHE*/ + {"Rpl_status", (char*) 0, SHOW_RPL_STATUS}, {"Select_full_join", (char*) &select_full_join_count, SHOW_LONG}, {"Select_full_range_join", (char*) &select_full_range_join_count, SHOW_LONG}, {"Select_range", (char*) &select_range_count, SHOW_LONG}, {"Select_range_check", (char*) &select_range_check_count, SHOW_LONG}, {"Select_scan", (char*) &select_scan_count, SHOW_LONG}, - {"Slave_running", (char*) &slave_running, SHOW_BOOL}, {"Slave_open_temp_tables", (char*) &slave_open_temp_tables, SHOW_LONG}, + {"Slave_running", (char*) 0, SHOW_SLAVE_RUNNING}, {"Slow_launch_threads", (char*) &slow_launch_threads, SHOW_LONG}, {"Slow_queries", (char*) &long_query_count, SHOW_LONG}, {"Sort_merge_passes", (char*) &filesort_merge_passes, SHOW_LONG}, {"Sort_range", (char*) &filesort_range_count, SHOW_LONG}, {"Sort_rows", (char*) &filesort_rows, SHOW_LONG}, {"Sort_scan", (char*) &filesort_scan_count, SHOW_LONG}, +#ifdef HAVE_OPENSSL + {"Ssl_accepts", (char*) 0, SHOW_SSL_CTX_SESS_ACCEPT}, + {"Ssl_finished_accepts", (char*) 0, SHOW_SSL_CTX_SESS_ACCEPT_GOOD}, + {"Ssl_finished_connects", (char*) 0, SHOW_SSL_CTX_SESS_CONNECT_GOOD}, + {"Ssl_accept_renegotiates", (char*) 0, SHOW_SSL_CTX_SESS_ACCEPT_RENEGOTIATE}, + {"Ssl_connect_renegotiates", (char*) 0, SHOW_SSL_CTX_SESS_CONNECT_RENEGOTIATE}, + {"Ssl_callback_cache_hits", (char*) 0, SHOW_SSL_CTX_SESS_CB_HITS}, + {"Ssl_session_cache_hits", (char*) 0, SHOW_SSL_CTX_SESS_HITS}, + {"Ssl_session_cache_misses", (char*) 0, SHOW_SSL_CTX_SESS_MISSES}, + {"Ssl_session_cache_timeouts", (char*) 0, SHOW_SSL_CTX_SESS_TIMEOUTS}, + {"Ssl_used_session_cache_entries",(char*) 0, SHOW_SSL_CTX_SESS_NUMBER}, + {"Ssl_client_connects", (char*) 0, SHOW_SSL_CTX_SESS_CONNECT}, + {"Ssl_session_cache_overflows", (char*) 0, SHOW_SSL_CTX_SESS_CACHE_FULL}, + {"Ssl_session_cache_size", (char*) 0, SHOW_SSL_CTX_SESS_GET_CACHE_SIZE}, + {"Ssl_session_cache_mode", (char*) 0, SHOW_SSL_CTX_GET_SESSION_CACHE_MODE}, + {"Ssl_sessions_reused", (char*) 0, SHOW_SSL_SESSION_REUSED}, + {"Ssl_ctx_verify_mode", (char*) 0, SHOW_SSL_CTX_GET_VERIFY_MODE}, + {"Ssl_ctx_verify_depth", (char*) 0, SHOW_SSL_CTX_GET_VERIFY_DEPTH}, + {"Ssl_verify_mode", (char*) 0, SHOW_SSL_GET_VERIFY_MODE}, + {"Ssl_verify_depth", (char*) 0, SHOW_SSL_GET_VERIFY_DEPTH}, + {"Ssl_version", (char*) 0, SHOW_SSL_GET_VERSION}, + {"Ssl_cipher", (char*) 0, SHOW_SSL_GET_CIPHER}, + {"Ssl_cipher_list", (char*) 0, SHOW_SSL_GET_CIPHER_LIST}, + {"Ssl_default_timeout", (char*) 0, SHOW_SSL_GET_DEFAULT_TIMEOUT}, +#endif /* HAVE_OPENSSL */ {"Table_locks_immediate", (char*) &locks_immediate, SHOW_LONG}, {"Table_locks_waited", (char*) &locks_waited, SHOW_LONG}, {"Threads_cached", (char*) &cached_thread_count, SHOW_LONG_CONST}, @@ -3224,7 +3851,7 @@ static void use_help(void) { print_version(); printf("Use '--help' or '--no-defaults --help' for a list of available options\n"); -} +} static void usage(void) { @@ -3236,161 +3863,28 @@ and you are welcome to modify and redistribute it under the GPL license\n\ Starts the MySQL server\n"); printf("Usage: %s [OPTIONS]\n", my_progname); - puts("\n\ - --ansi Use ANSI SQL syntax instead of MySQL syntax\n\ - -b, --basedir=path Path to installation directory. All paths are\n\ - usually resolved relative to this\n\ - --big-tables Allow big result sets by saving all temporary sets\n\ - on file (Solves most 'table full' errors)\n\ - --bind-address=IP Ip address to bind to\n\ - --bootstrap Used by mysql installation scripts\n\ - --character-sets-dir=...\n\ - Directory where character sets are\n\ - --chroot=path Chroot mysqld daemon during startup\n\ - --core-file Write core on errors\n\ - -h, --datadir=path Path to the database root"); -#ifndef DBUG_OFF - printf("\ - -#, --debug[=...] Debug log. Default is '%s'\n",default_dbug_option); -#ifdef SAFEMALLOC - puts("\ - --skip-safemalloc Don't use the memory allocation checking"); -#endif -#endif - puts("\ - --default-character-set=charset\n\ - Set the default character set\n\ - --default-table-type=type\n\ - Set the default table type for tables\n\ - --delay-key-write-for-all-tables\n\ - Don't flush key buffers between writes for any MyISAM\n\ - table\n\ - --enable-locking Enable system locking\n\ - -T, --exit-info Used for debugging; Use at your own risk!\n\ - --flush Flush tables to disk between SQL commands\n\ - -?, --help Display this help and exit\n\ - --init-file=file Read SQL commands from this file at startup\n\ - -L, --language=... Client error messages in given language. May be\n\ - given as a full path\n\ - --local-infile=[1|0] Enable/disable LOAD DATA LOCAL INFILE\n\ - -l, --log[=file] Log connections and queries to file\n\ - --log-bin[=file] Log queries in new binary format (for replication)\n\ - --log-bin-index=file File that holds the names for last binary log files\n\ - --log-update[=file] Log updates to file.# where # is a unique number\n\ - if not given.\n\ - --log-isam[=file] Log all MyISAM changes to file\n\ - --log-long-format Log some extra information to update log\n\ - --low-priority-updates INSERT/DELETE/UPDATE has lower priority than selects\n\ - --log-slow-queries=[file]\n\ - Log slow queries to this log file. Defaults logging\n\ - to hostname-slow.log\n\ - --pid-file=path Pid file used by safe_mysqld\n\ - --myisam-recover[=option[,option...]] where options is one of DEAULT,\n\ - BACKUP or FORCE.\n\ - --memlock Lock mysqld in memory\n\ - -n, --new Use very new possible 'unsafe' functions\n\ - -o, --old-protocol Use the old (3.20) protocol\n\ - -P, --port=... Port number to use for connection\n"); -#ifdef ONE_THREAD - puts("\ - --one-thread Only use one thread (for debugging under Linux)\n"); -#endif - 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\ - --safe-user-create Don't new users cretaion without privileges to the\n\ - mysql.user table\n\ - --skip-concurrent-insert\n\ - Don't use concurrent insert with MyISAM\n\ - --skip-delay-key-write\n\ - Ignore the delay_key_write option for 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\ - --skip-name-resolve Don't resolve hostnames.\n\ - All hostnames are IP's or 'localhost'\n\ - --skip-networking Don't allow connection with TCP/IP.\n\ - --skip-new Don't use new, possible wrong routines.\n"); - /* 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-show-database Don't allow 'SHOW DATABASE' commands\n\ - --skip-thread-priority\n\ - Don't give threads different priorities.\n\ - --socket=... Socket file to use for connection\n\ - -t, --tmpdir=path Path for temporary files\n\ - --sql-mode=option[,option[,option...]] where option can be one of:\n\ - REAL_AS_FLOAT, PIPES_AS_CONCAT, ANSI_QUOTES,\n\ - IGNORE_SPACE, SERIALIZE, ONLY_FULL_GROUP_BY.\n\ - --transaction-isolation\n\ - Default transaction isolation level\n\ - --temp-pool Use a pool of temporary files\n\ - -u, --user=user_name Run mysqld daemon as user\n\ - -V, --version output version information and exit\n\ - -W, --warnings Log some not critical warnings to the log file\n"); #ifdef __WIN__ puts("NT and Win32 specific options:\n\ --console Don't remove the console window\n\ --install Install the default service (NT)\n\ --install-manual Install the default service started manually (NT)\n\ + --install service_name Install an optional service (NT)\n\ + --install-manual service_name Install an optional service started manually (NT)\n\ --remove Remove the default service from the service list (NT)\n\ - --enable-named-pipe Enable the named pipe (NT)\n\ - --standalone Dummy option to start as a standalone program (NT)\ + --remove service_name Remove the service_name from the service list (NT)\n\ + --enable-named-pipe Only to be used for the default server (NT)\n\ + --standalone Dummy option to start as a standalone server (NT)\ "); -#ifdef USE_SYMDIR - puts("--use-symbolic-links Enable symbolic link support"); -#endif puts(""); #endif -#ifdef HAVE_BERKELEY_DB - puts("\ - --bdb-home= directory Berkeley home direcory\n\ - --bdb-lock-detect=# Berkeley lock detect\n\ - (DEFAULT, OLDEST, RANDOM or YOUNGEST, # sec)\n\ - --bdb-logdir=directory Berkeley DB log file directory\n\ - --bdb-no-sync Don't synchronously flush logs\n\ - --bdb-no-recover Don't try to recover Berkeley DB tables on start\n\ - --bdb-shared-data Start Berkeley DB in multi-process mode\n\ - --bdb-tmpdir=directory Berkeley DB tempfile name\n\ - --skip-bdb Don't use berkeley db (will save memory)\n\ -"); -#endif /* HAVE_BERKELEY_DB */ -#ifdef HAVE_GEMINI_DB - puts("\ - --gemini-recovery=mode Set Crash Recovery operating mode\n\ - (FULL, NONE, FORCE - default FULL)\n\ - --gemini-flush-log-at-commit\n\ - Every commit forces a write to the reovery log\n\ - --gemini-unbuffered-io Use unbuffered i/o\n\ - --skip-gemini Don't use gemini (will save memory)\n\ -"); -#endif -#ifdef HAVE_INNOBASE_DB - puts("\ - --innodb_data_home_dir=dir The common part for Innodb table spaces\n\ - --innodb_data_file_path=dir Path to individual files and their sizes\n\ - --innodb_flush_method=# With which method to flush data\n\ - --innodb_flush_log_at_trx_commit[=#]\n\ - Set to 0 if you don't want to flush logs\n\ - --innodb_log_arch_dir=dir Where full logs should be archived\n\ - --innodb_log_archive[=#] Set to 1 if you want to have logs archived\n\ - --innodb_log_group_home_dir=dir Path to innodb log files.\n\ - --skip-innodb Don't use Innodb (will save memory)\n\ -"); -#endif /* HAVE_INNOBASE_DB */ print_defaults("my",load_default_groups); puts(""); - -#include "sslopt-usage.h" - fix_paths(); set_ports(); + + my_print_help(my_long_options); + my_print_variables(my_long_options); + printf("\ To see what values a running MySQL server is using, type\n\ 'mysqladmin variables' instead of 'mysqld --help'.\n\ @@ -3423,17 +3917,11 @@ The default values (after parsing the command line arguments) are:\n\n"); puts("\nsystem locking is not in use"); if (opt_noacl) puts("\nGrant tables are not used. All users have full access rights"); - printf("\nPossible variables for option --set-variable (-O) are:\n"); - for (uint i=0 ; changeable_vars[i].name ; i++) - printf("%-20s current value: %lu\n", - changeable_vars[i].name, - (ulong) *changeable_vars[i].varptr); } static void set_options(void) { - set_all_changeable_vars( changeable_vars ); #if !defined( my_pthread_setprio ) && !defined( HAVE_PTHREAD_SETSCHEDPARAM ) opt_specialflag |= SPECIAL_NO_PRIOR; #endif @@ -3458,578 +3946,448 @@ 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 ); } - /* Initiates DEBUG - but no debugging here ! */ -static void get_options(int argc,char **argv) +static my_bool +get_one_option(int optid, const struct my_option *opt __attribute__((unused)), + char *argument) { - int c,option_index=0; - - myisam_delay_key_write=1; // Allow use of this - while ((c=getopt_long(argc,argv,"ab:C:h:#::T::?l::L:O:P:sS::t:u:noVvWI?", - long_options, &option_index)) != EOF) - { - switch(c) { - case '#': + switch(optid) { + case '#': #ifndef DBUG_OFF - DBUG_PUSH(optarg ? optarg : default_dbug_option); -#endif - opt_endinfo=1; /* unireg: memory allocation */ - break; - case 'W': - opt_warnings=1; - break; - case 'a': - opt_sql_mode = (MODE_REAL_AS_FLOAT | MODE_PIPES_AS_CONCAT | - MODE_ANSI_QUOTES | MODE_IGNORE_SPACE | MODE_SERIALIZABLE - | MODE_ONLY_FULL_GROUP_BY); - default_tx_isolation= ISO_SERIALIZABLE; - break; - case 'b': - strmake(mysql_home,optarg,sizeof(mysql_home)-1); - break; - case 'l': - opt_log=1; - opt_logname=optarg; // Use hostname.log if null - break; - case 'h': - strmake(mysql_real_data_home,optarg, sizeof(mysql_real_data_home)-1); - break; - case 'L': - strmake(language, optarg, sizeof(language)-1); - break; - case 'n': - opt_specialflag|= SPECIAL_NEW_FUNC; - break; - case 'o': - protocol_version=PROTOCOL_VERSION-1; - break; - case 'O': - if (set_changeable_var(optarg, changeable_vars)) - { - use_help(); - exit(1); - } - break; - case 'P': - mysql_port= (unsigned int) atoi(optarg); - break; - case OPT_LOCAL_INFILE: - opt_local_infile= test(!optarg || atoi(optarg) != 0); - break; - case OPT_SLAVE_SKIP_ERRORS: - init_slave_skip_errors(optarg); - break; - case OPT_SAFEMALLOC_MEM_LIMIT: + DBUG_PUSH(argument ? argument : default_dbug_option); +#endif + opt_endinfo=1; /* unireg: memory allocation */ + break; + case 'a': + opt_sql_mode = (MODE_REAL_AS_FLOAT | MODE_PIPES_AS_CONCAT | + MODE_ANSI_QUOTES | MODE_IGNORE_SPACE | MODE_SERIALIZABLE + | MODE_ONLY_FULL_GROUP_BY); + default_tx_isolation= ISO_SERIALIZABLE; + break; + case 'b': + strmake(mysql_home,argument,sizeof(mysql_home)-1); + break; + case 'l': + opt_log=1; + break; + case 'h': + strmake(mysql_real_data_home,argument, sizeof(mysql_real_data_home)-1); + break; + case 'L': + strmake(language, argument, sizeof(language)-1); + break; + case 'n': + opt_specialflag|= SPECIAL_NEW_FUNC; + break; + case 'o': + protocol_version=PROTOCOL_VERSION-1; + break; + case OPT_LOCAL_INFILE: + opt_local_infile= test(!argument || atoi(argument) != 0); + break; + case OPT_SLAVE_SKIP_ERRORS: + init_slave_skip_errors(argument); + break; + case OPT_SAFEMALLOC_MEM_LIMIT: #if !defined(DBUG_OFF) && defined(SAFEMALLOC) - safemalloc_mem_limit = atoi(optarg); + safemalloc_mem_limit = atoi(argument); #endif - 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; - case OPT_TEMP_POOL: - use_temp_pool=1; - break; - case 'u': - mysqld_user=optarg; - break; - case 'v': - case 'V': - print_version(); - exit(0); - case 'I': - case '?': - usage(); - exit(0); - case 'T': - 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 + break; + case 'v': + case 'V': + print_version(); + exit(0); + case 'I': + case '?': + usage(); + exit(0); + case 'T': + test_flags= argument ? (uint) atoi(argument) : 0; + opt_endinfo=1; + break; + case (int) OPT_BIG_TABLES: + thd_startup_options|=OPTION_BIG_TABLES; + break; + case (int) OPT_ISAM_LOG: + opt_myisam_log=1; + break; + case (int) OPT_UPDATE_LOG: + opt_update_log=1; + break; + case (int) OPT_BIN_LOG: + opt_bin_log=1; + break; + case (int) OPT_INIT_RPL_ROLE: + { + int role; + if ((role=find_type(argument, &rpl_role_typelib, 2)) <= 0) { - fprintf(stderr,"%s: Unrecognized option: %s\n",my_progname,optarg); - use_help(); + fprintf(stderr, "Unknown replication role: %s\n", argument); exit(1); } + rpl_status = (role == 1) ? RPL_AUTH_MASTER : RPL_IDLE_SLAVE; break; - case (int) OPT_BIG_TABLES: - thd_startup_options|=OPTION_BIG_TABLES; - break; - case (int) OPT_ISAM_LOG: - opt_myisam_log=1; - if (optarg) - myisam_log_filename=optarg; - break; - case (int) OPT_UPDATE_LOG: - opt_update_log=1; - opt_update_logname=optarg; // Use hostname.# if null - break; - case (int) OPT_BIN_LOG_INDEX: - opt_binlog_index_name = optarg; - break; - case (int) OPT_BIN_LOG: - opt_bin_log=1; - x_free(opt_bin_logname); - if (optarg && optarg[0]) - opt_bin_logname=my_strdup(optarg,MYF(0)); - break; - // needs to be handled (as no-op) in non-debugging mode for test suite - case (int)OPT_DISCONNECT_SLAVE_EVENT_COUNT: -#ifndef DBUG_OFF - disconnect_slave_event_count = atoi(optarg); -#endif - break; - case (int)OPT_ABORT_SLAVE_EVENT_COUNT: -#ifndef DBUG_OFF - abort_slave_event_count = atoi(optarg); -#endif - break; - case (int)OPT_SPORADIC_BINLOG_DUMP_FAIL: -#ifndef DBUG_OFF - opt_sporadic_binlog_dump_fail = 1; -#endif - break; - case (int)OPT_MAX_BINLOG_DUMP_EVENTS: -#ifndef DBUG_OFF - max_binlog_dump_events = atoi(optarg); -#endif + } + case (int)OPT_REPLICATE_IGNORE_DB: + { + i_string *db = new i_string(argument); + replicate_ignore_db.push_back(db); break; - - case (int) OPT_LOG_SLAVE_UPDATES: - opt_log_slave_updates = 1; + } + case (int)OPT_REPLICATE_DO_DB: + { + i_string *db = new i_string(argument); + replicate_do_db.push_back(db); break; - - case (int)OPT_REPLICATE_IGNORE_DB: + } + case (int)OPT_REPLICATE_REWRITE_DB: + { + char* key = argument,*p, *val; + p = strstr(argument, "->"); + if (!p) { - i_string *db = new i_string(optarg); - replicate_ignore_db.push_back(db); - break; + fprintf(stderr, + "Bad syntax in replicate-rewrite-db - missing '->'!\n"); + exit(1); } - case (int)OPT_REPLICATE_DO_DB: + val = p--; + while(isspace(*p) && p > argument) *p-- = 0; + if(p == argument) { - i_string *db = new i_string(optarg); - replicate_do_db.push_back(db); - break; + fprintf(stderr, + "Bad syntax in replicate-rewrite-db - empty FROM db!\n"); + exit(1); } - case (int)OPT_REPLICATE_REWRITE_DB: + *val = 0; + val += 2; + while(*val && isspace(*val)) *val++; + if (!*val) { - char* key = optarg,*p, *val; - p = strstr(optarg, "->"); - if (!p) - { - fprintf(stderr, - "Bad syntax in replicate-rewrite-db - missing '->'!\n"); - exit(1); - } - val = p--; - while(isspace(*p) && p > optarg) *p-- = 0; - if(p == optarg) - { - fprintf(stderr, - "Bad syntax in replicate-rewrite-db - empty FROM db!\n"); - exit(1); - } - *val = 0; - val += 2; - while(*val && isspace(*val)) *val++; - if (!*val) - { - fprintf(stderr, - "Bad syntax in replicate-rewrite-db - empty TO db!\n"); - exit(1); - } - - i_string_pair* db_pair = new i_string_pair(key, val); - replicate_rewrite_db.push_back(db_pair); - break; + fprintf(stderr, + "Bad syntax in replicate-rewrite-db - empty TO db!\n"); + exit(1); } - case (int)OPT_BINLOG_IGNORE_DB: - { - i_string *db = new i_string(optarg); - binlog_ignore_db.push_back(db); - break; - } - case (int)OPT_BINLOG_DO_DB: - { - i_string *db = new i_string(optarg); - binlog_do_db.push_back(db); - break; - } - case (int)OPT_REPLICATE_DO_TABLE: - { - if (!do_table_inited) - init_table_rule_hash(&replicate_do_table, &do_table_inited); - if(add_table_rule(&replicate_do_table, optarg)) - { - fprintf(stderr, "Could not add do table rule '%s'!\n", optarg); - exit(1); - } - table_rules_on = 1; - break; - } - case (int)OPT_REPLICATE_WILD_DO_TABLE: + i_string_pair* db_pair = new i_string_pair(key, val); + replicate_rewrite_db.push_back(db_pair); + break; + } + + case (int)OPT_BINLOG_IGNORE_DB: + { + i_string *db = new i_string(argument); + binlog_ignore_db.push_back(db); + break; + } + case (int)OPT_BINLOG_DO_DB: + { + i_string *db = new i_string(argument); + binlog_do_db.push_back(db); + break; + } + case (int)OPT_REPLICATE_DO_TABLE: + { + if (!do_table_inited) + init_table_rule_hash(&replicate_do_table, &do_table_inited); + if(add_table_rule(&replicate_do_table, argument)) { - if (!wild_do_table_inited) - init_table_rule_array(&replicate_wild_do_table, - &wild_do_table_inited); - if(add_wild_table_rule(&replicate_wild_do_table, optarg)) - { - fprintf(stderr, "Could not add do table rule '%s'!\n", optarg); - exit(1); - } - table_rules_on = 1; - break; + fprintf(stderr, "Could not add do table rule '%s'!\n", argument); + exit(1); } - case (int)OPT_REPLICATE_WILD_IGNORE_TABLE: + table_rules_on = 1; + break; + } + case (int)OPT_REPLICATE_WILD_DO_TABLE: + { + if (!wild_do_table_inited) + init_table_rule_array(&replicate_wild_do_table, + &wild_do_table_inited); + if(add_wild_table_rule(&replicate_wild_do_table, argument)) { - if (!wild_ignore_table_inited) - init_table_rule_array(&replicate_wild_ignore_table, - &wild_ignore_table_inited); - if(add_wild_table_rule(&replicate_wild_ignore_table, optarg)) - { - fprintf(stderr, "Could not add ignore table rule '%s'!\n", optarg); - exit(1); - } - table_rules_on = 1; - break; + fprintf(stderr, "Could not add do table rule '%s'!\n", argument); + exit(1); } - case (int)OPT_REPLICATE_IGNORE_TABLE: + table_rules_on = 1; + break; + } + case (int)OPT_REPLICATE_WILD_IGNORE_TABLE: + { + if (!wild_ignore_table_inited) + init_table_rule_array(&replicate_wild_ignore_table, + &wild_ignore_table_inited); + if(add_wild_table_rule(&replicate_wild_ignore_table, argument)) { - if (!ignore_table_inited) - init_table_rule_hash(&replicate_ignore_table, &ignore_table_inited); - if(add_table_rule(&replicate_ignore_table, optarg)) - { - fprintf(stderr, "Could not add ignore table rule '%s'!\n", optarg); - exit(1); - } - table_rules_on = 1; - break; + fprintf(stderr, "Could not add ignore table rule '%s'!\n", argument); + exit(1); } - case (int) OPT_SQL_BIN_UPDATE_SAME: - opt_sql_bin_update = 1; - break; - case (int) OPT_SLOW_QUERY_LOG: - opt_slow_log=1; - opt_slow_logname=optarg; - break; - case (int)OPT_SKIP_SLAVE_START: - opt_skip_slave_start = 1; - break; - case (int) OPT_SKIP_NEW: - opt_specialflag|= SPECIAL_NO_NEW_FUNC; - default_table_type=DB_TYPE_ISAM; - myisam_delay_key_write=0; - myisam_concurrent_insert=0; - myisam_recover_options= HA_RECOVER_NONE; - my_disable_symlinks=1; - ha_open_options&= ~HA_OPEN_ABORT_IF_CRASHED; - break; - case (int) OPT_SAFE: - opt_specialflag|= SPECIAL_SAFE_MODE; - myisam_delay_key_write=0; - myisam_recover_options= HA_RECOVER_NONE; // To be changed - ha_open_options&= ~HA_OPEN_ABORT_IF_CRASHED; - break; - case (int) OPT_SKIP_CONCURRENT_INSERT: - myisam_concurrent_insert=0; + table_rules_on = 1; break; - case (int) OPT_SKIP_PRIOR: - opt_specialflag|= SPECIAL_NO_PRIOR; - break; - case (int) OPT_SKIP_GRANT: - opt_noacl=1; - break; - case (int) OPT_SKIP_LOCK: - my_disable_locking=1; - break; - case (int) OPT_SKIP_HOST_CACHE: - opt_specialflag|= SPECIAL_NO_HOST_CACHE; - break; - case (int) OPT_ENABLE_LOCK: - my_disable_locking=0; - break; - case (int) OPT_USE_LOCKING: - my_disable_locking=0; - break; - case (int) OPT_SKIP_RESOLVE: - opt_specialflag|=SPECIAL_NO_RESOLVE; - break; - case (int) OPT_LONG_FORMAT: - opt_specialflag|=SPECIAL_LONG_LOG_FORMAT; - break; - case (int) OPT_SKIP_NETWORKING: - opt_disable_networking=1; - mysql_port=0; - break; - case (int) OPT_SKIP_SHOW_DB: - opt_skip_show_db=1; - opt_specialflag|=SPECIAL_SKIP_SHOW_DB; - mysql_port=0; - break; - case (int) OPT_MEMLOCK: - locked_in_memory=1; - break; - case (int) OPT_ONE_THREAD: - test_flags |= TEST_NO_THREADS; - break; - case (int) OPT_WANT_CORE: - test_flags |= TEST_CORE_ON_SIGNAL; - break; - case (int) OPT_SKIP_STACK_TRACE: - test_flags|=TEST_NO_STACKTRACE; - break; - case (int) OPT_SKIP_SYMLINKS: - my_disable_symlinks=1; - break; - case (int) OPT_BIND_ADDRESS: - if (optarg && isdigit(optarg[0])) + } + case (int)OPT_REPLICATE_IGNORE_TABLE: + { + if (!ignore_table_inited) + init_table_rule_hash(&replicate_ignore_table, &ignore_table_inited); + if(add_table_rule(&replicate_ignore_table, argument)) { - my_bind_addr = (ulong) inet_addr(optarg); + fprintf(stderr, "Could not add ignore table rule '%s'!\n", argument); + exit(1); } + table_rules_on = 1; + break; + } + case (int) OPT_SLOW_QUERY_LOG: + opt_slow_log=1; + break; + case (int)OPT_RECKLESS_SLAVE: + opt_reckless_slave = 1; + init_slave_skip_errors("all"); + break; + case (int) OPT_SKIP_NEW: + opt_specialflag|= SPECIAL_NO_NEW_FUNC; + myisam_delay_key_write=0; + 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; +#ifdef HAVE_QUERY_CACHE + query_cache_size=0; +#endif + break; + case (int) OPT_SAFE: + opt_specialflag|= SPECIAL_SAFE_MODE; + myisam_delay_key_write=0; + myisam_recover_options= HA_RECOVER_NONE; // To be changed + ha_open_options&= ~HA_OPEN_ABORT_IF_CRASHED; + break; + case (int) OPT_SKIP_PRIOR: + opt_specialflag|= SPECIAL_NO_PRIOR; + break; + case (int) OPT_SKIP_LOCK: + my_disable_locking=myisam_single_user= 1; + break; + case (int) OPT_SKIP_HOST_CACHE: + opt_specialflag|= SPECIAL_NO_HOST_CACHE; + break; + case (int) OPT_ENABLE_LOCK: + my_disable_locking=myisam_single_user=0; + break; + case (int) OPT_USE_LOCKING: + my_disable_locking=0; + break; + case (int) OPT_SKIP_RESOLVE: + opt_specialflag|=SPECIAL_NO_RESOLVE; + break; + case (int) OPT_LONG_FORMAT: + opt_specialflag|=SPECIAL_LONG_LOG_FORMAT; + break; + case (int) OPT_SKIP_NETWORKING: + opt_disable_networking=1; + mysql_port=0; + break; + case (int) OPT_SKIP_SHOW_DB: + opt_skip_show_db=1; + opt_specialflag|=SPECIAL_SKIP_SHOW_DB; + mysql_port=0; + break; + case (int) OPT_ONE_THREAD: + test_flags |= TEST_NO_THREADS; + break; + case (int) OPT_WANT_CORE: + test_flags |= TEST_CORE_ON_SIGNAL; + break; + case (int) OPT_SKIP_STACK_TRACE: + test_flags|=TEST_NO_STACKTRACE; + break; + case (int) OPT_SKIP_SYMLINKS: + my_disable_symlinks=1; + my_use_symdir=0; + have_symlink=SHOW_OPTION_DISABLED; + break; + case (int) OPT_BIND_ADDRESS: + if (argument && isdigit(argument[0])) + { + my_bind_addr = (ulong) inet_addr(argument); + } + else + { + struct hostent *ent; + if (!argument || !argument[0]) + ent=gethostbyname(argument); else { - struct hostent *ent; - if (!optarg || !optarg[0]) - ent=gethostbyname(optarg); - else + char myhostname[255]; + if (gethostname(myhostname,sizeof(myhostname)) < 0) { - char myhostname[255]; - if (gethostname(myhostname,sizeof(myhostname)) < 0) - { - sql_perror("Can't start server: cannot get my own hostname!"); - exit(1); - } - ent=gethostbyname(myhostname); - } - if (!ent) - { - sql_perror("Can't start server: cannot resolve hostname!"); + sql_perror("Can't start server: cannot get my own hostname!"); exit(1); } - my_bind_addr = (ulong) ((in_addr*)ent->h_addr_list[0])->s_addr; + ent=gethostbyname(myhostname); } - break; - case (int) OPT_PID_FILE: - strmake(pidfile_name, optarg, sizeof(pidfile_name)-1); - break; - case (int) OPT_INIT_FILE: - opt_init_file=optarg; - break; - case (int) OPT_HAVE_NAMED_PIPE: -#if __NT__ - opt_enable_named_pipe=1; -#endif - break; + if (!ent) + { + sql_perror("Can't start server: cannot resolve hostname!"); + exit(1); + } + my_bind_addr = (ulong) ((in_addr*)ent->h_addr_list[0])->s_addr; + } + break; + case (int) OPT_PID_FILE: + strmake(pidfile_name, argument, sizeof(pidfile_name)-1); + break; #ifdef __WIN__ - case (int) OPT_STANDALONE: /* Dummy option for NT */ - break; - case (int) OPT_CONSOLE: - opt_console=1; - break; -#endif - case (int) OPT_FLUSH: - nisam_flush=myisam_flush=1; - flush_time=0; // No auto flush - break; - case OPT_LOW_PRIORITY_UPDATES: - thd_startup_options|=OPTION_LOW_PRIORITY_UPDATES; - thr_upgraded_concurrent_insert_lock= TL_WRITE_LOW_PRIORITY; - low_priority_updates=1; - break; - case OPT_BOOTSTRAP: - opt_noacl=opt_bootstrap=1; - break; - case OPT_TABLE_TYPE: + case (int) OPT_STANDALONE: /* Dummy option for NT */ + break; +#endif + case (int) OPT_FLUSH: +#ifdef HAVE_ISAM + nisam_flush=1; +#endif + myisam_flush=1; + flush_time=0; // No auto flush + break; + case OPT_LOW_PRIORITY_UPDATES: + thd_startup_options|=OPTION_LOW_PRIORITY_UPDATES; + thr_upgraded_concurrent_insert_lock= TL_WRITE_LOW_PRIORITY; + low_priority_updates=1; + break; + case OPT_BOOTSTRAP: + opt_noacl=opt_bootstrap=1; + break; + case OPT_TABLE_TYPE: { int type; - if ((type=find_type(optarg, &ha_table_typelib, 2)) <= 0) + if ((type=find_type(argument, &ha_table_typelib, 2)) <= 0) { - fprintf(stderr,"Unknown table type: %s\n",optarg); + fprintf(stderr,"Unknown table type: %s\n",argument); exit(1); } default_table_type= (enum db_type) type; break; } - case OPT_SERVER_ID: - server_id = atoi(optarg); - server_id_supplied = 1; - break; - case OPT_DELAY_KEY_WRITE: - ha_open_options|=HA_OPEN_DELAY_KEY_WRITE; - myisam_delay_key_write=1; - break; - case OPT_SKIP_DELAY_KEY_WRITE: - myisam_delay_key_write=0; - break; - case 'C': - strmake(default_charset, optarg, sizeof(default_charset)-1); - break; - case OPT_CHARSETS_DIR: - strmake(mysql_charsets_dir, optarg, sizeof(mysql_charsets_dir)-1); - charsets_dir = mysql_charsets_dir; - break; + case OPT_SERVER_ID: + server_id_supplied = 1; + break; + case OPT_DELAY_KEY_WRITE: + ha_open_options|=HA_OPEN_DELAY_KEY_WRITE; + myisam_delay_key_write=1; + break; + case 'C': + strmake(default_charset, argument, sizeof(default_charset)-1); + break; + case OPT_CHARSETS_DIR: + strmake(mysql_charsets_dir, argument, sizeof(mysql_charsets_dir)-1); + charsets_dir = mysql_charsets_dir; + break; +#ifdef HAVE_OPENSSL #include "sslopt-case.h" - case OPT_TX_ISOLATION: +#endif + case OPT_DES_KEY_FILE: +#ifdef HAVE_OPENSSL + des_key_file=argument; +#endif + break; + case OPT_TX_ISOLATION: { int type; - if ((type=find_type(optarg, &tx_isolation_typelib, 2)) <= 0) + if ((type=find_type(argument, &tx_isolation_typelib, 2)) <= 0) { - fprintf(stderr,"Unknown transaction isolation type: %s\n",optarg); + fprintf(stderr,"Unknown transaction isolation type: %s\n",argument); exit(1); } default_tx_isolation= (enum_tx_isolation) (type-1); break; } #ifdef HAVE_BERKELEY_DB - case OPT_BDB_LOG: - berkeley_logdir=optarg; - break; - case OPT_BDB_HOME: - berkeley_home=optarg; - break; - case OPT_BDB_NOSYNC: - berkeley_env_flags|=DB_TXN_NOSYNC; - break; - case OPT_BDB_NO_RECOVER: - berkeley_init_flags&= ~(DB_RECOVER); - break; - case OPT_BDB_TMP: - berkeley_tmpdir=optarg; - break; - case OPT_BDB_LOCK: + case OPT_BDB_NOSYNC: + berkeley_env_flags|=DB_TXN_NOSYNC; + break; + case OPT_BDB_NO_RECOVER: + berkeley_init_flags&= ~(DB_RECOVER); + break; + case OPT_BDB_LOCK: { int type; - if ((type=find_type(optarg, &berkeley_lock_typelib, 2)) > 0) + if ((type=find_type(argument, &berkeley_lock_typelib, 2)) > 0) berkeley_lock_type=berkeley_lock_types[type-1]; else { - if (test_if_int(optarg,(uint) strlen(optarg))) - berkeley_lock_scan_time=atoi(optarg); + if (test_if_int(argument,(uint) strlen(argument))) + berkeley_lock_scan_time=atoi(argument); else { - fprintf(stderr,"Unknown lock type: %s\n",optarg); + fprintf(stderr,"Unknown lock type: %s\n",argument); exit(1); } } break; } - case OPT_BDB_SHARED: - berkeley_init_flags&= ~(DB_PRIVATE); - berkeley_shared_data=1; - break; + case OPT_BDB_SHARED: + berkeley_init_flags&= ~(DB_PRIVATE); + berkeley_shared_data=1; + break; #endif /* HAVE_BERKELEY_DB */ - case OPT_BDB_SKIP: + case OPT_BDB_SKIP: #ifdef HAVE_BERKELEY_DB - berkeley_skip=1; - have_berkeley_db=SHOW_OPTION_DISABLED; + berkeley_skip=1; + have_berkeley_db=SHOW_OPTION_DISABLED; #endif - break; - case OPT_GEMINI_SKIP: -#ifdef HAVE_GEMINI_DB - gemini_skip=1; - have_gemini=SHOW_OPTION_DISABLED; - break; - case OPT_GEMINI_RECOVER: - gemini_recovery_options_str=optarg; - if ((gemini_recovery_options= - find_bit_type(optarg, &gemini_recovery_typelib)) == ~(ulong) 0) - { - fprintf(stderr, "Unknown option to gemini-recovery: %s\n",optarg); - exit(1); - } - break; - case OPT_GEMINI_FLUSH_LOG: - gemini_options |= GEMOPT_FLUSH_LOG; - break; - case OPT_GEMINI_UNBUFFERED_IO: - gemini_options |= GEMOPT_UNBUFFERED_IO; -#endif - break; - case OPT_INNODB_SKIP: + break; + case OPT_INNODB_SKIP: #ifdef HAVE_INNOBASE_DB - innodb_skip=1; - have_innodb=SHOW_OPTION_DISABLED; + innodb_skip=1; + have_innodb=SHOW_OPTION_DISABLED; #endif - break; - case OPT_INNODB_DATA_FILE_PATH: + break; + case OPT_INNODB_DATA_FILE_PATH: #ifdef HAVE_INNOBASE_DB - innobase_data_file_path=optarg; + innobase_data_file_path=argument; #endif - break; + break; #ifdef HAVE_INNOBASE_DB - case OPT_INNODB_DATA_HOME_DIR: - innobase_data_home_dir=optarg; - break; - case OPT_INNODB_LOG_GROUP_HOME_DIR: - innobase_log_group_home_dir=optarg; - break; - case OPT_INNODB_LOG_ARCH_DIR: - innobase_log_arch_dir=optarg; - break; - case OPT_INNODB_LOG_ARCHIVE: - innobase_log_archive= optarg ? test(atoi(optarg)) : 1; - break; - case OPT_INNODB_FLUSH_LOG_AT_TRX_COMMIT: - innobase_flush_log_at_trx_commit= optarg ? test(atoi(optarg)) : 1; - break; - case OPT_INNODB_FAST_SHUTDOWN: - innobase_fast_shutdown= optarg ? test(atoi(optarg)) : 1; - break; - case OPT_INNODB_UNIX_FILE_FLUSH_METHOD: - innobase_unix_file_flush_method=optarg; - break; + case OPT_INNODB_LOG_ARCHIVE: + innobase_log_archive= argument ? test(atoi(argument)) : 1; + break; + case OPT_INNODB_FLUSH_LOG_AT_TRX_COMMIT: + innobase_flush_log_at_trx_commit= argument ? test(atoi(argument)) : 1; + break; + case OPT_INNODB_FAST_SHUTDOWN: + innobase_fast_shutdown= argument ? test(atoi(argument)) : 1; + break; #endif /* HAVE_INNOBASE_DB */ - case OPT_MYISAM_RECOVER: + case OPT_MYISAM_RECOVER: { - if (!optarg) + if (!argument || !argument[0]) { myisam_recover_options= HA_RECOVER_DEFAULT; myisam_recover_options_str= myisam_recover_typelib.type_names[0]; } else { - myisam_recover_options_str=optarg; + myisam_recover_options_str=argument; if ((myisam_recover_options= - find_bit_type(optarg, &myisam_recover_typelib)) == ~(ulong) 0) + find_bit_type(argument, &myisam_recover_typelib)) == ~(ulong) 0) { - fprintf(stderr, "Unknown option to myisam-recover: %s\n",optarg); + fprintf(stderr, "Unknown option to myisam-recover: %s\n",argument); exit(1); } } ha_open_options|=HA_OPEN_ABORT_IF_CRASHED; break; } - case OPT_SQL_MODE: + case OPT_SQL_MODE: { - sql_mode_str = optarg; + sql_mode_str = argument; if ((opt_sql_mode = - find_bit_type(optarg, &sql_mode_typelib)) == ~(ulong) 0) + find_bit_type(argument, &sql_mode_typelib)) == ~(ulong) 0) { - fprintf(stderr, "Unknown option to sql-mode: %s\n", optarg); + fprintf(stderr, "Unknown option to sql-mode: %s\n", argument); exit(1); } default_tx_isolation= ((opt_sql_mode & MODE_SERIALIZABLE) ? @@ -4037,59 +4395,53 @@ static void get_options(int argc,char **argv) ISO_READ_COMMITTED); break; } - case OPT_MASTER_HOST: - master_host=optarg; - break; - case OPT_MASTER_USER: - master_user=optarg; - break; - case OPT_MASTER_PASSWORD: - master_password=optarg; - break; - case OPT_MASTER_INFO_FILE: - master_info_file=optarg; - break; - case OPT_MASTER_PORT: - master_port= atoi(optarg); - break; - case OPT_MASTER_CONNECT_RETRY: - master_connect_retry= atoi(optarg); - break; - case OPT_MASTER_RETRY_COUNT: - master_retry_count= atoi(optarg); - break; - case OPT_SAFE_SHOW_DB: - opt_safe_show_db=1; - break; - case OPT_SAFE_USER_CREATE: - opt_safe_user_create=1; - break; - case OPT_SKIP_SAFEMALLOC: + case OPT_MASTER_PASSWORD: + master_password=argument; + break; + case OPT_SKIP_SAFEMALLOC: #ifdef SAFEMALLOC - sf_malloc_quick=1; + sf_malloc_quick=1; #endif - break; - default: - fprintf(stderr,"%s: Unrecognized option: %c\n",my_progname,c); - use_help(); - exit(1); - } + break; } - // Skipp empty arguments (from shell) - while (argc != optind && !argv[optind][0]) - optind++; - if (argc != optind) + return 0; +} + + /* Initiates DEBUG - but no debugging here ! */ + +static void get_options(int argc,char **argv) +{ + int ho_error; + + myisam_delay_key_write=1; // Allow use of this +#ifndef HAVE_purify + my_use_symdir=1; // Use internal symbolic links +#else + /* Symlinks gives too many warnings with purify */ + my_disable_symlinks=1; + my_use_symdir=0; + have_symlink=SHOW_OPTION_DISABLED; +#endif + + if ((ho_error=handle_options(&argc, &argv, my_long_options, get_one_option))) { - fprintf(stderr,"%s: Too many parameters\n",my_progname); - use_help(); + printf("%s: handle_options() failed with error %d\n", my_progname, + ho_error); exit(1); } + fix_paths(); default_table_type_name=ha_table_typelib.type_names[default_table_type-1]; default_tx_isolation_name=tx_isolation_typelib.type_names[default_tx_isolation]; /* To be deleted in MySQL 4.0 */ if (!record_rnd_cache_size) record_rnd_cache_size=my_default_record_cache_size; + + /* Fix variables 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); + + myisam_block_size=(uint) 1 << my_bit_log2(opt_myisam_block_size); } @@ -4107,12 +4459,34 @@ static char *get_relative_path(const char *path) } +/* + Fix filename and replace extension where 'dir' is relative to + mysql_real_data_home. + Return 1 if len(path) > FN_REFLEN +*/ + +bool +fn_format_relative_to_data_home(my_string to, const char *name, + const char *dir, const char *extension) +{ + char tmp_path[FN_REFLEN]; + if (!test_if_hard_path(dir)) + { + strxnmov(tmp_path,sizeof(tmp_path)-1, mysql_real_data_home, + dir, NullS); + dir=tmp_path; + } + return !fn_format(to, name, dir, extension, + MY_REPLACE_EXT | MY_UNPACK_FILENAME | MY_SAFE_PATH); +} + + static void fix_paths(void) { (void) fn_format(mysql_home,mysql_home,"","",16); // Remove symlinks - convert_dirname(mysql_home); - convert_dirname(mysql_real_data_home); - convert_dirname(language); + convert_dirname(mysql_home,mysql_home,NullS); + convert_dirname(mysql_real_data_home,mysql_real_data_home,NullS); + convert_dirname(language,language,NullS); (void) my_load_path(mysql_home,mysql_home,""); // Resolve current dir (void) my_load_path(mysql_real_data_home,mysql_real_data_home,mysql_home); (void) my_load_path(pidfile_name,pidfile_name,mysql_real_data_home); @@ -4122,7 +4496,7 @@ static void fix_paths(void) strmake(buff,sharedir,sizeof(buff)-1); /* purecov: tested */ else strxnmov(buff,sizeof(buff)-1,mysql_home,sharedir,NullS); - convert_dirname(buff); + convert_dirname(buff,buff,NullS); (void) my_load_path(language,language,buff); /* If --character-sets-dir isn't given, use shared library dir */ @@ -4137,11 +4511,16 @@ static void fix_paths(void) char *tmp= (char*) my_malloc(FN_REFLEN,MYF(MY_FAE)); if (tmp) { - strmake(tmp, mysql_tmpdir, FN_REFLEN-1); - mysql_tmpdir=tmp; - convert_dirname(mysql_tmpdir); - mysql_tmpdir=(char*) my_realloc(mysql_tmpdir,(uint) strlen(mysql_tmpdir)+1, + char *end=convert_dirname(tmp, mysql_tmpdir, NullS); + + mysql_tmpdir=(char*) my_realloc(tmp,(uint) (end-tmp)+1, MYF(MY_HOLD_ON_ERROR)); + allocated_mysql_tmpdir=mysql_tmpdir; + } + if (!slave_load_tmpdir) + { + // no need to check return value, if we fail, my_malloc() never returns + slave_load_tmpdir = (char*) my_strdup(mysql_tmpdir, MYF(MY_FAE)); } } @@ -4160,16 +4539,17 @@ static uint set_maximum_open_files(uint max_file_limit) rlimit.rlim_cur=rlimit.rlim_max=max_file_limit; if (setrlimit(RLIMIT_NOFILE,&rlimit)) { - sql_print_error("Warning: setrlimit couldn't increase number of open files to more than %ld", - old_cur); /* purecov: inspected */ + sql_print_error("Warning: setrlimit couldn't increase number of open files to more than %lu (request: %u)", + old_cur, max_file_limit); /* purecov: inspected */ max_file_limit=old_cur; } else { (void) getrlimit(RLIMIT_NOFILE,&rlimit); if ((uint) rlimit.rlim_cur != max_file_limit) - sql_print_error("Warning: setrlimit returned ok, but didn't change limits. Max open files is %ld", - (ulong) rlimit.rlim_cur); /* purecov: inspected */ + sql_print_error("Warning: setrlimit returned ok, but didn't change limits. Max open files is %ld (request: %u)", + (ulong) rlimit.rlim_cur, + max_file_limit); /* purecov: inspected */ max_file_limit=rlimit.rlim_cur; } } @@ -4217,6 +4597,7 @@ static ulong find_bit_type(const char *x, TYPELIB *bit_lib) DBUG_PRINT("enter",("x: '%s'",x)); found=0; + found_end= 0; pos=(my_string) x; while (*pos == ' ') pos++; found_end= *pos == 0; @@ -4225,7 +4606,7 @@ static ulong find_bit_type(const char *x, TYPELIB *bit_lib) if (!*(end=strcend(pos,','))) /* Let end point at fieldend */ { while (end > pos && end[-1] == ' ') - end--; /* Skipp end-space */ + end--; /* Skip end-space */ found_end=1; } found_int=0; found_count=0; diff --git a/sql/net_pkg.cc b/sql/net_pkg.cc index 0b50b34c7bd..1ab3e18424f 100644 --- a/sql/net_pkg.cc +++ b/sql/net_pkg.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 */ @@ -30,6 +30,7 @@ void send_error(NET *net, uint sql_errno, const char *err) err ? err : net->last_error[0] ? net->last_error : "NULL")); + query_cache_abort(net); if (thd) thd->query_error = 1; // needed to catch query errors during replication if (!err) @@ -51,6 +52,7 @@ void send_error(NET *net, uint sql_errno, const char *err) { if (thd && thd->bootstrap) { + /* In bootstrap it's ok to print on stderr */ fprintf(stderr,"ERROR: %d %s\n",sql_errno,err); } DBUG_VOID_RETURN; @@ -102,11 +104,17 @@ net_printf(NET *net, uint errcode, ...) DBUG_ENTER("net_printf"); DBUG_PRINT("enter",("message: %u",errcode)); - if(thd) thd->query_error = 1; - // if we are here, something is wrong :-) - + if (thd) + thd->query_error = 1; // if we are here, something is wrong :-) + query_cache_abort(net); // Safety va_start(args,errcode); - format=ER(errcode); + /* + The following is needed to make net_printf() work with 0 argument for + errorcode and use the argument after that as the format string. This + is useful for rare errors that are not worth the hassle to put in + errmsg.sys, but at the same time, the message is not fixed text + */ + format=errcode ? ER(errcode) : va_arg(args,char*); offset= net->return_errno ? 2 : 0; text_pos=(char*) net->buff+head_length+offset+1; (void) vsprintf(my_const_cast(char*) (text_pos),format,args); @@ -119,6 +127,7 @@ net_printf(NET *net, uint errcode, ...) { if (thd && thd->bootstrap) { + /* In bootstrap it's ok to print on stderr */ fprintf(stderr,"ERROR: %d %s\n",errcode,text_pos); thd->fatal_error=1; } @@ -142,7 +151,7 @@ send_ok(NET *net,ha_rows affected_rows,ulonglong id,const char *message) { if (net->no_send_ok) // hack for re-parsing queries return; - + char buff[MYSQL_ERRMSG_SIZE+10],*pos; DBUG_ENTER("send_ok"); buff[0]=0; // No fields diff --git a/sql/net_serv.cc b/sql/net_serv.cc index 750079b39a5..0d6e548a873 100644 --- a/sql/net_serv.cc +++ b/sql/net_serv.cc @@ -1,19 +1,18 @@ -/* 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, +/* 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 - 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 */ + 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 */ /* Write and read of logical packets to/from socket ** Writes are cached into net_buffer_length big packets. @@ -22,95 +21,83 @@ ** 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 <violite.h> +#include <my_global.h> +#include <mysql.h> +#include <mysql_embed.h> +#include <mysql_com.h> +#include <mysqld_error.h> #include <my_sys.h> #include <m_string.h> -#include "mysql.h" -#include "mysqld_error.h" +#include <my_net.h> +#include <violite.h> #include <signal.h> #include <errno.h> -#include <sys/types.h> + +/* + The following handles the differences when this is linked between the + client and the server. + + This gives an 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. +*/ #ifdef MYSQL_SERVER ulong max_allowed_packet=65536; extern ulong net_read_timeout,net_write_timeout; extern uint test_flags; +#define USE_QUERY_CACHE +extern void query_cache_insert(NET *net, const char *packet, ulong length); #else ulong max_allowed_packet=16*1024*1024L; ulong net_read_timeout= NET_READ_TIMEOUT; ulong net_write_timeout= NET_WRITE_TIMEOUT; #endif -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 */ -#endif -#if !defined(MSDOS) && !defined(__WIN__) && !defined(HAVE_BROKEN_NETINET_INCLUDES) && !defined(__BEOS__) -#include <netinet/in_systm.h> -#include <netinet/in.h> -#include <netinet/ip.h> -#if !defined(alpha_linux_port) -#include <netinet/tcp.h> +#if defined(__WIN__) || !defined(MYSQL_SERVER) + /* The following is because alarms doesn't work on windows. */ +#define NO_ALARM #endif -#endif -#include "mysqld_error.h" -#ifdef MYSQL_SERVER + +#ifndef NO_ALARM #include "my_pthread.h" -#include "thr_alarm.h" void sql_print_error(const char *format,...); #define RETRY_COUNT mysqld_net_retry_count extern ulong mysqld_net_retry_count; -#else - -#ifdef OS2 /* avoid name conflict */ -#define thr_alarm_t thr_alarm_t_net -#define ALARM ALARM_net -#endif - -typedef my_bool thr_alarm_t; -typedef my_bool ALARM; -#define thr_alarm_init(A) (*(A))=0 -#define thr_alarm_in_use(A) (*(A)) -#define thr_end_alarm(A) -#define thr_alarm(A,B,C) local_thr_alarm((A),(B),(C)) -inline int local_thr_alarm(my_bool *A,int B __attribute__((unused)),ALARM *C __attribute__((unused))) -{ - *A=1; - return 0; -} -#define thr_got_alarm(A) 0 -#define RETRY_COUNT 1 -#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 +#define DONT_USE_THR_ALARM +#define RETRY_COUNT 1 +#endif /* NO_ALARM */ -/* -** 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. -*/ +#include "thr_alarm.h" #define TEST_BLOCKING 8 -static int net_write_buff(NET *net,const char *packet,uint len); +#define MAX_THREE_BYTES 255L*255L*255L + +ulong net_buffer_length=8192; /* Default length. Enlarged if necessary */ + +static int net_write_buff(NET *net,const char *packet,ulong len); /* 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((uint32) 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; @@ -119,12 +106,13 @@ int my_net_init(NET *net, Vio* vio) net->no_send_ok = 0; net->error=0; net->return_errno=0; net->return_status=0; net->timeout=(uint) net_read_timeout; /* Timeout for read */ - net->pkt_nr=0; + net->pkt_nr=net->compress_pkt_nr=0; net->write_pos=net->read_pos = net->buff; net->last_error[0]=0; net->compress=0; net->reading_or_writing=0; net->where_b = net->remain_in_buf=0; net->last_errno=0; + net->query_cache_query=0; if (vio != 0) /* If real connection */ { @@ -157,8 +145,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, (uint32) pkt_length + + NET_HEADER_SIZE + COMP_HEADER_SIZE, + MYF(MY_WME)))) { net->error=1; #ifdef MYSQL_SERVER @@ -175,22 +167,22 @@ static my_bool net_realloc(NET *net, ulong length) void net_clear(NET *net) { -#ifndef EXTRA_DEBUG - int count; // One may get 'unused' warning +#if !defined(EXTRA_DEBUG) && !defined(EMBEDDED_LIBRARY) + int count; /* One may get 'unused' warn */ bool is_blocking=vio_is_blocking(net->vio); if (is_blocking) vio_blocking(net->vio, FALSE); if (!vio_is_blocking(net->vio)) /* Safety if SSL */ { while ( (count = vio_read(net->vio, (char*) (net->buff), - net->max_packet)) > 0) + (uint32) net->max_packet)) > 0) DBUG_PRINT("info",("skipped %d bytes from file: %s", count,vio_description(net->vio))); if (is_blocking) vio_blocking(net->vio, TRUE); } #endif /* EXTRA_DEBUG */ - net->pkt_nr=0; /* Ready for new command */ + net->pkt_nr=net->compress_pkt_nr=0; /* Ready for new command */ net->write_pos=net->buff; } @@ -203,9 +195,12 @@ int net_flush(NET *net) if (net->buff != net->write_pos) { error=net_real_write(net,(char*) net->buff, - (uint) (net->write_pos - net->buff)); + (ulong) (net->write_pos - net->buff)); net->write_pos=net->buff; } + /* Sync packet number if using compression */ + if (net->compress) + net->pkt_nr=net->compress_pkt_nr; DBUG_RETURN(error); } @@ -214,44 +209,91 @@ 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]= (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++); + buff[3]= (uchar) net->pkt_nr++; if (net_write_buff(net,(char*) buff,NET_HEADER_SIZE)) return 1; 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) { + ulong length=len+1; /* 1 extra byte for command */ uchar buff[NET_HEADER_SIZE+1]; - uint length=len+1; /* 1 extra byte for command */ + 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]= (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)); + buff[3]= (uchar) net->pkt_nr++; + 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); + ulong left_length=(ulong) (net->buff_end - net->write_pos); while (len > left_length) { @@ -268,21 +310,30 @@ 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) { - int length; + long int length; char *pos,*end; thr_alarm_t alarmed; -#if !defined(__WIN__) +#ifndef NO_ALARM ALARM alarm_buff; #endif uint retry_count=0; my_bool net_blocking = vio_is_blocking(net->vio); DBUG_ENTER("net_real_write"); +#if defined(MYSQL_SERVER) && defined(HAVE_QUERY_CACHE) + if (net->query_cache_query != 0) + query_cache_insert(net, packet, len); +#endif + if (net->error == 2) DBUG_RETURN(-1); /* socket can't be used */ @@ -293,8 +344,8 @@ net_real_write(NET *net,const char *packet,ulong len) ulong complen; uchar *b; uint header_length=NET_HEADER_SIZE+COMP_HEADER_SIZE; - if (!(b=(uchar*) my_malloc(len + NET_HEADER_SIZE + COMP_HEADER_SIZE, - MYF(MY_WME)))) + if (!(b=(uchar*) my_malloc((uint32) len + NET_HEADER_SIZE + + COMP_HEADER_SIZE, MYF(MY_WME)))) { #ifdef MYSQL_SERVER net->last_errno=ER_OUT_OF_RESOURCES; @@ -313,25 +364,25 @@ net_real_write(NET *net,const char *packet,ulong len) } int3store(&b[NET_HEADER_SIZE],complen); int3store(b,len); - b[3]=(uchar) (net->pkt_nr++); + b[3]=(uchar) (net->compress_pkt_nr++); len+= header_length; packet= (char*) b; } #endif /* HAVE_COMPRESS */ /* DBUG_DUMP("net",packet,len); */ -#ifdef MYSQL_SERVER +#ifndef NO_ALARM thr_alarm_init(&alarmed); if (net_blocking) thr_alarm(&alarmed,(uint) net_write_timeout,&alarm_buff); #else alarmed=0; -#endif /* MYSQL_SERVER */ +#endif /* NO_ALARM */ pos=(char*) packet; end=pos+len; while (pos != end) { - if ((int) (length=vio_write(net->vio,pos,(int) (end-pos))) <= 0) + if ((long) (length=vio_write(net->vio,pos,(uint32) (end-pos))) <= 0) { my_bool interrupted = vio_should_retry(net->vio); #if (!defined(__WIN__) && !defined(__EMX__) && !defined(OS2)) @@ -408,14 +459,13 @@ net_real_write(NET *net,const char *packet,ulong len) ** Read something from server/clinet *****************************************************************************/ -#ifdef MYSQL_SERVER - +#ifndef NO_ALARM /* Help function to clear the commuication buffer when we get a too big packet */ -static void my_net_skip_rest(NET *net, ulong remain, thr_alarm_t *alarmed) +static void my_net_skip_rest(NET *net, uint32 remain, thr_alarm_t *alarmed) { ALARM alarm_buff; uint retry_count=0; @@ -431,21 +481,27 @@ 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) + 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 -= (uint32) length; + statistic_add(bytes_received,length,&LOCK_bytes_received); } } -#endif /* MYSQL_SERVER */ +#endif /* NO_ALARM */ + +/* + 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 +static ulong my_real_read(NET *net, ulong *complen) { uchar *pos; @@ -453,20 +509,20 @@ my_real_read(NET *net, ulong *complen) uint i,retry_count=0; ulong len=packet_error; thr_alarm_t alarmed; -#if (!defined(__WIN__) && !defined(__EMX__) && !defined(OS2)) || defined(MYSQL_SERVER) +#ifndef NO_ALARM ALARM alarm_buff; #endif my_bool net_blocking=vio_is_blocking(net->vio); - ulong remain= (net->compress ? NET_HEADER_SIZE+COMP_HEADER_SIZE : - NET_HEADER_SIZE); + uint32 remain= (net->compress ? NET_HEADER_SIZE+COMP_HEADER_SIZE : + NET_HEADER_SIZE); *complen = 0; net->reading_or_writing=1; thr_alarm_init(&alarmed); -#ifdef MYSQL_SERVER +#ifndef NO_ALARM if (net_blocking) thr_alarm(&alarmed,net->timeout,&alarm_buff); -#endif /* MYSQL_SERVER */ +#endif /* NO_ALARM */ pos = net->buff + net->where_b; /* net->packet -4 */ for (i=0 ; i < 2 ; i++) @@ -535,7 +591,7 @@ my_real_read(NET *net, ulong *complen) continue; } #endif - DBUG_PRINT("error",("Couldn't read packet: remain: %d errno: %d length: %d alarmed: %d", remain,vio_errno(net->vio),length,alarmed)); + DBUG_PRINT("error",("Couldn't read packet: remain: %lu errno: %d length: %ld alarmed: %d", remain,vio_errno(net->vio),length,alarmed)); len= packet_error; net->error=2; /* Close socket */ #ifdef MYSQL_SERVER @@ -544,7 +600,7 @@ my_real_read(NET *net, ulong *complen) #endif goto end; } - remain -= (ulong) length; + remain -= (uint32) length; pos+= (ulong) length; statistic_add(bytes_received,(ulong) length,&LOCK_bytes_received); } @@ -556,9 +612,9 @@ my_real_read(NET *net, ulong *complen) if (net->buff[net->where_b] != (uchar) 255) { DBUG_PRINT("error", - ("Packets out of order (Found: %d, expected %d)", + ("Packets out of order (Found: %d, expected %u)", (int) net->buff[net->where_b + 3], - (uint) (uchar) net->pkt_nr)); + net->pkt_nr)); #ifdef EXTRA_DEBUG fprintf(stderr,"Packets out of order (Found: %d, expected %d)\n", (int) net->buff[net->where_b + 3], @@ -571,7 +627,7 @@ my_real_read(NET *net, ulong *complen) #endif goto end; } - net->pkt_nr++; + net->compress_pkt_nr= ++net->pkt_nr; #ifdef HAVE_COMPRESS if (net->compress) { @@ -581,23 +637,24 @@ 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 +#ifndef NO_ALARM if (i == 1) - my_net_skip_rest(net, len, &alarmed); + my_net_skip_rest(net, (uint32) len, &alarmed); #endif len= packet_error; /* Return error */ goto end; } } pos=net->buff + net->where_b; - remain = len; + remain = (uint32) len; } } @@ -611,7 +668,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; @@ -620,65 +691,136 @@ 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 */ + ulong 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 len 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= (ulong) (buf_length - start_of_packet); + len = ((ulong) (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 +} + +int net_request_file(NET* net, const char* fname) +{ + char tmp [FN_REFLEN+1],*end; + DBUG_ENTER("net_request_file"); + tmp[0] = (char) 251; /* NULL_LENGTH */ + end=strnmov(tmp+1,fname,sizeof(tmp)-2); + DBUG_RETURN(my_net_write(net,tmp,(uint) (end-tmp)) || + net_flush(net)); } diff --git a/sql/nt_servc.cc b/sql/nt_servc.cc index 8c705a94f55..be2ceb9cb7a 100644 --- a/sql/nt_servc.cc +++ b/sql/nt_servc.cc @@ -479,3 +479,33 @@ If this condition persist, reboot the machine and try again\n"); return ret_value; } +/* ------------------------------------------------------------------------ + -------------------------------------------------------------------------- */ +BOOL NTService::IsService(LPCSTR ServiceName) +{ + BOOL ret_value=FALSE; + SC_HANDLE service, scm; + + if (scm = OpenSCManager(0, 0,SC_MANAGER_ENUMERATE_SERVICE)) + { + if ((service = OpenService(scm,ServiceName, SERVICE_ALL_ACCESS ))) + { + ret_value=TRUE; + CloseServiceHandle(service); + } + CloseServiceHandle(scm); + } + return ret_value; +} +/* ------------------------------------------------------------------------ + -------------------------------------------------------------------------- */ +BOOL NTService::got_service_option(char **argv, char *service_option) +{ + char *option = argv[1]; + + while (*option) + if (!strcmp(option++, service_option)) + return TRUE; + + return FALSE; +} diff --git a/sql/nt_servc.h b/sql/nt_servc.h index 40d1a8c03fa..ab3238e0f41 100644 --- a/sql/nt_servc.h +++ b/sql/nt_servc.h @@ -52,6 +52,8 @@ class NTService LPCSTR szAccountName=NULL,LPCSTR szPassword=NULL); BOOL SeekStatus(LPCSTR szInternName, int OperationType); BOOL Remove(LPCSTR szInternName); + BOOL IsService(LPCSTR ServiceName); + BOOL got_service_option(char **argv, char *service_option); void Stop(void); //to be called from app. to stop service diff --git a/sql/opt_ft.h b/sql/opt_ft.h index dcbbb8abcec..b055edc107c 100644 --- a/sql/opt_ft.h +++ b/sql/opt_ft.h @@ -29,7 +29,7 @@ public: TABLE_REF *ref; FT_SELECT(TABLE *table, TABLE_REF *tref) : - QUICK_SELECT (table,tref->key,1), ref(tref) {} + QUICK_SELECT (table,tref->key,1), ref(tref) { init(); } int init() { return error=file->ft_init(); } int get_next() { return error=file->ft_read(record); } diff --git a/sql/opt_range.cc b/sql/opt_range.cc index b95b97d670f..7d2d04dbdf1 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 @@ -279,21 +280,21 @@ public: typedef struct st_qsel_param { - uint baseflag,keys,max_key_part; - table_map prev_tables,read_tables,current_table; TABLE *table; - bool quick; // Don't calulate possible keys KEY_PART *key_parts,*key_parts_end,*key[MAX_KEY]; + MEM_ROOT *mem_root; + table_map prev_tables,read_tables,current_table; + uint baseflag,keys,max_key_part; uint real_keynr[MAX_KEY]; char min_key[MAX_KEY_LENGTH+MAX_FIELD_WIDTH], max_key[MAX_KEY_LENGTH+MAX_FIELD_WIDTH]; + bool quick; // Don't calulate possible keys } PARAM; - static SEL_TREE * get_mm_parts(PARAM *param,Field *field, Item_func::Functype type,Item *value, Item_result cmp_type); -static SEL_ARG *get_mm_leaf(Field *field,KEY_PART *key_part, +static SEL_ARG *get_mm_leaf(PARAM *param,Field *field,KEY_PART *key_part, Item_func::Functype type,Item *value); static bool like_range(const char *ptr,uint length,char wild_prefix, uint field_length, char *min_str,char *max_str, @@ -382,7 +383,7 @@ SQL_SELECT::~SQL_SELECT() #undef index // Fix for Unixware 7 QUICK_SELECT::QUICK_SELECT(TABLE *table,uint key_nr,bool no_alloc) - :error(0),index(key_nr),max_used_key_length(0),head(table), + :dont_free(0),error(0),index(key_nr),max_used_key_length(0),head(table), it(ranges),range(0) { if (!no_alloc) @@ -399,13 +400,11 @@ QUICK_SELECT::QUICK_SELECT(TABLE *table,uint key_nr,bool no_alloc) QUICK_SELECT::~QUICK_SELECT() { - file->index_end(); - free_root(&alloc,MYF(0)); -} - -int QUICK_SELECT::init() -{ - return error=file->index_init(index); + if (!dont_free) + { + file->index_end(); + free_root(&alloc,MYF(0)); + } } QUICK_RANGE::QUICK_RANGE() @@ -533,7 +532,7 @@ static int sel_cmp(Field *field, char *a,char *b,uint8 a_flag,uint8 b_flag) } if (*a) goto end; // NULL where equal - a++; b++; // Skipp NULL marker + a++; b++; // Skip NULL marker } cmp=field->key_cmp((byte*) a,(byte*) b); if (cmp) return cmp < 0 ? -1 : 1; // The values differed @@ -586,6 +585,9 @@ int SQL_SELECT::test_quick_select(key_map keys_to_use, table_map prev_tables, uint idx; double scan_time; DBUG_ENTER("test_quick_select"); + DBUG_PRINT("enter",("keys_to_use: %lu prev_tables: %lu const_tables: %lu", + (ulong) keys_to_use, (ulong) prev_tables, + (ulong) const_tables)); delete quick; quick=0; @@ -593,7 +595,7 @@ int SQL_SELECT::test_quick_select(key_map keys_to_use, table_map prev_tables, if (!cond || (specialflag & SPECIAL_SAFE_MODE) && ! force_quick_range || !limit) DBUG_RETURN(0); /* purecov: inspected */ - if (!((basflag= head->file->option_flag()) & HA_KEYPOS_TO_RNDPOS) && + if (!((basflag= head->file->table_flags()) & HA_KEYPOS_TO_RNDPOS) && keys_to_use == (uint) ~0 || !keys_to_use) DBUG_RETURN(0); /* Not smart database */ records=head->file->records; @@ -604,7 +606,7 @@ int SQL_SELECT::test_quick_select(key_map keys_to_use, table_map prev_tables, if (limit < records) read_time=(double) records+scan_time+1; // Force to use index else if (read_time <= 2.0 && !force_quick_range) - DBUG_RETURN(0); /* No nead for quick select */ + DBUG_RETURN(0); /* No need for quick select */ DBUG_PRINT("info",("Time to scan table: %ld",(long) read_time)); @@ -623,6 +625,7 @@ int SQL_SELECT::test_quick_select(key_map keys_to_use, table_map prev_tables, param.current_table= head->map; param.table=head; param.keys=0; + param.mem_root= &alloc; current_thd->no_errors=1; // Don't warn about NULL init_sql_alloc(&alloc,2048,0); @@ -689,7 +692,7 @@ int SQL_SELECT::test_quick_select(key_map keys_to_use, table_map prev_tables, found_records=check_quick_select(¶m,idx, *key); if (found_records != HA_POS_ERROR && found_records > 2 && head->used_keys & ((table_map) 1 << param.real_keynr[idx]) && - (head->file->option_flag() & HA_HAVE_KEY_READ_ONLY)) + (head->file->table_flags() & HA_HAVE_KEY_READ_ONLY)) { /* ** We can resolve this by only reading through this key @@ -891,7 +894,7 @@ get_mm_parts(PARAM *param,Field *field, Item_func::Functype type,Item *value, tree=new SEL_TREE(); if (!value || !(value->used_tables() & ~param->read_tables)) { - sel_arg=get_mm_leaf(key_part->field,key_part,type,value); + sel_arg=get_mm_leaf(param,key_part->field,key_part,type,value); if (!sel_arg) continue; if (sel_arg->type == SEL_ARG::IMPOSSIBLE) @@ -911,7 +914,7 @@ get_mm_parts(PARAM *param,Field *field, Item_func::Functype type,Item *value, static SEL_ARG * -get_mm_leaf(Field *field,KEY_PART *key_part, +get_mm_leaf(PARAM *param, Field *field, KEY_PART *key_part, Item_func::Functype type,Item *value) { uint maybe_null=(uint) field->real_maybe_null(); @@ -926,7 +929,7 @@ get_mm_leaf(Field *field,KEY_PART *key_part, String tmp(buff1,sizeof(buff1)),*res; uint length,offset,min_length,max_length; - if (!field->optimize_range()) + if (!field->optimize_range((uint) key_part->key)) DBUG_RETURN(0); // Can't optimize this if (!(res= value->val_str(&tmp))) DBUG_RETURN(&null_element); @@ -956,7 +959,7 @@ get_mm_leaf(Field *field,KEY_PART *key_part, field_length=length; } length+=offset; - if (!(min_str= (char*) sql_alloc(length*2))) + if (!(min_str= (char*) alloc_root(param->mem_root, length*2))) DBUG_RETURN(0); max_str=min_str+length; if (maybe_null) @@ -1007,7 +1010,8 @@ get_mm_leaf(Field *field,KEY_PART *key_part, DBUG_RETURN(tree); } - if (!field->optimize_range() && type != Item_func::EQ_FUNC && + if (!field->optimize_range((uint) key_part->key) && + type != Item_func::EQ_FUNC && type != Item_func::EQUAL_FUNC) DBUG_RETURN(0); // Can't optimize this @@ -1023,7 +1027,8 @@ get_mm_leaf(Field *field,KEY_PART *key_part, if (type == Item_func::EQUAL_FUNC) { /* convert column_name <=> NULL -> column_name IS NULL */ - char *str= (char*) sql_alloc(1); // Get local copy of key + // Get local copy of key + char *str= (char*) alloc_root(param->mem_root,1); if (!*str) DBUG_RETURN(0); *str = 1; @@ -1032,7 +1037,8 @@ get_mm_leaf(Field *field,KEY_PART *key_part, DBUG_RETURN(&null_element); // NULL is never true } // Get local copy of key - char *str= (char*) sql_alloc(key_part->part_length+maybe_null); + char *str= (char*) alloc_root(param->mem_root, + key_part->part_length+maybe_null); if (!str) DBUG_RETURN(0); if (maybe_null) @@ -1098,7 +1104,7 @@ static bool like_range(const char *ptr,uint ptr_length,char escape, { if (*ptr == escape && ptr+1 != end) { - ptr++; // Skipp escape + ptr++; // Skip escape *min_str++= *max_str++ = *ptr; continue; } @@ -2232,7 +2238,7 @@ get_quick_select(PARAM *param,uint idx,SEL_ARG *key_tree) else { quick->key_parts=(KEY_PART*) - sql_memdup(param->key[idx], + memdup_root(&quick->alloc,(char*) param->key[idx], sizeof(KEY_PART)* param->table->key_info[param->real_keynr[idx]].key_parts); } @@ -2403,7 +2409,7 @@ QUICK_SELECT *get_quick_select_for_ref(TABLE *table, TABLE_REF *ref) (key_info->flags & HA_NOSAME)) ? EQ_RANGE : 0); if (!(quick->key_parts=key_part=(KEY_PART *) - sql_alloc(sizeof(KEY_PART)*ref->key_parts))) + alloc_root(&quick->alloc,sizeof(KEY_PART)*ref->key_parts))) goto err; for (part=0 ; part < ref->key_parts ;part++,key_part++) @@ -2455,8 +2461,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 +2522,225 @@ 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, uint used_key_parts) + : QUICK_SELECT(*q), rev_it(rev_ranges) +{ + bool not_read_after_key = file->table_flags() & HA_NOT_READ_AFTER_KEY; + QUICK_RANGE *r; + + it.rewind(); + for (r = it++; r; r = it++) + { + rev_ranges.push_front(r); + if (not_read_after_key && range_reads_after_key(r) || + test_if_null_range(r,used_key_parts)) + { + it.rewind(); // Reset range + error = HA_ERR_UNSUPPORTED; + dont_free=1; // Don't free memory from 'q' + return; + } + } + /* Remove EQ_RANGE flag for keys that are not using the full key */ + for (r = rev_it++; r; r = rev_it++) + { + if ((r->flag & EQ_RANGE) && + head->key_info[index].key_length != r->max_length) + r->flag&= ~EQ_RANGE; + } + rev_it.rewind(); + q->dont_free=1; // Don't free shared mem + delete q; +} + + +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, which means that 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) + { + 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 */ + result=file->index_read(record, (byte*) range->max_key, + range->max_length, + ((range->flag & NEAR_MAX) ? + HA_READ_KEY_EXACT : 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 + } +} + +/* + * 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 = 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 + See comment in get_next() about this + */ + +bool QUICK_SELECT_DESC::range_reads_after_key(QUICK_RANGE *range) +{ + return ((range->flag & (NO_MAX_RANGE | NEAR_MAX)) || + !(range->flag & EQ_RANGE) || + head->key_info[index].key_length != range->max_length) ? 1 : 0; +} + +/* True if we are reading over a key that may have a NULL value */ + +bool QUICK_SELECT_DESC::test_if_null_range(QUICK_RANGE *range, + uint used_key_parts) +{ + uint offset,end; + KEY_PART *key_part = key_parts, + *key_part_end= key_part+used_key_parts; + + for (offset= 0, end = min(range->min_length, range->max_length) ; + offset < end && key_part != key_part_end ; + offset += key_part++->part_length) + { + uint null_length=test(key_part->null_bit); + if (!memcmp((char*) range->min_key+offset, (char*) range->max_key+offset, + key_part->part_length + null_length)) + { + offset+=null_length; + continue; + } + if (null_length && range->min_key[offset]) + return 1; // min_key is null and max_key isn't + // Range doesn't cover NULL. This is ok if there is no more null parts + break; + } + /* + If the next min_range is > NULL, then we can use this, even if + it's a NULL key + Example: SELECT * FROM t1 WHERE a = 2 AND b >0 ORDER BY a DESC,b DESC; + + */ + if (key_part != key_part_end && key_part->null_bit) + { + if (offset >= range->min_length || range->min_key[offset]) + return 1; // Could be null + key_part++; + } + /* + If any of the key parts used in the ORDER BY could be NULL, we can't + use the key to sort the data. + */ + for (; key_part != key_part_end ; key_part++) + if (key_part->null_bit) + return 1; // Covers null part + return 0; +} + + /***************************************************************************** ** Print a quick range for debugging ** TODO: diff --git a/sql/opt_range.h b/sql/opt_range.h index 247dd260817..f48a3936a17 100644 --- a/sql/opt_range.h +++ b/sql/opt_range.h @@ -48,15 +48,16 @@ class QUICK_RANGE :public Sql_alloc { uint flag_arg) : min_key((char*) sql_memdup(min_key_arg,min_length_arg+1)), max_key((char*) sql_memdup(max_key_arg,max_length_arg+1)), - min_length(min_length_arg), - max_length(max_length_arg), - flag(flag_arg) + min_length((uint16) min_length_arg), + max_length((uint16) max_length_arg), + flag((uint16) flag_arg) {} }; + class QUICK_SELECT { public: - bool next; + bool next,dont_free; int error; uint index,max_used_key_length; TABLE *head; @@ -74,12 +75,30 @@ public: QUICK_SELECT(TABLE *table,uint index_arg,bool no_alloc=0); virtual ~QUICK_SELECT(); void reset(void) { next=0; it.rewind(); } - virtual int init(); + int init() { return error=file->index_init(index); } virtual int get_next(); + virtual bool reverse_sorted() { return 0; } int cmp_next(QUICK_RANGE *range); bool unique_key_range(); }; + +class QUICK_SELECT_DESC: public QUICK_SELECT +{ +public: + QUICK_SELECT_DESC(QUICK_SELECT *q, uint used_key_parts); + int get_next(); + bool reverse_sorted() { return 1; } +private: + int cmp_prev(QUICK_RANGE *range); + bool range_reads_after_key(QUICK_RANGE *range); + bool test_if_null_range(QUICK_RANGE *range, uint used_key_parts); + void reset(void) { next=0; rev_it.rewind(); } + 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/opt_sum.cc b/sql/opt_sum.cc index df49d52d54a..efb4c4916a5 100644 --- a/sql/opt_sum.cc +++ b/sql/opt_sum.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 */ @@ -32,7 +32,7 @@ static bool find_range_key(TABLE_REF *ref, Field* field,COND *cond); int opt_sum_query(TABLE_LIST *tables, List<Item> &all_fields,COND *conds) { - List_iterator<Item> it(all_fields); + List_iterator_fast<Item> it(all_fields); int const_result=1; bool recalc_const_item=0; table_map removed_tables=0; @@ -55,7 +55,7 @@ int opt_sum_query(TABLE_LIST *tables, List<Item> &all_fields,COND *conds) TABLE_LIST *table; for (table=tables; table ; table=table->next) { - if (table->on_expr || (table->table->file->option_flag() & + if (table->on_expr || (table->table->file->table_flags() & HA_NOT_EXACT_COUNT)) { const_result=0; // Can't optimize left join @@ -141,7 +141,7 @@ int opt_sum_query(TABLE_LIST *tables, List<Item> &all_fields,COND *conds) break; } TABLE *table=((Item_field*) expr)->field->table; - if ((table->file->option_flag() & HA_NOT_READ_AFTER_KEY)) + if ((table->file->table_flags() & HA_NOT_READ_AFTER_KEY)) { const_result=0; break; @@ -205,7 +205,7 @@ uint count_table_entries(COND *cond,TABLE *table) if (((Item_cond*) cond)->functype() == Item_func::COND_OR_FUNC) return (cond->used_tables() & table->map) ? MAX_REF_PARTS+1 : 0; - List_iterator<Item> li(*((Item_cond*) cond)->argument_list()); + List_iterator_fast<Item> li(*((Item_cond*) cond)->argument_list()); Item *item; uint count=0; while ((item=li++)) @@ -250,7 +250,7 @@ bool part_of_cond(COND *cond,Field *field) if (((Item_cond*) cond)->functype() == Item_func::COND_OR_FUNC) return 0; // Already checked - List_iterator<Item> li(*((Item_cond*) cond)->argument_list()); + List_iterator_fast<Item> li(*((Item_cond*) cond)->argument_list()); Item *item; while ((item=li++)) { @@ -291,21 +291,28 @@ bool part_of_cond(COND *cond,Field *field) static bool find_range_key(TABLE_REF *ref, Field* field, COND *cond) { if (!(field->flags & PART_KEY_FLAG)) - return 0; // Not part of a key. Skipp it + return 0; // Not part of a key. Skip it TABLE *table=field->table; - if (table->file->option_flag() & HA_WRONG_ASCII_ORDER) - return(0); // Can't use key to find last row uint idx=0; /* Check if some key has field as first key part */ - if (field->key_start && (! cond || ! (cond->used_tables() & table->map))) + if ((field->key_start & field->table->keys_in_use_for_query) && + (! cond || ! (cond->used_tables() & table->map))) { - for (key_map key=field->key_start ; !(key & 1) ; idx++) - key>>=1; + for (key_map key=field->key_start ;;) + { + for (; !(key & 1) ; idx++) + key>>=1; + if (!(table->file->index_flags(idx) & HA_WRONG_ASCII_ORDER)) + break; // Key is ok + /* Can't use this key, for looking up min() or max(), end if last one */ + if (key == 1) + return 0; + } ref->key_length=0; ref->key=idx; - if (field->part_of_key & ((table_map) 1 << idx)) + if (field->part_of_key & ((key_map) 1 << idx)) { table->key_read=1; table->file->extra(HA_EXTRA_KEYREAD); @@ -322,6 +329,7 @@ static bool find_range_key(TABLE_REF *ref, Field* field, COND *cond) return 0; KEY *keyinfo,*keyinfo_end; + idx=0; for (keyinfo=table->key_info, keyinfo_end=keyinfo+table->keys ; keyinfo != keyinfo_end; keyinfo++,idx++) @@ -337,7 +345,8 @@ static bool find_range_key(TABLE_REF *ref, Field* field, COND *cond) part++) { if (!part_of_cond(cond,part->field) || - left_length < part->store_length) + left_length < part->store_length || + (table->file->index_flags(idx) & HA_WRONG_ASCII_ORDER)) break; // Save found constant if (part->null_bit) @@ -350,7 +359,7 @@ static bool find_range_key(TABLE_REF *ref, Field* field, COND *cond) { ref->key_length= (uint) (key_ptr-ref->key_buff); ref->key=idx; - if (field->part_of_key & ((table_map) 1 << idx)) + if (field->part_of_key & ((key_map) 1 << idx)) { table->key_read=1; table->file->extra(HA_EXTRA_KEYREAD); diff --git a/sql/password.c b/sql/password.c index 1c88aabcce2..48181ea18e6 100644 --- a/sql/password.c +++ b/sql/password.c @@ -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 */ @@ -34,7 +34,7 @@ This saves a hashed number as a string in the password field. *****************************************************************************/ -#include <global.h> +#include <my_global.h> #include <my_sys.h> #include <m_string.h> #include "mysql.h" diff --git a/sql/procedure.cc b/sql/procedure.cc index 526bbe0feab..437bd82d6e5 100644 --- a/sql/procedure.cc +++ b/sql/procedure.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 */ diff --git a/sql/procedure.h b/sql/procedure.h index 1583f1169ce..db0e0b7f9e2 100644 --- a/sql/procedure.h +++ b/sql/procedure.h @@ -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 */ diff --git a/sql/records.cc b/sql/records.cc index d436f4f58fe..29ace3cd652 100644 --- a/sql/records.cc +++ b/sql/records.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 */ @@ -51,12 +51,7 @@ void init_read_record(READ_RECORD *info,THD *thd, TABLE *table, tempfile= &select->file; else tempfile= table->io_cache; - if (select && select->quick && (! tempfile || !tempfile->buffer)) - { - DBUG_PRINT("info",("using rr_quick")); - info->read_record=rr_quick; - } - else if (tempfile && my_b_inited(tempfile)) // Test if ref-records was used + if (tempfile && my_b_inited(tempfile)) // Test if ref-records was used { DBUG_PRINT("info",("using rr_from_tempfile")); info->read_record=rr_from_tempfile; @@ -84,8 +79,14 @@ void init_read_record(READ_RECORD *info,THD *thd, TABLE *table, } } } + else if (select && select->quick) + { + DBUG_PRINT("info",("using rr_quick")); + info->read_record=rr_quick; + } else if (table->record_pointers) { + DBUG_PRINT("info",("using record_pointers")); table->file->rnd_init(0); info->cache_pos=table->record_pointers; info->cache_end=info->cache_pos+ table->found_records*info->ref_length; @@ -101,7 +102,7 @@ void init_read_record(READ_RECORD *info,THD *thd, TABLE *table, (int) table->reginfo.lock_type <= (int) TL_READ_HIGH_PRIORITY || !(table->db_options_in_use & HA_OPTION_PACK_RECORD) || (use_record_cache < 0 && - !(table->file->option_flag() & HA_NOT_DELETE_WITH_CACHE))) + !(table->file->table_flags() & HA_NOT_DELETE_WITH_CACHE))) VOID(table->file->extra(HA_EXTRA_CACHE)); // Cache reads } DBUG_VOID_RETURN; diff --git a/sql/repl_failsafe.cc b/sql/repl_failsafe.cc new file mode 100644 index 00000000000..257418d1682 --- /dev/null +++ b/sql/repl_failsafe.cc @@ -0,0 +1,853 @@ +/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB & Sasha + + 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 */ + +// Sasha Pachev <sasha@mysql.com> is currently in charge of this file + +#include "mysql_priv.h" +#include "repl_failsafe.h" +#include "sql_repl.h" +#include "slave.h" +#include "sql_acl.h" +#include "mini_client.h" +#include "log_event.h" +#include <mysql.h> +#include <thr_alarm.h> + +#define SLAVE_LIST_CHUNK 128 +#define SLAVE_ERRMSG_SIZE (FN_REFLEN+64) + + +RPL_STATUS rpl_status=RPL_NULL; +pthread_mutex_t LOCK_rpl_status; +pthread_cond_t COND_rpl_status; +HASH slave_list; +extern const char* any_db; + +const char *rpl_role_type[] = {"MASTER","SLAVE",NullS}; +TYPELIB rpl_role_typelib = {array_elements(rpl_role_type)-1,"", + rpl_role_type}; + +const char* rpl_status_type[] = {"AUTH_MASTER","ACTIVE_SLAVE","IDLE_SLAVE", + "LOST_SOLDIER","TROOP_SOLDIER", + "RECOVERY_CAPTAIN","NULL",NullS}; +TYPELIB rpl_status_typelib= {array_elements(rpl_status_type)-1,"", + rpl_status_type}; + +static Slave_log_event* find_slave_event(IO_CACHE* log, + const char* log_file_name, + char* errmsg); + +static int init_failsafe_rpl_thread(THD* thd) +{ + DBUG_ENTER("init_failsafe_rpl_thread"); + thd->system_thread = thd->bootstrap = 1; + thd->client_capabilities = 0; + my_net_init(&thd->net, 0); + thd->net.timeout = slave_net_timeout; + thd->max_packet_length=thd->net.max_packet; + thd->master_access= ~0; + thd->priv_user = 0; + thd->system_thread = 1; + pthread_mutex_lock(&LOCK_thread_count); + thd->thread_id = thread_id++; + pthread_mutex_unlock(&LOCK_thread_count); + + if (init_thr_lock() || + my_pthread_setspecific_ptr(THR_THD, thd) || + my_pthread_setspecific_ptr(THR_MALLOC, &thd->mem_root) || + my_pthread_setspecific_ptr(THR_NET, &thd->net)) + { + close_connection(&thd->net,ER_OUT_OF_RESOURCES); // is this needed? + end_thread(thd,0); + DBUG_RETURN(-1); + } + + thd->mysys_var=my_thread_var; + thd->dbug_thread_id=my_thread_id(); +#if !defined(__WIN__) && !defined(OS2) + sigset_t set; + VOID(sigemptyset(&set)); // Get mask in use + VOID(pthread_sigmask(SIG_UNBLOCK,&set,&thd->block_signals)); +#endif + + thd->mem_root.free=thd->mem_root.used=0; + if (thd->max_join_size == (ulong) ~0L) + thd->options |= OPTION_BIG_SELECTS; + + thd->proc_info="Thread initialized"; + thd->version=refresh_version; + thd->set_time(); + DBUG_RETURN(0); +} + +void change_rpl_status(RPL_STATUS from_status, RPL_STATUS to_status) +{ + pthread_mutex_lock(&LOCK_rpl_status); + if (rpl_status == from_status || rpl_status == RPL_ANY) + rpl_status = to_status; + pthread_cond_signal(&COND_rpl_status); + pthread_mutex_unlock(&LOCK_rpl_status); +} + +#define get_object(p, obj) \ +{\ + uint len = (uint)*p++; \ + if (p + len > p_end || len >= sizeof(obj)) \ + goto err; \ + strmake(obj,(char*) p,len); \ + p+= len; \ +}\ + +static inline int cmp_master_pos(Slave_log_event* sev, LEX_MASTER_INFO* mi) +{ + return cmp_master_pos(sev->master_log, sev->master_pos, mi->log_file_name, + mi->pos); +} + +void unregister_slave(THD* thd, bool only_mine, bool need_mutex) +{ + if (need_mutex) + pthread_mutex_lock(&LOCK_slave_list); + if (thd->server_id) + { + SLAVE_INFO* old_si; + if ((old_si = (SLAVE_INFO*)hash_search(&slave_list, + (byte*)&thd->server_id, 4)) && + (!only_mine || old_si->thd == thd)) + hash_delete(&slave_list, (byte*)old_si); + } + if (need_mutex) + pthread_mutex_unlock(&LOCK_slave_list); +} + +int register_slave(THD* thd, uchar* packet, uint packet_length) +{ + SLAVE_INFO *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; + + thd->server_id = si->server_id = uint4korr(p); + p += 4; + get_object(p,si->host); + get_object(p,si->user); + get_object(p,si->password); + si->port = uint2korr(p); + p += 2; + si->rpl_recovery_rank = uint4korr(p); + p += 4; + if (!(si->master_id = uint4korr(p))) + si->master_id = server_id; + si->thd = thd; + pthread_mutex_lock(&LOCK_slave_list); + + unregister_slave(thd,0,0); + res = hash_insert(&slave_list, (byte*) si); + pthread_mutex_unlock(&LOCK_slave_list); + return res; + +err: + if (si) + my_free((gptr) si, MYF(MY_WME)); + return res; +} + +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((gptr) 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() +{ + /* No protection by a mutex needed as we are only called at shutdown */ + if (hash_inited(&slave_list)) + { + hash_free(&slave_list); + pthread_mutex_destroy(&LOCK_slave_list); + } +} + +static int find_target_pos(LEX_MASTER_INFO* mi, IO_CACHE* log, char* errmsg) +{ + uint32 log_pos = (uint32) mi->pos; + uint32 target_server_id = mi->server_id; + + for (;;) + { + Log_event* ev; + if (!(ev = Log_event::read_log_event(log, (pthread_mutex_t*)0, + 0))) + { + if (log->error > 0) + strmov(errmsg, "Binary log truncated in the middle of event"); + else if (log->error < 0) + strmov(errmsg, "I/O error reading binary log"); + else + strmov(errmsg, "Could not find target event in the binary log"); + return 1; + } + + if (ev->log_pos == log_pos && ev->server_id == target_server_id) + { + delete ev; + mi->pos = my_b_tell(log); + return 0; + } + + delete ev; + } +} + + +int translate_master(THD* thd, LEX_MASTER_INFO* mi, char* errmsg) +{ + LOG_INFO linfo; + char search_file_name[FN_REFLEN],last_log_name[FN_REFLEN]; + IO_CACHE log; + File file = -1, last_file = -1; + pthread_mutex_t *log_lock; + const char* errmsg_p; + Slave_log_event* sev = 0; + my_off_t last_pos = 0; + int error = 1; + int cmp_res; + LINT_INIT(cmp_res); + + if (!mysql_bin_log.is_open()) + { + strmov(errmsg,"Binary log is not open"); + return 1; + } + + if (!server_id_supplied) + { + strmov(errmsg, "Misconfigured master - server id was not set"); + return 1; + } + + linfo.index_file_offset = 0; + + + search_file_name[0] = 0; + + if (mysql_bin_log.find_first_log(&linfo, search_file_name)) + { + strmov(errmsg,"Could not find first log"); + return 1; + } + thd->current_linfo = &linfo; + + bzero((char*) &log,sizeof(log)); + log_lock = mysql_bin_log.get_log_lock(); + pthread_mutex_lock(log_lock); + + for (;;) + { + if ((file=open_binlog(&log, linfo.log_file_name, &errmsg_p)) < 0) + { + strmov(errmsg, errmsg_p); + goto err; + } + + if (!(sev = find_slave_event(&log, linfo.log_file_name, errmsg))) + goto err; + + cmp_res = cmp_master_pos(sev, mi); + delete sev; + + if (!cmp_res) + { + /* Copy basename */ + fn_format(mi->log_file_name, linfo.log_file_name, "","",1); + mi->pos = my_b_tell(&log); + goto mi_inited; + } + else if (cmp_res > 0) + { + if (!last_pos) + { + strmov(errmsg, + "Slave event in first log points past the target position"); + goto err; + } + end_io_cache(&log); + (void) my_close(file, MYF(MY_WME)); + if (init_io_cache(&log, (file = last_file), IO_SIZE, READ_CACHE, 0, 0, + MYF(MY_WME))) + { + errmsg[0] = 0; + goto err; + } + break; + } + + strmov(last_log_name, linfo.log_file_name); + last_pos = my_b_tell(&log); + + switch (mysql_bin_log.find_next_log(&linfo)) { + case LOG_INFO_EOF: + if (last_file >= 0) + (void)my_close(last_file, MYF(MY_WME)); + last_file = -1; + goto found_log; + case 0: + break; + default: + strmov(errmsg, "Error reading log index"); + goto err; + } + + end_io_cache(&log); + if (last_file >= 0) + (void) my_close(last_file, MYF(MY_WME)); + last_file = file; + } + +found_log: + my_b_seek(&log, last_pos); + if (find_target_pos(mi,&log,errmsg)) + goto err; + fn_format(mi->log_file_name, last_log_name, "","",1); /* Copy basename */ + +mi_inited: + error = 0; +err: + pthread_mutex_unlock(log_lock); + end_io_cache(&log); + pthread_mutex_lock(&LOCK_thread_count); + thd->current_linfo = 0; + pthread_mutex_unlock(&LOCK_thread_count); + if (file >= 0) + (void) my_close(file, MYF(MY_WME)); + if (last_file >= 0 && last_file != file) + (void) my_close(last_file, MYF(MY_WME)); + + return error; +} + +// caller must delete result when done +static Slave_log_event* find_slave_event(IO_CACHE* log, + const char* log_file_name, + char* errmsg) +{ + Log_event* ev; + int i; + bool slave_event_found = 0; + LINT_INIT(ev); + + for (i = 0; i < 2; i++) + { + if (!(ev = Log_event::read_log_event(log, (pthread_mutex_t*)0, 0))) + { + my_snprintf(errmsg, SLAVE_ERRMSG_SIZE, + "Error reading event in log '%s'", + (char*)log_file_name); + return 0; + } + if (ev->get_type_code() == SLAVE_EVENT) + { + slave_event_found = 1; + break; + } + delete ev; + } + if (!slave_event_found) + { + my_snprintf(errmsg, SLAVE_ERRMSG_SIZE, + "Could not find slave event in log '%s'", + (char*)log_file_name); + delete ev; + return 0; + } + + return (Slave_log_event*)ev; +} + + +int show_new_master(THD* thd) +{ + DBUG_ENTER("show_new_master"); + List<Item> field_list; + char errmsg[SLAVE_ERRMSG_SIZE]; + LEX_MASTER_INFO* lex_mi = &thd->lex.mi; + + errmsg[0]=0; // Safety + if (translate_master(thd, lex_mi, errmsg)) + { + if (errmsg[0]) + net_printf(&thd->net, ER_ERROR_WHEN_EXECUTING_COMMAND, + "SHOW NEW MASTER", errmsg); + else + send_error(&thd->net, 0); + + DBUG_RETURN(1); + } + else + { + String* packet = &thd->packet; + field_list.push_back(new Item_empty_string("Log_name", 20)); + field_list.push_back(new Item_empty_string("Log_pos", 20)); + if (send_fields(thd, field_list, 1)) + DBUG_RETURN(-1); + packet->length(0); + net_store_data(packet, lex_mi->log_file_name); + net_store_data(packet, (longlong)lex_mi->pos); + if (my_net_write(&thd->net, packet->ptr(), packet->length())) + DBUG_RETURN(-1); + send_eof(&thd->net); + DBUG_RETURN(0); + } +} + +int update_slave_list(MYSQL* mysql) +{ + MYSQL_RES* res=0; + MYSQL_ROW row; + const char* error=0; + bool have_auth_info; + int port_ind; + + if (mc_mysql_query(mysql,"SHOW SLAVE HOSTS",0) || + !(res = mc_mysql_store_result(mysql))) + { + error = "Query error"; + goto err; + } + + switch (mc_mysql_num_fields(res)) + { + case 5: + have_auth_info = 0; + port_ind=2; + break; + case 7: + have_auth_info = 1; + port_ind=4; + break; + default: + error = "Invalid number of fields in SHOW SLAVE HOSTS"; + goto err; + } + + pthread_mutex_lock(&LOCK_slave_list); + + while ((row = mc_mysql_fetch_row(res))) + { + uint32 server_id; + SLAVE_INFO* si, *old_si; + server_id = atoi(row[0]); + if ((old_si = (SLAVE_INFO*)hash_search(&slave_list, + (byte*)&server_id,4))) + si = old_si; + else + { + if (!(si = (SLAVE_INFO*)my_malloc(sizeof(SLAVE_INFO), MYF(MY_WME)))) + { + error = "Out of memory"; + pthread_mutex_unlock(&LOCK_slave_list); + goto err; + } + si->server_id = server_id; + hash_insert(&slave_list, (byte*)si); + } + strnmov(si->host, row[1], sizeof(si->host)); + si->port = atoi(row[port_ind]); + si->rpl_recovery_rank = atoi(row[port_ind+1]); + si->master_id = atoi(row[port_ind+2]); + if (have_auth_info) + { + strnmov(si->user, row[2], sizeof(si->user)); + strnmov(si->password, row[3], sizeof(si->password)); + } + } + pthread_mutex_unlock(&LOCK_slave_list); +err: + if (res) + mc_mysql_free_result(res); + if (error) + { + sql_print_error("Error updating slave list:",error); + return 1; + } + return 0; +} + +int find_recovery_captain(THD* thd, MYSQL* mysql) +{ + + return 0; +} + +pthread_handler_decl(handle_failsafe_rpl,arg) +{ + DBUG_ENTER("handle_failsafe_rpl"); + THD *thd = new THD; + thd->thread_stack = (char*)&thd; + MYSQL* recovery_captain = 0; + pthread_detach_this_thread(); + if (init_failsafe_rpl_thread(thd) || !(recovery_captain=mc_mysql_init(0))) + { + sql_print_error("Could not initialize failsafe replication thread"); + goto err; + } + pthread_mutex_lock(&LOCK_rpl_status); + while (!thd->killed && !abort_loop) + { + bool break_req_chain = 0; + const char* msg = thd->enter_cond(&COND_rpl_status, + &LOCK_rpl_status, "Waiting for request"); + pthread_cond_wait(&COND_rpl_status, &LOCK_rpl_status); + thd->proc_info="Processing request"; + while (!break_req_chain) + { + switch (rpl_status) + { + case RPL_LOST_SOLDIER: + if (find_recovery_captain(thd, recovery_captain)) + rpl_status=RPL_TROOP_SOLDIER; + else + rpl_status=RPL_RECOVERY_CAPTAIN; + break_req_chain=1; /* for now until other states are implemented */ + break; + default: + break_req_chain=1; + break; + } + } + thd->exit_cond(msg); + } + pthread_mutex_unlock(&LOCK_rpl_status); +err: + if (recovery_captain) + mc_mysql_close(recovery_captain); + delete thd; + my_thread_end(); + pthread_exit(0); + DBUG_RETURN(0); +} + +int show_slave_hosts(THD* thd) +{ + List<Item> field_list; + NET* net = &thd->net; + String* packet = &thd->packet; + DBUG_ENTER("show_slave_hosts"); + + 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)); + field_list.push_back(new Item_empty_string("Rpl_recovery_rank", 20)); + field_list.push_back(new Item_empty_string("Master_id", 20)); + + if (send_fields(thd, field_list, 1)) + DBUG_RETURN(-1); + + pthread_mutex_lock(&LOCK_slave_list); + + for (uint 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, (uint32) si->port); + net_store_data(packet, si->rpl_recovery_rank); + net_store_data(packet, si->master_id); + 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 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, MASTER_INFO* mi) +{ + 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; + table.updating = 1; + if (!tables_ok(thd, &table)) + continue; + } + if ((error = fetch_master_table(thd, db, table_name, 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; + const char* errmsg=0; + int restart_thread_mask; + mc_mysql_init(&mysql); + + // we do not want anyone messing with the slave at all for the entire + // duration of the data load; + LOCK_ACTIVE_MI; + lock_slave_threads(active_mi); + init_thread_mask(&restart_thread_mask,active_mi,0 /*not inverse*/); + if (restart_thread_mask && + (error=terminate_slave_threads(active_mi,restart_thread_mask, + 1 /*skip lock*/))) + { + send_error(&thd->net,error); + unlock_slave_threads(active_mi); + UNLOCK_ACTIVE_MI; + return 1; + } + + if (connect_to_master(thd, &mysql, active_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; + + 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 = (uint) 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++) + { + // since we know how many rows we have, this can never be NULL + MYSQL_ROW row = mc_mysql_fetch_row(db_res); + char* db = row[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 + TODO - 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 (mysql_rm_db(thd, db, 1,1) || + mysql_create_db(thd, db, 0, 1)) + { + send_error(&thd->net, 0, 0); + 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,active_mi))) + { + // 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(active_mi->master_log_name, row[0], + sizeof(active_mi->master_log_name)); + active_mi->master_log_pos = strtoull(row[1], (char**) 0, 10); + if (active_mi->master_log_pos < 4) + active_mi->master_log_pos = 4; // don't hit the magic number + active_mi->rli.pending = 0; + flush_master_info(active_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; + } + } + thd->proc_info="purging old relay logs"; + if (purge_relay_logs(&active_mi->rli,0 /* not only reset, but also reinit*/, + &errmsg)) + { + send_error(&thd->net, 0, "Failed purging old relay logs"); + unlock_slave_threads(active_mi); + UNLOCK_ACTIVE_MI; + return 1; + } + pthread_mutex_lock(&active_mi->rli.data_lock); + active_mi->rli.master_log_pos = active_mi->master_log_pos; + strnmov(active_mi->rli.master_log_name,active_mi->master_log_name, + sizeof(active_mi->rli.master_log_name)); + flush_relay_log_info(&active_mi->rli); + pthread_cond_broadcast(&active_mi->rli.data_cond); + pthread_mutex_unlock(&active_mi->rli.data_lock); + thd->proc_info = "starting slave"; + if (restart_thread_mask) + { + error=start_slave_threads(0 /* mutex not needed*/, + 1 /* wait for start*/, + active_mi,master_info_file,relay_log_info_file, + restart_thread_mask); + } + +err: + unlock_slave_threads(active_mi); + UNLOCK_ACTIVE_MI; + thd->proc_info = 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/repl_failsafe.h b/sql/repl_failsafe.h new file mode 100644 index 00000000000..77bc03ce8c0 --- /dev/null +++ b/sql/repl_failsafe.h @@ -0,0 +1,38 @@ +#ifndef REPL_FAILSAFE_H +#define REPL_FAILSAFE_H + +#include "mysql.h" +#include "my_sys.h" +#include "slave.h" + +typedef enum {RPL_AUTH_MASTER=0,RPL_ACTIVE_SLAVE,RPL_IDLE_SLAVE, + RPL_LOST_SOLDIER,RPL_TROOP_SOLDIER, + RPL_RECOVERY_CAPTAIN,RPL_NULL /* inactive */, + RPL_ANY /* wild card used by change_rpl_status */ } RPL_STATUS; +extern RPL_STATUS rpl_status; + +extern pthread_mutex_t LOCK_rpl_status; +extern pthread_cond_t COND_rpl_status; +extern TYPELIB rpl_role_typelib, rpl_status_typelib; +extern uint rpl_recovery_rank; +extern const char* rpl_role_type[], *rpl_status_type[]; + +pthread_handler_decl(handle_failsafe_rpl,arg); +void change_rpl_status(RPL_STATUS from_status, RPL_STATUS to_status); +int find_recovery_captain(THD* thd, MYSQL* mysql); +int update_slave_list(MYSQL* mysql); + +extern HASH slave_list; + +int load_master_data(THD* thd); +int connect_to_master(THD *thd, MYSQL* mysql, MASTER_INFO* mi); + +int show_new_master(THD* thd); +int show_slave_hosts(THD* thd); +int translate_master(THD* thd, LEX_MASTER_INFO* mi, char* errmsg); +void init_slave_list(); +void end_slave_list(); +int register_slave(THD* thd, uchar* packet, uint packet_length); +void unregister_slave(THD* thd, bool only_mine, bool need_mutex); + +#endif diff --git a/sql/share/Makefile.am b/sql/share/Makefile.am index b72f7493e20..c70ac9ccf57 100644 --- a/sql/share/Makefile.am +++ b/sql/share/Makefile.am @@ -28,5 +28,11 @@ install-data-local: $(INSTALL_DATA) $(srcdir)/charsets/Index $(DESTDIR)$(pkgdatadir)/charsets/Index $(INSTALL_DATA) $(srcdir)/charsets/*.conf $(DESTDIR)$(pkgdatadir)/charsets +fix_errors: + for lang in @AVAILABLE_LANGUAGES@; \ + do \ + ../../extra/comp_err $(srcdir)/$$lang/errmsg.txt $(srcdir)/$$lang/errmsg.sys; \ + done + # Don't update the files from bitkeeper %::SCCS/s.% diff --git a/sql/share/charsets/Index b/sql/share/charsets/Index index b91e27e7c02..5cf30682cc0 100644 --- a/sql/share/charsets/Index +++ b/sql/share/charsets/Index @@ -1,6 +1,7 @@ # sql/share/charsets/Index # -# This file lists all of the available character sets. +# This file lists all of the available character sets. Please keep this +# file sorted by character set number. big5 1 @@ -34,3 +35,4 @@ croat 27 gbk 28 cp1257 29 latin5 30 +latin1_de 31 diff --git a/sql/share/czech/errmsg.txt b/sql/share/czech/errmsg.txt index 093954c202b..11a3063151d 100644 --- a/sql/share/czech/errmsg.txt +++ b/sql/share/czech/errmsg.txt @@ -14,198 +14,198 @@ "isamchk", "NE", "ANO", -"Nemohu vytvo-Bøit soubor '%-.64s' (chybový kód: %d)",-A -"Nemohu vytvo-Bøit tabulku '%-.64s' (chybový kód: %d)",-A -"Nemohu vytvo-Bøit databázi '%-.64s', chyba %d",-A -"Nemohu vytvo-Bøit databázi '%-.64s', databáze ji¾ existuje",-A -"Nemohu zru-B¹it databázi '%-.64s', databáze neexistuje",-A -"Chyba p-Bøi ru¹ení databáze (nemohu vymazat '%-.64s', chyba %d)",-A -"Chyba p-Bøi ru¹ení databáze (nemohu vymazat adresáø '%-.64s', chyba %d)",-A -"Chyba p-Bøi výmazu '%-.64s' (chybový kód: %d)",-A -"Nemohu -Bèíst záznam v systémové tabulce",-A -"Nemohu z-Bískat stav '%-.64s' (chybový kód: %d)",-A -"Chyba p-Bøi zji¹»ování pracovní adresáø (chybový kód: %d)",-A -"Nemohu uzamknout soubor (chybov-Bý kód: %d)",-A -"Nemohu otev-Bøít soubor '%-.64s' (chybový kód: %d)",-A -"Nemohu naj-Bít soubor '%-.64s' (chybový kód: %d)",-A -"Nemohu -Bèíst adresáø '%-.64s' (chybový kód: %d)",-A -"Nemohu zm-Bìnit adresáø na '%-.64s' (chybový kód: %d)",-A -"Z-Báznam byl zmìnìn od posledního ètení v tabulce '%-.64s'",-A -"Disk je pln-Bý (%s), èekám na uvolnìní nìjakého místa ...",-A -"Nemohu zapsat, zdvojen-Bý klíè v tabulce '%-.64s'",-A -"Chyba p-Bøi zavírání '%-.64s' (chybový kód: %d)",-A -"Chyba p-Bøi ètení souboru '%-.64s' (chybový kód: %d)",-A -"Chyba p-Bøi pøejmenování '%-.64s' na '%-.64s' (chybový kód: %d)",-A -"Chyba p-Bøi zápisu do souboru '%-.64s' (chybový kód: %d)",-A -"'%-.64s' je zam-Bèen proti zmìnám",-A -"T-Bøídìní pøeru¹eno",-A +"Nemohu vytvo-Bøit soubor '%-.64s' (chybový kód: %d)", +"Nemohu vytvo-Bøit tabulku '%-.64s' (chybový kód: %d)", +"Nemohu vytvo-Bøit databázi '%-.64s', chyba %d", +"Nemohu vytvo-Bøit databázi '%-.64s', databáze ji¾ existuje", +"Nemohu zru-B¹it databázi '%-.64s', databáze neexistuje", +"Chyba p-Bøi ru¹ení databáze (nemohu vymazat '%-.64s', chyba %d)", +"Chyba p-Bøi ru¹ení databáze (nemohu vymazat adresáø '%-.64s', chyba %d)", +"Chyba p-Bøi výmazu '%-.64s' (chybový kód: %d)", +"Nemohu -Bèíst záznam v systémové tabulce", +"Nemohu z-Bískat stav '%-.64s' (chybový kód: %d)", +"Chyba p-Bøi zji¹»ování pracovní adresáø (chybový kód: %d)", +"Nemohu uzamknout soubor (chybov-Bý kód: %d)", +"Nemohu otev-Bøít soubor '%-.64s' (chybový kód: %d)", +"Nemohu naj-Bít soubor '%-.64s' (chybový kód: %d)", +"Nemohu -Bèíst adresáø '%-.64s' (chybový kód: %d)", +"Nemohu zm-Bìnit adresáø na '%-.64s' (chybový kód: %d)", +"Z-Báznam byl zmìnìn od posledního ètení v tabulce '%-.64s'", +"Disk je pln-Bý (%s), èekám na uvolnìní nìjakého místa ...", +"Nemohu zapsat, zdvojen-Bý klíè v tabulce '%-.64s'", +"Chyba p-Bøi zavírání '%-.64s' (chybový kód: %d)", +"Chyba p-Bøi ètení souboru '%-.64s' (chybový kód: %d)", +"Chyba p-Bøi pøejmenování '%-.64s' na '%-.64s' (chybový kód: %d)", +"Chyba p-Bøi zápisu do souboru '%-.64s' (chybový kód: %d)", +"'%-.64s' je zam-Bèen proti zmìnám", +"T-Bøídìní pøeru¹eno", "Pohled '%-.64s' pro '%-.64s' neexistuje", -"Obsluha tabulky vr-Bátila chybu %d",-A -"Obsluha tabulky '%-.64s' nem-Bá tento parametr",-A -"Nemohu naj-Bít záznam v '%-.64s'",-A -"Nespr-Bávná informace v souboru '%-.64s'",-A -"Nespr-Bávný klíè pro tabulku '%-.64s'. Pokuste se ho opravit",-A -"Star-Bý klíèový soubor pro '%-.64s'. Opravte ho.",-A -"'%-.64s' je jen pro -Bètení",-A -"M-Bálo pamìti. Pøestartujte daemona a zkuste znovu (je potøeba %d bytù)",-A -"M-Bálo pamìti pro tøídìní. Zvy¹te velikost tøídícího bufferu",-A -"Neo-Bèekávaný konec souboru pøi ètení '%-.64s' (chybový kód: %d)",-A -"P-Bøíli¹ mnoho spojení",-A -"M-Bálo prostoru/pamìti pro thread",-A -"Nemohu zjistit jm-Béno stroje pro Va¹i adresu",-A -"Chyba p-Bøi ustavování spojení",-A -"P-Bøístup pro u¾ivatele '%-.32s@%-.64s' k databázi '%-.64s' není povolen",-A -"P-Bøístup pro u¾ivatele '%-.32s@%-.64s' (s heslem %s)",-A -"Nebyla vybr-Bána ¾ádná databáze",-A -"Nezn-Bámý pøíkaz",-A -"Sloupec '%-.64s' nem-Bù¾e být null",-A -"Nezn-Bámá databáze '%-.64s'",-A -"Tabulka '%-.64s' ji-B¾ existuje",-A -"Nezn-Bámá tabulka '%-.64s'",-A -"Sloupec '%-.64s' v %s nen-Bí zcela jasný",-A -"Prob-Bíhá ukonèování práce serveru",-A -"Nezn-Bámý sloupec '%-.64s' v %s",-A -"Pou-B¾ité '%-.64s' nebylo v group by",-A -"Nemohu pou-B¾ít group na '%-.64s'",-A -"P-Bøíkaz obsahuje zároveò funkci sum a sloupce",-A -"Po-Bèet sloupcù neodpovídá zadané hodnotì",-A -"Jm-Béno identifikátoru '%-.64s' je pøíli¹ dlouhé",-A -"Zdvojen-Bé jméno sloupce '%-.64s'",-A -"Zdvojen-Bé jméno klíèe '%-.64s'",-A -"Zvojen-Bý klíè '%-.64s' (èíslo klíèe %d)",-A -"Chybn-Bá specifikace sloupce '%-.64s'",-A -"%s bl-Bízko '%-.64s' na øádku %d",-A -"V-Býsledek dotazu je prázdný",-A -"Nejednozna-Bèná tabulka/alias: '%-.64s'",-A -"Chybn-Bá defaultní hodnota pro '%-.64s'",-A -"Definov-Báno více primárních klíèù",-A -"Zad-Báno pøíli¹ mnoho klíèù, je povoleno nejvíce %d klíèù",-A -"Zad-Báno pøíli¹ mnoho èást klíèù, je povoleno nejvíce %d èástí",-A -"Zadan-Bý klíè byl pøíli¹ dlouhý, nejvìt¹í délka klíèe je %d",-A -"Kl-Bíèový sloupec '%-.64s' v tabulce neexistuje",-A -"Blob sloupec '%-.64s' nem-Bù¾e být pou¾it jako klíè",-A -"P-Bøíli¹ velká délka sloupce '%-.64s' (nejvíce %d). Pou¾ijte BLOB",-A -"M-Bù¾ete mít pouze jedno AUTO pole a to musí být definováno jako klíè",-A -"%s: p-Bøipraven na spojení\n",-A -"%s: norm-Bální ukonèení\n",-A -"%s: p-Bøijat signal %d, konèím\n",-A -"%s: ukon-Bèení práce hotovo\n",-A -"%s: n-Básilné uzavøení threadu %ld u¾ivatele '%-.64s'\n",-A -"Nemohu vytvo-Bøit IP socket",-A -"Tabulka '%-.64s' nem-Bá index odpovídající CREATE INDEX. Vytvoøte tabulku znovu",-A -"Argument separ-Bátoru polo¾ek nebyl oèekáván. Pøeètìte si manuál",-A -"Nen-Bí mo¾né pou¾ít pevný rowlength s BLOBem. Pou¾ijte 'fields terminated by'.",-A -"Soubor '%-.64s' mus-Bí být v adresáøi databáze nebo èitelný pro v¹echny",-A -"Soubor '%-.64s' ji-B¾ existuje",-A -"Z-Báznamù: %ld Vymazáno: %ld Pøeskoèeno: %ld Varování: %ld",-A -"Z-Báznamù: %ld Zdvojených: %ld",-A -"Chybn-Bá podèást klíèe -- není to øetìzec nebo je del¹í ne¾ délka èásti klíèe",-A -"Nen-Bí mo¾né vymazat v¹echny polo¾ky s ALTER TABLE. Pou¾ijte DROP TABLE",-A -"Nemohu zru-B¹it '%-.64s' (provést DROP). Zkontrolujte, zda neexistují záznamy/klíèe",-A -"Z-Báznamù: %ld Zdvojených: %ld Varování: %ld",-A -"INSERT TABLE '%-.64s' nen-Bí dovoleno v seznamu tabulek FROM",-A -"Nezn-Bámá identifikace threadu: %lu",-A -"Nejste vlastn-Bíkem threadu %lu",-A -"Nejsou pou-B¾ity ¾ádné tabulky",-A -"P-Bøíli¹ mnoho øetìzcù pro sloupec %s a SET",-A -"Nemohu vytvo-Bøit jednoznaèné jméno logovacího souboru %s.(1-999)\n",-A -"Tabulka '%-.64s' byla zam-Bèena s READ a nemù¾e být zmìnìna",-A -"Tabulka '%-.64s' nebyla zam-Bèena s LOCK TABLES",-A -"Blob polo-B¾ka '%-.64s' nemù¾e mít defaultní hodnotu",-A -"Nep-Bøípustné jméno databáze '%-.64s'",-A -"Nep-Bøípustné jméno tabulky '%-.64s'",-A -"Zadan-Bý SELECT by procházel pøíli¹ mnoho záznamù a trval velmi dlouho. Zkontrolujte tvar WHERE a je-li SELECT v poøádku, pou¾ijte SET OPTION SQL_BIG_SELECTS=1",-A -"Nezn-Bámá chyba",-A -"Nezn-Bámá procedura %s",-A -"Chybn-Bý poèet parametrù procedury %s",-A -"Chybn-Bé parametry procedury %s",-A -"Nezn-Bámá tabulka '%-.64s' v %s",-A -"Polo-B¾ka '%-.64s' je zadána dvakrát",-A -"Nespr-Bávné pou¾ití funkce group",-A -"Tabulka '%-.64s' pou-B¾ívá roz¹íøení, které v této verzi MySQL není",-A -"Tabulka mus-Bí mít alespoò jeden sloupec",-A -"Tabulka '%-.64s' je pln-Bá",-A -"Nezn-Bámá znaková sada: '%-.64s'",-A -"P-Bøíli¹ mnoho tabulek, MySQL jich mù¾e mít v joinu jen %d",-A -"P-Bøíli¹ mnoho polo¾ek",-A -"-BØádek je pøíli¹ velký. Maximální velikost øádku, nepoèítaje polo¾ky blob, je %d. Musíte zmìnit nìkteré polo¾ky na blob",-A -"P-Bøeteèení zásobníku threadu: pou¾ito %ld z %ld. Pou¾ijte 'mysqld -O thread_stack=#' k zadání vìt¹ího zásobníku",-A -"V OUTER JOIN byl nalezen k-Bøí¾ový odkaz. Provìøte ON podmínky",-A -"Sloupec '%-.32s' je pou-B¾it s UNIQUE nebo INDEX, ale není definován jako NOT NULL",-A -"Nemohu na-Bèíst funkci '%-.64s'",-A +"Obsluha tabulky vr-Bátila chybu %d", +"Obsluha tabulky '%-.64s' nem-Bá tento parametr", +"Nemohu naj-Bít záznam v '%-.64s'", +"Nespr-Bávná informace v souboru '%-.64s'", +"Nespr-Bávný klíè pro tabulku '%-.64s'. Pokuste se ho opravit", +"Star-Bý klíèový soubor pro '%-.64s'. Opravte ho.", +"'%-.64s' je jen pro -Bètení", +"M-Bálo pamìti. Pøestartujte daemona a zkuste znovu (je potøeba %d bytù)", +"M-Bálo pamìti pro tøídìní. Zvy¹te velikost tøídícího bufferu", +"Neo-Bèekávaný konec souboru pøi ètení '%-.64s' (chybový kód: %d)", +"P-Bøíli¹ mnoho spojení", +"M-Bálo prostoru/pamìti pro thread", +"Nemohu zjistit jm-Béno stroje pro Va¹i adresu", +"Chyba p-Bøi ustavování spojení", +"P-Bøístup pro u¾ivatele '%-.32s@%-.64s' k databázi '%-.64s' není povolen", +"P-Bøístup pro u¾ivatele '%-.32s@%-.64s' (s heslem %s)", +"Nebyla vybr-Bána ¾ádná databáze", +"Nezn-Bámý pøíkaz", +"Sloupec '%-.64s' nem-Bù¾e být null", +"Nezn-Bámá databáze '%-.64s'", +"Tabulka '%-.64s' ji-B¾ existuje", +"Nezn-Bámá tabulka '%-.64s'", +"Sloupec '%-.64s' v %s nen-Bí zcela jasný", +"Prob-Bíhá ukonèování práce serveru", +"Nezn-Bámý sloupec '%-.64s' v %s", +"Pou-B¾ité '%-.64s' nebylo v group by", +"Nemohu pou-B¾ít group na '%-.64s'", +"P-Bøíkaz obsahuje zároveò funkci sum a sloupce", +"Po-Bèet sloupcù neodpovídá zadané hodnotì", +"Jm-Béno identifikátoru '%-.64s' je pøíli¹ dlouhé", +"Zdvojen-Bé jméno sloupce '%-.64s'", +"Zdvojen-Bé jméno klíèe '%-.64s'", +"Zvojen-Bý klíè '%-.64s' (èíslo klíèe %d)", +"Chybn-Bá specifikace sloupce '%-.64s'", +"%s bl-Bízko '%-.64s' na øádku %d", +"V-Býsledek dotazu je prázdný", +"Nejednozna-Bèná tabulka/alias: '%-.64s'", +"Chybn-Bá defaultní hodnota pro '%-.64s'", +"Definov-Báno více primárních klíèù", +"Zad-Báno pøíli¹ mnoho klíèù, je povoleno nejvíce %d klíèù", +"Zad-Báno pøíli¹ mnoho èást klíèù, je povoleno nejvíce %d èástí", +"Zadan-Bý klíè byl pøíli¹ dlouhý, nejvìt¹í délka klíèe je %d", +"Kl-Bíèový sloupec '%-.64s' v tabulce neexistuje", +"Blob sloupec '%-.64s' nem-Bù¾e být pou¾it jako klíè", +"P-Bøíli¹ velká délka sloupce '%-.64s' (nejvíce %d). Pou¾ijte BLOB", +"M-Bù¾ete mít pouze jedno AUTO pole a to musí být definováno jako klíè", +"%s: p-Bøipraven na spojení\n", +"%s: norm-Bální ukonèení\n", +"%s: p-Bøijat signal %d, konèím\n", +"%s: ukon-Bèení práce hotovo\n", +"%s: n-Básilné uzavøení threadu %ld u¾ivatele '%-.64s'\n", +"Nemohu vytvo-Bøit IP socket", +"Tabulka '%-.64s' nem-Bá index odpovídající CREATE INDEX. Vytvoøte tabulku znovu", +"Argument separ-Bátoru polo¾ek nebyl oèekáván. Pøeètìte si manuál", +"Nen-Bí mo¾né pou¾ít pevný rowlength s BLOBem. Pou¾ijte 'fields terminated by'.", +"Soubor '%-.64s' mus-Bí být v adresáøi databáze nebo èitelný pro v¹echny", +"Soubor '%-.64s' ji-B¾ existuje", +"Z-Báznamù: %ld Vymazáno: %ld Pøeskoèeno: %ld Varování: %ld", +"Z-Báznamù: %ld Zdvojených: %ld", +"Chybn-Bá podèást klíèe -- není to øetìzec nebo je del¹í ne¾ délka èásti klíèe", +"Nen-Bí mo¾né vymazat v¹echny polo¾ky s ALTER TABLE. Pou¾ijte DROP TABLE", +"Nemohu zru-B¹it '%-.64s' (provést DROP). Zkontrolujte, zda neexistují záznamy/klíèe", +"Z-Báznamù: %ld Zdvojených: %ld Varování: %ld", +"INSERT TABLE '%-.64s' nen-Bí dovoleno v seznamu tabulek FROM", +"Nezn-Bámá identifikace threadu: %lu", +"Nejste vlastn-Bíkem threadu %lu", +"Nejsou pou-B¾ity ¾ádné tabulky", +"P-Bøíli¹ mnoho øetìzcù pro sloupec %s a SET", +"Nemohu vytvo-Bøit jednoznaèné jméno logovacího souboru %s.(1-999)\n", +"Tabulka '%-.64s' byla zam-Bèena s READ a nemù¾e být zmìnìna", +"Tabulka '%-.64s' nebyla zam-Bèena s LOCK TABLES", +"Blob polo-B¾ka '%-.64s' nemù¾e mít defaultní hodnotu", +"Nep-Bøípustné jméno databáze '%-.64s'", +"Nep-Bøípustné jméno tabulky '%-.64s'", +"Zadan-Bý SELECT by procházel pøíli¹ mnoho záznamù a trval velmi dlouho. Zkontrolujte tvar WHERE a je-li SELECT v poøádku, pou¾ijte SET OPTION SQL_BIG_SELECTS=1", +"Nezn-Bámá chyba", +"Nezn-Bámá procedura %s", +"Chybn-Bý poèet parametrù procedury %s", +"Chybn-Bé parametry procedury %s", +"Nezn-Bámá tabulka '%-.64s' v %s", +"Polo-B¾ka '%-.64s' je zadána dvakrát", +"Nespr-Bávné pou¾ití funkce group", +"Tabulka '%-.64s' pou-B¾ívá roz¹íøení, které v této verzi MySQL není", +"Tabulka mus-Bí mít alespoò jeden sloupec", +"Tabulka '%-.64s' je pln-Bá", +"Nezn-Bámá znaková sada: '%-.64s'", +"P-Bøíli¹ mnoho tabulek, MySQL jich mù¾e mít v joinu jen %d", +"P-Bøíli¹ mnoho polo¾ek", +"-BØádek je pøíli¹ velký. Maximální velikost øádku, nepoèítaje polo¾ky blob, je %d. Musíte zmìnit nìkteré polo¾ky na blob", +"P-Bøeteèení zásobníku threadu: pou¾ito %ld z %ld. Pou¾ijte 'mysqld -O thread_stack=#' k zadání vìt¹ího zásobníku", +"V OUTER JOIN byl nalezen k-Bøí¾ový odkaz. Provìøte ON podmínky", +"Sloupec '%-.32s' je pou-B¾it s UNIQUE nebo INDEX, ale není definován jako NOT NULL", +"Nemohu na-Bèíst funkci '%-.64s'", "Nemohu inicializovat funkci '%-.64s'; %-.80s", -"Pro sd-Bílenou knihovnu nejsou povoleny cesty",-A -"Funkce '%-.64s' ji-B¾ existuje",-A -"Nemohu otev-Bøít sdílenou knihovnu '%-.64s' (errno: %d %s)",-A -"Nemohu naj-Bít funkci '%-.64s' v knihovnì'",-A -"Funkce '%-.64s' nen-Bí definována",-A -"Stroj '%-.64s' je zablokov-Bán kvùli mnoha chybám pøi pøipojování. Odblokujete pou¾itím 'mysqladmin flush-hosts'",-A -"Stroj '%-.64s' nem-Bá povoleno se k tomuto MySQL serveru pøipojit",-A -"Pou-B¾íváte MySQL jako anonymní u¾ivatel a anonymní u¾ivatelé nemají povoleno mìnit hesla",-A -"Na zm-Bìnu hesel ostatním musíte mít právo provést update tabulek v databázi mysql",-A -"V tabulce user nen-Bí ¾ádný odpovídající øádek",-A -"Nalezen-Bých øádkù: %ld Zmìnìno: %ld Varování: %ld",-A -"Nemohu vytvo-Bøit nový thread (errno %d). Pokud je je¹tì nìjaká volná pamì», podívejte se do manuálu na èást o chybách specifických pro jednotlivé operaèní systémy",-A -"Po-Bèet sloupcù neodpovídá poètu hodnot na øádku %ld",-A -"Nemohu znovuotev-Bøít tabulku: '%-.64s',-A -"Neplatn-Bé u¾ití hodnoty NULL",-A -"Regul-Bární výraz vrátil chybu '%-.64s'",-A -"Pokud nen-Bí ¾ádná GROUP BY klauzule, není dovoleno souèasné pou¾ití GROUP polo¾ek (MIN(),MAX(),COUNT()...) s ne GROUP polo¾kami",-A -"Neexistuje odpov-Bídající grant pro u¾ivatele '%-.32s' na stroji '%-.64s'",-A -"%-.16s p-Bøíkaz nepøístupný pro u¾ivatele: '%-.32s@%-.64s' pro tabulku '%-.64s'",-A -"%-.16s p-Bøíkaz nepøístupný pro u¾ivatele: '%-.32s@%-.64s' pro sloupec '%-.64s' v tabulce '%-.64s'",-A -"Neplatn-Bý pøíkaz GRANT/REVOKE. Prosím, pøeètìte si v manuálu, jaká privilegia je mo¾né pou¾ít.",-A -"Argument p-Bøíkazu GRANT u¾ivatel nebo stroj je pøíli¹ dlouhý",-A +"Pro sd-Bílenou knihovnu nejsou povoleny cesty", +"Funkce '%-.64s' ji-B¾ existuje", +"Nemohu otev-Bøít sdílenou knihovnu '%-.64s' (errno: %d %s)", +"Nemohu naj-Bít funkci '%-.64s' v knihovnì'", +"Funkce '%-.64s' nen-Bí definována", +"Stroj '%-.64s' je zablokov-Bán kvùli mnoha chybám pøi pøipojování. Odblokujete pou¾itím 'mysqladmin flush-hosts'", +"Stroj '%-.64s' nem-Bá povoleno se k tomuto MySQL serveru pøipojit", +"Pou-B¾íváte MySQL jako anonymní u¾ivatel a anonymní u¾ivatelé nemají povoleno mìnit hesla", +"Na zm-Bìnu hesel ostatním musíte mít právo provést update tabulek v databázi mysql", +"V tabulce user nen-Bí ¾ádný odpovídající øádek", +"Nalezen-Bých øádkù: %ld Zmìnìno: %ld Varování: %ld", +"Nemohu vytvo-Bøit nový thread (errno %d). Pokud je je¹tì nìjaká volná pamì», podívejte se do manuálu na èást o chybách specifických pro jednotlivé operaèní systémy", +"Po-Bèet sloupcù neodpovídá poètu hodnot na øádku %ld", +"Nemohu znovuotev-Bøít tabulku: '%-.64s', +"Neplatn-Bé u¾ití hodnoty NULL", +"Regul-Bární výraz vrátil chybu '%-.64s'", +"Pokud nen-Bí ¾ádná GROUP BY klauzule, není dovoleno souèasné pou¾ití GROUP polo¾ek (MIN(),MAX(),COUNT()...) s ne GROUP polo¾kami", +"Neexistuje odpov-Bídající grant pro u¾ivatele '%-.32s' na stroji '%-.64s'", +"%-.16s p-Bøíkaz nepøístupný pro u¾ivatele: '%-.32s@%-.64s' pro tabulku '%-.64s'", +"%-.16s p-Bøíkaz nepøístupný pro u¾ivatele: '%-.32s@%-.64s' pro sloupec '%-.64s' v tabulce '%-.64s'", +"Neplatn-Bý pøíkaz GRANT/REVOKE. Prosím, pøeètìte si v manuálu, jaká privilegia je mo¾né pou¾ít.", +"Argument p-Bøíkazu GRANT u¾ivatel nebo stroj je pøíli¹ dlouhý", "Tabulka '%-64s.%s' neexistuje", -"Neexistuje odpov-Bídající grant pro u¾ivatele '%-.32s' na stroji '%-.64s' pro tabulku '%-.64s'",-A -"Pou-B¾itý pøíkaz není v této verzi MySQL povolen",-A -"Va-B¹e syntaxe je nìjaká divná",-A -"Zpo-B¾dìný insert threadu nebyl schopen získat po¾adovaný zámek pro tabulku %-.64s",-A -"P-Bøíli¹ mnoho zpo¾dìných threadù",-A -"Zru-B¹eno spojení %ld do databáze: '%-.64s' u¾ivatel: '%-.64s' (%s)",-A -"Zji-B¹tìn pøíchozí packet del¹í ne¾ 'max_allowed_packet'",-A -"Zji-B¹tìna chyba pøi ètení z roury spojení",-A -"Zji-B¹tìna chyba fcntl()",-A -"P-Bøíchozí packety v chybném poøadí",-A -"Nemohu rozkomprimovat komunika-Bèní packet",-A -"Zji-B¹tìna chyba pøi ètení komunikaèního packetu",-A -"Zji-B¹tìn timeout pøi ètení komunikaèního packetu",-A -"Zji-B¹tìna chyba pøi zápisu komunikaèního packetu",-A -"Zji-B¹tìn timeout pøi zápisu komunikaèního packetu",-A -"V-Býsledný øetìzec je del¹í ne¾ max_allowed_packet",-A -"Typ pou-B¾ité tabulky nepodporuje BLOB/TEXT sloupce",-A -"Typ pou-B¾ité tabulky nepodporuje AUTO_INCREMENT sloupce",-A -"INSERT DELAYED nen-Bí mo¾no s tabulkou '%-.64s' pou¾ít, proto¾e je zamèená pomocí LOCK TABLES",-A -"Nespr-Bávné jméno sloupce '%-.100s'",-A -"Handler pou-B¾ité tabulky neumí indexovat sloupce '%-.64s'",-A -"V-B¹echny tabulky v MERGE tabulce nejsou definovány stejnì",-A -"Kv-Bùli unique constraintu nemozu zapsat do tabulky '%-.64s'",-A -"BLOB sloupec '%-.64s' je pou-B¾it ve specifikaci klíèe bez délky",-A -"V-B¹echny èásti primárního klíèe musejí být NOT NULL; pokud potøebujete NULL, pou¾ijte UNIQUE",-A -"V-Býsledek obsahuje více ne¾ jeden øádek",-A -"Tento typ tabulky vy-B¾aduje primární klíè",-A -"Tato verze MySQL nen-Bí zkompilována s podporou RAID",-A -"Update tabulky bez WHERE s kl-Bíèem není v módu bezpeèných update dovoleno",-A -"Kl-Bíè '%-.64s' v tabulce '%-.64s' neexistuje",-A -"Nemohu otev-Bøít tabulku",-A +"Neexistuje odpov-Bídající grant pro u¾ivatele '%-.32s' na stroji '%-.64s' pro tabulku '%-.64s'", +"Pou-B¾itý pøíkaz není v této verzi MySQL povolen", +"Va-B¹e syntaxe je nìjaká divná", +"Zpo-B¾dìný insert threadu nebyl schopen získat po¾adovaný zámek pro tabulku %-.64s", +"P-Bøíli¹ mnoho zpo¾dìných threadù", +"Zru-B¹eno spojení %ld do databáze: '%-.64s' u¾ivatel: '%-.64s' (%s)", +"Zji-B¹tìn pøíchozí packet del¹í ne¾ 'max_allowed_packet'", +"Zji-B¹tìna chyba pøi ètení z roury spojení", +"Zji-B¹tìna chyba fcntl()", +"P-Bøíchozí packety v chybném poøadí", +"Nemohu rozkomprimovat komunika-Bèní packet", +"Zji-B¹tìna chyba pøi ètení komunikaèního packetu", +"Zji-B¹tìn timeout pøi ètení komunikaèního packetu", +"Zji-B¹tìna chyba pøi zápisu komunikaèního packetu", +"Zji-B¹tìn timeout pøi zápisu komunikaèního packetu", +"V-Býsledný øetìzec je del¹í ne¾ max_allowed_packet", +"Typ pou-B¾ité tabulky nepodporuje BLOB/TEXT sloupce", +"Typ pou-B¾ité tabulky nepodporuje AUTO_INCREMENT sloupce", +"INSERT DELAYED nen-Bí mo¾no s tabulkou '%-.64s' pou¾ít, proto¾e je zamèená pomocí LOCK TABLES", +"Nespr-Bávné jméno sloupce '%-.100s'", +"Handler pou-B¾ité tabulky neumí indexovat sloupce '%-.64s'", +"V-B¹echny tabulky v MERGE tabulce nejsou definovány stejnì", +"Kv-Bùli unique constraintu nemozu zapsat do tabulky '%-.64s'", +"BLOB sloupec '%-.64s' je pou-B¾it ve specifikaci klíèe bez délky", +"V-B¹echny èásti primárního klíèe musejí být NOT NULL; pokud potøebujete NULL, pou¾ijte UNIQUE", +"V-Býsledek obsahuje více ne¾ jeden øádek", +"Tento typ tabulky vy-B¾aduje primární klíè", +"Tato verze MySQL nen-Bí zkompilována s podporou RAID", +"Update tabulky bez WHERE s kl-Bíèem není v módu bezpeèných update dovoleno", +"Kl-Bíè '%-.64s' v tabulce '%-.64s' neexistuje", +"Nemohu otev-Bøít tabulku", "Handler tabulky nepodporuje check/repair", -"Proveden-Bí tohoto pøíkazu není v transakci dovoleno",-A -"Chyba %d p-Bøi COMMIT",-A -"Chyba %d p-Bøi ROLLBACK",-A -"Chyba %d p-Bøi FLUSH_LOGS",-A -"Chyba %d p-Bøi CHECKPOINT",-A -"Spojen-Bí %ld do databáze: '%-.64s' u¾ivatel: '%-.32s' stroj: `%-.64s' (%-.64s) bylo pøeru¹eno",-A -"Handler tabulky nepodporuje bin-Bární dump",-A -"Binlog uzav-Bøen pøi pokusu o FLUSH MASTER",-A -"P-Bøebudování indexu dumpnuté tabulky '%-.64s' nebylo úspì¹né",-A +"Proveden-Bí tohoto pøíkazu není v transakci dovoleno", +"Chyba %d p-Bøi COMMIT", +"Chyba %d p-Bøi ROLLBACK", +"Chyba %d p-Bøi FLUSH_LOGS", +"Chyba %d p-Bøi CHECKPOINT", +"Spojen-Bí %ld do databáze: '%-.64s' u¾ivatel: '%-.32s' stroj: `%-.64s' (%-.64s) bylo pøeru¹eno", +"Handler tabulky nepodporuje bin-Bární dump", +"Binlog uzav-Bøen pøi pokusu o FLUSH MASTER", +"P-Bøebudování indexu dumpnuté tabulky '%-.64s' nebylo úspì¹né", "Chyba masteru: '%-.64s'", -"S-Bí»ová chyba pøi ètení z masteru",-A -"S-Bí»ová chyba pøi zápisu na master",-A -"-B®ádný sloupec nemá vytvoøen fulltextový index",-A -"Nemohu prov-Bést zadaný pøíkaz, proto¾e existují aktivní zamèené tabulky nebo aktivní transakce",-A -"Nezn-Bámá systémová promìnná '%-.64s'",-A -"Tabulka '%-.64s' je ozna-Bèena jako poru¹ená a mìla by být opravena",-A -"Tabulka '%-.64s' je ozna-Bèena jako poru¹ená a poslední (automatická?) oprava se nezdaøila",-A +"S-Bí»ová chyba pøi ètení z masteru", +"S-Bí»ová chyba pøi zápisu na master", +"-B®ádný sloupec nemá vytvoøen fulltextový index", +"Nemohu prov-Bést zadaný pøíkaz, proto¾e existují aktivní zamèené tabulky nebo aktivní transakce", +"Nezn-Bámá systémová promìnná '%-.64s'", +"Tabulka '%-.64s' je ozna-Bèena jako poru¹ená a mìla by být opravena", +"Tabulka '%-.64s' je ozna-Bèena jako poru¹ená a poslední (automatická?) oprava se nezdaøila", "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", @@ -228,3 +228,12 @@ "Cannot add foreign key constraint", "Cannot add a child row: a foreign key constraint fails", "Cannot delete a parent row: a foreign key constraint fails", +"Error connecting to master: %-.128s", +"Error running query on master: %-.128s", +"Error when executing command %s: %-.128s", +"Wrong usage of %s and %s", +"The used SELECT statements have a different number of columns", +"Can't execute the query because you have a conflicting read lock", +"Mixing of transactional and non-transactional tables is disabled", +"Option '%s' used twice in statement", +"User '%-64s' has exceeded the '%s' resource (current value: %ld)", diff --git a/sql/share/danish/errmsg.txt b/sql/share/danish/errmsg.txt index 6ee6e23a18e..e87f093e7cb 100644 --- a/sql/share/danish/errmsg.txt +++ b/sql/share/danish/errmsg.txt @@ -222,3 +222,12 @@ "Cannot add foreign key constraint", "Cannot add a child row: a foreign key constraint fails", "Cannot delete a parent row: a foreign key constraint fails", +"Error connecting to master: %-.128s", +"Error running query on master: %-.128s", +"Error when executing command %s: %-.128s", +"Wrong usage of %s and %s", +"The used SELECT statements have a different number of columns", +"Can't execute the query because you have a conflicting read lock", +"Mixing of transactional and non-transactional tables is disabled", +"Option '%s' used twice in statement", +"User '%-64s' has exceeded the '%s' resource (current value: %ld)", diff --git a/sql/share/dutch/errmsg.txt b/sql/share/dutch/errmsg.txt index b857eb3104d..1ae3c78d66b 100644 --- a/sql/share/dutch/errmsg.txt +++ b/sql/share/dutch/errmsg.txt @@ -2,9 +2,11 @@ This file is public domain and comes with NO WARRANTY of any kind Dutch error messages (share/dutch/errmsg.txt) - Arjen G. Lentz (agl@bitbike.com) + 2001-08-02 - Arjen Lentz (agl@bitbike.com) Completed earlier partial translation; worked on consistency and spelling. - Version: 02-08-2001 + 2002-01-29 - Arjen Lentz (arjen@mysql.com) + 2002-04-11 - Arjen Lentz (arjen@mysql.com) + Translated new error messages. */ "hashchk", @@ -16,9 +18,9 @@ "Kan database '%-.64s' niet aanmaken (Errcode: %d)", "Kan database '%-.64s' niet aanmaken. Database bestaat reeds", "Kan database '%-.64s' niet verwijderen. Database bestaat niet", -"Error verwijderen database (kan '%-.64s' niet verwijderen, Errcode: %d)", -"Error verwijderen database (kan rmdir '%-.64s' niet uitvoeren, Errcode: %d)", -"Error bij het verwijderen van '%-.64s' (Errcode: %d)", +"Fout bij verwijderen database (kan '%-.64s' niet verwijderen, Errcode: %d)", +"Fout bij verwijderen database (kan rmdir '%-.64s' niet uitvoeren, Errcode: %d)", +"Fout bij het verwijderen van '%-.64s' (Errcode: %d)", "Kan record niet lezen in de systeem tabel", "Kan de status niet krijgen van '%-.64s' (Errcode: %d)", "Kan de werkdirectory niet krijgen (Errcode: %d)", @@ -167,8 +169,8 @@ "Communicatiepakket kon niet worden gedecomprimeerd", "Fout bij het lezen van communicatiepakketten" "Timeout bij het lezen van communicatiepakketten", -"Got an error writing communication packets", -"Got timeout writing communication packets", +"Fout bij het schrijven van communicatiepakketten", +"Timeout bij het schrijven van communicatiepakketten", "Resultaat string is langer dan max_allowed_packet", "Het gebruikte tabel type ondersteunt geen BLOB/TEXT kolommen", "Het gebruikte tabel type ondersteunt geen AUTO_INCREMENT kolommen", @@ -218,10 +220,19 @@ "DROP DATABASE niet toegestaan terwijl thread een globale 'read lock' bezit", "CREATE DATABASE niet toegestaan terwijl thread een globale 'read lock' bezit", "Foutieve parameters voor %s", -"%-.32s@%-.64s is not allowed to create new users", -"Incorrect table definition; All MERGE tables must be in the same database", -"Deadlock found when trying to get lock; Try restarting transaction", -"The used table type doesn't support FULLTEXT indexes", -"Cannot add foreign key constraint", -"Cannot add a child row: a foreign key constraint fails", -"Cannot delete a parent row: a foreign key constraint fails", +"%-.32s@%-.64s mag geen nieuwe gebruikers creeren", +"Incorrecte tabel definitie; Alle MERGE tabellen moeten tot dezelfde database behoren", +"Deadlock gevonden tijdens lock-aanvraag poging; Probeer herstart van de transactie", +"Het gebruikte tabel type ondersteund geen FULLTEXT indexen", +"Kan foreign key beperking niet toevoegen", +"Kan onderliggende rij niet toevoegen: foreign key beperking gefaald", +"Kan bovenliggende rij nite verwijderen: foreign key beperking gefaald", +"Fout bij opbouwen verbinding naar master: %-.128s", +"Fout bij uitvoeren query op master: %-.128s", +"Fout tijdens uitvoeren van commando %s: %-.128s", +"Foutief gebruik van %s en %s", +"De gebruikte SELECT commando's hebben een verschillend aantal kolommen", +"Kan de query niet uitvoeren vanwege een conflicterende read lock", +"Het combineren van transactionele en niet-transactionele tabellen is uitgeschakeld.", +"Optie '%s' tweemaal gebruikt in opdracht", +"Gebruiker '%-64s' heeft het maximale gebruik van de '%s' faciliteit overschreden (huidige waarde: %ld)", diff --git a/sql/share/english/errmsg.txt b/sql/share/english/errmsg.txt index b67c1e1a0df..39f84e4d02f 100644 --- a/sql/share/english/errmsg.txt +++ b/sql/share/english/errmsg.txt @@ -219,3 +219,12 @@ "Cannot add foreign key constraint", "Cannot add a child row: a foreign key constraint fails", "Cannot delete a parent row: a foreign key constraint fails", +"Error connecting to master: %-.128s", +"Error running query on master: %-.128s", +"Error when executing command %s: %-.128s", +"Wrong usage of %s and %s", +"The used SELECT statements have a different number of columns", +"Can't execute the query because you have a conflicting read lock", +"Mixing of transactional and non-transactional tables is disabled", +"Option '%s' used twice in statement", +"User '%-64s' has exceeded the '%s' resource (current value: %ld)", diff --git a/sql/share/estonian/errmsg.txt b/sql/share/estonian/errmsg.txt index d57f6871e12..e4b27eb5336 100644 --- a/sql/share/estonian/errmsg.txt +++ b/sql/share/estonian/errmsg.txt @@ -1,225 +1,235 @@ -/* Copyright Abandoned 1997 TCX DataKonsult AB & Monty Program KB & Detron HB - This file is public domain and comes with NO WARRANTY of any kind +/* + Copyright Abandoned 1997 MySQL AB + This file is public domain and comes with NO WARRANTY of any kind + Esialgne tõlge: Tõnu Samuel (tonu@spam.ee) + Parandanud ja täiendanud: Indrek Siitan (tfr@mysql.com) - Translated into estonian language by Tonu Samuel - email: tonu@spam.ee */ "hashchk", "isamchk", "EI", "JAH", -"Ei saa luua tabelit '%-.64s' (vea kood: %d)", -"Ei saa luua tabelit '%-.64s' (vea kood: %d)", -"Ei saa luua andmebaasi '%-.64s'. (vea kood: %d)", -"Ei saa luua andmebaasi '%-.64s'. Andmebaas on juba olemas", -"Ei saa kustutada andmebaasi '%-.64s'. Andmebaasi ei eksisteeri", -"Ei saa kustutada andmebaasi (Ei saa kustutada faili '%-.64s', vea kood: %d)", -"Ei saa kustutada andmebaasi (Ei saa kustutada kataloogi '%-.64s', vea kood: %d)", -"Viga '%-.64s' kustutamisel (vea kood: %d)", -"Ei saa lugeda kirjet in süsteemsest tabelist", -"Ei saa lugeda '%-.64s' olekut (vea kood: %d)", -"Ei saa teada jooksva kataloogi nime (vea kood: %d)", -"Ei saa avada lukustusfaili (vea kood: %d)", -"Ei saa avada faili: '%-.64s'. (vea kood: %d)", -"Ei leia faili: '%-.64s' (vea kood: %d)", -"Ei saa lugeda kataloogi '%-.64s' (vea kood: %d)", -"Ei saa siseneda kataloogi '%-.64s' (vea kood: %d)", -"Kirje on muutunud võrreldes eelmise lugemisega tabelis '%-.64s'", -"Ketas on täis (%s). Ootame kuni tekib vaba ruumi....", -"Ei saa kirjutada, Korduv võti tabelis '%-.64s'", -"Viga faili '%-.64s' sulgemisel (vea kood: %d)", -"Viga faili '%-.64s' lugemisel (vea kood: %d)", -"Viga faili '%-.64s' ringi nimetamisel '%-.64s'-ks (vea kood: %d)", -"Viga faili '%-.64s' kirjutamisel (vea kood: %d)", +"Ei suuda luua faili '%-.64s' (veakood: %d)", +"Ei suuda luua tabelit '%-.64s' (veakood: %d)", +"Ei suuda luua andmebaasi '%-.64s'. (veakood: %d)", +"Ei suuda luua andmebaasi '%-.64s': andmebaas juba eksisteerib", +"Ei suuda kustutada andmebaasi '%-.64s': andmebaasi ei eksisteeri", +"Viga andmebaasi kustutamisel (ei suuda kustutada faili '%-.64s', veakood: %d)", +"Viga andmebaasi kustutamisel (ei suuda kustutada kataloogi '%-.64s', veakood: %d)", +"Viga '%-.64s' kustutamisel (veakood: %d)", +"Ei suuda lugeda kirjet süsteemsest tabelist", +"Ei suuda lugeda '%-.64s' olekut (veakood: %d)", +"Ei suuda identifitseerida jooksvat kataloogi (veakood: %d)", +"Ei suuda lukustada faili (veakood: %d)", +"Ei suuda avada faili '%-.64s'. (veakood: %d)", +"Ei suuda leida faili '%-.64s' (veakood: %d)", +"Ei suuda lugeda kataloogi '%-.64s' (veakood: %d)", +"Ei suuda siseneda kataloogi '%-.64s' (veakood: %d)", +"Kirje tabelis '%-.64s' on muutunud viimasest lugemisest saadik", +"Ketas täis (%s). Ootame kuni tekib vaba ruumi...", +"Ei saa kirjutada, korduv võti tabelis '%-.64s'", +"Viga faili '%-.64s' sulgemisel (veakood: %d)", +"Viga faili '%-.64s' lugemisel (veakood: %d)", +"Viga faili '%-.64s' ümbernimetamisel '%-.64s'-ks (veakood: %d)", +"Viga faili '%-.64s' kirjutamisel (veakood: %d)", "'%-.64s' on lukustatud muudatuste vastu", "Sorteerimine katkestatud", -"Vaade '%-.64s' puudub '%-.64s' jaoks", -"Viga %d tabelitöötluses", -"Table handler for '%-.64s' doesn't have this option", +"Vaade '%-.64s' ei eksisteeri '%-.64s' jaoks", +"Tabeli handler tagastas vea %d", +"Tabeli '%-.64s' handler ei toeta antud operatsiooni", "Ei suuda leida kirjet '%-.64s'-s", -"Väär informatsiion failis '%-.64s'", -"Vigastatud võtmefail tabelile '%-.64s'", -"Vana võtmefail tabelile '%-.64s'. Proovi teda parandada", -"Tabel '%-.64s' on ainult lugemise õigusega", -"Mälu sai otsa. Proovi MySQL uuesti käivitada (Puudu jäi %d baiti)", -"Mälu sai sorteerimie ajal otsa. Suurenda MySQL-i sorteerimispuhvrit", -"Ootamatu faili lõpp leitud faili '%-.64s' lugemisel (vea kood: %d)", +"Vigane informatsioon failis '%-.64s'", +"Tabeli '%-.64s' võtmefail on vigane; Proovi seda parandada", +"Tabeli '%-.64s' võtmefail on aegunud; Paranda see!", +"Tabel '%-.64s' on ainult lugemiseks", +"Mälu sai otsa. Proovi MySQL uuesti käivitada (puudu jäi %d baiti)", +"Mälu sai sorteerimisel otsa. Suurenda MySQL-i sorteerimispuhvrit", +"Ootamatu faililõpumärgend faili '%-.64s' lugemisel (veakood: %d)", "Liiga palju samaaegseid ühendusi", -"Mälu sai otsa. Võimalik, et aitab swap-i lisamine või käsu 'ulimit' abil MySQL-le rohkema mälu kasutamise lubamine.", +"Mälu sai otsa. Võimalik, et aitab swap-i lisamine või käsu 'ulimit' abil MySQL-le rohkema mälu kasutamise lubamine", "Ei suuda lahendada IP aadressi masina nimeks", "Väär handshake", -"Ligipääs piiratud kasutajale: '%-.32s@%-.64s' andmebaasi '%-.64s'", -"Ligipääs piiratud kasutajale: '%-.32s@%-.64s' (Kasutab parooli: %s)", -"Andmebaas pole valitud", +"Ligipääs keelatud kasutajale '%-.32s@%-.64s' andmebaasile '%-.64s'", +"Ligipääs keelatud kasutajale '%-.32s@%-.64s' (kasutab parooli: %s)", +"Andmebaasi ei ole valitud", "Tundmatu käsk", -"Tulp '%-.64s' ei saa olla null", +"Tulp '%-.64s' ei saa omada nullväärtust", "Tundmatu andmebaas '%-.64s'", -"Tabel '%-.64s' on juba olemas", +"Tabel '%-.64s' juba eksisteerib", "Tundmatu tabel '%-.64s'", -"Tulp: '%-.64s' in %-.64s on väär", +"Väli '%-.64s' %-.64s-s ei ole ühene", "Serveri seiskamine käib", -"Tundmatu tulp '%-.64s' in '%-.64s'", -"'%-.64s' puudub GROUP BY-s", +"Tundmatu tulp '%-.64s' '%-.64s'-s", +"'%-.64s' puudub GROUP BY klauslis", "Ei saa grupeerida '%-.64s' järgi", -"Lauses on korraga nii tulbad kui summad", -"Tuplade arv tabelis erineb antud väärtuste arvust", +"Lauses on korraga nii tulbad kui summeerimisfunktsioonid", +"Tulpade arv erineb väärtuste arvust", "Identifikaatori '%-.100s' nimi on liiga pikk", "Kattuv tulba nimi '%-.64s'", "Kattuv võtme nimi '%-.64s'", -"Kattuv nimi '%-.64s' võtmele %d", -"Väär tulba kirjeldus tulbale '%-.64s'", -"%s '%-.80s' ligidal reas %d", +"Kattuv väärtus '%-.64s' võtmele %d", +"Vigane tulba kirjeldus tulbale '%-.64s'", +"%s '%-.80s' ligidal real %d", "Tühi päring", -"Pole unikaalne tabel/alias '%-.64s'", -"Vale vaikeväärtus '%-.64s'", -"Mitut põhivõtit (PRIMARY KEY) ei saa olla", -"Liiga palju võtmeid määratletud. Maksimaalselt võib olla %d võtit", +"Ei ole unikaalne tabel/alias '%-.64s'", +"Vigane vaikeväärtus '%-.64s' jaoks", +"Mitut primaarset võtit ei saa olla", +"Liiga palju võtmeid. Maksimaalselt võib olla %d võtit", "Võti koosneb liiga paljudest osadest. Maksimaalselt võib olla %d osa", -"Määratletud võti sai liiga pikk. Maksimaalne lubatud pikkus on %d", -"Võtme tulp '%-.64s' puudub antud tabelis", -"BLOB tulpa '%-.64s' ei saa kasutada võtmena", -"Tulba '%-.64s' pikkus on liiga pikk (maksimaalne = %d).", -"Tabeli kohta saab olla ainult üks auto_increment tulp ja see peab olema samas ka võtmena", +"Võti on liiga pikk. Maksimaalne võtmepikkus on %d", +"Võtme tulp '%-.64s' puudub tabelis", +"BLOB-tüüpi tulpa '%-.64s' ei saa kasutada võtmena", +"Tulba '%-.64s' pikkus on liiga pikk (maksimaalne pikkus: %d). Kasuta BLOB väljatüüpi", +"Vigane tabelikirjeldus; Tabelis tohib olla üks auto_increment tüüpi tulp ning see peab olema defineeritud võtmena", "%s: ootab ühendusi\n", "%s: MySQL lõpetas\n", -"%s: Sain signaali %d. Lõpetan!\n", +"%s: sain signaali %d. Lõpetan!\n", "%s: Lõpp\n", -"%s: Sulgen jõuga threadi %ld kasutaja: '%-.64s'\n", -"Ei saa luua IP pesa", +"%s: Sulgen jõuga lõime %ld kasutaja: '%-.32s'\n", +"Ei suuda luua IP socketit", "Tabelil '%-.64s' puuduvad võtmed. Loo tabel uuesti", -"Väljade eraldaja on väär. Vaata kasutamisjuhendisse", -"BLOB väljadega ei saa kasutada fikseeritud väljapikkust. Seetõttu on vajalik lisaklausel 'fields terminated by'.", -"Fail '%-.64s' peab asuma andmebaasi kataloogis ning olema loetav", -"Fail '%-.64s' on juba olemas", -"Kirjed: %ld Kustutatud: %ld Vahele jäetud: %ld Hoiatusi: %ld", -"Kirjed: %ld Topelt: %ld", -"Väär võtme osa. Kasutatud võtme osa ei ole string või on pikkus pikem kui võtme osa", -"ALTER TABLE abil ei saa koiki tulpasid kustutada. DROP TABLE kustutab terve tabeli", -"Ei saa kustutada '%-.64s'. On selline tulp või võti üldse olemas?", -"Kirjed: %ld Topelt: %ld Hoiatusi: %ld", -"INSERT TABLE '%-.64s' pole lubatud FROM tabelite nimekirjas", -"Tundmatu threadi id: %lu", -"Pole threadi %lu omanik", -"Pole kasutatud tabeleid", -"Liiga palju stringe tulbale %-.64s ja tüübile SET", -"Ei saa luua ainulaadset failinime %-.64s.(1-999)\n", -"Tabel '%-.64s' on lukustatud ainult lugemiseks ja sinna kirjutada ei saa", -"Tabel '%-.64s' pole lukustatud käsuga LOCK TABLES", -"BLOB tüüpi tulbal '%-.64s' ei saa olla vaikeväärtust", -"Väär andmebaasi nimi '%-.100s'", -"Väär tabeli nimi '%-.100s'", -"SELECT lause peab läbi vaatama suure hulga kirjeid ja võtaks tõenäoliselt liiga kaua aega. Tasub kontrollide WHERE klauslit ja vajadusel kasutada käsku SET OPTION SQL_BIG_SELECTS=1", +"Väljade eraldaja erineb oodatust. Tutvu kasutajajuhendiga", +"BLOB-tüüpi väljade olemasolul ei saa kasutada fikseeritud väljapikkust. Vajalik 'fields terminated by' määrang.", +"Fail '%-.64s' peab asuma andmebaasi kataloogis või olema kõigile loetav", +"Fail '%-.80s' juba eksisteerib", +"Kirjeid: %ld Kustutatud: %ld Vahele jäetud: %ld Hoiatusi: %ld", +"Kirjeid: %ld Kattuvaid: %ld", +"Vigane võtme osa. Kasutatud võtmeosa ei ole string tüüpi, määratud pikkus on pikem kui võtmeosa või tabelihandler ei toeta seda tüüpi võtmeid", +"ALTER TABLE kasutades ei saa kustutada kõiki tulpasid. Kustuta tabel DROP TABLE abil", +"Ei suuda kustutada '%-.64s'. Kontrolli kas tulp/võti eksisteerib", +"Kirjeid: %ld Kattuvaid: %ld Hoiatusi: %ld", +"INSERT TABLE '%-.64s' ei ole lubatud FROM tabelite nimekirjas", +"Tundmatu lõim: %lu", +"Ei ole lõime %lu omanik", +"Ühtegi tabelit pole kasutusel", +"Liiga palju string tulbale %-.64s tüübile SET", +"Ei suuda luua unikaalset logifaili nime %-.64s.(1-999)\n", +"Tabel '%-.64s' on lukustatud READ lukuga ning ei ole muudetav", +"Tabel '%-.64s' ei ole lukustatud käsuga LOCK TABLES", +"BLOB-tüüpi tulp '%-.64s' ei saa omada vaikeväärtust", +"Vigane andmebaasi nimi '%-.100s'", +"Vigane tabeli nimi '%-.100s'", +"SELECT lause peab läbi vaatama suure hulga kirjeid ja võtaks tõenäoliselt liiga kaua aega. Tasub kontrollida WHERE klauslit ja vajadusel kasutada käsku SET OPTION SQL_BIG_SELECTS=1", "Tundmatu viga", "Tundmatu protseduur '%-.64s'", -"Väär parameetrite hulk protseduurile '%-.64s'", -"Valed parameetrid protseduurile '%-.64s'", -"Tundmatu tabel '%-.64s' %s-s", +"Vale parameetrite hulk protseduurile '%-.64s'", +"Vigased parameetrid protseduurile '%-.64s'", +"Tundmatu tabel '%-.64s' %-.32s-s", "Tulp '%-.64s' on määratletud topelt", -"GROUP BY funktsiooni väärkasutamine", -"Tabel '%-.64s' kasutab laiendit, mis on tundmatu sellele MySQL versioonile", -"Tabelil peab olema vähemalt üks tulp", +"Vigane grupeerimisfunktsiooni kasutus", +"Tabel '%-.64s' kasutab laiendust, mis ei eksisteeri antud MySQL versioonis", +"Tabelis peab olema vähemalt üks tulp", "Tabel '%-.64s' on täis", -"Tundmatu kooditabel: '%-.64s'", -"Liiga palju tabeleid. MySQL oskab kasutada kuni %d tabelit JOINi puhul", +"Vigane kooditabel '%-.64s'", +"Liiga palju tabeleid. MySQL suudab JOINiga ühendada kuni %d tabelit", "Liiga palju tulpasid", -"Liiga pikk kirje. Maksimaalne kirje pikkus arvestamata BLOB tüüpi on %d. Võib-olla aitab mõnede väljade muutmine BLOB tüübiks", -"Threadi stack overrun: Used: %ld of a %ld stack. Use 'mysqld -O thlugeda_stack=#' to specify a bigger stack if needed", -"Ristsõltuvus OUTER JOIN-s. ON tingimused tuleks üle kontrollida", -"Tulp '%-.64s' on kasutused indeksis kui pole defineeritud tüübiga NOT NULL", -"Ei saa avada funktsiooni '%-.64s'", -"Ei saa algväärtustada funktsiooni '%-.64s'; %-.80s", +"Liiga pikk kirje. Kirje maksimumpikkus arvestamata BLOB-tüüpi välju on %d. Muuda mõned väljad BLOB-tüüpi väljadeks", +"Thread stack overrun: Used: %ld of a %ld stack. Use 'mysqld -O thread_stack=#' to specify a bigger stack if needed", +"Ristsõltuvus OUTER JOIN klauslis. Kontrolli oma ON tingimusi", +"Tulp '%-.64s' on kasutusel indeksina, kuid ei ole määratletud kui NOT NULL", +"Ei suuda avada funktsiooni '%-.64s'", +"Ei suuda algväärtustada funktsiooni '%-.64s'; %-.80s", "Teegi nimes ei tohi olla kataloogi", -"Funktsioon '%-.64s' on juba olemas", -"Ei saa avada teeki '%-.64s' (vea kood: %d %s)", -"Ei leia funktsiooni '%-.64s' selles teegis'", -"Funktsiooni '%-.64s' pole defineeritud", -"Masin '%-.64s' blokeeritud hulgaliste ühendusvigade pärast. Blokeeringu saab eemaldada käsuga 'mysqladmin flush-hosts'", -"Masinale '%-.64s' pole lubatud ligipääsu sellele MySQL serverile", +"Funktsioon '%-.64s' juba eksisteerib", +"Ei suuda avada jagatud teeki '%-.64s' (veakood: %d %-.64s)", +"Ei leia funktsiooni '%-.64s' antud teegis", +"Funktsioon '%-.64s' ei ole defineeritud", +"Masin '%-.64s' on blokeeritud hulgaliste ühendusvigade tõttu. Blokeeringu saab tühistada 'mysqladmin flush-hosts' käsuga", +"Masinal '%-.64s' puudub ligipääs sellele MySQL serverile", "Te kasutate MySQL-i anonüümse kasutajana, kelledel pole parooli muutmise õigust", -"Teil peab olema tabelite muutmise õigus muutmaks teiste paroole", -"Ei leia kirjet kasutajate tabelis", -"Sobinud kirjed: %ld Muudetud: %ld Hoiatusi: %ld", -"Ei saa luua threadi (vea kood %d). Kui mälu pole otsas, tasub operatsioonisüsteemi spetsiifilist viga", -"Tulpade arv ei vasta väärtuste hulgale reas %ld", -"Ei saa avada tabelit: '%-.64s', +"Teiste paroolide muutmiseks on nõutav tabelite muutmisõigus 'mysql' andmebaasis", +"Ei leia vastavat kirjet kasutajate tabelis", +"Sobinud kirjeid: %ld Muudetud: %ld Hoiatusi: %ld", +"Ei suuda luua uut lõime (veakood %d). Kui mälu ei ole otsas, on tõenäoliselt tegemist operatsioonisüsteemispetsiifilise veaga", +"Tulpade hulk erineb väärtuste hulgast real %ld", +"Ei suuda taasavada tabelit '%-.64s'", "NULL väärtuse väärkasutus", -"Viga '%-.64s' regexp-i käest", -"GROUP tulpade segamine (MIN(),MAX(),COUNT()...) on väär kui ei kasutata GROUP BY klauslit", -"Sellist õigust ei ole kasutajale '%-.32s' masinast '%-.64s'", -"%-.16s käsk pole lubatud kasutajale '%-.32s@%-.64s' tabelile '%-.64s'", -"%-.16s käsk pole lubatud kasutajale '%-.32s@%-.64s' tulbale '%-.64s' tabelis '%-.64s'", -"Väär GRANT/REVOKE kasutus", -"Masina või kasutaja nimi on liiga pikk GRANT lauses", -"Tabelit '%-64s.%s' ei leitud", -"Sellist õigust pole kasutajale '%-.32s' masinast '%-.64s' tabelile '%-.64s'", -"Antud käsk pole lubatud selle MySQL-i versiooniga", +"regexp tagastas vea '%-.64s'", +"GROUP tulpade (MIN(),MAX(),COUNT()...) kooskasutamine tavaliste tulpadega ilma GROUP BY klauslita ei ole lubatud", +"Sellist õigust ei ole defineeritud kasutajale '%-.32s' masinast '%-.64s'", +"%-.16s käsk ei ole lubatud kasutajale '%-.32s@%-.64s' tabelis '%-.64s'", +"%-.16s käsk ei ole lubatud kasutajale '%-.32s@%-.64s' tulbale '%-.64s' tabelis '%-.64s'", +"Vigane GRANT/REVOKE käsk. Tutvu kasutajajuhendiga", +"Masina või kasutaja nimi GRANT lauses on liiga pikk", +"Tabelit '%-.64s.%-.64s' ei eksisteeri", +"Sellist õigust ei ole defineeritud kasutajale '%-.32s' masinast '%-.64s' tabelile '%-.64s'", +"Antud käsk ei ole lubatud käesolevas MySQL versioonis", "Viga SQL süntaksis", -"INSERT DELAYED thread ei saanud nõutavat lukku tabelile %-.64s", -"Liiga palju DELAYED threade on kasutusel", -"Ühendus katkestatud %ld andmebaasile '%-.64s' kasutaja '%-.64s' (%s)", -"Sain lubatust suurema paketi (max_allowed_packet)", -"Got a read error from the connection pipe", -"Got an error from fcntl()", -"Got packets out of order", -"Ei suuda ühendust lahti pakkida", -"Viga ühenduse lugemisel", -"Aeg sai otsa ühenduse lugemisel", -"Viga ühenduse kirjutamisel", -"Aeg sai otsa ühenduse kirjutamisel", -"Tulemuseks saadud string on pikem kui max_allowed_packet väärtus", -"Kasutatud tabeli tüüp ei toeta BLOB/TEXT tulpasid", -"Kasutatud tabeli tüüp ei toeta AUTO_INCREMENT tulpasid", -"INSERT DELAYED käsku ei saa kasutada tabeliga '%-.64s', kuna see on lukus käsuga LOCK TABLES", -"Väär tulba nimi '%-.100s'", -"Kasutusel olev tabelite haldur ei oska indekseerida tulpa '%-.64s'", -"All tables in the MERGE table are not identically defined", -"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", -"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'", -"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)", +"INSERT DELAYED lõim ei suutnud saada soovitud lukku tabelile %-.64s", +"Liiga palju DELAYED lõimesid kasutusel", +"Ühendus katkestatud %ld andmebaasile: '%-.64s' kasutajale: '%-.32s' (%-.64s)", +"Saabus suurem pakett kui lubatud 'max_allowed_packet' muutujaga", +"Viga ühendustoru lugemisel", +"fcntl() tagastas vea", +"Paketid saabusid vales järjekorras", +"Viga andmepaketi lahtipakkimisel", +"Viga andmepaketi lugemisel", +"Kontrollaja ületamine andmepakettide lugemisel", +"Viga andmepaketi kirjutamisel", +"Kontrollaja ületamine andmepakettide kirjutamisel", +"Tulemus on pikem kui lubatud 'max_allowed_packet' muutujaga", +"Valitud tabelitüüp ei toeta BLOB/TEXT tüüpi välju", +"Valitud tabelitüüp ei toeta AUTO_INCREMENT tüüpi välju", +"INSERT DELAYED ei saa kasutada tabeli '%-.64s' peal, kuna see on lukustatud LOCK TABLES käsuga", +"Vigane tulba nimi '%-.100s'", +"Tabelihandler ei oska indekseerida tulpa '%-.64s'", +"Kõik tabelid MERGE tabeli määratluses ei ole identsed", +"Ei suuda kirjutada tabelisse '%-.64s', kuna see rikub ühesuse kitsendust", +"BLOB-tüüpi tulp '%-.64s' on kasutusel võtmes ilma pikkust määratlemata", +"Kõik PRIMARY KEY peavad olema määratletud NOT NULL piiranguga; vajadusel kasuta UNIQUE tüüpi võtit", +"Tulemis oli rohkem kui üks kirje", +"Antud tabelitüüp nõuab primaarset võtit", +"Antud MySQL versioon on kompileeritud ilma RAID toeta", +"Katse muuta tabelit turvalises rezhiimis ilma WHERE klauslita", +"Võti '%-.64s' ei eksisteeri tabelis '%-.64s'", +"Ei suuda avada tabelit", +"Antud tabelitüüp ei toeta CHECK/REPAIR käske", +"Seda käsku ei saa kasutada transaktsiooni sees", +"Viga %d käsu COMMIT täitmisel", +"Viga %d käsu ROLLBACK täitmisel", +"Viga %d käsu FLUSH_LOGS täitmisel", +"Viga %d käsu CHECKPOINT täitmisel", +"Ühendus katkestatud %ld 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'", "Error from master: '%-.64s'", "Net error reading from master", "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", -"Tundmatu süsteemne muutja '%-.64s'", +"Ei suutnud leida FULLTEXT indeksit, mis kattuks kasutatud tulpadega", +"Ei suuda täita antud käsku kuna on aktiivseid lukke või käimasolev transaktsioon", +"Tundmatu süsteemne muutuja '%-.64s'", "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', +"Tabel '%-.64s' on märgitud vigaseks ja viimane (automaatne?) parandus ebaõnnestus", +"Hoiatus: mõnesid transaktsioone mittetoetavaid tabeleid ei suudetud tagasi kerida", +"Mitme lausendiga transaktsioon nõudis rohkem ruumi kui lubatud 'max_binlog_cache_size' muutujaga. Suurenda muutuja väärtust ja proovi uuesti", "This operation cannot be performed with a running slave, run SLAVE STOP first", "This operation requires a running slave, configure slave and do SLAVE START", "The server is not configured as slave, fix in config file or with CHANGE MASTER TO", "Could not initialize master info structure, check permisions on master.info", "Could not create slave thread, check system resources", -"User %-.64s has already more than 'max_user_connections' active connections", -"You may only use constant expressions with 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", -"DROP DATABASE not allowed while thread is holding global read lock", -"CREATE DATABASE not allowed while thread is holding global read lock", -"Wrong arguments to %s", -"%-.32s@%-.64s is not allowed to create new users", -"Incorrect table definition; All MERGE tables must be in the same database", -"Deadlock found when trying to get lock; Try restarting transaction", -"The used table type doesn't support FULLTEXT indexes", +"Kasutajal %-.64s on juba rohkem ühendusi kui lubatud 'max_user_connections' muutujaga", +"Ainult konstantsed suurused on lubatud SET klauslis", +"Kontrollaeg ületatud luku järel ootamisel; Proovi transaktsiooni otsast alata", +"Lukkude koguarv ületab lukutabeli suuruse", +"Uuenduslukke ei saa kasutada READ UNCOMMITTED transaktsiooni käigus", +"DROP DATABASE ei ole lubatud kui lõim omab globaalset READ lukku", +"CREATE DATABASE ei ole lubatud kui lõim omab globaalset READ lukku", +"Vigased parameetrid %s-le", +"Kasutajal %-.32s@%-.64s ei ole lubatud luua uusi kasutajaid", +"Vigane tabelimääratlus; kõik MERGE tabeli liikmed peavad asuma samas andmebaasis", +"Lukustamisel tekkis tupik (deadlock); alusta transaktsiooni otsast", +"Antud tabelitüüp ei toeta FULLTEXT indekseid", "Cannot add foreign key constraint", "Cannot add a child row: a foreign key constraint fails", "Cannot delete a parent row: a foreign key constraint fails", +"Error connecting to master: %-.128s", +"Error running query on master: %-.128s", +"Viga käsu %s täitmisel: %-.128s", +"Vigane %s ja %s kasutus", +"Tulpade arv kasutatud SELECT lausetes ei kattu", +"Ei suuda täita päringut konfliktse luku tõttu", +"Transaktsioone toetavate ning mittetoetavate tabelite kooskasutamine ei ole lubatud", +"Määrangut '%s' on lauses kasutatud topelt", +"User '%-64s' has exceeded the '%s' resource (current value: %ld)", diff --git a/sql/share/french/errmsg.txt b/sql/share/french/errmsg.txt index 4848a3266bc..5e0288657d5 100644 --- a/sql/share/french/errmsg.txt +++ b/sql/share/french/errmsg.txt @@ -219,3 +219,12 @@ "Cannot add foreign key constraint", "Cannot add a child row: a foreign key constraint fails", "Cannot delete a parent row: a foreign key constraint fails", +"Error connecting to master: %-.128s", +"Error running query on master: %-.128s", +"Error when executing command %s: %-.128s", +"Wrong usage of %s and %s", +"The used SELECT statements have a different number of columns", +"Can't execute the query because you have a conflicting read lock", +"Mixing of transactional and non-transactional tables is disabled", +"Option '%s' used twice in statement", +"User '%-64s' has exceeded the '%s' resource (current value: %ld)", diff --git a/sql/share/german/errmsg.txt b/sql/share/german/errmsg.txt index fe76c757d11..58c21ccbdfc 100644 --- a/sql/share/german/errmsg.txt +++ b/sql/share/german/errmsg.txt @@ -222,3 +222,12 @@ "Cannot add foreign key constraint", "Cannot add a child row: a foreign key constraint fails", "Cannot delete a parent row: a foreign key constraint fails", +"Error connecting to master: %-.128s", +"Error running query on master: %-.128s", +"Error when executing command %s: %-.128s", +"Wrong usage of %s and %s", +"The used SELECT statements have a different number of columns", +"Can't execute the query because you have a conflicting read lock", +"Mixing of transactional and non-transactional tables is disabled", +"Option '%s' used twice in statement", +"User '%-64s' has exceeded the '%s' resource (current value: %ld)", diff --git a/sql/share/greek/errmsg.txt b/sql/share/greek/errmsg.txt index b491b0ef1f9..c0387742de0 100644 --- a/sql/share/greek/errmsg.txt +++ b/sql/share/greek/errmsg.txt @@ -219,3 +219,12 @@ "Cannot add foreign key constraint", "Cannot add a child row: a foreign key constraint fails", "Cannot delete a parent row: a foreign key constraint fails", +"Error connecting to master: %-.128s", +"Error running query on master: %-.128s", +"Error when executing command %s: %-.128s", +"Wrong usage of %s and %s", +"The used SELECT statements have a different number of columns", +"Can't execute the query because you have a conflicting read lock", +"Mixing of transactional and non-transactional tables is disabled", +"Option '%s' used twice in statement", +"User '%-64s' has exceeded the '%s' resource (current value: %ld)", diff --git a/sql/share/hungarian/errmsg.txt b/sql/share/hungarian/errmsg.txt index fc6a9ba0643..85563d00f03 100644 --- a/sql/share/hungarian/errmsg.txt +++ b/sql/share/hungarian/errmsg.txt @@ -221,3 +221,12 @@ "Cannot add foreign key constraint", "Cannot add a child row: a foreign key constraint fails", "Cannot delete a parent row: a foreign key constraint fails", +"Error connecting to master: %-.128s", +"Error running query on master: %-.128s", +"Error when executing command %s: %-.128s", +"Wrong usage of %s and %s", +"The used SELECT statements have a different number of columns", +"Can't execute the query because you have a conflicting read lock", +"Mixing of transactional and non-transactional tables is disabled", +"Option '%s' used twice in statement", +"User '%-64s' has exceeded the '%s' resource (current value: %ld)", diff --git a/sql/share/italian/errmsg.txt b/sql/share/italian/errmsg.txt index 3da78e82d99..228de271ad6 100644 --- a/sql/share/italian/errmsg.txt +++ b/sql/share/italian/errmsg.txt @@ -219,3 +219,12 @@ "Cannot add foreign key constraint", "Cannot add a child row: a foreign key constraint fails", "Cannot delete a parent row: a foreign key constraint fails", +"Error connecting to master: %-.128s", +"Error running query on master: %-.128s", +"Error when executing command %s: %-.128s", +"Wrong usage of %s and %s", +"The used SELECT statements have a different number of columns", +"Can't execute the query because you have a conflicting read lock", +"Mixing of transactional and non-transactional tables is disabled", +"Option '%s' used twice in statement", +"User '%-64s' has exceeded the '%s' resource (current value: %ld)", diff --git a/sql/share/japanese/errmsg.txt b/sql/share/japanese/errmsg.txt index dc2299e4336..6ffe5a25bf4 100644 --- a/sql/share/japanese/errmsg.txt +++ b/sql/share/japanese/errmsg.txt @@ -221,3 +221,12 @@ "Cannot add foreign key constraint", "Cannot add a child row: a foreign key constraint fails", "Cannot delete a parent row: a foreign key constraint fails", +"Error connecting to master: %-.128s", +"Error running query on master: %-.128s", +"Error when executing command %s: %-.128s", +"Wrong usage of %s and %s", +"The used SELECT statements have a different number of columns", +"Can't execute the query because you have a conflicting read lock", +"Mixing of transactional and non-transactional tables is disabled", +"Option '%s' used twice in statement", +"User '%-64s' has exceeded the '%s' resource (current value: %ld)", diff --git a/sql/share/korean/errmsg.txt b/sql/share/korean/errmsg.txt index 79bf767a3c8..fa2a6dc8624 100644 --- a/sql/share/korean/errmsg.txt +++ b/sql/share/korean/errmsg.txt @@ -219,3 +219,12 @@ "Cannot add foreign key constraint", "Cannot add a child row: a foreign key constraint fails", "Cannot delete a parent row: a foreign key constraint fails", +"Error connecting to master: %-.128s", +"Error running query on master: %-.128s", +"Error when executing command %s: %-.128s", +"Wrong usage of %s and %s", +"The used SELECT statements have a different number of columns", +"Can't execute the query because you have a conflicting read lock", +"Mixing of transactional and non-transactional tables is disabled", +"Option '%s' used twice in statement", +"User '%-64s' has exceeded the '%s' resource (current value: %ld)", diff --git a/sql/share/norwegian-ny/errmsg.txt b/sql/share/norwegian-ny/errmsg.txt index a583a541b71..c3dbee47967 100644 --- a/sql/share/norwegian-ny/errmsg.txt +++ b/sql/share/norwegian-ny/errmsg.txt @@ -221,3 +221,12 @@ "Cannot add foreign key constraint", "Cannot add a child row: a foreign key constraint fails", "Cannot delete a parent row: a foreign key constraint fails", +"Error connecting to master: %-.128s", +"Error running query on master: %-.128s", +"Error when executing command %s: %-.128s", +"Wrong usage of %s and %s", +"The used SELECT statements have a different number of columns", +"Can't execute the query because you have a conflicting read lock", +"Mixing of transactional and non-transactional tables is disabled", +"Option '%s' used twice in statement", +"User '%-64s' has exceeded the '%s' resource (current value: %ld)", diff --git a/sql/share/norwegian/errmsg.txt b/sql/share/norwegian/errmsg.txt index 85fa1f04f63..436b968e3df 100644 --- a/sql/share/norwegian/errmsg.txt +++ b/sql/share/norwegian/errmsg.txt @@ -221,3 +221,12 @@ "Cannot add foreign key constraint", "Cannot add a child row: a foreign key constraint fails", "Cannot delete a parent row: a foreign key constraint fails", +"Error connecting to master: %-.128s", +"Error running query on master: %-.128s", +"Error when executing command %s: %-.128s", +"Wrong usage of %s and %s", +"The used SELECT statements have a different number of columns", +"Can't execute the query because you have a conflicting read lock", +"Mixing of transactional and non-transactional tables is disabled", +"Option '%s' used twice in statement", +"User '%-64s' has exceeded the '%s' resource (current value: %ld)", diff --git a/sql/share/polish/errmsg.txt b/sql/share/polish/errmsg.txt index af484b4c850..3883d98f7dd 100644 --- a/sql/share/polish/errmsg.txt +++ b/sql/share/polish/errmsg.txt @@ -223,3 +223,12 @@ "Cannot add foreign key constraint", "Cannot add a child row: a foreign key constraint fails", "Cannot delete a parent row: a foreign key constraint fails", +"Error connecting to master: %-.128s", +"Error running query on master: %-.128s", +"Error when executing command %s: %-.128s", +"Wrong usage of %s and %s", +"The used SELECT statements have a different number of columns", +"Can't execute the query because you have a conflicting read lock", +"Mixing of transactional and non-transactional tables is disabled", +"Option '%s' used twice in statement", +"User '%-64s' has exceeded the '%s' resource (current value: %ld)", diff --git a/sql/share/portuguese/errmsg.txt b/sql/share/portuguese/errmsg.txt index 7737fe6b1fd..335d5b96105 100644 --- a/sql/share/portuguese/errmsg.txt +++ b/sql/share/portuguese/errmsg.txt @@ -219,3 +219,12 @@ "Cannot add foreign key constraint", "Cannot add a child row: a foreign key constraint fails", "Cannot delete a parent row: a foreign key constraint fails", +"Error connecting to master: %-.128s", +"Error running query on master: %-.128s", +"Error when executing command %s: %-.128s", +"Wrong usage of %s and %s", +"The used SELECT statements have a different number of columns", +"Can't execute the query because you have a conflicting read lock", +"Mixing of transactional and non-transactional tables is disabled", +"Option '%s' used twice in statement", +"User '%-64s' has exceeded the '%s' resource (current value: %ld)", diff --git a/sql/share/romanian/errmsg.txt b/sql/share/romanian/errmsg.txt index e8927777b8a..a5df91da19a 100644 --- a/sql/share/romanian/errmsg.txt +++ b/sql/share/romanian/errmsg.txt @@ -223,3 +223,12 @@ "Cannot add foreign key constraint", "Cannot add a child row: a foreign key constraint fails", "Cannot delete a parent row: a foreign key constraint fails", +"Error connecting to master: %-.128s", +"Error running query on master: %-.128s", +"Error when executing command %s: %-.128s", +"Wrong usage of %s and %s", +"The used SELECT statements have a different number of columns", +"Can't execute the query because you have a conflicting read lock", +"Mixing of transactional and non-transactional tables is disabled", +"Option '%s' used twice in statement", +"User '%-64s' has exceeded the '%s' resource (current value: %ld)", diff --git a/sql/share/russian/errmsg.txt b/sql/share/russian/errmsg.txt index 5c94f2ee31b..36b165bfca2 100644 --- a/sql/share/russian/errmsg.txt +++ b/sql/share/russian/errmsg.txt @@ -222,3 +222,12 @@ "Cannot add foreign key constraint", "Cannot add a child row: a foreign key constraint fails", "Cannot delete a parent row: a foreign key constraint fails", +"ïÛÉÂËÁ ÓÏÅÄÉÎÅÎÉÑ Ó master: %-.128s", +"ïÛÉÂËÁ ×Ù×ÏÌÎÅÎÉÑ ÚÁÐÒÏÓÁ ÎÁ master: %-.128s", +"ïÛÉÂËÁ ×ÙÐÏÌÎÅÎÉÑ ËÏÍÁÎÄÙ %s: %-.128s", +"îÅÐÒÁ×ÉÌØÎÏÅ ÉÓÐÏÌØÚÏ×ÁÎÉÅ %s É %s", +"éÓÐÏÌØÚÕÅÍÙÅ SELECT-×ÙÒÁÖÅÎÉÑ ÉÍÅÀÔ ÒÁÚÎÙÅ ËÏÌÉÞÅÓÔ×Á ÓÔÏÌÂÃÏ×", +"îÅ×ÏÚÍÏÖÎÏ ×ÙÐÏÌÎÉÔØ ÚÁÐÒÏÓ ÉÚ-ÚÁ ËÏÎÆÌÉËÔÎÏÊ ÂÌÏËÉÒÏ×ËÉ ÞÔÅÎÉÑ", +"ïÄÎÏ×ÒÅÍÅÎÎÏÅ ÉÓÐÏÌØÚÏ×ÁÎÉÅ transactional É non-transactional ÔÁÂÌÉà ÏÔËÌÀÞÅÎÏ", +"ïÐÃÉÑ '%s' ÉÓÐÏÌØÚÏ×ÁÎÁ Ä×ÁÖÄÙ", +"User '%-64s' has exceeded the '%s' resource (current value: %ld)", diff --git a/sql/share/slovak/errmsg.txt b/sql/share/slovak/errmsg.txt index 3f96880bda1..2b8878fb411 100644 --- a/sql/share/slovak/errmsg.txt +++ b/sql/share/slovak/errmsg.txt @@ -227,3 +227,12 @@ "Cannot add foreign key constraint", "Cannot add a child row: a foreign key constraint fails", "Cannot delete a parent row: a foreign key constraint fails", +"Error connecting to master: %-.128s", +"Error running query on master: %-.128s", +"Error when executing command %s: %-.128s", +"Wrong usage of %s and %s", +"The used SELECT statements have a different number of columns", +"Can't execute the query because you have a conflicting read lock", +"Mixing of transactional and non-transactional tables is disabled", +"Option '%s' used twice in statement", +"User '%-64s' has exceeded the '%s' resource (current value: %ld)", diff --git a/sql/share/spanish/errmsg.txt b/sql/share/spanish/errmsg.txt index c718ac93fc4..c69f021db73 100644 --- a/sql/share/spanish/errmsg.txt +++ b/sql/share/spanish/errmsg.txt @@ -220,3 +220,12 @@ "Cannot add foreign key constraint", "Cannot add a child row: a foreign key constraint fails", "Cannot delete a parent row: a foreign key constraint fails", +"Error de coneccion a master: %-128s", +"Error executando el query en master: %-128%", +"Error de %s: %-128%", +"Wrong usage of %s and %s", +"The used SELECT statements have a different number of columns", +"Can't execute the query because you have a conflicting read lock", +"Mixing of transactional and non-transactional tables is disabled", +"Option '%s' used twice in statement", +"User '%-64s' has exceeded the '%s' resource (current value: %ld)", diff --git a/sql/share/swedish/errmsg.OLD b/sql/share/swedish/errmsg.OLD index cc54e051e63..3dd14c8b613 100644 --- a/sql/share/swedish/errmsg.OLD +++ b/sql/share/swedish/errmsg.OLD @@ -205,11 +205,17 @@ "Kunde inte initializera replications-strukturerna. Kontrollera privilegerna för 'master.info'", "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", -"DROP DATABASE not allowed while thread is holding global read lock", -"CREATE DATABASE not allowed while thread is holding global read lock", -#ER_WRONG_ARGUMENTS +"Man kan endast använda konstant-uttryck med SET", +"Fick inte ett lås i tid", +"Antal lås överskrider antalet reserverade lås", +"Updaterings-lås kan inte göras när man använder READ UNCOMMITTED", +"DROP DATABASE är inte tillåtet när man har ett globalt läs-lås", +"CREATE DATABASE är inte tillåtet när man har ett globalt läs-lås", "Felaktiga argument till %s", +"%-.32s@%-.64s har inte rättigheter att skapa nya användare", +"Fick fel vid anslutning till master: %-.128s", +"Fick fel vid utförande av command på mastern: %-.128s", +"Fick fel vid utförande av %s: %-.128s", +"Felaktig använding av %s and %s", +"SELECT kommandona har olika antal kolumner" +"Kan inte utföra kommandot emedan du har ett READ lås", diff --git a/sql/share/swedish/errmsg.txt b/sql/share/swedish/errmsg.txt index ce6bb0e80f0..4bb810e1909 100644 --- a/sql/share/swedish/errmsg.txt +++ b/sql/share/swedish/errmsg.txt @@ -219,3 +219,12 @@ "Kan inte lägga till 'FOREIGN KEY constraint'", "FOREIGN KEY konflikt: Kan inte skriva barn", "FOREIGN KEY konflikt: Kan inte radera fader", +"Fick fel vid anslutning till master: %-.128s", +"Fick fel vid utförande av command på mastern: %-.128s", +"Fick fel vid utförande av %s: %-.128s", +"Felaktig använding av %s and %s", +"SELECT kommandona har olika antal kolumner" +"Kan inte utföra kommandot emedan du har ett READ lås", +"Blandning av transaktionella och icke-transaktionella tabeller är inaktiverat", +"Option '%s' användes två gånger", +"Användare '%-64s' har överskridit '%s' (nuvarande värde: %ld)", diff --git a/sql/share/ukrainian/errmsg.txt b/sql/share/ukrainian/errmsg.txt index b167ebd5f3d..6777f1230e7 100644 --- a/sql/share/ukrainian/errmsg.txt +++ b/sql/share/ukrainian/errmsg.txt @@ -224,3 +224,12 @@ "Cannot add foreign key constraint", "Cannot add a child row: a foreign key constraint fails", "Cannot delete a parent row: a foreign key constraint fails", +"Error connecting to master: %-.128s", +"Error running query on master: %-.128s", +"Error when executing command %s: %-.128s", +"Wrong usage of %s and %s", +"The used SELECT statements have a different number of columns", +"Can't execute the query because you have a conflicting read lock", +"Mixing of transactional and non-transactional tables is disabled", +"Option '%s' used twice in statement", +"User '%-64s' has exceeded the '%s' resource (current value: %ld)", diff --git a/sql/slave.cc b/sql/slave.cc index 946cf483e4b..b473d8ab1e3 100644 --- a/sql/slave.cc +++ b/sql/slave.cc @@ -20,48 +20,138 @@ #include <myisam.h> #include "mini_client.h" #include "slave.h" +#include "sql_repl.h" +#include "repl_failsafe.h" #include <thr_alarm.h> #include <my_dir.h> +#include <assert.h> -#define RPL_LOG_NAME (glob_mi.log_file_name[0] ? glob_mi.log_file_name :\ - "FIRST") - -volatile bool slave_running = 0; -pthread_t slave_real_id; -MASTER_INFO glob_mi; -MY_BITMAP slave_error_mask; bool use_slave_mask = 0; +MY_BITMAP slave_error_mask; + +typedef bool (*CHECK_KILLED_FUNC)(THD*,void*); + +volatile bool slave_sql_running = 0, slave_io_running = 0; +char* slave_load_tmpdir = 0; +MASTER_INFO main_mi; +MASTER_INFO* active_mi; +volatile int active_mi_in_use = 0; HASH replicate_do_table, replicate_ignore_table; DYNAMIC_ARRAY replicate_wild_do_table, replicate_wild_ignore_table; bool do_table_inited = 0, ignore_table_inited = 0; bool wild_do_table_inited = 0, wild_ignore_table_inited = 0; bool table_rules_on = 0; -uint32 slave_skip_counter = 0; static TABLE* save_temporary_tables = 0; -THD* slave_thd = 0; +ulong relay_log_space_limit = 0; /* TODO: fix variables to access ulonglong + values and make it ulonglong */ // when slave thread exits, we need to remember the temporary tables so we // can re-use them on slave start -static int last_slave_errno = 0; -static char last_slave_error[1024] = ""; +// TODO: move the vars below under MASTER_INFO #ifndef DBUG_OFF int disconnect_slave_event_count = 0, abort_slave_event_count = 0; -static int events_till_disconnect = -1, events_till_abort = -1; +static int events_till_disconnect = -1; +int events_till_abort = -1; static int stuck_count = 0; #endif - -inline void skip_load_data_infile(NET* net); -inline bool slave_killed(THD* thd); -static int init_slave_thread(THD* thd); +typedef enum { SLAVE_THD_IO, SLAVE_THD_SQL} SLAVE_THD_TYPE; + +void skip_load_data_infile(NET* net); +static int process_io_rotate(MASTER_INFO* mi, Rotate_log_event* rev); +static int process_io_create_file(MASTER_INFO* mi, Create_file_log_event* cev); +static int queue_old_event(MASTER_INFO* mi, const char* buf, + uint event_len); +static bool wait_for_relay_log_space(RELAY_LOG_INFO* rli); +static inline bool io_slave_killed(THD* thd,MASTER_INFO* mi); +static inline bool sql_slave_killed(THD* thd,RELAY_LOG_INFO* rli); +static int count_relay_log_space(RELAY_LOG_INFO* rli); +static int init_slave_thread(THD* thd, SLAVE_THD_TYPE thd_type); 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 connect_to_master(THD* thd, MYSQL* mysql, MASTER_INFO* mi, + bool reconnect); +static int safe_sleep(THD* thd, int sec, CHECK_KILLED_FUNC thread_killed, + void* thread_killed_arg); +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); -static int check_expected_error(THD* thd, int expected_error); +static int check_master_version(MYSQL* mysql, MASTER_INFO* mi); + +char* rewrite_db(char* db); + +void init_thread_mask(int* mask,MASTER_INFO* mi,bool inverse) +{ + bool set_io = mi->slave_running, set_sql = mi->rli.slave_running; + if (inverse) + { + /* This makes me think of the Russian idiom "I am not I, and this is + not my horse", which is used to deny reponsibility for + one's actions. + */ + set_io = !set_io; + set_sql = !set_sql; + } + register int tmp_mask=0; + if (set_io) + tmp_mask |= SLAVE_IO; + if (set_sql) + tmp_mask |= SLAVE_SQL; + *mask = tmp_mask; +} + +void lock_slave_threads(MASTER_INFO* mi) +{ + //TODO: see if we can do this without dual mutex + pthread_mutex_lock(&mi->run_lock); + pthread_mutex_lock(&mi->rli.run_lock); +} + +void unlock_slave_threads(MASTER_INFO* mi) +{ + //TODO: see if we can do this without dual mutex + pthread_mutex_unlock(&mi->rli.run_lock); + pthread_mutex_unlock(&mi->run_lock); +} + +int init_slave() +{ + // TODO (multi-master): replace this with list initialization + active_mi = &main_mi; + + // TODO: the code below is a copy-paste mess - clean it up + /* + make sure slave thread gets started if server_id is set, + valid master.info is present, and master_host has not been specified + */ + if (server_id && !master_host) + { + // TODO: re-write this to interate through the list of files + // for multi-master + char fname[FN_REFLEN+128]; + MY_STAT stat_area; + fn_format(fname, master_info_file, mysql_data_home, "", 4+16+32); + if (my_stat(fname, &stat_area, MYF(0)) && + !init_master_info(active_mi,master_info_file,relay_log_info_file)) + master_host = active_mi->host; + } + // slave thread + if (master_host) + { + if (!opt_skip_slave_start && start_slave_threads(1 /* need mutex */, + 0 /* no wait for start*/, + active_mi, + master_info_file, + relay_log_info_file, + SLAVE_IO|SLAVE_SQL + )) + sql_print_error("Warning: Can't create threads to handle slave"); + else if (opt_skip_slave_start) + if (init_master_info(active_mi, master_info_file, relay_log_info_file)) + sql_print_error("Warning: failed to initialized master info"); + } + return 0; +} static void free_table_ent(TABLE_RULE_ENT* e) { @@ -75,10 +165,92 @@ static byte* get_table_key(TABLE_RULE_ENT* e, uint* len, return (byte*)e->db; } +// TODO: check proper initialization of master_log_name/master_log_pos +int init_relay_log_pos(RELAY_LOG_INFO* rli,const char* log, + ulonglong pos, bool need_data_lock, + const char** errmsg) +{ + *errmsg=0; + if (rli->log_pos_current) + return 0; + pthread_mutex_t *log_lock=rli->relay_log.get_log_lock(); + pthread_mutex_lock(log_lock); + if (need_data_lock) + pthread_mutex_lock(&rli->data_lock); + + if (rli->cur_log_fd >= 0) + { + end_io_cache(&rli->cache_buf); + my_close(rli->cur_log_fd, MYF(MY_WME)); + rli->cur_log_fd = -1; + } + + if (!log) + log = rli->relay_log_name; // already inited + if (!pos) + pos = rli->relay_log_pos; // already inited + else + rli->relay_log_pos = pos; + + // test to see if the previous run was with the skip of purging + // if yes, we do not purge when we restart + if (rli->relay_log.find_first_log(&rli->linfo,"")) + { + *errmsg="Could not find first log during relay log initialization"; + goto err; + } + if (strcmp(log,rli->linfo.log_file_name)) + rli->skip_log_purge=1; + + if (rli->relay_log.find_first_log(&rli->linfo,log)) + { + *errmsg="Could not find target log during relay log initialization"; + goto err; + } + strnmov(rli->relay_log_name,rli->linfo.log_file_name, + sizeof(rli->relay_log_name)); + // to make end_io_cache(&rli->cache_buf) safe in all cases + if (!rli->inited) + bzero((char*) &rli->cache_buf, sizeof(IO_CACHE)); + if (rli->relay_log.is_active(rli->linfo.log_file_name)) + { + if (my_b_tell((rli->cur_log=rli->relay_log.get_log_file())) == 0 && + check_binlog_magic(rli->cur_log,errmsg)) + { + goto err; + } + rli->cur_log_init_count=rli->cur_log->init_count; + } + else + { + if (rli->inited) + end_io_cache(&rli->cache_buf); + if (rli->cur_log_fd>=0) + my_close(rli->cur_log_fd,MYF(MY_WME)); + if ((rli->cur_log_fd=open_binlog(&rli->cache_buf, + rli->linfo.log_file_name,errmsg)) < 0) + { + goto err; + } + rli->cur_log = &rli->cache_buf; + } + if (pos > 4) + my_b_seek(rli->cur_log,(off_t)pos); + rli->log_pos_current=1; +err: + pthread_cond_broadcast(&rli->data_cond); + if (need_data_lock) + pthread_mutex_unlock(&rli->data_lock); + pthread_mutex_unlock(log_lock); + return (*errmsg) ? 1 : 0; +} + /* called from get_options() in mysqld.cc on start-up */ -void init_slave_skip_errors(char* arg) + +void init_slave_skip_errors(const char* arg) { - char* p; + const char *p; + my_bool last_was_digit = 0; if (bitmap_init(&slave_error_mask,MAX_SLAVE_ERROR,0)) { fprintf(stderr, "Badly out of memory, please check your system status\n"); @@ -104,6 +276,238 @@ void init_slave_skip_errors(char* arg) } } +/* + We assume we have a run lock on rli and that the both slave thread + are not running +*/ + +int purge_relay_logs(RELAY_LOG_INFO* rli, bool just_reset, const char** errmsg) +{ + DBUG_ENTER("purge_relay_logs"); + if (!rli->inited) + DBUG_RETURN(0); /* successfully do nothing */ + DBUG_ASSERT(rli->slave_running == 0); + DBUG_ASSERT(rli->mi->slave_running == 0); + int error=0; + rli->slave_skip_counter=0; + pthread_mutex_lock(&rli->data_lock); + rli->pending=0; + rli->master_log_name[0]=0; + rli->master_log_pos=0; // 0 means uninitialized + if (rli->relay_log.reset_logs(rli->sql_thd) || + rli->relay_log.find_first_log(&rli->linfo,"")) + { + *errmsg = "Failed during log reset"; + error=1; + goto err; + } + strnmov(rli->relay_log_name,rli->linfo.log_file_name, + sizeof(rli->relay_log_name)-1); + rli->log_space_total=4; //just first log with magic number and nothing else + rli->relay_log_pos=4; + rli->relay_log.reset_bytes_written(); + rli->log_pos_current=0; + if (!just_reset) + error = init_relay_log_pos(rli,0,0,0/*do not need data lock*/,errmsg); +err: +#ifndef DBUG_OFF + char buf[22]; +#endif + DBUG_PRINT("info",("log_space_total=%s",llstr(rli->log_space_total,buf))); + pthread_mutex_unlock(&rli->data_lock); + DBUG_RETURN(error); +} + +int terminate_slave_threads(MASTER_INFO* mi,int thread_mask,bool skip_lock) +{ + if (!mi->inited) + return 0; /* successfully do nothing */ + int error,force_all = (thread_mask & SLAVE_FORCE_ALL); + pthread_mutex_t *sql_lock = &mi->rli.run_lock, *io_lock = &mi->run_lock; + pthread_mutex_t *sql_cond_lock,*io_cond_lock; + + sql_cond_lock=sql_lock; + io_cond_lock=io_lock; + + if (skip_lock) + { + sql_lock = io_lock = 0; + } + if ((thread_mask & (SLAVE_IO|SLAVE_FORCE_ALL)) && mi->slave_running) + { + mi->abort_slave=1; + if ((error=terminate_slave_thread(mi->io_thd,io_lock, + io_cond_lock, + &mi->stop_cond, + &mi->slave_running)) && + !force_all) + return error; + } + if ((thread_mask & (SLAVE_SQL|SLAVE_FORCE_ALL)) && mi->rli.slave_running) + { + DBUG_ASSERT(mi->rli.sql_thd != 0) ; + mi->rli.abort_slave=1; + if ((error=terminate_slave_thread(mi->rli.sql_thd,sql_lock, + sql_cond_lock, + &mi->rli.stop_cond, + &mi->rli.slave_running)) && + !force_all) + return error; + } + return 0; +} + +int terminate_slave_thread(THD* thd, pthread_mutex_t* term_lock, + pthread_mutex_t *cond_lock, + pthread_cond_t* term_cond, + volatile bool* slave_running) +{ + if (term_lock) + { + pthread_mutex_lock(term_lock); + if (!*slave_running) + { + pthread_mutex_unlock(term_lock); + return ER_SLAVE_NOT_RUNNING; + } + } + DBUG_ASSERT(thd != 0); + /* is is criticate to test if the slave is running. Otherwise, we might + be referening freed memory trying to kick it + */ + THD_CHECK_SENTRY(thd); + if (*slave_running) + { + KICK_SLAVE(thd); + } + while (*slave_running) + { + /* there is a small chance that slave thread might miss the first + alarm. To protect againts it, resend the signal until it reacts + */ + struct timespec abstime; +#ifdef HAVE_TIMESPEC_TS_SEC + abstime.ts_sec=time(NULL)+2; + abstime.ts_nsec=0; +#elif defined(__WIN__) + abstime.tv_sec=time((time_t*) 0)+2; + abstime.tv_nsec=0; +#else + struct timeval tv; + gettimeofday(&tv,0); + abstime.tv_sec=tv.tv_sec+2; + abstime.tv_nsec=tv.tv_usec*1000; +#endif + DBUG_ASSERT_LOCK(cond_lock); + pthread_cond_timedwait(term_cond, cond_lock, &abstime); + if (*slave_running) + { + KICK_SLAVE(thd); + } + } + if (term_lock) + pthread_mutex_unlock(term_lock); + return 0; +} + +int start_slave_thread(pthread_handler h_func, pthread_mutex_t* start_lock, + pthread_mutex_t *cond_lock, + pthread_cond_t* start_cond, + volatile bool* slave_running, + MASTER_INFO* mi) +{ + pthread_t th; + DBUG_ASSERT(mi->inited); + if (start_lock) + pthread_mutex_lock(start_lock); + if (!server_id) + { + if (start_cond) + pthread_cond_broadcast(start_cond); + if (start_lock) + pthread_mutex_unlock(start_lock); + sql_print_error("Server id not set, will not start slave"); + return ER_BAD_SLAVE; + } + + if (*slave_running) + { + if (start_cond) + pthread_cond_broadcast(start_cond); + if (start_lock) + pthread_mutex_unlock(start_lock); + return ER_SLAVE_MUST_STOP; + } + if (pthread_create(&th, &connection_attrib, h_func, (void*)mi)) + { + if (start_lock) + pthread_mutex_unlock(start_lock); + return ER_SLAVE_THREAD; + } + if (start_cond && cond_lock) + { + THD* thd = current_thd; + while (!*slave_running) + { + const char* old_msg = thd->enter_cond(start_cond,cond_lock, + "Waiting for slave thread to start"); + pthread_cond_wait(start_cond,cond_lock); + thd->exit_cond(old_msg); + // TODO: in a very rare case of init_slave_thread failing, it is + // possible that we can get stuck here since slave_running will not + // be set. We need to change slave_running to int and have -1 as + // error code + if (thd->killed) + { + pthread_mutex_unlock(cond_lock); + return ER_SERVER_SHUTDOWN; + } + } + } + if (start_lock) + pthread_mutex_unlock(start_lock); + return 0; +} +/* SLAVE_FORCE_ALL is not implemented here on purpose since it does not make + sense to do that for starting a slave - we always care if it actually + started the threads that were not previously running +*/ +int start_slave_threads(bool need_slave_mutex, bool wait_for_start, + MASTER_INFO* mi, const char* master_info_fname, + const char* slave_info_fname, int thread_mask) +{ + pthread_mutex_t *lock_io=0,*lock_sql=0,*lock_cond_io=0,*lock_cond_sql=0; + pthread_cond_t* cond_io=0,*cond_sql=0; + int error=0; + + if (need_slave_mutex) + { + lock_io = &mi->run_lock; + lock_sql = &mi->rli.run_lock; + } + if (wait_for_start) + { + cond_io = &mi->start_cond; + cond_sql = &mi->rli.start_cond; + lock_cond_io = &mi->run_lock; + lock_cond_sql = &mi->rli.run_lock; + } + if (init_master_info(mi,master_info_fname,slave_info_fname)) + return ER_MASTER_INFO; + + if ((thread_mask & SLAVE_IO) && + (error=start_slave_thread(handle_slave_io,lock_io,lock_cond_io, + cond_io,&mi->slave_running, + mi))) + return error; + if ((thread_mask & SLAVE_SQL) && + (error=start_slave_thread(handle_slave_sql,lock_sql,lock_cond_sql, + cond_sql, + &mi->rli.slave_running,mi))) + return error; + return 0; +} + void init_table_rule_hash(HASH* h, bool* h_inited) { hash_init(h, TABLE_RULE_HASH_SIZE,0,0, @@ -124,11 +528,11 @@ static TABLE_RULE_ENT* find_wild(DYNAMIC_ARRAY *a, const char* key, int len) uint i; const char* key_end = key + len; - for(i = 0; i < a->elements; i++) + for (i = 0; i < a->elements; i++) { TABLE_RULE_ENT* e ; get_dynamic(a, (gptr)&e, i); - if(!wild_case_compare(key, key_end, (const char*)e->db, + if (!wild_case_compare(key, key_end, (const char*)e->db, (const char*)(e->db + e->key_len),'\\')) return e; } @@ -152,7 +556,7 @@ int tables_ok(THD* thd, TABLE_LIST* tables) if (hash_search(&replicate_do_table, (byte*) hash_key, len)) return 1; } - if (ignore_table_inited) // if there are any do's + if (ignore_table_inited) // if there are any ignores { if (hash_search(&replicate_ignore_table, (byte*) hash_key, len)) return 0; @@ -217,38 +621,55 @@ static void free_string_array(DYNAMIC_ARRAY *a) delete_dynamic(a); } +static int end_slave_on_walk(MASTER_INFO* mi, gptr /*unused*/) +{ + end_master_info(mi); + return 0; +} + void end_slave() { - pthread_mutex_lock(&LOCK_slave); - if (slave_running) - { - abort_slave = 1; - thr_alarm_kill(slave_real_id); -#ifdef SIGNAL_WITH_VIO_CLOSE - slave_thd->close_active_vio(); -#endif - while (slave_running) - pthread_cond_wait(&COND_slave_stopped, &LOCK_slave); - } - pthread_mutex_unlock(&LOCK_slave); - - end_master_info(&glob_mi); - if(do_table_inited) + // TODO: replace the line below with + // list_walk(&master_list, (list_walk_action)end_slave_on_walk,0); + // once multi-master code is ready + terminate_slave_threads(active_mi,SLAVE_FORCE_ALL); + end_master_info(active_mi); + if (do_table_inited) hash_free(&replicate_do_table); - if(ignore_table_inited) + if (ignore_table_inited) hash_free(&replicate_ignore_table); - if(wild_do_table_inited) + if (wild_do_table_inited) free_string_array(&replicate_wild_do_table); - if(wild_ignore_table_inited) + if (wild_ignore_table_inited) free_string_array(&replicate_wild_ignore_table); } -inline bool slave_killed(THD* thd) +static bool io_slave_killed(THD* thd, MASTER_INFO* mi) +{ + DBUG_ASSERT(mi->io_thd == thd); + DBUG_ASSERT(mi->slave_running == 1); // tracking buffer overrun + return mi->abort_slave || abort_loop || thd->killed; +} + +static bool sql_slave_killed(THD* thd, RELAY_LOG_INFO* rli) +{ + DBUG_ASSERT(rli->sql_thd == thd); + DBUG_ASSERT(rli->slave_running == 1);// tracking buffer overrun + return rli->abort_slave || abort_loop || thd->killed; +} + +void slave_print_error(RELAY_LOG_INFO* rli, int err_code, const char* msg, ...) { - return abort_slave || abort_loop || thd->killed; + va_list args; + va_start(args,msg); + my_vsnprintf(rli->last_slave_error, + sizeof(rli->last_slave_error), msg, args); + sql_print_error("Slave: %s, error_code=%d", rli->last_slave_error, + err_code); + rli->last_slave_errno = err_code; } -inline void skip_load_data_infile(NET* net) +void skip_load_data_infile(NET* net) { (void)my_net_write(net, "\xfb/dev/null", 10); (void)net_flush(net); @@ -256,7 +677,7 @@ inline void skip_load_data_infile(NET* net) send_ok(net); // the master expects it } -inline char* rewrite_db(char* db) +char* rewrite_db(char* db) { if(replicate_rewrite_db.is_empty() || !db) return db; I_List_iterator<i_string_pair> it(replicate_rewrite_db); @@ -274,7 +695,7 @@ inline char* rewrite_db(char* db) int db_ok(const char* db, I_List<i_string> &do_list, I_List<i_string> &ignore_list ) { - if(do_list.is_empty() && ignore_list.is_empty()) + if (do_list.is_empty() && ignore_list.is_empty()) return 1; // ok to replicate if the user puts no constraints // if the user has specified restrictions on which databases to replicate @@ -352,16 +773,65 @@ static int init_intvar_from_file(int* var, IO_CACHE* f, int default_val) return 1; } +static int check_master_version(MYSQL* mysql, MASTER_INFO* mi) +{ + MYSQL_RES* res; + MYSQL_ROW row; + const char* version; + const char* errmsg = 0; + + if (mc_mysql_query(mysql, "SELECT VERSION()", 0) + || !(res = mc_mysql_store_result(mysql))) + { + sql_print_error("Error checking master version: %s", + mc_mysql_error(mysql)); + return 1; + } + if (!(row = mc_mysql_fetch_row(res))) + { + errmsg = "Master returned no rows for SELECT VERSION()"; + goto err; + } + if (!(version = row[0])) + { + errmsg = "Master reported NULL for the version"; + goto err; + } + + switch (*version) + { + case '3': + mi->old_format = 1; + break; + case '4': + mi->old_format = 0; + break; + default: + errmsg = "Master reported unrecognized MySQL version"; + goto err; + } +err: + if (res) + mc_mysql_free_result(res); + if (errmsg) + { + sql_print_error(errmsg); + return 1; + } + return 0; +} + static int create_table_from_dump(THD* thd, NET* net, const char* db, const char* table_name) { - uint packet_len = my_net_read(net); // read create table statement + ulong packet_len = my_net_read(net); // read create table statement Vio* save_vio; HA_CHECK_OPT check_opt; TABLE_LIST tables; int error= 1; handler *file; + uint save_options; if (packet_len == packet_error) { @@ -387,12 +857,17 @@ static int create_table_from_dump(THD* thd, NET* net, const char* db, thd->current_tablenr = 0; thd->query_error = 0; thd->net.no_send_ok = 1; + + /* we do not want to log create table statement */ + save_options = thd->options; + thd->options &= ~OPTION_BIN_LOG; 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 + thd->options = save_options; if (thd->query_error) goto err; // mysql_parse took care of the error send @@ -420,8 +895,7 @@ static int create_table_from_dump(THD* thd, NET* net, const char* db, } check_opt.init(); - check_opt.flags|= T_VERY_SILENT | T_CALC_CHECKSUM; - check_opt.quick = 1; + check_opt.flags|= T_VERY_SILENT | T_CALC_CHECKSUM | T_QUICK; thd->proc_info = "Rebuilding the index on master dump table"; // we do not want repair() to spam us with messages // just send them to the error log, and report the failure in case of @@ -439,51 +913,62 @@ err: return error; } -int fetch_nx_table(THD* thd, MASTER_INFO* mi) +int fetch_master_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) - { - sql_print_error("fetch_nx_table: Error in mysql_init()"); - nx_errno = ER_GET_ERRNO; + int fetch_errno = 0; + bool called_connected = (mysql != NULL); + if (!called_connected && !(mysql = mc_mysql_init(NULL))) + { + sql_print_error("fetch_master_table: Error in mysql_init()"); + fetch_errno = ER_GET_ERRNO; goto err; } - safe_connect(thd, mysql, mi); - if (slave_killed(thd)) + 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); + fetch_errno = ER_CONNECT_TO_MASTER; + goto err; + } + } + if (thd->killed) 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 "); + fetch_errno = ER_GET_ERRNO; + sql_print_error("fetch_master_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 "); + sql_print_error("fetch_master_table: failed on create table "); goto err; } - 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"); + if (fetch_errno && thd->net.vio) + send_error(&thd->net, fetch_errno, "Error in fetch_master_table"); thd->net.no_send_ok = 0; // Clear up garbage after create_table_from_dump return error; } void end_master_info(MASTER_INFO* mi) { - if(mi->fd >= 0) + if (!mi->inited) + return; + end_relay_log_info(&mi->rli); + if (mi->fd >= 0) { end_io_cache(&mi->file); (void)my_close(mi->fd, MYF(MY_WME)); @@ -492,21 +977,209 @@ void end_master_info(MASTER_INFO* mi) mi->inited = 0; } -int init_master_info(MASTER_INFO* mi) +int init_relay_log_info(RELAY_LOG_INFO* rli, const char* info_fname) +{ + DBUG_ENTER("init_relay_log_info"); + if (rli->inited) + DBUG_RETURN(0); + MY_STAT stat_area; + char fname[FN_REFLEN+128]; + int info_fd; + const char* msg = 0; + int error = 0; + fn_format(fname, info_fname, + mysql_data_home, "", 4+32); + pthread_mutex_lock(&rli->data_lock); + info_fd = rli->info_fd; + rli->pending = 0; + rli->cur_log_fd = -1; + rli->slave_skip_counter=0; + rli->log_pos_current=0; + rli->abort_pos_wait=0; + rli->skip_log_purge=0; + rli->log_space_limit = relay_log_space_limit; + rli->log_space_total = 0; + // TODO: make this work with multi-master + if (!opt_relay_logname) + { + char tmp[FN_REFLEN]; + /* TODO: The following should be using fn_format(); We just need to + first change fn_format() to cut the file name if it's too long. + */ + strmake(tmp,glob_hostname,FN_REFLEN-5); + strmov(strcend(tmp,'.'),"-relay-bin"); + opt_relay_logname=my_strdup(tmp,MYF(MY_WME)); + } + rli->relay_log.set_index_file_name(opt_relaylog_index_name); + open_log(&rli->relay_log, glob_hostname, opt_relay_logname, "-relay-bin", + LOG_BIN, 1 /* read_append cache */, + 1 /* no auto events*/); + + /* if file does not exist */ + if (!my_stat(fname, &stat_area, MYF(0))) + { + // if someone removed the file from underneath our feet, just close + // the old descriptor and re-create the old file + if (info_fd >= 0) + my_close(info_fd, MYF(MY_WME)); + if ((info_fd = my_open(fname, O_CREAT|O_RDWR|O_BINARY, MYF(MY_WME))) < 0 + || init_io_cache(&rli->info_file, info_fd, IO_SIZE*2, READ_CACHE, 0L,0, + MYF(MY_WME))) + { + if(info_fd >= 0) + my_close(info_fd, MYF(0)); + rli->info_fd=-1; + pthread_mutex_unlock(&rli->data_lock); + DBUG_RETURN(1); + } + if (init_relay_log_pos(rli,"",4,0/*no data mutex*/,&msg)) + goto err; + rli->master_log_pos = 0; // uninitialized + rli->info_fd = info_fd; + } + else // file exists + { + if(info_fd >= 0) + reinit_io_cache(&rli->info_file, READ_CACHE, 0L,0,0); + else if((info_fd = my_open(fname, O_RDWR|O_BINARY, MYF(MY_WME))) < 0 + || init_io_cache(&rli->info_file, info_fd, + IO_SIZE*2, READ_CACHE, 0L, + 0, MYF(MY_WME))) + { + if (info_fd >= 0) + my_close(info_fd, MYF(0)); + rli->info_fd=-1; + pthread_mutex_unlock(&rli->data_lock); + DBUG_RETURN(1); + } + + rli->info_fd = info_fd; + if (init_strvar_from_file(rli->relay_log_name, + sizeof(rli->relay_log_name), &rli->info_file, + (char*)"") || + init_intvar_from_file((int*)&rli->relay_log_pos, + &rli->info_file, 4) || + init_strvar_from_file(rli->master_log_name, + sizeof(rli->master_log_name), &rli->info_file, + (char*)"") || + init_intvar_from_file((int*)&rli->master_log_pos, + &rli->info_file, 0)) + { + msg="Error reading slave log configuration"; + goto err; + } + if (init_relay_log_pos(rli,0 /*log already inited*/, + 0 /*pos already inited*/, + 0 /* no data lock*/, + &msg)) + goto err; + } + DBUG_ASSERT(rli->relay_log_pos >= 4); + DBUG_ASSERT(my_b_tell(rli->cur_log) == rli->relay_log_pos); + rli->inited = 1; + // now change the cache from READ to WRITE - must do this + // before flush_relay_log_info + reinit_io_cache(&rli->info_file, WRITE_CACHE,0L,0,1); + error=test(flush_relay_log_info(rli)); + if (count_relay_log_space(rli)) + { + msg="Error counting relay log space"; + goto err; + } + pthread_mutex_unlock(&rli->data_lock); + DBUG_RETURN(error); + +err: + sql_print_error(msg); + end_io_cache(&rli->info_file); + my_close(info_fd, MYF(0)); + rli->info_fd=-1; + pthread_mutex_unlock(&rli->data_lock); + DBUG_RETURN(1); +} + +static inline int add_relay_log(RELAY_LOG_INFO* rli,LOG_INFO* linfo) +{ + MY_STAT s; + DBUG_ENTER("add_relay_log"); + if (!my_stat(linfo->log_file_name,&s,MYF(0))) + { + sql_print_error("log %s listed in the index, but failed to stat", + linfo->log_file_name); + DBUG_RETURN(1); + } + rli->log_space_total += s.st_size; +#ifndef DBUG_OFF + char buf[22]; +#endif + DBUG_PRINT("info",("log_space_total: %s", llstr(rli->log_space_total,buf))); + DBUG_RETURN(0); +} + +static bool wait_for_relay_log_space(RELAY_LOG_INFO* rli) +{ + bool slave_killed; + LINT_INIT(slave_killed); + MASTER_INFO* mi = rli->mi; + const char* save_proc_info; + THD* thd = mi->io_thd; + DBUG_ENTER("wait_for_relay_log_space"); + pthread_mutex_lock(&rli->log_space_lock); + save_proc_info = thd->proc_info; + thd->proc_info = "Waiting for relay log space to free"; + while (rli->log_space_limit < rli->log_space_total && + !(slave_killed=io_slave_killed(thd,mi))) + { + pthread_cond_wait(&rli->log_space_cond, &rli->log_space_lock); + } + thd->proc_info = save_proc_info; + pthread_mutex_unlock(&rli->log_space_lock); + DBUG_RETURN(slave_killed); +} + +static int count_relay_log_space(RELAY_LOG_INFO* rli) +{ + LOG_INFO linfo; + DBUG_ENTER("count_relay_log_space"); + rli->log_space_total = 0; + if (rli->relay_log.find_first_log(&linfo,"")) + { + sql_print_error("Could not find first log while counting relay log space"); + DBUG_RETURN(1); + } + if (add_relay_log(rli,&linfo)) + DBUG_RETURN(1); + while (!rli->relay_log.find_next_log(&linfo)) + { + if (add_relay_log(rli,&linfo)) + DBUG_RETURN(1); + } + DBUG_RETURN(0); +} + +int init_master_info(MASTER_INFO* mi, const char* master_info_fname, + const char* slave_info_fname) { if (mi->inited) return 0; - int fd,length,error; + if (init_relay_log_info(&mi->rli, slave_info_fname)) + return 1; + mi->rli.mi = mi; + mi->mysql=0; + mi->file_id=1; + mi->ignore_stop_event=0; + int fd,error; 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_fname, mysql_data_home, "", 4+32); - // we need a mutex while we are changing master info parameters to - // keep other threads from reading bogus info + /* + We need a mutex while we are changing master info parameters to + keep other threads from reading bogus info + */ - pthread_mutex_lock(&mi->lock); - mi->pending = 0; + pthread_mutex_lock(&mi->data_lock); fd = mi->fd; // we do not want any messages if the file does not exist @@ -522,11 +1195,13 @@ int init_master_info(MASTER_INFO* mi) { if(fd >= 0) my_close(fd, MYF(0)); - pthread_mutex_unlock(&mi->lock); + mi->fd=-1; + end_relay_log_info(&mi->rli); + pthread_mutex_unlock(&mi->data_lock); return 1; } - mi->log_file_name[0] = 0; - mi->pos = 4; // skip magic number + mi->master_log_name[0] = 0; + mi->master_log_pos = 4; // skip magic number mi->fd = fd; if (master_host) @@ -548,99 +1223,147 @@ int init_master_info(MASTER_INFO* mi) { if(fd >= 0) my_close(fd, MYF(0)); - pthread_mutex_unlock(&mi->lock); + mi->fd=-1; + end_relay_log_info(&mi->rli); + pthread_mutex_unlock(&mi->data_lock); return 1; } - - if ((length=my_b_gets(&mi->file, mi->log_file_name, - sizeof(mi->log_file_name))) < 1) - { - msg="Error reading log file name from master info file "; - goto error; - } - - mi->log_file_name[length-1]= 0; // kill \n - /* Reuse fname buffer */ - if(!my_b_gets(&mi->file, fname, sizeof(fname))) - { - msg="Error reading log file position from master info file"; - goto error; - } - mi->pos = strtoull(fname,(char**) 0, 10); mi->fd = fd; - if(init_strvar_from_file(mi->host, sizeof(mi->host), &mi->file, - master_host) || - init_strvar_from_file(mi->user, sizeof(mi->user), &mi->file, - master_user) || - init_strvar_from_file(mi->password, HASH_PASSWORD_LENGTH+1, &mi->file, - 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)) + if (init_strvar_from_file(mi->master_log_name, + sizeof(mi->master_log_name), &mi->file, + (char*)"") || + init_intvar_from_file((int*)&mi->master_log_pos, &mi->file, 4) || + init_strvar_from_file(mi->host, sizeof(mi->host), &mi->file, + master_host) || + init_strvar_from_file(mi->user, sizeof(mi->user), &mi->file, + master_user) || + init_strvar_from_file(mi->password, HASH_PASSWORD_LENGTH+1, &mi->file, + 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)) { msg="Error reading master configuration"; - goto error; + goto err; } } mi->inited = 1; - // now change the cache from READ to WRITE - must do this - // before flush_master_info - reinit_io_cache(&mi->file, WRITE_CACHE, 0L,0,1); + // now change cache READ -> WRITE - must do this before flush_master_info + reinit_io_cache(&mi->file, WRITE_CACHE,0L,0,1); error=test(flush_master_info(mi)); - pthread_mutex_unlock(&mi->lock); + pthread_mutex_unlock(&mi->data_lock); return error; -error: +err: sql_print_error(msg); end_io_cache(&mi->file); + end_relay_log_info(&mi->rli); + DBUG_ASSERT(fd>=0); my_close(fd, MYF(0)); - pthread_mutex_unlock(&mi->lock); + mi->fd=-1; + pthread_mutex_unlock(&mi->data_lock); return 1; } -int show_master_info(THD* thd) +int register_slave_on_master(MYSQL* mysql) { + String packet; + 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); + int4store(buf, rpl_recovery_rank); + packet.append(buf, 4); + int4store(buf, 0); /* tell the master will fill in master_id */ + packet.append(buf, 4); + + 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, MASTER_INFO* mi) +{ + // TODO: fix this for multi-master DBUG_ENTER("show_master_info"); List<Item> field_list; field_list.push_back(new Item_empty_string("Master_Host", - sizeof(glob_mi.host))); + sizeof(mi->host))); field_list.push_back(new Item_empty_string("Master_User", - sizeof(glob_mi.user))); + sizeof(mi->user))); field_list.push_back(new Item_empty_string("Master_Port", 6)); field_list.push_back(new Item_empty_string("Connect_retry", 6)); - field_list.push_back( new Item_empty_string("Log_File", + field_list.push_back(new Item_empty_string("Master_Log_File", + FN_REFLEN)); + field_list.push_back(new Item_empty_string("Read_Master_Log_Pos", 12)); + field_list.push_back(new Item_empty_string("Relay_Log_File", + FN_REFLEN)); + field_list.push_back(new Item_empty_string("Relay_Log_Pos", 12)); + field_list.push_back(new Item_empty_string("Relay_Master_Log_File", FN_REFLEN)); - field_list.push_back(new Item_empty_string("Pos", 12)); - field_list.push_back(new Item_empty_string("Slave_Running", 3)); + field_list.push_back(new Item_empty_string("Slave_IO_Running", 3)); + field_list.push_back(new Item_empty_string("Slave_SQL_Running", 3)); field_list.push_back(new Item_empty_string("Replicate_do_db", 20)); field_list.push_back(new Item_empty_string("Replicate_ignore_db", 20)); 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("Exec_master_log_pos", 12)); + field_list.push_back(new Item_empty_string("Relay_log_space", 12)); if(send_fields(thd, field_list, 1)) DBUG_RETURN(-1); String* packet = &thd->packet; packet->length(0); - pthread_mutex_lock(&glob_mi.lock); - net_store_data(packet, glob_mi.host); - net_store_data(packet, glob_mi.user); - 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 - pthread_mutex_unlock(&glob_mi.lock); - pthread_mutex_lock(&LOCK_slave); - net_store_data(packet, slave_running ? "Yes":"No"); - pthread_mutex_unlock(&LOCK_slave); + pthread_mutex_lock(&mi->data_lock); + pthread_mutex_lock(&mi->rli.data_lock); + net_store_data(packet, mi->host); + net_store_data(packet, mi->user); + net_store_data(packet, (uint32) mi->port); + net_store_data(packet, (uint32) mi->connect_retry); + net_store_data(packet, mi->master_log_name); + net_store_data(packet, (longlong) mi->master_log_pos); + net_store_data(packet, mi->rli.relay_log_name + + dirname_length(mi->rli.relay_log_name)); + net_store_data(packet, (longlong) mi->rli.relay_log_pos); + net_store_data(packet, mi->rli.master_log_name); + net_store_data(packet, mi->slave_running ? "Yes":"No"); + net_store_data(packet, mi->rli.slave_running ? "Yes":"No"); net_store_data(packet, &replicate_do_db); net_store_data(packet, &replicate_ignore_db); - 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, (uint32)mi->rli.last_slave_errno); + net_store_data(packet, mi->rli.last_slave_error); + net_store_data(packet, mi->rli.slave_skip_counter); + net_store_data(packet, (longlong) mi->rli.master_log_pos); + net_store_data(packet, (longlong) mi->rli.log_space_total); + pthread_mutex_unlock(&mi->rli.data_lock); + pthread_mutex_unlock(&mi->data_lock); if (my_net_write(&thd->net, (char*)thd->packet.ptr(), packet->length())) DBUG_RETURN(-1); @@ -655,54 +1378,65 @@ int flush_master_info(MASTER_INFO* mi) char lbuf[22]; my_b_seek(file, 0L); - my_b_printf(file, "%s\n%s\n%s\n%s\n%s\n%d\n%d\n", - mi->log_file_name, llstr(mi->pos, lbuf), mi->host, mi->user, - mi->password, mi->port, mi->connect_retry); + my_b_printf(file, "%s\n%s\n%s\n%s\n%s\n%d\n%d\n%d\n", + mi->master_log_name, llstr(mi->master_log_pos, lbuf), + mi->host, mi->user, + mi->password, mi->port, mi->connect_retry + ); flush_io_cache(file); return 0; } -int st_master_info::wait_for_pos(THD* thd, String* log_name, ulonglong log_pos) +int st_relay_log_info::wait_for_pos(THD* thd, String* log_name, + ulonglong log_pos) { if (!inited) return -1; - bool pos_reached; + bool pos_reached = 0; int event_count = 0; - pthread_mutex_lock(&lock); - while(!thd->killed) + pthread_mutex_lock(&data_lock); + abort_pos_wait=0; // abort only if master info changes during wait + while (!thd->killed || !abort_pos_wait) { int cmp_result; - if (*log_file_name) + if (abort_pos_wait) + { + abort_pos_wait=0; + pthread_mutex_unlock(&data_lock); + return -1; + } + DBUG_ASSERT(*master_log_name || master_log_pos == 0); + if (*master_log_name) { /* We should use dirname_length() here when we have a version of this that doesn't modify the argument */ - char *basename = strrchr(log_file_name, FN_LIBCHAR); + char *basename = strrchr(master_log_name, FN_LIBCHAR); if (basename) ++basename; else - basename = log_file_name; + basename = master_log_name; cmp_result = strncmp(basename, log_name->ptr(), log_name->length()); } else cmp_result = 0; - pos_reached = ((!cmp_result && pos >= log_pos) || cmp_result > 0); + pos_reached = ((!cmp_result && master_log_pos >= log_pos) || + cmp_result > 0); if (pos_reached || thd->killed) break; - const char* msg = thd->enter_cond(&cond, &lock, + const char* msg = thd->enter_cond(&data_cond, &data_lock, "Waiting for master update"); - pthread_cond_wait(&cond, &lock); + pthread_cond_wait(&data_cond, &data_lock); thd->exit_cond(msg); event_count++; } - pthread_mutex_unlock(&lock); + pthread_mutex_unlock(&data_lock); return thd->killed ? -1 : event_count; } - -static int init_slave_thread(THD* thd) +static int init_slave_thread(THD* thd, SLAVE_THD_TYPE thd_type) { DBUG_ENTER("init_slave_thread"); thd->system_thread = thd->bootstrap = 1; @@ -716,17 +1450,13 @@ static int init_slave_thread(THD* thd) thd->options = (((opt_log_slave_updates) ? OPTION_BIN_LOG:0) | OPTION_AUTO_IS_NULL) ; thd->system_thread = 1; thd->client_capabilities = CLIENT_LOCAL_FILES; - slave_real_id=thd->real_id=pthread_self(); + thd->real_id=pthread_self(); pthread_mutex_lock(&LOCK_thread_count); thd->thread_id = thread_id++; pthread_mutex_unlock(&LOCK_thread_count); - if (init_thr_lock() || - my_pthread_setspecific_ptr(THR_THD, thd) || - my_pthread_setspecific_ptr(THR_MALLOC, &thd->mem_root) || - my_pthread_setspecific_ptr(THR_NET, &thd->net)) + if (init_thr_lock() || thd->store_globals()) { - close_connection(&thd->net,ER_OUT_OF_RESOURCES); // is this needed? end_thread(thd,0); DBUG_RETURN(-1); } @@ -739,18 +1469,24 @@ static int init_slave_thread(THD* thd) VOID(pthread_sigmask(SIG_UNBLOCK,&set,&thd->block_signals)); #endif - thd->mem_root.free=thd->mem_root.used=0; // Probably not needed if (thd->max_join_size == (ulong) ~0L) thd->options |= OPTION_BIG_SELECTS; - thd->proc_info="Waiting for master update"; + if (thd_type == SLAVE_THD_SQL) + { + thd->proc_info = "Waiting for the next event in slave queue"; + } + else + { + thd->proc_info="Waiting for master update"; + } thd->version=refresh_version; thd->set_time(); - DBUG_RETURN(0); } -static int safe_sleep(THD* thd, int sec) +static int safe_sleep(THD* thd, int sec, CHECK_KILLED_FUNC thread_killed, + void* thread_killed_arg) { thr_alarm_t alarmed; thr_alarm_init(&alarmed); @@ -773,21 +1509,21 @@ static int safe_sleep(THD* thd, int sec) if (thr_alarm_in_use(&alarmed)) thr_end_alarm(&alarmed); - if (slave_killed(thd)) + if ((*thread_killed)(thd,thread_killed_arg)) return 1; start_time=time((time_t*) 0); } return 0; } - static int request_dump(MYSQL* mysql, MASTER_INFO* mi) { char buf[FN_REFLEN + 10]; int len; int binlog_flags = 0; // for now - char* logname = mi->log_file_name; - int4store(buf, mi->pos); + char* logname = mi->master_log_name; + // TODO if big log files: Change next to int8store() + int4store(buf, (longlong) mi->master_log_pos); int2store(buf + 4, binlog_flags); int4store(buf + 6, server_id); len = (uint) strlen(logname); @@ -805,7 +1541,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; @@ -833,10 +1569,9 @@ command"); return 0; } - -static uint read_event(MYSQL* mysql, MASTER_INFO *mi) +static ulong read_event(MYSQL* mysql, MASTER_INFO *mi) { - uint len = packet_error; + ulong len = packet_error; // my_real_read() will time us out // we check if we were told to die, and if not, try reading again @@ -868,328 +1603,71 @@ server_errno=%d)", return len - 1; } -static int check_expected_error(THD* thd, int expected_error) +int check_expected_error(THD* thd, RELAY_LOG_INFO* rli, int expected_error) { - switch(expected_error) + switch (expected_error) { case ER_NET_READ_ERROR: case ER_NET_ERROR_ON_WRITE: case ER_SERVER_SHUTDOWN: case ER_NEW_ABORTING_CONNECTION: - my_snprintf(last_slave_error, sizeof(last_slave_error), + my_snprintf(rli->last_slave_error, sizeof(rli->last_slave_error), "Slave: query '%s' partially completed on the master \ and was aborted. There is a chance that your master is inconsistent at this \ point. If you are sure that your master is ok, run this query manually on the\ slave and then restart the slave with SET SQL_SLAVE_SKIP_COUNTER=1;\ SLAVE START;", thd->query); - last_slave_errno = expected_error; - sql_print_error("%s",last_slave_error); + rli->last_slave_errno = expected_error; + sql_print_error("%s",rli->last_slave_error); return 1; default: return 0; } } -inline int ignored_error_code(int err_code) -{ - return use_slave_mask && bitmap_is_set(&slave_error_mask, err_code); -} - -static int exec_event(THD* thd, NET* net, MASTER_INFO* mi, int event_len) +static int exec_relay_log_event(THD* thd, RELAY_LOG_INFO* rli) { - Log_event * ev = Log_event::read_log_event((const char*)net->read_pos + 1, - event_len); - char llbuff[22]; - + DBUG_ASSERT(rli->sql_thd==thd); + Log_event * ev = next_event(rli); + DBUG_ASSERT(rli->sql_thd==thd); + if (sql_slave_killed(thd,rli)) + return 1; if (ev) { int type_code = ev->get_type_code(); - if (ev->server_id == ::server_id || slave_skip_counter) + int exec_res; + pthread_mutex_lock(&rli->data_lock); + if (ev->server_id == ::server_id || + (rli->slave_skip_counter && type_code != ROTATE_EVENT)) { - if(type_code == LOAD_EVENT) - skip_load_data_infile(net); - - mi->inc_pos(event_len); - flush_master_info(mi); - if(slave_skip_counter && /* protect against common user error of + /* TODO: I/O thread should not even log events with the same server id */ + rli->inc_pos(ev->get_event_len(), + type_code != STOP_EVENT ? ev->log_pos : LL(0), + 1/* skip lock*/); + flush_relay_log_info(rli); + if (rli->slave_skip_counter && /* protect against common user error of setting the counter to 1 instead of 2 while recovering from an failed auto-increment insert */ - !(type_code == INTVAR_EVENT && - slave_skip_counter == 1)) - --slave_skip_counter; + !((type_code == INTVAR_EVENT || type_code == STOP_EVENT) && + rli->slave_skip_counter == 1)) + --rli->slave_skip_counter; + pthread_mutex_unlock(&rli->data_lock); delete ev; return 0; // avoid infinite update loops } + pthread_mutex_unlock(&rli->data_lock); thd->server_id = ev->server_id; // use the original server id for logging thd->set_time(); // time the query - if(!ev->when) + if (!ev->when) ev->when = time(NULL); - - switch(type_code) { - case QUERY_EVENT: - { - Query_log_event* qev = (Query_log_event*)ev; - int q_len = qev->q_len; - int expected_error,actual_error = 0; - init_sql_alloc(&thd->mem_root, 8192,0); - thd->db = rewrite_db((char*)qev->db); - if (db_ok(thd->db, replicate_do_db, replicate_ignore_db)) - { - thd->query = (char*)qev->query; - thd->set_time((time_t)qev->when); - thd->current_tablenr = 0; - 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; - thd->slave_proxy_id = qev->thread_id; // for temp tables - - // sanity check to make sure the master did not get a really bad - // error on the query - if (ignored_error_code((expected_error=qev->error_code)) || - !check_expected_error(thd, expected_error)) - { - mysql_parse(thd, thd->query, q_len); - if (expected_error != - (actual_error = thd->net.last_errno) && expected_error && - !ignored_error_code(actual_error)) - { - const char* errmsg = "Slave: did not get the expected error\ - running query from master - expected: '%s' (%d), got '%s' (%d)"; - sql_print_error(errmsg, ER_SAFE(expected_error), - expected_error, - actual_error ? thd->net.last_error:"no error", - actual_error); - thd->query_error = 1; - } - else if (expected_error == actual_error || - ignored_error_code(actual_error)) - { - thd->query_error = 0; - *last_slave_error = 0; - last_slave_errno = 0; - } - } - else - { - // master could be inconsistent, abort and tell DBA to check/fix it - thd->db = thd->query = 0; - thd->convert_set = 0; - close_thread_tables(thd); - free_root(&thd->mem_root,0); - delete ev; - return 1; - } - } - thd->db = 0; // prevent db from being freed - thd->query = 0; // just to be sure - // assume no convert for next query unless set explictly - thd->convert_set = 0; - close_thread_tables(thd); - - if (thd->query_error || thd->fatal_error) - { - sql_print_error("Slave: error running query '%s' ", - qev->query); - last_slave_errno = actual_error ? actual_error : -1; - my_snprintf(last_slave_error, sizeof(last_slave_error), - "error '%s' on query '%s'", - actual_error ? thd->net.last_error : - "unexpected success or fatal error", - qev->query - ); - free_root(&thd->mem_root,0); - delete ev; - return 1; - } - free_root(&thd->mem_root,0); - delete ev; - - mi->inc_pos(event_len); - flush_master_info(mi); - break; - } - - case LOAD_EVENT: - { - Load_log_event* lev = (Load_log_event*)ev; - init_sql_alloc(&thd->mem_root, 8192,0); - thd->db = rewrite_db((char*)lev->db); - thd->query = 0; - thd->query_error = 0; - - if(db_ok(thd->db, replicate_do_db, replicate_ignore_db)) - { - thd->set_time((time_t)lev->when); - thd->current_tablenr = 0; - VOID(pthread_mutex_lock(&LOCK_thread_count)); - thd->query_id = query_id++; - VOID(pthread_mutex_unlock(&LOCK_thread_count)); - - TABLE_LIST tables; - bzero((char*) &tables,sizeof(tables)); - tables.db = thd->db; - tables.name = tables.real_name = (char*)lev->table_name; - tables.lock_type = TL_WRITE; - // the table will be opened in mysql_load - if(table_rules_on && !tables_ok(thd, &tables)) - { - skip_load_data_infile(net); - } - else - { - enum enum_duplicates handle_dup = DUP_IGNORE; - if(lev->sql_ex.opt_flags && REPLACE_FLAG) - handle_dup = DUP_REPLACE; - sql_exchange ex((char*)lev->fname, lev->sql_ex.opt_flags && - DUMPFILE_FLAG ); - String field_term(&lev->sql_ex.field_term, 1), - enclosed(&lev->sql_ex.enclosed, 1), - line_term(&lev->sql_ex.line_term,1), - escaped(&lev->sql_ex.escaped, 1), - line_start(&lev->sql_ex.line_start, 1); - - ex.field_term = &field_term; - if(lev->sql_ex.empty_flags & FIELD_TERM_EMPTY) - ex.field_term->length(0); - - ex.enclosed = &enclosed; - if(lev->sql_ex.empty_flags & ENCLOSED_EMPTY) - ex.enclosed->length(0); - - ex.line_term = &line_term; - if(lev->sql_ex.empty_flags & LINE_TERM_EMPTY) - ex.line_term->length(0); - - ex.line_start = &line_start; - if(lev->sql_ex.empty_flags & LINE_START_EMPTY) - ex.line_start->length(0); - - ex.escaped = &escaped; - if(lev->sql_ex.empty_flags & ESCAPED_EMPTY) - ex.escaped->length(0); - - ex.opt_enclosed = (lev->sql_ex.opt_flags & OPT_ENCLOSED_FLAG); - if(lev->sql_ex.empty_flags & FIELD_TERM_EMPTY) - ex.field_term->length(0); - - ex.skip_lines = lev->skip_lines; - - - List<Item> fields; - lev->set_fields(fields); - thd->slave_proxy_id = thd->thread_id; - thd->net.vio = net->vio; - // mysql_load will use thd->net to read the file - thd->net.pkt_nr = net->pkt_nr; - // make sure the client does not get confused - // about the packet sequence - if(mysql_load(thd, &ex, &tables, fields, handle_dup, 1, - TL_WRITE)) - thd->query_error = 1; - if(thd->cuted_fields) - sql_print_error("Slave: load data infile at position %s in log \ -'%s' produced %d warning(s)", llstr(glob_mi.pos,llbuff), RPL_LOG_NAME, - thd->cuted_fields ); - net->pkt_nr = thd->net.pkt_nr; - } - } - else - { - // we will just ask the master to send us /dev/null if we do not - // want to load the data :-) - skip_load_data_infile(net); - } - - thd->net.vio = 0; - thd->db = 0;// prevent db from being freed - close_thread_tables(thd); - if(thd->query_error) - { - int sql_error = thd->net.last_errno; - if(!sql_error) - sql_error = ER_UNKNOWN_ERROR; - - sql_print_error("Slave: Error '%s' running load data infile ", - ER(sql_error)); - delete ev; - free_root(&thd->mem_root,0); - return 1; - } - - delete ev; - free_root(&thd->mem_root,0); - - if(thd->fatal_error) - { - sql_print_error("Slave: Fatal error running query '%s' ", - thd->query); - return 1; - } - - mi->inc_pos(event_len); - flush_master_info(mi); - break; - } - - case START_EVENT: - mi->inc_pos(event_len); - flush_master_info(mi); - delete ev; - break; - - case STOP_EVENT: - if(mi->pos > 4) // stop event should be ignored after rotate event - { - close_temporary_tables(thd); - mi->inc_pos(event_len); - flush_master_info(mi); - } - delete ev; - break; - case ROTATE_EVENT: - { - Rotate_log_event* rev = (Rotate_log_event*)ev; - int ident_len = rev->ident_len; - 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 - pthread_cond_broadcast(&mi->cond); - pthread_mutex_unlock(&mi->lock); - flush_master_info(mi); -#ifndef DBUG_OFF - if(abort_slave_event_count) - ++events_till_abort; -#endif - delete ev; - break; - } - - case INTVAR_EVENT: - { - Intvar_log_event* iev = (Intvar_log_event*)ev; - switch(iev->type) - { - case LAST_INSERT_ID_EVENT: - thd->last_insert_id_used = 1; - thd->last_insert_id = iev->val; - break; - case INSERT_ID_EVENT: - thd->next_insert_id = iev->val; - break; - - } - mi->inc_pending(event_len); - delete ev; - break; - } - } + ev->thd = thd; + thd->log_pos = ev->log_pos; + exec_res = ev->exec_event(rli); + DBUG_ASSERT(rli->sql_thd==thd); + delete ev; + return exec_res; } else { @@ -1199,338 +1677,722 @@ This may also be a network problem, or just a bug in the master or slave code.\ "); return 1; } - return 0; } - -// slave thread -pthread_handler_decl(handle_slave,arg __attribute__((unused))) +/* slave I/O thread */ +pthread_handler_decl(handle_slave_io,arg) { #ifndef DBUG_OFF - slave_begin: +slave_begin: #endif THD *thd; // needs to be first for thread_stack MYSQL *mysql = NULL ; + MASTER_INFO* mi = (MASTER_INFO*)arg; char llbuff[22]; - - pthread_mutex_lock(&LOCK_slave); - if (!server_id) - { - pthread_cond_broadcast(&COND_slave_start); - pthread_mutex_unlock(&LOCK_slave); - sql_print_error("Server id not set, will not start slave"); - pthread_exit((void*)1); - } + bool retried_once = 0; + ulonglong last_failed_pos = 0; // TODO: see if last_failed_pos is needed + DBUG_ASSERT(mi->inited); - if(slave_running) - { - pthread_cond_broadcast(&COND_slave_start); - pthread_mutex_unlock(&LOCK_slave); - pthread_exit((void*)1); // safety just in case - } - slave_running = 1; - abort_slave = 0; + pthread_mutex_lock(&mi->run_lock); #ifndef DBUG_OFF - events_till_abort = abort_slave_event_count; + mi->events_till_abort = abort_slave_event_count; #endif - pthread_cond_broadcast(&COND_slave_start); - pthread_mutex_unlock(&LOCK_slave); - - // int error = 1; - bool retried_once = 0; - ulonglong last_failed_pos = 0; // needs to call my_thread_init(), otherwise we get a coredump in DBUG_ stuff my_thread_init(); - slave_thd = thd = new THD; // note that contructor of THD uses DBUG_ ! - thd->set_time(); - DBUG_ENTER("handle_slave"); + thd = new THD; // note that contructor of THD uses DBUG_ ! + DBUG_ENTER("handle_slave_io"); + THD_CHECK_SENTRY(thd); pthread_detach_this_thread(); - if (init_slave_thread(thd) || init_master_info(&glob_mi)) - { - sql_print_error("Failed during slave thread initialization"); - goto err; - } + if (init_slave_thread(thd, SLAVE_THD_IO)) + { + pthread_cond_broadcast(&mi->start_cond); + pthread_mutex_unlock(&mi->run_lock); + sql_print_error("Failed during slave I/O thread initialization"); + goto err; + } + mi->io_thd = thd; thd->thread_stack = (char*)&thd; // remember where our stack is - thd->temporary_tables = save_temporary_tables; // restore temp tables threads.append(thd); - glob_mi.pending = 0; //this should always be set to 0 when the slave thread - // is started + mi->slave_running = 1; + mi->abort_slave = 0; + pthread_cond_broadcast(&mi->start_cond); + pthread_mutex_unlock(&mi->run_lock); DBUG_PRINT("info",("master info: log_file_name=%s, position=%s", - glob_mi.log_file_name, llstr(glob_mi.pos,llbuff))); - + mi->master_log_name, llstr(mi->master_log_pos,llbuff))); - if (!(mysql = mc_mysql_init(NULL))) + if (!(mi->mysql = mysql = mc_mysql_init(NULL))) { - sql_print_error("Slave thread: error in mc_mysql_init()"); + sql_print_error("Slave I/O thread: error in mc_mysql_init()"); goto err; } thd->proc_info = "connecting to master"; #ifndef DBUG_OFF - sql_print_error("Slave thread initialized"); + sql_print_error("Slave I/O thread initialized"); #endif // we can get killed during safe_connect - if (!safe_connect(thd, mysql, &glob_mi)) - sql_print_error("Slave: connected to master '%s@%s:%d',\ - replication started in log '%s' at position %s", glob_mi.user, - glob_mi.host, glob_mi.port, - RPL_LOG_NAME, - llstr(glob_mi.pos,llbuff)); + if (!safe_connect(thd, mysql, mi)) + sql_print_error("Slave I/O thread: connected to master '%s@%s:%d',\ + replication started in log '%s' at position %s", mi->user, + mi->host, mi->port, + IO_RPL_LOG_NAME, + llstr(mi->master_log_pos,llbuff)); else { - sql_print_error("Slave thread killed while connecting to master"); + sql_print_error("Slave I/O thread killed while connecting to master"); goto err; } - + connected: + + thd->slave_net = &mysql->net; + thd->proc_info = "Checking master version"; + if (check_master_version(mysql, mi)) + goto err; + if (!mi->old_format) + { + /* + 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"; + if (register_slave_on_master(mysql) || update_slave_list(mysql)) + goto err; + } - while (!slave_killed(thd)) + while (!io_slave_killed(thd,mi)) { - thd->proc_info = "Requesting binlog dump"; - if(request_dump(mysql, &glob_mi)) - { - sql_print_error("Failed on request_dump()"); - if(slave_killed(thd)) - { - sql_print_error("Slave thread killed while requesting master \ + thd->proc_info = "Requesting binlog dump"; + if (request_dump(mysql, mi)) + { + sql_print_error("Failed on request_dump()"); + if(io_slave_killed(thd,mi)) + { + sql_print_error("Slave I/O thread killed while requesting master \ dump"); - goto err; - } + goto err; + } - thd->proc_info = "Waiiting to reconnect after a failed dump request"; - if(mysql->net.vio) - vio_close(mysql->net.vio); - // first time retry immediately, assuming that we can recover - // right away - if first time fails, sleep between re-tries - // hopefuly the admin can fix the problem sometime - if(retried_once) - safe_sleep(thd, glob_mi.connect_retry); - else - retried_once = 1; + thd->proc_info = "Waiiting to reconnect after a failed dump request"; + mc_end_server(mysql); + /* + First time retry immediately, assuming that we can recover + right away - if first time fails, sleep between re-tries + hopefuly the admin can fix the problem sometime + */ + if (retried_once) + safe_sleep(thd,mi->connect_retry,(CHECK_KILLED_FUNC)io_slave_killed, + (void*)mi); + else + retried_once = 1; - if(slave_killed(thd)) - { - sql_print_error("Slave thread killed while retrying master \ + if (io_slave_killed(thd,mi)) + { + sql_print_error("Slave I/O thread killed while retrying master \ dump"); - goto err; - } - - thd->proc_info = "Reconnecting after a failed dump request"; - last_failed_pos=glob_mi.pos; - sql_print_error("Slave: failed dump request, reconnecting to \ -try again, log '%s' at postion %s", RPL_LOG_NAME, - llstr(last_failed_pos,llbuff)); - if(safe_reconnect(thd, mysql, &glob_mi) || slave_killed(thd)) - { - sql_print_error("Slave thread killed during or after reconnect"); - goto err; - } - - goto connected; - } + goto err; + } - while(!slave_killed(thd)) - { - thd->proc_info = "Reading master update"; - uint event_len = read_event(mysql, &glob_mi); - if(slave_killed(thd)) - { - sql_print_error("Slave thread killed while reading event"); - goto err; - } + thd->proc_info = "Reconnecting after a failed dump request"; + sql_print_error("Slave I/O thread: failed dump request, \ +reconnecting to try again, log '%s' at postion %s", IO_RPL_LOG_NAME, + llstr(mi->master_log_pos,llbuff)); + if (safe_reconnect(thd, mysql, mi) || io_slave_killed(thd,mi)) + { + sql_print_error("Slave I/O thread killed during or \ +after reconnect"); + goto err; + } + + goto connected; + } + + while (!io_slave_killed(thd,mi)) + { + thd->proc_info = "Reading master update"; + ulong event_len = read_event(mysql, mi); + if (io_slave_killed(thd,mi)) + { + sql_print_error("Slave I/O thread killed while reading event"); + goto err; + } - if (event_len == packet_error) - { - if(mc_mysql_errno(mysql) == ER_NET_PACKET_TOO_LARGE) - { - sql_print_error("Log entry on master is longer than \ -max_allowed_packet on slave. Slave thread will be aborted. If the entry is \ -really supposed to be that long, restart the server with a higher value of \ -max_allowed_packet. The current value is %ld", max_allowed_packet); - goto err; - } + if (event_len == packet_error) + { + if (mc_mysql_errno(mysql) == ER_NET_PACKET_TOO_LARGE) + { + sql_print_error("Log entry on master is longer than \ +max_allowed_packet (%ld) on slave. Slave thread will be aborted. If the entry \ +is correct, restart the server with a higher value of max_allowed_packet", + max_allowed_packet); + goto err; + } - thd->proc_info = "Waiting to reconnect after a failed read"; - if(mysql->net.vio) - vio_close(mysql->net.vio); - if(retried_once) // punish repeat offender with sleep - safe_sleep(thd, glob_mi.connect_retry); - else - retried_once = 1; + thd->proc_info = "Waiting to reconnect after a failed read"; + mc_end_server(mysql); + if (retried_once) // punish repeat offender with sleep + safe_sleep(thd,mi->connect_retry,(CHECK_KILLED_FUNC)io_slave_killed, + (void*)mi); + else + retried_once = 1; - if(slave_killed(thd)) - { - sql_print_error("Slave thread killed while waiting to \ + if (io_slave_killed(thd,mi)) + { + sql_print_error("Slave I/O thread killed while waiting to \ reconnect after a failed read"); - goto err; - } - thd->proc_info = "Reconnecting after a failed read"; - last_failed_pos= glob_mi.pos; - sql_print_error("Slave: Failed reading log event, \ -reconnecting to retry, log '%s' position %s", RPL_LOG_NAME, - llstr(last_failed_pos, llbuff)); - if(safe_reconnect(thd, mysql, &glob_mi) || slave_killed(thd)) - { - sql_print_error("Slave thread killed during or after a \ + goto err; + } + thd->proc_info = "Reconnecting after a failed read"; + sql_print_error("Slave I/O thread: Failed reading log event, \ +reconnecting to retry, log '%s' position %s", IO_RPL_LOG_NAME, + llstr(mi->master_log_pos, llbuff)); + if (safe_reconnect(thd, mysql, mi) || io_slave_killed(thd,mi)) + { + sql_print_error("Slave I/O thread killed during or after a \ reconnect done to recover from failed read"); - goto err; - } - - goto connected; - } // if(event_len == packet_error) + goto err; + } + goto connected; + } // if(event_len == packet_error) - thd->proc_info = "Processing master log event"; - if(exec_event(thd, &mysql->net, &glob_mi, event_len)) - { - sql_print_error("\ -Error running query, slave aborted. Fix the problem, and re-start \ -the slave thread with \"mysqladmin start-slave\". We stopped at log \ -'%s' position %s", - RPL_LOG_NAME, llstr(glob_mi.pos, llbuff)); - goto err; - // there was an error running the query - // abort the slave thread, when the problem is fixed, the user - // should restart the slave with mysqladmin start-slave - } + thd->proc_info = "Queueing event from master"; + if (queue_event(mi,(const char*)mysql->net.read_pos + 1, + event_len)) + { + sql_print_error("Slave I/O thread could not queue event \ +from master"); + goto err; + } + flush_master_info(mi); + if (mi->rli.log_space_limit && mi->rli.log_space_limit < + mi->rli.log_space_total) + if (wait_for_relay_log_space(&mi->rli)) + { + sql_print_error("Slave I/O thread aborted while waiting for relay \ +log space"); + goto err; + } + // TODO: check debugging abort code #ifndef DBUG_OFF - if(abort_slave_event_count && !--events_till_abort) - { - sql_print_error("Slave: debugging abort"); - goto err; - } + if (abort_slave_event_count && !--events_till_abort) + { + sql_print_error("Slave I/O thread: debugging abort"); + goto err; + } #endif - - // successful exec with offset advance, - // the slave repents and his sins are forgiven! - if(glob_mi.pos > last_failed_pos) - { - retried_once = 0; + } + } + + // error = 0; +err: + // print the current replication position + sql_print_error("Slave I/O thread exiting, read up to log '%s', position %s", + IO_RPL_LOG_NAME, llstr(mi->master_log_pos,llbuff)); + thd->query = thd->db = 0; // extra safety + if (mysql) + { + mc_mysql_close(mysql); + mi->mysql=0; + } + thd->proc_info = "Waiting for slave mutex on exit"; + pthread_mutex_lock(&mi->run_lock); + mi->slave_running = 0; + mi->io_thd = 0; + // TODO: make rpl_status part of MASTER_INFO + change_rpl_status(RPL_ACTIVE_SLAVE,RPL_IDLE_SLAVE); + mi->abort_slave = 0; // TODO: check if this is needed + DBUG_ASSERT(thd->net.buff != 0); + net_end(&thd->net); // destructor will not free it, because net.vio is 0 + pthread_mutex_lock(&LOCK_thread_count); + THD_CHECK_SENTRY(thd); + delete thd; + pthread_mutex_unlock(&LOCK_thread_count); + my_thread_end(); // clean-up before broadcast + pthread_cond_broadcast(&mi->stop_cond); // tell the world we are done + pthread_mutex_unlock(&mi->run_lock); #ifndef DBUG_OFF - stuck_count = 0; -#endif - } + if(abort_slave_event_count && !events_till_abort) + goto slave_begin; +#endif + pthread_exit(0); + DBUG_RETURN(0); // Can't return anything here +} + + +/* slave SQL logic thread */ + +pthread_handler_decl(handle_slave_sql,arg) +{ #ifndef DBUG_OFF - else - { - // show a little mercy, allow slave to read one more event - // before cutting him off - otherwise he gets stuck - // on Intvar events, since they do not advance the offset - // immediately - if (++stuck_count > 2) - events_till_disconnect++; - } -#endif - } // while(!slave_killed(thd)) - read/exec loop - } // while(!slave_killed(thd)) - slave loop +slave_begin: +#endif + THD *thd; /* needs to be first for thread_stack */ + MYSQL *mysql = NULL ; + bool retried_once = 0; + ulonglong last_failed_pos = 0; // TODO: see if this can be removed + char llbuff[22],llbuff1[22]; + RELAY_LOG_INFO* rli = &((MASTER_INFO*)arg)->rli; + const char* errmsg=0; + DBUG_ASSERT(rli->inited); + pthread_mutex_lock(&rli->run_lock); + DBUG_ASSERT(!rli->slave_running); +#ifndef DBUG_OFF + rli->events_till_abort = abort_slave_event_count; +#endif + + + // needs to call my_thread_init(), otherwise we get a coredump in DBUG_ stuff + my_thread_init(); + thd = new THD; // note that contructor of THD uses DBUG_ ! + DBUG_ENTER("handle_slave_sql"); + THD_CHECK_SENTRY(thd); + + pthread_detach_this_thread(); + if (init_slave_thread(thd, SLAVE_THD_SQL)) + { + /* + TODO: this is currently broken - slave start and change master + will be stuck if we fail here + */ + pthread_cond_broadcast(&rli->start_cond); + pthread_mutex_unlock(&rli->run_lock); + sql_print_error("Failed during slave thread initialization"); + goto err; + } + THD_CHECK_SENTRY(thd); + thd->thread_stack = (char*)&thd; // remember where our stack is + thd->temporary_tables = rli->save_temporary_tables; // restore temp tables + threads.append(thd); + rli->sql_thd = thd; + rli->slave_running = 1; + rli->abort_slave = 0; + pthread_cond_broadcast(&rli->start_cond); + pthread_mutex_unlock(&rli->run_lock); + rli->pending = 0; //this should always be set to 0 when the slave thread + // is started + if (init_relay_log_pos(rli,0,0,1/*need data lock*/,&errmsg)) + { + sql_print_error("Error initializing relay log position: %s", + errmsg); + goto err; + } + DBUG_ASSERT(rli->relay_log_pos >= 4); + DBUG_ASSERT(my_b_tell(rli->cur_log) == rli->relay_log_pos); + + DBUG_PRINT("info",("master info: log_file_name=%s, position=%s", + rli->master_log_name, llstr(rli->master_log_pos,llbuff))); + DBUG_ASSERT(rli->sql_thd == thd); + sql_print_error("Slave SQL thread initialized, starting replication in \ +log '%s' at position %s,relay log: name='%s',pos='%s'", RPL_LOG_NAME, + llstr(rli->master_log_pos,llbuff),rli->relay_log_name, + llstr(rli->relay_log_pos,llbuff1)); + while (!sql_slave_killed(thd,rli)) + { + thd->proc_info = "Processing master log event"; + DBUG_ASSERT(rli->sql_thd == thd); + THD_CHECK_SENTRY(thd); + if (exec_relay_log_event(thd,rli)) + { + // do not scare the user if SQL thread was simply killed or stopped + if (!sql_slave_killed(thd,rli)) + sql_print_error("\ +Error running query, slave SQL thread aborted. Fix the problem, and restart \ +the slave SQL thread with \"SLAVE START\". We stopped at log \ +'%s' position %s", + RPL_LOG_NAME, llstr(rli->master_log_pos, llbuff)); + goto err; + } + } // while(!sql_slave_killed(thd,rli)) - read/exec loop // error = 0; err: // print the current replication position - sql_print_error("Slave thread exiting, replication stopped in log '%s' at \ -position %s", - RPL_LOG_NAME, llstr(glob_mi.pos,llbuff)); + sql_print_error("Slave SQL thread exiting, replication stopped in log \ + '%s' at position %s", + RPL_LOG_NAME, llstr(rli->master_log_pos,llbuff)); thd->query = thd->db = 0; // extra safety - if(mysql) - mc_mysql_close(mysql); thd->proc_info = "Waiting for slave mutex on exit"; - pthread_mutex_lock(&LOCK_slave); - slave_running = 0; - abort_slave = 0; - save_temporary_tables = thd->temporary_tables; + pthread_mutex_lock(&rli->run_lock); + DBUG_ASSERT(rli->slave_running == 1); // tracking buffer overrun + rli->slave_running = 0; + rli->save_temporary_tables = thd->temporary_tables; + + /* + TODO: see if we can do this conditionally in next_event() instead + to avoid unneeded position re-init + */ + rli->log_pos_current=0; thd->temporary_tables = 0; // remove tempation from destructor to close them - pthread_cond_broadcast(&COND_slave_stopped); // tell the world we are done - pthread_mutex_unlock(&LOCK_slave); + DBUG_ASSERT(thd->net.buff != 0); net_end(&thd->net); // destructor will not free it, because we are weird - slave_thd = 0; + DBUG_ASSERT(rli->sql_thd == thd); + THD_CHECK_SENTRY(thd); + rli->sql_thd = 0; + pthread_mutex_lock(&LOCK_thread_count); + THD_CHECK_SENTRY(thd); delete thd; - my_thread_end(); -#ifndef DBUG_OFF - if(abort_slave_event_count && !events_till_abort) + pthread_mutex_unlock(&LOCK_thread_count); + my_thread_end(); // clean-up before broadcasting termination + pthread_cond_broadcast(&rli->stop_cond); + // tell the world we are done + pthread_mutex_unlock(&rli->run_lock); +#ifndef DBUG_OFF // TODO: reconsider the code below + if (abort_slave_event_count && !rli->events_till_abort) goto slave_begin; #endif pthread_exit(0); DBUG_RETURN(0); // Can't return anything here } +static int process_io_create_file(MASTER_INFO* mi, Create_file_log_event* cev) +{ + int error = 1; + ulong num_bytes; + bool cev_not_written; + THD* thd; + NET* net = &mi->mysql->net; -/* try to connect until successful or slave killed */ + if (unlikely(!cev->is_valid())) + return 1; + /* + TODO: fix to honor table rules, not only db rules + */ + if (!db_ok(cev->db, replicate_do_db, replicate_ignore_db)) + { + skip_load_data_infile(net); + return 0; + } + DBUG_ASSERT(cev->inited_from_old); + thd = mi->io_thd; + thd->file_id = cev->file_id = mi->file_id++; + thd->server_id = cev->server_id; + cev_not_written = 1; + + if (unlikely(net_request_file(net,cev->fname))) + { + sql_print_error("Slave I/O: failed requesting download of '%s'", + cev->fname); + goto err; + } -static int safe_connect(THD* thd, MYSQL* mysql, MASTER_INFO* mi) + /* this dummy block is so we could instantiate Append_block_log_event + once and then modify it slightly instead of doing it multiple times + in the loop + */ + { + Append_block_log_event aev(thd,0,0); + + for (;;) + { + if (unlikely((num_bytes=my_net_read(net)) == packet_error)) + { + sql_print_error("Network read error downloading '%s' from master", + cev->fname); + goto err; + } + if (unlikely(!num_bytes)) /* eof */ + { + send_ok(net); /* 3.23 master wants it */ + Execute_load_log_event xev(thd); + xev.log_pos = mi->master_log_pos; + if (unlikely(mi->rli.relay_log.append(&xev))) + { + sql_print_error("Slave I/O: error writing Exec_load event to \ +relay log"); + goto err; + } + mi->rli.relay_log.harvest_bytes_written(&mi->rli.log_space_total); + break; + } + if (unlikely(cev_not_written)) + { + cev->block = (char*)net->read_pos; + cev->block_len = num_bytes; + cev->log_pos = mi->master_log_pos; + if (unlikely(mi->rli.relay_log.append(cev))) + { + sql_print_error("Slave I/O: error writing Create_file event to \ +relay log"); + goto err; + } + cev_not_written=0; + mi->rli.relay_log.harvest_bytes_written(&mi->rli.log_space_total); + } + else + { + aev.block = (char*)net->read_pos; + aev.block_len = num_bytes; + aev.log_pos = mi->master_log_pos; + if (unlikely(mi->rli.relay_log.append(&aev))) + { + sql_print_error("Slave I/O: error writing Append_block event to \ +relay log"); + goto err; + } + mi->rli.relay_log.harvest_bytes_written(&mi->rli.log_space_total) ; + } + } + } + error=0; +err: + return error; +} + +// We assume we already locked mi->data_lock +static int process_io_rotate(MASTER_INFO* mi, Rotate_log_event* rev) { - int slave_was_killed; + if (unlikely(!rev->is_valid())) + return 1; + DBUG_ASSERT(rev->ident_len<sizeof(mi->master_log_name)); + memcpy(mi->master_log_name,rev->new_log_ident, + rev->ident_len); + mi->master_log_name[rev->ident_len] = 0; + mi->master_log_pos = rev->pos; #ifndef DBUG_OFF - events_till_disconnect = disconnect_slave_event_count; -#endif - while(!(slave_was_killed = slave_killed(thd)) && - !mc_mysql_connect(mysql, mi->host, mi->user, mi->password, 0, - mi->port, 0, 0)) + /* + If we do not do this, we will be getting the first + rotate event forever, so we need to not disconnect after one. + */ + if (disconnect_slave_event_count) + events_till_disconnect++; +#endif + return 0; +} + +/* + TODO: verify the issue with stop events, see if we need them at all + in the relay log + TODO: test this code before release - it has to be tested on a separte + setup with 3.23 master +*/ + +static int queue_old_event(MASTER_INFO *mi, const char *buf, + ulong event_len) +{ + const char *errmsg = 0; + bool inc_pos = 1; + bool processed_stop_event = 0; + char* tmp_buf = 0; + /* if we get Load event, we need to pass a non-reusable buffer + to read_log_event, so we do a trick + */ + if (buf[EVENT_TYPE_OFFSET] == LOAD_EVENT) { - sql_print_error("Slave thread: error connecting to master: %s (%d),\ - retry in %d sec", mc_mysql_error(mysql), errno, mi->connect_retry); - safe_sleep(thd, mi->connect_retry); + if (unlikely(!(tmp_buf=(char*)my_malloc(event_len+1,MYF(MY_WME))))) + { + sql_print_error("Slave I/O: out of memory for Load event"); + return 1; + } + memcpy(tmp_buf,buf,event_len); + tmp_buf[event_len]=0; // Create_file constructor wants null-term buffer + buf = (const char*)tmp_buf; } - - if(!slave_was_killed) + Log_event *ev = Log_event::read_log_event(buf,event_len, &errmsg, + 1 /*old format*/ ); + if (unlikely(!ev)) + { + sql_print_error("Read invalid event from master: '%s',\ + master could be corrupt but a more likely cause of this is a bug", + errmsg); + my_free((char*)tmp_buf, MYF(MY_ALLOW_ZERO_PTR)); + return 1; + } + pthread_mutex_lock(&mi->data_lock); + ev->log_pos = mi->master_log_pos; + switch (ev->get_type_code()) { + case ROTATE_EVENT: + if (unlikely(process_io_rotate(mi,(Rotate_log_event*)ev))) { - mysql_log.write(thd, COM_CONNECT_OUT, "%s@%s:%d", - mi->user, mi->host, mi->port); -#ifdef SIGNAL_WITH_VIO_CLOSE - thd->set_active_vio(mysql->net.vio); -#endif + delete ev; + pthread_mutex_unlock(&mi->data_lock); + DBUG_ASSERT(!tmp_buf); + return 1; } + mi->ignore_stop_event=1; + inc_pos = 0; + break; + case STOP_EVENT: + processed_stop_event=1; + break; + case CREATE_FILE_EVENT: + { + int error = process_io_create_file(mi,(Create_file_log_event*)ev); + delete ev; + mi->master_log_pos += event_len; + pthread_mutex_unlock(&mi->data_lock); + DBUG_ASSERT(tmp_buf); + my_free((char*)tmp_buf, MYF(0)); + return error; + } + default: + mi->ignore_stop_event=0; + break; + } + if (likely(!processed_stop_event || !mi->ignore_stop_event)) + { + if (unlikely(mi->rli.relay_log.append(ev))) + { + delete ev; + pthread_mutex_unlock(&mi->data_lock); + DBUG_ASSERT(!tmp_buf); + return 1; + } + mi->rli.relay_log.harvest_bytes_written(&mi->rli.log_space_total); + } + delete ev; + if (likely(inc_pos)) + mi->master_log_pos += event_len; + if (unlikely(processed_stop_event)) + mi->ignore_stop_event=1; + pthread_mutex_unlock(&mi->data_lock); + DBUG_ASSERT(!tmp_buf); + return 0; +} + +/* + TODO: verify the issue with stop events, see if we need them at all + in the relay log +*/ + +int queue_event(MASTER_INFO* mi,const char* buf, ulong event_len) +{ + int error=0; + bool inc_pos = 1; + bool processed_stop_event = 0; + if (mi->old_format) + return queue_old_event(mi,buf,event_len); + + pthread_mutex_lock(&mi->data_lock); - return slave_was_killed; + /* + TODO: figure out if other events in addition to Rotate + require special processing + */ + switch (buf[EVENT_TYPE_OFFSET]) { + case STOP_EVENT: + processed_stop_event=1; + break; + case ROTATE_EVENT: + { + Rotate_log_event rev(buf,event_len,0); + if (unlikely(process_io_rotate(mi,&rev))) + return 1; + inc_pos=0; + mi->ignore_stop_event=1; + break; + } + default: + mi->ignore_stop_event=0; + break; + } + + if (likely((!processed_stop_event || !mi->ignore_stop_event) && + !(error = mi->rli.relay_log.appendv(buf,event_len,0)))) + { + if (likely(inc_pos)) + mi->master_log_pos += event_len; + mi->rli.relay_log.harvest_bytes_written(&mi->rli.log_space_total); + } + if (unlikely(processed_stop_event)) + mi->ignore_stop_event=1; + pthread_mutex_unlock(&mi->data_lock); + return error; +} + + +void end_relay_log_info(RELAY_LOG_INFO* rli) +{ + if (!rli->inited) + return; + if (rli->info_fd >= 0) + { + end_io_cache(&rli->info_file); + (void)my_close(rli->info_fd, MYF(MY_WME)); + rli->info_fd = -1; + } + if (rli->cur_log_fd >= 0) + { + end_io_cache(&rli->cache_buf); + (void)my_close(rli->cur_log_fd, MYF(MY_WME)); + rli->cur_log_fd = -1; + } + rli->inited = 0; + rli->log_pos_current=0; + rli->relay_log.close(1); } +/* try to connect until successful or slave killed */ +static int safe_connect(THD* thd, MYSQL* mysql, MASTER_INFO* mi) +{ + return connect_to_master(thd, mysql, mi, 0); +} + + /* Try to connect until successful or slave killed or we have retried master_retry_count times */ -static int safe_reconnect(THD* thd, MYSQL* mysql, MASTER_INFO* mi) +static int connect_to_master(THD* thd, MYSQL* mysql, MASTER_INFO* mi, + bool reconnect) { int slave_was_killed; int last_errno= -2; // impossible error ulong err_count=0; char llbuff[22]; - /* - If we lost connection after reading a state set event - we will be re-reading it, so pending needs to be cleared - */ - mi->pending = 0; #ifndef DBUG_OFF events_till_disconnect = disconnect_slave_event_count; #endif - while (!(slave_was_killed = slave_killed(thd)) && mc_mysql_reconnect(mysql)) + while (!(slave_was_killed = io_slave_killed(thd,mi)) && + (reconnect ? mc_mysql_reconnect(mysql) != 0 : + !mc_mysql_connect(mysql, mi->host, mi->user, mi->password, 0, + mi->port, 0, 0))) { /* Don't repeat last error */ if (mc_mysql_errno(mysql) != last_errno) { - sql_print_error("Slave thread: error re-connecting to master: \ -%s, last_errno=%d, retry in %d sec", + sql_print_error("Slave I/O thread: error connecting to master \ +'%s@%s:%d': \ +%s, last_errno=%d, retry in %d sec",mi->user,mi->host,mi->port, mc_mysql_error(mysql), last_errno=mc_mysql_errno(mysql), mi->connect_retry); } - safe_sleep(thd, mi->connect_retry); - /* if master_retry_count is not set, keep trying until success */ + safe_sleep(thd,mi->connect_retry,(CHECK_KILLED_FUNC)io_slave_killed, + (void*)mi); + /* + By default we try forever. The reason is that failure will trigger + master election, so if the user did not set master_retry_count we + do not want to have electioin triggered on the first failure to + connect + */ if (master_retry_count && err_count++ == master_retry_count) { slave_was_killed=1; + if (reconnect) + change_rpl_status(RPL_ACTIVE_SLAVE,RPL_LOST_SOLDIER); break; } } if (!slave_was_killed) { - sql_print_error("Slave: reconnected to master '%s@%s:%d',\ -replication resumed in log '%s' at position %s", glob_mi.user, - glob_mi.host, glob_mi.port, - RPL_LOG_NAME, - llstr(glob_mi.pos,llbuff)); + if (reconnect) + sql_print_error("Slave: connected to master '%s@%s:%d',\ +replication resumed in log '%s' at position %s", mi->user, + mi->host, mi->port, + IO_RPL_LOG_NAME, + llstr(mi->master_log_pos,llbuff)); + else + { + change_rpl_status(RPL_IDLE_SLAVE,RPL_ACTIVE_SLAVE); + mysql_log.write(thd, COM_CONNECT_OUT, "%s@%s:%d", + mi->user, mi->host, mi->port); + } #ifdef SIGNAL_WITH_VIO_CLOSE thd->set_active_vio(mysql->net.vio); #endif @@ -1539,6 +2401,243 @@ replication resumed in log '%s' at position %s", glob_mi.user, return slave_was_killed; } + +/* + Try to connect until successful or slave killed or we have retried + master_retry_count times +*/ + +static int safe_reconnect(THD* thd, MYSQL* mysql, MASTER_INFO* mi) +{ + return connect_to_master(thd, mysql, mi, 1); +} + +int flush_relay_log_info(RELAY_LOG_INFO* rli) +{ + register IO_CACHE* file = &rli->info_file; + char lbuf[22],lbuf1[22]; + + my_b_seek(file, 0L); + my_b_printf(file, "%s\n%s\n%s\n%s\n", + rli->relay_log_name, llstr(rli->relay_log_pos, lbuf), + rli->master_log_name, llstr(rli->master_log_pos, lbuf1) + ); + flush_io_cache(file); + flush_io_cache(rli->cur_log); + return 0; +} + +IO_CACHE* reopen_relay_log(RELAY_LOG_INFO* rli, const char** errmsg) +{ + DBUG_ASSERT(rli->cur_log != &rli->cache_buf); + IO_CACHE* cur_log = rli->cur_log=&rli->cache_buf; + DBUG_ASSERT(rli->cur_log_fd == -1); + if ((rli->cur_log_fd=open_binlog(cur_log,rli->relay_log_name, + errmsg)) <0) + return 0; + my_b_seek(cur_log,rli->relay_log_pos); + return cur_log; +} + +Log_event* next_event(RELAY_LOG_INFO* rli) +{ + Log_event* ev; + IO_CACHE* cur_log = rli->cur_log; + pthread_mutex_t *log_lock = rli->relay_log.get_log_lock(); + const char* errmsg=0; + THD* thd = rli->sql_thd; + bool was_killed; + DBUG_ASSERT(thd != 0); + + /* + For most operations we need to protect rli members with data_lock, + so we will hold it for the most of the loop below + However, we will release it whenever it is worth the hassle, + and in the cases when we go into a pthread_cond_wait() with the + non-data_lock mutex + */ + pthread_mutex_lock(&rli->data_lock); + + for (; !(was_killed=sql_slave_killed(thd,rli)) ;) + { + /* + We can have two kinds of log reading: + hot_log - rli->cur_log points at the IO_CACHE of relay_log, which + is actively being updated by the I/O thread. We need to be careful + in this case and make sure that we are not looking at a stale log that + has already been rotated. If it has been, we reopen the log + the other case is much simpler - we just have a read only log that + nobody else will be updating. + */ + bool hot_log; + if ((hot_log = (cur_log != &rli->cache_buf))) + { + DBUG_ASSERT(rli->cur_log_fd == -1); // foreign descriptor + pthread_mutex_lock(log_lock); + + /* + Reading cur_log->init_count here is safe because the log will only + be rotated when we hold relay_log.LOCK_log + */ + if (cur_log->init_count != rli->cur_log_init_count) + { + if (!(cur_log=reopen_relay_log(rli,&errmsg))) + { + pthread_mutex_unlock(log_lock); + goto err; + } + pthread_mutex_unlock(log_lock); + hot_log=0; + } + } + DBUG_ASSERT(my_b_tell(cur_log) >= 4); + DBUG_ASSERT(my_b_tell(cur_log) == rli->relay_log_pos + rli->pending); + /* relay log is always in new format - if the master is 3.23, the + I/O thread will convert the format for us + */ + if ((ev=Log_event::read_log_event(cur_log,0,(bool)0/*new format*/))) + { + DBUG_ASSERT(thd==rli->sql_thd); + if (hot_log) + pthread_mutex_unlock(log_lock); + pthread_mutex_unlock(&rli->data_lock); + return ev; + } + DBUG_ASSERT(thd==rli->sql_thd); + if (opt_reckless_slave) + cur_log->error = 0; + if ( cur_log->error < 0) + { + errmsg = "slave SQL thread aborted because of I/O error"; + goto err; + } + + + if (!cur_log->error) /* EOF */ + { + /* + On a hot log, EOF means that there are no more updates to + process and we must block until I/O thread adds some and + signals us to continue + */ + if (hot_log) + { + DBUG_ASSERT(cur_log->init_count == rli->cur_log_init_count); + /* + We can, and should release data_lock while we are waiting for + update. If we do not, show slave status will block + */ + pthread_mutex_unlock(&rli->data_lock); + + /* + IMPORTANT: note that wait_for_update will unlock LOCK_log, but + expects the caller to lock it + */ + rli->relay_log.wait_for_update(rli->sql_thd); + + // re-acquire data lock since we released it earlier + pthread_mutex_lock(&rli->data_lock); + continue; + } + else + { + /* + If the log was not hot, we need to move to the next log in + sequence. The next log could be hot or cold, we deal with both + cases separately after doing some common initialization + */ + end_io_cache(cur_log); + DBUG_ASSERT(rli->cur_log_fd >= 0); + my_close(rli->cur_log_fd, MYF(MY_WME)); + rli->cur_log_fd = -1; + + // TODO: make skip_log_purge a start-up option. At this point this + // is not critical priority + if (!rli->skip_log_purge) + { + // purge_first_log will properly set up relay log coordinates in rli + if (rli->relay_log.purge_first_log(rli)) + { + errmsg = "Error purging processed log"; + goto err; + } + } + else + { + // TODO: verify that no lock is ok here. At this point, if we + // get this wrong, this is actually no big deal - the only time + // this code will ever be executed is if we are recovering from + // a bug when a full reload of the slave is not feasible or + // desirable. + if (rli->relay_log.find_next_log(&rli->linfo,0/*no lock*/)) + { + errmsg = "error switching to the next log"; + goto err; + } + rli->relay_log_pos = 4; + rli->pending=0; + strnmov(rli->relay_log_name,rli->linfo.log_file_name, + sizeof(rli->relay_log_name)); + flush_relay_log_info(rli); + } + + // next log is hot + if (rli->relay_log.is_active(rli->linfo.log_file_name)) + { +#ifdef EXTRA_DEBUG + sql_print_error("next log '%s' is currently active", + rli->linfo.log_file_name); +#endif + rli->cur_log = cur_log = rli->relay_log.get_log_file(); + rli->cur_log_init_count = cur_log->init_count; + DBUG_ASSERT(rli->cur_log_fd == -1); + + /* + Read pointer has to be at the start since we are the only + reader + */ + if (check_binlog_magic(cur_log,&errmsg)) + goto err; + continue; + } + /* + if we get here, the log was not hot, so we will have to + open it ourselves + */ +#ifdef EXTRA_DEBUG + sql_print_error("next log '%s' is not active", + rli->linfo.log_file_name); +#endif + // open_binlog() will check the magic header + if ((rli->cur_log_fd=open_binlog(cur_log,rli->linfo.log_file_name, + &errmsg)) <0) + goto err; + } + } + else // read failed with a non-EOF error + { + // TODO: come up with something better to handle this error + sql_print_error("Slave SQL thread: I/O error reading \ +event(errno=%d,cur_log->error=%d)", + my_errno,cur_log->error); + // set read position to the beginning of the event + my_b_seek(cur_log,rli->relay_log_pos+rli->pending); + /* otherwise, we have had a partial read */ + /* TODO; see if there is a way to do this without this goto */ + errmsg = "Aborting slave SQL thread because of partial event read"; + goto err; + } + + } + if (!errmsg && was_killed) + errmsg = "slave SQL thread was killed"; +err: + pthread_mutex_unlock(&rli->data_lock); + sql_print_error("Error reading relay log event: %s", errmsg); + return 0; +} + + #ifdef __GNUC__ template class I_List_iterator<i_string>; template class I_List_iterator<i_string_pair>; diff --git a/sql/slave.h b/sql/slave.h index 2934e675d56..34df17f2851 100644 --- a/sql/slave.h +++ b/sql/slave.h @@ -1,65 +1,311 @@ #ifndef SLAVE_H #define SLAVE_H +#include "mysql.h" +#include "my_list.h" #define SLAVE_NET_TIMEOUT 3600 +#define MAX_SLAVE_ERRMSG 1024 #define MAX_SLAVE_ERROR 2000 +/* + The replication is accomplished by starting two threads - I/O + thread, and SQL thread. I/O thread is associated with its + MASTER_INFO struct, so MASTER_INFO can be viewed as I/O thread + descriptor. SQL thread is associated with RELAY_LOG_INFO struct. + + I/O thread reads maintains a connection to the master, and reads log + events from the master as they arrive, queueing them by writing them + out into the temporary slave binary log (relay log). The SQL thread, + in turn, reads the slave binary log executing each event. + + Relay log is needed to be able to handle situations when there is a large + backlog of unprocessed events from the master (eg. one particular update + takes a day to finish), and to be able to restart the slave server without + having to re-read the master updates. + */ + extern ulong slave_net_timeout, master_retry_count; extern MY_BITMAP slave_error_mask; extern bool use_slave_mask; +extern char* slave_load_tmpdir; +extern my_string master_info_file,relay_log_info_file; +extern my_string opt_relay_logname, opt_relaylog_index_name; +extern bool opt_skip_slave_start, opt_reckless_slave; +extern ulong relay_log_space_limit; +struct st_master_info; + +/* + TODO: this needs to be redone, but for now it does not matter since + we do not have multi-master yet. +*/ + +#define LOCK_ACTIVE_MI { pthread_mutex_lock(&LOCK_active_mi); \ + ++active_mi_in_use; \ + pthread_mutex_unlock(&LOCK_active_mi);} + +#define UNLOCK_ACTIVE_MI { pthread_mutex_lock(&LOCK_active_mi); \ + --active_mi_in_use; \ + pthread_mutex_unlock(&LOCK_active_mi); } + +/* + st_relay_log_info contains information on the current relay log and + relay log offset, and master log name and log sequence corresponding to the + last update. Additionally, misc information specific to the SQL thread is + included. + + st_relay_log_info is initialized from the slave.info file if such exists. + Otherwise, data members are intialized with defaults. The initialization is + done with init_relay_log_info() call. + + The format of slave.info file: + + relay_log_name + relay_log_pos + master_log_name + master_log_pos + + To clean up, call end_relay_log_info() + */ + +typedef struct st_relay_log_info +{ + /*** The following variables can only be read when protect by data lock ****/ + /* + info_fd - file descriptor of the info file. set only during + initialization or clean up - safe to read anytime + cur_log_fd - file descriptor of the current read relay log + */ + File info_fd,cur_log_fd; + // name of current read relay log + char relay_log_name[FN_REFLEN]; + // master log name corresponding to current read position + char master_log_name[FN_REFLEN]; + // original log position of last processed event + volatile my_off_t master_log_pos; + + /* + current offset in the relay log. + pending - in some cases we do not increment offset immediately after + processing an event, because the following event needs to be processed + atomically together with this one ( so far, there is only one type of + such event - Intvar_event that sets auto_increment value). However, once + both events have been processed, we need to increment by the cumulative + offset. pending stored the extra offset to be added to the position. + */ + ulonglong relay_log_pos, pending; + + // protected with internal locks + // must get data_lock when resetting the logs + MYSQL_LOG relay_log; + LOG_INFO linfo; + IO_CACHE cache_buf,*cur_log; + + /*** The following variables are safe to read any time ***/ + + // IO_CACHE of the info file - set only during init or end + IO_CACHE info_file; + + /* + When we restart slave thread we need to have access to the previously + created temporary tables. Modified only on init/end and by the SQL + thread, read only by SQL thread. + */ + TABLE* save_temporary_tables; + + /* + standard lock acquistion order to avoid deadlocks: + run_lock, data_lock, relay_log.LOCK_log, relay_log.LOCK_index + */ + pthread_mutex_t data_lock,run_lock; + + /* + start_cond is broadcast when SQL thread is started + stop_cond - when stopped + data_cond - when data protected by data_lock changes + */ + pthread_cond_t start_cond, stop_cond, data_cond; + + // if not set, the value of other members of the structure are undefined + bool inited; + + // parent master info structure + struct st_master_info *mi; + + /* + Needed to deal properly with cur_log getting closed and re-opened with + a different log under our feet + */ + int cur_log_init_count; + + volatile bool abort_slave, slave_running; + + /* + Needed for problems when slave stops and we want to restart it + skipping one or more events in the master log that have caused + errors, and have been manually applied by DBA already. + */ + volatile uint32 slave_skip_counter; +#ifndef DBUG_OFF + int events_till_abort; +#endif + int last_slave_errno; + char last_slave_error[MAX_SLAVE_ERRMSG]; + THD* sql_thd; + bool log_pos_current; + bool abort_pos_wait; + bool skip_log_purge; + ulonglong log_space_limit,log_space_total; + pthread_mutex_t log_space_lock; + pthread_cond_t log_space_cond; + + st_relay_log_info():info_fd(-1),cur_log_fd(-1),inited(0), + cur_log_init_count(0), + abort_slave(0),slave_running(0), + log_pos_current(0),abort_pos_wait(0), + skip_log_purge(0) + { + relay_log_name[0] = master_log_name[0] = 0; + bzero(&info_file,sizeof(info_file)); + pthread_mutex_init(&run_lock, MY_MUTEX_INIT_FAST); + pthread_mutex_init(&data_lock, MY_MUTEX_INIT_FAST); + pthread_mutex_init(&log_space_lock, MY_MUTEX_INIT_FAST); + pthread_cond_init(&data_cond, NULL); + pthread_cond_init(&start_cond, NULL); + pthread_cond_init(&stop_cond, NULL); + pthread_cond_init(&log_space_cond, NULL); + } + ~st_relay_log_info() + { + pthread_mutex_destroy(&run_lock); + pthread_mutex_destroy(&data_lock); + pthread_mutex_destroy(&log_space_lock); + pthread_cond_destroy(&data_cond); + pthread_cond_destroy(&start_cond); + pthread_cond_destroy(&stop_cond); + pthread_cond_destroy(&log_space_cond); + } + inline void inc_pending(ulonglong val) + { + pending += val; + } + // TODO: this probably needs to be fixed + inline void inc_pos(ulonglong val, ulonglong log_pos, bool skip_lock=0) + { + if (!skip_lock) + pthread_mutex_lock(&data_lock); + relay_log_pos += val+pending; + pending = 0; + if (log_pos) + master_log_pos = log_pos+ val; + pthread_cond_broadcast(&data_cond); + if (!skip_lock) + pthread_mutex_unlock(&data_lock); + } + /* + thread safe read of position - not needed if we are in the slave thread, + but required otherwise + */ + inline void read_pos(ulonglong& var) + { + pthread_mutex_lock(&data_lock); + var = relay_log_pos; + pthread_mutex_unlock(&data_lock); + } + int wait_for_pos(THD* thd, String* log_name, ulonglong log_pos); +} RELAY_LOG_INFO; + +/* + repopen_relay_log() is called when we notice that the current "hot" log + got rotated under our feet +*/ + +IO_CACHE* reopen_relay_log(RELAY_LOG_INFO* rli, const char** errmsg); +Log_event* next_event(RELAY_LOG_INFO* rli); + + +/* + st_master_info contains information about how to connect to a master, + current master log name, and current log offset, as well as misc + control variables + + st_master_info is initialized once from the master.info file if such + exists. Otherwise, data members corresponding to master.info fields are + initialized with defaults specified by master-* options. The initialization + is done through init_master_info() call. + + The format of master.info file: + + log_name + log_pos + master_host + master_user + master_pass + master_port + master_connect_retry + + To write out the contents of master.info file to disk ( needed every + time we read and queue data from the master ), a call to + flush_master_info() is required. + + To clean up, call end_master_info() +*/ + + typedef struct st_master_info { - char log_file_name[FN_REFLEN]; - ulonglong pos,pending; - File fd; // we keep the file open, so we need to remember the file pointer + char master_log_name[FN_REFLEN]; + + my_off_t master_log_pos; + File fd; IO_CACHE file; + // the variables below are needed because we can change masters on the fly char host[HOSTNAME_LENGTH+1]; char user[USERNAME_LENGTH+1]; char password[HASH_PASSWORD_LENGTH+1]; + pthread_mutex_t data_lock,run_lock; + pthread_cond_t data_cond,start_cond,stop_cond; + THD *io_thd; + MYSQL* mysql; + uint32 file_id; // for 3.23 load data infile + RELAY_LOG_INFO rli; uint port; uint connect_retry; - pthread_mutex_t lock; - pthread_cond_t cond; +#ifndef DBUG_OFF + int events_till_abort; +#endif bool inited; + bool old_format; // master binlog is in 3.23 format + volatile bool abort_slave, slave_running; + bool ignore_stop_event; - st_master_info():pending(0),fd(-1),inited(0) + + st_master_info():fd(-1), io_thd(0), inited(0), old_format(0),abort_slave(0), + slave_running(0) { host[0] = 0; user[0] = 0; password[0] = 0; - pthread_mutex_init(&lock, MY_MUTEX_INIT_FAST); - pthread_cond_init(&cond, NULL); + bzero(&file,sizeof(file)); + pthread_mutex_init(&run_lock, MY_MUTEX_INIT_FAST); + pthread_mutex_init(&data_lock, MY_MUTEX_INIT_FAST); + pthread_cond_init(&data_cond, NULL); + pthread_cond_init(&start_cond, NULL); + pthread_cond_init(&stop_cond, NULL); } ~st_master_info() { - pthread_mutex_destroy(&lock); - pthread_cond_destroy(&cond); - } - inline void inc_pending(ulonglong val) - { - pending += val; - } - inline void inc_pos(ulonglong val) - { - pthread_mutex_lock(&lock); - pos += val + pending; - pending = 0; - pthread_cond_broadcast(&cond); - pthread_mutex_unlock(&lock); - } - // thread safe read of position - not needed if we are in the slave thread, - // but required otherwise - inline void read_pos(ulonglong& var) - { - pthread_mutex_lock(&lock); - var = pos; - pthread_mutex_unlock(&lock); + pthread_mutex_destroy(&run_lock); + pthread_mutex_destroy(&data_lock); + pthread_cond_destroy(&data_cond); + pthread_cond_destroy(&start_cond); + pthread_cond_destroy(&stop_cond); } - int wait_for_pos(THD* thd, String* log_name, ulonglong log_pos); } MASTER_INFO; +int queue_event(MASTER_INFO* mi,const char* buf,ulong event_len); + typedef struct st_table_rule_ent { char* db; @@ -69,45 +315,100 @@ typedef struct st_table_rule_ent #define TABLE_RULE_HASH_SIZE 16 #define TABLE_RULE_ARR_SIZE 16 +#define MAX_SLAVE_ERRMSG 1024 +#define RPL_LOG_NAME (rli->master_log_name[0] ? rli->master_log_name :\ + "FIRST") +#define IO_RPL_LOG_NAME (mi->master_log_name[0] ? mi->master_log_name :\ + "FIRST") + +/* masks for start/stop operations on io and sql slave threads */ +#define SLAVE_IO 1 +#define SLAVE_SQL 2 + +/* + If the following is set, if first gives an error, second will be + tried. Otherwise, if first fails, we fail. +*/ +#define SLAVE_FORCE_ALL 4 + +int init_slave(); +void init_slave_skip_errors(const char* arg); int flush_master_info(MASTER_INFO* mi); +int flush_relay_log_info(RELAY_LOG_INFO* rli); +int register_slave_on_master(MYSQL* mysql); +int terminate_slave_threads(MASTER_INFO* mi, int thread_mask, + bool skip_lock = 0); +int terminate_slave_thread(THD* thd, pthread_mutex_t* term_mutex, + pthread_mutex_t* cond_lock, + pthread_cond_t* term_cond, + volatile bool* slave_running); +int start_slave_threads(bool need_slave_mutex, bool wait_for_start, + MASTER_INFO* mi, const char* master_info_fname, + const char* slave_info_fname, int thread_mask); +/* + cond_lock is usually same as start_lock. It is needed for the case when + start_lock is 0 which happens if start_slave_thread() is called already + inside the start_lock section, but at the same time we want a + pthread_cond_wait() on start_cond,start_lock +*/ +int start_slave_thread(pthread_handler h_func, pthread_mutex_t* start_lock, + pthread_mutex_t *cond_lock, + pthread_cond_t* start_cond, + volatile bool* slave_running, + MASTER_INFO* mi); + +// If fd is -1, dump to NET +int mysql_table_dump(THD* thd, const char* db, + const char* tbl_name, int fd = -1); -int mysql_table_dump(THD* thd, char* db, char* tbl_name, int fd = -1); -// if fd is -1, dump to NET -int fetch_nx_table(THD* thd, MASTER_INFO* mi); // 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 fetch_master_table(THD* thd, const char* db_name, const char* table_name, + MASTER_INFO* mi, MYSQL* mysql); + +int show_master_info(THD* thd, MASTER_INFO* mi); int show_binlog_info(THD* thd); +// See if the query uses any tables that should not be replicated int tables_ok(THD* thd, TABLE_LIST* tables); -// see if the query uses any tables that should not be replicated +/* + Check to see if the database is ok to operate on with respect to the + do and ignore lists - used in replication +*/ int db_ok(const char* db, I_List<i_string> &do_list, I_List<i_string> &ignore_list ); -// check to see if the database is ok to operate on with respect to the -// do and ignore lists - used in replication int add_table_rule(HASH* h, const char* table_spec); int add_wild_table_rule(DYNAMIC_ARRAY* a, const char* table_spec); void init_table_rule_hash(HASH* h, bool* h_inited); void init_table_rule_array(DYNAMIC_ARRAY* a, bool* a_inited); -void init_slave_skip_errors(char* arg); +char* rewrite_db(char* db); +int check_expected_error(THD* thd, RELAY_LOG_INFO* rli, int error_code); +void skip_load_data_infile(NET* net); +void slave_print_error(RELAY_LOG_INFO* rli,int err_code, const char* msg, ...); void end_slave(); // clean up -int init_master_info(MASTER_INFO* mi); +int init_master_info(MASTER_INFO* mi, const char* master_info_fname, + const char* slave_info_fname); void end_master_info(MASTER_INFO* mi); +int init_relay_log_info(RELAY_LOG_INFO* rli, const char* info_fname); +void end_relay_log_info(RELAY_LOG_INFO* rli); +void lock_slave_threads(MASTER_INFO* mi); +void unlock_slave_threads(MASTER_INFO* mi); +void init_thread_mask(int* mask,MASTER_INFO* mi,bool inverse); +int init_relay_log_pos(RELAY_LOG_INFO* rli,const char* log,ulonglong pos, + bool need_data_lock, const char** errmsg); + +int purge_relay_logs(RELAY_LOG_INFO* rli,bool just_reset,const char** errmsg); + extern bool opt_log_slave_updates ; -pthread_handler_decl(handle_slave,arg); -extern bool volatile abort_loop, abort_slave, slave_running; -extern uint32 slave_skip_counter; -// needed for problems when slave stops and -// we want to restart it skipping one or more events in the master log that -// have caused errors, and have been manually applied by DBA already - -extern pthread_t slave_real_id; -extern THD* slave_thd; -extern MASTER_INFO glob_mi; +pthread_handler_decl(handle_slave_io,arg); +pthread_handler_decl(handle_slave_sql,arg); +extern bool volatile abort_loop; +extern MASTER_INFO main_mi, *active_mi; // active_mi for multi-master +extern volatile int active_mi_in_use; +extern LIST master_list; extern HASH replicate_do_table, replicate_ignore_table; extern DYNAMIC_ARRAY replicate_wild_do_table, replicate_wild_ignore_table; extern bool do_table_inited, ignore_table_inited, @@ -119,14 +420,13 @@ 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, relay_log_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; extern I_List<THD> threads; #endif - - diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index 446076e0d55..0b2d4ad76e8 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -1,15 +1,15 @@ -/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB - +/* 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 */ @@ -59,8 +59,13 @@ class ACL_USER :public ACL_ACCESS public: acl_host_and_ip host; uint hostname_length; + USER_RESOURCES user_resource; char *user,*password; ulong salt[2]; +#ifdef HAVE_OPENSSL + enum SSL_type ssl_type; + const char *ssl_cipher, *x509_issuer, *x509_subject; +#endif /* HAVE_OPENSSL */ }; class ACL_DB :public ACL_ACCESS @@ -106,7 +111,33 @@ static void update_hostname(acl_host_and_ip *host, const char *hostname); static bool compare_hostname(const acl_host_and_ip *host, const char *hostname, const char *ip); -int acl_init(bool dont_read_acl_tables) +extern char uc_update_queries[SQLCOM_END]; + +static void init_update_queries(void) +{ + uc_update_queries[SQLCOM_CREATE_TABLE]=1; + uc_update_queries[SQLCOM_CREATE_INDEX]=1; + uc_update_queries[SQLCOM_ALTER_TABLE]=1; + uc_update_queries[SQLCOM_UPDATE]=1; + uc_update_queries[SQLCOM_INSERT]=1; + uc_update_queries[SQLCOM_INSERT_SELECT]=1; + uc_update_queries[SQLCOM_DELETE]=1; + uc_update_queries[SQLCOM_TRUNCATE]=1; + uc_update_queries[SQLCOM_DROP_TABLE]=1; + uc_update_queries[SQLCOM_LOAD]=1; + uc_update_queries[SQLCOM_CREATE_DB]=1; + uc_update_queries[SQLCOM_DROP_DB]=1; + uc_update_queries[SQLCOM_REPLACE]=1; + uc_update_queries[SQLCOM_REPLACE_SELECT]=1; + uc_update_queries[SQLCOM_RENAME_TABLE]=1; + uc_update_queries[SQLCOM_BACKUP_TABLE]=1; + uc_update_queries[SQLCOM_RESTORE_TABLE]=1; + uc_update_queries[SQLCOM_DELETE_MULTI]=1; + uc_update_queries[SQLCOM_DROP_INDEX]=1; + uc_update_queries[SQLCOM_MULTI_UPDATE]=1; +} + +int acl_init(bool dont_read_acl_tables) { THD *thd; TABLE_LIST tables[3]; @@ -128,7 +159,8 @@ int acl_init(bool dont_read_acl_tables) thd->mysys_var=my_thread_var; thd->current_tablenr=0; thd->open_tables=0; - thd->db=my_strdup("mysql",MYF(0)); + thd->db= my_strdup("mysql",MYF(0)); + thd->db_length=5; // Safety bzero((char*) &tables,sizeof(tables)); tables[0].name=tables[0].real_name=(char*) "host"; tables[1].name=tables[1].real_name=(char*) "user"; @@ -199,6 +231,26 @@ int acl_init(bool dont_read_acl_tables) update_hostname(&user.host,get_field(&mem, table,0)); user.user=get_field(&mem, table,1); user.password=get_field(&mem, table,2); +#ifdef HAVE_OPENSSL + DBUG_PRINT("info",("table->fields=%d",table->fields)); + if (table->fields >= 21) /* From 4.0.0 we have more fields */ + { + char *ssl_type=get_field(&mem, table,17); + if (!strcmp(ssl_type, "ANY")) + user.ssl_type=SSL_TYPE_ANY; + else if (!strcmp(ssl_type, "X509")) + user.ssl_type=SSL_TYPE_X509; + else if (!strcmp(ssl_type, "SPECIFIED")) + user.ssl_type=SSL_TYPE_SPECIFIED; + else + user.ssl_type=SSL_TYPE_NONE; + user.ssl_cipher=get_field(&mem, table, 18); + user.x509_issuer=get_field(&mem, table, 19); + user.x509_subject=get_field(&mem, table, 20); + } + else + user.ssl_type=SSL_TYPE_NONE; +#endif /* HAVE_OPENSSL */ if (user.password && (length=(uint) strlen(user.password)) == 8 && protocol_version == PROTOCOL_VERSION) { @@ -218,6 +270,20 @@ int acl_init(bool dont_read_acl_tables) user.access=get_access(table,3); user.sort=get_sort(2,user.host.hostname,user.user); user.hostname_length=user.host.hostname ? (uint) strlen(user.host.hostname) : 0; + if (table->fields >=23) + { + /* Table has new MySQL usage limits */ + char *ptr = get_field(&mem, table, 21); + user.user_resource.questions=atoi(ptr); + ptr = get_field(&mem, table, 22); + user.user_resource.updates=atoi(ptr); + ptr = get_field(&mem, table, 23); + user.user_resource.connections=atoi(ptr); + if (user.user_resource.questions || user.user_resource.updates || user.user_resource.connections) + mqh_used=1; + } + else + bzero(&(user.user_resource),sizeof(user.user_resource)); #ifndef TO_BE_REMOVED if (table->fields <= 13) { // Without grant @@ -262,6 +328,7 @@ int acl_init(bool dont_read_acl_tables) init_check_host(); mysql_unlock_tables(thd, lock); + init_update_queries(); thd->version--; // Force close to free memory close_thread_tables(thd); delete thd; @@ -398,16 +465,20 @@ static int acl_compare(ACL_ACCESS *a,ACL_ACCESS *b) } -/* Get master privilges for user (priviliges for all tables) */ - +/* + Get master privilges for user (priviliges for all tables). + Required before connecting to MySQL +*/ -uint acl_getroot(const char *host, const char *ip, const char *user, +uint acl_getroot(THD *thd, const char *host, const char *ip, const char *user, const char *password,const char *message,char **priv_user, - bool old_ver) + bool old_ver, USER_RESOURCES *mqh) { uint user_access=NO_ACCESS; *priv_user=(char*) user; + char *ptr=0; + bzero(mqh,sizeof(USER_RESOURCES)); if (!initialized) return (uint) ~NO_ACCESS; // If no data allow anything /* purecov: tested */ VOID(pthread_mutex_lock(&acl_cache->lock)); @@ -428,7 +499,94 @@ uint acl_getroot(const char *host, const char *ip, const char *user, !check_scramble(password,message,acl_user->salt, (my_bool) old_ver))) { +#ifdef HAVE_OPENSSL + Vio *vio=thd->net.vio; + /* + In this point we know that user is allowed to connect + from given host by given username/password pair. Now + we check if SSL is required, if user is using SSL and + if X509 certificate attributes are OK + */ + switch(acl_user->ssl_type) { + case SSL_TYPE_NONE: /* SSL is not required to connect */ + user_access=acl_user->access; + break; + case SSL_TYPE_ANY: /* Any kind of SSL is good enough */ + if (vio_type(vio) == VIO_TYPE_SSL) + user_access=acl_user->access; + break; + case SSL_TYPE_X509: /* Client should have any valid certificate. */ + /* + Connections with non-valid certificates are dropped already + in sslaccept() anyway, so we do not check validity here. + */ + if (SSL_get_peer_certificate(vio->ssl_)) + user_access=acl_user->access; + break; + case SSL_TYPE_SPECIFIED: /* Client should have specified attrib */ + /* + We do not check for absence of SSL because without SSL it does + not pass all checks here anyway. + If cipher name is specified, we compare it to actual cipher in + use. + */ + if (acl_user->ssl_cipher) + DBUG_PRINT("info",("comparing ciphers: '%s' and '%s'", + acl_user->ssl_cipher, + SSL_get_cipher(vio->ssl_))); + if (!strcmp(acl_user->ssl_cipher,SSL_get_cipher(vio->ssl_))) + user_access=acl_user->access; + else + { + user_access=NO_ACCESS; + break; + } + /* Prepare certificate (if exists) */ + DBUG_PRINT("info",("checkpoint 1")); + X509* cert=SSL_get_peer_certificate(vio->ssl_); + DBUG_PRINT("info",("checkpoint 2")); + /* If X509 issuer is speified, we check it... */ + if (acl_user->x509_issuer) + { + DBUG_PRINT("info",("checkpoint 3")); + ptr = X509_NAME_oneline(X509_get_issuer_name(cert), 0, 0); + DBUG_PRINT("info",("comparing issuers: '%s' and '%s'", + acl_user->x509_issuer, ptr)); + if (!strcmp(acl_user->x509_issuer,ptr)) + user_access=acl_user->access; + else + { + user_access=NO_ACCESS; + free(ptr); + break; + } + free(ptr); + } + DBUG_PRINT("info",("checkpoint 4")); + /* X509 subject is specified, we check it .. */ + if (acl_user->x509_subject) + { + ptr = X509_NAME_oneline(X509_get_subject_name(cert), 0, 0); + DBUG_PRINT("info",("comparing subjects: '%s' and '%s'", + acl_user->x509_subject, ptr)); + if (!strcmp(acl_user->x509_subject,ptr)) + user_access=acl_user->access; + else + { + user_access=NO_ACCESS; + free(ptr); + break; + } + free(ptr); + } + DBUG_PRINT("info",("checkpoint 5")); + break; + } + DBUG_PRINT("info",("checkpoint 6")); +#else /* HAVE_OPENSSL */ user_access=acl_user->access; +#endif /* HAVE_OPENSSL */ + *mqh=acl_user->user_resource; if (!acl_user->user) *priv_user=(char*) ""; // Change to anonymous user /* purecov: inspected */ break; @@ -457,7 +615,13 @@ static byte* check_get_key(ACL_USER *buff,uint *length, } static void acl_update_user(const char *user, const char *host, - const char *password, uint privileges) + const char *password, + enum SSL_type ssl_type, + const char *ssl_cipher, + const char *x509_issuer, + const char *x509_subject, + USER_RESOURCES *mqh, + uint privileges) { for (uint i=0 ; i < acl_users.elements ; i++) { @@ -470,6 +634,13 @@ static void acl_update_user(const char *user, const char *host, acl_user->host.hostname && !strcmp(host,acl_user->host.hostname)) { acl_user->access=privileges; + acl_user->user_resource=*mqh; +#ifdef HAVE_OPENSSL + acl_user->ssl_type=ssl_type; + acl_user->ssl_cipher=ssl_cipher; + acl_user->x509_issuer=x509_issuer; + acl_user->x509_subject=x509_subject; +#endif /* HAVE_OPENSSL */ if (password) { if (!password[0]) @@ -488,7 +659,12 @@ static void acl_update_user(const char *user, const char *host, static void acl_insert_user(const char *user, const char *host, - const char *password, + const char *password, + enum SSL_type ssl_type, + const char *ssl_cipher, + const char *x509_issuer, + const char *x509_subject, + USER_RESOURCES *mqh, uint privileges) { ACL_USER acl_user; @@ -496,8 +672,15 @@ static void acl_insert_user(const char *user, const char *host, update_hostname(&acl_user.host,strdup_root(&mem,host)); acl_user.password=0; acl_user.access=privileges; + acl_user.user_resource = *mqh; acl_user.sort=get_sort(2,acl_user.host.hostname,acl_user.user); acl_user.hostname_length=(uint) strlen(acl_user.host.hostname); +#ifdef HAVE_OPENSSL + acl_user.ssl_type=ssl_type; + acl_user.ssl_cipher=ssl_cipher; + acl_user.x509_issuer=x509_issuer; + acl_user.x509_subject=x509_subject; +#endif /* HAVE_OPENSSL */ if (password) { acl_user.password=(char*) ""; // Just point at something @@ -570,12 +753,17 @@ uint acl_get(const char *host, const char *ip, const char *bin_ip, { uint host_access,db_access,i,key_length; db_access=0; host_access= ~0; - char key[ACL_KEY_LENGTH],*end; + char key[ACL_KEY_LENGTH],*tmp_db,*end; acl_entry *entry; VOID(pthread_mutex_lock(&acl_cache->lock)); memcpy_fixed(&key,bin_ip,sizeof(struct in_addr)); - end=strmov(strmov(key+sizeof(struct in_addr),user)+1,db); + end=strmov((tmp_db=strmov(key+sizeof(struct in_addr),user)+1),db); + if (lower_case_table_names) + { + casedn_str(tmp_db); + db=tmp_db; + } key_length=(uint) (end-key); if ((entry=(acl_entry*) acl_cache->search(key,key_length))) { @@ -641,7 +829,7 @@ int wild_case_compare(const char *str,const char *wildstr) { reg3 int flag; DBUG_ENTER("wild_case_compare"); - + DBUG_PRINT("enter",("str='%s', wildstr='%s'",str,wildstr)); while (*wildstr) { while (*wildstr && *wildstr != wild_many && *wildstr != wild_one) @@ -653,7 +841,7 @@ int wild_case_compare(const char *str,const char *wildstr) if (! *wildstr ) DBUG_RETURN (*str != 0); if (*wildstr++ == wild_one) { - if (! *str++) DBUG_RETURN (1); /* One char; skipp */ + if (! *str++) DBUG_RETURN (1); /* One char; skip */ } else { /* Found '*' */ @@ -765,10 +953,13 @@ bool change_password(THD *thd, const char *host, const char *user, char *new_password) { uint length=0; + DBUG_ENTER("change_password"); + DBUG_PRINT("enter",("thd=%x, host='%s', user='%s', new_password='%s'",thd,host,user,new_password)); + if (!initialized) { send_error(&thd->net, ER_PASSWORD_NOT_ALLOWED); /* purecov: inspected */ - return 1; /* purecov: inspected */ + DBUG_RETURN(1); /* purecov: inspected */ } if (!host) host=thd->ip; /* purecov: tested */ @@ -781,12 +972,12 @@ bool change_password(THD *thd, const char *host, const char *user, my_strcasecmp(host,thd->host ? thd->host : thd->ip))) { if (check_access(thd, UPDATE_ACL, "mysql",0,1)) - return 1; + DBUG_RETURN(1); } if (!thd->slave_thread && !thd->user[0]) { send_error(&thd->net, ER_PASSWORD_ANONYMOUS_USER); - return 1; + DBUG_RETURN(1); } VOID(pthread_mutex_lock(&acl_cache->lock)); ACL_USER *acl_user; @@ -794,7 +985,7 @@ bool change_password(THD *thd, const char *host, const char *user, { send_error(&thd->net, ER_PASSWORD_NO_MATCH); VOID(pthread_mutex_unlock(&acl_cache->lock)); - return 1; + DBUG_RETURN(1); } if (update_user_table(thd, acl_user->host.hostname ? acl_user->host.hostname : "", @@ -803,7 +994,7 @@ bool change_password(THD *thd, const char *host, const char *user, { VOID(pthread_mutex_unlock(&acl_cache->lock)); /* purecov: deadcode */ send_error(&thd->net,0); /* purecov: deadcode */ - return 1; /* purecov: deadcode */ + DBUG_RETURN(1); /* purecov: deadcode */ } get_salt_from_password(acl_user->salt,new_password); if (!new_password[0]) @@ -814,7 +1005,7 @@ bool change_password(THD *thd, const char *host, const char *user, VOID(pthread_mutex_unlock(&acl_cache->lock)); char buff[460]; - + Query_log_event qinfo(thd, buff); qinfo.q_len = my_sprintf(buff, @@ -824,7 +1015,7 @@ bool change_password(THD *thd, const char *host, const char *user, new_password)); mysql_update_log.write(thd,buff,qinfo.q_len); mysql_bin_log.write(&qinfo); - return 0; + DBUG_RETURN(0); } @@ -835,17 +1026,23 @@ bool change_password(THD *thd, const char *host, const char *user, static ACL_USER * find_acl_user(const char *host, const char *user) { + DBUG_ENTER("find_acl_user"); + DBUG_PRINT("enter",("host='%s', user='%s'",host,user)); for (uint i=0 ; i < acl_users.elements ; i++) { ACL_USER *acl_user=dynamic_element(&acl_users,i,ACL_USER*); + DBUG_PRINT("info",("strcmp('%s','%s'), compare_hostname('%s','%s'),", + user,acl_user->user,(host),(acl_user->host))); if (!acl_user->user && !user[0] || acl_user->user && !strcmp(user,acl_user->user)) { - if (compare_hostname(&acl_user->host,host,host)) - return acl_user; + if (compare_hostname(&(acl_user->host),host,host)) + { + DBUG_RETURN(acl_user); + } } } - return 0; + DBUG_RETURN(0); } /***************************************************************************** @@ -974,7 +1171,7 @@ static bool test_if_create_new_users(THD *thd) ** Handle GRANT commands ****************************************************************************/ -static int replace_user_table(TABLE *table, const LEX_USER &combo, +static int replace_user_table(THD *thd, TABLE *table, const LEX_USER &combo, uint rights, char what, bool create_user) { int error = -1; @@ -1013,7 +1210,7 @@ static int replace_user_table(TABLE *table, const LEX_USER &combo, my_printf_error(ER_NO_PERMISSION_TO_CREATE_USER, ER(ER_NO_PERMISSION_TO_CREATE_USER), MYF(0),thd->user, - thd->host ? thd->host : thd->ip ? thd->ip: ""); + thd->host_or_ip); error= -1; goto end; } @@ -1039,7 +1236,49 @@ static int replace_user_table(TABLE *table, const LEX_USER &combo, table->field[i]->store(&what,1); } rights=get_access(table,3); - +#ifdef HAVE_OPENSSL + /* We write down SSL related ACL stuff */ + DBUG_PRINT("info",("table->fields=%d",table->fields)); + if (table->fields >= 21) /* From 4.0.0 we have more fields */ + { + table->field[18]->store("",0); + table->field[19]->store("",0); + table->field[20]->store("",0); + switch (thd->lex.ssl_type) { + case SSL_TYPE_ANY: + table->field[17]->store("ANY",3); + break; + case SSL_TYPE_X509: + table->field[17]->store("X509",4); + break; + case SSL_TYPE_SPECIFIED: + table->field[17]->store("SPECIFIED",9); + if (thd->lex.ssl_cipher) + table->field[18]->store(thd->lex.ssl_cipher, + strlen(thd->lex.ssl_cipher)); + if (thd->lex.x509_issuer) + table->field[19]->store(thd->lex.x509_issuer, + strlen(thd->lex.x509_issuer)); + if (thd->lex.x509_subject) + table->field[20]->store(thd->lex.x509_subject, + strlen(thd->lex.x509_subject)); + break; + default: + table->field[17]->store("NONE",4); + } + } +#endif /* HAVE_OPENSSL */ + if (table->fields >= 23) + { + USER_RESOURCES mqh = thd->lex.mqh; + if (mqh.questions) + table->field[21]->store((longlong) mqh.questions); + if (mqh.updates) + table->field[22]->store((longlong) mqh.updates); + if (mqh.connections) + table->field[23]->store((longlong) mqh.connections); + mqh_used = mqh_used || mqh.questions || mqh.updates || mqh.connections; + } if (old_row_exists) { /* @@ -1066,16 +1305,28 @@ static int replace_user_table(TABLE *table, const LEX_USER &combo, } error=0; // Privileges granted / revoked - end: +end: if (!error) { acl_cache->clear(1); // Clear privilege cache if (!combo.password.str) password=0; // No password given on command if (old_row_exists) - acl_update_user(combo.user.str,combo.host.str,password,rights); + acl_update_user(combo.user.str,combo.host.str,password, + thd->lex.ssl_type, + thd->lex.ssl_cipher, + thd->lex.x509_issuer, + thd->lex.x509_subject, + &thd->lex.mqh, + rights); else - acl_insert_user(combo.user.str,combo.host.str,password,rights); + acl_insert_user(combo.user.str,combo.host.str,password, + thd->lex.ssl_type, + thd->lex.ssl_cipher, + thd->lex.x509_issuer, + thd->lex.x509_subject, + &thd->lex.mqh, + rights); } table->file->index_end(); DBUG_RETURN(error); @@ -1206,6 +1457,11 @@ public: db = strdup_root(&memex,d); user = strdup_root(&memex,u); tname= strdup_root(&memex,t); + if (lower_case_table_names) + { + casedn_str(db); + casedn_str(tname); + } key_length =(uint) strlen(d)+(uint) strlen(u)+(uint) strlen(t)+3; hash_key = (char*) alloc_root(&memex,key_length); strmov(strmov(strmov(hash_key,user)+1,db)+1,tname); @@ -1227,7 +1483,13 @@ public: privs = cols = 0; /* purecov: inspected */ return; /* purecov: inspected */ } - key_length = (uint) strlen(db) + (uint) strlen(user) + (uint) strlen (tname) + 3; + if (lower_case_table_names) + { + casedn_str(db); + casedn_str(tname); + } + key_length = ((uint) strlen(db) + (uint) strlen(user) + + (uint) strlen(tname) + 3); hash_key = (char*) alloc_root(&memex,key_length); strmov(strmov(strmov(hash_key,user)+1,db)+1,tname); privs = (uint) form->field[6]->val_int(); @@ -1510,8 +1772,7 @@ static int replace_table_table(THD *thd, GRANT_TABLE *grant_table, uint store_table_rights,store_col_rights; DBUG_ENTER("replace_table_table"); - strxmov(grantor,thd->user,"@",thd->host ? thd->host : thd->ip ? thd->ip :"", - NullS); + strxmov(grantor, thd->user, "@", thd->host_or_ip, NullS); // The following should always succeed as new users are created before // this function is called! @@ -1622,6 +1883,9 @@ int mysql_table_grant (THD *thd, TABLE_LIST *table_list, TABLE_LIST tables[3]; bool create_new_users=0; DBUG_ENTER("mysql_table_grant"); + DBUG_PRINT("info",("ssl_cipher=%s",thd->lex.ssl_cipher)); + DBUG_PRINT("info",("x509_issuer=%s",thd->lex.x509_issuer)); + DBUG_PRINT("info",("x509_subject=%s",thd->lex.x509_subject)); if (!initialized) { @@ -1711,9 +1975,10 @@ int mysql_table_grant (THD *thd, TABLE_LIST *table_list, continue; } /* Create user if needed */ - if (replace_user_table(tables[0].table, - *Str, - 0, + if (replace_user_table(thd, + tables[0].table, + *Str, + 0, revoke_grant ? 'N' : 'Y', create_new_users)) { @@ -1806,7 +2071,7 @@ int mysql_table_grant (THD *thd, TABLE_LIST *table_list, pthread_mutex_unlock(&LOCK_grant); if (!result) send_ok(&thd->net); - /* Tables are automaticly closed */ + /* Tables are automatically closed */ DBUG_RETURN(result); } @@ -1816,7 +2081,7 @@ int mysql_grant (THD *thd, const char *db, List <LEX_USER> &list, uint rights, { List_iterator <LEX_USER> str_list (list); LEX_USER *Str; - char what; + char what,tmp_db[NAME_LEN+1]; bool create_new_users=0; TABLE_LIST tables[2]; DBUG_ENTER("mysql_grant"); @@ -1828,6 +2093,12 @@ int mysql_grant (THD *thd, const char *db, List <LEX_USER> &list, uint rights, } what = (revoke_grant) ? 'N' : 'Y'; + if (lower_case_table_names && db) + { + strmov(tmp_db,db); + casedn_str(tmp_db); + db=tmp_db; + } /* open the mysql.user and mysql.db tables */ @@ -1867,7 +2138,8 @@ int mysql_grant (THD *thd, const char *db, List <LEX_USER> &list, uint rights, result= -1; continue; } - if ((replace_user_table(tables[0].table, + if ((replace_user_table(thd, + tables[0].table, *Str, (!db ? rights : 0), what, create_new_users))) result= -1; @@ -1923,7 +2195,8 @@ int grant_init (void) thd->mysys_var=my_thread_var; thd->current_tablenr=0; thd->open_tables=0; - thd->db=my_strdup("mysql",MYF(0)); + thd->db= my_strdup("mysql",MYF(0)); + thd->db_length=5; // Safety bzero((char*) &tables,sizeof(tables)); tables[0].name=tables[0].real_name= (char*) "tables_priv"; tables[1].name=tables[1].real_name= (char*) "columns_priv"; @@ -1959,7 +2232,7 @@ int grant_init (void) delete thd; DBUG_RETURN(0); // Empty table is ok! } - grant_option = TRUE; + grant_option= TRUE; t_table->file->index_end(); MEM_ROOT *old_root=my_pthread_getspecific_ptr(MEM_ROOT*,THR_MALLOC); @@ -2045,8 +2318,8 @@ bool check_grant(THD *thd, uint want_access, TABLE_LIST *tables, table->grant.want_privilege=0; continue; // Already checked } - const char *db = table->db ? table->db : thd->db; - GRANT_TABLE *grant_table = table_hash_search(thd->host,thd->ip,db,user, + GRANT_TABLE *grant_table = table_hash_search(thd->host,thd->ip, + table->db,user, table->real_name,0); if (!grant_table) { @@ -2100,7 +2373,7 @@ bool check_grant(THD *thd, uint want_access, TABLE_LIST *tables, net_printf(&thd->net,ER_TABLEACCESS_DENIED_ERROR, command, thd->priv_user, - thd->host ? thd->host : (thd->ip ? thd->ip : "unknown"), + thd->host_or_ip, table ? table->real_name : "unknown"); } return 1; @@ -2163,7 +2436,7 @@ bool check_grant_column (THD *thd,TABLE *table, const char *name, MYF(0), command, thd->priv_user, - thd->host ? thd->host : (thd->ip ? thd->ip : "unknown"), + thd->host_or_ip, name, table ? table->real_name : "unknown"); } @@ -2221,7 +2494,7 @@ bool check_grant_all_columns(THD *thd,uint want_access, TABLE *table) MYF(0), command, thd->priv_user, - thd->host ? thd->host : (thd->ip ? thd->ip : "unknown"), + thd->host_or_ip, field ? field->field_name : "unknown", table->real_name); return 1; @@ -2328,9 +2601,10 @@ int mysql_show_grants(THD *thd,LEX_USER *lex_user) { uint counter, want_access,index; int error = 0; + int ssl_options = 0; ACL_USER *acl_user; ACL_DB *acl_db; char buff[1024]; - DBUG_ENTER("mysql_grant"); + DBUG_ENTER("mysql_show_grants"); LINT_INIT(acl_user); if (!initialized) @@ -2421,8 +2695,64 @@ int mysql_show_grants(THD *thd,LEX_USER *lex_user) global.append(passd_buff); global.append('\''); } +#ifdef HAVE_OPENSSL + /* "show grants" SSL related stuff */ + if (acl_user->ssl_type == SSL_TYPE_ANY) + global.append(" REQUIRE SSL",12); + else if (acl_user->ssl_type==SSL_TYPE_X509) + global.append(" REQUIRE X509",13); + else if (acl_user->ssl_type==SSL_TYPE_SPECIFIED) + { + global.append(" REQUIRE ",9); + if (acl_user->x509_issuer) + { + if (ssl_options++) + global.append(" AND ",5); + global.append("ISSUER \"",8); + global.append(acl_user->x509_issuer,strlen(acl_user->x509_issuer)); + global.append("\"",1); + } + if (acl_user->x509_subject) + { + if (ssl_options++) + global.append(" AND ",5); + global.append("SUBJECT \"",9); + global.append(acl_user->x509_subject,strlen(acl_user->x509_subject)); + global.append("\"",1); + } + if (acl_user->ssl_cipher) + { + if (ssl_options++) + global.append(" AND ",5); + global.append("CIPHER \"",8); + global.append(acl_user->ssl_cipher,strlen(acl_user->ssl_cipher)); + global.append("\"",1); + } + } +#endif /* HAVE_OPENSSL */ if (want_access & GRANT_ACL) global.append(" WITH GRANT OPTION",18); + if (acl_user->user_resource.questions) + { + char buff[65], *p; // just as in int2str + global.append(" WITH MAX_QUERIES_PER_HOUR = ",29); + p=int2str(acl_user->user_resource.questions,buff,10); + global.append(buff,p-buff); + } + if (acl_user->user_resource.updates) + { + char buff[65], *p; // just as in int2str + global.append(" WITH MAX_UPDATES_PER_HOUR = ",29); + p=int2str(acl_user->user_resource.updates,buff,10); + global.append(buff,p-buff); + } + if (acl_user->user_resource.connections) + { + char buff[65], *p; // just as in int2str + global.append(" WITH MAX_CONNECTIONS_PER_HOUR = ",33); + p=int2str(acl_user->user_resource.connections,buff,10); + global.append(buff,p-buff); + } thd->packet.length(0); net_store_data(&thd->packet,global.ptr(),global.length()); if (my_net_write(&thd->net,(char*) thd->packet.ptr(), @@ -2470,9 +2800,9 @@ int mysql_show_grants(THD *thd,LEX_USER *lex_user) } } } - db.append (" ON ",4); + db.append (" ON '",5); db.append(acl_db->db); - db.append (".* TO '",7); + db.append ("'.* TO '",8); db.append(lex_user->user.str,lex_user->user.length); db.append ("'@'",3); db.append(lex_user->host.str, lex_user->host.length); @@ -2585,6 +2915,17 @@ int mysql_show_grants(THD *thd,LEX_USER *lex_user) } +void get_mqh(const char *user, const char *host, USER_CONN *uc) +{ + ACL_USER *acl_user; + if (initialized && (acl_user= find_acl_user(host,user))) + uc->user_resources= acl_user->user_resource; + else + bzero((char*) &uc->user_resources, sizeof(uc->user_resources)); +} + + + /***************************************************************************** ** Instantiate used templates *****************************************************************************/ diff --git a/sql/sql_acl.h b/sql/sql_acl.h index cf9696d51e7..9ac3bc6ed74 100644 --- a/sql/sql_acl.h +++ b/sql/sql_acl.h @@ -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 */ @@ -59,9 +59,9 @@ void acl_reload(void); void acl_free(bool end=0); uint acl_get(const char *host, const char *ip, const char *bin_ip, const char *user, const char *db); -uint acl_getroot(const char *host, const char *ip, const char *user, +uint acl_getroot(THD *thd, const char *host, const char *ip, const char *user, const char *password,const char *scramble,char **priv_user, - bool old_ver); + bool old_ver, USER_RESOURCES *max); bool acl_check_host(const char *host, const char *ip); bool change_password(THD *thd, const char *host, const char *user, char *password); @@ -82,3 +82,4 @@ bool check_grant_db(THD *thd,const char *db); uint get_table_grant(THD *thd, TABLE_LIST *table); uint get_column_grant(THD *thd, TABLE_LIST *table, Field *field); int mysql_show_grants(THD *thd, LEX_USER *user); +void get_mqh(const char *user, const char *host, USER_CONN *uc); diff --git a/sql/sql_analyse.cc b/sql/sql_analyse.cc index de367e8c052..df8a8f1fdde 100644 --- a/sql/sql_analyse.cc +++ b/sql/sql_analyse.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 */ @@ -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) @@ -96,7 +127,7 @@ proc_analyse_init(THD *thd, ORDER *param, select_result *result, pc->f_end = pc->f_info + field_list.elements; pc->fields = field_list; - List_iterator<Item> it(pc->fields); + List_iterator_fast<Item> it(pc->fields); f_info = pc->f_info; Item *item; @@ -859,7 +890,7 @@ int collect_string(String *element, int collect_real(double *element, element_count count __attribute__((unused)), TREE_INFO *info) { - char buff[255]; + char buff[MAX_FIELD_WIDTH]; String s(buff, sizeof(buff)); if (info->found) @@ -878,7 +909,7 @@ int collect_longlong(longlong *element, element_count count __attribute__((unused)), TREE_INFO *info) { - char buff[255]; + char buff[MAX_FIELD_WIDTH]; String s(buff, sizeof(buff)); if (info->found) @@ -897,7 +928,7 @@ int collect_ulonglong(ulonglong *element, element_count count __attribute__((unused)), TREE_INFO *info) { - char buff[255]; + char buff[MAX_FIELD_WIDTH]; String s(buff, sizeof(buff)); if (info->found) diff --git a/sql/sql_analyse.h b/sql/sql_analyse.h index ce5c0af6a96..1c60d0c150f 100644 --- a/sql/sql_analyse.h +++ b/sql/sql_analyse.h @@ -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 */ @@ -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; @@ -105,9 +114,9 @@ public: max_arg(""), sum(0), 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, - 0, (void (*)(void*)) free_string); }; + { init_tree(&tree, 0, 0, sizeof(String), a->binary ? + (qsort_cmp2) stringcmp2 : (qsort_cmp2) sortcmp2, + 0, (tree_element_free) free_string, NULL); }; void add(); void get_opt_type(String*, ha_rows); @@ -145,8 +154,8 @@ class field_real: public field_info 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); } + { init_tree(&tree, 0, 0, sizeof(double), + (qsort_cmp2) compare_double2, 0, NULL, NULL); } void add(); void get_opt_type(String*, ha_rows); @@ -191,8 +200,8 @@ class field_longlong: public field_info 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); } + { init_tree(&tree, 0, 0, sizeof(longlong), + (qsort_cmp2) compare_longlong2, 0, NULL, NULL); } void add(); void get_opt_type(String*, ha_rows); @@ -236,8 +245,8 @@ class field_ulonglong: public field_info 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); } + { init_tree(&tree, 0, 0, sizeof(ulonglong), + (qsort_cmp2) compare_ulonglong2, 0, NULL, 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 e4a797efaaf..4d6c4ba1bb9 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -15,7 +15,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -/* Basic functions neaded by many modules */ +/* Basic functions needed by many modules */ #include "mysql_priv.h" #include "sql_acl.h" @@ -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,75 +109,73 @@ 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 ; idx < open_cache.records; idx++) + 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,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(**start_list)+entry->key_length))) { - result = -1; + open_list=0; // Out of memory break; } + strmov((*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; + *start_list=0; } - 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); } @@ -195,10 +191,11 @@ query_table_status(THD *thd,const char *db,const char *table_name) bool send_fields(THD *thd,List<Item> &list,uint flag) { - List_iterator<Item> it(list); + List_iterator_fast<Item> it(list); Item *item; char buff[80]; CONVERT *convert= (flag & 4) ? (CONVERT*) 0 : thd->convert_set; + DBUG_ENTER("send_fields"); String tmp((char*) buff,sizeof(buff)),*res,*packet= &thd->packet; @@ -259,11 +256,11 @@ 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); - return 0; + send_eof(&thd->net,1); + DBUG_RETURN(0); err: send_error(&thd->net,ER_OUT_OF_RESOURCES); /* purecov: inspected */ - return 1; /* purecov: inspected */ + DBUG_RETURN(1); /* purecov: inspected */ } @@ -304,6 +301,7 @@ static void free_cache_entry(TABLE *table) void free_io_cache(TABLE *table) { + DBUG_ENTER("free_io_cache"); if (table->io_cache) { close_cached_file(table->io_cache); @@ -315,6 +313,7 @@ void free_io_cache(TABLE *table) my_free((gptr) table->record_pointers,MYF(0)); table->record_pointers=0; } + DBUG_VOID_RETURN; } /* Close all tables which aren't in use by any thread */ @@ -418,7 +417,6 @@ void close_thread_tables(THD *thd, bool locked) DBUG_VOID_RETURN; // LOCK TABLES in use } - TABLE *table,*next; bool found_old_table=0; if (thd->lock) @@ -431,41 +429,10 @@ 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; - } - } + while (thd->open_tables) + found_old_table|=close_thread_table(thd, &thd->open_tables); thd->some_tables_deleted=0; - thd->open_tables=0; + /* 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 */ @@ -481,6 +448,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) @@ -505,7 +514,7 @@ void close_temporary_tables(THD *thd) const uint init_query_buf_size = 11; // "drop table " uint query_buf_size; bool found_user_tables = 0; - + LINT_INIT(end); query_buf_size = init_query_buf_size; @@ -582,7 +591,7 @@ bool close_temporary_table(THD *thd, const char *db, const char *table_name) table= *prev; *prev= table->next; close_temporary(table); - if(thd->slave_thread) + if (thd->slave_thread) --slave_open_temp_tables; return 0; } @@ -605,8 +614,6 @@ bool rename_temporary_table(THD* thd, TABLE *table, const char *db, } - - /* move table first in unused links */ static void relink_unused(TABLE *table) @@ -690,7 +697,7 @@ TABLE *reopen_name_locked_table(THD* thd, TABLE_LIST* table_list) if (thd->killed) DBUG_RETURN(0); TABLE* table; - if(!(table = table_list->table)) + if (!(table = table_list->table)) DBUG_RETURN(0); char* db = thd->db ? thd->db : table_list->db; @@ -703,11 +710,11 @@ TABLE *reopen_name_locked_table(THD* thd, TABLE_LIST* table_list) if (open_unireg_entry(thd, table, db, table_name, table_name, 1) || !(table->table_cache_key =memdup_root(&table->mem_root,(char*) key, key_length))) - { - closefrm(table); - pthread_mutex_unlock(&LOCK_open); - DBUG_RETURN(0); - } + { + closefrm(table); + pthread_mutex_unlock(&LOCK_open); + DBUG_RETURN(0); + } table->key_length=key_length; table->version=0; @@ -837,25 +844,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)); @@ -896,7 +884,7 @@ TABLE *open_table(THD *thd,const char *db,const char *table_name, table->outer_join=table->null_row=table->maybe_null=0; table->status=STATUS_NO_RECORD; table->keys_in_use_for_query=table->used_keys= table->keys_in_use; - dbug_assert(table->key_read == 0); + DBUG_ASSERT(table->key_read == 0); DBUG_RETURN(table); } @@ -1180,7 +1168,7 @@ bool wait_for_tables(THD *thd) /* Now we can open all tables without any interference */ thd->proc_info="Reopen tables"; result=reopen_tables(thd,0,0); - + } pthread_mutex_unlock(&LOCK_open); thd->proc_info=0; @@ -1315,7 +1303,6 @@ static int open_unireg_entry(THD *thd, TABLE *entry, const char *db, if (error) goto err; } - (void) entry->file->extra(HA_EXTRA_NO_READCHECK); // Not needed in SQL DBUG_RETURN(0); err: DBUG_RETURN(1); @@ -1338,7 +1325,7 @@ int open_tables(THD *thd,TABLE_LIST *start) { if (!tables->table && !(tables->table=open_table(thd, - tables->db ? tables->db : thd->db, + tables->db, tables->real_name, tables->name, &refresh))) { @@ -1395,7 +1382,7 @@ TABLE *open_ltable(THD *thd, TABLE_LIST *table_list, thr_lock_type lock_type) DBUG_ENTER("open_ltable"); thd->proc_info="Opening table"; - while (!(table=open_table(thd,table_list->db ? table_list->db : thd->db, + while (!(table=open_table(thd,table_list->db, table_list->real_name,table_list->name, &refresh)) && refresh) ; if (table) @@ -1404,11 +1391,7 @@ TABLE *open_ltable(THD *thd, TABLE_LIST *table_list, thr_lock_type lock_type) #if defined( __WIN__) || defined(OS2) /* Win32 can't drop a file that is open */ - if (lock_type == TL_WRITE_ALLOW_READ -#ifdef HAVE_GEMINI_DB - && table->db_type != DB_TYPE_GEMINI -#endif /* HAVE_GEMINI_DB */ - ) + if (lock_type == TL_WRITE_ALLOW_READ) { lock_type= TL_WRITE; } @@ -1517,7 +1500,6 @@ TABLE *open_temporary_table(THD *thd, const char *path, const char *db, DBUG_RETURN(0); } - tmp_table->file->extra(HA_EXTRA_NO_READCHECK); // Not needed in SQL tmp_table->reginfo.lock_type=TL_WRITE; // Simulate locked tmp_table->tmp_table = (tmp_table->file->has_transactions() ? TRANSACTIONAL_TMP_TABLE : TMP_TABLE); @@ -1534,8 +1516,8 @@ TABLE *open_temporary_table(THD *thd, const char *path, const char *db, { tmp_table->next=thd->temporary_tables; thd->temporary_tables=tmp_table; - if(thd->slave_thread) - ++slave_open_temp_tables; + if (thd->slave_thread) + slave_open_temp_tables++; } DBUG_RETURN(tmp_table); } @@ -1595,18 +1577,13 @@ Field *find_field_in_table(THD *thd,TABLE *table,const char *name,uint length, { field->query_id=thd->query_id; table->used_fields++; - if (field->part_of_key) - { - if (!(field->part_of_key & table->ref_primary_key)) - table->used_keys&=field->part_of_key; - } - else - table->used_keys=0; + table->used_keys&=field->part_of_key; } else thd->dupp_field=field; } - if (check_grants && !thd->master_access && check_grant_column(thd,table,name,length)) + if (check_grants && !thd->master_access && + check_grant_column(thd,table,name,length)) return WRONG_GRANT; return field; } @@ -1627,9 +1604,7 @@ find_field_in_tables(THD *thd,Item_field *item,TABLE_LIST *tables) for (; tables ; tables=tables->next) { if (!strcmp(tables->name,table_name) && - (!db || - (tables->db && !strcmp(db,tables->db)) || - (!tables->db && !strcmp(db,thd->db)))) + (!db || !strcmp(db,tables->db))) { found_table=1; Field *find=find_field_in_table(thd,tables->table,name,length, @@ -1672,7 +1647,8 @@ find_field_in_tables(THD *thd,Item_field *item,TABLE_LIST *tables) for (; tables ; tables=tables->next) { Field *field=find_field_in_table(thd,tables->table,name,length, - grant_option && !thd->master_access, allow_rowid); + grant_option && + !thd->master_access, allow_rowid); if (field) { if (field == WRONG_GRANT) @@ -1753,14 +1729,15 @@ find_item_in_list(Item *find,List<Item> &items) ****************************************************************************/ int setup_fields(THD *thd, TABLE_LIST *tables, List<Item> &fields, - bool set_query_id, List<Item> *sum_func_list) + bool set_query_id, List<Item> *sum_func_list, + bool allow_sum_func) { reg2 Item *item; List_iterator<Item> it(fields); DBUG_ENTER("setup_fields"); thd->set_query_id=set_query_id; - thd->allow_sum_func= test(sum_func_list); + thd->allow_sum_func= allow_sum_func; thd->where="field list"; while ((item=it++)) @@ -1776,7 +1753,8 @@ int setup_fields(THD *thd, TABLE_LIST *tables, List<Item> &fields, { if (item->fix_fields(thd,tables)) DBUG_RETURN(-1); /* purecov: inspected */ - if (item->with_sum_func && item->type() != Item::SUM_FUNC_ITEM) + if (item->with_sum_func && item->type() != Item::SUM_FUNC_ITEM && + sum_func_list) item->split_sum_func(*sum_func_list); thd->used_tables|=item->used_tables(); } @@ -1795,27 +1773,40 @@ bool setup_tables(TABLE_LIST *tables) { DBUG_ENTER("setup_tables"); uint tablenr=0; - for (TABLE_LIST *table=tables ; table ; table=table->next,tablenr++) - { - table->table->tablenr=tablenr; - table->table->map= (table_map) 1 << tablenr; - if ((table->table->outer_join=table->outer_join)) - table->table->maybe_null=1; // LEFT OUTER JOIN ... - if (table->use_index) + for (TABLE_LIST *table_list=tables ; table_list ; + table_list=table_list->next,tablenr++) + { + TABLE *table=table_list->table; + + table->used_fields=0; + table->const_table=0; + table->outer_join=table->null_row=0; + table->status=STATUS_NO_RECORD; + table->keys_in_use_for_query=table->used_keys= table->keys_in_use; + table->maybe_null=test(table->outer_join=table_list->outer_join); + table->tablenr=tablenr; + table->map= (table_map) 1 << tablenr; + if (table_list->use_index) { - key_map map= get_key_map_from_key_list(table->table, - table->use_index); + key_map map= get_key_map_from_key_list(table, + table_list->use_index); if (map == ~(key_map) 0) DBUG_RETURN(1); - table->table->keys_in_use_for_query=map; + table->keys_in_use_for_query=map; } - if (table->ignore_index) + if (table_list->ignore_index) { - key_map map= get_key_map_from_key_list(table->table, - table->ignore_index); + key_map map= get_key_map_from_key_list(table, + table_list->ignore_index); if (map == ~(key_map) 0) DBUG_RETURN(1); - table->table->keys_in_use_for_query &= ~map; + table->keys_in_use_for_query &= ~map; + } + if (table_list->shared) + { + /* Clear query_id that may have been set by previous select */ + for (Field **ptr=table->field ; *ptr ; ptr++) + (*ptr)->query_id=0; } } if (tablenr > MAX_TABLES) @@ -1831,7 +1822,7 @@ static key_map get_key_map_from_key_list(TABLE *table, List<String> *index_list) { key_map map=0; - List_iterator<String> it(*index_list); + List_iterator_fast<String> it(*index_list); String *name; uint pos; while ((name=it++)) @@ -1852,7 +1843,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) { @@ -1867,8 +1858,7 @@ insert_fields(THD *thd,TABLE_LIST *tables, const char *db_name, check_grant_all_columns(thd,SELECT_ACL,table) ) DBUG_RETURN(-1); if (!table_name || (!strcmp(table_name,tables->name) && - (!db_name || !tables->db || - !strcmp(tables->db,db_name)))) + (!db_name || !strcmp(tables->db,db_name)))) { Field **ptr=table->field,*field; thd->used_tables|=table->map; @@ -1882,14 +1872,7 @@ insert_fields(THD *thd,TABLE_LIST *tables, const char *db_name, if (field->query_id == thd->query_id) thd->dupp_field=field; field->query_id=thd->query_id; - - if (field->part_of_key) - { - if (!(field->part_of_key & table->ref_primary_key)) - table->used_keys&=field->part_of_key; - } - else - table->used_keys=0; + table->used_keys&=field->part_of_key; } /* All fields are used */ table->used_fields=table->fields; @@ -1957,7 +1940,6 @@ int setup_conds(THD *thd,TABLE_LIST *tables,COND **conds) // TODO: This could be optimized to use hashed names if t2 had a hash for (j=0 ; j < t2->fields ; j++) { - key_map tmp_map; if (!my_strcasecmp(t1->field[i]->field_name, t2->field[j]->field_name)) { @@ -1970,20 +1952,8 @@ int setup_conds(THD *thd,TABLE_LIST *tables,COND **conds) /* Mark field used for table cache */ t1->field[i]->query_id=t2->field[j]->query_id=thd->query_id; cond_and->list.push_back(tmp); - if ((tmp_map=t1->field[i]->part_of_key)) - { - if (!(tmp_map & t1->ref_primary_key)) - t1->used_keys&=tmp_map; - } - else - t1->used_keys=0; - if ((tmp_map=t2->field[j]->part_of_key)) - { - if (!(tmp_map & t2->ref_primary_key)) - t2->used_keys&=tmp_map; - } - else - t2->used_keys=0; + t1->used_keys&= t1->field[i]->part_of_key; + t2->used_keys&= t2->field[j]->part_of_key; break; } } @@ -2011,7 +1981,7 @@ int setup_conds(THD *thd,TABLE_LIST *tables,COND **conds) int fill_record(List<Item> &fields,List<Item> &values) { - List_iterator<Item> f(fields),v(values); + List_iterator_fast<Item> f(fields),v(values); Item *value; Item_field *field; DBUG_ENTER("fill_record"); @@ -2029,7 +1999,7 @@ fill_record(List<Item> &fields,List<Item> &values) int fill_record(Field **ptr,List<Item> &values) { - List_iterator<Item> v(values); + List_iterator_fast<Item> v(values); Item *value; DBUG_ENTER("fill_record"); @@ -2092,7 +2062,8 @@ int mysql_create_index(THD *thd, TABLE_LIST *table_list, List<Key> &keys) create_info.db_type=DB_TYPE_DEFAULT; DBUG_RETURN(mysql_alter_table(thd,table_list->db,table_list->real_name, &create_info, table_list, - fields, keys, drop, alter, (ORDER*)0, FALSE, DUP_ERROR)); + fields, keys, drop, alter, (ORDER*)0, FALSE, + DUP_ERROR)); } @@ -2107,7 +2078,8 @@ int mysql_drop_index(THD *thd, TABLE_LIST *table_list, List<Alter_drop> &drop) create_info.db_type=DB_TYPE_DEFAULT; DBUG_RETURN(mysql_alter_table(thd,table_list->db,table_list->real_name, &create_info, table_list, - fields, keys, drop, alter, (ORDER*)0, FALSE, DUP_ERROR)); + fields, keys, drop, alter, (ORDER*)0, FALSE, + DUP_ERROR)); } /***************************************************************************** @@ -2172,7 +2144,10 @@ bool remove_table_from_cache(THD *thd, const char *db, const char *table_name, THD *in_use; table->version=0L; /* Free when thread is ready */ if (!(in_use=table->in_use)) + { + DBUG_PRINT("info",("Table was not in use")); relink_unused(table); + } else if (in_use != thd) { in_use->some_tables_deleted=1; @@ -2202,8 +2177,8 @@ bool remove_table_from_cache(THD *thd, const char *db, const char *table_name, int setup_ftfuncs(THD *thd) { - List_iterator<Item_func_match> li(thd->lex.ftfunc_list), - lj(thd->lex.ftfunc_list); + List_iterator<Item_func_match> li(thd->lex.select->ftfunc_list), + lj(thd->lex.select->ftfunc_list); Item_func_match *ftf, *ftf2; while ((ftf=li++)) @@ -2223,16 +2198,17 @@ int setup_ftfuncs(THD *thd) int init_ftfuncs(THD *thd, bool no_order) { - List_iterator<Item_func_match> li(thd->lex.ftfunc_list); - Item_func_match *ifm; - DBUG_PRINT("info",("Performing FULLTEXT search")); - thd->proc_info="FULLTEXT initialization"; - - while ((ifm=li++)) + if (thd->lex.select->ftfunc_list.elements) { - ifm->init_search(no_order); - } + List_iterator<Item_func_match> li(thd->lex.select->ftfunc_list); + Item_func_match *ifm; + DBUG_PRINT("info",("Performing FULLTEXT search")); + thd->proc_info="FULLTEXT initialization"; + while ((ifm=li++)) + { + ifm->init_search(no_order); + } + } return 0; } - diff --git a/sql/sql_cache.cc b/sql/sql_cache.cc index 09d436c0c9c..87ca7003b7b 100644 --- a/sql/sql_cache.cc +++ b/sql/sql_cache.cc @@ -1,98 +1,3465 @@ -/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB - +/* 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 */ +/* + Description of the query cache: + +1. Query_cache object consists of + - query cache memory pool (cache) + - queries hash (queries) + - tables hash (tables) + - list of blocks ordered as they allocated in memory +(first_block) + - list of queries block (queries_blocks) + - list of used tables (tables_blocks) + +2. Query cache memory pool (cache) consists of + - table of steps of memory bins allocation + - table of free memory bins + - blocks of memory + +3. Memory blocks + +Every memory block has the following structure: + ++----------------------------------------------------------+ +| Block header (Query_cache_block structure) | ++----------------------------------------------------------+ +|Table of database table lists (used for queries & tables) | ++----------------------------------------------------------+ +| Type depended header | +|(Query_cache_query, Query_cache_table, Query_cache_result)| ++----------------------------------------------------------+ +| Data ... | ++----------------------------------------------------------+ + +Block header consists of: +- type: + FREE Free memory block + QUERY Query block + RESULT Ready to send result + RES_CONT Result's continuation + RES_BEG First block of results, that is not yet complete, + written to cache + RES_INCOMPLETE Allocated for results data block + TABLE Block with database table description + INCOMPLETE The destroyed block +- length of block (length) +- length of data & headers (used) +- physical list links (pnext/pprev) - used for the list of + blocks ordered as they are allocated in physical memory +- logical list links (next/prev) - used for queries block list, tables block + list, free memory block lists and list of results block in query +- number of elements in table of database table list (n_tables) + +4. Query & results blocks + +Query stored in cache consists of following blocks: + +more more +recent+-------------+ old +<-----|Query block 1|------> double linked list of queries block + prev | | next + +-------------+ + <-| table 0 |-> (see "Table of database table lists" description) + <-| table 1 |-> + | ... | +--------------------------+ + +-------------+ +-------------------------+ | +NET | | | V V | +struct| | +-+------------+ +------------+ | +<-----|query header |----->|Result block|-->|Result block|-+ doublelinked +writer| |result| |<--| | list of results + +-------------+ +------------+ +------------+ + |charset | +------------+ +------------+ no table of dbtables + |encoding + | | result | | result | + |query text |<-----| header | | header |------+ + +-------------+parent| | | |parent| + ^ +------------+ +------------+ | + | |result data | |result data | | + | +------------+ +------------+ | + +---------------------------------------------------+ + +First query is registered. During the registration query block is +allocated. This query block is included in query hash and is linked +with appropriate database tables lists (if there is no appropriate +list exists it will be created). + +Later when query has performed results is written into the result blocks. +A result block cannot be smaller then QUERY_CACHE_MIN_RESULT_DATA_SIZE. + +When new result is written to cache it is appended to the last result +block, if no more free space left in the last block, new block is +allocated. + +5. Table of database table lists. + +For quick invalidation of queries all query are linked in lists on used +database tables basis (when table will be changed (insert/delete/...) +this queries will be removed from cache). + +Root of such list is table block: + + +------------+ list of used tables (used while invalidation of +<----| Table |-----> whole database) + prev| block |next +-----------+ + | | +-----------+ |Query block| + | | |Query block| +-----------+ + +------------+ +-----------+ | ... | + +->| table 0 |------>|table 0 |----->| table N |---+ + |+-| |<------| |<-----| |<-+| + || +------------+ | ... | | ... | || + || |table header| +-----------+ +-----------+ || + || +------------+ | ... | | ... | || + || |db name + | +-----------+ +-----------+ || + || |table name | || + || +------------+ || + |+--------------------------------------------------------+| + +----------------------------------------------------------+ + +Table block is included into the tables hash (tables). + +6. Free blocks, free blocks bins & steps of freeblock bins. + +When we just started only one free memory block existed. All query +cache memory (that will be used for block allocation) were +containing in this block. +When a new block is allocated we find most suitable memory block +(minimal of >= required size). If such a block can not be found, we try +to find max block < required size (if we allocate block for results). +If there is no free memory, oldest query is removed from cache, and then +we try to allocate memory. Last step should be repeated until we find +suitable block or until there is no unlocked query found. + +If the block is found and its length more then we need, it should be +split into 2 blocks. +New blocks cannot be smaller then min_allocation_unit_bytes. + +When a block becomes free, its neighbor-blocks should be tested and if +there are free blocks among them, they should be joined into one block. + +Free memory blocks are stored in bins according to their sizes. +The bins are stored in size-descending order. +These bins are distributed (by size) approximately logarithmically. + +First bin (number 0) stores free blocks with +size <= query_cache_size>>QUERY_CACHE_MEM_BIN_FIRST_STEP_PWR2. +It is first (number 0) step. +On the next step distributed (1 + QUERY_CACHE_MEM_BIN_PARTS_INC) * +QUERY_CACHE_MEM_BIN_PARTS_MUL bins. This bins allocated in interval from +query_cache_size>>QUERY_CACHE_MEM_BIN_FIRST_STEP_PWR2 to +query_cache_size>>QUERY_CACHE_MEM_BIN_FIRST_STEP_PWR2 >> +QUERY_CACHE_MEM_BIN_STEP_PWR2 +... +On each step interval decreases in 2 power of +QUERY_CACHE_MEM_BIN_STEP_PWR2 +times, number of bins (that distributed on this step) increases. If on +the previous step there were N bins distributed , on the current there +would be distributed +(N + QUERY_CACHE_MEM_BIN_PARTS_INC) * QUERY_CACHE_MEM_BIN_PARTS_MUL +bins. +Last distributed bin stores blocks with size near min_allocation_unit +bytes. + +For example: + query_cache_size>>QUERY_CACHE_MEM_BIN_FIRST_STEP_PWR2 = 100, + min_allocation_unit = 17, + QUERY_CACHE_MEM_BIN_STEP_PWR2 = 1, + QUERY_CACHE_MEM_BIN_PARTS_INC = 1, + QUERY_CACHE_MEM_BIN_PARTS_MUL = 1 + (in followed picture showed right (low) bound of bin): + + | 100>>1 50>>1 |25>>1| + | | | | | | + | 100 75 50 41 33 25 21 18 15| 12 | - bins right (low) bounds + + |\---/\-----/\--------/\--------|---/ | + | 0 1 2 3 | | - steps + \-----------------------------/ \---/ + bins that we store in cache this bin showed for example only + + +Calculation of steps/bins distribution is performed only when query cache +is resized. + +When we need to find appropriate bin, first we should find appropriate +step, then we should calculate number of bins that are using data +stored in Query_cache_memory_bin_step structure. + +Free memory blocks are sorted in bins in lists with size-ascending order +(more small blocks needed frequently then bigger one). + +7. Packing cache. + +Query cache packing is divided into two operation: + - pack_cache + - join_results + +pack_cache moved all blocks to "top" of cache and create one block of free +space at the "bottom": + + before pack_cache after pack_cache + +-------------+ +-------------+ + | query 1 | | query 1 | + +-------------+ +-------------+ + | table 1 | | table 1 | + +-------------+ +-------------+ + | results 1.1 | | results 1.1 | + +-------------+ +-------------+ + | free | | query 2 | + +-------------+ +-------------+ + | query 2 | | table 2 | + +-------------+ ---> +-------------+ + | table 2 | | results 1.2 | + +-------------+ +-------------+ + | results 1.2 | | results 2 | + +-------------+ +-------------+ + | free | | free | + +-------------+ | | + | results 2 | | | + +-------------+ | | + | free | | | + +-------------+ +-------------+ + +pack_cache scan blocks in physical address order and move every non-free +block "higher". + +pack_cach remove every free block it finds. The length of the deleted block +is accumulated to the "gap". All non free blocks should be shifted with the +"gap" step. + +join_results scans all complete queries. If the results of query are not +stored in the same block, join_results tries to move results so, that they +are stored in one block. + + before join_results after join_results + +-------------+ +-------------+ + | query 1 | | query 1 | + +-------------+ +-------------+ + | table 1 | | table 1 | + +-------------+ +-------------+ + | results 1.1 | | free | + +-------------+ +-------------+ + | query 2 | | query 2 | + +-------------+ +-------------+ + | table 2 | | table 2 | + +-------------+ ---> +-------------+ + | results 1.2 | | free | + +-------------+ +-------------+ + | results 2 | | results 2 | + +-------------+ +-------------+ + | free | | results 1 | + | | | | + | | +-------------+ + | | | free | + | | | | + +-------------+ +-------------+ + +If join_results allocated new block(s) then we need call pack_cache again. + +TODO list: + + - Delayed till after-parsing qache answer (for column rights processing) + - Optimize cache resizing + - if new_size < old_size then pack & shrink + - if new_size > old_size copy cached query to new cache + - Move MRG_MYISAM table type processing to handlers, something like: + tables_used->table->file->register_used_filenames(callback, + first_argument); +*/ + #include "mysql_priv.h" +#ifdef HAVE_QUERY_CACHE #include <m_ctype.h> #include <my_dir.h> #include <hash.h> +#include "sql_acl.h" +#include "ha_myisammrg.h" +#ifndef MASTER +#include "../srclib/myisammrg/myrg_def.h" +#else +#include "../myisammrg/myrg_def.h" +#endif +#include <assert.h> + +#if defined(EXTRA_DEBUG) && !defined(DBUG_OFF) +#define MUTEX_LOCK(M) { DBUG_PRINT("lock", ("mutex lock 0x%lx", (ulong)(M))); \ + pthread_mutex_lock(M);} +#define MUTEX_UNLOCK(M) {DBUG_PRINT("lock", ("mutex unlock 0x%lx",\ + (ulong)(M))); pthread_mutex_unlock(M);} +#define SEM_LOCK(M) { int val = 0; sem_getvalue (M, &val); \ + DBUG_PRINT("lock", ("sem lock 0x%lx (%d)", (ulong)(M), val)); \ + sem_wait(M); DBUG_PRINT("lock", ("sem lock ok")); } +#define SEM_UNLOCK(M) {DBUG_PRINT("info", ("sem unlock 0x%lx", (ulong)(M))); \ + sem_post(M); DBUG_PRINT("info", ("sem unlock ok")); } +#define STRUCT_LOCK(M) {DBUG_PRINT("lock", ("%d struct lock...",__LINE__)); \ + pthread_mutex_lock(M);DBUG_PRINT("lock", ("struct lock OK"));} +#define STRUCT_UNLOCK(M) { \ + DBUG_PRINT("lock", ("%d struct unlock...",__LINE__)); \ + pthread_mutex_unlock(M);DBUG_PRINT("lock", ("struct unlock OK"));} +#define BLOCK_LOCK_WR(B) {DBUG_PRINT("lock", ("%d LOCK_WR 0x%lx",\ + __LINE__,(ulong)(B))); \ + B->query()->lock_writing();} +#define BLOCK_LOCK_RD(B) {DBUG_PRINT("lock", ("%d LOCK_RD 0x%lx",\ + __LINE__,(ulong)(B))); \ + B->query()->lock_reading();} +#define BLOCK_UNLOCK_WR(B) { \ + DBUG_PRINT("lock", ("%d UNLOCK_WR 0x%lx",\ + __LINE__,(ulong)(B)));B->query()->unlock_writing();} +#define BLOCK_UNLOCK_RD(B) { \ + DBUG_PRINT("lock", ("%d UNLOCK_RD 0x%lx",\ + __LINE__,(ulong)(B)));B->query()->unlock_reading();} +#define DUMP(C) DBUG_EXECUTE("qcache", {\ + (C)->cache_dump(); (C)->queries_dump();(C)->tables_dump();}) +#else +#define MUTEX_LOCK(M) pthread_mutex_lock(M) +#define MUTEX_UNLOCK(M) pthread_mutex_unlock(M) +#define SEM_LOCK(M) sem_wait(M) +#define SEM_UNLOCK(M) sem_post(M) +#define STRUCT_LOCK(M) pthread_mutex_lock(M) +#define STRUCT_UNLOCK(M) pthread_mutex_unlock(M) +#define BLOCK_LOCK_WR(B) B->query()->lock_writing() +#define BLOCK_LOCK_RD(B) B->query()->lock_reading() +#define BLOCK_UNLOCK_WR(B) B->query()->unlock_writing() +#define BLOCK_UNLOCK_RD(B) B->query()->unlock_reading() +#define DUMP(C) +#endif + +/***************************************************************************** + Query_cache_block_table method(s) +*****************************************************************************/ + +inline Query_cache_block * Query_cache_block_table::block() +{ + return (Query_cache_block *)(((byte*)this) - + ALIGN_SIZE(sizeof(Query_cache_block_table)*n) - + ALIGN_SIZE(sizeof(Query_cache_block))); +}; + +/***************************************************************************** + Query_cache_block method(s) +*****************************************************************************/ + +void Query_cache_block::init(ulong block_length) +{ + DBUG_ENTER("Query_cache_block::init"); + DBUG_PRINT("qcache", ("init block 0x%lx length: %lu", (ulong) this, + block_length)); + length = block_length; + used = 0; + type = Query_cache_block::FREE; + n_tables = 0; + DBUG_VOID_RETURN; +} + +void Query_cache_block::destroy() +{ + DBUG_ENTER("Query_cache_block::destroy"); + DBUG_PRINT("qcache", ("destroy block 0x%lx, type %d", + (ulong) this, type)); + type = INCOMPLETE; + DBUG_VOID_RETURN; +} + +inline uint Query_cache_block::headers_len() +{ + return (ALIGN_SIZE(sizeof(Query_cache_block_table)*n_tables) + + ALIGN_SIZE(sizeof(Query_cache_block))); +} + +inline gptr Query_cache_block::data(void) +{ + return (gptr)( ((byte*)this) + headers_len() ); +} + +inline Query_cache_query * Query_cache_block::query() +{ +#ifndef DBUG_OFF + if (type != QUERY) + query_cache.wreck(__LINE__, "incorrect block type"); +#endif + return (Query_cache_query *) data(); +} + +inline Query_cache_table * Query_cache_block::table() +{ +#ifndef DBUG_OFF + if (type != TABLE) + query_cache.wreck(__LINE__, "incorrect block type"); +#endif + return (Query_cache_table *) data(); +} + +inline Query_cache_result * Query_cache_block::result() +{ +#ifndef DBUG_OFF + if (type != RESULT && type != RES_CONT && type != RES_BEG && + type != RES_INCOMPLETE) + query_cache.wreck(__LINE__, "incorrect block type"); +#endif + return (Query_cache_result *) data(); +} + +inline Query_cache_block_table * Query_cache_block::table(TABLE_COUNTER_TYPE n) +{ + return ((Query_cache_block_table *) + (((byte*)this)+ALIGN_SIZE(sizeof(Query_cache_block)) + + n*sizeof(Query_cache_block_table))); +} + + +/***************************************************************************** + * Query_cache_table method(s) + *****************************************************************************/ + +extern "C" +{ +byte *query_cache_table_get_key(const byte *record, uint *length, + my_bool not_used __attribute__((unused))) +{ + Query_cache_block* table_block = (Query_cache_block*) record; + *length = (table_block->used - table_block->headers_len() - + ALIGN_SIZE(sizeof(Query_cache_table))); + return (((byte *) table_block->data()) + + ALIGN_SIZE(sizeof(Query_cache_table))); +} +} + +/***************************************************************************** + Query_cache_query methods +*****************************************************************************/ + +void Query_cache_query::init_n_lock() +{ + DBUG_ENTER("Query_cache_query::init_n_lock"); + res=0; wri = 0; len = 0; + sem_init(&lock, 0, 1); + pthread_mutex_init(&clients_guard,MY_MUTEX_INIT_FAST); + clients = 0; + lock_writing(); + DBUG_PRINT("qcache", ("inited & locked query for block 0x%lx", + ((byte*) this)-ALIGN_SIZE(sizeof(Query_cache_block)))); + DBUG_VOID_RETURN; +} + + +void Query_cache_query::unlock_n_destroy() +{ + DBUG_ENTER("Query_cache_query::unlock_n_destroy"); + DBUG_PRINT("qcache", ("destroyed & unlocked query for block 0x%lx", + ((byte*)this)-ALIGN_SIZE(sizeof(Query_cache_block)))); + /* + The following call is not needed on system where one can destroy an + active semaphore + */ + this->unlock_writing(); + sem_destroy(&lock); + pthread_mutex_destroy(&clients_guard); + DBUG_VOID_RETURN; +} -#define SQL_CACHE_LENGTH 30 // 300 crashes apple gcc. -HASH sql_cache; -static LEX lex_array_static[SQL_CACHE_LENGTH]; -LEX * lex_array = lex_array_static; -int last_lex_array_item = SQL_CACHE_LENGTH - 1; +/* + Following methods work for block read/write locking only in this + particular case and in interaction with structure_guard_mutex. -/* Function to return a text string from a LEX struct */ -static byte *cache_key(const byte *record, uint *length, my_bool not_used) + Lock for write prevents any other locking. (exclusive use) + Lock for read prevents only locking for write. +*/ + +void Query_cache_query::lock_writing() +{ + SEM_LOCK(&lock); +} + + +/* + Needed for finding queries, that we may delete from cache. + We don't want to wait while block become unlocked. In addition, + block locking means that query is now used and we don't need to + remove it. +*/ + +my_bool Query_cache_query::try_lock_writing() +{ + DBUG_ENTER("Query_cache_block::try_lock_writing"); + if (sem_trywait(&lock)!=0 || clients != 0) + { + DBUG_PRINT("info", ("can't lock semaphore")); + DBUG_RETURN(0); + } + DBUG_PRINT("info", ("mutex 'lock' 0x%lx locked", (ulong) &lock)); + DBUG_RETURN(1); +} + + +void Query_cache_query::lock_reading() +{ + MUTEX_LOCK(&clients_guard); + if ( ++clients == 1 ) + SEM_LOCK(&lock); + MUTEX_UNLOCK(&clients_guard); +} + + +void Query_cache_query::unlock_writing() +{ + SEM_UNLOCK(&lock); +} + + +void Query_cache_query::unlock_reading() +{ + /* + To avoid unlocking semaphore before unlocking mutex (that may cause + destroying locked mutex), we use temporary boolean variable 'unlock'. + */ + MUTEX_LOCK(&clients_guard); + bool ulock = ((--clients) == 0); + MUTEX_UNLOCK(&clients_guard); + if (ulock) SEM_UNLOCK(&lock); +} + +extern "C" +{ +byte *query_cache_query_get_key(const byte *record, uint *length, + my_bool not_used) +{ + Query_cache_block *query_block = (Query_cache_block*) record; + *length = (query_block->used - query_block->headers_len() - + ALIGN_SIZE(sizeof(Query_cache_query))); + return (((byte *) query_block->data()) + + ALIGN_SIZE(sizeof(Query_cache_query))); +} +} + +/***************************************************************************** + Functions to store things into the query cache +*****************************************************************************/ + +/* + Insert the packet into the query cache. + This should only be called if net->query_cache_query != 0 +*/ + +void query_cache_insert(NET *net, const char *packet, ulong length) +{ + DBUG_ENTER("query_cache_insert"); + +#ifndef DBUG_OFF + // Check if we have called query_cache.wreck() (which disables the cache) + if (query_cache.query_cache_size == 0) + DBUG_VOID_RETURN; +#endif + + STRUCT_LOCK(&query_cache.structure_guard_mutex); + Query_cache_block *query_block = ((Query_cache_block*) + net->query_cache_query); + if (query_block) + { + Query_cache_query *header = query_block->query(); + Query_cache_block *result = header->result(); + + DUMP(&query_cache); + BLOCK_LOCK_WR(query_block); + DBUG_PRINT("qcache", ("insert packet %lu bytes long",length)); + + /* + On success STRUCT_UNLOCK(&query_cache.structure_guard_mutex) will be + done by query_cache.append_result_data if success (if not we need + query_cache.structure_guard_mutex locked to free query) + */ + if (!query_cache.append_result_data(&result, length, (gptr) packet, + query_block)) + { + query_cache.refused++; + DBUG_PRINT("warning", ("Can't append data")); + header->result(result); + DBUG_PRINT("qcache", ("free query 0x%lx", (ulong) query_block)); + // The following call will remove the lock on query_block + query_cache.free_query(query_block); + // append_result_data no success => we need unlock + STRUCT_UNLOCK(&query_cache.structure_guard_mutex); + DBUG_VOID_RETURN; + } + header->result(result); + BLOCK_UNLOCK_WR(query_block); + } + else + STRUCT_UNLOCK(&query_cache.structure_guard_mutex); + DBUG_EXECUTE("check_querycache",query_cache.check_integrity(0);); + DBUG_VOID_RETURN; +} + + +void query_cache_abort(NET *net) +{ + DBUG_ENTER("query_cache_abort"); + +#ifndef DBUG_OFF + // Check if we have called query_cache.wreck() (which disables the cache) + if (query_cache.query_cache_size == 0) + DBUG_VOID_RETURN; +#endif + if (net->query_cache_query != 0) // Quick check on unlocked structure + { + STRUCT_LOCK(&query_cache.structure_guard_mutex); + Query_cache_block *query_block = ((Query_cache_block*) + net->query_cache_query); + if (query_block) // Test if changed by other thread + { + DUMP(&query_cache); + BLOCK_LOCK_WR(query_block); + // The following call will remove the lock on query_block + query_cache.free_query(query_block); + } + net->query_cache_query=0; + DBUG_EXECUTE("check_querycache",query_cache.check_integrity(1);); + STRUCT_UNLOCK(&query_cache.structure_guard_mutex); + } + DBUG_VOID_RETURN; +} + + +void query_cache_end_of_result(NET *net) { -#ifdef QQ - LEX *lex=(LEX*) record; - *length = lex->sql_query_length; - // *length = strlen(lex->ptr); - return (byte*) lex->sql_query_text; - // return (byte*) lex->ptr; + DBUG_ENTER("query_cache_end_of_result"); + +#ifndef DBUG_OFF + // Check if we have called query_cache.wreck() (which disables the cache) + if (query_cache.query_cache_size == 0) DBUG_VOID_RETURN; +#endif + + if (net->query_cache_query != 0) // Quick check on unlocked structure + { + STRUCT_LOCK(&query_cache.structure_guard_mutex); + Query_cache_block *query_block = ((Query_cache_block*) + net->query_cache_query); + if (query_block) + { + DUMP(&query_cache); + BLOCK_LOCK_WR(query_block); + Query_cache_query *header = query_block->query(); + Query_cache_block *last_result_block = header->result()->prev; + ulong allign_size = ALIGN_SIZE(last_result_block->used); + ulong len = max(query_cache.min_allocation_unit, allign_size); + if (last_result_block->length >= query_cache.min_allocation_unit + len) + query_cache.split_block(last_result_block,len); + STRUCT_UNLOCK(&query_cache.structure_guard_mutex); + +#ifndef DBUG_OFF + if (header->result() == 0) + { + DBUG_PRINT("error", ("end of data whith no result. query '%s'", + header->query())); + query_cache.wreck(__LINE__, ""); + DBUG_VOID_RETURN; + } #endif - return 0; + header->found_rows(current_thd->limit_found_rows); + header->result()->type = Query_cache_block::RESULT; + header->writer(0); + BLOCK_UNLOCK_WR(query_block); + } + else + { + // Cache was flushed or resized and query was deleted => do nothing + STRUCT_UNLOCK(&query_cache.structure_guard_mutex); + } + net->query_cache_query=0; + DBUG_EXECUTE("check_querycache",query_cache.check_integrity(0);); + } + DBUG_VOID_RETURN; +} + +void query_cache_invalidate_by_MyISAM_filename(const char *filename) +{ + query_cache.invalidate_by_MyISAM_filename(filename); + DBUG_EXECUTE("check_querycache",query_cache.check_integrity(0);); +} + + +/***************************************************************************** + Query_cache methods +*****************************************************************************/ + +Query_cache::Query_cache(ulong query_cache_limit, + ulong min_allocation_unit, + ulong min_result_data_size, + uint def_query_hash_size , + uint def_table_hash_size) + :query_cache_size(0), + query_cache_limit(query_cache_limit), + queries_in_cache(0), hits(0), inserts(0), refused(0), + total_blocks(0), + min_allocation_unit(min_allocation_unit), + min_result_data_size(min_result_data_size), + def_query_hash_size(def_query_hash_size), + def_table_hash_size(def_table_hash_size), + initialized(0) +{ + ulong min_needed=(ALIGN_SIZE(sizeof(Query_cache_block)) + + ALIGN_SIZE(sizeof(Query_cache_block_table)) + + ALIGN_SIZE(sizeof(Query_cache_query)) + 3); + set_if_bigger(min_allocation_unit,min_needed); + this->min_allocation_unit = min_allocation_unit; + set_if_bigger(this->min_result_data_size,min_allocation_unit); +} + + +ulong Query_cache::resize(ulong query_cache_size) +{ + DBUG_ENTER("Query_cache::resize"); + DBUG_PRINT("qcache", ("from %lu to %lu",this->query_cache_size, + query_cache_size)); + free_cache(0); + this->query_cache_size=query_cache_size; + DBUG_RETURN(init_cache()); +} + + +void Query_cache::store_query(THD *thd, TABLE_LIST *tables_used) +{ + TABLE_COUNTER_TYPE tables; + ulong tot_length; + DBUG_ENTER("Query_cache::store_query"); + if (query_cache_size == 0) + DBUG_VOID_RETURN; + + if ((tables = is_cacheable(thd, thd->query_length, + thd->query, &thd->lex, tables_used))) + { + NET *net = &thd->net; + byte flags = (thd->client_capabilities & CLIENT_LONG_FLAG ? 0x80 : 0); + STRUCT_LOCK(&structure_guard_mutex); + + if (query_cache_size == 0) + DBUG_VOID_RETURN; + DUMP(this); + + /* Key is query + database + flag */ + if (thd->db_length) + { + memcpy(thd->query+thd->query_length+1, thd->db, thd->db_length); + DBUG_PRINT("qcache", ("database : %s length %u", + thd->db, thd->db_length)); + } + else + { + DBUG_PRINT("qcache", ("No active database")); + } + /* + Prepare flags: + most significant bit - CLIENT_LONG_FLAG, + other - charset number (0 no charset convertion) + */ + if (thd->convert_set != 0) + { + flags|= (byte) thd->convert_set->number(); + DBUG_ASSERT(thd->convert_set->number() < 128); + } + tot_length=thd->query_length+thd->db_length+2; + thd->query[tot_length-1] = (char) flags; + + /* Check if another thread is processing the same query? */ + Query_cache_block *competitor = (Query_cache_block *) + hash_search(&queries, (byte*) thd->query, tot_length); + DBUG_PRINT("qcache", ("competitor 0x%lx, flags %x", (ulong) competitor, + flags)); + if (competitor == 0) + { + /* Query is not in cache and no one is working with it; Store it */ + Query_cache_block *query_block; + query_block= write_block_data(tot_length, (gptr) thd->query, + ALIGN_SIZE(sizeof(Query_cache_query)), + Query_cache_block::QUERY, tables, 1); + if (query_block != 0) + { + DBUG_PRINT("qcache", ("query block 0x%lx allocated, %lu", + (ulong) query_block, query_block->used)); + + Query_cache_query *header = query_block->query(); + header->init_n_lock(); + if (hash_insert(&queries, (byte*) query_block)) + { + refused++; + DBUG_PRINT("qcache", ("insertion in query hash")); + header->unlock_n_destroy(); + free_memory_block(query_block); + STRUCT_UNLOCK(&structure_guard_mutex); + goto end; + } + if (!register_all_tables(query_block, tables_used, tables)) + { + refused++; + DBUG_PRINT("warning", ("tables list including failed")); + hash_delete(&queries, (byte *) query_block); + header->unlock_n_destroy(); + free_memory_block(query_block); + STRUCT_UNLOCK(&structure_guard_mutex); + goto end; + } + double_linked_list_simple_include(query_block, &queries_blocks); + inserts++; + queries_in_cache++; + STRUCT_UNLOCK(&structure_guard_mutex); + + net->query_cache_query= (gptr) query_block; + header->writer(net); + // init_n_lock make query block locked + BLOCK_UNLOCK_WR(query_block); + } + else + { + // We have not enough memory to store query => do nothing + refused++; + STRUCT_UNLOCK(&structure_guard_mutex); + DBUG_PRINT("warning", ("Can't allocate query")); + } + } + else + { + // Another thread is processing the same query => do nothing + refused++; + STRUCT_UNLOCK(&structure_guard_mutex); + DBUG_PRINT("qcache", ("Another thread process same query")); + } + } + else + statistic_increment(refused, &structure_guard_mutex); + +end: + DBUG_VOID_RETURN; +} + +/* + Check if the query is in the cache. If it was cached, send it + to the user. + + RESULTS + 1 Query was not cached. + 0 The query was cached and user was sent the result. + -1 The query was cached but we didn't have rights to use it. + No error is sent to the client yet. +*/ + + + +int +Query_cache::send_result_to_client(THD *thd, char *sql, uint query_length) +{ + Query_cache_query *query; + Query_cache_block *first_result_block, *result_block; + Query_cache_block_table *block_table, *block_table_end; + ulong tot_length; + byte flags; + DBUG_ENTER("Query_cache::send_result_to_client"); + + if (query_cache_size == 0 || + /* + it is not possible to check has_transactions() function of handler + because tables not opened yet + */ + (thd->options & (OPTION_NOT_AUTO_COMMIT | OPTION_BEGIN)) || + thd->query_cache_type == 0) + + { + DBUG_PRINT("qcache", ("query cache disabled or not in autocommit mode")); + goto err; + } + + /* Check that we haven't forgot to reset the query cache variables */ + DBUG_ASSERT(thd->net.query_cache_query == 0); + + if (!thd->safe_to_cache_query) + { + DBUG_PRINT("qcache", ("SELECT is non-cacheable")); + goto err; + } + + /* + Test if the query is a SELECT + (pre-space is removed in dispatch_command) + */ + if (toupper(sql[0]) != 'S' || toupper(sql[1]) != 'E' || + toupper(sql[2]) !='L') + { + DBUG_PRINT("qcache", ("The statement is not a SELECT; Not cached")); + goto err; + } + + STRUCT_LOCK(&structure_guard_mutex); + if (query_cache_size == 0) + { + DBUG_PRINT("qcache", ("query cache disabled")); + goto err_unlock; + } + Query_cache_block *query_block; + + tot_length=query_length+thd->db_length+2; + if (thd->db_length) + { + memcpy(sql+query_length+1, thd->db, thd->db_length); + DBUG_PRINT("qcache", ("database: '%s' length %u", + thd->db, thd->db_length)); + } + else + { + DBUG_PRINT("qcache", ("No active database")); + } + /* + prepare flags: + Most significant bit - CLIENT_LONG_FLAG, + Other - charset number (0 no charset convertion) + */ + flags = (thd->client_capabilities & CLIENT_LONG_FLAG ? 0x80 : 0); + if (thd->convert_set != 0) + { + flags |= (byte) thd->convert_set->number(); + DBUG_ASSERT(thd->convert_set->number() < 128); + } + sql[tot_length-1] = (char) flags; + query_block = (Query_cache_block *) hash_search(&queries, (byte*) sql, + tot_length); + /* Quick abort on unlocked data */ + if (query_block == 0 || + query_block->query()->result() == 0 || + query_block->query()->result()->type != Query_cache_block::RESULT) + { + DBUG_PRINT("qcache", ("No query in query hash or no results")); + goto err_unlock; + } + DBUG_PRINT("qcache", ("Query in query hash 0x%lx", (ulong)query_block)); + + /* Now lock and test that nothing changed while blocks was unlocked */ + BLOCK_LOCK_RD(query_block); + + query = query_block->query(); + result_block= first_result_block= query->result(); + + if (result_block == 0 || result_block->type != Query_cache_block::RESULT) + { + /* The query is probably yet processed */ + DBUG_PRINT("qcache", ("query found, but no data or data incomplete")); + BLOCK_UNLOCK_RD(query_block); + goto err_unlock; + } + DBUG_PRINT("qcache", ("Query have result 0x%lx", (ulong) query)); + + // Check access; + block_table= query_block->table(0); + block_table_end= block_table+query_block->n_tables; + for ( ; block_table != block_table_end; block_table++) + { + TABLE_LIST table_list; + bzero((char*) &table_list,sizeof(table_list)); + + Query_cache_table *table = block_table->parent; + table_list.db = table->db(); + table_list.name = table_list.real_name = table->table(); + if (check_table_access(thd,SELECT_ACL,&table_list,1)) + { + DBUG_PRINT("qcache", + ("probably no SELECT access to %s.%s => return to normal processing", + table_list.db, table_list.name)); + refused++; // This is actually a hit + STRUCT_UNLOCK(&structure_guard_mutex); + thd->safe_to_cache_query=0; // Don't try to cache this + BLOCK_UNLOCK_RD(query_block); + DBUG_RETURN(-1); // Privilege error + } + if (table_list.grant.want_privilege) + { + DBUG_PRINT("qcache", ("Need to check column privileges for %s.%s", + table_list.db, table_list.name)); + BLOCK_UNLOCK_RD(query_block); + thd->safe_to_cache_query=0; // Don't try to cache this + goto err_unlock; // Parse query + } + } + move_to_query_list_end(query_block); + hits++; + STRUCT_UNLOCK(&structure_guard_mutex); + + /* + Send cached result to client + */ + do + { + DBUG_PRINT("qcache", ("Results (len %lu, used %lu, headers %lu)", + result_block->length, result_block->used, + result_block->headers_len()+ + ALIGN_SIZE(sizeof(Query_cache_result)))); + + Query_cache_result *result = result_block->result(); + if (net_real_write(&thd->net, result->data(), + result_block->used - + result_block->headers_len() - + ALIGN_SIZE(sizeof(Query_cache_result)))) + break; // Client aborted + result_block = result_block->next; + } while (result_block != first_result_block); + + thd->limit_found_rows = query->found_rows(); + + BLOCK_UNLOCK_RD(query_block); + DBUG_RETURN(1); // Result sent to client + +err_unlock: + STRUCT_UNLOCK(&structure_guard_mutex); +err: + DBUG_RETURN(0); // Query was not cached +} + +/* + Remove all cached queries that uses any of the tables in the list +*/ + +void Query_cache::invalidate(THD *thd, TABLE_LIST *tables_used, + my_bool using_transactions) +{ + DBUG_ENTER("Query_cache::invalidate (table list)"); + if (query_cache_size > 0) + { + STRUCT_LOCK(&structure_guard_mutex); + if (query_cache_size > 0) + { + DUMP(this); + + using_transactions = using_transactions && + (thd->options & (OPTION_NOT_AUTO_COMMIT | OPTION_BEGIN)); + for ( ; tables_used; tables_used=tables_used->next) + { + DBUG_ASSERT(!using_transactions || tables_used->table!=0); + if (using_transactions && + tables_used->table->file->has_transactions()) + /* + Tables_used->table can't be 0 in transaction. + Only 'drop' invalidate not opened table, but 'drop' + force transaction finish. + */ + thd->add_changed_table(tables_used->table); + else + invalidate_table(tables_used); + } + } + STRUCT_UNLOCK(&structure_guard_mutex); + } + DBUG_VOID_RETURN; +} + +void Query_cache::invalidate(CHANGED_TABLE_LIST *tables_used) +{ + DBUG_ENTER("Query_cache::invalidate (changed table list)"); + if (query_cache_size > 0 && tables_used) + { + STRUCT_LOCK(&structure_guard_mutex); + if (query_cache_size > 0) + { + DUMP(this); + for ( ; tables_used; tables_used=tables_used->next) + { + invalidate_table((byte*) tables_used->key, tables_used->key_length); + DBUG_PRINT("qcache", (" db %s, table %s", tables_used->key, + tables_used->table_name)); + } + } + STRUCT_UNLOCK(&structure_guard_mutex); + } + DBUG_VOID_RETURN; } -/* At the moment we do not really want to do anything upon delete */ -static void free_cache_entry(void *entry) +/* + Remove all cached queries that uses the given table +*/ + +void Query_cache::invalidate(THD *thd, TABLE *table, + my_bool using_transactions) { + DBUG_ENTER("Query_cache::invalidate (table)"); + + if (query_cache_size > 0) + { + STRUCT_LOCK(&structure_guard_mutex); + if (query_cache_size > 0) + { + using_transactions = using_transactions && + (thd->options & (OPTION_NOT_AUTO_COMMIT | OPTION_BEGIN)); + if (using_transactions && table->file->has_transactions()) + thd->add_changed_table(table); + else + invalidate_table(table); + } + STRUCT_UNLOCK(&structure_guard_mutex); + } + DBUG_VOID_RETURN; } -/* Initialization of the SQL cache hash -- should be called during - the bootstrap stage */ -bool sql_cache_init(void) +/* + Remove all cached queries that uses the given database +*/ + +void Query_cache::invalidate(char *db) { - if (query_buff_size) + DBUG_ENTER("Query_cache::invalidate (db)"); + if (query_cache_size > 0) { - VOID(hash_init(&sql_cache, 4096, 0, 0, - cache_key, - (void (*)(void*)) free_cache_entry, - 0)); + STRUCT_LOCK(&structure_guard_mutex); + if (query_cache_size > 0) + { + DUMP(this); + /* invalidate_table reduce list while only root of list remain */ + while (tables_blocks !=0 ) + invalidate_table(tables_blocks); + } + STRUCT_UNLOCK(&structure_guard_mutex); } - return 0; + DBUG_VOID_RETURN; } -/* Clearing the SQL cache hash -- during shutdown */ -void sql_cache_free(void) + +void Query_cache::invalidate_by_MyISAM_filename(const char *filename) { - hash_free(&sql_cache); + DBUG_ENTER("Query_cache::invalidate_by_MyISAM_filename"); + if (query_cache_size > 0) + { + /* Calculate the key outside the lock to make the lock shorter */ + char key[MAX_DBKEY_LENGTH]; + uint32 db_length; + uint key_length= filename_2_table_key(key, filename, &db_length); + STRUCT_LOCK(&structure_guard_mutex); + if (query_cache_size > 0) // Safety if cache removed + { + Query_cache_block *table_block; + if ((table_block = (Query_cache_block*) hash_search(&tables, + (byte*) key, + key_length))) + invalidate_table(table_block); + } + STRUCT_UNLOCK(&structure_guard_mutex); + } + DBUG_VOID_RETURN; } -/* Finds whether the SQL command is already in the cache, at any case - establishes correct LEX structure in the THD (either from - cache or a new one) */ + /* Remove all queries from cache */ -int sql_cache_hit(THD *thd, char *sql, uint length) +void Query_cache::flush() { -#ifdef QQ - LEX *ptr; - ptr = (LEX *)hash_search(&sql_cache, sql, length); - if (ptr) { - fprintf(stderr, "Query `%s' -- hit in the cache (%p)\n", ptr->sql_query_text, ptr); - thd->lex_ptr = ptr; - ptr->thd = thd; - } else { - thd->lex_ptr = ptr = lex_array + last_lex_array_item--; + DBUG_ENTER("Query_cache::flush"); + STRUCT_LOCK(&structure_guard_mutex); + if (query_cache_size > 0) + { + DUMP(this); + flush_cache(); + DUMP(this); + } + + DBUG_EXECUTE("check_querycache",query_cache.check_integrity(1);); + STRUCT_UNLOCK(&structure_guard_mutex); + DBUG_VOID_RETURN; +} + + /* Join result in cache in 1 block (if result length > join_limit) */ + +void Query_cache::pack(ulong join_limit, uint iteration_limit) +{ + DBUG_ENTER("Query_cache::pack"); + uint i = 0; + do + { + pack_cache(); + } while ((++i < iteration_limit) && join_results(join_limit)); + DBUG_VOID_RETURN; +} + + +void Query_cache::destroy() +{ + if ( !initialized ) + { + DBUG_PRINT("qcache", ("Query Cache not initialized")); + return; + } + DBUG_ENTER("Query_cache::destroy"); + free_cache(1); + pthread_mutex_destroy(&structure_guard_mutex); + initialized = 0; + DBUG_VOID_RETURN; +} + + +/***************************************************************************** + init/destroy +*****************************************************************************/ + +void Query_cache::init() +{ + DBUG_ENTER("Query_cache::init"); + pthread_mutex_init(&structure_guard_mutex,MY_MUTEX_INIT_FAST); + initialized = 1; + DBUG_VOID_RETURN; +} + + +ulong Query_cache::init_cache() +{ + uint mem_bin_count, num, step; + ulong mem_bin_size, prev_size, inc; + ulong additional_data_size, max_mem_bin_size, approx_additional_data_size; + + DBUG_ENTER("Query_cache::init_cache"); + if (!initialized) + init(); + approx_additional_data_size = (sizeof(Query_cache) + + sizeof(gptr)*(def_query_hash_size+ + def_query_hash_size)); + if (query_cache_size < approx_additional_data_size) + goto err; + + query_cache_size -= approx_additional_data_size; + + /* + Count memory bins number. + Check section 6. in start comment for the used algorithm. + */ + + max_mem_bin_size = query_cache_size >> QUERY_CACHE_MEM_BIN_FIRST_STEP_PWR2; + mem_bin_count = (uint) ((1 + QUERY_CACHE_MEM_BIN_PARTS_INC) * + QUERY_CACHE_MEM_BIN_PARTS_MUL); + mem_bin_num = 1; + mem_bin_steps = 1; + mem_bin_size = max_mem_bin_size >> QUERY_CACHE_MEM_BIN_STEP_PWR2; + prev_size = 0; + while (mem_bin_size > min_allocation_unit) + { + mem_bin_num += mem_bin_count; + prev_size = mem_bin_size; + mem_bin_size >>= QUERY_CACHE_MEM_BIN_STEP_PWR2; + mem_bin_steps++; + mem_bin_count += QUERY_CACHE_MEM_BIN_PARTS_INC; + mem_bin_count = (uint) (mem_bin_count * QUERY_CACHE_MEM_BIN_PARTS_MUL); + + // Prevent too small bins spacing + if (mem_bin_count > (mem_bin_size >> QUERY_CACHE_MEM_BIN_SPC_LIM_PWR2)) + mem_bin_count= (mem_bin_size >> QUERY_CACHE_MEM_BIN_SPC_LIM_PWR2); + } + inc = (prev_size - mem_bin_size) / mem_bin_count; + mem_bin_num += (mem_bin_count - (min_allocation_unit - mem_bin_size)/inc); + mem_bin_steps++; + additional_data_size = ((mem_bin_num+1) * + ALIGN_SIZE(sizeof(Query_cache_memory_bin))+ + (mem_bin_steps * + ALIGN_SIZE(sizeof(Query_cache_memory_bin_step)))); + + if (query_cache_size < additional_data_size) + goto err; + query_cache_size -= additional_data_size; + + STRUCT_LOCK(&structure_guard_mutex); + if (query_cache_size <= min_allocation_unit) + { + DBUG_PRINT("qcache", + (" query_cache_size <= min_allocation_unit => cache disabled")); + STRUCT_UNLOCK(&structure_guard_mutex); + goto err; + } + + if (!(cache = (byte *) + my_malloc_lock(query_cache_size+additional_data_size, MYF(0)))) + { + STRUCT_UNLOCK(&structure_guard_mutex); + goto err; + } + + DBUG_PRINT("qcache", ("cache length %lu, min unit %lu, %u bins", + query_cache_size, min_allocation_unit, mem_bin_num)); - lex_start(thd, (uchar *)sql, length); + steps = (Query_cache_memory_bin_step *) cache; + bins = ((Query_cache_memory_bin *) + (cache + mem_bin_steps * + ALIGN_SIZE(sizeof(Query_cache_memory_bin_step)))); - if (hash_insert(&sql_cache, (const byte *)ptr)) { - fprintf(stderr, "Out of memory during hash_insert?\n"); + first_block = (Query_cache_block *) (cache + additional_data_size); + first_block->init(query_cache_size); + total_blocks++; + first_block->pnext=first_block->pprev=first_block; + first_block->next=first_block->prev=first_block; + + /* Prepare bins */ + + bins[0].init(max_mem_bin_size); + steps[0].init(max_mem_bin_size,0,0); + mem_bin_count = (uint) ((1 + QUERY_CACHE_MEM_BIN_PARTS_INC) * + QUERY_CACHE_MEM_BIN_PARTS_MUL); + num= step= 1; + mem_bin_size = max_mem_bin_size >> QUERY_CACHE_MEM_BIN_STEP_PWR2; + while (mem_bin_size > min_allocation_unit) + { + ulong incr = (steps[step-1].size - mem_bin_size) / mem_bin_count; + unsigned long size = mem_bin_size; + for (uint i= mem_bin_count; i > 0; i--) + { + bins[num+i-1].init(size); + size += incr; } - fprintf(stderr, "Query `%s' not found in the cache -- insert %p from slot %d\n", thd->lex_ptr->ptr, ptr, last_lex_array_item+1); - if (!hash_search(&sql_cache, sql, length)) { - fprintf(stderr, "I just enterred a hash key but it's not where -- what's that?\n"); - } else { - fprintf(stderr, "Inserted to cache\n"); + num += mem_bin_count; + steps[step].init(mem_bin_size, num-1, incr); + mem_bin_size >>= QUERY_CACHE_MEM_BIN_STEP_PWR2; + step++; + mem_bin_count += QUERY_CACHE_MEM_BIN_PARTS_INC; + mem_bin_count = (uint) (mem_bin_count * QUERY_CACHE_MEM_BIN_PARTS_MUL); + if (mem_bin_count > (mem_bin_size >> QUERY_CACHE_MEM_BIN_SPC_LIM_PWR2)) + mem_bin_count=(mem_bin_size >> QUERY_CACHE_MEM_BIN_SPC_LIM_PWR2); + } + inc = (steps[step-1].size - mem_bin_size) / mem_bin_count; + + /* + num + mem_bin_count > mem_bin_num, but index never be > mem_bin_num + because block with size < min_allocated_unit never will be requested + */ + + steps[step].init(mem_bin_size, num + mem_bin_count - 1, inc); + { + uint skiped = (min_allocation_unit - mem_bin_size)/inc; + ulong size = mem_bin_size + inc*skiped; + uint i = mem_bin_count - skiped; + while (i-- > 0) + { + bins[num+i].init(size); + size += inc; } - return 0; } + bins[mem_bin_num].number = 1; // For easy end test in get_free_block + free_memory = free_memory_blocks = 0; + insert_into_free_memory_list(first_block); + + DUMP(this); + + VOID(hash_init(&queries,def_query_hash_size, 0, 0, + query_cache_query_get_key, 0, 0)); + VOID(hash_init(&tables,def_table_hash_size, 0, 0, + query_cache_table_get_key, 0, 0)); + + queries_in_cache = 0; + queries_blocks = 0; + STRUCT_UNLOCK(&structure_guard_mutex); + DBUG_RETURN(query_cache_size + + additional_data_size + approx_additional_data_size); + +err: + make_disabled(); + DBUG_RETURN(0); +} + + +/* Disable the use of the query cache */ + +void Query_cache::make_disabled() +{ + DBUG_ENTER("Query_cache::make_disabled"); + query_cache_size= 0; + free_memory= 0; + bins= 0; + steps= 0; + cache= 0; + mem_bin_num= mem_bin_steps= 0; + queries_in_cache= 0; + first_block= 0; + DBUG_VOID_RETURN; +} + + +void Query_cache::free_cache(my_bool destruction) +{ + DBUG_ENTER("Query_cache::free_cache"); + if (query_cache_size > 0) + { + if (!destruction) + STRUCT_LOCK(&structure_guard_mutex); + + flush_cache(); +#ifndef DBUG_OFF + if (bins[0].free_blocks == 0) + { + wreck(__LINE__,"no free memory found in (bins[0].free_blocks"); + DBUG_VOID_RETURN; + } +#endif + + /* Becasue we did a flush, all cache memory must be in one this block */ + bins[0].free_blocks->destroy(); + total_blocks--; +#ifndef DBUG_OFF + if (free_memory != query_cache_size) + DBUG_PRINT("qcache", ("free memory %lu (should be %lu)", + free_memory , query_cache_size)); #endif - return 1; + my_free((gptr) cache, MYF(MY_ALLOW_ZERO_PTR)); + make_disabled(); + hash_free(&queries); + hash_free(&tables); + if (!destruction) + STRUCT_UNLOCK(&structure_guard_mutex); + } + DBUG_VOID_RETURN; +} + +/***************************************************************************** + Free block data +*****************************************************************************/ + +/* + The following assumes we have a lock on the cache +*/ + +void Query_cache::flush_cache() +{ + while (queries_blocks != 0) + { + BLOCK_LOCK_WR(queries_blocks); + free_query(queries_blocks); + } +} + +/* + Free oldest query that is not in use by another thread. + Returns 1 if we couldn't remove anything +*/ + +my_bool Query_cache::free_old_query() +{ + DBUG_ENTER("Query_cache::free_old_query"); + if (queries_blocks) + { + /* + try_lock_writing used to prevent client because here lock + sequence is breached. + Also we don't need remove locked queries at this point. + */ + Query_cache_block *query_block = 0; + if (queries_blocks != 0) + { + Query_cache_block *block = queries_blocks; + /* Search until we find first query that we can remove */ + do + { + Query_cache_query *header = block->query(); + if (header->result() != 0 && + header->result()->type == Query_cache_block::RESULT && + block->query()->try_lock_writing()) + { + query_block = block; + break; + } + } while ((block=block->next) != queries_blocks ); + } + + if (query_block != 0) + { + free_query(query_block); + DBUG_RETURN(0); + } + } + DBUG_RETURN(1); // Nothing to remove +} + +/* + Free query from query cache. + query_block must be locked for writing. + This function will remove (and destroy) the lock for the query. +*/ + +void Query_cache::free_query(Query_cache_block *query_block) +{ + DBUG_ENTER("Query_cache::free_query"); + DBUG_PRINT("qcache", ("free query 0x%lx %lu bytes result", + (ulong) query_block, + query_block->query()->length() )); + + queries_in_cache--; + hash_delete(&queries,(byte *) query_block); + + Query_cache_query *query = query_block->query(); + + if (query->writer() != 0) + { + /* Tell MySQL that this query should not be cached anymore */ + query->writer()->query_cache_query = 0; + query->writer(0); + } + double_linked_list_exclude(query_block, &queries_blocks); + Query_cache_block_table *table=query_block->table(0); + + for (TABLE_COUNTER_TYPE i=0; i < query_block->n_tables; i++) + unlink_table(table++); + Query_cache_block *result_block = query->result(); + + /* + The following is true when query destruction was called and no results + in query . (query just registered and then abort/pack/flush called) + */ + if (result_block != 0) + { + Query_cache_block *block = result_block; + do + { + Query_cache_block *current = block; + block = block->next; + free_memory_block(current); + } while (block != result_block); + } + + query->unlock_n_destroy(); + free_memory_block(query_block); + + DBUG_VOID_RETURN; } + +/***************************************************************************** + Query data creation +*****************************************************************************/ + +Query_cache_block * +Query_cache::write_block_data(ulong data_len, gptr data, + ulong header_len, + Query_cache_block::block_type type, + TABLE_COUNTER_TYPE ntab, + my_bool under_guard) +{ + ulong all_headers_len = (ALIGN_SIZE(sizeof(Query_cache_block)) + + ALIGN_SIZE(ntab*sizeof(Query_cache_block_table)) + + header_len); + ulong len = data_len + all_headers_len; + DBUG_ENTER("Query_cache::write_block_data"); + DBUG_PRINT("qcache", ("data: %ld, header: %ld, all header: %ld", + data_len, header_len, all_headers_len)); + Query_cache_block *block = allocate_block(max(len, min_allocation_unit), + 1, 0, under_guard); + if (block != 0) + { + block->type = type; + block->n_tables = ntab; + block->used = len; + + memcpy((void*) (((byte *) block)+ all_headers_len), + (void*) data, data_len); + } + DBUG_RETURN(block); +} + + +/* + On success STRUCT_UNLOCK(&query_cache.structure_guard_mutex) will be done. +*/ + +my_bool +Query_cache::append_result_data(Query_cache_block **current_block, + ulong data_len, gptr data, + Query_cache_block *query_block) +{ + DBUG_ENTER("Query_cache::append_result_data"); + DBUG_PRINT("qcache", ("append %lu bytes to 0x%lx query", + data_len, query_block)); + + if (query_block->query()->add(data_len) > query_cache_limit) + { + DBUG_PRINT("qcache", ("size limit reached %lu > %lu", + query_block->query()->length(), + query_cache_limit)); + DBUG_RETURN(0); + } + if (*current_block == 0) + { + DBUG_PRINT("qcache", ("allocated first result data block %lu", data_len)); + /* + STRUCT_UNLOCK(&structure_guard_mutex) Will be done by + write_result_data if success; + */ + DBUG_RETURN(write_result_data(current_block, data_len, data, query_block, + Query_cache_block::RES_BEG)); + } + Query_cache_block *last_block = (*current_block)->prev; + + DBUG_PRINT("qcache", ("lastblock 0x%lx len %lu used %lu", + (ulong) last_block, last_block->length, + last_block->used)); + my_bool success = 1; + ulong last_block_free_space= last_block->length - last_block->used; + + /* + We will first allocate and write the 'tail' of data, that doesn't fit + in the 'last_block'. Only if this succeeds, we will fill the last_block. + This saves us a memcpy if the query doesn't fit in the query cache. + */ + + // Try join blocks if physically next block is free... + ulong tail = data_len - last_block_free_space; + ulong append_min = get_min_append_result_data_size(); + if (last_block_free_space < data_len && + append_next_free_block(last_block, + max(tail, append_min))) + last_block_free_space = last_block->length - last_block->used; + // If no space in last block (even after join) allocate new block + if (last_block_free_space < data_len) + { + DBUG_PRINT("qcache", ("allocate new block for %lu bytes", + data_len-last_block_free_space)); + Query_cache_block *new_block = 0; + /* + On success STRUCT_UNLOCK(&structure_guard_mutex) will be done + by the next call + */ + success = write_result_data(&new_block, data_len-last_block_free_space, + (gptr)(((byte*)data)+last_block_free_space), + query_block, + Query_cache_block::RES_CONT); + /* + new_block may be != 0 even !success (if write_result_data + allocate a small block but failed to allocate continue) + */ + if (new_block != 0) + double_linked_list_join(last_block, new_block); + } + else + { + // It is success (nobody can prevent us write data) + STRUCT_UNLOCK(&structure_guard_mutex); + } + + // Now finally write data to the last block + if (success && last_block_free_space > 0) + { + ulong to_copy = min(data_len,last_block_free_space); + DBUG_PRINT("qcache", ("use free space %lub at block 0x%lx to copy %lub", + last_block_free_space, (ulong)last_block, to_copy)); + memcpy((void*) (((byte*) last_block) + last_block->used), (void*) data, + to_copy); + last_block->used+=to_copy; + } + DBUG_RETURN(success); +} + + +my_bool Query_cache::write_result_data(Query_cache_block **result_block, + ulong data_len, gptr data, + Query_cache_block *query_block, + Query_cache_block::block_type type) +{ + DBUG_ENTER("Query_cache::write_result_data"); + DBUG_PRINT("qcache", ("data_len %lu",data_len)); + + /* + Reserve block(s) for filling + During data allocation we must have structure_guard_mutex locked. + As data copy is not a fast operation, it's better if we don't have + structure_guard_mutex locked during data coping. + Thus we first allocate space and lock query, then unlock + structure_guard_mutex and copy data. + */ + + my_bool success = allocate_data_chain(result_block, data_len, query_block, + type == Query_cache_block::RES_BEG); + if (success) + { + // It is success (nobody can prevent us write data) + STRUCT_UNLOCK(&structure_guard_mutex); + byte *rest = (byte*) data; + Query_cache_block *block = *result_block; + uint headers_len = (ALIGN_SIZE(sizeof(Query_cache_block)) + + ALIGN_SIZE(sizeof(Query_cache_result))); + // Now fill list of blocks that created by allocate_data_chain + do + { + block->type = type; + ulong length = block->used - headers_len; + DBUG_PRINT("qcache", ("write %lu byte in block 0x%lx",length, + (ulong)block)); + memcpy((void*)(((byte*) block)+headers_len), (void*) rest, length); + rest += length; + block = block->next; + type = Query_cache_block::RES_CONT; + } while (block != *result_block); + } + else + { + if (*result_block != 0) + { + // Destroy list of blocks that was created & locked by lock_result_data + Query_cache_block *block = *result_block; + do + { + Query_cache_block *current = block; + block = block->next; + free_memory_block(current); + } while (block != *result_block); + *result_block = 0; + /* + It is not success => not unlock structure_guard_mutex (we need it to + free query) + */ + } + } + DBUG_PRINT("qcache", ("success %d", (int) success)); + DBUG_RETURN(success); +} + +inline ulong Query_cache::get_min_first_result_data_size() +{ + if (queries_in_cache < QUERY_CACHE_MIN_ESTIMATED_QUERIES_NUMBER) + return min_result_data_size; + ulong avg_result = (query_cache_size - free_memory) / queries_in_cache; + avg_result = min(avg_result, query_cache_limit); + return max(min_result_data_size, avg_result); +} + +inline ulong Query_cache::get_min_append_result_data_size() +{ + return min_result_data_size; +} + +/* + Allocate one or more blocks to hold data +*/ + +my_bool Query_cache::allocate_data_chain(Query_cache_block **result_block, + ulong data_len, + Query_cache_block *query_block, + my_bool first_block) +{ + ulong all_headers_len = (ALIGN_SIZE(sizeof(Query_cache_block)) + + ALIGN_SIZE(sizeof(Query_cache_result))); + ulong len = data_len + all_headers_len; + DBUG_ENTER("Query_cache::allocate_data_chain"); + DBUG_PRINT("qcache", ("data_len %lu, all_headers_len %lu", + data_len, all_headers_len)); + + ulong min_size = (first_block ? + get_min_first_result_data_size(): + get_min_append_result_data_size()); + *result_block = allocate_block(max(min_size,len), + min_result_data_size == 0, + all_headers_len + min_result_data_size, + 1); + my_bool success = (*result_block != 0); + if (success) + { + Query_cache_block *new_block= *result_block; + new_block->n_tables = 0; + new_block->used = 0; + new_block->type = Query_cache_block::RES_INCOMPLETE; + new_block->next = new_block->prev = new_block; + Query_cache_result *header = new_block->result(); + header->parent(query_block); + + if (new_block->length < len) + { + /* + We got less memory then we need (no big memory blocks) => + Continue to allocated more blocks until we got everything we need. + */ + Query_cache_block *next_block; + if ((success = allocate_data_chain(&next_block, + len - new_block->length, + query_block, first_block))) + double_linked_list_join(new_block, next_block); + } + if (success) + { + new_block->used = min(len, new_block->length); + + DBUG_PRINT("qcache", ("Block len %lu used %lu", + new_block->length, new_block->used)); + } + else + DBUG_PRINT("warning", ("Can't allocate block for continue")); + } + else + DBUG_PRINT("warning", ("Can't allocate block for results")); + DBUG_RETURN(success); +} + +/***************************************************************************** + Tables management +*****************************************************************************/ + +/* + Invalidate the first table in the table_list +*/ + +void Query_cache::invalidate_table(TABLE_LIST *table_list) +{ + if (table_list->table != 0) + invalidate_table(table_list->table); // Table is open + else + { + char key[MAX_DBKEY_LENGTH]; + uint key_length; + Query_cache_block *table_block; + key_length=(uint) (strmov(strmov(key,table_list->db)+1, + table_list->real_name) -key)+ 1; + + // We don't store temporary tables => no key_length+=4 ... + if ((table_block = (Query_cache_block*) + hash_search(&tables,(byte*) key,key_length))) + invalidate_table(table_block); + } +} + +void Query_cache::invalidate_table(TABLE *table) +{ + invalidate_table((byte*) table->table_cache_key, table->key_length); +} + +void Query_cache::invalidate_table(byte * key, uint32 key_length) +{ + Query_cache_block *table_block; + if ((table_block = ((Query_cache_block*) + hash_search(&tables, key, key_length)))) + invalidate_table(table_block); +} + +void Query_cache::invalidate_table(Query_cache_block *table_block) +{ + Query_cache_block_table *list_root = table_block->table(0); + while (list_root->next != list_root) + { + Query_cache_block *query_block = list_root->next->block(); + BLOCK_LOCK_WR(query_block); + free_query(query_block); + } +} + + +my_bool Query_cache::register_all_tables(Query_cache_block *block, + TABLE_LIST *tables_used, + TABLE_COUNTER_TYPE tables) +{ + TABLE_COUNTER_TYPE n; + DBUG_PRINT("qcache", ("register tables block 0x%lx, n %d, header %x", + (ulong) block, (int) tables, + (int) ALIGN_SIZE(sizeof(Query_cache_block)))); + + Query_cache_block_table *block_table = block->table(0); + + for (n=0; tables_used; tables_used=tables_used->next, n++, block_table++) + { + DBUG_PRINT("qcache", + ("table %s, db %s, openinfo at 0x%lx, keylen %u, key at 0x%lx", + tables_used->real_name, tables_used->db, + (ulong) tables_used->table, + tables_used->table->key_length, + (ulong) tables_used->table->table_cache_key)); + block_table->n=n; + if (!insert_table(tables_used->table->key_length, + tables_used->table->table_cache_key, block_table, + tables_used->db_length)) + break; + + if (tables_used->table->db_type == DB_TYPE_MRG_MYISAM) + { + ha_myisammrg *handler = (ha_myisammrg *) tables_used->table->file; + MYRG_INFO *file = handler->myrg_info(); + for (MYRG_TABLE *table = file->open_tables; + table != file->end_table ; + table++) + { + char key[MAX_DBKEY_LENGTH]; + uint32 db_length; + uint key_length =filename_2_table_key(key, table->table->filename, + &db_length); + (++block_table)->n= ++n; + if (!insert_table(key_length, key, block_table, + db_length)) + goto err; + } + } + } + +err: + if (tables_used) + { + DBUG_PRINT("qcache", ("failed at table %d", (int) n)); + /* Unlink the tables we allocated above */ + for (Query_cache_block_table *tmp = block->table(0) ; + tmp != block_table; + tmp++) + unlink_table(tmp); + } + return (tables_used == 0); +} + +/* + Insert used tablename in cache + Returns 0 on error +*/ + +my_bool +Query_cache::insert_table(uint key_len, char *key, + Query_cache_block_table *node, + uint32 db_length) +{ + DBUG_ENTER("Query_cache::insert_table"); + DBUG_PRINT("qcache", ("insert table node 0x%lx, len %d", + (ulong)node, key_len)); + + Query_cache_block *table_block = ((Query_cache_block *) + hash_search(&tables, (byte*) key, + key_len)); + + if (table_block == 0) + { + DBUG_PRINT("qcache", ("new table block from 0x%lx (%u)", + (ulong) key, (int) key_len)); + table_block = write_block_data(key_len, (gptr) key, + ALIGN_SIZE(sizeof(Query_cache_table)), + Query_cache_block::TABLE, + 1, 1); + if (table_block == 0) + { + DBUG_PRINT("qcache", ("Can't write table name to cache")); + DBUG_RETURN(0); + } + Query_cache_table *header = table_block->table(); + double_linked_list_simple_include(table_block, + &tables_blocks); + Query_cache_block_table *list_root = table_block->table(0); + list_root->n = 0; + list_root->next = list_root->prev = list_root; + if (hash_insert(&tables, (const byte *) table_block)) + { + DBUG_PRINT("qcache", ("Can't insert table to hash")); + // write_block_data return locked block + free_memory_block(table_block); + DBUG_RETURN(0); + } + char *db = header->db(); + header->table(db + db_length + 1); + } + + Query_cache_block_table *list_root = table_block->table(0); + node->next = list_root->next; + list_root->next = node; + node->next->prev = node; + node->prev = list_root; + node->parent = table_block->table(); + DBUG_RETURN(1); +} + + +void Query_cache::unlink_table(Query_cache_block_table *node) +{ + DBUG_ENTER("Query_cache::unlink_table"); + node->prev->next = node->next; + node->next->prev = node->prev; + Query_cache_block_table *neighbour = node->next; + if (neighbour->next == neighbour) + { + // list is empty (neighbor is root of list) + Query_cache_block *table_block = neighbour->block(); + double_linked_list_exclude(table_block, + &tables_blocks); + hash_delete(&tables,(byte *) table_block); + free_memory_block(table_block); + } + DBUG_VOID_RETURN; +} + +/***************************************************************************** + Free memory management +*****************************************************************************/ + +Query_cache_block * +Query_cache::allocate_block(ulong len, my_bool not_less, ulong min, + my_bool under_guard) +{ + DBUG_ENTER("Query_cache::allocate_n_lock_block"); + DBUG_PRINT("qcache", ("len %lu, not less %d, min %lu, uder_guard %d", + len, not_less,min,under_guard)); + + if (len >= min(query_cache_size, query_cache_limit)) + { + DBUG_PRINT("qcache", ("Query cache hase only %lu memory and limit %lu", + query_cache_size, query_cache_limit)); + DBUG_RETURN(0); // in any case we don't have such piece of memory + } + + if (!under_guard) + STRUCT_LOCK(&structure_guard_mutex); + + /* Free old queries until we have enough memory to store this block */ + Query_cache_block *block; + do + { + block= get_free_block(len, not_less, min); + } + while (block == 0 && !free_old_query()); + + if (block != 0) // If we found a suitable block + { + if (block->length >= ALIGN_SIZE(len) + min_allocation_unit) + split_block(block,ALIGN_SIZE(len)); + } + + if (!under_guard) + STRUCT_UNLOCK(&structure_guard_mutex); + DBUG_RETURN(block); +} + + +Query_cache_block * +Query_cache::get_free_block(ulong len, my_bool not_less, ulong min) +{ + Query_cache_block *block = 0, *first = 0; + DBUG_ENTER("Query_cache::get_free_block"); + DBUG_PRINT("qcache",("length %lu, not_less %d, min %lu", len, + (int)not_less, min)); + + /* Find block with minimal size > len */ + uint start = find_bin(len); + // try matching bin + if (bins[start].number != 0) + { + Query_cache_block *list = bins[start].free_blocks; + ulong max_len = list->prev->length; + if (list->prev->length >= len) // check block with max size + { + first = list; + uint n = 0; + while ( n < QUERY_CACHE_MEM_BIN_TRY && + first->length < len) //we don't need irst->next != list + { + first=first->next; + n++; + } + if (first->length >= len) + block=first; + else // we don't need if (first->next != list) + { + n = 0; + block = list->prev; + while (n < QUERY_CACHE_MEM_BIN_TRY && + block->length > len) + { + block=block->prev; + n++; + } + if (block->length < len) + block=block->next; + } + } + else + first = list->prev; + } + if (block == 0 && start > 0) + { + DBUG_PRINT("qcache",("Try bins with bigger block size")); + // Try more big bins + int i = start - 1; + while (i > 0 && bins[i].number == 0) + i--; + if (bins[i].number > 0) + block = bins[i].free_blocks; + } + + // If no big blocks => try less size (if it is possible) + if (block == 0 && ! not_less) + { + DBUG_PRINT("qcache",("Try to allocate a smaller block")); + if (first != 0 && first->length > min) + block = first; + else + { + uint i = start + 1; + /* bins[mem_bin_num].number contains 1 for easy end test */ + for (i= start+1 ; bins[i].number == 0 ; i++) ; + if (i < mem_bin_num && bins[i].free_blocks->prev->length >= min) + block = bins[i].free_blocks->prev; + } + } + if (block != 0) + exclude_from_free_memory_list(block); + + DBUG_PRINT("qcache",("getting block 0x%lx", (ulong) block)); + DBUG_RETURN(block); +} + + +void Query_cache::free_memory_block(Query_cache_block *block) +{ + DBUG_ENTER("Query_cache::free_memory_block"); + block->used=0; + DBUG_PRINT("qcache",("first_block 0x%lx, block 0x%lx, pnext 0x%lx pprev 0x%lx", + (ulong) first_block, (ulong) block,block->pnext, + (ulong) block->pprev)); + + if (block->pnext != first_block && block->pnext->is_free()) + block = join_free_blocks(block, block->pnext); + if (block != first_block && block->pprev->is_free()) + block = join_free_blocks(block->pprev, block->pprev); + insert_into_free_memory_list(block); + DBUG_VOID_RETURN; +} + + +void Query_cache::split_block(Query_cache_block *block, ulong len) +{ + DBUG_ENTER("Query_cache::split_block"); + Query_cache_block *new_block = (Query_cache_block*)(((byte*) block)+len); + + new_block->init(block->length - len); + total_blocks++; + block->length=len; + new_block->pnext = block->pnext; + block->pnext = new_block; + new_block->pprev = block; + new_block->pnext->pprev = new_block; + + if (block->type == Query_cache_block::FREE) + // if block was free then it already joined with all free neighbours + insert_into_free_memory_list(new_block); + else + free_memory_block(new_block); + + DBUG_PRINT("qcache", ("split 0x%lx (%lu) new 0x%lx", + (ulong) block, len, (ulong) new_block)); + DBUG_VOID_RETURN; +} + + +Query_cache_block * +Query_cache::join_free_blocks(Query_cache_block *first_block, + Query_cache_block *block_in_list) +{ + Query_cache_block *second_block; + DBUG_ENTER("Query_cache::join_free_blocks"); + DBUG_PRINT("qcache", + ("join first 0x%lx, pnext 0x%lx, in list 0x%lx", + (ulong) first_block, (ulong) first_block->pnext, + (ulong) block_in_list)); + + exclude_from_free_memory_list(block_in_list); + second_block = first_block->pnext; + // May be was not free block + second_block->used=0; + second_block->destroy(); + total_blocks--; + + first_block->length += second_block->length; + first_block->pnext = second_block->pnext; + second_block->pnext->pprev = first_block; + + DBUG_RETURN(first_block); +} + + +my_bool Query_cache::append_next_free_block(Query_cache_block *block, + ulong add_size) +{ + Query_cache_block *next_block = block->pnext; + DBUG_ENTER("Query_cache::append_next_free_block"); + DBUG_PRINT("enter", ("block 0x%lx, add_size %lu", (ulong) block, + add_size)); + + if (next_block != first_block && next_block->is_free()) + { + ulong old_len = block->length; + exclude_from_free_memory_list(next_block); + next_block->destroy(); + total_blocks--; + + block->length += next_block->length; + block->pnext = next_block->pnext; + next_block->pnext->pprev = block; + + if (block->length > ALIGN_SIZE(old_len + add_size) + min_allocation_unit) + split_block(block,ALIGN_SIZE(old_len + add_size)); + DBUG_PRINT("exit", ("block was appended")); + DBUG_RETURN(1); + } + DBUG_RETURN(0); +} + + +void Query_cache::exclude_from_free_memory_list(Query_cache_block *free_block) +{ + DBUG_ENTER("Query_cache::exclude_from_free_memory_list"); + Query_cache_memory_bin *bin = *((Query_cache_memory_bin **) + free_block->data()); + double_linked_list_exclude(free_block, &bin->free_blocks); + bin->number--; + free_memory-=free_block->length; + free_memory_blocks--; + DBUG_PRINT("qcache",("exclude block 0x%lx, bin 0x%lx", (ulong) free_block, + (ulong) bin)); + DBUG_VOID_RETURN; +} + +void Query_cache::insert_into_free_memory_list(Query_cache_block *free_block) +{ + DBUG_ENTER("Query_cache::insert_into_free_memory_list"); + uint idx = find_bin(free_block->length); + insert_into_free_memory_sorted_list(free_block, &bins[idx].free_blocks); + /* + We have enough memory in block for storing bin reference due to + min_allocation_unit choice + */ + Query_cache_memory_bin **bin_ptr = ((Query_cache_memory_bin**) + free_block->data()); + *bin_ptr = bins+idx; + (*bin_ptr)->number++; + DBUG_PRINT("qcache",("insert block 0x%lx, bin[%d] 0x%lx", + (ulong) free_block, idx, (ulong) *bin_ptr)); + DBUG_VOID_RETURN; +} + +uint Query_cache::find_bin(ulong size) +{ + DBUG_ENTER("Query_cache::find_bin"); + // Binary search + int left = 0, right = mem_bin_steps; + do + { + int middle = (left + right) / 2; + if (steps[middle].size > size) + left = middle+1; + else + right = middle; + } while (left < right); + if (left == 0) + { + // first bin not subordinate of common rules + DBUG_PRINT("qcache", ("first bin (# 0), size %lu",size)); + DBUG_RETURN(0); + } + uint bin = steps[left].idx - + (uint)((size - steps[left].size)/steps[left].increment); +#ifndef DBUG_OFF + bins_dump(); +#endif + DBUG_PRINT("qcache", ("bin %u step %u, size %lu step size %lu", + bin, left, size, steps[left].size)); + DBUG_RETURN(bin); +} + + +/***************************************************************************** + Lists management +*****************************************************************************/ + +void Query_cache::move_to_query_list_end(Query_cache_block *query_block) +{ + DBUG_ENTER("Query_cache::move_to_query_list_end"); + double_linked_list_exclude(query_block, &queries_blocks); + double_linked_list_simple_include(query_block, &queries_blocks); + DBUG_VOID_RETURN; +} + + +void Query_cache::insert_into_free_memory_sorted_list(Query_cache_block * + new_block, + Query_cache_block ** + list) +{ + DBUG_ENTER("Query_cache::insert_into_free_memory_sorted_list"); + /* + list sorted by size in ascendant order, because we need small blocks + more frequently than bigger ones + */ + + new_block->used = 0; + new_block->n_tables = 0; + new_block->type = Query_cache_block::FREE; + + if (*list == 0) + { + *list = new_block->next=new_block->prev=new_block; + DBUG_PRINT("qcache", ("inserted into empty list")); + } + else + { + Query_cache_block *point = *list; + if (point->length >= new_block->length) + { + point = point->prev; + *list = new_block; + } + else + { + /* Find right position in sorted list to put block */ + while (point->next != *list && + point->next->length < new_block->length) + point=point->next; + } + new_block->prev = point; + new_block->next = point->next; + new_block->next->prev = new_block; + point->next = new_block; + } + free_memory+=new_block->length; + free_memory_blocks++; + DBUG_VOID_RETURN; +} + + +void +Query_cache::double_linked_list_simple_include(Query_cache_block *point, + Query_cache_block ** + list_pointer) +{ + DBUG_ENTER("Query_cache::double_linked_list_simple_include"); + DBUG_PRINT("qcache", ("including block 0x%lx", (ulong) point)); + if (*list_pointer == 0) + *list_pointer=point->next=point->prev=point; + else + { + // insert to and of list + point->next = (*list_pointer); + point->prev = (*list_pointer)->prev; + point->prev->next = point; + (*list_pointer)->prev = point; + } + DBUG_VOID_RETURN; +} + +void +Query_cache::double_linked_list_exclude(Query_cache_block *point, + Query_cache_block **list_pointer) +{ + DBUG_ENTER("Query_cache::double_linked_list_exclude"); + DBUG_PRINT("qcache", ("excluding block 0x%lx, list 0x%lx", + (ulong) point, (ulong) list_pointer)); + if (point->next == point) + *list_pointer = 0; // empty list + else + { + point->next->prev = point->prev; + point->prev->next = point->next; + if (point == *list_pointer) + *list_pointer = point->next; + } + DBUG_VOID_RETURN; +} + + +void Query_cache::double_linked_list_join(Query_cache_block *head_tail, + Query_cache_block *tail_head) +{ + Query_cache_block *head_head = head_tail->next, + *tail_tail = tail_head->prev; + head_head->prev = tail_tail; + head_tail->next = tail_head; + tail_head->prev = head_tail; + tail_tail->next = head_head; +} + +/***************************************************************************** + Query +*****************************************************************************/ + +/* + If query is cacheable return number tables in query + (query without tables are not cached) +*/ + +TABLE_COUNTER_TYPE Query_cache::is_cacheable(THD *thd, uint32 query_len, + char *query, + LEX *lex, TABLE_LIST *tables_used) +{ + TABLE_COUNTER_TYPE tables = 0; + DBUG_ENTER("Query_cache::is_cacheable"); + + if (lex->sql_command == SQLCOM_SELECT && + thd->temporary_tables == 0 && + (thd->query_cache_type == 1 || + (thd->query_cache_type == 2 && (lex->select->options & + OPTION_TO_QUERY_CACHE))) && + thd->safe_to_cache_query) + { + my_bool has_transactions = 0; + DBUG_PRINT("qcache", ("options %lx %lx, type %u", + OPTION_TO_QUERY_CACHE, + lex->select->options, + (int) thd->query_cache_type)); + + for (; tables_used; tables_used=tables_used->next) + { + tables++; + DBUG_PRINT("qcache", ("table %s, db %s, type %u", + tables_used->real_name, + tables_used->db, tables_used->table->db_type)); + has_transactions = (has_transactions || + tables_used->table->file->has_transactions()); + + if (tables_used->table->db_type == DB_TYPE_MRG_ISAM) + { + DBUG_PRINT("qcache", ("select not cacheable: used MRG_ISAM table(s)")); + DBUG_RETURN(0); + } + if (tables_used->table->db_type == DB_TYPE_MRG_MYISAM) + { + ha_myisammrg *handler = (ha_myisammrg *)tables_used->table->file; + MYRG_INFO *file = handler->myrg_info(); + tables+= (file->end_table - file->open_tables); + } + } + + if ((thd->options & (OPTION_NOT_AUTO_COMMIT | OPTION_BEGIN)) && + has_transactions) + { + DBUG_PRINT("qcache", ("not in autocommin mode")); + DBUG_RETURN(0); + } + DBUG_PRINT("qcache", ("select is using %d tables", tables)); + DBUG_RETURN(tables); + } + + DBUG_PRINT("qcache", + ("not interesting query: %d or not cacheable, options %lx %lx, type %u", + (int) lex->sql_command, + OPTION_TO_QUERY_CACHE, + lex->select->options, + (int) thd->query_cache_type)); + DBUG_RETURN(0); +} + + +/***************************************************************************** + Packing +*****************************************************************************/ + +void Query_cache::pack_cache() +{ + DBUG_ENTER("Query_cache::pack_cache"); + STRUCT_LOCK(&structure_guard_mutex); + DBUG_EXECUTE("check_querycache",query_cache.check_integrity(1);); + + byte *border = 0; + Query_cache_block *before = 0; + ulong gap = 0; + my_bool ok = 1; + Query_cache_block *block = first_block; + DUMP(this); + + if (first_block) + { + do + { + Query_cache_block *next=block->pnext; + ok = move_by_type(&border, &before, &gap, block); + block = next; + } while (ok && block != first_block); + + if (border != 0) + { + Query_cache_block *new_block = (Query_cache_block *) border; + new_block->init(gap); + total_blocks++; + new_block->pnext = before->pnext; + before->pnext = new_block; + new_block->pprev = before; + new_block->pnext->pprev = new_block; + insert_into_free_memory_list(new_block); + } + DUMP(this); + } + + DBUG_EXECUTE("check_querycache",query_cache.check_integrity(1);); + STRUCT_UNLOCK(&structure_guard_mutex); + DBUG_VOID_RETURN; +} + + +my_bool Query_cache::move_by_type(byte **border, + Query_cache_block **before, ulong *gap, + Query_cache_block *block) +{ + DBUG_ENTER("Query_cache::move_by_type"); + + my_bool ok = 1; + switch (block->type) { + case Query_cache_block::FREE: + { + DBUG_PRINT("qcache", ("block 0x%lx FREE", (ulong) block)); + if (*border == 0) + { + *border = (byte *) block; + *before = block->pprev; + DBUG_PRINT("qcache", ("gap beginning here")); + } + exclude_from_free_memory_list(block); + *gap +=block->length; + block->pprev->pnext=block->pnext; + block->pnext->pprev=block->pprev; + block->destroy(); + total_blocks--; + DBUG_PRINT("qcache", ("added to gap (%lu)", *gap)); + break; + } + case Query_cache_block::TABLE: + { + DBUG_PRINT("qcache", ("block 0x%lx TABLE", (ulong) block)); + if (*border == 0) + break; + ulong len = block->length, used = block->used; + Query_cache_block_table *list_root = block->table(0); + Query_cache_block_table *tprev = list_root->prev, + *tnext = list_root->next; + Query_cache_block *prev = block->prev, + *next = block->next, + *pprev = block->pprev, + *pnext = block->pnext, + *new_block =(Query_cache_block *) *border; + uint tablename_offset = block->table()->table() - block->table()->db(); + char *data = (char*) block->data(); + byte *key; + uint key_length; + key=query_cache_table_get_key((byte*) block, &key_length, 0); + hash_search(&tables, (byte*) key, key_length); + + block->destroy(); + new_block->init(len); + new_block->type=Query_cache_block::TABLE; + new_block->used=used; + new_block->n_tables=1; + memmove((char*) new_block->data(), data, len-new_block->headers_len()); + relink(block, new_block, next, prev, pnext, pprev); + if (tables_blocks == block) + tables_blocks = new_block; + + Query_cache_block_table *nlist_root = new_block->table(0); + nlist_root->n = 0; + nlist_root->next = tnext; + tnext->prev = nlist_root; + nlist_root->prev = tprev; + tprev->next = nlist_root; + DBUG_PRINT("qcache", + ("list_root: 0x%lx tnext 0x%lx tprev 0x%lx tprev->next 0x%lx tnext->prev 0x%lx", + (ulong) list_root, (ulong) tnext, (ulong) tprev, + (ulong)tprev->next, (ulong)tnext->prev)); + /* + Go through all queries that uses this table and change them to + point to the new table object + */ + Query_cache_table *new_block_table=new_block->table(); + for (;tnext != nlist_root; tnext=tnext->next) + tnext->parent= new_block_table; + *border += len; + *before = new_block; + /* Fix pointer to table name */ + new_block->table()->table(new_block->table()->db() + tablename_offset); + /* Fix hash to point at moved block */ + hash_replace(&tables, tables.current_record, (byte*) new_block); + + DBUG_PRINT("qcache", ("moved %lu bytes to 0x%lx, new gap at 0x%lx", + len, (ulong) new_block, (ulong) *border)); + break; + } + case Query_cache_block::QUERY: + { + DBUG_PRINT("qcache", ("block 0x%lx QUERY", (ulong) block)); + if (*border == 0) + break; + BLOCK_LOCK_WR(block); + ulong len = block->length, used = block->used; + TABLE_COUNTER_TYPE n_tables = block->n_tables; + Query_cache_block *prev = block->prev, + *next = block->next, + *pprev = block->pprev, + *pnext = block->pnext, + *new_block =(Query_cache_block*) *border; + char *data = (char*) block->data(); + Query_cache_block *first_result_block = ((Query_cache_query *) + block->data())->result(); + byte *key; + uint key_length; + key=query_cache_query_get_key((byte*) block, &key_length, 0); + hash_search(&queries, (byte*) key, key_length); + // Move table of used tables + memmove((char*) new_block->table(0), (char*) block->table(0), + ALIGN_SIZE(n_tables*sizeof(Query_cache_block_table))); + block->query()->unlock_n_destroy(); + block->destroy(); + new_block->init(len); + new_block->type=Query_cache_block::QUERY; + new_block->used=used; + new_block->n_tables=n_tables; + memmove((char*) new_block->data(), data, len - new_block->headers_len()); + relink(block, new_block, next, prev, pnext, pprev); + if (queries_blocks == block) + queries_blocks = new_block; + for (TABLE_COUNTER_TYPE j=0; j < n_tables; j++) + { + Query_cache_block_table *block_table = new_block->table(j); + block_table->next->prev = block_table; + block_table->prev->next = block_table; + } + DBUG_PRINT("qcache", ("after circle tt")); + *border += len; + *before = new_block; + new_block->query()->result(first_result_block); + if (first_result_block != 0) + { + Query_cache_block *result_block = first_result_block; + do + { + result_block->result()->parent(new_block); + result_block = result_block->next; + } while ( result_block != first_result_block ); + } + Query_cache_query *new_query= ((Query_cache_query *) new_block->data()); + sem_init(&new_query->lock, 0, 1); + pthread_mutex_init(&new_query->clients_guard,MY_MUTEX_INIT_FAST); + + /* + If someone is writing to this block, inform the writer that the block + has been moved. + */ + NET *net = new_block->query()->writer(); + if (net != 0) + { + net->query_cache_query= (gptr) new_block; + } + /* Fix hash to point at moved block */ + hash_replace(&queries, queries.current_record, (byte*) new_block); + DBUG_PRINT("qcache", ("moved %lu bytes to 0x%lx, new gap at 0x%lx", + len, (ulong) new_block, (ulong) *border)); + break; + } + case Query_cache_block::RES_INCOMPLETE: + case Query_cache_block::RES_BEG: + case Query_cache_block::RES_CONT: + case Query_cache_block::RESULT: + { + DBUG_PRINT("qcache", ("block 0x%lx RES* (%d)", (ulong) block, + (int) block->type)); + if (*border == 0) + break; + Query_cache_block *query_block = block->result()->parent(), + *next = block->next, + *prev = block->prev; + Query_cache_block::block_type type = block->type; + BLOCK_LOCK_WR(query_block); + ulong len = block->length, used = block->used; + Query_cache_block *pprev = block->pprev, + *pnext = block->pnext, + *new_block =(Query_cache_block*) *border; + char *data = (char*) block->data(); + block->destroy(); + new_block->init(len); + new_block->type=type; + new_block->used=used; + memmove((char*) new_block->data(), data, len - new_block->headers_len()); + relink(block, new_block, next, prev, pnext, pprev); + new_block->result()->parent(query_block); + Query_cache_query *query = query_block->query(); + if (query->result() == block) + query->result(new_block); + *border += len; + *before = new_block; + /* If result writing complete && we have free space in block */ + ulong free_space = new_block->length - new_block->used; + if (query->result()->type == Query_cache_block::RESULT && + new_block->length > new_block->used && + *gap + free_space > min_allocation_unit && + new_block->length - free_space > min_allocation_unit) + { + *border -= free_space; + *gap += free_space; + new_block->length -= free_space; + } + BLOCK_UNLOCK_WR(query_block); + DBUG_PRINT("qcache", ("moved %lu bytes to 0x%lx, new gap at 0x%lx", + len, (ulong) new_block, (ulong) *border)); + break; + } + default: + DBUG_PRINT("error", ("unexpected block type %d, block 0x%lx", + (int)block->type, (ulong) block)); + ok = 0; + } + DBUG_RETURN(ok); +} + + +void Query_cache::relink(Query_cache_block *oblock, + Query_cache_block *nblock, + Query_cache_block *next, Query_cache_block *prev, + Query_cache_block *pnext, Query_cache_block *pprev) +{ + if (prev == oblock) //check pointer to himself + { + nblock->prev = nblock; + nblock->next = nblock; + } + else + { + nblock->prev = prev; + prev->next=nblock; + } + if (next != oblock) + { + nblock->next = next; + next->prev=nblock; + } + nblock->pprev = pprev; // Physical pointer to himself have only 1 free block + nblock->pnext = pnext; + pprev->pnext=nblock; + pnext->pprev=nblock; +} + + +my_bool Query_cache::join_results(ulong join_limit) +{ + my_bool has_moving = 0; + DBUG_ENTER("Query_cache::join_results"); + + STRUCT_LOCK(&structure_guard_mutex); + if (queries_blocks != 0) + { + Query_cache_block *block = queries_blocks; + do + { + Query_cache_query *header = block->query(); + if (header->result() != 0 && + header->result()->type == Query_cache_block::RESULT && + header->length() > join_limit) + { + Query_cache_block *new_result_block = + get_free_block(header->length() + + ALIGN_SIZE(sizeof(Query_cache_block)) + + ALIGN_SIZE(sizeof(Query_cache_result)), 1, 0); + if (new_result_block != 0) + { + has_moving = 1; + Query_cache_block *first_result = header->result(); + ulong new_len = (header->length() + + ALIGN_SIZE(sizeof(Query_cache_block)) + + ALIGN_SIZE(sizeof(Query_cache_result))); + if (new_result_block->length > + ALIGN_SIZE(new_len) + min_allocation_unit) + split_block(new_result_block, ALIGN_SIZE(new_len)); + BLOCK_LOCK_WR(block); + header->result(new_result_block); + new_result_block->type = Query_cache_block::RESULT; + new_result_block->n_tables = 0; + new_result_block->used = new_len; + + new_result_block->next = new_result_block->prev = new_result_block; + DBUG_PRINT("qcache", ("new block %lu/%lu (%lu)", + new_result_block->length, + new_result_block->used, + header->length())); + + Query_cache_result *new_result = new_result_block->result(); + new_result->parent(block); + byte *write_to = (byte*) new_result->data(); + Query_cache_block *result_block = first_result; + do + { + ulong len = (result_block->used - result_block->headers_len() - + ALIGN_SIZE(sizeof(Query_cache_result))); + DBUG_PRINT("loop", ("add block %lu/%lu (%lu)", + result_block->length, + result_block->used, + len)); + memcpy((char *) write_to, + (char*) result_block->result()->data(), + len); + write_to += len; + Query_cache_block *old_result_block = result_block; + result_block = result_block->next; + free_memory_block(old_result_block); + } while (result_block != first_result); + BLOCK_UNLOCK_WR(block); + } + } + block = block->next; + } while ( block != queries_blocks ); + } + STRUCT_UNLOCK(&structure_guard_mutex); + DBUG_RETURN(has_moving); +} + + +uint Query_cache::filename_2_table_key (char *key, const char *path, + uint32 *db_length) +{ + char tablename[FN_REFLEN+2], *filename, *dbname; + DBUG_ENTER("Query_cache::filename_2_table_key"); + + /* Safety if filename didn't have a directory name */ + tablename[0]= FN_LIBCHAR; + tablename[1]= FN_LIBCHAR; + /* Convert filename to this OS's format in tablename */ + fn_format(tablename + 2, path, "", "", MY_REPLACE_EXT); + filename= tablename + dirname_length(tablename + 2) + 2; + /* Find start of databasename */ + for (dbname= filename - 2 ; dbname[-1] != FN_LIBCHAR ; dbname--) ; + *db_length= (filename - dbname) - 1; + DBUG_PRINT("qcache", ("table '%-.*s.%s'", *db_length, dbname, filename)); + + DBUG_RETURN((uint) (strmov(strmake(key, dbname, *db_length) + 1, + filename) -key) + 1); +} + + +/**************************************************************************** + Functions to be used when debugging +****************************************************************************/ + +#if defined(DBUG_OFF) && !defined(USE_QUERY_CACHE_INTEGRITY_CHECK) + +void wreck(uint line, const char *message) {} +void bins_dump() {} +void cache_dump() {} +void queries_dump() {} +void tables_dump() {} +my_bool check_integrity(bool not_locked) { return 0; } +my_bool in_list(Query_cache_block * root, Query_cache_block * point, + const char *name) { return 0;} +my_bool in_blocks(Query_cache_block * point) { return 0; } + +#else + +void Query_cache::wreck(uint line, const char *message) +{ + THD *thd=current_thd; + DBUG_ENTER("Query_cache::wreck"); + query_cache_size = 0; + if (*message) + DBUG_PRINT("error", (" %s", message)); + DBUG_PRINT("warning", ("==================================")); + DBUG_PRINT("warning", ("%5d QUERY CACHE WRECK => DISABLED",line)); + DBUG_PRINT("warning", ("==================================")); + if (thd) + thd->killed = 1; + cache_dump(); + /* check_integrity(0); */ /* Can't call it here because of locks */ + bins_dump(); + DBUG_VOID_RETURN; +} + + +void Query_cache::bins_dump() +{ + uint i; + + if ( !initialized ) + { + DBUG_PRINT("qcache", ("Query Cache not initialized")); + return; + } + + DBUG_PRINT("qcache", ("mem_bin_num=%u, mem_bin_steps=%u", + mem_bin_num, mem_bin_steps)); + DBUG_PRINT("qcache", ("-------------------------")); + DBUG_PRINT("qcache", (" size idx step")); + DBUG_PRINT("qcache", ("-------------------------")); + for (i=0; i < mem_bin_steps; i++) + { + DBUG_PRINT("qcache", ("%10lu %3d %10lu", steps[i].size, steps[i].idx, + steps[i].increment)); + } + DBUG_PRINT("qcache", ("-------------------------")); + DBUG_PRINT("qcache", (" size num")); + DBUG_PRINT("qcache", ("-------------------------")); + for (i=0; i < mem_bin_num; i++) + { + DBUG_PRINT("qcache", ("%10lu %3d 0x%lx", bins[i].size, bins[i].number, + (ulong)&(bins[i]))); + if (bins[i].free_blocks) + { + Query_cache_block *block = bins[i].free_blocks; + do{ + DBUG_PRINT("qcache", ("\\-- %lu 0x%lx 0x%lx 0x%lx 0x%lx 0x%lx", + block->length, (ulong)block, + (ulong)block->next, (ulong)block->prev, + (ulong)block->pnext, (ulong)block->pprev)); + block = block->next; + } while ( block != bins[i].free_blocks ); + } + } + DBUG_PRINT("qcache", ("-------------------------")); +} + + +void Query_cache::cache_dump() +{ + + if ( !initialized ) + { + DBUG_PRINT("qcache", ("Query Cache not initialized")); + return; + } + + DBUG_PRINT("qcache", ("-------------------------------------")); + DBUG_PRINT("qcache", (" length used t nt")); + DBUG_PRINT("qcache", ("-------------------------------------")); + Query_cache_block *i = first_block; + do + { + DBUG_PRINT("qcache", + ("%10lu %10lu %1d %2d 0x%lx 0x%lx 0x%lx 0x%lx 0x%lx", + i->length, i->used, (int)i->type, + i->n_tables, (ulong)i, + (ulong)i->next, (ulong)i->prev, (ulong)i->pnext, + (ulong)i->pprev)); + i = i->pnext; + } while ( i != first_block ); + DBUG_PRINT("qcache", ("-------------------------------------")); +} + + +void Query_cache::queries_dump() +{ + + if ( !initialized ) + { + DBUG_PRINT("qcache", ("Query Cache not initialized")); + return; + } + + DBUG_PRINT("qcache", ("------------------")); + DBUG_PRINT("qcache", (" QUERIES")); + DBUG_PRINT("qcache", ("------------------")); + if (queries_blocks != 0) + { + Query_cache_block *block = queries_blocks; + do + { + uint len; + char *str = (char*) query_cache_query_get_key((byte*) block, &len, 0); + len--; // Point at flags + uint flags = (uint) (uchar) str[len]; + str[len]=0; + DBUG_PRINT("qcache", ("%u (%u,%u) '%s' '%s'", + ((flags & QUERY_CACHE_CLIENT_LONG_FLAG_MASK)? 1:0), + (flags & QUERY_CACHE_CHARSET_CONVERT_MASK), len, + str,strend(str)+1)); + DBUG_PRINT("qcache", ("-b- 0x%lx 0x%lx 0x%lx 0x%lx 0x%lx", (ulong) block, + (ulong) block->next, (ulong) block->prev, + (ulong)block->pnext, (ulong)block->pprev)); + str[len]=(char) flags; + for (TABLE_COUNTER_TYPE t = 0; t < block->n_tables; t++) + { + Query_cache_table *table = block->table(t)->parent; + DBUG_PRINT("qcache", ("-t- '%s' '%s'", table->db(), table->table())); + } + Query_cache_query *header = block->query(); + if (header->result()) + { + Query_cache_block *result_block = header->result(); + Query_cache_block *result_beg = result_block; + do + { + DBUG_PRINT("qcache", ("-r- %u %lu/%lu 0x%lx 0x%lx 0x%lx 0x%lx 0x%lx", + (uint) result_block->type, + result_block->length, result_block->used, + (ulong) result_block, + (ulong) result_block->next, + (ulong) result_block->prev, + (ulong) result_block->pnext, + (ulong) result_block->pprev)); + result_block = result_block->next; + } while ( result_block != result_beg ); + } + } while ((block=block->next) != queries_blocks); + } + else + { + DBUG_PRINT("qcache", ("no queries in list")); + } + DBUG_PRINT("qcache", ("------------------")); +} + + +void Query_cache::tables_dump() +{ + + if ( !initialized ) + { + DBUG_PRINT("qcache", ("Query Cache not initialized")); + return; + } + + DBUG_PRINT("qcache", ("--------------------")); + DBUG_PRINT("qcache", ("TABLES")); + DBUG_PRINT("qcache", ("--------------------")); + if (tables_blocks != 0) + { + Query_cache_block *table_block = tables_blocks; + do + { + Query_cache_table *table = table_block->table(); + DBUG_PRINT("qcache", ("'%s' '%s'", table->db(), table->table())); + table_block = table_block->next; + } while ( table_block != tables_blocks); + } + else + DBUG_PRINT("qcache", ("no tables in list")); + DBUG_PRINT("qcache", ("--------------------")); +} + + +my_bool Query_cache::check_integrity(bool not_locked) +{ + my_bool result = 0; + uint i; + DBUG_ENTER("check_integrity"); + + if (query_cache_size == 0) + { + DBUG_PRINT("qcache", ("Query Cache not initialized")); + DBUG_RETURN(0); + } + if (!not_locked) + STRUCT_LOCK(&structure_guard_mutex); + + if (hash_check(&queries)) + { + DBUG_PRINT("error", ("queries hash is damaged")); + result = 1; + } + + if (hash_check(&tables)) + { + DBUG_PRINT("error", ("tables hash is damaged")); + result = 1; + } + + DBUG_PRINT("qcache", ("physical address check ...")); + ulong free=0, used=0; + Query_cache_block * block = first_block; + do + { + DBUG_PRINT("qcache", ("block 0x%lx, type %u...", + (ulong) block, (uint) block->type)); + // Check memory allocation + if (block->pnext == first_block) // Is it last block? + { + if ( ((byte*)block) + block->length != + ((byte*)first_block) + query_cache_size ) + { + DBUG_PRINT("error", + ("block 0x%lx, type %u, ended at 0x%lx, but cache ended at 0x%lx", + (ulong) block, (uint) block->type, + (ulong) (((byte*)block) + block->length), + (ulong) (((byte*)first_block) + query_cache_size))); + result = 1; + } + } + else + if (((byte*)block) + block->length != ((byte*)block->pnext)) + { + DBUG_PRINT("error", + ("block 0x%lx, type %u, ended at 0x%lx, but next block begining at 0x%lx", + (ulong) block, (uint) block->type, + (ulong) (((byte*)block) + block->length), + (ulong) ((byte*)block->pnext))); + } + if (block->type == Query_cache_block::FREE) + free+=block->length; + else + used+=block->length; + switch(block->type) { + case Query_cache_block::FREE: + { + Query_cache_memory_bin *bin = *((Query_cache_memory_bin **) + block->data()); + //is it correct pointer? + if ( ((byte*)bin) < ((byte*)bins) || + ((byte*)bin) >= ((byte*)first_block)) + { + DBUG_PRINT("error", + ("free block 0x%lx have bin pointer 0x%lx beyaond of bins array bounds [0x%lx,0x%lx]", + (ulong) block, + (ulong) bin, + (ulong) bins, + (ulong) first_block)); + result = 1; + } + else + { + int idx = (((byte*)bin) - ((byte*)bins)) / + sizeof(Query_cache_memory_bin); + if (in_list(bins[idx].free_blocks, block, "free memory")) + result = 1; + } + break; + } + case Query_cache_block::TABLE: + if (in_list(tables_blocks, block, "tables")) + result = 1; + if (in_table_list(block->table(0), block->table(0), "table list root")) + result = 1; + break; + case Query_cache_block::QUERY: + { + if (in_list(queries_blocks, block, "query")) + result = 1; + for (TABLE_COUNTER_TYPE j=0; j < block->n_tables; j++) + { + Query_cache_block_table *block_table = block->table(j); + Query_cache_block_table *block_table_root = + (Query_cache_block_table *) + (((byte*)block_table->parent) - + ALIGN_SIZE(sizeof(Query_cache_block_table))); + + if (in_table_list(block_table, block_table_root, "table list")) + result = 1; + } + break; + } + case Query_cache_block::RES_INCOMPLETE: + // This type of block can be not lincked yet (in multithread environment) + break; + case Query_cache_block::RES_BEG: + case Query_cache_block::RES_CONT: + case Query_cache_block::RESULT: + { + Query_cache_block * query_block = block->result()->parent(); + if ( ((byte*)query_block) < ((byte*)first_block) || + ((byte*)query_block) >= (((byte*)first_block) + query_cache_size)) + { + DBUG_PRINT("error", + ("result block 0x%lx have query block pointer 0x%lx beyaond of block pool bounds [0x%lx,0x%lx]", + (ulong) block, + (ulong) query_block, + (ulong) first_block, + (ulong) (((byte*)first_block) + query_cache_size))); + result = 1; + } + else + { + BLOCK_LOCK_RD(query_block); + if (in_list(queries_blocks, query_block, "query from results")) + result = 1; + if (in_list(query_block->query()->result(), block, + "results")) + result = 1; + BLOCK_UNLOCK_RD(query_block); + } + break; + } + default: + DBUG_PRINT("error", + ("block 0x%lx have incorrect type %u", + block, block->type)); + result = 1; + } + + block = block->pnext; + } while (block != first_block); + + if (used + free != query_cache_size) + { + DBUG_PRINT("error", + ("used memory (%lu) + free memory (%lu) != query_cache_size (%lu)", + used, free, query_cache_size)); + result = 1; + } + + if (free != free_memory) + { + DBUG_PRINT("error", + ("free memory (%lu) != free_memory (%lu)", + free, free_memory)); + result = 1; + } + + DBUG_PRINT("qcache", ("check queries ...")); + if ((block = queries_blocks)) + { + do + { + DBUG_PRINT("qcache", ("block 0x%lx, type %u...", + (ulong) block, (uint) block->type)); + uint length; + byte *key = query_cache_query_get_key((byte*) block, &length, 0); + gptr val = hash_search(&queries, key, length); + if (((gptr)block) != val) + { + DBUG_PRINT("error", ("block 0x%lx found in queries hash like 0x%lx", + (ulong) block, (ulong) val)); + } + if (in_blocks(block)) + result = 1; + Query_cache_block * results = block->query()->result(); + if (results) + { + Query_cache_block * result_block = results; + do + { + DBUG_PRINT("qcache", ("block 0x%lx, type %u...", + (ulong) block, (uint) block->type)); + if (in_blocks(result_block)) + result = 1; + + result_block = result_block->next; + } while (result_block != results); + } + block = block->next; + } while (block != queries_blocks); + } + + DBUG_PRINT("qcache", ("check tables ...")); + if ((block = tables_blocks)) + { + do + { + DBUG_PRINT("qcache", ("block 0x%lx, type %u...", + (ulong) block, (uint) block->type)); + uint length; + byte *key = query_cache_table_get_key((byte*) block, &length, 0); + gptr val = hash_search(&tables, key, length); + if (((gptr)block) != val) + { + DBUG_PRINT("error", ("block 0x%lx found in tables hash like 0x%lx", + (ulong) block, (ulong) val)); + } + + if (in_blocks(block)) + result = 1; + block=block->next; + } while (block != tables_blocks); + } + + DBUG_PRINT("qcache", ("check free blocks")); + for (i = 0; i < mem_bin_num; i++) + { + if ((block = bins[i].free_blocks)) + { + uint count = 0; + do + { + DBUG_PRINT("qcache", ("block 0x%lx, type %u...", + (ulong) block, (uint) block->type)); + if (in_blocks(block)) + result = 1; + + count++; + block=block->next; + } while (block != bins[i].free_blocks); + if (count != bins[i].number) + { + DBUG_PRINT("error", ("bin[%d].number is %d, but bin have %d blocks", + bins[i].number, count)); + result = 1; + } + } + } + DBUG_ASSERT(result == 0); + if (!not_locked) + STRUCT_UNLOCK(&structure_guard_mutex); + DBUG_RETURN(result); +} + + +my_bool Query_cache::in_blocks(Query_cache_block * point) +{ + my_bool result = 0; + Query_cache_block *block = point; + //back + do + { + if (block->pprev->pnext != block) + { + DBUG_PRINT("error", + ("block 0x%lx in physical list is incorrect linked, prev block 0x%lx refered as next to 0x%lx (check from 0x%lx)", + (ulong) block, (ulong) block->pprev, + (ulong) block->pprev->pnext, + (ulong) point)); + //back trace + for(; block != point; block = block->pnext) + DBUG_PRINT("error", ("back trace 0x%lx", (ulong) block)); + result = 1; + goto err1; + } + block = block->pprev; + } while (block != first_block && block != point); + if (block != first_block) + { + DBUG_PRINT("error", + ("block 0x%lx (0x%lx<-->0x%lx) not owned by pysical list", + (ulong) block, (ulong) block->pprev, (ulong )block->pnext)); + return 1; + } + +err1: + //forward + block = point; + do + { + if (block->pnext->pprev != block) + { + DBUG_PRINT("error", + ("block 0x%lx in physicel list is incorrect linked, next block 0x%lx refered as prev to 0x%lx (check from 0x%lx)", + (ulong) block, (ulong) block->pnext, + (ulong) block->pnext->pprev, + (ulong) point)); + //back trace + for(; block != point; block = block->pprev) + DBUG_PRINT("error", ("back trace 0x%lx", (ulong) block)); + result = 1; + goto err2; + } + block = block->pnext; + } while (block != first_block); +err2: + return result; +} + + +my_bool Query_cache::in_list(Query_cache_block * root, + Query_cache_block * point, + const char *name) +{ + my_bool result = 0; + Query_cache_block *block = point; + //back + do + { + if (block->prev->next != block) + { + DBUG_PRINT("error", + ("block 0x%lx in list '%s' 0x%lx is incorrect linked, prev block 0x%lx refered as next to 0x%lx (check from 0x%lx)", + (ulong) block, name, (ulong) root, (ulong) block->prev, + (ulong) block->prev->next, + (ulong) point)); + //back trace + for(; block != point; block = block->next) + DBUG_PRINT("error", ("back trace 0x%lx", (ulong) block)); + result = 1; + goto err1; + } + block = block->prev; + } while (block != root && block != point); + if (block != root) + { + DBUG_PRINT("error", + ("block 0x%lx (0x%lx<-->0x%lx) not owned by list '%s' 0x%lx", + (ulong) block, + (ulong) block->prev, (ulong) block->next, + name, (ulong) root)); + return 1; + } +err1: + // forward + block = point; + do + { + if (block->next->prev != block) + { + DBUG_PRINT("error", + ("block 0x%lx in list '%s' 0x%lx is incorrect linked, next block 0x%lx refered as prev to 0x%lx (check from 0x%lx)", + (ulong) block, name, (ulong) root, (ulong) block->next, + (ulong) block->next->prev, + (ulong) point)); + //back trace + for (; block != point; block = block->prev) + DBUG_PRINT("error", ("back trace 0x%lx", (ulong) block)); + result = 1; + goto err2; + } + block = block->next; + } while (block != root); +err2: + return result; +} + +void dump_node(Query_cache_block_table * node, + const char * call, const char * descr) +{ + DBUG_PRINT("qcache", ("%s: %s: node: 0x%lx", call, descr, (ulong) node)); + DBUG_PRINT("qcache", ("%s: %s: node block: 0x%lx", + call, descr, (ulong) node->block())); + DBUG_PRINT("qcache", ("%s: %s: next: 0x%lx", call, descr, + (ulong) node->next)); + DBUG_PRINT("qcache", ("%s: %s: prev: 0x%lx", call, descr, + (ulong) node->prev)); +} + +my_bool Query_cache::in_table_list(Query_cache_block_table * root, + Query_cache_block_table * point, + const char *name) +{ + my_bool result = 0; + Query_cache_block_table *table = point; + dump_node(root, name, "parameter root"); + //back + do + { + dump_node(table, name, "list element << "); + if (table->prev->next != table) + { + DBUG_PRINT("error", + ("table 0x%lx(0x%lx) in list '%s' 0x%lx(0x%lx) is incorrect linked, prev table 0x%lx(0x%lx) refered as next to 0x%lx(0x%lx) (check from 0x%lx(0x%lx))", + (ulong) table, (ulong) table->block(), name, + (ulong) root, (ulong) root->block(), + (ulong) table->prev, (ulong) table->prev->block(), + (ulong) table->prev->next, + (ulong) table->prev->next->block(), + (ulong) point, (ulong) point->block())); + //back trace + for(; table != point; table = table->next) + DBUG_PRINT("error", ("back trace 0x%lx(0x%lx)", + (ulong) table, (ulong) table->block())); + result = 1; + goto err1; + } + table = table->prev; + } while (table != root && table != point); + if (table != root) + { + DBUG_PRINT("error", + ("table 0x%lx(0x%lx) (0x%lx(0x%lx)<-->0x%lx(0x%lx)) not owned by list '%s' 0x%lx(0x%lx)", + (ulong) table, (ulong) table->block(), + (ulong) table->prev, (ulong) table->prev->block(), + (ulong) table->next, (ulong) table->next->block(), + name, (ulong) root, (ulong) root->block())); + return 1; + } +err1: + // forward + table = point; + do + { + dump_node(table, name, "list element >> "); + if (table->next->prev != table) + { + DBUG_PRINT("error", + ("table 0x%lx(0x%lx) in list '%s' 0x%lx(0x%lx) is incorrect linked, next table 0x%lx(0x%lx) refered as prev to 0x%lx(0x%lx) (check from 0x%lx(0x%lx))", + (ulong) table, (ulong) table->block(), + name, (ulong) root, (ulong) root->block(), + (ulong) table->next, (ulong) table->next->block(), + (ulong) table->next->prev, + (ulong) table->next->prev->block(), + (ulong) point, (ulong) point->block())); + //back trace + for (; table != point; table = table->prev) + DBUG_PRINT("error", ("back trace 0x%lx(0x%lx)", + (ulong) table, (ulong) table->block())); + result = 1; + goto err2; + } + table = table->next; + } while (table != root); +err2: + return result; +} + +#endif /* DBUG_OFF */ + +#endif /*HAVE_QUERY_CACHE*/ diff --git a/sql/sql_cache.h b/sql/sql_cache.h new file mode 100644 index 00000000000..81ea80669b8 --- /dev/null +++ b/sql/sql_cache.h @@ -0,0 +1,410 @@ +/* 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 */ + +#ifndef _SQL_CACHE_H +#define _SQL_CACHE_H + +/* Query cache */ + +/* + Can't create new free memory block if unused memory in block less + then QUERY_CACHE_MIN_ALLOCATION_UNIT. + if QUERY_CACHE_MIN_ALLOCATION_UNIT == 0 then + QUERY_CACHE_MIN_ALLOCATION_UNIT choosed automaticaly +*/ +#define QUERY_CACHE_MIN_ALLOCATION_UNIT 512 + +/* inittial size of hashes */ +#define QUERY_CACHE_DEF_QUERY_HASH_SIZE 1024 +#define QUERY_CACHE_DEF_TABLE_HASH_SIZE 1024 + +/* minimal result data size when data allocated */ +#define QUERY_CACHE_MIN_RESULT_DATA_SIZE 1024*4 + +/* + start estimation of first result block size only when number of queries + bigger then: +*/ +#define QUERY_CACHE_MIN_ESTIMATED_QUERIES_NUMBER 3 + + + +/* memory bins size spacing (see at Query_cache::init_cache (sql_cache.cc)) */ +#define QUERY_CACHE_MEM_BIN_FIRST_STEP_PWR2 4 +#define QUERY_CACHE_MEM_BIN_STEP_PWR2 2 +#define QUERY_CACHE_MEM_BIN_PARTS_INC 1 +#define QUERY_CACHE_MEM_BIN_PARTS_MUL 1.2 +#define QUERY_CACHE_MEM_BIN_SPC_LIM_PWR2 3 + +/* how many free blocks check when finding most suitable before other 'end' + of list of free blocks */ +#define QUERY_CACHE_MEM_BIN_TRY 5 + +/* query flags masks */ +#define QUERY_CACHE_CLIENT_LONG_FLAG_MASK 0x80 +#define QUERY_CACHE_CHARSET_CONVERT_MASK 0x7F + +/* packing parameters */ +#define QUERY_CACHE_PACK_ITERATION 2 +#define QUERY_CACHE_PACK_LIMIT (512*1024L) + +#define TABLE_COUNTER_TYPE uint8 + +#include <my_semaphore.h> + +struct Query_cache_block; +struct Query_cache_block_table; +struct Query_cache_table; +struct Query_cache_query; +struct Query_cache_result; +class Query_cache; + + +struct Query_cache_block_table +{ + TABLE_COUNTER_TYPE n; // numbr in table (from 0) + Query_cache_block_table *next, *prev; + Query_cache_table *parent; + inline Query_cache_block *block(); +}; + + +struct Query_cache_block +{ + enum block_type {FREE, QUERY, RESULT, RES_CONT, RES_BEG, + RES_INCOMPLETE, TABLE, INCOMPLETE}; + + ulong length; // length of all block + ulong used; // length of data + /* + Not used **pprev, **prev because really needed access to pervious block: + *pprev to join free blocks + *prev to access to opposite side of list in cyclic sorted list + */ + Query_cache_block *pnext,*pprev, // physical next/previous block + *next,*prev; // logical next/previous block + block_type type; + TABLE_COUNTER_TYPE n_tables; // number of tables in query + + inline my_bool is_free(void) { return type == FREE; } + void init(ulong length); + void destroy(); + inline uint headers_len(); + inline gptr data(void); + inline Query_cache_query *query(); + inline Query_cache_table *table(); + inline Query_cache_result *result(); + inline Query_cache_block_table *table(TABLE_COUNTER_TYPE n); +}; + + +struct Query_cache_query +{ + ulonglong limit_found_rows; + Query_cache_block *res; + NET *wri; + ulong len; + sem_t lock; // R/W lock of block + pthread_mutex_t clients_guard; + uint clients; + + inline void init_n_lock(); + void unlock_n_destroy(); + inline ulonglong found_rows() { return limit_found_rows; } + inline void found_rows(ulonglong rows) { limit_found_rows = rows; } + inline Query_cache_block *result() { return res; } + inline void result(Query_cache_block *p) { res=p; } + inline NET *writer() { return wri; } + inline void writer(NET *p) { wri=p; } + inline ulong length() { return len; } + inline ulong add(ulong packet_len) { return(len += packet_len); } + inline void length(ulong length) { len = length; } + inline gptr query() + { + return (gptr)(((byte*)this)+ + ALIGN_SIZE(sizeof(Query_cache_query))); + } + void lock_writing(); + void lock_reading(); + my_bool try_lock_writing(); + void unlock_writing(); + void unlock_reading(); + static byte *cache_key(const byte *record, uint *length, my_bool not_used); +}; + + +struct Query_cache_table +{ + char *tbl; + + inline char *db() { return (char *) data(); } + inline char *table() { return tbl; } + inline void table(char *table) { tbl = table; } + inline gptr data() + { + return (gptr)(((byte*)this)+ + ALIGN_SIZE(sizeof(Query_cache_table))); + } +}; + +struct Query_cache_result +{ + Query_cache_block *query; + + inline gptr data() + { + return (gptr)(((byte*) this)+ + ALIGN_SIZE(sizeof(Query_cache_result))); + } + /* data_continue (if not whole packet contained by this block) */ + inline Query_cache_block *parent() { return query; } + inline void parent (Query_cache_block *p) { query=p; } +}; + + +extern "C" { + byte *query_cache_query_get_key(const byte *record, uint *length, + my_bool not_used); + byte *query_cache_table_get_key(const byte *record, uint *length, + my_bool not_used); + void query_cache_insert(THD *thd, const char *packet, ulong length); + void query_cache_end_of_result(THD *thd); + void query_cache_abort(THD *thd); + void query_cache_invalidate_by_MyISAM_filename(const char* filename); +} + +struct Query_cache_memory_bin +{ +#ifndef DBUG_OFF + ulong size; +#endif + uint number; + Query_cache_block *free_blocks; + + inline void init(ulong size) + { +#ifndef DBUG_OFF + this->size = size; +#endif + number = 0; + free_blocks = 0; + } +}; + +struct Query_cache_memory_bin_step +{ + ulong size; + ulong increment; + uint idx; + inline void init(ulong size, uint idx, ulong increment) + { + this->size = size; + this->idx = idx; + this->increment = increment; + } +}; + +class Query_cache +{ +public: + /* Info */ + ulong query_cache_size, query_cache_limit; + /* statistics */ + ulong free_memory, queries_in_cache, hits, inserts, refused, + free_memory_blocks, total_blocks; + +protected: + /* + The following mutex is locked when searching or changing global + query, tables lists or hashes. When we are operating inside the + query structure we locked an internal query block mutex. + LOCK SEQUENCE (to prevent deadlocks): + 1. structure_guard_mutex + 2. query block (for operation inside query (query block/results)) + */ + pthread_mutex_t structure_guard_mutex; + byte *cache; // cache memory + Query_cache_block *first_block; // physical location block list + Query_cache_block *queries_blocks; // query list (LIFO) + Query_cache_block *tables_blocks; + + Query_cache_memory_bin *bins; // free block lists + Query_cache_memory_bin_step *steps; // bins spacing info + HASH queries, tables; + /* options */ + ulong min_allocation_unit, min_result_data_size; + uint def_query_hash_size, def_table_hash_size; + uint mem_bin_num, mem_bin_steps; // See at init_cache & find_bin + + my_bool initialized; + + /* Exclude/include from cyclic double linked list */ + static void double_linked_list_exclude(Query_cache_block *point, + Query_cache_block **list_pointer); + static void double_linked_list_simple_include(Query_cache_block *point, + Query_cache_block ** + list_pointer); + static void double_linked_list_join(Query_cache_block *head_tail, + Query_cache_block *tail_head); + + /* Table key generation */ + static uint filename_2_table_key (char *key, const char *filename, + uint32 *db_langth); + + /* The following functions require that structure_guard_mutex is locked */ + void flush_cache(); + my_bool free_old_query(); + void free_query(Query_cache_block *point); + my_bool allocate_data_chain(Query_cache_block **result_block, + ulong data_len, + Query_cache_block *query_block, + my_bool first_block); + void invalidate_table(TABLE_LIST *table); + void invalidate_table(TABLE *table); + void invalidate_table(byte *key, uint32 key_length); + void invalidate_table(Query_cache_block *table_block); + my_bool register_all_tables(Query_cache_block *block, + TABLE_LIST *tables_used, + TABLE_COUNTER_TYPE tables); + my_bool insert_table(uint key_len, char *key, + Query_cache_block_table *node, + uint32 db_length); + void unlink_table(Query_cache_block_table *node); + Query_cache_block *get_free_block (ulong len, my_bool not_less, + ulong min); + void free_memory_block(Query_cache_block *point); + void split_block(Query_cache_block *block, ulong len); + Query_cache_block *join_free_blocks(Query_cache_block *first_block, + Query_cache_block *block_in_list); + my_bool append_next_free_block(Query_cache_block *block, + ulong add_size); + void exclude_from_free_memory_list(Query_cache_block *free_block); + void insert_into_free_memory_list(Query_cache_block *new_block); + my_bool move_by_type(byte **border, Query_cache_block **before, + ulong *gap, Query_cache_block *i); + uint find_bin(ulong size); + void move_to_query_list_end(Query_cache_block *block); + void insert_into_free_memory_sorted_list(Query_cache_block *new_block, + Query_cache_block **list); + void pack_cache(); + void relink(Query_cache_block *oblock, + Query_cache_block *nblock, + Query_cache_block *next, + Query_cache_block *prev, + Query_cache_block *pnext, + Query_cache_block *pprev); + my_bool join_results(ulong join_limit); + + /* + Following function control structure_guard_mutex + by themself or don't need structure_guard_mutex + */ + void init(); + ulong init_cache(); + void make_disabled(); + void free_cache(my_bool destruction); + Query_cache_block *write_block_data(ulong data_len, gptr data, + ulong header_len, + Query_cache_block::block_type type, + TABLE_COUNTER_TYPE ntab = 0, + my_bool under_guard=0); + my_bool append_result_data(Query_cache_block **result, + ulong data_len, gptr data, + Query_cache_block *parent); + my_bool write_result_data(Query_cache_block **result, + ulong data_len, gptr data, + Query_cache_block *parent, + Query_cache_block::block_type + type=Query_cache_block::RESULT); + inline ulong get_min_first_result_data_size(); + inline ulong get_min_append_result_data_size(); + Query_cache_block *allocate_block(ulong len, my_bool not_less, + ulong min, + my_bool under_guard=0); + /* + If query is cacheable return number tables in query + (query without tables not cached) + */ + TABLE_COUNTER_TYPE is_cacheable(THD *thd, uint32 query_len, char *query, + LEX *lex, TABLE_LIST *tables_used); + public: + + Query_cache(ulong query_cache_limit = ULONG_MAX, + ulong min_allocation_unit = QUERY_CACHE_MIN_ALLOCATION_UNIT, + ulong min_result_data_size = QUERY_CACHE_MIN_RESULT_DATA_SIZE, + uint def_query_hash_size = QUERY_CACHE_DEF_QUERY_HASH_SIZE, + uint def_table_hash_size = QUERY_CACHE_DEF_TABLE_HASH_SIZE); + + /* resize query cache (return real query size, 0 if disabled) */ + ulong resize(ulong query_cache_size); + inline void result_size_limit(ulong limit){query_cache_limit=limit;} + + /* register query in cache */ + void store_query(THD *thd, TABLE_LIST *used_tables); + + /* + Check if the query is in the cache and if this is true send the + data to client. + */ + int send_result_to_client(THD *thd, char *query, uint query_length); + + /* Remove all queries that uses any of the listed following tables */ + void invalidate(THD* thd, TABLE_LIST *tables_used, + my_bool using_transactions); + void invalidate(CHANGED_TABLE_LIST *tables_used); + void invalidate(THD* thd, TABLE *table, my_bool using_transactions); + + /* Remove all queries that uses any of the tables in following database */ + void invalidate(char *db); + + /* Remove all queries that uses any of the listed following table */ + void invalidate_by_MyISAM_filename(const char *filename); + + void flush(); + void pack(ulong join_limit = QUERY_CACHE_PACK_LIMIT, + uint iteration_limit = QUERY_CACHE_PACK_ITERATION); + + void destroy(); + + friend void query_cache_insert(NET *net, const char *packet, ulong length); + friend void query_cache_end_of_result(NET *net); + friend void query_cache_abort(NET *net); + + /* + The following functions are only used when debugging + We don't protect these with ifndef DEBUG_OFF to not have to recompile + everything if we want to add checks of the cache at some places. + */ + void wreck(uint line, const char *message); + void bins_dump(); + void cache_dump(); + void queries_dump(); + void tables_dump(); + my_bool check_integrity(bool not_locked); + my_bool in_list(Query_cache_block * root, Query_cache_block * point, + const char *name); + my_bool in_table_list(Query_cache_block_table * root, + Query_cache_block_table * point, + const char *name); + my_bool in_blocks(Query_cache_block * point); +}; + +extern Query_cache query_cache; +void query_cache_insert(NET *net, const char *packet, ulong length); +void query_cache_end_of_result(NET *net); +void query_cache_abort(NET *net); + +#endif diff --git a/sql/sql_class.cc b/sql/sql_class.cc index ace7c291ed3..c332181b410 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.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 */ @@ -34,6 +34,8 @@ #ifdef __WIN__ #include <io.h> #endif +#include <mysys_err.h> +#include <assert.h> /***************************************************************************** ** Instansiate templates @@ -49,6 +51,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 /**************************************************************************** @@ -80,34 +84,36 @@ THD::THD():user_time(0),fatal_error(0),last_insert_id_used(0), global_read_lock(0),bootstrap(0) { host=user=priv_user=db=query=ip=0; + host_or_ip="unknown ip"; locked=killed=count_cuted_fields=some_tables_deleted=no_errors=password= - query_start_used=0; - query_length=col_access=0; + query_start_used=safe_to_cache_query=0; + db_length=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; - gemini_spin_retries=0; cuted_fields=sent_row_count=0L; start_time=(time_t) 0; current_linfo = 0; slave_thread = 0; slave_proxy_id = 0; - last_nx_table = last_nx_db = 0; + file_id = 0; cond_count=0; convert_set=0; mysys_var=0; +#ifndef DBUG_OFF + dbug_sentry=THD_SENTRY_MAGIC; +#endif net.vio=0; ull=0; system_thread=cleanup_done=0; + transaction.changed_tables = 0; #ifdef __WIN__ real_id = 0; #endif -#ifdef HAVE_GEMINI_DB - bzero((char *)&gemini, sizeof(gemini)); -#endif #ifdef SIGNAL_WITH_VIO_CLOSE active_vio = 0; pthread_mutex_init(&active_vio_lock, MY_MUTEX_INIT_FAST); @@ -117,9 +123,16 @@ THD::THD():user_time(0),fatal_error(0),last_insert_id_used(0), proc_info="login"; where="field list"; server_id = ::server_id; - server_status=SERVER_STATUS_AUTOCOMMIT; + slave_net = 0; + log_pos = 0; + server_status= SERVER_STATUS_AUTOCOMMIT; update_lock_default= low_priority_updates ? TL_WRITE_LOW_PRIORITY : TL_WRITE; - options=thd_startup_options; + options= thd_startup_options; +#ifdef HAVE_QUERY_CACHE + query_cache_type= (byte) query_cache_startup_type; +#else + query_cache_type= 0; //Safety +#endif sql_mode=(uint) opt_sql_mode; inactive_timeout=net_wait_timeout; open_options=ha_open_options; @@ -133,6 +146,8 @@ THD::THD():user_time(0),fatal_error(0),last_insert_id_used(0), /* Initialize sub structures */ bzero((char*) &mem_root,sizeof(mem_root)); + bzero((char*) &transaction.mem_root,sizeof(transaction.mem_root)); + user_connect=(USER_CONN *)0; hash_init(&user_vars, USER_VARS_HASH_SIZE, 0, 0, (hash_get_key) get_var_key, (void (*)(void*)) free_var,0); @@ -160,6 +175,11 @@ void THD::cleanup(void) 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) @@ -174,6 +194,7 @@ void THD::cleanup(void) THD::~THD() { + THD_CHECK_SENTRY(this); DBUG_ENTER("~THD()"); /* Close connection */ if (net.vio) @@ -184,12 +205,7 @@ THD::~THD() if (!cleanup_done) cleanup(); if (global_read_lock) - { - pthread_mutex_lock(&LOCK_open); - ::global_read_lock--; - pthread_cond_broadcast(&COND_refresh); - pthread_mutex_unlock(&LOCK_open); - } + unlock_global_read_lock(this); if (ull) { pthread_mutex_lock(&LOCK_user_locks); @@ -199,7 +215,7 @@ THD::~THD() hash_free(&user_vars); DBUG_PRINT("info", ("freeing host")); - + if (host != localhost) // If not pointer to constant safeFree(host); if (user != delayed_user) @@ -207,17 +223,23 @@ THD::~THD() safeFree(db); safeFree(ip); free_root(&mem_root,MYF(0)); + free_root(&transaction.mem_root,MYF(0)); mysys_var=0; // Safety (shouldn't be needed) #ifdef SIGNAL_WITH_VIO_CLOSE pthread_mutex_destroy(&active_vio_lock); +#endif +#ifndef DBUG_OFF + dbug_sentry = THD_SENTRY_GONE; #endif DBUG_VOID_RETURN; } -void THD::prepare_to_die() +void THD::awake(bool prepare_to_die) { + THD_CHECK_SENTRY(this); + if (prepare_to_die) + killed = 1; thr_alarm_kill(real_id); - killed = 1; #ifdef SIGNAL_WITH_VIO_CLOSE close_active_vio(); #endif @@ -226,6 +248,10 @@ void THD::prepare_to_die() pthread_mutex_lock(&mysys_var->mutex); if (!system_thread) // Don't abort locks mysys_var->abort=1; + // this broadcast could be up in the air if the victim thread + // exits the cond in the time between read and broadcast, but that is + // ok since all we want to do is to make the victim thread get out + // of waiting on current_cond if (mysys_var->current_cond) { pthread_mutex_lock(mysys_var->current_mutex); @@ -246,6 +272,88 @@ bool THD::store_globals() my_pthread_setspecific_ptr(THR_NET, &net)); } +/* routings to adding tables to list of changed in transaction tables */ + +inline static void list_include(CHANGED_TABLE_LIST** prev, + CHANGED_TABLE_LIST* curr, + CHANGED_TABLE_LIST* new_table) +{ + if (new_table) + { + *prev = new_table; + (*prev)->next = curr; + } +} + +/* add table to list of changed in transaction tables */ +void THD::add_changed_table(TABLE *table) +{ + DBUG_ENTER("THD::add_changed_table (table)"); + + DBUG_ASSERT((options & (OPTION_NOT_AUTO_COMMIT | OPTION_BEGIN)) && + table->file->has_transactions()); + + CHANGED_TABLE_LIST** prev = &transaction.changed_tables; + CHANGED_TABLE_LIST* curr = transaction.changed_tables; + + for(; curr; prev = &(curr->next), curr = curr->next) + { + int cmp = (long)curr->key_length - (long)table->key_length; + if (cmp < 0) + { + list_include(prev, curr, changed_table_dup(table)); + DBUG_PRINT("info", + ("key_length %u %u", table->key_length, (*prev)->key_length)); + DBUG_VOID_RETURN; + } + else if (cmp == 0) + { + cmp = memcmp(curr->key ,table->table_cache_key, curr->key_length); + if (cmp < 0) + { + list_include(prev, curr, changed_table_dup(table)); + DBUG_PRINT("info", + ("key_length %u %u", table->key_length, (*prev)->key_length)); + DBUG_VOID_RETURN; + } + else if (cmp == 0) + { + DBUG_PRINT("info", ("already in list")); + DBUG_VOID_RETURN; + } + } + } + *prev = changed_table_dup(table); + DBUG_PRINT("info", ("key_length %u %u", table->key_length, (*prev)->key_length)); + DBUG_VOID_RETURN; +} + +CHANGED_TABLE_LIST* THD::changed_table_dup(TABLE *table) +{ + CHANGED_TABLE_LIST* new_table = + (CHANGED_TABLE_LIST*) trans_alloc(ALIGN_SIZE(sizeof(CHANGED_TABLE_LIST))+ + table->key_length + 1); + if (!new_table) + { + my_error(EE_OUTOFMEMORY, MYF(ME_BELL), + ALIGN_SIZE(sizeof(TABLE_LIST)) + table->key_length + 1); + killed= 1; + return 0; + } + + new_table->key = (char *) (((byte*)new_table)+ + ALIGN_SIZE(sizeof(CHANGED_TABLE_LIST))); + new_table->next = 0; + new_table->key_length = table->key_length; + uint32 db_len = ((new_table->table_name = + ::strmake(new_table->key, table->table_cache_key, + table->key_length) + 1) - new_table->key); + ::memcpy(new_table->key + db_len, table->table_cache_key + db_len, + table->key_length - db_len); + return new_table; +} + + /***************************************************************************** ** Functions to provide a interface to select results *****************************************************************************/ @@ -277,7 +385,7 @@ bool select_send::send_fields(List<Item> &list,uint flag) bool select_send::send_data(List<Item> &items) { - List_iterator<Item> li(items); + List_iterator_fast<Item> li(items); String *packet= &thd->packet; DBUG_ENTER("send_data"); @@ -290,7 +398,7 @@ bool select_send::send_data(List<Item> &items) Item *item; while ((item=li++)) { - if (item->send(packet)) + if (item->send(thd, packet)) { packet->free(); // Free used my_error(ER_OUT_OF_RESOURCES,MYF(0)); @@ -302,12 +410,6 @@ bool select_send::send_data(List<Item> &items) DBUG_RETURN(error); } - -void select_send::send_error(uint errcode,const char *err) -{ - ::send_error(&thd->net,errcode,err); -} - bool select_send::send_eof() { /* Unlock tables before sending packet to gain some speed */ @@ -370,7 +472,7 @@ select_export::prepare(List<Item> &list) } /* Check if there is any blobs in data */ { - List_iterator<Item> li(list); + List_iterator_fast<Item> li(list); Item *item; while ((item=li++)) { @@ -417,7 +519,7 @@ bool select_export::send_data(List<Item> &items) Item *item; char *buff_ptr=buff; uint used_length=0,items_left=items.elements; - List_iterator<Item> li(items); + List_iterator_fast<Item> li(items); if (my_b_write(&cache,(byte*) exchange->line_start->ptr(), exchange->line_start->length())) @@ -610,7 +712,7 @@ select_dump::prepare(List<Item> &list __attribute__((unused))) bool select_dump::send_data(List<Item> &items) { - List_iterator<Item> li(items); + List_iterator_fast<Item> li(items); char buff[MAX_FIELD_WIDTH]; String tmp(buff,sizeof(buff)),*res; tmp.length(0); diff --git a/sql/sql_class.h b/sql/sql_class.h index d9497907926..31da5c53bf8 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -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 */ @@ -21,10 +21,14 @@ #pragma interface /* gcc class implementation */ #endif +// TODO: create log.h and move all the log header stuff there + 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 }; @@ -38,6 +42,8 @@ enum enum_log_type { LOG_CLOSED, LOG_NORMAL, LOG_NEW, LOG_BIN }; #define LOG_INFO_FATAL -7 #define LOG_INFO_IN_USE -8 +struct st_relay_log_info; + typedef struct st_log_info { char log_file_name[FN_REFLEN]; @@ -49,6 +55,7 @@ typedef struct st_log_info ~st_log_info() { pthread_mutex_destroy(&lock);} } LOG_INFO; +class Log_event; class MYSQL_LOG { private: @@ -61,39 +68,82 @@ 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; + uint file_id; // current file sequence number for load data infile + // binary logging 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 + enum cache_type io_cache_type; + bool need_start_event; + pthread_cond_t update_cond; + bool no_auto_events; // for relay binlog + ulonglong bytes_written; + friend class Log_event; public: MYSQL_LOG(); ~MYSQL_LOG(); pthread_mutex_t* get_log_lock() { return &LOCK_log; } + void reset_bytes_written() + { + bytes_written = 0; + } + void harvest_bytes_written(ulonglong* counter) + { +#ifndef DBUG_OFF + char buf1[22],buf2[22]; +#endif + DBUG_ENTER("harvest_bytes_written"); + (*counter)+=bytes_written; + DBUG_PRINT("info",("counter=%s,bytes_written=%s", llstr(*counter,buf1), + llstr(bytes_written,buf2))); + bytes_written=0; + DBUG_VOID_RETURN; + } + IO_CACHE* get_log_file() { return &log_file; } + void signal_update() { pthread_cond_broadcast(&update_cond);} + void wait_for_update(THD* thd); + void set_need_start_event() { need_start_event = 1; } void set_index_file_name(const char* index_file_name = 0); - void init(enum_log_type log_type_arg); + void init(enum_log_type log_type_arg, + enum cache_type io_cache_type_arg = WRITE_CACHE, + bool no_auto_events_arg = 0); void open(const char *log_name,enum_log_type log_type, - const char *new_name=0); + const char *new_name, enum cache_type io_cache_type_arg, + bool no_auto_events_arg); void new_file(bool inside_mutex = 0); bool open_index(int options); void close_index(); - bool write(THD *thd, enum enum_server_command command,const char *format,...); + bool write(THD *thd, enum enum_server_command command, + const char *format,...); bool write(THD *thd, const char *query, uint query_length, time_t query_start=0); - bool write(Query_log_event* event_info); // binary log write - bool write(Load_log_event* event_info); + bool write(Log_event* event_info); // binary log write bool write(THD *thd, IO_CACHE *cache); + + /* + v stands for vector + invoked as appendv(buf1,len1,buf2,len2,...,bufn,lenn,0) + */ + bool appendv(const char* buf,uint len,...); + bool append(Log_event* ev); + int generate_new_name(char *new_name,const char *old_name); void make_log_name(char* buf, const char* log_ident); bool is_active(const char* log_file_name); int purge_logs(THD* thd, const char* to_log); + int purge_first_log(struct st_relay_log_info* rli); + int reset_logs(THD* thd); void close(bool exiting = 0); // if we are exiting, we also want to close the // index file // iterating through the log index file - int find_first_log(LOG_INFO* linfo, const char* log_name); - int find_next_log(LOG_INFO* linfo); + int find_first_log(LOG_INFO* linfo, const char* log_name, + bool need_mutex=1); + int find_next_log(LOG_INFO* linfo, bool need_mutex=1); int get_current_log(LOG_INFO* linfo); + uint next_file_id(); inline bool is_open() { return log_type != LOG_CLOSED; } char* get_index_fname() { return index_file_name;} @@ -114,14 +164,16 @@ class CONVERT void convert_array(const uchar *mapping,uchar *buff,uint length); public: const char *name; - CONVERT(const char *name_par,uchar *from_par,uchar *to_par) - :from_map(from_par),to_map(to_par),name(name_par) {} + uint numb; + CONVERT(const char *name_par,uchar *from_par,uchar *to_par, uint number) + :from_map(from_par),to_map(to_par),name(name_par),numb(number) {} friend CONVERT *get_convert_set(const char *name_ptr); inline void convert(char *a,uint length) { convert_array(from_map, (uchar*) a,length); } bool store(String *, const char *,uint); + inline uint number() { return numb; } }; typedef struct st_copy_info { @@ -165,11 +217,13 @@ class Key :public Sql_alloc { public: enum Keytype { PRIMARY, UNIQUE, MULTIPLE, FULLTEXT }; enum Keytype type; + enum ha_key_alg algorithm; List<key_part_spec> columns; const char *Name; Key(enum Keytype type_par,const char *name_arg,List<key_part_spec> &cols) - :type(type_par), columns(cols),Name(name_arg) {} + :type(type_par), algorithm(HA_KEY_ALG_UNDEF), columns(cols), Name(name_arg) + {} ~Key() {} const char *name() { return Name; } }; @@ -214,49 +268,106 @@ public: }; -/**************************************************************************** -** every connection is handle by a thread with a THD -****************************************************************************/ - class delayed_insert; +#define THD_SENTRY_MAGIC 0xfeedd1ff +#define THD_SENTRY_GONE 0xdeadbeef + +#define THD_CHECK_SENTRY(thd) DBUG_ASSERT(thd->dbug_sentry == THD_SENTRY_MAGIC) + +/* For each client connection we create a separate thread with THD serving as + a thread/connection descriptor */ + class THD :public ilink { public: - NET net; - LEX lex; - MEM_ROOT mem_root; - HASH user_vars; - String packet; /* Room for 1 row */ - struct sockaddr_in remote; - struct rand_struct rand; + NET net; // client connection descriptor + LEX lex; // parse tree descriptor + MEM_ROOT mem_root; // 1 command-life memory allocation pool + HASH user_vars; // hash for user variables + String packet; // dynamic string buffer used for network I/O + struct sockaddr_in remote; // client socket address + struct rand_struct rand; // used for authentication + + /* query points to the current query, + thread_stack is a pointer to the stack frame of handle_one_connection(), + which is called first in the thread for handling a client + */ char *query,*thread_stack; + /* + host - host of the client + user - user of the client, set to NULL until the user has been read from + the connection + priv_user - not sure why we have it, but it is set to "boot" when we run + with --bootstrap + db - currently selected database + ip - client IP + */ + char *host,*user,*priv_user,*db,*ip; - const char *proc_info; + /* proc_info points to a string that will show in the Info column of + SHOW PROCESSLIST output + host_or_ip points to host if host is available, otherwise points to ip + */ + const char *proc_info, *host_or_ip; + + /* + client_capabilities has flags describing what the client can do + sql_mode determines if certain non-standard SQL behaviour should be + enabled + max_packet_length - supposed to be maximum packet length the client + can handle, but it currently appears to be assigned but never used + except for one debugging statement + */ uint client_capabilities,sql_mode,max_packet_length; + + /* + master_access - privillege descriptor mask for system threads + db_access - privillege descriptor mask for regular threads + */ uint master_access,db_access; - TABLE *open_tables,*temporary_tables; + + /* + open_tables - list of regular tables in use by this thread + temporary_tables - list of temp tables in use by this thread + handler_tables - list of tables that were opened with HANDLER OPEN + and are still in use by this thread + */ + TABLE *open_tables,*temporary_tables, *handler_tables; + // TODO: document the variables below MYSQL_LOCK *lock,*locked_tables; ULL *ull; +#ifndef DBUG_OFF + uint dbug_sentry; // watch out for memory corruption +#endif struct st_my_thread_var *mysys_var; enum enum_server_command command; uint32 server_id; + uint32 file_id; // for LOAD DATA INFILE 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 + 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; delayed_insert *di; struct st_transactions { IO_CACHE trans_log; - THD_TRANS all; /* Trans since BEGIN WORK */ - THD_TRANS stmt; /* Trans for current statement */ + THD_TRANS all; // Trans since BEGIN WORK + THD_TRANS stmt; // Trans for current statement uint bdb_lock_count; + + /* + Tables changed in transaction (that must be invalidated in query cache). + List contain only transactional tables, that not invalidated in query + cache (instead of full list of changed in transaction tables). + */ + CHANGED_TABLE_LIST* changed_tables; + MEM_ROOT mem_root; // Transaction-life memory allocation pool + void cleanup() + { + changed_tables = 0; + free_root(&mem_root,MYF(MY_KEEP_PREALLOC)); + } } transaction; -#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,33 +377,44 @@ public: Vio* active_vio; pthread_mutex_t active_vio_lock; #endif - ulonglong next_insert_id,last_insert_id,current_insert_id; - ha_rows select_limit,offset_limit,default_select_limit,cuted_fields, - max_join_size, sent_row_count, examined_row_count; - table_map used_tables; - ulong query_id,version, inactive_timeout,options,thread_id; - ulong gemini_spin_retries; - long dbug_thread_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; + USER_CONN *user_connect; + ulong query_id,version, inactive_timeout,options,thread_id; + long dbug_thread_id; pthread_t real_id; - uint current_tablenr,tmp_table,cond_count,col_access,query_length; - uint server_status,open_options; + uint current_tablenr,tmp_table,cond_count,col_access; + uint server_status,open_options; + uint32 query_length; + uint32 db_length; enum_tx_isolation tx_isolation, session_tx_isolation; char scramble[9]; + uint8 query_cache_type; // type of query cache processing bool slave_thread; bool set_query_id,locked,count_cuted_fields,some_tables_deleted; bool no_errors, allow_sum_func, password, fatal_error; bool query_start_used,last_insert_id_used,insert_id_used; bool system_thread,in_lock_tables,global_read_lock; bool query_error, bootstrap, cleanup_done; + bool safe_to_cache_query; bool volatile killed; + /* + If we do a purge of binary logs, log index info of the threads + that are currently reading it needs to be adjusted. To do that + each thread that is using LOG_INFO needs to adjust the pointer to it + */ LOG_INFO* current_linfo; - // if we do a purge of binary logs, log index info of the threads - // that are currently reading it needs to be adjusted. To do that - // each thread that is using LOG_INFO needs to adjust the pointer to it - - ulong slave_proxy_id; // in slave thread we need to know in behalf of which - // thread the query is being run to replicate temp tables properly - + /* + In slave thread we need to know in behalf of which + thread the query is being run to replicate temp tables properly + */ + ulong slave_proxy_id; + NET* slave_net; // network connection from slave -> m. + my_off_t log_pos; + THD(); ~THD(); void cleanup(void); @@ -321,7 +443,7 @@ public: pthread_mutex_unlock(&active_vio_lock); } #endif - void prepare_to_die(); + void awake(bool prepare_to_die); inline const char* enter_cond(pthread_cond_t *cond, pthread_mutex_t* mutex, const char* msg) { @@ -355,16 +477,18 @@ public: } return last_insert_id; } + inline ulonglong found_rows(void) + { + return limit_found_rows; + } inline bool active_transaction() { #ifdef USING_TRANSACTIONS return (transaction.all.bdb_tid != 0 || - transaction.all.innodb_active_trans != 0 || - transaction.all.gemini_tid != 0); + transaction.all.innodb_active_trans != 0); #else return 0; #endif - } inline gptr alloc(unsigned int size) { return alloc_root(&mem_root,size); } inline gptr calloc(unsigned int size) @@ -376,11 +500,29 @@ public: } inline char *strdup(const char *str) { return strdup_root(&mem_root,str); } - inline char *memdup(const char *str, unsigned int size) + inline char *strmake(const char *str, uint size) + { return strmake_root(&mem_root,str,size); } + inline char *memdup(const char *str, uint size) { return memdup_root(&mem_root,str,size); } + inline char *memdup_w_gap(const char *str, uint size, uint gap) + { + gptr ptr; + if ((ptr=alloc_root(&mem_root,size+gap))) + memcpy(ptr,str,size); + return ptr; + } + inline gptr trans_alloc(unsigned int size) + { + return alloc_root(&transaction.mem_root,size); + } + void add_changed_table(TABLE *table); + CHANGED_TABLE_LIST * changed_table_dup(TABLE *table); }; - +/* + Used to hold information about file and file structure in exchainge + via non-DB file (...INTO OUTFILE..., ...LOAD DATA...) +*/ class sql_exchange :public Sql_alloc { public: @@ -399,6 +541,10 @@ public: ** This is used to get result from a select */ +class JOIN; + +void send_error(NET *net,uint sql_errno=0, const char *err=0); + class select_result :public Sql_alloc { protected: THD *thd; @@ -408,7 +554,11 @@ public: virtual int prepare(List<Item> &list) { return 0; } virtual bool send_fields(List<Item> &list,uint flag)=0; virtual bool send_data(List<Item> &items)=0; - virtual void send_error(uint errcode,const char *err)=0; + virtual void initialize_tables (JOIN *join=0) {} + virtual void send_error(uint errcode,const char *err) + { + ::send_error(&thd->net,errcode,err); + } virtual bool send_eof()=0; virtual void abort() {} }; @@ -419,7 +569,6 @@ public: select_send() {} bool send_fields(List<Item> &list,uint flag); bool send_data(List<Item> &items); - void send_error(uint errcode,const char *err); bool send_eof(); }; @@ -443,6 +592,7 @@ public: bool send_eof(); }; + class select_dump :public select_result { sql_exchange *exchange; File file; @@ -463,29 +613,28 @@ public: class select_insert :public select_result { - protected: + public: TABLE *table; List<Item> *fields; - uint save_time_stamp; ulonglong last_insert_id; COPY_INFO info; + uint save_time_stamp; -public: select_insert(TABLE *table_par,List<Item> *fields_par,enum_duplicates duplic) - :table(table_par),fields(fields_par), save_time_stamp(0),last_insert_id(0) - { - bzero((char*) &info,sizeof(info)); - info.handle_duplicates=duplic; - } + :table(table_par),fields(fields_par), last_insert_id(0), save_time_stamp(0) { + bzero((char*) &info,sizeof(info)); + info.handle_duplicates=duplic; + } ~select_insert(); int prepare(List<Item> &list); - bool send_fields(List<Item> &list, - uint flag) { return 0; } + bool send_fields(List<Item> &list, uint flag) + { return 0; } bool send_data(List<Item> &items); void send_error(uint errcode,const char *err); bool send_eof(); }; + class select_create: public select_insert { ORDER *group; const char *db; @@ -512,6 +661,22 @@ public: void abort(); }; +class select_union :public select_result { + public: + TABLE *table; + COPY_INFO info; + uint save_time_stamp; + + select_union(TABLE *table_par); + ~select_union(); + int prepare(List<Item> &list); + bool send_fields(List<Item> &list, uint flag) + { return 0; } + bool send_data(List<Item> &items); + bool send_eof(); + bool flush(); +}; + /* Structs used when sorting */ typedef struct st_sort_field { @@ -561,3 +726,89 @@ 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; + byte *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_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, not_trans_safe; + 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 initialize_tables (JOIN *join); + void send_error(uint errcode,const char *err); + int do_deletes (bool from_send_error); + bool send_eof(); + }; + + class multi_update : public select_result { + TABLE_LIST *update_tables, *table_being_updated; +// Unique **tempfiles; + COPY_INFO *infos; + TABLE **tmp_tables; + THD *thd; + ha_rows updated, found; + List<Item> fields; + List <Item> **fields_by_tables; + thr_lock_type lock_option; + enum enum_duplicates dupl; + uint num_of_tables, num_fields, num_updated, *save_time_stamps, *field_sequence; + int error; + bool do_update, not_trans_safe; + public: + multi_update(THD *thd_arg, TABLE_LIST *ut, List<Item> &fs, + enum enum_duplicates handle_duplicates, + thr_lock_type lock_option_arg, uint num); + ~multi_update(); + int prepare(List<Item> &list); + bool send_fields(List<Item> &list, + uint flag) { return 0; } + bool send_data(List<Item> &items); + void initialize_tables (JOIN *join); + void send_error(uint errcode,const char *err); + int do_updates (bool from_send_error); + bool send_eof(); + }; + diff --git a/sql/sql_crypt.cc b/sql/sql_crypt.cc index 371d63f8c73..f2e4a8934be 100644 --- a/sql/sql_crypt.cc +++ b/sql/sql_crypt.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 */ diff --git a/sql/sql_crypt.h b/sql/sql_crypt.h index b3a9d54133f..1b27f0a4d27 100644 --- a/sql/sql_crypt.h +++ b/sql/sql_crypt.h @@ -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 */ diff --git a/sql/sql_db.cc b/sql/sql_db.cc index 451a48042b5..07e7c5e6680 100644 --- a/sql/sql_db.cc +++ b/sql/sql_db.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 */ @@ -25,45 +25,28 @@ #include <direct.h> #endif -static long mysql_rm_known_files(THD *thd, MY_DIR *dirp, const char *path, +static long mysql_rm_known_files(THD *thd, MY_DIR *dirp, + const char *db, const char *path, uint level); /* 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, bool silent) { 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)); - VOID(pthread_mutex_lock(&LOCK_open)); // do not create database if another thread is holding read lock - if (global_read_lock) + if (wait_if_global_read_lock(thd,0)) { - if (thd->global_read_lock) - { - net_printf(&thd->net, ER_CREATE_DB_WITH_READ_LOCK); - VOID(pthread_mutex_unlock(&LOCK_open)); - goto exit; - } - while (global_read_lock && ! thd->killed) - { - (void) pthread_cond_wait(&COND_refresh,&LOCK_open); - } - - if (thd->killed) - { - net_printf(&thd->net, ER_SERVER_SHUTDOWN); - VOID(pthread_mutex_unlock(&LOCK_open)); - goto exit; - } - + error= -1; + goto exit2; } - - VOID(pthread_mutex_unlock(&LOCK_open)); /* Check directory */ (void)sprintf(path,"%s/%s", mysql_data_home, db); @@ -73,7 +56,8 @@ 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); + my_error(ER_DB_CREATE_EXISTS,MYF(0),db); + error = -1; goto exit; } result = 0; @@ -83,137 +67,148 @@ 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); + my_error(ER_CANT_CREATE_DB,MYF(0),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 (!silent) { - 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: + start_waiting_global_read_lock(thd); +exit2: VOID(pthread_mutex_unlock(&LOCK_mysql_create_db)); - DBUG_VOID_RETURN; + DBUG_RETURN(error); } -const char *del_exts[]= -{".frm",".ISM",".ISD",".ISM",".HSH",".DAT",".MRG",".MYI",".MYD", ".db", ".BAK", NullS}; +const char *del_exts[]= {".frm", ".BAK", ".TMD", NullS}; static TYPELIB deletable_extentions= {array_elements(del_exts)-1,"del_exts", del_exts}; +const char *known_exts[]= +{".ISM",".ISD",".ISM",".MRG",".MYI",".MYD",".db",NullS}; +static TYPELIB known_extentions= +{array_elements(known_exts)-1,"known_exts", known_exts}; -/* db-name is already validated when we come here */ +/* + Drop all tables in a database. + + db-name is already validated when we come here + 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 +*/ -void mysql_rm_db(THD *thd,char *db,bool if_exists) + +int mysql_rm_db(THD *thd,char *db,bool if_exists, bool silent) { long deleted=0; + int error = 0; char path[FN_REFLEN+16]; MY_DIR *dirp; DBUG_ENTER("mysql_rm_db"); VOID(pthread_mutex_lock(&LOCK_mysql_create_db)); - VOID(pthread_mutex_lock(&LOCK_open)); // do not drop database if another thread is holding read lock - if (global_read_lock) + if (wait_if_global_read_lock(thd,0)) { - if (thd->global_read_lock) - { - net_printf(&thd->net, ER_DROP_DB_WITH_READ_LOCK); - goto exit; - } - while (global_read_lock && ! thd->killed) - { - (void) pthread_cond_wait(&COND_refresh,&LOCK_open); - } - - if (thd->killed) - { - net_printf(&thd->net, ER_SERVER_SHUTDOWN); - goto exit; - } + error= -1; + goto exit2; } (void) sprintf(path,"%s/%s",mysql_data_home,db); unpack_dirname(path,path); // Convert if not unix /* See if the directory exists */ - if (!(dirp = my_dir(path,MYF(MY_WME | MY_DONT_SORT)))) + if (!(dirp = my_dir(path,MYF(MY_DONT_SORT)))) { if (!if_exists) - net_printf(&thd->net,ER_DB_DROP_EXISTS,db); - else + { + error= -1; + my_error(ER_DB_DROP_EXISTS,MYF(0),db); + } + else if (!silent) send_ok(&thd->net,0); goto exit; } remove_db_from_cache(db); - if ((deleted=mysql_rm_known_files(thd, dirp, path,0)) >= 0) + error = -1; + if ((deleted=mysql_rm_known_files(thd, dirp, db, path,0)) >= 0 && thd) { - if (!thd->query) - { - thd->query = path; - thd->query_length = (uint) (strxmov(path,"drop database ", db, NullS)- - path); - } - mysql_update_log.write(thd, thd->query, thd->query_length); - if (mysql_bin_log.is_open()) + ha_drop_database(path); + query_cache_invalidate1(db); + if (!silent) { - 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; + if (!thd->query) + { + thd->query = path; + thd->query_length = (uint) (strxmov(path,"drop 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,(ulong) deleted); } - send_ok(&thd->net,(ulong) deleted); + error = 0; } exit: - VOID(pthread_mutex_unlock(&LOCK_open)); + start_waiting_global_read_lock(thd); +exit2: VOID(pthread_mutex_unlock(&LOCK_mysql_create_db)); - /* It seems MySQL may call this function when there still are queries - running on tables of the database. Since InnoDB waits until the - queries have ended, we have to call ha_drop_database outside - the above two mutexes to avoid deadlocks. */ - - ha_drop_database(path); - - DBUG_VOID_RETURN; + DBUG_RETURN(error); } /* Removes files with known extensions plus all found subdirectories that are 2 digits (raid directories). + thd MUST be set when calling this function! */ -static long mysql_rm_known_files(THD *thd, MY_DIR *dirp, const char *org_path, - uint level) +static long mysql_rm_known_files(THD *thd, MY_DIR *dirp, const char *db, + const char *org_path, uint level) { long deleted=0; ulong found_other_files=0; char filePath[FN_REFLEN]; + TABLE_LIST *tot_list=0, **tot_list_next; DBUG_ENTER("mysql_rm_known_files"); DBUG_PRINT("enter",("path: %s", org_path)); - /* remove all files with known extensions */ + + tot_list_next= &tot_list; for (uint idx=2 ; idx < (uint) dirp->number_off_files && !thd->killed ; @@ -233,7 +228,7 @@ static long mysql_rm_known_files(THD *thd, MY_DIR *dirp, const char *org_path, if ((new_dirp = my_dir(newpath,MYF(MY_DONT_SORT)))) { DBUG_PRINT("my",("New subdir found: %s", newpath)); - if ((mysql_rm_known_files(thd,new_dirp,newpath,1)) < 0) + if ((mysql_rm_known_files(thd, new_dirp, NullS, newpath,1)) < 0) { my_dirend(dirp); DBUG_RETURN(-1); @@ -243,27 +238,44 @@ static long mysql_rm_known_files(THD *thd, MY_DIR *dirp, const char *org_path, } if (find_type(fn_ext(file->name),&deletable_extentions,1+2) <= 0) { - found_other_files++; + if (find_type(fn_ext(file->name),&known_extentions,1+2) <= 0) + found_other_files++; continue; } strxmov(filePath,org_path,"/",file->name,NullS); - unpack_filename(filePath,filePath); - if (my_delete(filePath,MYF(MY_WME))) + if (db && !my_strcasecmp(fn_ext(file->name), reg_ext)) { - net_printf(&thd->net,ER_DB_DROP_DELETE,filePath,my_error); - my_dirend(dirp); - DBUG_RETURN(-1); + /* Drop the table nicely */ + *fn_ext(file->name)=0; // Remove extension + TABLE_LIST *table_list=(TABLE_LIST*) + thd->calloc(sizeof(*table_list)+ strlen(db)+strlen(file->name)+2); + if (!table_list) + { + my_dirend(dirp); + DBUG_RETURN(-1); + } + table_list->db= (char*) (table_list+1); + strmov(table_list->real_name=strmov(table_list->db,db)+1, + file->name); + /* Link into list */ + (*tot_list_next)= table_list; + tot_list_next= &table_list->next; } - deleted++; - } + else + { + if (my_delete_with_symlink(filePath,MYF(MY_WME))) + { + my_dirend(dirp); + DBUG_RETURN(-1); + } + deleted++; + } + } my_dirend(dirp); - if (thd->killed) - { - send_error(&thd->net,ER_SERVER_SHUTDOWN); + if (thd->killed || (tot_list && mysql_rm_table_part2(thd, tot_list, 1, 1))) DBUG_RETURN(-1); - } /* If the directory is a symbolic link, remove the link first, then @@ -275,7 +287,7 @@ static long mysql_rm_known_files(THD *thd, MY_DIR *dirp, const char *org_path, char *path=unpack_filename(tmp_path,org_path); #ifdef HAVE_READLINK int error; - + /* Remove end FN_LIBCHAR as this causes problem on Linux in readlink */ pos=strend(path); if (pos > path && pos[-1] == FN_LIBCHAR) @@ -290,21 +302,21 @@ 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); DBUG_RETURN(-1); } /* Delete directory symbolic link pointed at */ path= filePath; } #endif - /* Remove last FN_LIBCHAR to not cause a probelm on OS/2 */ + /* Remove last FN_LIBCHAR to not cause a problem on OS/2 */ pos=strend(path); + if (pos > path && pos[-1] == FN_LIBCHAR) *--pos=0; /* 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); + my_error(ER_DB_DROP_RMDIR, MYF(0), path, errno); DBUG_RETURN(-1); } } @@ -314,25 +326,25 @@ static long mysql_rm_known_files(THD *thd, MY_DIR *dirp, const char *org_path, bool mysql_change_db(THD *thd,const char *name) { - int length; + int length, db_length; char *dbname=my_strdup((char*) name,MYF(MY_WME)); char path[FN_REFLEN]; uint db_access; DBUG_ENTER("mysql_change_db"); - if (!dbname || !(length=stripp_sp(dbname))) + if (!dbname || !(db_length=stripp_sp(dbname))) { x_free(dbname); /* purecov: inspected */ send_error(&thd->net,ER_NO_DB_ERROR); /* purecov: inspected */ DBUG_RETURN(1); /* purecov: inspected */ } - if ((length > NAME_LEN) || check_db_name(dbname)) + if ((db_length > NAME_LEN) || check_db_name(dbname)) { net_printf(&thd->net,ER_WRONG_DB_NAME, dbname); x_free(dbname); DBUG_RETURN(1); } - DBUG_PRINT("general",("Use database: %s", dbname)); + DBUG_PRINT("info",("Use database: %s", dbname)); if (test_all_bits(thd->master_access,DB_ACLS)) db_access=DB_ACLS; else @@ -343,11 +355,11 @@ bool mysql_change_db(THD *thd,const char *name) { net_printf(&thd->net,ER_DBACCESS_DENIED_ERROR, thd->priv_user, - thd->host ? thd->host : thd->ip ? thd->ip : "unknown", + thd->host_or_ip, dbname); mysql_log.write(thd,COM_INIT_DB,ER(ER_DBACCESS_DENIED_ERROR), thd->priv_user, - thd->host ? thd->host : thd->ip ? thd->ip : "unknown", + thd->host_or_ip, dbname); my_free(dbname,MYF(0)); DBUG_RETURN(1); @@ -366,6 +378,7 @@ bool mysql_change_db(THD *thd,const char *name) send_ok(&thd->net); x_free(thd->db); thd->db=dbname; + thd->db_length=db_length; thd->db_access=db_access; DBUG_RETURN(0); } diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index 930e71d7678..9d1037a9dc7 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -1,4 +1,4 @@ -/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB +/* 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 @@ -15,118 +15,28 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -/* Delete of records */ - -#include "mysql_priv.h" -#include "ha_innobase.h" - /* - Optimize delete of all rows by doing a full generate of the table - This will work even if the .ISM and .ISD tables are destroyed -*/ - -int generate_table(THD *thd, TABLE_LIST *table_list, TABLE *locked_table) -{ - char path[FN_REFLEN]; - int error; - TABLE **table_ptr; - DBUG_ENTER("generate_table"); + Delete of records and truncate of tables. - thd->proc_info="generate_table"; + Multi-table deletes were introduced by Monty and Sinisa +*/ - if (global_read_lock) - { - if(thd->global_read_lock) - { - my_error(ER_TABLE_NOT_LOCKED_FOR_WRITE,MYF(0), - table_list->real_name); - DBUG_RETURN(-1); - } - pthread_mutex_lock(&LOCK_open); - while (global_read_lock && ! thd->killed || - thd->version != refresh_version) - { - (void) pthread_cond_wait(&COND_refresh,&LOCK_open); - } - pthread_mutex_unlock(&LOCK_open); - } - /* If it is a temporary table, close and regenerate it */ - if ((table_ptr=find_temporary_table(thd,table_list->db, - table_list->real_name))) - { - TABLE *table= *table_ptr; - HA_CREATE_INFO create_info; - table->file->info(HA_STATUS_AUTO | HA_STATUS_NO_LOCK); - bzero((char*) &create_info,sizeof(create_info)); - create_info.auto_increment_value= table->file->auto_increment_value; - db_type table_type=table->db_type; - - strmov(path,table->path); - *table_ptr= table->next; // Unlink table from list - close_temporary(table,0); - *fn_ext(path)=0; // Remove the .frm extension - ha_create_table(path, &create_info,1); - if ((error= (int) !(open_temporary_table(thd, path, table_list->db, - table_list->real_name, 1)))) - { - (void) rm_temporary_table(table_type, path); - } - } - else - { - (void) sprintf(path,"%s/%s/%s%s",mysql_data_home,table_list->db, - table_list->real_name,reg_ext); - fn_format(path,path,"","",4); - VOID(pthread_mutex_lock(&LOCK_open)); - if (locked_table) - mysql_lock_abort(thd,locked_table); // end threads waiting on lock - // close all copies in use - if (remove_table_from_cache(thd,table_list->db,table_list->real_name)) - { - if (!locked_table) - { - VOID(pthread_mutex_unlock(&LOCK_open)); - DBUG_RETURN(1); // We must get a lock on table - } - } - if (locked_table) - locked_table->file->extra(HA_EXTRA_FORCE_REOPEN); - if (thd->locked_tables) - close_data_tables(thd,table_list->db,table_list->real_name); - else - close_thread_tables(thd,1); - HA_CREATE_INFO create_info; - bzero((char*) &create_info,sizeof(create_info)); - *fn_ext(path)=0; // Remove the .frm extension - error= ha_create_table(path,&create_info,1) ? -1 : 0; - if (thd->locked_tables && reopen_tables(thd,1,0)) - error= -1; - VOID(pthread_mutex_unlock(&LOCK_open)); - } - if (!error) - { - 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); - } - send_ok(&thd->net); // This should return record count - } - DBUG_RETURN(error ? -1 : 0); -} +#include "mysql_priv.h" +#include "ha_innodb.h" +#include "sql_select.h" -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; - SQL_SELECT *select; + SQL_SELECT *select=0; READ_RECORD info; - bool using_limit=limit != HA_POS_ERROR; - bool use_generate_table,using_transactions; + bool using_limit=limit != HA_POS_ERROR; + bool using_transactions; + ha_rows deleted; DBUG_ENTER("mysql_delete"); if (!table_list->db) @@ -137,40 +47,39 @@ int mysql_delete(THD *thd,TABLE_LIST *table_list,COND *conds,ha_rows limit, DBUG_RETURN(1); } - use_generate_table= (!using_limit && !conds && - !(specialflag & - (SPECIAL_NO_NEW_FUNC | SPECIAL_SAFE_MODE)) && - !(thd->options & - (OPTION_NOT_AUTO_COMMIT | OPTION_BEGIN))); -#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 -#endif - if (use_generate_table && ! thd->open_tables) - { - error=generate_table(thd,table_list,(TABLE*) 0); - if (error <= 0) - DBUG_RETURN(error); // Error or ok - } - if (!(table = open_ltable(thd,table_list, - limit != HA_POS_ERROR ? TL_WRITE_LOW_PRIORITY : - lock_type))) + if (!(table = open_ltable(thd,table_list, lock_type))) DBUG_RETURN(-1); table->file->info(HA_STATUS_VARIABLE | HA_STATUS_NO_LOCK); thd->proc_info="init"; - if (use_generate_table) - DBUG_RETURN(generate_table(thd,table_list,table)); table->map=1; if (setup_conds(thd,table_list,&conds) || setup_ftfuncs(thd)) DBUG_RETURN(-1); + /* Test if the user wants to delete all rows */ + if (!using_limit && (!conds || conds->const_item()) && + !(specialflag & (SPECIAL_NO_NEW_FUNC | SPECIAL_SAFE_MODE))) + { + deleted= table->file->records; + if (!(error=table->file->delete_all_rows())) + { + error= -1; // ok + goto cleanup; + } + if (error != HA_ERR_WRONG_COMMAND) + { + table->file->print_error(error,MYF(0)); + error=0; + goto cleanup; + } + /* Handler didn't support fast delete; Delete rows one by one */ + } + table->used_keys=table->quick_keys=0; // Can't use 'only index' select=make_select(table,0,0,conds,&error); if (error) DBUG_RETURN(-1); if ((select && select->check_quick(test(thd->options & SQL_SAFE_UPDATES), - limit)) || + limit)) || !limit) { delete select; @@ -181,7 +90,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; @@ -189,11 +98,37 @@ int mysql_delete(THD *thd,TABLE_LIST *table_list,COND *conds,ha_rows limit, DBUG_RETURN(1); } } - (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); - ulong deleted=0L; + + 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); + deleted=0L; init_ftfuncs(thd,1); thd->proc_info="updating"; while (!(error=info.read_record(&info)) && !thd->killed) @@ -221,9 +156,11 @@ int mysql_delete(THD *thd,TABLE_LIST *table_list,COND *conds,ha_rows limit, } thd->proc_info="end"; end_read_record(&info); - (void) table->file->extra(HA_EXTRA_READCHECK); + free_io_cache(table); // Will not do any harm if (options & OPTION_QUICK) (void) table->file->extra(HA_EXTRA_NORMAL); + +cleanup: using_transactions=table->file->has_transactions(); if (deleted && (error <= 0 || !using_transactions)) { @@ -244,6 +181,10 @@ int mysql_delete(THD *thd,TABLE_LIST *table_list,COND *conds,ha_rows limit, mysql_unlock_tables(thd, thd->lock); thd->lock=0; } + if (deleted) + { + query_cache_invalidate3(thd, table_list, 1); + } delete select; if (error >= 0) // Fatal error send_error(&thd->net,thd->killed ? ER_SERVER_SHUTDOWN: 0); @@ -255,3 +196,383 @@ int mysql_delete(THD *thd,TABLE_LIST *table_list,COND *conds,ha_rows limit, DBUG_RETURN(0); } + +/*************************************************************************** +** delete multiple tables from join +***************************************************************************/ + +#define MEM_STRIP_BUF_SIZE sortbuff_size + +int refposcmp2(void* arg, const void *a,const void *b) +{ + return memcmp(a,b, *(int*) arg); +} + +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; + not_trans_safe=false; + tempfiles = (Unique **) sql_calloc(sizeof(Unique *) * (num_of_tables-1)); + + /* 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; + table->used_keys=0; + tempfiles[counter] = new Unique (refposcmp2, + (void *) &table->file->ref_length, + table->file->ref_length, + MEM_STRIP_BUF_SIZE); + } +} + + +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); +} + + +void +multi_delete::initialize_tables(JOIN *join) +{ + TABLE_LIST *walk; + table_map tables_to_delete_from=0; + for (walk= delete_tables ; walk ; walk=walk->next) + tables_to_delete_from|= walk->table->map; + + walk= delete_tables; + for (JOIN_TAB *tab=join->join_tab, *end=join->join_tab+join->tables; + tab < end; + tab++) + { + if (tab->table->map & tables_to_delete_from) + { + /* We are going to delete from this table */ + walk->table=tab->table; + walk=walk->next; + if (tab == join->join_tab) + tab->table->no_keyread=1; + if (!not_trans_safe && !tab->table->file->has_transactions()) + not_trans_safe=true; + } + } + init_ftfuncs(thd,1); +} + + +multi_delete::~multi_delete() +{ + for (table_being_deleted=delete_tables ; + table_being_deleted ; + table_being_deleted=table_being_deleted->next) + { + TABLE *t=table_being_deleted->table; + free_io_cache(t); // Alloced by unique + t->no_keyread=0; + } + + for (uint counter = 0; counter < num_of_tables-1; counter++) + { + if (tempfiles[counter]) + delete tempfiles[counter]; + } +} + + +bool multi_delete::send_data(List<Item> &values) +{ + int secure_counter= -1; + DBUG_ENTER("multi_delete::send_data"); + + 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]); + + if (secure_counter < 0) + { + /* If this is the table we are scanning */ + table->status|= STATUS_DELETED; + if (!(error=table->file->delete_row(table->record[0]))) + deleted++; + else + { + table->file->print_error(error,MYF(0)); + DBUG_RETURN(1); + } + } + else + { + error=tempfiles[secure_counter]->unique_add((char*) table->file->ref); + if (error) + { + error=-1; + DBUG_RETURN(1); + } + } + } + DBUG_RETURN(0); +} + +void multi_delete::send_error(uint errcode,const char *err) +{ + DBUG_ENTER("multi_delete::send_error"); + + /* First send error what ever it is ... */ + ::send_error(&thd->net,errcode,err); + + /* If nothing deleted return */ + if (!deleted) + DBUG_VOID_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) || !not_trans_safe) + ha_rollback_stmt(thd); + else if (do_delete) + { + VOID(do_deletes(1)); + } + DBUG_VOID_RETURN; +} + + +/* + Do delete from other tables. + Returns values: + 0 ok + 1 error +*/ + +int multi_delete::do_deletes(bool from_send_error) +{ + int error = 0, counter = 0; + + 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; + if (tempfiles[counter]->get(table)) + { + error=1; + break; + } + + READ_RECORD info; + init_read_record(&info,thd,table,NULL,0,0); + while (!(error=info.read_record(&info)) && + (!thd->killed || from_send_error || not_trans_safe)) + { + if ((error=table->file->delete_row(table->record[0]))) + { + table->file->print_error(error,MYF(0)); + break; + } + deleted++; + } + end_read_record(&info); + if (error == -1) // End of file + error = 0; + } + return error; +} + + +/* + return: 0 sucess + 1 error +*/ + +bool multi_delete::send_eof() +{ + thd->proc_info="deleting from reference tables"; + + /* Does deletes for the last n - 1 tables, returns 0 if ok */ + int error = do_deletes(0); // returns 0 if success + + /* reset used flags */ + thd->proc_info="end"; + if (error) + { + ::send_error(&thd->net); + return 1; + } + + /* + Write the SQL statement to the binlog if we deleted + rows and we succeeded, or also in an error case when there + was a non-transaction-safe table involved, since + modifications in it cannot be rolled back. + */ + if (deleted || not_trans_safe) + { + mysql_update_log.write(thd,thd->query,thd->query_length); + if (mysql_bin_log.is_open()) + { + Query_log_event qinfo(thd, thd->query); + if (mysql_bin_log.write(&qinfo) && + !not_trans_safe) + error=1; // Log write failed: roll back the SQL statement + } + /* Commit or rollback the current SQL statement */ + VOID(ha_autocommit_or_rollback(thd,error > 0)); + } + if (deleted) + { + query_cache_invalidate3(thd, delete_tables, 1); + } + ::send_ok(&thd->net,deleted); + return 0; +} + + +/*************************************************************************** +* TRUNCATE TABLE +****************************************************************************/ + +/* + Optimize delete of all rows by doing a full generate of the table + This will work even if the .ISM and .ISD tables are destroyed + + dont_send_ok should be set if: + - We should always wants to generate the table (even if the table type + normally can't safely do this. + - We don't want an ok to be sent to the end user. + - We don't want to log the truncate command + - If we want to have a name lock on the table on exit without errors. +*/ + +int mysql_truncate(THD *thd, TABLE_LIST *table_list, bool dont_send_ok) +{ + HA_CREATE_INFO create_info; + char path[FN_REFLEN]; + TABLE **table_ptr; + int error; + DBUG_ENTER("mysql_truncate"); + + /* If it is a temporary table, close and regenerate it */ + if (!dont_send_ok && (table_ptr=find_temporary_table(thd,table_list->db, + table_list->real_name))) + { + TABLE *table= *table_ptr; + HA_CREATE_INFO create_info; + table->file->info(HA_STATUS_AUTO | HA_STATUS_NO_LOCK); + bzero((char*) &create_info,sizeof(create_info)); + create_info.auto_increment_value= table->file->auto_increment_value; + db_type table_type=table->db_type; + + strmov(path,table->path); + *table_ptr= table->next; // Unlink table from list + close_temporary(table,0); + *fn_ext(path)=0; // Remove the .frm extension + ha_create_table(path, &create_info,1); + // We don't need to call invalidate() because this table is not in cache + if ((error= (int) !(open_temporary_table(thd, path, table_list->db, + table_list->real_name, 1)))) + (void) rm_temporary_table(table_type, path); + /* Sasha: if we return here we will not have binloged the truncation and + we will not send_ok() to the client. Yes, we do need better coverage + testing, this bug has been here for a few months :-). + */ + goto end; + } + + (void) sprintf(path,"%s/%s/%s%s",mysql_data_home,table_list->db, + table_list->real_name,reg_ext); + fn_format(path,path,"","",4); + + if (!dont_send_ok) + { + db_type table_type; + if ((table_type=get_table_type(path)) == DB_TYPE_UNKNOWN) + { + my_error(ER_NO_SUCH_TABLE, MYF(0), table_list->real_name); + DBUG_RETURN(-1); + } + if (!ha_supports_generate(table_type)) + { + /* Probably InnoDB table */ + DBUG_RETURN(mysql_delete(thd,table_list, (COND*) 0, (ORDER*) 0, + HA_POS_ERROR, TL_WRITE, 0)); + } + if (lock_and_wait_for_table_name(thd, table_list)) + DBUG_RETURN(-1); + } + + bzero((char*) &create_info,sizeof(create_info)); + *fn_ext(path)=0; // Remove the .frm extension + error= ha_create_table(path,&create_info,1) ? -1 : 0; + query_cache_invalidate3(thd, table_list, 0); +end: + if (!dont_send_ok) + { + if (!error) + { + 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); + } + send_ok(&thd->net); // This should return record count + } + unlock_table_name(thd, table_list); + } + else if (error) + unlock_table_name(thd, table_list); + DBUG_RETURN(error ? -1 : 0); +} diff --git a/sql/sql_do.cc b/sql/sql_do.cc index 57a6f88ed63..70124c2d796 100644 --- a/sql/sql_do.cc +++ b/sql/sql_do.cc @@ -25,7 +25,7 @@ int mysql_do(THD *thd, List<Item> &values) List_iterator<Item> li(values); Item *value; DBUG_ENTER("mysql_do"); - if (setup_fields(thd,0, values, 0, 0)) + if (setup_fields(thd,0, values, 0, 0, 0)) DBUG_RETURN(-1); while ((value = li++)) value->val_int(); diff --git a/sql/sql_handler.cc b/sql/sql_handler.cc new file mode 100644 index 00000000000..f1dc5599f46 --- /dev/null +++ b/sql/sql_handler.cc @@ -0,0 +1,278 @@ +/* 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; + + // there can be only one table in *tables + if (!(tables->table->file->table_flags() & HA_CAN_SQL_HANDLER)) + { + my_printf_error(ER_ILLEGAL_HA,ER(ER_ILLEGAL_HA),MYF(0), tables->name); + mysql_ha_close(thd, tables,1); + return -1; + } + + send_ok(&thd->net); + return 0; +} + +int mysql_ha_close(THD *thd, TABLE_LIST *tables, bool dont_send_ok) +{ + TABLE **ptr=find_table_ptr_by_name(thd, tables->db, tables->name); + + if (*ptr) + { + VOID(pthread_mutex_lock(&LOCK_open)); + close_thread_table(thd, ptr); + VOID(pthread_mutex_unlock(&LOCK_open)); + } + else + { + my_printf_error(ER_UNKNOWN_TABLE,ER(ER_UNKNOWN_TABLE),MYF(0), + tables->name,"HANDLER"); + return -1; + } + if (!dont_send_ok) + 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); + uint num_rows; + it++; + + insert_fields(thd,tables,tables->db,tables->name,&it); + + table->file->index_init(keyno); + + select_limit+=offset_limit; + send_fields(thd,list,1); + + HANDLER_TABLES_HACK(thd); + MYSQL_LOCK *lock=mysql_lock_tables(thd,&tables->table,1); + HANDLER_TABLES_HACK(thd); + if (!lock) + goto err0; // mysql_lock_tables() printed error message already + + for (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_fast<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= (byte*) 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(thd,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); +err0: + 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 d8861390d87..6cc4f258c65 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.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 */ @@ -78,7 +78,8 @@ check_insert_fields(THD *thd,TABLE *table,List<Item> &fields, table_list.grant=table->grant; thd->dupp_field=0; - if (setup_tables(&table_list) || setup_fields(thd,&table_list,fields,1,0)) + if (setup_tables(&table_list) || + setup_fields(thd,&table_list,fields,1,0,0)) return -1; if (thd->dupp_field) { @@ -102,14 +103,14 @@ int mysql_insert(THD *thd,TABLE_LIST *table_list, List<Item> &fields, int error; bool log_on= ((thd->options & OPTION_UPDATE_LOG) || !(thd->master_access & PROCESS_ACL)); - bool using_transactions; + bool using_transactions, bulk_insert=0; uint value_count; uint save_time_stamp; ulong counter = 1; ulonglong id; COPY_INFO info; TABLE *table; - List_iterator<List_item> its(values_list); + List_iterator_fast<List_item> its(values_list); List_item *values; char *query=thd->query; DBUG_ENTER("mysql_insert"); @@ -151,7 +152,7 @@ int mysql_insert(THD *thd,TABLE_LIST *table_list, List<Item> &fields, save_time_stamp=table->time_stamp; values= its++; if (check_insert_fields(thd,table,fields,*values,1) || - setup_tables(table_list) || setup_fields(thd,table_list,*values,0,0)) + setup_tables(table_list) || setup_fields(thd,table_list,*values,0,0,0)) { table->time_stamp=save_time_stamp; goto abort; @@ -168,7 +169,7 @@ int mysql_insert(THD *thd,TABLE_LIST *table_list, List<Item> &fields, table->time_stamp=save_time_stamp; goto abort; } - if (setup_fields(thd,table_list,*values,0,0)) + if (setup_fields(thd,table_list,*values,0,0,0)) { table->time_stamp=save_time_stamp; goto abort; @@ -192,6 +193,14 @@ int mysql_insert(THD *thd,TABLE_LIST *table_list, List<Item> &fields, thd->proc_info="update"; if (duplic == DUP_IGNORE || duplic == DUP_REPLACE) table->file->extra(HA_EXTRA_IGNORE_DUP_KEY); + if ((bulk_insert= (values_list.elements > 1 && + lock_type != TL_WRITE_DELAYED && + !(specialflag & SPECIAL_SAFE_MODE)))) + { + table->file->extra(HA_EXTRA_WRITE_CACHE); + table->file->extra(HA_EXTRA_BULK_INSERT_BEGIN); + } + while ((values = its++)) { if (fields.elements || !value_count) @@ -256,6 +265,25 @@ int mysql_insert(THD *thd,TABLE_LIST *table_list, List<Item> &fields, } else { + if (bulk_insert) + { + if (table->file->extra(HA_EXTRA_NO_CACHE)) + { + if (!error) + { + table->file->print_error(my_errno,MYF(0)); + error=1; + } + } + if (table->file->extra(HA_EXTRA_BULK_INSERT_END)) + { + if (!error) + { + table->file->print_error(my_errno,MYF(0)); + error=1; + } + } + } if (id && values_list.elements != 1) thd->insert_id(id); // For update log else if (table->next_number_field) @@ -282,13 +310,16 @@ int mysql_insert(THD *thd,TABLE_LIST *table_list, List<Item> &fields, } } thd->proc_info="end"; + if (info.copied || info.deleted) + { + query_cache_invalidate3(thd, table_list, 1); + } table->time_stamp=save_time_stamp; // Restore auto timestamp ptr table->next_number_field=0; thd->count_cuted_fields=0; thd->next_insert_id=0; // Reset this if wrongly used if (duplic == DUP_IGNORE || duplic == DUP_REPLACE) table->file->extra(HA_EXTRA_NO_IGNORE_DUP_KEY); - if (error) goto abort; @@ -335,18 +366,18 @@ int write_record(TABLE *table,COPY_INFO *info) { int error; char *key=0; - + info->records++; if (info->handle_duplicates == DUP_REPLACE) { while ((error=table->file->write_row(table->record[0]))) { - if (error != HA_WRITE_SKIPP) + if (error != HA_WRITE_SKIP) goto err; uint key_nr; if ((int) (key_nr = table->file->get_dup_key(error)) < 0) { - error=HA_WRITE_SKIPP; /* Database can't find key */ + error=HA_WRITE_SKIP; /* Database can't find key */ goto err; } /* @@ -357,19 +388,19 @@ int write_record(TABLE *table,COPY_INFO *info) if (table->next_number_field && key_nr == table->next_number_index && table->file->auto_increment_column_changed) goto err; - if (table->file->option_flag() & HA_DUPP_POS) + if (table->file->table_flags() & HA_DUPP_POS) { if (table->file->rnd_pos(table->record[1],table->file->dupp_ref)) goto err; } else { - if (table->file->extra(HA_EXTRA_FLUSH_CACHE)) /* Not neaded with NISAM */ + if (table->file->extra(HA_EXTRA_FLUSH_CACHE)) /* Not needed with NISAM */ { error=my_errno; goto err; } - + if (!key) { if (!(key=(char*) my_safe_alloca(table->max_unique_length, @@ -507,7 +538,7 @@ public: } ~delayed_insert() { - /* The following is not really neaded, but just for safety */ + /* The following is not really needed, but just for safety */ delayed_row *row; while ((row=rows.get())) delete row; @@ -646,7 +677,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); @@ -710,9 +741,9 @@ TABLE *delayed_insert::get_local_table(THD* client_thd) } client_thd->proc_info="allocating local table"; - copy= (TABLE*) sql_alloc(sizeof(*copy)+ - (table->fields+1)*sizeof(Field**)+ - table->reclength); + copy= (TABLE*) client_thd->alloc(sizeof(*copy)+ + (table->fields+1)*sizeof(Field**)+ + table->reclength); if (!copy) goto error; *copy= *table; @@ -730,7 +761,7 @@ TABLE *delayed_insert::get_local_table(THD* client_thd) found_next_number_field=table->found_next_number_field; for (org_field=table->field ; *org_field ; org_field++,field++) { - if (!(*field= (*org_field)->new_field(copy))) + if (!(*field= (*org_field)->new_field(&client_thd->mem_root,copy))) return 0; (*field)->move_field(adjust_ptrs); // Point at copy->record[0] if (*org_field == found_next_number_field) @@ -959,23 +990,12 @@ static pthread_handler_decl(handle_delayed_insert,arg) if (!di->status && !di->stacked_inserts) { struct timespec abstime; -#if defined(HAVE_TIMESPEC_TS_SEC) - abstime.ts_sec=time((time_t*) 0)+(time_t) delayed_insert_timeout; - abstime.ts_nsec=0; -#elif defined(__WIN__) - abstime.tv_sec=time((time_t*) 0)+(time_t) delayed_insert_timeout; - abstime.tv_nsec=0; -#else - struct timeval tv; - gettimeofday(&tv,0); - abstime.tv_sec=tv.tv_sec+(time_t) delayed_insert_timeout; - abstime.tv_nsec=tv.tv_usec*1000; -#endif + set_timespec(abstime, delayed_insert_timeout); /* Information for pthread_kill */ di->thd.mysys_var->current_mutex= &di->mutex; di->thd.mysys_var->current_cond= &di->cond; - di->thd.proc_info=0; + di->thd.proc_info="Waiting for INSERT"; DBUG_PRINT("info",("Waiting for someone to insert rows")); while (!thd->killed) @@ -1010,6 +1030,7 @@ static pthread_handler_decl(handle_delayed_insert,arg) pthread_mutex_unlock(&di->thd.mysys_var->mutex); pthread_mutex_lock(&di->mutex); } + di->thd.proc_info=0; if (di->tables_in_use && ! thd->lock) { @@ -1198,6 +1219,7 @@ bool delayed_insert::handle_inserts(void) sql_print_error("%s",thd.net.last_error); goto err; } + query_cache_invalidate3(&thd, table, 1); if (thr_reschedule_write_lock(*thd.lock->locks)) { /* This should never happen */ @@ -1222,6 +1244,7 @@ bool delayed_insert::handle_inserts(void) sql_print_error("%s",thd.net.last_error); goto err; } + query_cache_invalidate3(&thd, table, 1); pthread_mutex_lock(&mutex); DBUG_RETURN(0); @@ -1255,7 +1278,7 @@ select_insert::prepare(List<Item> &values) restore_record(table,2); // Get empty record table->next_number_field=table->found_next_number_field; - thd->count_cuted_fields=1; /* calc cuted fields */ + thd->count_cuted_fields=1; // calc cuted fields thd->cuted_fields=0; if (info.handle_duplicates != DUP_REPLACE) table->file->extra(HA_EXTRA_WRITE_CACHE); @@ -1307,7 +1330,11 @@ void select_insert::send_error(uint errcode,const char *err) ::send_error(&thd->net,errcode,err); table->file->extra(HA_EXTRA_NO_CACHE); table->file->activate_all_index(thd); - ha_rollback(thd); + ha_rollback_stmt(thd); + if (info.copied || info.deleted) + { + query_cache_invalidate3(thd, table, 1); + } } @@ -1319,7 +1346,10 @@ bool select_insert::send_eof() table->file->extra(HA_EXTRA_NO_IGNORE_DUP_KEY); if ((error2=ha_autocommit_or_rollback(thd,error)) && ! error) error=error2; - + if (info.copied || info.deleted) + { + query_cache_invalidate3(thd, table, 1); + } if (error) { table->file->print_error(error,MYF(0)); @@ -1407,6 +1437,7 @@ bool select_create::send_data(List<Item> &values) extern HASH open_cache; + bool select_create::send_eof() { bool tmp=select_insert::send_eof(); @@ -1419,7 +1450,8 @@ bool select_create::send_eof() mysql_unlock_tables(thd, lock); if (!table->tmp_table) hash_delete(&open_cache,(byte*) table); - lock=0; table=0; + lock=0; + table=0; VOID(pthread_mutex_unlock(&LOCK_open)); } return tmp; @@ -1451,7 +1483,7 @@ void select_create::abort() *****************************************************************************/ #ifdef __GNUC__ -template class List_iterator<List_item>; +template class List_iterator_fast<List_item>; template class I_List<delayed_insert>; template class I_List_iterator<delayed_insert>; template class I_List<delayed_row>; diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index a3c3db8947e..42a8a700da3 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.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 */ @@ -142,20 +142,22 @@ 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->convert_set=thd->convert_set; + 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->sql_mode & MODE_IGNORE_SPACE); + lex->slave_thd_opt=0; + bzero(&lex->mi,sizeof(lex->mi)); return lex; } 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,12 +198,12 @@ 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 tmp.length=lex->yytoklen=length; - tmp.str=(char*) sql_strmake((char*) lex->tok_start,tmp.length); + tmp.str=(char*) lex->thd->strmake((char*) lex->tok_start,tmp.length); return tmp; } @@ -250,7 +252,7 @@ static char *get_text(LEX *lex) str=lex->tok_start+1; end=lex->ptr-1; - if (!(start=(uchar*) sql_alloc((uint) (end-str)+1))) + if (!(start=(uchar*) lex->thd->alloc((uint) (end-str)+1))) return (char*) ""; // Sql_alloc has set error flag if (!found_escape) { @@ -337,7 +339,8 @@ static const char *longlong_str="9223372036854775807"; static const uint longlong_len=19; static const char *signed_longlong_str="-9223372036854775808"; static const uint signed_longlong_len=19; - +static const char *unsigned_longlong_str="18446744073709551615"; +static const uint unsigned_longlong_len=20; inline static uint int_token(const char *str,uint length) { @@ -393,7 +396,13 @@ inline static uint int_token(const char *str,uint length) else if (length < longlong_len) return LONG_NUM; else if (length > longlong_len) - return REAL_NUM; + { + if (length > unsigned_longlong_len) + return REAL_NUM; + cmp=unsigned_longlong_str; + smaller=ULONGLONG_NUM; + bigger=REAL_NUM; + } else { cmp=longlong_str; @@ -430,7 +439,7 @@ int yylex(void *arg) switch(state) { case STATE_OPERATOR_OR_IDENT: // Next is operator or keyword case STATE_START: // Start of token - // Skipp startspace + // Skip startspace for (c=yyGet() ; (state_map[c] == STATE_SKIP) ; c= yyGet()) { if (c == '\n') @@ -458,6 +467,11 @@ int yylex(void *arg) return((int) c); case STATE_IDENT: // Incomplete keyword or ident + if ((c == 'x' || c == 'X') && yyPeek() == '\'') + { // Found x'hex-number' + state=STATE_HEX_NUMBER; + break; + } #if defined(USE_MB) && defined(USE_MB_IDENT) if (use_mb(default_charset_info)) { @@ -509,6 +523,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 '.' @@ -518,7 +534,7 @@ int yylex(void *arg) c=yyGet(); // should be '.' return((int) c); - case STATE_NUMBER_IDENT: // number or ident which starts with num + case STATE_NUMBER_IDENT: // number or ident which num-start while (isdigit((c = yyGet()))) ; if (state_map[c] != STATE_IDENT) { // Can't be identifier @@ -544,10 +560,10 @@ int yylex(void *arg) lex->tok_start[0] == '0' ) { // Varbinary while (isxdigit((c = yyGet()))) ; - if ((lex->ptr - lex->tok_start) >= 4) + if ((lex->ptr - lex->tok_start) >= 4 && state_map[c] != STATE_IDENT) { yylval->lex_str=get_token(lex,yyLength()); - yylval->lex_str.str+=2; // Skipp 0x + yylval->lex_str.str+=2; // Skip 0x yylval->lex_str.length-=2; lex->yytoklen-=2; return (HEX_NUM); @@ -597,10 +613,12 @@ 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: - lex->tok_start=lex->ptr; // Skipp first ` + lex->tok_start=lex->ptr; // Skip first ` #ifdef USE_MB if (use_mb(default_charset_info)) { @@ -625,14 +643,17 @@ int yylex(void *arg) 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 ` + yySkip(); // Skip end ` return(IDENT); case STATE_SIGNED_NUMBER: // Incomplete signed number if (prev_state == STATE_OPERATOR_OR_IDENT) { - if (c == '-' && yyPeek() == '-' && isspace(yyPeek2())) + if (c == '-' && yyPeek() == '-' && + (isspace(yyPeek2()) || iscntrl(yyPeek2()))) state=STATE_COMMENT; else state= STATE_CHAR; // Must be operator @@ -672,7 +693,7 @@ int yylex(void *arg) { c = yyGet(); if (c == '-' || c == '+') - c = yyGet(); // Skipp sign + c = yyGet(); // Skip sign if (!isdigit(c)) { // No digit after sign state= STATE_CHAR; @@ -685,6 +706,21 @@ int yylex(void *arg) yylval->lex_str=get_token(lex,yyLength()); return(REAL_NUM); + case STATE_HEX_NUMBER: // Found x'hexstring' + yyGet(); // Skip ' + while (isxdigit((c = yyGet()))) ; + length=(lex->ptr - lex->tok_start); // Length of hexnum+3 + if (!(length & 1) || c != '\'') + { + return(ABORT_SYM); // Illegal hex constant + } + yyGet(); // get_token makes an unget + yylval->lex_str=get_token(lex,length); + yylval->lex_str.str+=2; // Skip x' + yylval->lex_str.length-=3; // Don't count x' and last ' + lex->yytoklen-=3; + return (HEX_NUM); + case STATE_CMP_OP: // Incomplete comparison operator if (state_map[yyPeek()] == STATE_CMP_OP || state_map[yyPeek()] == STATE_LONG_CMP_OP) @@ -734,7 +770,7 @@ int yylex(void *arg) return(TEXT_STRING); case STATE_COMMENT: // Comment - lex->options|= OPTION_FOUND_COMMENT; + lex->select_lex.options|= OPTION_FOUND_COMMENT; while ((c = yyGet()) != '\n' && c) ; yyUnget(); // Safety against eof state = STATE_START; // Try again @@ -746,7 +782,7 @@ int yylex(void *arg) break; } yySkip(); // Skip '*' - lex->options|= OPTION_FOUND_COMMENT; + lex->select_lex.options|= OPTION_FOUND_COMMENT; if (yyPeek() == '!') // MySQL command in comment { ulong version=MYSQL_VERSION_ID; diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 2b889d356bb..8713e8f0be7 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -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 */ @@ -22,7 +22,7 @@ class Table_ident; class sql_exchange; class LEX_COLUMN; -// The following hack is neaded because mysql_yacc.cc does not define +// The following hack is needed because mysql_yacc.cc does not define // YYSTYPE before including this file #ifdef MYSQL_YACC @@ -53,7 +53,11 @@ 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_DO, SQLCOM_EMPTY_QUERY, + SQLCOM_SHOW_OPEN_TABLES, SQLCOM_LOAD_MASTER_DATA, + SQLCOM_HA_OPEN, SQLCOM_HA_CLOSE, SQLCOM_HA_READ, + SQLCOM_SHOW_SLAVE_HOSTS, SQLCOM_DELETE_MULTI, SQLCOM_MULTI_UPDATE, + SQLCOM_SHOW_BINLOG_EVENTS, SQLCOM_SHOW_NEW_MASTER, SQLCOM_DO, + SQLCOM_EMPTY_QUERY, SQLCOM_END }; @@ -63,6 +67,7 @@ enum lex_states { STATE_START, STATE_CHAR, STATE_IDENT, STATE_FOUND_IDENT, STATE_SIGNED_NUMBER, STATE_REAL, + STATE_HEX_NUMBER, STATE_CMP_OP, STATE_LONG_CMP_OP, STATE_STRING, @@ -92,60 +97,99 @@ typedef struct st_lex_master_info char* host, *user, *password,*log_file_name; uint port, connect_retry; ulonglong pos; + ulong server_id; + char* relay_log_name; + ulong relay_log_pos; } LEX_MASTER_INFO; + +enum sub_select_type {UNSPECIFIED_TYPE,UNION_TYPE, INTERSECT_TYPE, EXCEPT_TYPE, NOT_A_SELECT}; + +/* The state of the lex parsing for selects */ + +typedef struct st_select_lex { + enum sub_select_type linkage; + 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, braces; + 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 */ + char* x509_subject,*x509_issuer,*ssl_cipher; + enum SSL_type ssl_type; /* defined in violite.h */ 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; + THD *thd; udf_func udf; HA_CHECK_OPT check_opt; // check/repair options HA_CREATE_INFO create_info; LEX_MASTER_INFO mi; // used by CHANGE MASTER + USER_RESOURCES mqh; 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; + 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, union_option; 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; + uint slave_thd_opt; } LEX; diff --git a/sql/sql_list.cc b/sql/sql_list.cc index 7d5fc442121..1124605ca24 100644 --- a/sql/sql_list.cc +++ b/sql/sql_list.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 */ @@ -20,3 +20,5 @@ #endif #include "mysql_priv.h" + +list_node end_of_list; diff --git a/sql/sql_list.h b/sql/sql_list.h index d21f2e658dc..542eef623f0 100644 --- a/sql/sql_list.h +++ b/sql/sql_list.h @@ -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 */ @@ -41,25 +41,40 @@ public: /* ** basic single linked list ** Used for item and item_buffs. +** All list ends with a pointer to the 'end_of_list' element, which +** data pointer is a null pointer and the next pointer points to itself. +** This makes it very fast to traverse lists as we don't have to +** test for a specialend condition for list that can't contain a null +** pointer. */ +class list_node :public Sql_alloc +{ +public: + list_node *next; + void *info; + list_node(void *info_par,list_node *next_par) + :next(next_par),info(info_par) + {} + list_node() /* For end_of_list */ + { + info=0; + next= this; + } + friend class base_list; + friend class base_list_iterator; +}; + +extern list_node end_of_list; + class base_list :public Sql_alloc { protected: - class list_node :public Sql_alloc - { - public: - list_node *next; - void *info; - list_node(void *info_par,list_node *next_par) : next(next_par),info(info_par) {} - friend class base_list; - friend class base_list_iterator; - }; list_node *first,**last; public: uint elements; - inline void empty() { elements=0; first=0; last=&first;} + inline void empty() { elements=0; first= &end_of_list; last=&first;} inline base_list() { empty(); } inline base_list(const base_list &tmp) :Sql_alloc() { @@ -69,7 +84,7 @@ public: } inline bool push_back(void *info) { - if (((*last)=new list_node(info,0))) + if (((*last)=new list_node(info, &end_of_list))) { last= &(*last)->next; elements++; @@ -82,7 +97,7 @@ public: list_node *node=new list_node(info,first); if (node) { - if (!first) + if (last == &first) last= &node->next; first=node; elements++; @@ -96,22 +111,21 @@ public: delete *prev; *prev=node; if (!--elements) - { last= &first; - first=0; - } } inline void *pop(void) { - if (!first) return 0; + if (first == &end_of_list) return 0; list_node *tmp=first; first=first->next; if (!--elements) last= &first; return tmp->info; } - inline void *head() { return first ? first->info : 0; } - inline void **head_ref() { return first ? &first->info : 0; } + inline void *head() { return first->info; } + inline void **head_ref() { return first != &end_of_list ? &first->info : 0; } + inline bool is_empty() { return first == &end_of_list ; } + inline list_node *last_ref() { return &end_of_list; } friend class base_list_iterator; protected: @@ -129,7 +143,7 @@ protected: class base_list_iterator { base_list *list; - base_list::list_node **el,**prev,*current; + list_node **el,**prev,*current; public: base_list_iterator(base_list &list_par) :list(&list_par),el(&list_par.first), prev(0),current(0) @@ -137,16 +151,22 @@ public: inline void *next(void) { prev=el; - if (!(current= *el)) - return 0; + current= *el; el= ¤t->next; return current->info; } + inline void *next_fast(void) + { + list_node *tmp; + tmp= *el; + el= &tmp->next; + return tmp->info; + } inline void rewind(void) { el= &list->first; } - void *replace(void *element) + inline void *replace(void *element) { // Return old element void *tmp=current->info; current->info=element; @@ -155,7 +175,7 @@ public: void *replace(base_list &new_list) { void *ret_value=current->info; - if (new_list.first) + if (!new_list.is_empty()) { *new_list.last=current->next; current->info=new_list.first->info; @@ -182,7 +202,7 @@ public: } inline bool is_last(void) { - return *el == 0; + return el == &list->last_ref()->next; } }; @@ -200,7 +220,7 @@ public: void delete_elements(void) { list_node *element,*next; - for (element=first; element ; element=next) + for (element=first; element != &end_of_list; element=next) { next=element->next; delete (T*) element->info; @@ -215,18 +235,30 @@ template <class T> class List_iterator :public base_list_iterator public: List_iterator(List<T> &a) : base_list_iterator(a) {} inline T* operator++(int) { return (T*) base_list_iterator::next(); } - inline void rewind(void) { base_list_iterator::rewind(); } inline T *replace(T *a) { return (T*) base_list_iterator::replace(a); } inline T *replace(List<T> &a) { return (T*) base_list_iterator::replace(a); } - inline void remove(void) { base_list_iterator::remove(); } inline void after(T *a) { base_list_iterator::after(a); } inline T** ref(void) { return (T**) base_list_iterator::ref(); } - inline bool is_last(void) { return base_list_iterator::is_last(); } +}; + +template <class T> class List_iterator_fast :public base_list_iterator +{ +protected: + inline T *replace(T *a) { return (T*) 0; } + inline T *replace(List<T> &a) { return (T*) 0; } + inline void remove(void) { } + inline void after(T *a) { } + inline T** ref(void) { return (T**) 0; } + +public: + List_iterator_fast(List<T> &a) : base_list_iterator(a) {} + inline T* operator++(int) { return (T*) base_list_iterator::next_fast(); } + inline void rewind(void) { base_list_iterator::rewind(); } }; /* -** An simple intrusive list with automaticly removes element from list +** A simple intrusive list which automaticly removes element from list ** on delete (for THD element) */ diff --git a/sql/sql_load.cc b/sql/sql_load.cc index 1dcc8c2130e..419e3fccabd 100644 --- a/sql/sql_load.cc +++ b/sql/sql_load.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 */ @@ -20,6 +20,7 @@ #include "mysql_priv.h" #include <my_dir.h> #include <m_ctype.h> +#include "sql_repl.h" class READ_INFO { File file; @@ -32,6 +33,7 @@ class READ_INFO { int field_term_char,line_term_char,enclosed_char,escape_char; int *stack,*stack_pos; bool found_end_of_line,start_of_line,eof; + bool need_end_io_cache; IO_CACHE cache; NET *io_net; @@ -50,6 +52,18 @@ public: char unescape(char chr); int terminator(char *ptr,uint length); bool find_start_of_fields(); + // we need to force cache close before destructor is invoked to log + // the last read block + void end_io_cache() + { + ::end_io_cache(&cache); + need_end_io_cache = 0; + } + + // either this method, or we need to make cache public + // arg must be set from mysql_load() since constructor does not see + // either the table or THD value + void set_io_cache_arg(void* arg) { cache.arg = arg; } }; static int read_fixed_length(THD *thd,COPY_INFO &info,TABLE *table, @@ -67,10 +81,11 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list, File file; TABLE *table; int error; - uint save_skip_lines = ex->skip_lines; String *field_term=ex->field_term,*escaped=ex->escaped, *enclosed=ex->enclosed; bool is_fifo=0; + LOAD_FILE_INFO lf_info; + char * db = table_list->db ? table_list->db : thd->db; bool using_transactions; DBUG_ENTER("mysql_load"); @@ -80,7 +95,6 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list, MYF(0)); DBUG_RETURN(-1); } - if (!(table = open_ltable(thd,table_list,lock_type))) DBUG_RETURN(-1); if (!fields.elements) @@ -92,7 +106,7 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list, else { // Part field list thd->dupp_field=0; - if (setup_tables(table_list) || setup_fields(thd,table_list,fields,1,0)) + if (setup_tables(table_list) || setup_fields(thd,table_list,fields,1,0,0)) DBUG_RETURN(-1); if (thd->dupp_field) { @@ -103,7 +117,7 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list, uint tot_length=0; bool use_blobs=0,use_timestamp=0; - List_iterator<Item> it(fields); + List_iterator_fast<Item> it(fields); Item_field *field; while ((field=(Item_field*) it++)) @@ -133,12 +147,7 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list, if (read_file_from_client) { - char tmp [FN_REFLEN+1],*end; - DBUG_PRINT("info",("reading local file")); - tmp[0] = (char) 251; /* NULL_LENGTH */ - end=strnmov(tmp+1,ex->file_name,sizeof(tmp)-2); - (void) my_net_write(&thd->net,tmp,(uint) (end-tmp)); - (void) net_flush(&thd->net); + (void)net_request_file(&thd->net,ex->file_name); file = -1; } else @@ -161,9 +170,10 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list, MY_STAT stat_info; if (!my_stat(name,&stat_info,MYF(MY_WME))) DBUG_RETURN(-1); - - // the file must be: - if (!((stat_info.st_mode & S_IROTH) == S_IROTH && // readable by others + + // if we are not in slave thread, the file must be: + if (!thd->slave_thread && + !((stat_info.st_mode & S_IROTH) == S_IROTH && // readable by others #ifndef __EMX__ (stat_info.st_mode & S_IFLNK) != S_IFLNK && // and not a symlink #endif @@ -196,13 +206,27 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list, DBUG_RETURN(-1); // Can't allocate buffers } + if (!opt_old_rpl_compat && mysql_bin_log.is_open()) + { + lf_info.thd = thd; + lf_info.ex = ex; + lf_info.db = db; + lf_info.table_name = table_list->real_name; + lf_info.fields = &fields; + lf_info.handle_dup = handle_duplicates; + lf_info.wrote_create_file = 0; + lf_info.last_pos_in_file = HA_POS_ERROR; + read_info.set_io_cache_arg((void*)&lf_info); + } restore_record(table,2); thd->count_cuted_fields=1; /* calc cuted fields */ thd->cuted_fields=0L; if (ex->line_term->length() && field_term->length()) { - while (ex->skip_lines--) + // ex->skip_lines needs to be preserved for logging + uint skip_lines = ex->skip_lines; + while (skip_lines--) { if (read_info.next_line()) break; @@ -215,6 +239,7 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list, table->time_stamp=0; table->next_number_field=table->found_next_number_field; VOID(table->file->extra(HA_EXTRA_WRITE_CACHE)); + VOID(table->file->extra(HA_EXTRA_BULK_INSERT_BEGIN)); if (handle_duplicates == DUP_IGNORE || handle_duplicates == DUP_REPLACE) table->file->extra(HA_EXTRA_IGNORE_DUP_KEY); @@ -224,9 +249,10 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list, error=read_fixed_length(thd,info,table,fields,read_info); else error=read_sep_field(thd,info,table,fields,read_info,*enclosed); - if (table->file->extra(HA_EXTRA_NO_CACHE) || - table->file->activate_all_index(thd)) - error=1; /* purecov: inspected */ + if (table->file->extra(HA_EXTRA_NO_CACHE)) + error=1; /* purecov: inspected */ + if (table->file->activate_all_index(thd)) + error=1; /* purecov: inspected */ table->file->extra(HA_EXTRA_NO_IGNORE_DUP_KEY); table->time_stamp=save_time_stamp; table->next_number_field=0; @@ -245,7 +271,15 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list, { if (using_transactions) ha_autocommit_or_rollback(thd,error); - DBUG_RETURN(-1); // Error on read + if (!opt_old_rpl_compat && mysql_bin_log.is_open()) + { + if (lf_info.wrote_create_file) + { + Delete_file_log_event d(thd); + mysql_bin_log.write(&d); + } + } + DBUG_RETURN(-1); // Error on read } sprintf(name,ER(ER_LOAD_INFO),info.records,info.deleted, info.records-info.copied,thd->cuted_fields); @@ -253,15 +287,26 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list, // on the slave thd->query is never initialized if(!thd->slave_thread) mysql_update_log.write(thd,thd->query,thd->query_length); - + if (!using_transactions) thd->options|=OPTION_STATUS_NO_TRANS_UPDATE; - if (!read_file_from_client && mysql_bin_log.is_open()) + if (mysql_bin_log.is_open()) { - ex->skip_lines = save_skip_lines; - Load_log_event qinfo(thd, ex, table->table_cache_key, table->table_name, - fields, handle_duplicates); - mysql_bin_log.write(&qinfo); + if (opt_old_rpl_compat && !read_file_from_client) + { + Load_log_event qinfo(thd, ex, db, table->table_name, fields, + handle_duplicates); + mysql_bin_log.write(&qinfo); + } + if (!opt_old_rpl_compat) + { + read_info.end_io_cache(); // make sure last block gets logged + if (lf_info.wrote_create_file) + { + Execute_load_log_event e(thd); + mysql_bin_log.write(&e); + } + } } if (using_transactions) error=ha_autocommit_or_rollback(thd,error); @@ -277,7 +322,7 @@ static int read_fixed_length(THD *thd,COPY_INFO &info,TABLE *table,List<Item> &fields, READ_INFO &read_info) { - List_iterator<Item> it(fields); + List_iterator_fast<Item> it(fields); Item_field *sql_field; DBUG_ENTER("read_fixed_length"); @@ -325,7 +370,7 @@ read_fixed_length(THD *thd,COPY_INFO &info,TABLE *table,List<Item> &fields, DBUG_RETURN(1); if (table->next_number_field) table->next_number_field->reset(); // Clear for next record - if (read_info.next_line()) // Skipp to next line + if (read_info.next_line()) // Skip to next line break; if (read_info.line_cuted) thd->cuted_fields++; /* To long row */ @@ -340,7 +385,7 @@ read_sep_field(THD *thd,COPY_INFO &info,TABLE *table, List<Item> &fields, READ_INFO &read_info, String &enclosed) { - List_iterator<Item> it(fields); + List_iterator_fast<Item> it(fields); Item_field *sql_field; uint enclosed_length; DBUG_ENTER("read_sep_field"); @@ -401,7 +446,7 @@ read_sep_field(THD *thd,COPY_INFO &info,TABLE *table, DBUG_RETURN(1); if (table->next_number_field) table->next_number_field->reset(); // Clear for next record - if (read_info.next_line()) // Skipp to next line + if (read_info.next_line()) // Skip to next line break; if (read_info.line_cuted) thd->cuted_fields++; /* To long row */ @@ -488,6 +533,21 @@ READ_INFO::READ_INFO(File file_par, uint tot_length, String &field_term, my_free((gptr) buffer,MYF(0)); /* purecov: inspected */ error=1; } + else + { + /* init_io_cache() will not initialize read_function member + if the cache is READ_NET. The reason is explained in + mysys/mf_iocache.c. So we work around the problem with a + manual assignment + */ + if (get_it_from_net) + cache.read_function = _my_b_net_read; + + need_end_io_cache = 1; + if (!opt_old_rpl_compat && mysql_bin_log.is_open()) + cache.pre_read = cache.pre_close = + (IO_CACHE_CALLBACK) log_loaded_block; + } } } @@ -496,7 +556,8 @@ READ_INFO::~READ_INFO() { if (!error) { - end_io_cache(&cache); + if (need_end_io_cache) + ::end_io_cache(&cache); my_free((gptr) buffer,MYF(0)); error=1; } @@ -536,10 +597,10 @@ int READ_INFO::read_field() if (found_end_of_line) return 1; // One have to call next_line - /* Skipp until we find 'line_start' */ + /* Skip until we find 'line_start' */ if (start_of_line) - { // Skipp until line_start + { // Skip until line_start start_of_line=0; if (find_start_of_fields()) return 1; @@ -682,7 +743,7 @@ found_eof: /* ** One can't use fixed length with multi-byte charset ** */ - + int READ_INFO::read_fixed_length() { int chr; @@ -691,7 +752,7 @@ int READ_INFO::read_fixed_length() return 1; // One have to call next_line if (start_of_line) - { // Skipp until line_start + { // Skip until line_start start_of_line=0; if (find_start_of_fields()) return 1; diff --git a/sql/sql_manager.cc b/sql/sql_manager.cc index 53953c96d0b..13cac83fc3f 100644 --- a/sql/sql_manager.cc +++ b/sql/sql_manager.cc @@ -55,13 +55,7 @@ pthread_handler_decl(handle_manager,arg __attribute__((unused))) { if (reset_flush_time) { -#ifdef HAVE_TIMESPEC_TS_SEC - abstime.ts_sec = time(NULL)+flush_time; // Bsd 2.1 - abstime.ts_nsec = 0; -#else - abstime.tv_sec = time(NULL)+flush_time; // Linux or Solairs - abstime.tv_nsec = 0; -#endif + set_timespec(abstime, flush_time); reset_flush_time = FALSE; } while (!manager_status && !error && !abort_loop) diff --git a/sql/sql_map.cc b/sql/sql_map.cc index 4578b85d10a..e7e24f957c6 100644 --- a/sql/sql_map.cc +++ b/sql/sql_map.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 */ diff --git a/sql/sql_map.h b/sql/sql_map.h index 34f2f755b43..632eb6e4f64 100644 --- a/sql/sql_map.h +++ b/sql/sql_map.h @@ -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 */ diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 06a121ea30c..87b7b0238b7 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -1,4 +1,4 @@ -/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB +/* 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 @@ -14,18 +14,44 @@ 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" #include "sql_repl.h" +#include "repl_failsafe.h" #include <m_ctype.h> #include <thr_alarm.h> #include <myisam.h> #include <my_dir.h> #include <assert.h> +#ifdef HAVE_OPENSSL +/* + Without SSL the handshake consists of one packet. This packet + has both client capabilites and scrambled password. + With SSL the handshake might consist of two packets. If the first + packet (client capabilities) has CLIENT_SSL flag set, we have to + switch to SSL and read the second packet. The scrambled password + is in the second packet and client_capabilites field will be ignored. + Maybe it is better to accept flags other than CLIENT_SSL from the + second packet? +*/ +#define SSL_HANDSHAKE_SIZE 2 +#define NORMAL_HANDSHAKE_SIZE 6 +#define MIN_HANDSHAKE_SIZE 2 +#else +#define MIN_HANDSHAKE_SIZE 6 +#endif /* HAVE_OPENSSL */ #define SCRAMBLE_LENGTH 8 +#define MEM_ROOT_BLOCK_SIZE 8192 +#define MEM_ROOT_PREALLOC 8192 +#define TRANS_MEM_ROOT_BLOCK_SIZE 4096 +#define TRANS_MEM_ROOT_PREALLOC 4096 extern int yyparse(void); extern "C" pthread_mutex_t THR_LOCK_keycache; @@ -33,16 +59,18 @@ extern "C" pthread_mutex_t THR_LOCK_keycache; extern "C" int gethostname(char *name, int namelen); #endif -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 int check_for_max_user_connections(USER_CONN *uc); +static bool check_mqh(THD *thd); +static void decrease_user_connections(USER_CONN *uc); 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, - TABLE_LIST *tables); +static bool check_dup(const char *db, const char *name, TABLE_LIST *tables); static void mysql_init_query(THD *thd); static void remove_escape(char *name); static void refresh_status(void); +static bool append_file_to_dir(THD *thd, char **filename_ptr, + char *table_name); +static bool create_total_list(THD *thd, LEX *lex, TABLE_LIST **result); const char *any_db="*any*"; // Special symbol for check_access @@ -50,13 +78,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__ @@ -66,8 +94,8 @@ static void test_signal(int sig_ptr) MessageBox(NULL,"Test signal","DBUG",MB_OK); #endif #if defined(OS2) - fprintf( stderr, "Test signal %d\n", sig_ptr); - fflush( stderr); + fprintf(stderr, "Test signal %d\n", sig_ptr); + fflush(stderr); #endif } static void init_signals(void) @@ -93,10 +121,68 @@ inline bool end_active_trans(THD *thd) } +static HASH hash_user_connections; +extern pthread_mutex_t LOCK_user_conn; + +static int get_or_create_user_conn(THD *thd, const char *user, + const char *host, + USER_RESOURCES *mqh) +{ + int return_val=0; + uint temp_len, user_len, host_len; + char temp_user[USERNAME_LENGTH+HOSTNAME_LENGTH+2]; + struct user_conn *uc; + + DBUG_ASSERT(user != 0); + DBUG_ASSERT(host != 0); + + user_len=strlen(user); + host_len=strlen(host); + temp_len= (strmov(strmov(temp_user, user)+1, host) - temp_user)+1; + (void) pthread_mutex_lock(&LOCK_user_conn); + if (!(uc = (struct user_conn *) hash_search(&hash_user_connections, + (byte*) temp_user, temp_len))) + { + /* First connection for user; Create a user connection object */ + if (!(uc= ((struct user_conn*) + my_malloc(sizeof(struct user_conn) + temp_len+1, + MYF(MY_WME))))) + { + send_error(¤t_thd->net, 0, NullS); // Out of memory + return_val=1; + goto end; + } + uc->user=(char*) (uc+1); + memcpy(uc->user,temp_user,temp_len+1); + uc->user_len= user_len; + uc->host=uc->user + uc->user_len + 1; + uc->len = temp_len; + uc->connections = 1; + uc->questions=uc->updates=uc->conn_per_hour=0; + uc->user_resources=*mqh; + if (mqh->connections > max_user_connections) + uc->user_resources.connections = max_user_connections; + uc->intime=thd->thr_create_time; + if (hash_insert(&hash_user_connections, (byte*) uc)) + { + my_free((char*) uc,0); + send_error(¤t_thd->net, 0, NullS); // Out of memory + return_val=1; + goto end; + } + } + thd->user_connect=uc; +end: + (void) pthread_mutex_unlock(&LOCK_user_conn); + return return_val; + +} + + /* -** Check if user is ok -** Updates: -** thd->user, thd->master_access, thd->priv_user, thd->db, thd->db_access + Check if user is ok + Updates: + thd->user, thd->master_access, thd->priv_user, thd->db, thd->db_access */ static bool check_user(THD *thd,enum_server_command command, const char *user, @@ -104,32 +190,34 @@ static bool check_user(THD *thd,enum_server_command command, const char *user, { NET *net= &thd->net; thd->db=0; + thd->db_length=0; + USER_RESOURCES ur; if (!(thd->user = my_strdup(user, MYF(0)))) { send_error(net,ER_OUT_OF_RESOURCES); return 1; } - thd->master_access=acl_getroot(thd->host, thd->ip, thd->user, + thd->master_access=acl_getroot(thd, thd->host, thd->ip, thd->user, passwd, thd->scramble, &thd->priv_user, protocol_version == 9 || !(thd->client_capabilities & - CLIENT_LONG_PASSWORD)); - DBUG_PRINT("general", + CLIENT_LONG_PASSWORD),&ur); + DBUG_PRINT("info", ("Capabilities: %d packet_length: %d Host: '%s' User: '%s' Using password: %s Access: %u db: '%s'", thd->client_capabilities, thd->max_packet_length, - thd->host ? thd->host : thd->ip, thd->priv_user, + thd->host_or_ip, thd->priv_user, passwd[0] ? "yes": "no", thd->master_access, thd->db ? thd->db : "*none*")); if (thd->master_access & NO_ACCESS) { net_printf(net, ER_ACCESS_DENIED_ERROR, thd->user, - thd->host ? thd->host : thd->ip, + thd->host_or_ip, passwd[0] ? ER(ER_YES) : ER(ER_NO)); mysql_log.write(thd,COM_CONNECT,ER(ER_ACCESS_DENIED_ERROR), thd->user, - thd->host ? thd->host : thd->ip ? thd->ip : "unknown ip", + thd->host_or_ip, passwd[0] ? ER(ER_YES) : ER(ER_NO)); return(1); // Error already given } @@ -150,17 +238,21 @@ static bool check_user(THD *thd,enum_server_command command, const char *user, (char*) "%s@%s on %s" : (char*) "%s@%s as anonymous on %s"), user, - thd->host ? thd->host : thd->ip ? thd->ip : "unknown ip", + thd->host_or_ip, db ? db : (char*) ""); thd->db_access=0; - if (max_user_connections && - check_for_max_user_connections(user, strlen(user), thd->host)) + /* Don't allow user to connect if he has done too many queries */ + if ((ur.questions || ur.updates || ur.connections) && + get_or_create_user_conn(thd,user,thd->host_or_ip,&ur)) + return -1; + if (thd->user_connect && thd->user_connect->user_resources.connections && + check_for_max_user_connections(thd->user_connect)) return -1; if (db && db[0]) { bool error=test(mysql_change_db(thd,db)); - if (error) - decrease_user_connections(thd->user,thd->host); + if (error && thd->user_connect) + decrease_user_connections(thd->user_connect); return error; } else @@ -168,20 +260,12 @@ static bool check_user(THD *thd,enum_server_command command, const char *user, return 0; // ok } + /* -** check for maximum allowable user connections -** if mysql server is started with corresponding -** variable that is greater then 0 + Check for maximum allowable user connections, if the mysqld server is + started with corresponding variable that is greater then 0. */ -static HASH hash_user_connections; -extern pthread_mutex_t LOCK_user_conn; - -struct user_conn { - char *user; - uint len, connections; -}; - static byte* get_key_conn(user_conn *buff, uint *length, my_bool not_used __attribute__((unused))) { @@ -189,8 +273,6 @@ static byte* get_key_conn(user_conn *buff, uint *length, return (byte*) buff->user; } -#define DEF_USER_COUNT 50 - static void free_user(struct user_conn *uc) { my_free((char*) uc,MYF(0)); @@ -198,100 +280,55 @@ static void free_user(struct user_conn *uc) void init_max_user_conn(void) { - (void) hash_init(&hash_user_connections,DEF_USER_COUNT,0,0, + (void) hash_init(&hash_user_connections,max_connections,0,0, (hash_get_key) get_key_conn, (void (*)(void*)) free_user, 0); } -static int check_for_max_user_connections(const char *user, int u_length, - const char *host) +static int check_for_max_user_connections(USER_CONN *uc) { - int error=1; - uint temp_len; - char temp_user[USERNAME_LENGTH+HOSTNAME_LENGTH+2]; - struct user_conn *uc; - if (!user) - user=""; - if (!host) - host=""; + int error=0; DBUG_ENTER("check_for_max_user_connections"); - DBUG_PRINT("enter",("user: '%s' host: '%s'", user, host)); - - temp_len= (uint) (strxnmov(temp_user, sizeof(temp_user), user, "@", host, - NullS) - temp_user); - (void) pthread_mutex_lock(&LOCK_user_conn); - uc = (struct user_conn *) hash_search(&hash_user_connections, - (byte*) temp_user, temp_len); - if (uc) /* user found ; check for no. of connections */ + + if (max_user_connections && ( max_user_connections <= (uint) uc->connections)) { - if (max_user_connections == (uint) uc->connections) - { - net_printf(&(current_thd->net),ER_TOO_MANY_USER_CONNECTIONS, temp_user); - goto end; - } - uc->connections++; + net_printf(&(current_thd->net),ER_TOO_MANY_USER_CONNECTIONS, uc->user); + error=1; + goto end; } - else + uc->connections++; +if (uc->user_resources.connections && uc->conn_per_hour++ >= uc->user_resources.connections) { - /* the user is not found in the cache; Insert it */ - struct user_conn *uc= ((struct user_conn*) - my_malloc(sizeof(struct user_conn) + temp_len+1, - MYF(MY_WME))); - if (!uc) - { - send_error(¤t_thd->net, 0, NullS); // Out of memory - goto end; - } - uc->user=(char*) (uc+1); - memcpy(uc->user,temp_user,temp_len+1); - uc->len = temp_len; - uc->connections = 1; - if (hash_insert(&hash_user_connections, (byte*) uc)) - { - my_free((char*) uc,0); - send_error(¤t_thd->net, 0, NullS); // Out of memory - goto end; - } + net_printf(¤t_thd->net, ER_USER_LIMIT_REACHED, uc->user, "max_connections", + (long) uc->user_resources.connections); + error=1; + goto end; } - error=0; - end: - (void) pthread_mutex_unlock(&LOCK_user_conn); DBUG_RETURN(error); } -static void decrease_user_connections(const char *user, const char *host) +static void decrease_user_connections(USER_CONN *uc) { - char temp_user[USERNAME_LENGTH+HOSTNAME_LENGTH+2]; - int temp_len; - struct user_conn *uc; - if (!max_user_connections) +/* if (!max_user_connections) return; - if (!user) - user=""; - if (!host) - host=""; - DBUG_ENTER("decrease_user_connections"); - DBUG_PRINT("enter",("user: '%s' host: '%s'", user, host)); - - temp_len= (uint) (strxnmov(temp_user, sizeof(temp_user), user, "@", host, - NullS) - temp_user); - (void) pthread_mutex_lock(&LOCK_user_conn); +*/ - uc = (struct user_conn *) hash_search(&hash_user_connections, - (byte*) temp_user, temp_len); - dbug_assert(uc != 0); // We should always find the user - if (!uc) - goto end; // Safety; Something went wrong - if (! --uc->connections) + DBUG_ENTER("decrease_user_connections"); + if (mqh_used) + { + if (uc->conn_per_hour) + uc->conn_per_hour--; + } + else if (!--uc->connections) { /* Last connection for user; Delete it */ + (void) pthread_mutex_lock(&LOCK_user_conn); (void) hash_delete(&hash_user_connections,(byte*) uc); + (void) pthread_mutex_unlock(&LOCK_user_conn); } -end: - (void) pthread_mutex_unlock(&LOCK_user_conn); DBUG_VOID_RETURN; } @@ -303,22 +340,115 @@ void free_max_user_conn(void) /* -** check connnetion and get priviliges -** returns 0 on ok, -1 < if error is given > 0 on error. + Check if maximum queries per hour limit has been reached + returns 0 if OK. + + In theory we would need a mutex in the USER_CONN structure for this to be 100 % + safe, but as the worst scenario is that we would miss counting a couple of + queries, this isn't critical. */ +char uc_update_queries[SQLCOM_END]; + +static bool check_mqh(THD *thd) +{ + bool error=0; + DBUG_ENTER("check_mqh"); + USER_CONN *uc=thd->user_connect; + DBUG_ASSERT(uc != 0); + uint check_command = thd->lex.sql_command; + + + if (check_command < (uint) SQLCOM_END) + { + if (uc->user_resources.updates && uc_update_queries[check_command] && + ++(uc->updates) > uc->user_resources.updates) + { + net_printf(&thd->net, ER_USER_LIMIT_REACHED, uc->user, "max_updates", + (long) uc->user_resources.updates); + error=1; + goto end; + } + } + else + { + bool my_start = thd->start_time != 0; + time_t check_time = (my_start) ? thd->start_time : time(NULL); + + if (check_time - uc->intime >= 3600) + { + (void) pthread_mutex_lock(&LOCK_user_conn); + uc->questions=1; + uc->updates=0; + uc->conn_per_hour=0; + uc->intime=check_time; + (void) pthread_mutex_unlock(&LOCK_user_conn); + } + else if (uc->user_resources.questions && ++(uc->questions) > uc->user_resources.questions) + { + net_printf(&thd->net, ER_USER_LIMIT_REACHED, uc->user, "max_questions", + (long) uc->user_resources.questions); + error=1; + goto end; + } + } +end: + DBUG_RETURN(error); +} + + +static void reset_mqh(THD *thd, LEX_USER *lu, USER_RESOURCES *mqh, bool get_them=false) +{ + + (void) pthread_mutex_lock(&LOCK_user_conn); + if (lu) // for GRANT + { + USER_CONN *uc; + uint temp_len=lu->user.length+lu->host.length+2; + char temp_user[USERNAME_LENGTH+HOSTNAME_LENGTH+2]; + + memcpy(temp_user,lu->user.str,lu->user.length); + memcpy(temp_user+lu->user.length+1,lu->host.str,lu->host.length); + temp_user[lu->user.length]='\0'; temp_user[temp_len-1]=0; + if ((uc = (struct user_conn *) hash_search(&hash_user_connections, + (byte*) temp_user, temp_len-1))) + { + uc->questions=0; + uc->user_resources=*mqh; + uc->updates=0; + uc->conn_per_hour=0; + } + } + else // for FLUSH PRIVILEGES and FLUSH USER_RESOURCES + { + for (uint idx=0;idx < hash_user_connections.records; idx++) + { + USER_CONN *uc=(struct user_conn *) hash_element(&hash_user_connections, idx); + if (get_them) + get_mqh(uc->user,uc->host,uc); + uc->questions=0; + uc->updates=0; + uc->conn_per_hour=0; + } + } + (void) pthread_mutex_unlock(&LOCK_user_conn); +} + + +/* + Check connnetion and get priviliges + Returns 0 on ok, -1 < if error is given > 0 on error. +*/ static int check_connections(THD *thd) { uint connect_errors=0; NET *net= &thd->net; - /* - ** store the connection details - */ + /* Store the connection details */ DBUG_PRINT("info", (("check_connections called by thread %d"), thd->thread_id)); - DBUG_PRINT("general",("New connection received on %s", + DBUG_PRINT("info",("New connection received on %s", vio_description(net->vio))); if (!thd->host) // If TCP/IP connection { @@ -328,6 +458,7 @@ check_connections(THD *thd) return (ER_BAD_HOST_ERROR); if (!(thd->ip = my_strdup(ip,MYF(0)))) return (ER_OUT_OF_RESOURCES); + thd->host_or_ip=thd->ip; #if !defined(HAVE_SYS_UN_H) || defined(HAVE_mit_thread) /* Fast local hostname resolve for Win32 */ if (!strcmp(thd->ip,"127.0.0.1")) @@ -341,65 +472,50 @@ check_connections(THD *thd) if (connect_errors > max_connect_errors) return(ER_HOST_IS_BLOCKED); } - DBUG_PRINT("general",("Host: %s ip: %s", - thd->host ? thd->host : "unknown host", - thd->ip ? thd->ip : "unknown ip")); + DBUG_PRINT("info",("Host: %s ip: %s", + thd->host ? thd->host : "unknown host", + thd->ip ? thd->ip : "unknown ip")); if (acl_check_host(thd->host,thd->ip)) return(ER_HOST_NOT_PRIVILEGED); } else /* Hostname given means that the connection was on a socket */ { - DBUG_PRINT("general",("Host: %s",thd->host)); + DBUG_PRINT("info",("Host: %s",thd->host)); + thd->host_or_ip=thd->host; thd->ip=0; bzero((char*) &thd->remote,sizeof(struct sockaddr)); } vio_keepalive(net->vio, TRUE); - /* nasty, but any other way? */ - uint pkt_len = 0; + ulong pkt_len=0; { /* buff[] needs to big enough to hold the server_version variable */ char buff[SERVER_VERSION_LENGTH + SCRAMBLE_LENGTH+32],*end; int client_flags = CLIENT_LONG_FLAG | CLIENT_CONNECT_WITH_DB; + if (opt_using_transactions) client_flags|=CLIENT_TRANSACTIONS; #ifdef HAVE_COMPRESS client_flags |= CLIENT_COMPRESS; #endif /* HAVE_COMPRESS */ +#ifdef HAVE_OPENSSL + if (ssl_acceptor_fd) + client_flags |= CLIENT_SSL; /* Wow, SSL is avalaible! */ +#endif /* HAVE_OPENSSL */ - end=strmov(buff,server_version)+1; + end=strnmov(buff,server_version,SERVER_VERSION_LENGTH)+1; int4store((uchar*) end,thd->thread_id); end+=4; memcpy(end,thd->scramble,SCRAMBLE_LENGTH+1); end+=SCRAMBLE_LENGTH +1; -#ifdef HAVE_OPENSSL - if (ssl_acceptor_fd) - client_flags |= CLIENT_SSL; /* Wow, SSL is avalaible! */ - /* - * Without SSL the handshake consists of one packet. This packet - * has both client capabilites and scrambled password. - * With SSL the handshake might consist of two packets. If the first - * packet (client capabilities) has CLIENT_SSL flag set, we have to - * switch to SSL and read the second packet. The scrambled password - * is in the second packet and client_capabilites field will be ignored. - * Maybe it is better to accept flags other than CLIENT_SSL from the - * second packet? - */ -#define SSL_HANDSHAKE_SIZE 2 -#define NORMAL_HANDSHAKE_SIZE 6 -#define MIN_HANDSHAKE_SIZE 2 - -#else -#define MIN_HANDSHAKE_SIZE 6 -#endif /* HAVE_OPENSSL */ int2store(end,client_flags); - end[2]=MY_CHARSET_CURRENT; + end[2]=(char) MY_CHARSET_CURRENT; int2store(end+3,thd->server_status); bzero(end+5,13); end+=18; - if (net_write_command(net,protocol_version, buff, + if (net_write_command(net,(uchar) protocol_version, buff, (uint) (end-buff)) || - (pkt_len=my_net_read(net)) == packet_error || + (pkt_len= my_net_read(net)) == packet_error || pkt_len < MIN_HANDSHAKE_SIZE) { inc_host_errors(&thd->remote.sin_addr); @@ -418,23 +534,18 @@ check_connections(THD *thd) if (thd->client_capabilities & CLIENT_IGNORE_SPACE) thd->sql_mode|= MODE_IGNORE_SPACE; #ifdef HAVE_OPENSSL - DBUG_PRINT("info", - ("pkt_len:%d, client capabilities: %d", - pkt_len, thd->client_capabilities) ); + DBUG_PRINT("info", ("client capabilities: %d", thd->client_capabilities)); if (thd->client_capabilities & CLIENT_SSL) { - 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); + sslaccept(ssl_acceptor_fd, net->vio, thd->inactive_timeout); DBUG_PRINT("info", ("Reading user information over SSL layer")); if ((pkt_len=my_net_read(net)) == packet_error || pkt_len < NORMAL_HANDSHAKE_SIZE) { - DBUG_PRINT("info", ("pkt_len:%d", pkt_len)); - DBUG_PRINT("error", ("Failed to read user information")); + DBUG_PRINT("error", ("Failed to read user information (pkt_len= %lu)", + pkt_len)); inc_host_errors(&thd->remote.sin_addr); return(ER_HANDSHAKE_ERROR); } @@ -463,7 +574,7 @@ check_connections(THD *thd) if ((thd->client_capabilities & CLIENT_TRANSACTIONS) && opt_using_transactions) thd->net.return_status= &thd->server_status; - net->timeout=net_read_timeout; + net->timeout=(uint) net_read_timeout; if (check_user(thd,COM_CONNECT, user, passwd, db, 1)) return (-1); thd->password=test(passwd[0]); @@ -481,9 +592,9 @@ pthread_handler_decl(handle_one_connection,arg) pthread_detach_this_thread(); -#if !defined( __WIN__) && !defined(OS2) /* Win32 calls this in pthread_create */ - if (my_thread_init()) // needed to be called first before we call - // DBUG_ macros +#if !defined( __WIN__) && !defined(OS2) // Win32 calls this in pthread_create + // The following calls needs to be done before we call DBUG_ macros + if (my_thread_init()) { close_connection(&thd->net,ER_OUT_OF_RESOURCES); statistic_increment(aborted_connects,&LOCK_thread_count); @@ -492,13 +603,13 @@ pthread_handler_decl(handle_one_connection,arg) } #endif - // handle_one_connection() is the only way a thread would start - // and would always be on top of the stack - // therefore, the thread stack always starts at the address of the first - // local variable of handle_one_connection, which is thd - // we need to know the start of the stack so that we could check for - // stack overruns - + /* + handle_one_connection() is the only way a thread would start + and would always be on top of the stack, therefore, the thread + stack always starts at the address of the first local variable + of handle_one_connection, which is thd. We need to know the + start of the stack so that we could check for stack overruns. + */ DBUG_PRINT("info", ("handle_one_connection called by thread %d\n", thd->thread_id)); // now that we've called my_thread_init(), it is safe to call DBUG_* @@ -530,7 +641,7 @@ pthread_handler_decl(handle_one_connection,arg) if ((error=check_connections(thd))) { // Wrong permissions if (error > 0) - net_printf(net,error,thd->host ? thd->host : (thd->ip ? thd->ip : "")); + net_printf(net,error,thd->host_or_ip); #ifdef __NT__ if (vio_type(net->vio) == VIO_TYPE_NAMEDPIPE) sleep(1); /* must wait after eof() */ @@ -548,27 +659,30 @@ pthread_handler_decl(handle_one_connection,arg) thd->command=COM_SLEEP; thd->version=refresh_version; thd->set_time(); - init_sql_alloc(&thd->mem_root,8192,8192); + init_sql_alloc(&thd->mem_root, MEM_ROOT_BLOCK_SIZE, MEM_ROOT_PREALLOC); + init_sql_alloc(&thd->transaction.mem_root, + TRANS_MEM_ROOT_BLOCK_SIZE, TRANS_MEM_ROOT_PREALLOC); while (!net->error && net->vio != 0 && !thd->killed) { if (do_command(thd)) break; } + if (thd->user_connect) + decrease_user_connections(thd->user_connect); free_root(&thd->mem_root,MYF(0)); if (net->error && net->vio != 0) { if (!thd->killed && opt_warnings) - sql_print_error(ER(ER_NEW_ABORTING_CONNECTION), - thd->thread_id,(thd->db ? thd->db : "unconnected"), - thd->user ? thd->user : "unauthenticated", - (thd->host ? thd->host : thd->ip ? thd->ip : "unknown"), - (net->last_errno ? ER(net->last_errno) : - ER(ER_UNKNOWN_ERROR))); + sql_print_error(ER(ER_NEW_ABORTING_CONNECTION), + thd->thread_id,(thd->db ? thd->db : "unconnected"), + thd->user ? thd->user : "unauthenticated", + thd->host_or_ip, + (net->last_errno ? ER(net->last_errno) : + ER(ER_UNKNOWN_ERROR))); send_error(net,net->last_errno,NullS); thread_safe_increment(aborted_threads,&LOCK_thread_count); } - - decrease_user_connections(thd->user,thd->host); + end_thread: close_connection(net); end_thread(thd,1); @@ -620,7 +734,9 @@ pthread_handler_decl(handle_bootstrap,arg) thd->priv_user=thd->user=(char*)"boot"; buff= (char*) thd->net.buff; - init_sql_alloc(&thd->mem_root,8192,8192); + init_sql_alloc(&thd->mem_root, MEM_ROOT_BLOCK_SIZE, MEM_ROOT_PREALLOC); + init_sql_alloc(&thd->transaction.mem_root, + TRANS_MEM_ROOT_BLOCK_SIZE, TRANS_MEM_ROOT_PREALLOC); while (fgets(buff, thd->net.max_packet, file)) { uint length=(uint) strlen(buff); @@ -628,13 +744,23 @@ pthread_handler_decl(handle_bootstrap,arg) length--; buff[length]=0; thd->current_tablenr=0; - thd->query= thd->memdup(buff,length+1); + thd->query_length=length; + thd->query= thd->memdup_w_gap(buff, length+1, thd->db_length+1); + thd->query[length] = '\0'; thd->query_id=query_id++; + if (mqh_used && thd->user_connect && check_mqh(thd)) + { + thd->net.error = 0; + close_thread_tables(thd); // Free tables + free_root(&thd->mem_root,MYF(MY_KEEP_PREALLOC)); + break; + } mysql_parse(thd,thd->query,length); close_thread_tables(thd); // Free tables if (thd->fatal_error) break; free_root(&thd->mem_root,MYF(MY_KEEP_PREALLOC)); + free_root(&thd->transaction.mem_root,MYF(MY_KEEP_PREALLOC)); } thd->priv_user=thd->user=0; @@ -664,7 +790,7 @@ int mysql_table_dump(THD* thd, char* db, char* tbl_name, int fd) int error = 0; DBUG_ENTER("mysql_table_dump"); db = (db && db[0]) ? db : thd->db; - if (!(table_list = (TABLE_LIST*) sql_calloc(sizeof(TABLE_LIST)))) + if (!(table_list = (TABLE_LIST*) thd->calloc(sizeof(TABLE_LIST)))) DBUG_RETURN(1); // out of memory table_list->db = db; table_list->real_name = table_list->name = tbl_name; @@ -686,21 +812,19 @@ int mysql_table_dump(THD* thd, char* db, char* tbl_name, int fd) goto err; thd->free_list = 0; + thd->query_length=(uint) strlen(tbl_name); thd->query = tbl_name; - if((error = mysqld_dump_create_info(thd, table, -1))) - { - my_error(ER_GET_ERRNO, MYF(0)); - goto err; - } + if ((error = mysqld_dump_create_info(thd, table, -1))) + { + my_error(ER_GET_ERRNO, MYF(0)); + goto err; + } net_flush(&thd->net); - error = table->file->dump(thd,fd); - if(error) - my_error(ER_GET_ERRNO, MYF(0)); + if ((error = table->file->dump(thd,fd))) + my_error(ER_GET_ERRNO, MYF(0)); err: - close_thread_tables(thd); - DBUG_RETURN(error); } @@ -710,13 +834,10 @@ err: bool do_command(THD *thd) { char *packet; - uint old_timeout,packet_length; - bool error=0; + uint old_timeout; + ulong packet_length; 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; @@ -724,26 +845,42 @@ bool do_command(THD *thd) packet=0; old_timeout=net->timeout; - net->timeout=thd->inactive_timeout; /* Wait max for 8 hours */ + net->timeout=(uint) thd->inactive_timeout; // Wait max for 8 hours net->last_error[0]=0; // Clear error message net->last_errno=0; net_new_transaction(net); if ((packet_length=my_net_read(net)) == packet_error) { - DBUG_PRINT("general",("Got error reading command from socket %s", - vio_description(net->vio) )); + DBUG_PRINT("info",("Got error reading command from socket %s", + vio_description(net->vio) )); return TRUE; } else { packet=(char*) net->read_pos; command = (enum enum_server_command) (uchar) packet[0]; - DBUG_PRINT("general",("Command on %s = %d (%s)", - vio_description(net->vio), command, - command_name[command])); + DBUG_PRINT("info",("Command on %s = %d (%s)", + 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, (uint) 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; @@ -752,27 +889,34 @@ 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: thread_safe_increment(com_stat[SQLCOM_CHANGE_DB],&LOCK_thread_count); - 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: { thread_safe_increment(com_other,&LOCK_thread_count); slow_command = TRUE; - char* data = packet + 1; - uint db_len = *data; - uint tbl_len = *(data + db_len + 1); - char* db = sql_alloc(db_len + tbl_len + 2); - memcpy(db, data + 1, db_len); + uint db_len = *(uchar*)packet; + uint tbl_len = *(uchar*)(packet + db_len + 1); + char* db = thd->alloc(db_len + tbl_len + 2); + 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)) + if (mysql_table_dump(thd, db, tbl_name, -1)) send_error(&thd->net); // dump to NET break; @@ -780,16 +924,18 @@ bool do_command(THD *thd) case COM_CHANGE_USER: { thread_safe_increment(com_other,&LOCK_thread_count); - char *user= (char*) packet+1; + char *user= (char*) packet; char *passwd= strend(user)+1; char *db= strend(passwd)+1; /* Save user and privileges */ uint save_master_access=thd->master_access; uint save_db_access= thd->db_access; + uint save_db_length= thd->db_length; char *save_user= thd->user; char *save_priv_user= thd->priv_user; char *save_db= thd->db; + USER_CONN *save_uc= thd->user_connect; if ((uint) ((uchar*) db - net->read_pos) > packet_length) { // Check if protocol is ok @@ -803,11 +949,13 @@ bool do_command(THD *thd) thd->master_access=save_master_access; thd->db_access=save_db_access; thd->db=save_db; + thd->db_length=save_db_length; thd->user=save_user; thd->priv_user=save_priv_user; break; } - decrease_user_connections (save_user, thd->host); + if (max_connections && save_uc) + decrease_user_connections(save_uc); x_free((gptr) save_db); x_free((gptr) save_user); thd->password=test(passwd[0]); @@ -816,28 +964,44 @@ bool do_command(THD *thd) case COM_QUERY: { + packet_length--; // Remove end null + /* Remove garage at start and end of query */ + while (isspace(packet[0]) && packet_length > 0) + { + packet++; + packet_length--; + } char *pos=packet+packet_length; // Point at end null - /* Remove garage at end of query */ - while (packet_length > 0 && pos[-1] == ';') + while (packet_length > 0 && (pos[-1] == ';' || isspace(pos[-1]))) { pos--; packet_length--; } - *pos=0; - if (!(thd->query= (char*) thd->memdup((gptr) (packet+1),packet_length))) + /* We must allocate some extra memory for query cache */ + if (!(thd->query= (char*) thd->memdup_w_gap((gptr) (packet), + packet_length, + thd->db_length+2))) 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); mysql_log.write(thd,command,"%s",thd->query); DBUG_PRINT("query",("%s",thd->query)); - mysql_parse(thd,thd->query,packet_length-1); + if (mqh_used && thd->user_connect && check_mqh(thd)) + { + error = TRUE; // Abort client + net->error = 0; // Don't give abort message + break; + } + /* thd->query_length is set by mysql_parse() */ + mysql_parse(thd,thd->query,packet_length); if (!(specialflag & SPECIAL_NO_PRIOR)) my_pthread_setprio(pthread_self(),WAIT_PRIOR); DBUG_PRINT("info",("query ready")); break; } - case COM_FIELD_LIST: // This isn't actually neaded + case COM_FIELD_LIST: // This isn't actually needed #ifdef DONT_ALLOW_SHOW_COMMANDS send_error(&thd->net,ER_NOT_ALLOWED_COMMAND); /* purecov: inspected */ break; @@ -853,8 +1017,11 @@ 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); + packet=strend(packet)+1; + // command not cachable => no gap for data base name + if (!(thd->query=fields=thd->memdup(packet,thd->query_length+1))) + break; mysql_log.write(thd,command,"%s %s",table_list.real_name,fields); remove_escape(table_list.real_name); // This can't have wildcards @@ -875,10 +1042,10 @@ bool do_command(THD *thd) error=TRUE; // End server break; - case COM_CREATE_DB: + case COM_CREATE_DB: // QQ: To be removed { - char *db=thd->strdup(packet+1); thread_safe_increment(com_stat[SQLCOM_CREATE_DB],&LOCK_thread_count); + char *db=thd->strdup(packet); // null test to handle EOM if (!db || !stripp_sp(db) || check_db_name(db)) { @@ -887,44 +1054,49 @@ bool do_command(THD *thd) } if (check_access(thd,CREATE_ACL,db,0,1)) break; - mysql_log.write(thd,command,packet+1); - mysql_create_db(thd,db,0); + mysql_log.write(thd,command,packet); + mysql_create_db(thd,db,0,0); break; } - case COM_DROP_DB: + case COM_DROP_DB: // QQ: To be removed { - char *db=thd->strdup(packet+1); thread_safe_increment(com_stat[SQLCOM_DROP_DB],&LOCK_thread_count); + char *db=thd->strdup(packet); // null test to handle EOM if (!db || !stripp_sp(db) || check_db_name(db)) { net_printf(&thd->net,ER_WRONG_DB_NAME, db ? db : "NULL"); break; } - if (check_access(thd,DROP_ACL,db,0,1) || end_active_trans(thd)) + if (thd->locked_tables || thd->active_transaction()) + { + send_error(&thd->net,ER_LOCK_OR_ACTIVE_TRANSACTION); break; + } mysql_log.write(thd,command,db); - mysql_rm_db(thd,db,0); + mysql_rm_db(thd,db,0,0); break; } case COM_BINLOG_DUMP: { thread_safe_increment(com_other,&LOCK_thread_count); slow_command = TRUE; - if(check_access(thd, FILE_ACL, any_db)) + if (check_access(thd, FILE_ACL, any_db)) break; mysql_log.write(thd,command, 0); 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)); + thd->server_id=0; /* avoid suicide */ + 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); + unregister_slave(thd,1,1); // fake COM_QUIT -- if we get here, the thread needs to terminate error = TRUE; net->error = 0; @@ -932,8 +1104,8 @@ bool do_command(THD *thd) } case COM_REFRESH: { - uint options=(uchar) packet[1]; thread_safe_increment(com_stat[SQLCOM_FLUSH],&LOCK_thread_count); + ulong options= (ulong) (uchar) packet[0]; if (check_access(thd,RELOAD_ACL,any_db)) break; mysql_log.write(thd,command,NullS); @@ -959,6 +1131,7 @@ bool do_command(THD *thd) close_connection(net); close_thread_tables(thd); // Free before kill free_root(&thd->mem_root,MYF(0)); + free_root(&thd->transaction.mem_root,MYF(0)); kill_mysql(); error=TRUE; break; @@ -970,7 +1143,7 @@ bool do_command(THD *thd) char buff[200]; ulong uptime = (ulong) (thd->start_time - start_time); sprintf((char*) buff, - "Uptime: %ld Threads: %d Questions: %lu Slow queries: %ld Opens: %ld Flush tables: %ld Open tables: %d Queries per second avg: %.3f", + "Uptime: %ld Threads: %d Questions: %lu Slow queries: %ld Opens: %ld Flush tables: %ld Open tables: %u Queries per second avg: %.3f", uptime, (int) thread_count,thd->query_id,long_query_count, opened_tables,refresh_version, cached_tables(), @@ -999,7 +1172,7 @@ bool do_command(THD *thd) case COM_PROCESS_KILL: { thread_safe_increment(com_stat[SQLCOM_KILL],&LOCK_thread_count); - ulong id=(ulong) uint4korr(packet+1); + ulong id=(ulong) uint4korr(packet); kill_one_thread(thd,id); break; } @@ -1037,7 +1210,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))) { @@ -1068,33 +1241,47 @@ 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_lex.table_list.first; + SELECT_LEX *select_lex = lex->select; DBUG_ENTER("mysql_execute_command"); if (thd->slave_thread) { - // skip if we are in the slave thread, some table - // rules have been given and the table list says the query should not be - // replicated - if(table_rules_on && tables && !tables_ok(thd,tables)) + /* + Skip if we are in the slave thread, some table rules have been + given and the table list says the query should not be replicated + */ + if (table_rules_on && tables && !tables_ok(thd,tables)) DBUG_VOID_RETURN; - // this is a workaround to deal with the shortcoming - // in 3.23.44-3.23.46 masters - // in RELEASE_LOCK() logging. We re-write SELECT RELEASE_LOCK() as - // DO RELEASE_LOCK() +#ifndef TO_BE_DELETED + /* + This is a workaround to deal with the shortcoming in 3.23.44-3.23.46 + masters in RELEASE_LOCK() logging. We re-write SELECT RELEASE_LOCK() + as DO RELEASE_LOCK() + */ if (lex->sql_command == SQLCOM_SELECT) { lex->sql_command = SQLCOM_DO; - lex->insert_list = &lex->item_list; + lex->insert_list = &select_lex->item_list; } +#endif } + /* + Skip if we are in the slave thread, some table rules have been given + and the table list says the query should not be replicated + */ + if ((lex->select_lex.next && create_total_list(thd,lex,&tables)) || + (table_rules_on && tables && thd->slave_thread && + !tables_ok(thd,tables))) + DBUG_VOID_RETURN; + thread_safe_increment(com_stat[lex->sql_command],&LOCK_thread_count); switch (lex->sql_command) { case SQLCOM_SELECT: { select_result *result; - if (lex->options & SELECT_DESCRIBE) + if (select_lex->options & SELECT_DESCRIBE) lex->exchange=0; if (tables) { @@ -1112,10 +1299,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) { @@ -1140,8 +1329,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; } @@ -1159,22 +1348,11 @@ mysql_execute_command(void) if (!(res=open_and_lock_tables(thd,tables))) { - res=mysql_select(thd,tables,lex->item_list, - lex->where, - (ORDER*) lex->order_list.first, - (ORDER*) lex->group_list.first, - lex->having, - (ORDER*) lex->proc_list.first, - lex->options | thd->options, - result); - if (res) - result->abort(); + query_cache_store_query(thd, tables); + res=handle_select(thd, lex, result); } - delete result; -#ifdef DELETE_ITEMS - delete lex->having; - delete lex->where; -#endif + else + delete result; break; } case SQLCOM_DO: @@ -1186,54 +1364,86 @@ mysql_execute_command(void) break; case SQLCOM_PURGE: - { - if (check_process_priv(thd)) - goto error; - res = purge_master_logs(thd, lex->to_log); - break; - } + { + if (check_process_priv(thd)) + goto error; + res = purge_master_logs(thd, lex->to_log); + break; + } + case SQLCOM_SHOW_NEW_MASTER: + { + if (check_access(thd, FILE_ACL, any_db)) + goto error; + res = show_new_master(thd); + 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) || - check_table_access(thd,SELECT_ACL, tables) || - check_access(thd, FILE_ACL, any_db)) - goto error; /* purecov: inspected */ - res = mysql_backup_table(thd, tables); + { + if (check_db_used(thd,tables) || + check_table_access(thd,SELECT_ACL, tables) || + check_access(thd, FILE_ACL, any_db)) + goto error; /* purecov: inspected */ + res = mysql_backup_table(thd, tables); - break; - } + break; + } case SQLCOM_RESTORE_TABLE: - { - if (check_db_used(thd,tables) || - check_table_access(thd,INSERT_ACL, tables) || - check_access(thd, FILE_ACL, any_db)) - goto error; /* purecov: inspected */ - res = mysql_restore_table(thd, tables); - break; - } + { + if (check_db_used(thd,tables) || + check_table_access(thd,INSERT_ACL, tables) || + check_access(thd, FILE_ACL, any_db)) + goto error; /* purecov: inspected */ + res = mysql_restore_table(thd, tables); + break; + } case SQLCOM_CHANGE_MASTER: - { - if(check_access(thd, PROCESS_ACL, any_db)) - goto error; - res = change_master(thd); - break; - } + { + if (check_access(thd, PROCESS_ACL, any_db)) + goto error; + LOCK_ACTIVE_MI; + res = change_master(thd,active_mi); + UNLOCK_ACTIVE_MI; + break; + } case SQLCOM_SHOW_SLAVE_STAT: - { - if (check_process_priv(thd)) - goto error; - res = show_master_info(thd); - break; - } + { + if (check_process_priv(thd)) + goto error; + LOCK_ACTIVE_MI; + res = show_master_info(thd,active_mi); + UNLOCK_ACTIVE_MI; + break; + } case SQLCOM_SHOW_MASTER_STAT: - { - if (check_process_priv(thd)) - goto error; - res = show_binlog_info(thd); - break; - } + { + if (check_process_priv(thd)) + goto error; + 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) tables->db=thd->db; if (check_access(thd,CREATE_ACL,tables->db,&tables->grant.privilege)) @@ -1246,21 +1456,23 @@ mysql_execute_command(void) bool error=check_grant(thd,CREATE_ACL,tables); tables->next=tmp_table_list; if (error) - goto error; + goto error; } if (strlen(tables->name) > NAME_LEN) { net_printf(&thd->net,ER_WRONG_TABLE_NAME,tables->name); break; } - - thd->last_nx_table = tables->real_name; - thd->last_nx_db = tables->db; - if (fetch_nx_table(thd, &glob_mi)) - break; // fetch_nx_table did send the error to the client - send_ok(&thd->net); + LOCK_ACTIVE_MI; + // fetch_master_table will send the error to the client on failure + if (!fetch_master_table(thd, tables->db, tables->real_name, + active_mi, 0)) + { + send_ok(&thd->net); + } + UNLOCK_ACTIVE_MI; break; - + } case SQLCOM_CREATE_TABLE: if (!tables->db) tables->db=thd->db; @@ -1285,12 +1497,25 @@ mysql_execute_command(void) res=0; break; } - if (lex->item_list.elements) // With select +#ifndef HAVE_READLINK + lex->create_info.data_file_name=lex->create_info.index_file_name=0; +#else + /* Fix names if symlinked tables */ + if (append_file_to_dir(thd, &lex->create_info.data_file_name, + tables->name) || + append_file_to_dir(thd,&lex->create_info.index_file_name, + tables->name)) + { + res=-1; + break; + } +#endif + if (select_lex->item_list.elements) // With select { select_result *result; if (!(lex->create_info.options & HA_LEX_CREATE_TMP_TABLE) && - check_dup(thd,tables->db,tables->real_name,tables->next)) + check_dup(tables->db, tables->real_name, tables->next)) { net_printf(&thd->net,ER_INSERT_TABLE_USED,tables->real_name); DBUG_VOID_RETURN; @@ -1303,31 +1528,22 @@ 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 + /* Skip first table, which is the table we are creating */ + lex->select_lex.table_list.first= + (byte*) (((TABLE_LIST *) lex->select_lex.table_list.first)->next); if (!(res=open_and_lock_tables(thd,tables->next))) { - if ((result=new select_create(tables->db ? tables->db : thd->db, - tables->real_name, &lex->create_info, - lex->create_list, - lex->key_list, - lex->item_list,lex->duplicates))) - { - res=mysql_select(thd,tables->next,lex->item_list, - lex->where, - (ORDER*) lex->order_list.first, - (ORDER*) lex->group_list.first, - lex->having, - (ORDER*) lex->proc_list.first, - lex->options | thd->options, - result); - if (res) - result->abort(); - delete result; - } + if ((result=new select_create(tables->db ? tables->db : thd->db, + tables->real_name, &lex->create_info, + lex->create_list, + lex->key_list, + select_lex->item_list,lex->duplicates))) + res=handle_select(thd, lex, result); else res= -1; } @@ -1356,12 +1572,19 @@ mysql_execute_command(void) break; case SQLCOM_SLAVE_START: - start_slave(thd); + { + LOCK_ACTIVE_MI; + start_slave(thd,active_mi,1 /* net report*/); + UNLOCK_ACTIVE_MI; break; + } case SQLCOM_SLAVE_STOP: - stop_slave(thd); + { + LOCK_ACTIVE_MI; + stop_slave(thd,active_mi,1/* net report*/); + UNLOCK_ACTIVE_MI; break; - + } case SQLCOM_ALTER_TABLE: #if defined(DONT_ALLOW_SHOW_COMMANDS) send_error(&thd->net,ER_NOT_ALLOWED_COMMAND); /* purecov: inspected */ @@ -1377,10 +1600,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)) @@ -1396,22 +1619,27 @@ 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 @@ -1435,11 +1663,12 @@ mysql_execute_command(void) old_list.next=new_list.next=0; if (check_grant(thd,ALTER_ACL,&old_list) || (!test_all_bits(table->next->grant.privilege, - INSERT_ACL | CREATE_ACL) && + INSERT_ACL | CREATE_ACL) && check_grant(thd,INSERT_ACL | CREATE_ACL, &new_list))) goto error; } } + query_cache_invalidate3(thd, tables, 0); if (end_active_trans(thd)) res= -1; else if (mysql_rename_tables(thd,tables)) @@ -1473,21 +1702,23 @@ mysql_execute_command(void) } #endif case SQLCOM_REPAIR: - { - if (check_db_used(thd,tables) || - check_table_access(thd,SELECT_ACL | INSERT_ACL, tables)) - goto error; /* purecov: inspected */ - res = mysql_repair_table(thd, tables, &lex->check_opt); - break; - } + { + if (check_db_used(thd,tables) || + check_table_access(thd,SELECT_ACL | INSERT_ACL, tables)) + goto error; /* purecov: inspected */ + res = mysql_repair_table(thd, tables, &lex->check_opt); + query_cache_invalidate3(thd, tables, 0); + break; + } case SQLCOM_CHECK: - { - if (check_db_used(thd,tables) || - check_table_access(thd, SELECT_ACL | EXTRA_ACL , tables)) - goto error; /* purecov: inspected */ - res = mysql_check_table(thd, tables, &lex->check_opt); - break; - } + { + if (check_db_used(thd,tables) || + check_table_access(thd, SELECT_ACL | EXTRA_ACL , tables)) + goto error; /* purecov: inspected */ + res = mysql_check_table(thd, tables, &lex->check_opt); + query_cache_invalidate3(thd, tables, 0); + break; + } case SQLCOM_ANALYZE: { if (check_db_used(thd,tables) || @@ -1529,23 +1760,73 @@ 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, - lex->value_list, - lex->where, - lex->select_limit, - lex->duplicates, - lex->lock_option); + if (select_lex->table_list.elements == 1) + { + res = mysql_update(thd,tables, + select_lex->item_list, + lex->value_list, + 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; + } + else + { + multi_update *result; + uint table_count; + TABLE_LIST *auxi; + lex->sql_command=SQLCOM_MULTI_UPDATE; + for (auxi=(TABLE_LIST*) tables, table_count=0 ; auxi ; auxi=auxi->next) + { + table_count++; + auxi->lock_type=TL_WRITE; + } + if (select_lex->order_list.elements || (select_lex->select_limit && select_lex->select_limit < INT_MAX)) + { + send_error(&thd->net,ER_NOT_ALLOWED_COMMAND); /// will have to come up with something better eventually + DBUG_VOID_RETURN; + } + tables->grant.want_privilege=(SELECT_ACL & ~tables->grant.privilege); + if ((res=open_and_lock_tables(thd,tables))) + break; + if (!setup_fields(thd,tables,select_lex->item_list,1,0,0) && + !setup_fields(thd,tables,lex->value_list,0,0,0) && ! thd->fatal_error && + (result=new multi_update(thd,tables,select_lex->item_list,lex->duplicates, + lex->lock_option, table_count))) + { + List <Item> total_list; + List_iterator <Item> field_list(select_lex->item_list); + List_iterator <Item> value_list(lex->value_list); + Item *item; + while ((item=field_list++)) + total_list.push_back(item); + while ((item=value_list++)) + total_list.push_back(item); + + res=mysql_select(thd,tables,total_list, + select_lex->where, + (ORDER *)NULL,(ORDER *)NULL,(Item *)NULL, + (ORDER *)NULL, + select_lex->options | thd->options | + SELECT_NO_JOIN_CACHE, + result); + delete result; + } + else + res= -1; // Error is not sent + close_thread_tables(thd); + } + break; case SQLCOM_INSERT: if (check_access(thd,INSERT_ACL,tables->db,&tables->grant.privilege)) goto error; /* purecov: inspected */ @@ -1570,6 +1851,7 @@ mysql_execute_command(void) case SQLCOM_REPLACE_SELECT: case SQLCOM_INSERT_SELECT: { + // Check that we have modify privileges for the first table and // select privileges for the rest { @@ -1587,51 +1869,50 @@ 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)) + if (check_dup(tables->db, tables->real_name, tables->next)) { net_printf(&thd->net,ER_INSERT_TABLE_USED,tables->real_name); DBUG_VOID_RETURN; } - tables->lock_type=TL_WRITE; // update first table + tables->lock_type=TL_WRITE; // update first table { TABLE_LIST *table; for (table = tables->next ; table ; table=table->next) table->lock_type= lex->lock_option; } - if (!(res=open_and_lock_tables(thd,tables))) + + /* Skip first table, which is the table we are inserting in */ + lex->select_lex.table_list.first= + (byte*) (((TABLE_LIST *) lex->select_lex.table_list.first)->next); + if (!(res=open_and_lock_tables(thd, tables))) { if ((result=new select_insert(tables->table,&lex->field_list, - lex->sql_command == SQLCOM_REPLACE_SELECT ? - DUP_REPLACE : DUP_IGNORE))) - { - res=mysql_select(thd,tables->next,lex->item_list, - lex->where, - (ORDER*) lex->order_list.first, - (ORDER*) lex->group_list.first, - lex->having, - (ORDER*) lex->proc_list.first, - lex->options | thd->options, - result); - delete result; - } - else - res= -1; + lex->duplicates))) + res=handle_select(thd,lex,result); } -#ifdef DELETE_ITEMS - delete lex->having; - delete lex->where; -#endif + else + res= -1; break; } case SQLCOM_TRUNCATE: - lex->where=0; - lex->select_limit=HA_POS_ERROR; - /* Fall through */ + if (check_access(thd,DELETE_ACL,tables->db,&tables->grant.privilege)) + goto error; /* purecov: inspected */ + /* + Don't allow this within a transaction because we want to use + re-generate table + */ + if (thd->locked_tables || thd->active_transaction()) + { + send_error(&thd->net,ER_LOCK_OR_ACTIVE_TRANSACTION,NullS); + goto error; + } + res=mysql_truncate(thd,tables); + break; case SQLCOM_DELETE: { if (check_access(thd,DELETE_ACL,tables->db,&tables->grant.privilege)) @@ -1644,20 +1925,87 @@ 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_DROP_TABLE: + case SQLCOM_DELETE_MULTI: + { + 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) { - if (check_table_access(thd,DROP_ACL,tables)) - goto error; /* purecov: inspected */ - if (end_active_trans(thd)) - res= -1; - else - res = mysql_rm_table(thd,tables,lex->drop_if_exists); + 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 (!thd->fatal_error && (result=new multi_delete(thd,aux_tables, + lex->lock_option,table_count))) + { + res=mysql_select(thd,tables,select_lex->item_list, + select_lex->where, + (ORDER *)NULL,(ORDER *)NULL,(Item *)NULL, + (ORDER *)NULL, + select_lex->options | thd->options | + SELECT_NO_JOIN_CACHE, + result); + delete result; + } + else + res= -1; // Error is not sent + close_thread_tables(thd); break; + } + case SQLCOM_DROP_TABLE: + { + if (check_table_access(thd,DROP_ACL,tables)) + goto error; /* purecov: inspected */ + if (end_active_trans(thd)) + res= -1; + else + res = mysql_rm_table(thd,tables,lex->drop_if_exists); + } + break; case SQLCOM_DROP_INDEX: if (!tables->db) tables->db=thd->db; @@ -1672,7 +2020,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) && @@ -1708,13 +2056,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 */ @@ -1729,34 +2076,32 @@ 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 */ DBUG_VOID_RETURN; #else { - char *db=tables->db ? tables->db : thd->db; - if (!db) + char *db=tables->db; + if (!*db) { send_error(&thd->net,ER_NO_DB_ERROR); /* purecov: inspected */ goto error; /* purecov: inspected */ } remove_escape(db); // Fix escaped '_' remove_escape(tables->name); - if (!tables->db) - tables->db=thd->db; if (check_access(thd,SELECT_ACL | EXTRA_ACL,db,&thd->col_access)) goto error; /* purecov: inspected */ tables->grant.privilege=thd->col_access; @@ -1774,7 +2119,7 @@ mysql_execute_command(void) DBUG_VOID_RETURN; #else { - char *db=tables->db ? tables->db : thd->db; + char *db=tables->db; if (!db) { send_error(&thd->net,ER_NO_DB_ERROR); /* purecov: inspected */ @@ -1794,7 +2139,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: { @@ -1815,7 +2160,7 @@ mysql_execute_command(void) goto error; } if (check_access(thd,privilege,tables->db,&tables->grant.privilege) || - grant_option && check_grant(thd,privilege,tables)) + grant_option && check_grant(thd,privilege,tables)) goto error; } res=mysql_load(thd, lex->exchange, tables, lex->field_list, @@ -1824,22 +2169,17 @@ mysql_execute_command(void) } case SQLCOM_SET_OPTION: { - uint org_options=thd->options; - thd->options=lex->options; + ulong org_options=thd->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) - { - thd->gemini_spin_retries= lex->gemini_spin_retries; - ha_set_spin_retries(thd->gemini_spin_retries); - } DBUG_PRINT("info",("options: %ld limit: %ld", 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)) { @@ -1873,13 +2213,7 @@ mysql_execute_command(void) thd->options&= ~(ulong) (OPTION_TABLE_LOCK); } if (thd->global_read_lock) - { - thd->global_read_lock=0; - pthread_mutex_lock(&LOCK_open); - global_read_lock--; - pthread_cond_broadcast(&COND_refresh); - pthread_mutex_unlock(&LOCK_open); - } + unlock_global_read_lock(thd); send_ok(&thd->net); break; case SQLCOM_LOCK_TABLES: @@ -1891,6 +2225,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; thd->options|= OPTION_TABLE_LOCK; if (!(res=open_and_lock_tables(thd,tables))) @@ -1904,30 +2240,34 @@ mysql_execute_command(void) thd->in_lock_tables=0; break; case SQLCOM_CREATE_DB: + { + if (!stripp_sp(lex->name) || check_db_name(lex->name)) { - if (!stripp_sp(lex->name) || check_db_name(lex->name)) - { - net_printf(&thd->net,ER_WRONG_DB_NAME, lex->name); - break; - } - if (check_access(thd,CREATE_ACL,lex->name,0,1)) - break; - mysql_create_db(thd,lex->name,lex->create_info.options); + net_printf(&thd->net,ER_WRONG_DB_NAME, lex->name); break; } + if (check_access(thd,CREATE_ACL,lex->name,0,1)) + break; + res=mysql_create_db(thd,lex->name,lex->create_info.options,0); + break; + } case SQLCOM_DROP_DB: + { + if (!stripp_sp(lex->name) || check_db_name(lex->name)) { - if (!stripp_sp(lex->name) || check_db_name(lex->name)) - { - net_printf(&thd->net,ER_WRONG_DB_NAME, lex->name); - break; - } - if (check_access(thd,DROP_ACL,lex->name,0,1) || - end_active_trans(thd)) - break; - mysql_rm_db(thd,lex->name,lex->drop_if_exists); + net_printf(&thd->net,ER_WRONG_DB_NAME, lex->name); break; } + if (check_access(thd,DROP_ACL,lex->name,0,1)) + break; + if (thd->locked_tables || thd->active_transaction()) + { + send_error(&thd->net,ER_LOCK_OR_ACTIVE_TRANSACTION); + goto error; + } + res=mysql_rm_db(thd,lex->name,lex->drop_if_exists,0); + break; + } case SQLCOM_CREATE_FUNCTION: if (check_access(thd,INSERT_ACL,"mysql",0,1)) break; @@ -1948,78 +2288,83 @@ mysql_execute_command(void) res= -1; #endif break; - case SQLCOM_REVOKE: - case SQLCOM_GRANT: - { - 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->grant.privilege : 0, - tables ? 0 : 1)) - goto error; - - /* Check that the user isn't trying to change a password for another - user if he doesn't have UPDATE privilege to the MySQL database */ - - if (thd->user) // If not replication - { - LEX_USER *user; - List_iterator <LEX_USER> user_list(lex->users_list); - while ((user=user_list++)) - { - if (user->password.str && - (strcmp(thd->user,user->user.str) || - user->host.str && - my_strcasecmp(user->host.str, thd->host ? thd->host : thd->ip))) - { - if (check_access(thd, UPDATE_ACL, "mysql",0,1)) - goto error; - break; // We are allowed to do changes - } - } - } - if (tables) - { - if (grant_option && check_grant(thd, - (lex->grant | lex->grant_tot_col | - GRANT_ACL), - tables)) - goto error; - res = mysql_table_grant(thd,tables,lex->users_list, lex->columns, - lex->grant, lex->sql_command == SQLCOM_REVOKE); - if(!res) - { - 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); - } - } - } - else - { - if (lex->columns.elements) - { - net_printf(&thd->net,ER_ILLEGAL_GRANT_FOR_TABLE); - res=1; - } - else - res = mysql_grant(thd, lex->db, lex->users_list, lex->grant, - lex->sql_command == SQLCOM_REVOKE); - if (!res) - { - 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); - } - } - } - break; - } + case SQLCOM_REVOKE: + case SQLCOM_GRANT: + { + if (check_access(thd, lex->grant | lex->grant_tot_col | GRANT_ACL, + tables && tables->db ? tables->db : select_lex->db, + tables ? &tables->grant.privilege : 0, + tables ? 0 : 1)) + goto error; + + /* Check that the user isn't trying to change a password for another + user if he doesn't have UPDATE privilege to the MySQL database */ + + if (thd->user) // If not replication + { + LEX_USER *user; + List_iterator <LEX_USER> user_list(lex->users_list); + while ((user=user_list++)) + { + if (user->password.str && + (strcmp(thd->user,user->user.str) || + user->host.str && + my_strcasecmp(user->host.str, thd->host_or_ip))) + { + if (check_access(thd, UPDATE_ACL, "mysql",0,1)) + goto error; + break; // We are allowed to do changes + } + } + } + if (tables) + { + if (grant_option && check_grant(thd, + (lex->grant | lex->grant_tot_col | + GRANT_ACL), + tables)) + goto error; + if (!(res = mysql_table_grant(thd,tables,lex->users_list, lex->columns, + lex->grant, + lex->sql_command == SQLCOM_REVOKE))) + { + 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); + } + } + } + else + { + if (lex->columns.elements) + { + send_error(&thd->net,ER_ILLEGAL_GRANT_FOR_TABLE); + res=1; + } + else + res = mysql_grant(thd, select_lex->db, lex->users_list, lex->grant, + lex->sql_command == SQLCOM_REVOKE); + if (!res) + { + 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 (mqh_used && (lex->mqh.questions || lex->mqh.updates || lex->mqh.connections) && lex->sql_command == SQLCOM_GRANT) + { + List_iterator <LEX_USER> str_list(lex->users_list); + LEX_USER *user; + while ((user=str_list++)) + reset_mqh(thd,user,&(lex->mqh)); + } + } + } + break; + } case SQLCOM_FLUSH: case SQLCOM_RESET: if (check_access(thd,RELOAD_ACL,any_db) || check_db_used(thd, tables)) @@ -2034,13 +2379,40 @@ mysql_execute_command(void) break; case SQLCOM_SHOW_GRANTS: res=0; - if ((thd->priv_user && !strcmp(thd->priv_user,lex->grant_user->user.str)) || + if ((thd->priv_user && + !strcmp(thd->priv_user,lex->grant_user->user.str)) || !check_access(thd, SELECT_ACL, "mysql",0,1)) { 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 (thd->locked_tables) + { + thd->lock=thd->locked_tables; + thd->locked_tables=0; // Will be automaticly closed + close_thread_tables(thd); // Free tables + } if (end_active_trans(thd)) { res= -1; @@ -2059,13 +2431,18 @@ mysql_execute_command(void) even if there is a problem with the OPTION_AUTO_COMMIT flag (Which of course should never happen...) */ + { thd->options&= ~(ulong) (OPTION_BEGIN | OPTION_STATUS_NO_TRANS_UPDATE); thd->server_status&= ~SERVER_STATUS_IN_TRANS; if (!ha_commit(thd)) + { send_ok(&thd->net); + } else res= -1; + thd->transaction.cleanup(); break; + } case SQLCOM_ROLLBACK: thd->server_status&= ~SERVER_STATUS_IN_TRANS; if (!ha_rollback(thd)) @@ -2078,6 +2455,7 @@ mysql_execute_command(void) else res= -1; thd->options&= ~(ulong) (OPTION_BEGIN | OPTION_STATUS_NO_TRANS_UPDATE); + thd->transaction.cleanup(); break; default: /* Impossible */ send_ok(&thd->net); @@ -2104,7 +2482,7 @@ error: bool check_access(THD *thd,uint want_access,const char *db, uint *save_priv, - bool dont_check_global_grants) + bool dont_check_global_grants, bool no_errors) { uint db_access,dummy; if (save_priv) @@ -2112,9 +2490,10 @@ check_access(THD *thd,uint want_access,const char *db, uint *save_priv, else save_priv= &dummy; - if (!db && !thd->db && !dont_check_global_grants) + if ((!db || !db[0]) && !thd->db && !dont_check_global_grants) { - send_error(&thd->net,ER_NO_DB_ERROR); /* purecov: tested */ + if (!no_errors) + send_error(&thd->net,ER_NO_DB_ERROR); /* purecov: tested */ return TRUE; /* purecov: tested */ } @@ -2126,16 +2505,17 @@ check_access(THD *thd,uint want_access,const char *db, uint *save_priv, if ((want_access & ~thd->master_access) & ~(DB_ACLS | EXTRA_ACL) || ! db && dont_check_global_grants) { // We can never grant this - net_printf(&thd->net,ER_ACCESS_DENIED_ERROR, - thd->priv_user, - thd->host ? thd->host : (thd->ip ? thd->ip : "unknown"), - thd->password ? ER(ER_YES) : ER(ER_NO));/* purecov: tested */ + if (!no_errors) + net_printf(&thd->net,ER_ACCESS_DENIED_ERROR, + thd->priv_user, + thd->host_or_ip, + thd->password ? ER(ER_YES) : ER(ER_NO));/* purecov: tested */ return TRUE; /* purecov: tested */ } if (db == any_db) return FALSE; // Allow select on anything - + if (db && (!thd->db || strcmp(db,thd->db))) db_access=acl_get(thd->host, thd->ip, (char*) &thd->remote.sin_addr, thd->priv_user, db); /* purecov: inspected */ @@ -2149,10 +2529,11 @@ check_access(THD *thd,uint want_access,const char *db, uint *save_priv, ((grant_option && !dont_check_global_grants) && !(want_access & ~TABLE_ACLS))) return FALSE; /* Ok */ - net_printf(&thd->net,ER_DBACCESS_DENIED_ERROR, - thd->priv_user, - thd->host ? thd->host : (thd->ip ? thd->ip : "unknown"), - db ? db : thd->db ? thd->db : "unknown"); /* purecov: tested */ + if (!no_errors) + net_printf(&thd->net,ER_DBACCESS_DENIED_ERROR, + thd->priv_user, + thd->host_or_ip, + db ? db : thd->db ? thd->db : "unknown"); /* purecov: tested */ return TRUE; /* purecov: tested */ } @@ -2169,7 +2550,8 @@ bool check_process_priv(THD *thd) */ bool -check_table_access(THD *thd,uint want_access,TABLE_LIST *tables) +check_table_access(THD *thd,uint want_access,TABLE_LIST *tables, + bool no_errors) { uint found=0,found_access=0; TABLE_LIST *org_tables=tables; @@ -2184,20 +2566,20 @@ check_table_access(THD *thd,uint want_access,TABLE_LIST *tables) tables->grant.privilege=found_access; else { - if (check_access(thd,want_access,tables->db,&tables->grant.privilege)) + if (check_access(thd,want_access,tables->db,&tables->grant.privilege, + 0, no_errors)) return TRUE; // Access denied found_access=tables->grant.privilege; found=1; } } - else if (check_access(thd,want_access,tables->db,&tables->grant.privilege)) + else if (check_access(thd,want_access,tables->db,&tables->grant.privilege, + 0, no_errors)) 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), no_errors); return FALSE; } @@ -2301,42 +2683,75 @@ bool my_yyoverflow(short **yyss, YYSTYPE **yyvs, int *yystacksize) /**************************************************************************** - Initialize global thd variables neaded for query + Initialize global thd variables needed for query ****************************************************************************/ 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->free_list=0; - - thd->lex.table_list.first=0; - thd->lex.table_list.next= (byte**) &thd->lex.table_list.first; + thd->lex.select_lex.table_list.elements=0; + thd->free_list=0; thd->lex.union_option=0; + 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->lex.select_lex.next=0; 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; + thd->safe_to_cache_query=1; DBUG_VOID_RETURN; } 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; + SELECT_LEX *select_lex = lex->select; + select_lex->where=select_lex->having=0; + select_lex->select_limit=lex->thd->default_select_limit; + select_lex->offset_limit=0; + select_lex->options=0; + select_lex->linkage=UNSPECIFIED_TYPE; 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; } +bool +mysql_new_select(LEX *lex) +{ + SELECT_LEX *select_lex = (SELECT_LEX *) lex->thd->calloc(sizeof(SELECT_LEX)); + if (!select_lex) + return 1; + lex->select->next=select_lex; + lex->select=select_lex; + select_lex->table_list.next= (byte**) &select_lex->table_list.first; + select_lex->item_list.empty(); + select_lex->when_list.empty(); + select_lex->expr_list.empty(); + select_lex->interval_list.empty(); + select_lex->use_index.empty(); + select_lex->ftfunc_list.empty(); + return 0; +} + +void mysql_init_multi_delete(LEX *lex) +{ + lex->sql_command = SQLCOM_DELETE_MULTI; + mysql_init_select(lex); + lex->select->select_limit=HA_POS_ERROR; + 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); +} void mysql_parse(THD *thd,char *inBuf,uint length) @@ -2345,12 +2760,27 @@ mysql_parse(THD *thd,char *inBuf,uint length) mysql_init_query(thd); thd->query_length = length; - LEX *lex=lex_start(thd, (uchar*) inBuf, length); - if (!yyparse() && ! thd->fatal_error) - mysql_execute_command(); - thd->proc_info="freeing items"; - free_items(thd); /* Free strings used by items */ - lex_end(lex); + if (query_cache_send_result_to_client(thd, inBuf, length) <= 0) + { + LEX *lex=lex_start(thd, (uchar*) inBuf, length); + if (!yyparse() && ! thd->fatal_error) + { + if (mqh_used && thd->user_connect && check_mqh(thd)) + { + thd->net.error = 0; + } + else + { + mysql_execute_command(); + query_cache_end_of_result(&thd->net); + } + } + else + query_cache_abort(&thd->net); + thd->proc_info="freeing items"; + free_items(thd); /* Free strings used by items */ + lex_end(lex); + } DBUG_VOID_RETURN; } @@ -2605,7 +3035,7 @@ bool add_field_to_list(char *field_name, enum_field_types type, if (new_field->length >= MAX_FIELD_WIDTH || (!new_field->length && !(new_field->flags & BLOB_FLAG) && - type != FIELD_TYPE_STRING)) + type != FIELD_TYPE_STRING && type != FIELD_TYPE_VAR_STRING)) { net_printf(&thd->net,ER_TOO_BIG_FIELDLENGTH,field_name, MAX_FIELD_WIDTH-1); /* purecov: inspected */ @@ -2656,6 +3086,8 @@ add_proc_to_list(Item *item) static void remove_escape(char *name) { + if (!*name) // For empty DB names + return; char *to; #ifdef USE_MB char *strend=name+(uint) strlen(name); @@ -2675,7 +3107,7 @@ static void remove_escape(char *name) } #endif if (*name == '\\' && name[1]) - name++; // Skipp '\\' + name++; // Skip '\\' *to++= *name; } *to=0; @@ -2714,7 +3146,6 @@ TABLE_LIST *add_table_to_list(Table_ident *table, LEX_STRING *alias, register TABLE_LIST *ptr; THD *thd=current_thd; char *alias_str; - const char *current_db; DBUG_ENTER("add_table_to_list"); if (!table) @@ -2729,15 +3160,35 @@ TABLE_LIST *add_table_to_list(Table_ident *table, LEX_STRING *alias, } if (!alias) /* Alias is case sensitive */ - if (!(alias_str=sql_strmake(alias_str,table->table.length))) + if (!(alias_str=thd->memdup(alias_str,table->table.length+1))) DBUG_RETURN(0); - if (lower_case_table_names) - casedn_str(table->table.str); + if (!(ptr = (TABLE_LIST *) thd->calloc(sizeof(TABLE_LIST)))) DBUG_RETURN(0); /* purecov: inspected */ - ptr->db= table->db.str; - ptr->real_name=table->table.str; + if (table->db.str) + { + ptr->db= table->db.str; + ptr->db_length= table->db.length; + } + else if (thd->db) + { + ptr->db= thd->db; + ptr->db_length= thd->db_length; + } + else + { + ptr->db= (char*) ""; + ptr->db_length= 0; + } + ptr->name=alias_str; + if (lower_case_table_names) + { + casedn_str(ptr->db); + casedn_str(table->table.str); + } + ptr->real_name=table->table.str; + ptr->real_name_length=table->table.length; ptr->lock_type=flags; ptr->updating=updating; if (use_index) @@ -2748,26 +3199,86 @@ TABLE_LIST *add_table_to_list(Table_ident *table, LEX_STRING *alias, sizeof(*ignore_index)); /* check that used name is unique */ - current_db=thd->db ? thd->db : ""; - 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) && - !strcmp(ptr->db ? ptr->db : current_db, - tables->db ? tables->db : current_db)) + if (!strcmp(alias_str,tables->name) && !strcmp(ptr->db, tables->db)) { net_printf(&thd->net,ER_NONUNIQ_TABLE,alias_str); /* purecov: tested */ DBUG_RETURN(0); /* purecov: tested */ } } } - 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); } + +/* +** This is used for UNION to create a new table list of all used tables +** The table_list->table entry in all used tables are set to point +** to the entries in this list. +*/ + +static bool create_total_list(THD *thd, LEX *lex, TABLE_LIST **result) +{ + /* Handle the case when we are not using union */ + if (!lex->select_lex.next) + { + *result= (TABLE_LIST*) lex->select_lex.table_list.first; + return 0; + } + + SELECT_LEX *sl; + TABLE_LIST **new_table_list= result, *aux; + + *new_table_list=0; // end result list + for (sl= &lex->select_lex; sl; sl=sl->next) + { + if (sl->order_list.first && sl->next && !sl->braces) + { + net_printf(&thd->net,ER_WRONG_USAGE,"UNION","ORDER BY"); + return 1; + } + if ((aux= (TABLE_LIST*) sl->table_list.first)) + { + TABLE_LIST *next; + for (; aux; aux=next) + { + TABLE_LIST *cursor; + next= aux->next; + for (cursor= *result; cursor; cursor=cursor->next) + if (!strcmp(cursor->db,aux->db) && + !strcmp(cursor->real_name,aux->real_name) && + !strcmp(cursor->name, aux->name)) + break; + if (!cursor) + { + /* Add not used table to the total table list */ + aux->lock_type= lex->lock_option; + if (!(cursor = (TABLE_LIST *) thd->memdup((char*) aux, + sizeof(*aux)))) + { + send_error(&thd->net,0); + return 1; + } + *new_table_list= cursor; + new_table_list= &cursor->next; + *new_table_list=0; // end result list + } + else + aux->shared=1; // Mark that it's used twice + aux->table=(TABLE *) cursor; + } + } + } + return 0; +} + + void add_join_on(TABLE_LIST *b,Item *expr) { if (!b->on_expr) @@ -2787,27 +3298,27 @@ void add_join_natural(TABLE_LIST *a,TABLE_LIST *b) /* Check if name is used in table list */ -static bool check_dup(THD *thd,const char *db,const char *name, - TABLE_LIST *tables) +static bool check_dup(const char *db, const char *name, TABLE_LIST *tables) { - const char *thd_db=thd->db ? thd->db : any_db; for (; tables ; tables=tables->next) - if (!strcmp(name,tables->real_name) && - !strcmp(db ? db : thd_db, tables->db ? tables->db : thd_db)) + if (!strcmp(name,tables->real_name) && !strcmp(db,tables->db)) return 1; return 0; } -bool reload_acl_and_cache(THD *thd, uint options, TABLE_LIST *tables) +bool reload_acl_and_cache(THD *thd, ulong options, TABLE_LIST *tables) { bool result=0; select_errors=0; /* Write if more errors */ + // TODO: figure out what's up with the commented out line below // mysql_log.flush(); // Flush log if (options & REFRESH_GRANT) { acl_reload(); grant_reload(); + if (mqh_used) + reset_mqh(thd,(LEX_USER *) NULL, 0, true); } if (options & REFRESH_LOG) { @@ -2818,12 +3329,23 @@ bool reload_acl_and_cache(THD *thd, uint options, TABLE_LIST *tables) if (ha_flush_logs()) result=1; } +#ifdef HAVE_QUERY_CACHE + if (options & REFRESH_QUERY_CACHE_FREE) + { + query_cache.pack(); // FLUSH QUERY CACHE + options &= ~REFRESH_QUERY_CACHE; //don't flush all cache, just free memory + } + if (options & (REFRESH_TABLES | REFRESH_QUERY_CACHE)) + { + query_cache.flush(); // RESET QUERY CACHE + } +#endif /*HAVE_QUERY_CACHE*/ if (options & (REFRESH_TABLES | REFRESH_READ_LOCK)) { - if ((options & REFRESH_READ_LOCK) && thd && ! thd->global_read_lock) + if ((options & REFRESH_READ_LOCK) && thd) { - thd->global_read_lock=1; - thread_safe_increment(global_read_lock,&LOCK_open); + if (lock_global_read_lock(thd)) + return 1; } result=close_cached_tables(thd,(options & REFRESH_FAST) ? 0 : 1, tables); } @@ -2834,11 +3356,25 @@ bool reload_acl_and_cache(THD *thd, uint options, TABLE_LIST *tables) if (options & REFRESH_THREADS) flush_thread_cache(); if (options & REFRESH_MASTER) - reset_master(); - if (options & REFRESH_SLAVE) - reset_slave(); - - return result; + if (reset_master(thd)) + result=1; +#ifdef OPENSSL + if (options & REFRESH_DES_KEY_FILE) + { + if (des_key_file) + result=load_des_key_file(des_key_file); + } +#endif + if (options & REFRESH_SLAVE) + { + LOCK_ACTIVE_MI; + if (reset_slave(active_mi)) + result=1; + UNLOCK_ACTIVE_MI; + } + if (options & REFRESH_USER_RESOURCES) + reset_mqh(thd,(LEX_USER *) NULL, 0); + return result; } @@ -2855,7 +3391,7 @@ void kill_one_thread(THD *thd, ulong id) if ((thd->master_access & PROCESS_ACL) || !strcmp(thd->user,tmp->user)) { - tmp->prepare_to_die(); + tmp->awake(1 /*prepare to die*/); error=0; } else @@ -2884,3 +3420,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(THD *thd, char **filename_ptr, char *table_name) +{ + char buff[FN_REFLEN],*ptr, *end; + 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); + end=convert_dirname(buff, *filename_ptr, NullS); + if (!(ptr=thd->alloc((uint) (end-buff)+(uint) 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_rename.cc b/sql/sql_rename.cc index a6614f3f3f6..305491c7346 100644 --- a/sql/sql_rename.cc +++ b/sql/sql_rename.cc @@ -31,19 +31,21 @@ static TABLE_LIST *rename_tables(THD *thd, TABLE_LIST *table_list, bool mysql_rename_tables(THD *thd, TABLE_LIST *table_list) { - bool error=1,cerror,got_all_locks=1; + bool error=1,got_all_locks=1; TABLE_LIST *lock_table,*ren_table=0; DBUG_ENTER("mysql_rename_tables"); - - /* Avoid problems with a rename on a table that we have locked or - if the user is trying to to do this in a transcation context */ + + /* + Avoid problems with a rename on a table that we have locked or + if the user is trying to to do this in a transcation context + */ if (thd->locked_tables || thd->active_transaction()) { my_error(ER_LOCK_OR_ACTIVE_TRANSACTION,MYF(0)); DBUG_RETURN(1); } - + VOID(pthread_mutex_lock(&LOCK_open)); for (lock_table=table_list ; lock_table ; lock_table=lock_table->next) { @@ -53,13 +55,13 @@ bool mysql_rename_tables(THD *thd, TABLE_LIST *table_list) if (got_lock) got_all_locks=0; } - + if (!got_all_locks && wait_for_locked_table_names(thd,table_list)) goto end; if (!(ren_table=rename_tables(thd,table_list,0))) error=0; - + end: if (ren_table) { @@ -80,19 +82,14 @@ end: for (table=table_list ; table->next != ren_table ; table=table->next->next) ; - table=table->next->next; // Skipp error table + table=table->next->next; // Skip error table /* Revert to old names */ rename_tables(thd, table, 1); /* Note that lock_table == 0 here, so the unlock loop will work */ } /* Lets hope this doesn't fail as the result will be messy */ - if ((cerror=ha_commit_rename(thd))) - { - my_error(ER_GET_ERRNO,MYF(0),cerror); - error= 1; - } - else if (!error) + if (!error) { mysql_update_log.write(thd,thd->query,thd->query_length); if (mysql_bin_log.is_open()) @@ -134,7 +131,7 @@ rename_tables(THD *thd, TABLE_LIST *table_list, bool skip_error) if (!access(name,F_OK)) { my_error(ER_TABLE_EXISTS_ERROR,MYF(0),name); - return ren_table; // This can't be skipped + DBUG_RETURN(ren_table); // This can't be skipped } sprintf(name,"%s/%s/%s%s",mysql_data_home, ren_table->db,ren_table->real_name, @@ -143,7 +140,7 @@ rename_tables(THD *thd, TABLE_LIST *table_list, bool skip_error) { my_error(ER_FILE_NOT_FOUND, MYF(0), name, my_errno); if (!skip_error) - return ren_table; + DBUG_RETURN(ren_table); } else if (mysql_rename_table(table_type, ren_table->db, ren_table->real_name, diff --git a/sql/sql_repl.cc b/sql/sql_repl.cc index 1940ff360c2..398ff443ad4 100644 --- a/sql/sql_repl.cc +++ b/sql/sql_repl.cc @@ -1,4 +1,4 @@ -/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB +/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB & Sasha 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 @@ -15,17 +15,17 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ // Sasha Pachev <sasha@mysql.com> is currently in charge of this file -// Do not mess with it without his permission! #include "mysql_priv.h" #include "sql_repl.h" #include "sql_acl.h" #include "log_event.h" +#include "mini_client.h" #include <thr_alarm.h> #include <my_dir.h> +#include <assert.h> extern const char* any_db; -extern pthread_handler_decl(handle_slave,arg); #ifndef DBUG_OFF int max_binlog_dump_events = 0; // unlimited @@ -33,43 +33,66 @@ bool opt_sporadic_binlog_dump_fail = 0; static int binlog_dump_count = 0; #endif +int check_binlog_magic(IO_CACHE* log, const char** errmsg) +{ + char magic[4]; + DBUG_ASSERT(my_b_tell(log) == 0); + + if (my_b_read(log, (byte*) magic, sizeof(magic))) + { + *errmsg = "I/O error reading the header from the binary log"; + sql_print_error("%s, errno=%d, io cache code=%d", *errmsg, my_errno, + log->error); + return 1; + } + if (memcmp(magic, BINLOG_MAGIC, sizeof(magic))) + { + *errmsg = "Binlog has bad magic number; It's not a binary log file that can be used by this version of MySQL"; + return 1; + } + return 0; +} + 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); - // find the last slash - if(p) - p++; - else - p = log_file_name; + char* p = log_file_name+dirname_length(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); + + // TODO: check what problems this may cause and fix them + int4store(header + LOG_POS_OFFSET, 0); + packet->append(header, sizeof(header)); + /* We need to split the next statement because of problem with cxx */ + int4store(buf,4); // tell slave to skip magic number + int4store(buf+4,0); + packet->append(buf, ROTATE_HEADER_LEN); packet->append(p,ident_len); - if(my_net_write(net, (char*)packet->ptr(), packet->length())) - { - *errmsg = "failed on my_net_write()"; - return -1; - } + if (my_net_write(net, (char*)packet->ptr(), packet->length())) + { + *errmsg = "failed on my_net_write()"; + return -1; + } return 0; } - static int send_file(THD *thd) { NET* net = &thd->net; int fd = -1,bytes, error = 1; char fname[FN_REFLEN+1]; - char *buf; const char *errmsg = 0; int old_timeout; uint packet_len; + char buf[IO_SIZE]; // It's safe to alloc this DBUG_ENTER("send_file"); // the client might be slow loading the data, give him wait_timeout to do @@ -77,40 +100,32 @@ static int send_file(THD *thd) old_timeout = thd->net.timeout; thd->net.timeout = thd->inactive_timeout; - // spare the stack - if(!(buf = alloc_root(&thd->mem_root,IO_SIZE))) - { - errmsg = "Out of memory"; - goto err; - } - // we need net_flush here because the client will not know it needs to send // us the file name until it has processed the load event entry if (net_flush(net) || (packet_len = my_net_read(net)) == packet_error) { - errmsg = "Failed reading file name"; + errmsg = "while reading file name"; goto err; } - *((char*)net->read_pos + packet_len) = 0; // terminate with \0 - //for fn_format - fn_format(fname, (char*)net->read_pos + 1, "", "", 4); + // terminate with \0 for fn_format + *((char*)net->read_pos + packet_len) = 0; + fn_format(fname, (char*) net->read_pos + 1, "", "", 4); // this is needed to make replicate-ignore-db if (!strcmp(fname,"/dev/null")) goto end; - if ((fd = my_open(fname, O_RDONLY, MYF(MY_WME))) < 0) + if ((fd = my_open(fname, O_RDONLY, MYF(0))) < 0) { - errmsg = "Failed on my_open()"; + errmsg = "on open of file"; goto err; } - while ((bytes = (int) my_read(fd, (byte*) buf, IO_SIZE, - MYF(MY_WME))) > 0) + while ((bytes = (int) my_read(fd, (byte*) buf, IO_SIZE, MYF(0))) > 0) { if (my_net_write(net, buf, bytes)) { - errmsg = "Failed on my_net_write()"; + errmsg = "while writing data to client"; goto err; } } @@ -119,18 +134,18 @@ static int send_file(THD *thd) if (my_net_write(net, "", 0) || net_flush(net) || (my_net_read(net) == packet_error)) { - errmsg = "failed negotiating file transfer close"; + errmsg = "while negotiating file transfer close"; goto err; } error = 0; err: thd->net.timeout = old_timeout; - if(fd >= 0) - (void) my_close(fd, MYF(MY_WME)); + if (fd >= 0) + (void) my_close(fd, MYF(0)); if (errmsg) { - sql_print_error("failed in send_file() : %s", errmsg); + sql_print_error("Failed in send_file() %s", errmsg); DBUG_PRINT("error", (errmsg)); } DBUG_RETURN(error); @@ -138,34 +153,27 @@ static int send_file(THD *thd) File open_binlog(IO_CACHE *log, const char *log_file_name, - const char **errmsg) + const char **errmsg) { File file; - char magic[4]; + if ((file = my_open(log_file_name, O_RDONLY | O_BINARY, MYF(MY_WME))) < 0 || init_io_cache(log, file, IO_SIZE*2, READ_CACHE, 0, 0, - MYF(MY_WME))) + MYF(MY_WME | MY_DONT_CHECK_FILESIZE))) { - *errmsg = "Could not open log file"; // This will not be sent + *errmsg = "Could not open log file"; // This will not be sent goto err; } - - if (my_b_read(log, (byte*) magic, sizeof(magic))) - { - *errmsg = "I/O error reading binlog magic number"; + if (check_binlog_magic(log,errmsg)) goto err; - } - if (memcmp(magic, BINLOG_MAGIC, 4)) - { - *errmsg = "Binlog has bad magic number, fire your magician"; - goto err; - } return file; err: - if (file > 0) + if (file >= 0) + { my_close(file,MYF(0)); - end_io_cache(log); + end_io_cache(log); + } return -1; } @@ -173,26 +181,27 @@ err: void adjust_linfo_offsets(my_off_t purge_offset) { THD *tmp; - + pthread_mutex_lock(&LOCK_thread_count); I_List_iterator<THD> it(threads); - - while((tmp=it++)) - { - LOG_INFO* linfo; - if((linfo = tmp->current_linfo)) - { - pthread_mutex_lock(&linfo->lock); - // no big deal if we just started reading the log - // nothing to adjust - if(linfo->index_file_offset < purge_offset) - linfo->fatal = (linfo->index_file_offset != 0); - else - linfo->index_file_offset -= purge_offset; - pthread_mutex_unlock(&linfo->lock); - } - } + while ((tmp=it++)) + { + LOG_INFO* linfo; + if ((linfo = tmp->current_linfo)) + { + pthread_mutex_lock(&linfo->lock); + /* index file offset can be less that purge offset + only if we just started reading the index file. In that case + we have nothing to adjust + */ + if (linfo->index_file_offset < purge_offset) + linfo->fatal = (linfo->index_file_offset != 0); + else + linfo->index_file_offset -= purge_offset; + pthread_mutex_unlock(&linfo->lock); + } + } pthread_mutex_unlock(&LOCK_thread_count); } @@ -202,21 +211,21 @@ bool log_in_use(const char* log_name) int log_name_len = strlen(log_name) + 1; THD *tmp; bool result = 0; - + pthread_mutex_lock(&LOCK_thread_count); I_List_iterator<THD> it(threads); - - while((tmp=it++)) + + while ((tmp=it++)) + { + LOG_INFO* linfo; + if ((linfo = tmp->current_linfo)) { - LOG_INFO* linfo; - if((linfo = tmp->current_linfo)) - { - pthread_mutex_lock(&linfo->lock); - result = !memcmp(log_name, linfo->log_file_name, log_name_len); - pthread_mutex_unlock(&linfo->lock); - if(result) break; - } - } + pthread_mutex_lock(&linfo->lock); + result = !memcmp(log_name, linfo->log_file_name, log_name_len); + pthread_mutex_unlock(&linfo->lock); + if (result) break; + } + } pthread_mutex_unlock(&LOCK_thread_count); return result; @@ -226,35 +235,35 @@ bool log_in_use(const char* log_name) int purge_master_logs(THD* thd, const char* to_log) { char search_file_name[FN_REFLEN]; + const char* errmsg = 0; + mysql_bin_log.make_log_name(search_file_name, to_log); int res = mysql_bin_log.purge_logs(thd, search_file_name); - const char* errmsg = 0; - switch(res) - { - case 0: break; - case LOG_INFO_EOF: errmsg = "Target log not found in binlog index"; break; - case LOG_INFO_IO: errmsg = "I/O error reading log index file"; break; - case LOG_INFO_INVALID: errmsg = "Server configuration does not permit \ + + switch(res) { + case 0: break; + case LOG_INFO_EOF: errmsg = "Target log not found in binlog index"; break; + case LOG_INFO_IO: errmsg = "I/O error reading log index file"; break; + case LOG_INFO_INVALID: errmsg = "Server configuration does not permit \ binlog purge"; break; - case LOG_INFO_SEEK: errmsg = "Failed on fseek()"; break; - case LOG_INFO_PURGE_NO_ROTATE: errmsg = "Cannot purge unrotatable log"; - break; - case LOG_INFO_MEM: errmsg = "Out of memory"; break; - case LOG_INFO_FATAL: errmsg = "Fatal error during purge"; break; - case LOG_INFO_IN_USE: errmsg = "A purgeable log is in use, will not purge"; - break; - default: - errmsg = "Unknown error during purge"; break; - } - - if(errmsg) - { - send_error(&thd->net, 0, errmsg); - return 1; - } + case LOG_INFO_SEEK: errmsg = "Failed on fseek()"; break; + case LOG_INFO_PURGE_NO_ROTATE: errmsg = "Cannot purge unrotatable log"; + break; + case LOG_INFO_MEM: errmsg = "Out of memory"; break; + case LOG_INFO_FATAL: errmsg = "Fatal error during purge"; break; + case LOG_INFO_IN_USE: errmsg = "A purgeable log is in use, will not purge"; + break; + default: errmsg = "Unknown error during purge"; break; + } + + if (errmsg) + { + send_error(&thd->net, 0, errmsg); + return 1; + } else send_ok(&thd->net); - + return 0; } @@ -272,7 +281,7 @@ void mysql_binlog_send(THD* thd, char* log_ident, ulong pos, ushort flags) NET* net = &thd->net; #ifndef DBUG_OFF int left_events = max_binlog_dump_events; -#endif +#endif DBUG_ENTER("mysql_binlog_send"); bzero((char*) &log,sizeof(log)); @@ -282,25 +291,25 @@ void mysql_binlog_send(THD* thd, char* log_ident, ulong pos, ushort flags) errmsg = "Master failed COM_BINLOG_DUMP to test if slave can recover"; goto err; } -#endif +#endif - if(!mysql_bin_log.is_open()) + if (!mysql_bin_log.is_open()) { errmsg = "Binary log is not open"; goto err; } - if(!server_id_supplied) - { - errmsg = "Misconfigured master - server id was not set"; - goto err; - } - + if (!server_id_supplied) + { + errmsg = "Misconfigured master - server id was not set"; + goto err; + } + if (log_ident[0]) mysql_bin_log.make_log_name(search_file_name, log_ident); else search_file_name[0] = 0; - + linfo.index_file_offset = 0; thd->current_linfo = &linfo; @@ -315,20 +324,21 @@ 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; } - + my_b_seek(&log, pos); // Seek will done on next read packet->length(0); - packet->append("\0", 1); // we need to start a packet with something other than 255 // to distiquish it from error + packet->append("\0", 1); - // tell the client log name with a fake rotate_event // if we are at the start of the log - if(pos == 4) + if (pos == 4) { + // tell the client log name with a fake rotate_event if (fake_rotate_event(net, packet, log_file_name, &errmsg)) goto err; packet->length(0); @@ -338,17 +348,17 @@ void mysql_binlog_send(THD* thd, char* log_ident, ulong pos, ushort flags) while (!net->error && net->vio != 0 && !thd->killed) { pthread_mutex_t *log_lock = mysql_bin_log.get_log_lock(); - + while (!(error = Log_event::read_log_event(&log, packet, log_lock))) { #ifndef DBUG_OFF - if(max_binlog_dump_events && !left_events--) + if (max_binlog_dump_events && !left_events--) { net_flush(net); errmsg = "Debugging binlog dump abort"; goto err; } -#endif +#endif if (my_net_write(net, (char*)packet->ptr(), packet->length()) ) { errmsg = "Failed on my_net_write()"; @@ -358,7 +368,7 @@ void mysql_binlog_send(THD* thd, char* log_ident, ulong pos, ushort flags) (*packet)[LOG_EVENT_OFFSET+1] )); if ((*packet)[LOG_EVENT_OFFSET+1] == LOAD_EVENT) { - if(send_file(thd)) + if (send_file(thd)) { errmsg = "failed in send_file()"; goto err; @@ -367,15 +377,15 @@ void mysql_binlog_send(THD* thd, char* log_ident, ulong pos, ushort flags) packet->length(0); packet->append("\0",1); } - + // TODO: now that we are logging the offset, check to make sure + // the recorded offset and the actual match if (error != LOG_READ_EOF) { - switch(error) - { - case LOG_READ_BOGUS: + switch(error) { + case LOG_READ_BOGUS: errmsg = "bogus data in log event"; break; - case LOG_READ_TOO_LARGE: + case LOG_READ_TOO_LARGE: errmsg = "log event entry exceeded max_allowed_packet -\ increase max_allowed_packet on master"; break; @@ -395,12 +405,12 @@ void mysql_binlog_send(THD* thd, char* log_ident, ulong pos, ushort flags) goto err; } - if(!(flags & BINLOG_DUMP_NON_BLOCK) && + if (!(flags & BINLOG_DUMP_NON_BLOCK) && mysql_bin_log.is_active(log_file_name)) + { // block until there is more data in the log // unless non-blocking mode requested - { - if(net_flush(net)) + if (net_flush(net)) { errmsg = "failed on net_flush()"; goto err; @@ -412,64 +422,56 @@ void mysql_binlog_send(THD* thd, char* log_ident, ulong pos, ushort flags) // to signal us { log.error=0; - - // tell the kill thread how to wake us up - thd->mysys_var->current_mutex = log_lock; - thd->mysys_var->current_cond = &COND_binlog_update; - const char* proc_info = thd->proc_info; - thd->proc_info = "Slave connection: waiting for binlog update"; - bool read_packet = 0, fatal_error = 0; #ifndef DBUG_OFF - if(max_binlog_dump_events && !left_events--) + if (max_binlog_dump_events && !left_events--) { net_flush(net); errmsg = "Debugging binlog dump abort"; goto err; } -#endif +#endif // no one will update the log while we are reading // now, but we'll be quick and just read one record pthread_mutex_lock(log_lock); - switch (Log_event::read_log_event(&log, packet, (pthread_mutex_t*) 0)) + switch (Log_event::read_log_event(&log, packet, (pthread_mutex_t*)0)) { case 0: + pthread_mutex_unlock(log_lock); read_packet = 1; // we read successfully, so we'll need to send it to the // slave break; case LOG_READ_EOF: - DBUG_PRINT("wait",("waiting for data on binary log")); + DBUG_PRINT("wait",("waiting for data in binary log")); + // wait_for_update unlocks the log lock - needed to avoid race if (!thd->killed) - pthread_cond_wait(&COND_binlog_update, log_lock); + mysql_bin_log.wait_for_update(thd); + else + pthread_mutex_unlock(log_lock); + DBUG_PRINT("wait",("binary log received update")); break; default: + pthread_mutex_unlock(log_lock); fatal_error = 1; break; } - pthread_mutex_unlock(log_lock); - - pthread_mutex_lock(&thd->mysys_var->mutex); - thd->mysys_var->current_mutex= 0; - thd->mysys_var->current_cond= 0; - thd->proc_info= proc_info; - pthread_mutex_unlock(&thd->mysys_var->mutex); - - if(read_packet) + + if (read_packet) { thd->proc_info = "sending update to slave"; - if(my_net_write(net, (char*)packet->ptr(), packet->length()) ) + if (my_net_write(net, (char*)packet->ptr(), packet->length()) ) { errmsg = "Failed on my_net_write()"; goto err; } - if((*packet)[LOG_EVENT_OFFSET+1] == LOAD_EVENT) + if ((*packet)[LOG_EVENT_OFFSET+1] == LOAD_EVENT) { - if(send_file(thd)) + if (send_file(thd)) { errmsg = "failed in send_file()"; goto err; @@ -481,7 +483,7 @@ void mysql_binlog_send(THD* thd, char* log_ident, ulong pos, ushort flags) // we hit EOF pretty quick } - if(fatal_error) + if (fatal_error) { errmsg = "error reading log entry"; goto err; @@ -494,8 +496,7 @@ void mysql_binlog_send(THD* thd, char* log_ident, ulong pos, ushort flags) bool loop_breaker = 0; // need this to break out of the for loop from switch thd->proc_info = "switching to next log"; - switch(mysql_bin_log.find_next_log(&linfo)) - { + switch (mysql_bin_log.find_next_log(&linfo)) { case LOG_INFO_EOF: loop_breaker = (flags & BINLOG_DUMP_NON_BLOCK); break; @@ -506,12 +507,12 @@ void mysql_binlog_send(THD* thd, char* log_ident, ulong pos, ushort flags) goto err; } - if(loop_breaker) + if (loop_breaker) break; end_io_cache(&log); (void) my_close(file, MYF(MY_WME)); - + // fake Rotate_log event just in case it did not make it to the log // otherwise the slave make get confused about the offset if ((file=open_binlog(&log, log_file_name, &errmsg)) < 0 || @@ -525,13 +526,14 @@ void mysql_binlog_send(THD* thd, char* log_ident, ulong pos, ushort flags) end_io_cache(&log); (void)my_close(file, MYF(MY_WME)); - + send_eof(&thd->net); thd->proc_info = "waiting to finalize termination"; pthread_mutex_lock(&LOCK_thread_count); thd->current_linfo = 0; pthread_mutex_unlock(&LOCK_thread_count); DBUG_VOID_RETURN; + err: thd->proc_info = "waiting to finalize termination"; end_io_cache(&log); @@ -549,126 +551,114 @@ void mysql_binlog_send(THD* thd, char* log_ident, ulong pos, ushort flags) DBUG_VOID_RETURN; } -int start_slave(THD* thd , bool net_report) +int start_slave(THD* thd , MASTER_INFO* mi, bool net_report) { - if(!thd) thd = current_thd; - NET* net = &thd->net; int slave_errno = 0; + if (!thd) thd = current_thd; + NET* net = &thd->net; + int thread_mask; + if (check_access(thd, PROCESS_ACL, any_db)) return 1; - pthread_mutex_lock(&LOCK_slave); - if(!slave_running) - { - if(init_master_info(&glob_mi)) - slave_errno = ER_MASTER_INFO; - else if(server_id_supplied && *glob_mi.host) - { - pthread_t hThread; - if(pthread_create(&hThread, &connection_attrib, handle_slave, 0)) - { - slave_errno = ER_SLAVE_THREAD; - } - while(!slave_running) // slave might already be running by now - pthread_cond_wait(&COND_slave_start, &LOCK_slave); - } - else - slave_errno = ER_BAD_SLAVE; - } + lock_slave_threads(mi); // this allows us to cleanly read slave_running + init_thread_mask(&thread_mask,mi,1 /* inverse */); + if (thd->lex.slave_thd_opt) + thread_mask &= thd->lex.slave_thd_opt; + if (thread_mask) + { + if (server_id_supplied && (!mi->inited || (mi->inited && *mi->host))) + slave_errno = start_slave_threads(0 /*no mutex */, + 1 /* wait for start */, + mi, + master_info_file,relay_log_info_file, + thread_mask); + else + slave_errno = ER_BAD_SLAVE; + } else slave_errno = ER_SLAVE_MUST_STOP; - - pthread_mutex_unlock(&LOCK_slave); - if(slave_errno) - { - if(net_report) send_error(net, slave_errno); - return 1; - } - else if(net_report) + + unlock_slave_threads(mi); + + if (slave_errno) + { + if (net_report) + send_error(net, slave_errno); + return 1; + } + else if (net_report) send_ok(net); return 0; } -int stop_slave(THD* thd, bool net_report ) +int stop_slave(THD* thd, MASTER_INFO* mi, bool net_report ) { - if(!thd) thd = current_thd; - NET* net = &thd->net; int slave_errno = 0; - + if (!thd) thd = current_thd; + NET* net = &thd->net; + if (check_access(thd, PROCESS_ACL, any_db)) return 1; + thd->proc_info = "Killing slave"; + int thread_mask; + lock_slave_threads(mi); + init_thread_mask(&thread_mask,mi,0 /* not inverse*/); + if (thd->lex.slave_thd_opt) + thread_mask &= thd->lex.slave_thd_opt; + slave_errno = (thread_mask) ? + terminate_slave_threads(mi,thread_mask, + 1 /*skip lock */) : ER_SLAVE_NOT_RUNNING; + unlock_slave_threads(mi); + thd->proc_info = 0; - pthread_mutex_lock(&LOCK_slave); - if (slave_running) + if (slave_errno) { - abort_slave = 1; - KICK_SLAVE; - // do not abort the slave in the middle of a query, so we do not set - // thd->killed for the slave thread - thd->proc_info = "waiting for slave to die"; - while(slave_running) - { - /* there is a small chance that slave thread might miss the first - alarm. To protect againts it, resend the signal until it reacts - */ - - struct timespec abstime; -#ifdef HAVE_TIMESPEC_TS_SEC - abstime.ts_sec=time(NULL)+2; - abstime.ts_nsec=0; -#elif defined(__WIN__) - abstime.tv_sec=time((time_t*) 0)+2; - abstime.tv_nsec=0; -#else - struct timeval tv; - gettimeofday(&tv,0); - abstime.tv_sec=tv.tv_sec+2; - abstime.tv_nsec=tv.tv_usec*1000; -#endif - pthread_cond_timedwait(&COND_slave_stopped, &LOCK_slave, &abstime); - if (slave_running) - KICK_SLAVE; - } + if (net_report) + send_error(net, slave_errno); + return 1; } - else - slave_errno = ER_SLAVE_NOT_RUNNING; - - pthread_mutex_unlock(&LOCK_slave); - thd->proc_info = 0; - - if(slave_errno) - { - if(net_report) send_error(net, slave_errno); - return 1; - } - else if(net_report) + else if (net_report) send_ok(net); return 0; } -void reset_slave() +int reset_slave(MASTER_INFO* mi) { MY_STAT stat_area; char fname[FN_REFLEN]; - bool slave_was_running ; - - pthread_mutex_lock(&LOCK_slave); - if((slave_was_running = slave_running)) - { - pthread_mutex_unlock(&LOCK_slave); - stop_slave(0,0); - } - else - pthread_mutex_unlock(&LOCK_slave); + int restart_thread_mask = 0,error=0; + const char* errmsg=0; + + lock_slave_threads(mi); + init_thread_mask(&restart_thread_mask,mi,0 /* not inverse */); + if ((error=terminate_slave_threads(mi,restart_thread_mask,1 /*skip lock*/)) + || (error=purge_relay_logs(&mi->rli,1 /*just reset*/,&errmsg))) + goto err; - end_master_info(&glob_mi); - fn_format(fname, master_info_file, mysql_data_home, "", 4+16+32); - if(my_stat(fname, &stat_area, MYF(0))) - if(my_delete(fname, MYF(MY_WME))) - return; - if(slave_was_running) - start_slave(0,0); + end_master_info(mi); + fn_format(fname, master_info_file, mysql_data_home, "", 4+32); + if (my_stat(fname, &stat_area, MYF(0)) && my_delete(fname, MYF(MY_WME))) + { + error=1; + goto err; + } + fn_format(fname, relay_log_info_file, mysql_data_home, "", 4+32); + if (my_stat(fname, &stat_area, MYF(0)) && my_delete(fname, MYF(MY_WME))) + { + error=1; + goto err; + } + if (restart_thread_mask) + error=start_slave_threads(0 /* mutex not needed*/, + 1 /* wait for start*/, + mi,master_info_file,relay_log_info_file, + restart_thread_mask); + // TODO: fix error messages so they get to the client +err: + unlock_slave_threads(mi); + return error; } void kill_zombie_dump_threads(uint32 slave_server_id) @@ -677,121 +667,271 @@ void kill_zombie_dump_threads(uint32 slave_server_id) I_List_iterator<THD> it(threads); THD *tmp; - while((tmp=it++)) + while ((tmp=it++)) + { + if (tmp->command == COM_BINLOG_DUMP && + tmp->server_id == slave_server_id) { - if(tmp->command == COM_BINLOG_DUMP && - tmp->server_id == slave_server_id) - { - // here we do not call kill_one_thread() - // it will be slow because it will iterate through the list - // again. Plus it double-locks LOCK_thread_count, which - // make safe_mutex complain and abort - // so we just to our own thread murder - - thr_alarm_kill(tmp->real_id); - tmp->killed = 1; - tmp->mysys_var->abort = 1; - pthread_mutex_lock(&tmp->mysys_var->mutex); - if(tmp->mysys_var->current_cond) - { - pthread_mutex_lock(tmp->mysys_var->current_mutex); - pthread_cond_broadcast(tmp->mysys_var->current_cond); - pthread_mutex_unlock(tmp->mysys_var->current_mutex); - } - pthread_mutex_unlock(&tmp->mysys_var->mutex); - } - } - + /* + Here we do not call kill_one_thread() as + it will be slow because it will iterate through the list + again. Plus it double-locks LOCK_tread_count, which + make safe_mutex complain and abort. + We just to do kill the thread ourselves. + */ + tmp->awake(1/*prepare to die*/); + } + } pthread_mutex_unlock(&LOCK_thread_count); } -int change_master(THD* thd) + +int change_master(THD* thd, MASTER_INFO* mi) { - bool slave_was_running; + int error=0,restart_thread_mask; + const char* errmsg=0; + bool need_relay_log_purge=1; + // kill slave thread - pthread_mutex_lock(&LOCK_slave); - if((slave_was_running = slave_running)) - { - abort_slave = 1; - KICK_SLAVE; - thd->proc_info = "waiting for slave to die"; - while(slave_running) - pthread_cond_wait(&COND_slave_stopped, &LOCK_slave); // wait until done - } - pthread_mutex_unlock(&LOCK_slave); + lock_slave_threads(mi); + init_thread_mask(&restart_thread_mask,mi,0 /*not inverse*/); + if (restart_thread_mask && + (error=terminate_slave_threads(mi, + restart_thread_mask, + 1 /*skip lock*/))) + { + send_error(&thd->net,error); + unlock_slave_threads(mi); + return 1; + } thd->proc_info = "changing master"; LEX_MASTER_INFO* lex_mi = &thd->lex.mi; + // TODO: see if needs re-write + if (init_master_info(mi,master_info_file,relay_log_info_file)) + { + send_error(&thd->net, 0, "Could not initialize master info"); + unlock_slave_threads(mi); + return 1; + } + + /* data lock not needed since we have already stopped the running threads, + and we have the hold on the run locks which will keep all threads that + could possibly modify the data structures from running + */ + if ((lex_mi->host || lex_mi->port) && !lex_mi->log_file_name && !lex_mi->pos) + { + // if we change host or port, we must reset the postion + mi->master_log_name[0] = 0; + mi->master_log_pos = 4; // skip magic number + mi->rli.pending = 0; + } + + if (lex_mi->log_file_name) + strmake(mi->master_log_name, lex_mi->log_file_name, + sizeof(mi->master_log_name)); + if (lex_mi->pos) + { + mi->master_log_pos = lex_mi->pos; + mi->rli.pending = 0; + } + + if (lex_mi->host) + strmake(mi->host, lex_mi->host, sizeof(mi->host)); + if (lex_mi->user) + strmake(mi->user, lex_mi->user, sizeof(mi->user)); + if (lex_mi->password) + strmake(mi->password, lex_mi->password, sizeof(mi->password)); + if (lex_mi->port) + mi->port = lex_mi->port; + if (lex_mi->connect_retry) + mi->connect_retry = lex_mi->connect_retry; + + if (lex_mi->relay_log_name) + { + need_relay_log_purge = 0; + mi->rli.skip_log_purge=1; + strnmov(mi->rli.relay_log_name,lex_mi->relay_log_name, + sizeof(mi->rli.relay_log_name)-1); + } + + if (lex_mi->relay_log_pos) + { + need_relay_log_purge=0; + mi->rli.relay_log_pos=lex_mi->relay_log_pos; + } - if(init_master_info(&glob_mi)) + flush_master_info(mi); + if (need_relay_log_purge) + { + mi->rli.skip_log_purge=0; + thd->proc_info="purging old relay logs"; + if (purge_relay_logs(&mi->rli,0 /* not only reset, but also reinit*/, + &errmsg)) { - send_error(&thd->net, 0, "Could not initialize master info"); + net_printf(&thd->net, 0, "Failed purging old relay logs: %s",errmsg); return 1; } - - pthread_mutex_lock(&glob_mi.lock); - if((lex_mi->host || lex_mi->port) && !lex_mi->log_file_name && !lex_mi->pos) - { - // if we change host or port, we must reset the postion - glob_mi.log_file_name[0] = 0; - glob_mi.pos = 4; // skip magic number - glob_mi.pending = 0; - } - - if(lex_mi->log_file_name) - strmake(glob_mi.log_file_name, lex_mi->log_file_name, - sizeof(glob_mi.log_file_name)); - if(lex_mi->pos) - { - glob_mi.pos = lex_mi->pos; - glob_mi.pending = 0; } - - if(lex_mi->host) + else + { + const char* msg; + if (init_relay_log_pos(&mi->rli,0/*log already inited*/, + 0 /*pos already inited*/, + 0 /*no data lock*/, + &msg)) { - strmake(glob_mi.host, lex_mi->host, sizeof(glob_mi.host)); + //Sasha: note that I had to change net_printf() to make this work + net_printf(&thd->net,0,"Failed initializing relay log position: %s",msg); + unlock_slave_threads(mi); + return 1; } - if(lex_mi->user) - strmake(glob_mi.user, lex_mi->user, sizeof(glob_mi.user)); - if(lex_mi->password) - strmake(glob_mi.password, lex_mi->password, sizeof(glob_mi.password)); - if(lex_mi->port) - glob_mi.port = lex_mi->port; - if(lex_mi->connect_retry) - glob_mi.connect_retry = lex_mi->connect_retry; - - flush_master_info(&glob_mi); - pthread_mutex_unlock(&glob_mi.lock); + + } + mi->rli.master_log_pos = mi->master_log_pos; + strnmov(mi->rli.master_log_name,mi->master_log_name, + sizeof(mi->rli.master_log_name)); + if (!mi->rli.master_log_name[0]) // uninitialized case + mi->rli.master_log_pos=0; + + pthread_mutex_lock(&mi->rli.data_lock); + mi->rli.abort_pos_wait = 1; + pthread_cond_broadcast(&mi->data_cond); + pthread_mutex_unlock(&mi->rli.data_lock); + thd->proc_info = "starting slave"; - if(slave_was_running) - start_slave(0,0); + if (restart_thread_mask) + error=start_slave_threads(0 /* mutex not needed*/, + 1 /* wait for start*/, + mi,master_info_file,relay_log_info_file, + restart_thread_mask); + unlock_slave_threads(mi); thd->proc_info = 0; - - send_ok(&thd->net); + if (error) + send_error(&thd->net,error); + else + send_ok(&thd->net); return 0; } -void reset_master() +int reset_master(THD* thd) { - if(!mysql_bin_log.is_open()) + if (!mysql_bin_log.is_open()) { my_error(ER_FLUSH_MASTER_BINLOG_CLOSED, MYF(ME_BELL+ME_WAITTANG)); - return; + return 1; } + return mysql_bin_log.reset_logs(thd); +} - LOG_INFO linfo; - if (mysql_bin_log.find_first_log(&linfo, "")) - return; +int cmp_master_pos(const char* log_file_name1, ulonglong log_pos1, + const char* log_file_name2, ulonglong log_pos2) +{ + int res; + if ((res = strcmp(log_file_name1, log_file_name2))) + return res; + if (log_pos1 > log_pos2) + return 1; + else if (log_pos1 == log_pos2) + return 0; + return -1; +} - for(;;) +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()) { - my_delete(linfo.log_file_name, MYF(MY_WME)); - if (mysql_bin_log.find_next_log(&linfo)) - break; + 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; + my_off_t pos = 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,(pthread_mutex_t*)0,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"; + pthread_mutex_unlock(mysql_bin_log.get_log_lock()); + goto err; + } + + pthread_mutex_unlock(mysql_bin_log.get_log_lock()); } - mysql_bin_log.close(1); // exiting close - my_delete(mysql_bin_log.get_index_fname(), MYF(MY_WME)); - mysql_bin_log.open(opt_bin_logname,LOG_BIN); +err: + if (file >= 0) + { + end_io_cache(&log); + (void) my_close(file, MYF(MY_WME)); + } + + if (errmsg) + { + net_printf(&thd->net, ER_ERROR_WHEN_EXECUTING_COMMAND, + "SHOW BINLOG EVENTS", errmsg); + DBUG_RETURN(1); + } + + send_eof(&thd->net); + DBUG_RETURN(0); } int show_binlog_info(THD* thd) @@ -803,36 +943,37 @@ int show_binlog_info(THD* thd) field_list.push_back(new Item_empty_string("Binlog_do_db",20)); field_list.push_back(new Item_empty_string("Binlog_ignore_db",20)); - if(send_fields(thd, field_list, 1)) + if (send_fields(thd, field_list, 1)) DBUG_RETURN(-1); String* packet = &thd->packet; packet->length(0); - if(mysql_bin_log.is_open()) - { - LOG_INFO li; - mysql_bin_log.get_current_log(&li); - int dir_len = dirname_length(li.log_file_name); - net_store_data(packet, li.log_file_name + dir_len); - net_store_data(packet, (longlong)li.pos); - net_store_data(packet, &binlog_do_db); - net_store_data(packet, &binlog_ignore_db); - } + if (mysql_bin_log.is_open()) + { + LOG_INFO li; + mysql_bin_log.get_current_log(&li); + int dir_len = dirname_length(li.log_file_name); + net_store_data(packet, li.log_file_name + dir_len); + net_store_data(packet, (longlong)li.pos); + net_store_data(packet, &binlog_do_db); + net_store_data(packet, &binlog_ignore_db); + } else - { - net_store_null(packet); - net_store_null(packet); - net_store_null(packet); - net_store_null(packet); - } + { + net_store_null(packet); + net_store_null(packet); + net_store_null(packet); + net_store_null(packet); + } - if(my_net_write(&thd->net, (char*)thd->packet.ptr(), packet->length())) + if (my_net_write(&thd->net, (char*)thd->packet.ptr(), packet->length())) DBUG_RETURN(-1); send_eof(&thd->net); DBUG_RETURN(0); } + int show_binlogs(THD* thd) { const char* errmsg = 0; @@ -843,20 +984,20 @@ int show_binlogs(THD* thd) String* packet = &thd->packet; IO_CACHE io_cache; uint length; - - if(!mysql_bin_log.is_open()) + + if (!mysql_bin_log.is_open()) { errmsg = "binlog is not open"; goto err; } field_list.push_back(new Item_empty_string("Log_name", 128)); - if(send_fields(thd, field_list, 1)) + if (send_fields(thd, field_list, 1)) { sql_print_error("Failed in send_fields"); return 1; } - + mysql_bin_log.lock_index(); index_file = mysql_bin_log.get_index_file(); if (index_file < 0) @@ -876,7 +1017,7 @@ int show_binlogs(THD* thd) int dir_len = dirname_length(fname); packet->length(0); net_store_data(packet, fname + dir_len, length-dir_len); - if(my_net_write(net, (char*) packet->ptr(), packet->length())) + if (my_net_write(net, (char*) packet->ptr(), packet->length())) { sql_print_error("Failed in my_net_write"); end_io_cache(&io_cache); @@ -884,10 +1025,10 @@ int show_binlogs(THD* thd) return 1; } } - + mysql_bin_log.unlock_index(); end_io_cache(&io_cache); - send_eof(net); + send_eof(net); return 0; err2: @@ -898,5 +1039,34 @@ err: return 1; } - - +int log_loaded_block(IO_CACHE* file) +{ + LOAD_FILE_INFO* lf_info; + uint block_len ; + + /* file->request_pos contains position where we started last read */ + char* buffer = (char*) file->request_pos; + if (!(block_len = (char*) file->read_end - (char*) buffer)) + return 0; + lf_info = (LOAD_FILE_INFO*) file->arg; + if (lf_info->last_pos_in_file != HA_POS_ERROR && + lf_info->last_pos_in_file >= file->pos_in_file) + return 0; + lf_info->last_pos_in_file = file->pos_in_file; + if (lf_info->wrote_create_file) + { + Append_block_log_event a(lf_info->thd, buffer, block_len); + mysql_bin_log.write(&a); + } + else + { + Create_file_log_event c(lf_info->thd,lf_info->ex,lf_info->db, + lf_info->table_name, *lf_info->fields, + lf_info->handle_dup, buffer, + block_len); + mysql_bin_log.write(&c); + lf_info->wrote_create_file = 1; + DBUG_SYNC_POINT("debug_lock.created_file_event",10); + } + return 0; +} diff --git a/sql/sql_repl.h b/sql/sql_repl.h index aa07d859aec..360fd50a1e3 100644 --- a/sql/sql_repl.h +++ b/sql/sql_repl.h @@ -3,6 +3,18 @@ #include "slave.h" +typedef struct st_slave_info +{ + uint32 server_id; + uint32 rpl_recovery_rank, master_id; + char host[HOSTNAME_LENGTH+1]; + char user[USERNAME_LENGTH+1]; + char password[HASH_PASSWORD_LENGTH+1]; + uint16 port; + THD* thd; +} SLAVE_INFO; + +extern bool opt_show_slave_auth_info, opt_old_rpl_compat; extern char* master_host; extern my_string opt_bin_logname, master_info_file; extern uint32 server_id; @@ -14,26 +26,40 @@ extern int max_binlog_dump_events; extern bool opt_sporadic_binlog_dump_fail; #endif -#ifdef SIGNAL_WITH_VIO_CLOSE -#define KICK_SLAVE { slave_thd->close_active_vio(); \ - thr_alarm_kill(slave_real_id); } -#else -#define KICK_SLAVE thr_alarm_kill(slave_real_id); -#endif +#define KICK_SLAVE(thd) thd->awake(0 /* do not prepare to die*/); File open_binlog(IO_CACHE *log, const char *log_file_name, const char **errmsg); -int start_slave(THD* thd = 0, bool net_report = 1); -int stop_slave(THD* thd = 0, bool net_report = 1); -int change_master(THD* thd); -void reset_slave(); -void reset_master(); +int start_slave(THD* thd, MASTER_INFO* mi, bool net_report); +int stop_slave(THD* thd, MASTER_INFO* mi, bool net_report); +int change_master(THD* thd, MASTER_INFO* mi); +int show_binlog_events(THD* thd); +int cmp_master_pos(const char* log_file_name1, ulonglong log_pos1, + const char* log_file_name2, ulonglong log_pos2); +int reset_slave(MASTER_INFO* mi); +int reset_master(THD* thd); 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); int show_binlogs(THD* thd); extern int init_master_info(MASTER_INFO* mi); void kill_zombie_dump_threads(uint32 slave_server_id); +int check_binlog_magic(IO_CACHE* log, const char** errmsg); + +typedef struct st_load_file_info +{ + THD* thd; + sql_exchange* ex; + List <Item> *fields; + enum enum_duplicates handle_dup; + char* db; + char* table_name; + bool wrote_create_file; + my_off_t last_pos_in_file; +} LOAD_FILE_INFO; + +int log_loaded_block(IO_CACHE* file); #endif + diff --git a/sql/sql_select.cc b/sql/sql_select.cc index c6339c2c34f..d48bc3b3a18 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -41,13 +41,16 @@ static bool update_ref_and_keys(THD *thd, DYNAMIC_ARRAY *keyuse, uint tables,COND *conds,table_map table_map); static int sort_keyuse(KEYUSE *a,KEYUSE *b); static void set_position(JOIN *join,uint index,JOIN_TAB *table,KEYUSE *key); +static bool create_ref_for_key(JOIN *join, JOIN_TAB *j, KEYUSE *org_keyuse, + table_map used_tables); static void find_best_combination(JOIN *join,table_map rest_tables); static void find_best(JOIN *join,table_map rest_tables,uint index, double record_count,double read_time); static uint cache_record_length(JOIN *join,uint index); static double prev_record_reads(JOIN *join,table_map found_ref); static bool get_best_combination(JOIN *join); -static store_key *get_store_key(KEYUSE *keyuse, table_map used_tables, +static store_key *get_store_key(THD *thd, + KEYUSE *keyuse, table_map used_tables, KEY_PART_INFO *key_part, char *key_buff, uint maybe_null); static bool make_simple_join(JOIN *join,TABLE *tmp_table); @@ -68,7 +71,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); @@ -83,20 +86,23 @@ static int end_unique_update(JOIN *join,JOIN_TAB *join_tab, static int end_write_group(JOIN *join, JOIN_TAB *join_tab, bool end_of_records); static int test_if_group_changed(List<Item_buff> &list); -static int join_read_const_tables(JOIN *join); +static int join_read_const_table(JOIN_TAB *tab, POSITION *pos); static int join_read_system(JOIN_TAB *tab); static int join_read_const(JOIN_TAB *tab); static int join_read_key(JOIN_TAB *tab); static int join_read_always_key(JOIN_TAB *tab); +static int join_read_last_key(JOIN_TAB *tab); static int join_no_more_records(READ_RECORD *info); static int join_read_next(READ_RECORD *info); static int join_init_quick_read_record(JOIN_TAB *tab); static int test_if_quick_select(JOIN_TAB *tab); static int join_init_read_record(JOIN_TAB *tab); -static int join_init_read_first_with_key(JOIN_TAB *tab); -static int join_init_read_next_with_key(READ_RECORD *info); -static int join_init_read_last_with_key(JOIN_TAB *tab); -static int join_init_read_prev_with_key(READ_RECORD *info); +static int join_read_first(JOIN_TAB *tab); +static int join_read_next(READ_RECORD *info); +static int join_read_next_same(READ_RECORD *info); +static int join_read_last(JOIN_TAB *tab); +static int join_read_prev_same(READ_RECORD *info); +static int join_read_prev(READ_RECORD *info); static int join_ft_read_first(JOIN_TAB *tab); static int join_ft_read_next(READ_RECORD *info); static COND *make_cond_for_table(COND *cond,table_map table, @@ -104,9 +110,8 @@ 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, Item *having); static int remove_dup_with_compare(THD *thd, TABLE *entry, Field **field, @@ -139,23 +144,50 @@ static bool add_ref_to_table_cond(THD *thd, JOIN_TAB *join_tab); static void init_sum_functions(Item_sum **func); static bool update_sum_func(Item_sum **func); static void select_describe(JOIN *join, bool need_tmp_table, bool need_order, - bool distinct); + bool distinct, const char *message=NullS); static void describe_info(THD *thd, const char *info); +/* + This handles SELECT with and without UNION +*/ + +int handle_select(THD *thd, LEX *lex, select_result *result) +{ + int res; + register SELECT_LEX *select_lex = &lex->select_lex; + if (select_lex->next) + res=mysql_union(thd,lex,result); + else + res=mysql_select(thd,(TABLE_LIST*) select_lex->table_list.first, + select_lex->item_list, + select_lex->where, + (ORDER*) select_lex->order_list.first, + (ORDER*) select_lex->group_list.first, + select_lex->having, + (ORDER*) lex->proc_list.first, + select_lex->options | thd->options, + result); + if (res && result) + result->abort(); + delete result; + return res; +} + + /***************************************************************************** ** check fields, find best join, do the select and output fields. -** mysql_select assumes that all tables are allready opened +** mysql_select assumes that all tables are already opened *****************************************************************************/ int mysql_select(THD *thd,TABLE_LIST *tables,List<Item> &fields,COND *conds, ORDER *order, ORDER *group,Item *having,ORDER *proc_param, - uint select_options,select_result *result) + ulong select_options,select_result *result) { TABLE *tmp_table; - int error,tmp; + int error, tmp_error; bool need_tmp,hidden_group_fields; - bool simple_order,simple_group,no_order; + bool simple_order,simple_group,no_order, skip_sort_order, buffer_result; Item::cond_result cond_value; SQL_SELECT *select; DYNAMIC_ARRAY keyuse; @@ -163,20 +195,23 @@ mysql_select(THD *thd,TABLE_LIST *tables,List<Item> &fields,COND *conds, Procedure *procedure; List<Item> all_fields(fields); bool select_distinct; + SELECT_LEX *select_lex = &(thd->lex.select_lex); + SELECT_LEX *cur_sel = thd->lex.select; DBUG_ENTER("mysql_select"); /* Check that all tables, fields, conds and order are ok */ select_distinct=test(select_options & SELECT_DISTINCT); + buffer_result=test(select_options & OPTION_BUFFER_RESULT) && !test(select_options & OPTION_FOUND_ROWS); 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 if (setup_tables(tables) || - setup_fields(thd,tables,fields,1,&all_fields) || + setup_fields(thd,tables,fields,1,&all_fields,1) || setup_conds(thd,tables,&conds) || setup_order(thd,tables,fields,all_fields,order) || setup_group(thd,tables,fields,all_fields,group,&hidden_group_fields)) @@ -205,7 +240,7 @@ mysql_select(THD *thd,TABLE_LIST *tables,List<Item> &fields,COND *conds, if (!group) { uint flag=0; - List_iterator<Item> it(fields); + List_iterator_fast<Item> it(fields); Item *item; while ((item= it++)) { @@ -274,12 +309,15 @@ 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)) { - my_message(ER_WRONG_SUM_SELECT,ER(ER_WRONG_SUM_SELECT)); + my_message(ER_WRONG_SUM_SELECT,ER(ER_WRONG_SUM_SELECT),MYF(0)); delete procedure; DBUG_RETURN(-1); } @@ -315,10 +353,13 @@ mysql_select(THD *thd,TABLE_LIST *tables,List<Item> &fields,COND *conds, } if (cond_value == Item::COND_FALSE || !thd->select_limit) { /* Impossible cond */ - error=return_zero_rows(result, tables, fields, - join.tmp_table_param.sum_func_count != 0 && !group, - select_options,"Impossible WHERE",having, - procedure); + if (select_options & SELECT_DESCRIBE && select_lex->next) + select_describe(&join,false,false,false,"Impossible WHERE"); + else + error=return_zero_rows(result, tables, fields, + join.tmp_table_param.sum_func_count != 0 && !group, + select_options,"Impossible WHERE",having, + procedure); delete procedure; DBUG_RETURN(error); } @@ -331,17 +372,23 @@ mysql_select(THD *thd,TABLE_LIST *tables,List<Item> &fields,COND *conds, { if (res < 0) { - error=return_zero_rows(result, tables, fields, !group, - select_options,"No matching min/max row", - having,procedure); + if (select_options & SELECT_DESCRIBE && select_lex->next) + select_describe(&join,false,false,false,"No matching min/max row"); + else + error=return_zero_rows(result, tables, fields, !group, + select_options,"No matching min/max row", + having,procedure); delete procedure; DBUG_RETURN(error); } if (select_options & SELECT_DESCRIBE) { - describe_info(thd,"Select tables optimized away"); + if (select_lex->next) + select_describe(&join,false,false,false,"Select tables optimized away"); + else + describe_info(thd,"Select tables optimized away"); delete procedure; - DBUG_RETURN(0); + DBUG_RETURN(error); } tables=0; // All tables resolved } @@ -350,13 +397,18 @@ mysql_select(THD *thd,TABLE_LIST *tables,List<Item> &fields,COND *conds, { // Only test of functions error=0; if (select_options & SELECT_DESCRIBE) - describe_info(thd,"No tables used"); + { + if (select_lex->next) + select_describe(&join,false,false,false,"No tables used"); + else + describe_info(thd,"No tables used"); + } else { 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; @@ -368,7 +420,7 @@ mysql_select(THD *thd,TABLE_LIST *tables,List<Item> &fields,COND *conds, error=(int) result->send_eof(); } delete procedure; - DBUG_RETURN(0); + DBUG_RETURN(error); } error = -1; @@ -376,13 +428,12 @@ mysql_select(THD *thd,TABLE_LIST *tables,List<Item> &fields,COND *conds, /* Calculate how to do the join */ thd->proc_info="statistics"; - if (make_join_statistics(&join,tables,conds,&keyuse) || - thd->fatal_error) + if (make_join_statistics(&join,tables,conds,&keyuse) || thd->fatal_error) goto err; thd->proc_info="preparing"; - if ((tmp=join_read_const_tables(&join)) > 0) - goto err; - if (tmp && !(select_options & SELECT_DESCRIBE)) + result->initialize_tables(&join); + if (join.const_table_map != join.found_const_table_map && + !(select_options & SELECT_DESCRIBE)) { error=return_zero_rows(result,tables,fields, join.tmp_table_param.sum_func_count != 0 && @@ -397,7 +448,8 @@ mysql_select(THD *thd,TABLE_LIST *tables,List<Item> &fields,COND *conds, error= 1; /* purecov: inspected */ goto err; /* purecov: inspected */ } - if (join.const_tables && !thd->locked_tables) + if (join.const_tables && !thd->locked_tables && + !(select_options & SELECT_NO_UNLOCK)) { TABLE **table, **end; for (table=join.table, end=table + join.const_tables ; @@ -428,11 +480,14 @@ mysql_select(THD *thd,TABLE_LIST *tables,List<Item> &fields,COND *conds, } if (make_join_select(&join,select,conds)) { - error=return_zero_rows(result,tables,fields, - join.tmp_table_param.sum_func_count != 0 && !group, - select_options, - "Impossible WHERE noticed after reading const tables", - having,procedure); + if (select_options & SELECT_DESCRIBE && select_lex->next) + select_describe(&join,false,false,false,"Impossible WHERE noticed after reading const tables"); + else + error=return_zero_rows(result,tables,fields, + join.tmp_table_param.sum_func_count != 0 && !group, + select_options, + "Impossible WHERE noticed after reading const tables", + having,procedure); goto err; } @@ -446,7 +501,12 @@ 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))) { @@ -477,7 +537,7 @@ mysql_select(THD *thd,TABLE_LIST *tables,List<Item> &fields,COND *conds, (!group && join.tmp_table_param.sum_func_count)) order=0; - // Can't use sort on head table if using cache + // Can't use sort on head table if using row cache if (join.full_join) { if (group) @@ -488,11 +548,13 @@ mysql_select(THD *thd,TABLE_LIST *tables,List<Item> &fields,COND *conds, need_tmp= (join.const_tables != join.tables && ((select_distinct || !simple_order || !simple_group) || - (group && order) || - test(select_options & OPTION_BUFFER_RESULT))); + (group && order) || buffer_result)); - make_join_readinfo(&join, (select_options & SELECT_DESCRIBE) | - (thd->lex.ftfunc_list.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)) | + (cur_sel->ftfunc_list.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 @@ -506,7 +568,7 @@ mysql_select(THD *thd,TABLE_LIST *tables,List<Item> &fields,COND *conds, for (uint i_h = join.const_tables; i_h < join.tables; i_h++) { TABLE* table_h = join.join_tab[i_h].table; - if (table_h->db_type == DB_TYPE_INNOBASE) + if (table_h->db_type == DB_TYPE_INNODB) table_h->file->extra(HA_EXTRA_DONT_USE_CURSOR_TO_UPDATE); } } @@ -531,7 +593,7 @@ mysql_select(THD *thd,TABLE_LIST *tables,List<Item> &fields,COND *conds, ((group && join.const_tables != join.tables && (!simple_group || !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,19 +608,19 @@ mysql_select(THD *thd,TABLE_LIST *tables,List<Item> &fields,COND *conds, (join.const_tables == join.tables || (simple_order && 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 && - (!need_tmp || order != group || simple_group)), + order != 0 && !skip_sort_order, select_distinct); error=0; goto err; } /* Perform FULLTEXT search before all regular searches */ - init_ftfuncs(thd, test(order)); + init_ftfuncs(thd,test(order)); /* Create a tmp table if distinct or if the sort is too complicated */ if (need_tmp) @@ -573,7 +635,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 */ @@ -623,12 +686,22 @@ 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 */ thd->proc_info="Copying to tmp table"; - if (do_select(&join,(List<Item> *) 0,tmp_table,0)) + if ((tmp_error=do_select(&join,(List<Item> *) 0,tmp_table,0))) + { + error=tmp_error; goto err; /* purecov: inspected */ + } if (join.having) join.having=having=0; // Allready done @@ -701,9 +774,11 @@ mysql_select(THD *thd,TABLE_LIST *tables,List<Item> &fields,COND *conds, group=0; } thd->proc_info="Copying to group table"; + tmp_error= -1; if (make_sum_func_list(&join,all_fields) || - do_select(&join,(List<Item> *) 0,tmp_table2,0)) + (tmp_error=do_select(&join,(List<Item> *) 0,tmp_table2,0))) { + error=tmp_error; free_tmp_table(thd,tmp_table2); goto err; /* purecov: inspected */ } @@ -750,7 +825,7 @@ mysql_select(THD *thd,TABLE_LIST *tables,List<Item> &fields,COND *conds, (procedure && (procedure->flags & PROC_GROUP))) { alloc_group_fields(&join,group); - setup_copy_fields(&join.tmp_table_param,all_fields); + setup_copy_fields(thd, &join.tmp_table_param,all_fields); if (make_sum_func_list(&join,all_fields) || thd->fatal_error) goto err; /* purecov: inspected */ } @@ -761,13 +836,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 */ } @@ -776,7 +872,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); @@ -797,7 +894,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"); @@ -805,7 +902,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) @@ -825,7 +922,7 @@ make_join_statistics(JOIN *join,TABLE_LIST *tables,COND *conds, { int error; uint i,table_count,const_count,found_ref,refs,key,const_ref,eq_part; - table_map const_table_map,all_table_map; + table_map const_table_map,found_const_table_map,all_table_map; TABLE **table_vector; JOIN_TAB *stat,*stat_end,*s,**stat_ref; SQL_SELECT *select; @@ -845,7 +942,7 @@ make_join_statistics(JOIN *join,TABLE_LIST *tables,COND *conds, join->best_ref=stat_vector; stat_end=stat+table_count; - const_table_map=all_table_map=0; + const_table_map=found_const_table_map=all_table_map=0; const_count=0; for (s=stat,i=0 ; tables ; s++,tables=tables->next,i++) @@ -860,13 +957,13 @@ make_join_statistics(JOIN *join,TABLE_LIST *tables,COND *conds, bzero((char*) table->const_key_parts, sizeof(key_part_map)*table->keys); all_table_map|= table->map; s->join=join; + s->info=0; // For describe if ((s->on_expr=tables->on_expr)) { + /* Left join */ if (!table->file->records) { // Empty table - s->key_dependent=s->dependent=0; - s->type=JT_SYSTEM; - const_table_map|=table->map; + s->key_dependent=s->dependent=0; // Ignore LEFT JOIN depend. set_position(join,const_count++,s,(KEYUSE*) 0); continue; } @@ -885,11 +982,9 @@ make_join_statistics(JOIN *join,TABLE_LIST *tables,COND *conds, s->dependent=(table_map) 0; s->key_dependent=(table_map) 0; if ((table->system || table->file->records <= 1) && ! s->dependent && - !(table->file->option_flag() & HA_NOT_EXACT_COUNT) && + !(table->file->table_flags() & HA_NOT_EXACT_COUNT) && !table->fulltext_searched) { - s->type=JT_SYSTEM; - const_table_map|=table->map; set_position(join,const_count++,s,(KEYUSE*) 0); } } @@ -897,10 +992,10 @@ make_join_statistics(JOIN *join,TABLE_LIST *tables,COND *conds, join->outer_join=outer_join; /* - ** If outer join: Re-arrange tables in stat_vector so that outer join - ** tables are after all tables it is dependent of. - ** For example: SELECT * from A LEFT JOIN B ON B.c=C.c, C WHERE A.C=C.C - ** Will shift table B after table C. + If outer join: Re-arrange tables in stat_vector so that outer join + tables are after all tables it is dependent of. + For example: SELECT * from A LEFT JOIN B ON B.c=C.c, C WHERE A.C=C.C + Will shift table B after table C. */ if (outer_join) { @@ -937,31 +1032,66 @@ make_join_statistics(JOIN *join,TABLE_LIST *tables,COND *conds, conds,~outer_join)) DBUG_RETURN(1); + /* Read tables with 0 or 1 rows (system tables) */ + join->const_table_map=const_table_map; + + for (POSITION *p_pos=join->positions, *p_end=p_pos+const_count; + p_pos < p_end ; + p_pos++) + { + int tmp; + s= p_pos->table; + s->type=JT_SYSTEM; + join->const_table_map|=s->table->map; + if ((tmp=join_read_const_table(s, p_pos))) + { + if (tmp > 0) + DBUG_RETURN(1); // Fatal error + } + else + found_const_table_map|= s->table->map; + } + /* loop until no more const tables are found */ int ref_changed; do { ref_changed = 0; found_ref=0; - for (JOIN_TAB **pos=stat_vector+const_count; (s= *pos) ; pos++) + + /* + We only have to loop from stat_vector + const_count as + set_position() will move all const_tables first in stat_vector + */ + + for (JOIN_TAB **pos=stat_vector+const_count ; (s= *pos) ; pos++) { + TABLE *table=s->table; if (s->dependent) // If dependent on some table { - if (s->dependent & ~(const_table_map)) // All dep. must be constants + // All dep. must be constants + if (s->dependent & ~(join->const_table_map)) continue; - if (s->table->file->records <= 1L && - !(s->table->file->option_flag() & HA_NOT_EXACT_COUNT)) + if (table->file->records <= 1L && + !(table->file->table_flags() & HA_NOT_EXACT_COUNT)) { // system table + int tmp; s->type=JT_SYSTEM; - const_table_map|=s->table->map; + join->const_table_map|=table->map; set_position(join,const_count++,s,(KEYUSE*) 0); + if ((tmp=join_read_const_table(s,join->positions+const_count-1))) + { + if (tmp > 0) + DBUG_RETURN(1); // Fatal error + } + else + found_const_table_map|= table->map; continue; } } /* check if table can be read by key or table only uses const refs */ if ((keyuse=s->keyuse)) { - TABLE *table=s->table; s->type= JT_REF; while (keyuse->table == table) { @@ -974,7 +1104,7 @@ make_join_statistics(JOIN *join,TABLE_LIST *tables,COND *conds, { if (keyuse->val->type() != Item::NULL_ITEM) { - if (!((~const_table_map) & keyuse->used_tables)) + if (!((~join->const_table_map) & keyuse->used_tables)) const_ref|= (key_map) 1 << keyuse->keypart; else refs|=keyuse->used_tables; @@ -989,10 +1119,22 @@ make_join_statistics(JOIN *join,TABLE_LIST *tables,COND *conds, { if (const_ref == eq_part) { // Found everything for ref. + int tmp; + ref_changed = 1; s->type=JT_CONST; - const_table_map|=table->map; + join->const_table_map|=table->map; set_position(join,const_count++,s,start_keyuse); - ref_changed = 1; + if (create_ref_for_key(join, s, start_keyuse, + join->const_table_map)) + DBUG_RETURN(1); + if ((tmp=join_read_const_table(s, + join->positions+const_count-1))) + { + if (tmp > 0) + DBUG_RETURN(1); // Fatal error + } + else + found_const_table_map|= table->map; break; } else @@ -1001,7 +1143,7 @@ make_join_statistics(JOIN *join,TABLE_LIST *tables,COND *conds, } } } - } while (const_table_map & found_ref && ref_changed); + } while (join->const_table_map & found_ref && ref_changed); /* Calc how many (possible) matched records in each table */ @@ -1017,8 +1159,11 @@ make_join_statistics(JOIN *join,TABLE_LIST *tables,COND *conds, s->found_records=s->records=s->table->file->records; 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); + /* + Set a max range of how many seeks we can expect when using keys + 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; @@ -1028,10 +1173,11 @@ make_join_statistics(JOIN *join,TABLE_LIST *tables,COND *conds, { ha_rows records; if (!select) - select=make_select(s->table,0, - 0, + select=make_select(s->table, join->const_table_map, + join->const_table_map, 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; @@ -1049,10 +1195,10 @@ make_join_statistics(JOIN *join,TABLE_LIST *tables,COND *conds, join->map2table=stat_ref; join->table= join->all_tables=table_vector; join->const_tables=const_count; - join->const_table_map=const_table_map; + join->found_const_table_map=found_const_table_map; if (join->const_tables != join->tables) - find_best_combination(join,all_table_map & ~const_table_map); + find_best_combination(join,all_table_map & ~join->const_table_map); else { memcpy((gptr) join->best_positions,(gptr) join->positions, @@ -1089,7 +1235,7 @@ merge_key_fields(KEY_FIELD *start,KEY_FIELD *new_fields,KEY_FIELD *end, if (start == new_fields) return start; // Impossible or if (new_fields == end) - return start; // No new fields, skipp all + return start; // No new fields, skip all KEY_FIELD *first_free=new_fields; @@ -1151,7 +1297,7 @@ add_key_field(KEY_FIELD **key_fields,uint and_level, // Don't remove column IS NULL on a LEFT JOIN table if (!eq_func || !value || value->type() != Item::NULL_ITEM || !field->table->maybe_null || field->null_ptr) - return; // Not a key. Skipp it + return; // Not a key. Skip it exists_optimize=1; } else @@ -1170,11 +1316,13 @@ add_key_field(KEY_FIELD **key_fields,uint and_level, else { JOIN_TAB *stat=field->table->reginfo.join_tab; - stat[0].keys|=field->key_start; // Add possible keys + key_map possible_keys= (field->key_start & + field->table->keys_in_use_for_query); + stat[0].keys|= possible_keys; // Add possible keys if (!value) { // Probably BETWEEN or IN - stat[0].const_keys |= field->key_start; + stat[0].const_keys |= possible_keys; return; // Can't be used as eq key } @@ -1188,7 +1336,7 @@ add_key_field(KEY_FIELD **key_fields,uint and_level, */ stat[0].key_dependent|=used_tables; if (value->const_item()) - stat[0].const_keys |= field->key_start; + stat[0].const_keys |= possible_keys; /* We can't always use indexes when comparing a string index to a number. cmp_type() is checked to allow compare of dates to numbers */ @@ -1215,7 +1363,7 @@ add_key_fields(JOIN_TAB *stat,KEY_FIELD **key_fields,uint *and_level, { if (cond->type() == Item_func::COND_ITEM) { - List_iterator<Item> li(*((Item_cond*) cond)->argument_list()); + List_iterator_fast<Item> li(*((Item_cond*) cond)->argument_list()); KEY_FIELD *org_key_fields= *key_fields; if (((Item_cond*) cond)->functype() == Item_func::COND_AND_FUNC) @@ -1369,27 +1517,27 @@ add_ft_keys(DYNAMIC_ARRAY *keyuse_array, *arg1=(Item_func *)(func->arguments()[1]); if ((functype == Item_func::GE_FUNC || functype == Item_func::GT_FUNC) && - arg0->type() == Item::FUNC_ITEM && + arg0->type() == Item::FUNC_ITEM && arg0->functype() == Item_func::FT_FUNC && - arg1->const_item() && arg1->val()>=0) + arg1->const_item() && arg1->val()>0) cond_func=(Item_func_match *) arg0; else if ((functype == Item_func::LE_FUNC || functype == Item_func::LT_FUNC) && arg1->type() == Item::FUNC_ITEM && arg1->functype() == Item_func::FT_FUNC && - arg0->const_item() && arg0->val()>=0) + arg0->const_item() && arg0->val()>0) cond_func=(Item_func_match *) arg1; } } else if (cond->type() == Item::COND_ITEM) { - List_iterator<Item> li(*((Item_cond*) cond)->argument_list()); + List_iterator_fast<Item> li(*((Item_cond*) cond)->argument_list()); if (((Item_cond*) cond)->functype() == Item_func::COND_AND_FUNC) { Item *item; /* - I', (Sergei) too lazy to implement proper recursive descent here, + I'm (Sergei) too lazy to implement proper recursive descent here, and anyway, nobody will use such a stupid queries that will require it :-) May be later... @@ -1406,7 +1554,7 @@ add_ft_keys(DYNAMIC_ARRAY *keyuse_array, } } - if(!cond_func) + if (!cond_func || cond_func->key == NO_SUCH_KEY) return; KEYUSE keyuse; @@ -1469,7 +1617,7 @@ update_ref_and_keys(THD *thd, DYNAMIC_ARRAY *keyuse,JOIN_TAB *join_tab, add_key_part(keyuse,field); } - if (thd->lex.ftfunc_list.elements) + if (thd->lex.select->ftfunc_list.elements) { add_ft_keys(keyuse,join_tab,cond,normal_tables); } @@ -1477,7 +1625,7 @@ update_ref_and_keys(THD *thd, DYNAMIC_ARRAY *keyuse,JOIN_TAB *join_tab, /* ** remove ref if there is a keypart which is a ref and a const. ** remove keyparts without previous keyparts. - ** Special treatment for ft-keys. SerG. + ** Special treatment for ft-keys. */ if (keyuse->elements) { @@ -1718,7 +1866,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); } @@ -1734,7 +1882,7 @@ find_best(JOIN *join,table_map rest_tables,uint idx,double record_count, ** Set tmp to (previous record count) * (records / combination) */ if ((found_part & 1) && - !(table->file->option_flag() & HA_ONLY_WHOLE_INDEX)) + !(table->file->index_flags(key) & HA_ONLY_WHOLE_INDEX)) { max_key_part=max_part_bit(found_part); /* Check if quick_range could determinate how many rows we @@ -1788,7 +1936,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 @@ -1813,10 +1961,17 @@ find_best(JOIN *join,table_map rest_tables,uint idx,double record_count, /* Don't test table scan if it can't be better. Prefer key lookup if we would use the same key for scanning. + + Don't do a table scan on InnoDB tables, if we can read the used + parts of the row from any of the used index. + This is because table scans uses index and we would not win + anything by using a table scan. */ if ((records >= s->found_records || best > s->read_time) && !(s->quick && best_key && s->quick->index == best_key->key && - best_max_key_part >= s->table->quick_key_parts[best_key->key])) + best_max_key_part >= s->table->quick_key_parts[best_key->key]) && + !((s->table->file->table_flags() & HA_TABLE_SCAN_ON_INDEX) && + s->table->used_keys && best_key)) { // Check full join if (s->on_expr) { @@ -1961,21 +2116,18 @@ prev_record_reads(JOIN *join,table_map found_ref) static bool get_best_combination(JOIN *join) { - uint i,key,tablenr; + uint i,tablenr; table_map used_tables; - TABLE *table; JOIN_TAB *join_tab,*j; KEYUSE *keyuse; - KEY *keyinfo; uint table_count; + THD *thd=join->thd; table_count=join->tables; if (!(join->join_tab=join_tab= - (JOIN_TAB*) join->thd->alloc(sizeof(JOIN_TAB)*table_count))) + (JOIN_TAB*) thd->alloc(sizeof(JOIN_TAB)*table_count))) return TRUE; - join->const_tables=0; /* for checking */ - join->const_table_map=0; join->full_join=0; used_tables=0; @@ -1984,188 +2136,188 @@ get_best_combination(JOIN *join) TABLE *form; *j= *join->best_positions[tablenr].table; form=join->table[tablenr]=j->table; - j->ref.key = -1; - j->ref.key_parts=0; - j->info=0; // For describe used_tables|= form->map; form->reginfo.join_tab=j; if (!j->on_expr) form->reginfo.not_exists_optimize=0; // Only with LEFT JOIN + if (j->type == JT_CONST) + continue; // Handled in make_join_stat.. + + j->ref.key = -1; + j->ref.key_parts=0; if (j->type == JT_SYSTEM) - { - j->table->const_table=1; - if (join->const_tables == tablenr) - { - join->const_tables++; - join->const_table_map|=form->map; - } continue; - } if (!j->keys || !(keyuse= join->best_positions[tablenr].key)) { j->type=JT_ALL; if (tablenr != join->const_tables) join->full_join=1; } - else - { - uint keyparts,length; - bool ftkey=(keyuse->keypart == FT_KEYPART); - /* - ** Use best key from find_best - */ - table=j->table; - key=keyuse->key; + else if (create_ref_for_key(join, j, keyuse, used_tables)) + return TRUE; // Something went wrong + } - keyinfo=table->key_info+key; - if (ftkey) - { - Item_func_match *ifm=(Item_func_match *)keyuse->val; + for (i=0 ; i < table_count ; i++) + join->map2table[join->join_tab[i].table->tablenr]=join->join_tab+i; + update_depend_map(join); + return 0; +} - length=0; - keyparts=1; - ifm->join_key=1; - } - else - { - keyparts=length=0; - do - { - if (!((~used_tables) & keyuse->used_tables)) - { - if (keyparts == keyuse->keypart) - { - keyparts++; - length+=keyinfo->key_part[keyuse->keypart].store_length; - } - } - keyuse++; - } while (keyuse->table == table && keyuse->key == key); - } /* not ftkey */ - - /* set up fieldref */ - keyinfo=table->key_info+key; - j->ref.key_parts=keyparts; - j->ref.key_length=length; - j->ref.key=(int) key; - if (!(j->ref.key_buff= (byte*) sql_calloc(ALIGN_SIZE(length)*2)) || - !(j->ref.key_copy= (store_key**) sql_alloc((sizeof(store_key*) * - (keyparts+1)))) || - !(j->ref.items= (Item**) sql_alloc(sizeof(Item*)*keyparts))) - { - return TRUE; - } - j->ref.key_buff2=j->ref.key_buff+ALIGN_SIZE(length); - j->ref.key_err=1; - keyuse=join->best_positions[tablenr].key; - store_key **ref_key=j->ref.key_copy; - byte *key_buff=j->ref.key_buff; - if (ftkey) - { - j->ref.items[0]=((Item_func*)(keyuse->val))->key_item(); - if (keyuse->used_tables) - return TRUE; // not supported yet. SerG +static bool create_ref_for_key(JOIN *join, JOIN_TAB *j, KEYUSE *org_keyuse, + table_map used_tables) +{ + KEYUSE *keyuse=org_keyuse; + bool ftkey=(keyuse->keypart == FT_KEYPART); + THD *thd= join->thd; + uint keyparts,length,key; + TABLE *table; + KEY *keyinfo; - j->type=JT_FT; - } - else - { - THD *thd=join->thd; - for (i=0 ; i < keyparts ; keyuse++,i++) - { - while (keyuse->keypart != i || - ((~used_tables) & keyuse->used_tables)) - keyuse++; /* Skipp other parts */ - - uint maybe_null= test(keyinfo->key_part[i].null_bit); - j->ref.items[i]=keyuse->val; // Save for cond removal - if (!keyuse->used_tables && - !(join->select_options & SELECT_DESCRIBE)) - { // Compare against constant - store_key_item *tmp=new store_key_item(keyinfo->key_part[i].field, - (char*)key_buff + - maybe_null, - maybe_null ? - (char*) key_buff : 0, - keyinfo->key_part[i].length, - keyuse->val); - if (thd->fatal_error) - { - return TRUE; - } - tmp->copy(); - } - else - *ref_key++= get_store_key(keyuse,join->const_table_map, - &keyinfo->key_part[i], - (char*) key_buff,maybe_null); - key_buff+=keyinfo->key_part[i].store_length; - } - } /* not ftkey */ - *ref_key=0; // end_marker - if (j->type == JT_FT) /* no-op */; - else if (j->type == JT_CONST) + /* + ** Use best key from find_best + */ + table=j->table; + key=keyuse->key; + keyinfo=table->key_info+key; + + if (ftkey) + { + Item_func_match *ifm=(Item_func_match *)keyuse->val; + + length=0; + keyparts=1; + ifm->join_key=1; + } + else + { + keyparts=length=0; + do + { + if (!((~used_tables) & keyuse->used_tables)) { - j->table->const_table=1; - if (join->const_tables == tablenr) + if (keyparts == keyuse->keypart) { - join->const_tables++; - join->const_table_map|=form->map; + keyparts++; + length+=keyinfo->key_part[keyuse->keypart].store_length; } } - else if (((keyinfo->flags & (HA_NOSAME | HA_NULL_PART_KEY)) != HA_NOSAME) || - keyparts != keyinfo->key_parts) - j->type=JT_REF; /* Must read with repeat */ - else if (ref_key == j->ref.key_copy) - { /* Should never be reached */ - /* - This happen if we are using a constant expression in the ON part - of an LEFT JOIN. - SELECT * FROM a LEFT JOIN b ON b.key=30 - Here we should not mark the table as a 'const' as a field may - have a 'normal' value or a NULL value. - */ - j->type=JT_CONST; - if (join->const_tables == tablenr) + keyuse++; + } while (keyuse->table == table && keyuse->key == key); + } /* not ftkey */ + + /* set up fieldref */ + keyinfo=table->key_info+key; + j->ref.key_parts=keyparts; + j->ref.key_length=length; + j->ref.key=(int) key; + if (!(j->ref.key_buff= (byte*) thd->calloc(ALIGN_SIZE(length)*2)) || + !(j->ref.key_copy= (store_key**) thd->alloc((sizeof(store_key*) * + (keyparts+1)))) || + !(j->ref.items= (Item**) thd->alloc(sizeof(Item*)*keyparts))) + { + return TRUE; + } + j->ref.key_buff2=j->ref.key_buff+ALIGN_SIZE(length); + j->ref.key_err=1; + keyuse=org_keyuse; + + store_key **ref_key=j->ref.key_copy; + byte *key_buff=j->ref.key_buff; + if (ftkey) + { + j->ref.items[0]=((Item_func*)(keyuse->val))->key_item(); + if (keyuse->used_tables) + return TRUE; // not supported yet. SerG + + j->type=JT_FT; + } + else + { + uint i; + for (i=0 ; i < keyparts ; keyuse++,i++) + { + while (keyuse->keypart != i || + ((~used_tables) & keyuse->used_tables)) + keyuse++; /* Skip other parts */ + + uint maybe_null= test(keyinfo->key_part[i].null_bit); + j->ref.items[i]=keyuse->val; // Save for cond removal + if (!keyuse->used_tables && + !(join->select_options & SELECT_DESCRIBE)) + { // Compare against constant + store_key_item *tmp=new store_key_item(thd, + keyinfo->key_part[i].field, + (char*)key_buff + + maybe_null, + maybe_null ? + (char*) key_buff : 0, + keyinfo->key_part[i].length, + keyuse->val); + if (thd->fatal_error) { - join->const_tables++; - join->const_table_map|=form->map; + return TRUE; } + tmp->copy(); } else - j->type=JT_EQ_REF; - } + *ref_key++= get_store_key(thd, + keyuse,join->const_table_map, + &keyinfo->key_part[i], + (char*) key_buff,maybe_null); + key_buff+=keyinfo->key_part[i].store_length; + } + } /* not ftkey */ + *ref_key=0; // end_marker + if (j->type == JT_FT) /* no-op */; + else if (j->type == JT_CONST) + j->table->const_table=1; + else if (((keyinfo->flags & (HA_NOSAME | HA_NULL_PART_KEY)) + != HA_NOSAME) || + keyparts != keyinfo->key_parts) + j->type=JT_REF; /* Must read with repeat */ + else if (ref_key == j->ref.key_copy) + { /* Should never be reached */ + /* + This happen if we are using a constant expression in the ON part + of an LEFT JOIN. + SELECT * FROM a LEFT JOIN b ON b.key=30 + Here we should not mark the table as a 'const' as a field may + have a 'normal' value or a NULL value. + */ + j->type=JT_CONST; } - - for (i=0 ; i < table_count ; i++) - join->map2table[join->join_tab[i].table->tablenr]=join->join_tab+i; - update_depend_map(join); + else + j->type=JT_EQ_REF; return 0; } + static store_key * -get_store_key(KEYUSE *keyuse, table_map used_tables, KEY_PART_INFO *key_part, - char *key_buff, uint maybe_null) +get_store_key(THD *thd, KEYUSE *keyuse, table_map used_tables, + KEY_PART_INFO *key_part, char *key_buff, uint maybe_null) { if (!((~used_tables) & keyuse->used_tables)) // if const item { - return new store_key_const_item(key_part->field, + return new store_key_const_item(thd, + key_part->field, key_buff + maybe_null, maybe_null ? key_buff : 0, key_part->length, keyuse->val); } else if (keyuse->val->type() == Item::FIELD_ITEM) - return new store_key_field(key_part->field, + return new store_key_field(thd, + key_part->field, key_buff + maybe_null, maybe_null ? key_buff : 0, key_part->length, ((Item_field*) keyuse->val)->field, keyuse->val->full_name()); - return new store_key_item(key_part->field, + return new store_key_item(thd, + key_part->field, key_buff + maybe_null, maybe_null ? key_buff : 0, key_part->length, @@ -2203,13 +2355,15 @@ make_simple_join(JOIN *join,TABLE *tmp_table) join->tables=1; join->const_tables=0; join->const_table_map=0; - join->tmp_table_param.copy_field_count=join->tmp_table_param.field_count= - join->tmp_table_param.sum_func_count= join->tmp_table_param.func_count=0; - join->tmp_table_param.copy_field=0; + join->tmp_table_param.field_count= join->tmp_table_param.sum_func_count= + join->tmp_table_param.func_count=0; + join->tmp_table_param.copy_field=join->tmp_table_param.copy_field_end=0; join->first_record=join->sort_and_group=0; join->sum_funcs=0; join->send_records=(ha_rows) 0; join->group=0; + join->do_send_rows = 1; + join->row_limit=join->thd->select_limit; join_tab->cache.buff=0; /* No cacheing */ join_tab->table=tmp_table; @@ -2283,7 +2437,7 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond) { DBUG_EXECUTE("where",print_where(tmp,tab->table->table_name);); SQL_SELECT *sel=tab->select=(SQL_SELECT*) - sql_memdup((gptr) select, sizeof(SQL_SELECT)); + join->thd->memdup((gptr) select, sizeof(SQL_SELECT)); if (!sel) DBUG_RETURN(1); // End of memory tab->select_cond=sel->cond=tmp; @@ -2325,15 +2479,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; } @@ -2360,8 +2518,8 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond) current_map))) { DBUG_EXECUTE("where",print_where(tmp,"cache");); - tab->cache.select=(SQL_SELECT*) sql_memdup((gptr) sel, - sizeof(SQL_SELECT)); + tab->cache.select=(SQL_SELECT*) + join->thd->memdup((gptr) sel, sizeof(SQL_SELECT)); tab->cache.select->cond=tmp; tab->cache.select->read_tables=join->const_table_map; } @@ -2378,6 +2536,7 @@ static void make_join_readinfo(JOIN *join,uint options) { uint i; + SELECT_LEX *select_lex = &(join->thd->lex.select_lex); DBUG_ENTER("make_join_readinfo"); for (i=join->const_tables ; i < join->tables ; i++) @@ -2410,7 +2569,8 @@ make_join_readinfo(JOIN *join,uint options) table->file->index_init(tab->ref.key); tab->read_first_record= join_read_key; tab->read_record.read_record= join_no_more_records; - if (table->used_keys & ((key_map) 1 << tab->ref.key)) + if (table->used_keys & ((key_map) 1 << tab->ref.key) && + !table->no_keyread) { table->key_read=1; table->file->extra(HA_EXTRA_KEYREAD); @@ -2427,8 +2587,9 @@ make_join_readinfo(JOIN *join,uint options) tab->quick=0; table->file->index_init(tab->ref.key); tab->read_first_record= join_read_always_key; - tab->read_record.read_record= join_read_next; - if (table->used_keys & ((key_map) 1 << tab->ref.key)) + tab->read_record.read_record= join_read_next_same; + if (table->used_keys & ((key_map) 1 << tab->ref.key) && + !table->no_keyread) { table->key_read=1; table->file->extra(HA_EXTRA_KEYREAD); @@ -2445,7 +2606,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) || @@ -2458,7 +2619,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; + 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); } @@ -2473,7 +2634,7 @@ make_join_readinfo(JOIN *join,uint options) } else { - join->thd->lex.options|=QUERY_NO_INDEX_USED; + select_lex->options|=QUERY_NO_INDEX_USED; statistic_increment(select_scan_count, &LOCK_status); } } @@ -2485,22 +2646,25 @@ make_join_readinfo(JOIN *join,uint options) } else { - join->thd->lex.options|=QUERY_NO_INDEX_USED; + select_lex->options|=QUERY_NO_INDEX_USED; statistic_increment(select_full_join_count, &LOCK_status); } } - if (tab->select && tab->select->quick && - table->used_keys & ((key_map) 1 << tab->select->quick->index)) + if (!table->no_keyread) { - table->key_read=1; - table->file->extra(HA_EXTRA_KEYREAD); - } - else if (table->used_keys && ! (tab->select && tab->select->quick)) - { // Only read index tree - tab->index=find_shortest_key(table, table->used_keys); - tab->table->file->index_init(tab->index); - tab->read_first_record= join_init_read_first_with_key; - tab->type=JT_NEXT; // Read with index_first / index_next + if (tab->select && tab->select->quick && + table->used_keys & ((key_map) 1 << tab->select->quick->index)) + { + table->key_read=1; + table->file->extra(HA_EXTRA_KEYREAD); + } + else if (table->used_keys && ! (tab->select && tab->select->quick)) + { // Only read index tree + tab->index=find_shortest_key(table, table->used_keys); + tab->table->file->index_init(tab->index); + tab->read_first_record= join_read_first; + tab->type=JT_NEXT; // Read with index_first / index_next + } } } break; @@ -2553,14 +2717,16 @@ join_free(JOIN *join) } // We are not using tables anymore // Unlock all tables. We may be in an INSERT .... SELECT statement. - if (join->lock && join->thd->lock) + if (join->lock && join->thd->lock && + !(join->select_options & SELECT_NO_UNLOCK)) { mysql_unlock_read_tables(join->thd, join->lock);// Don't free join->lock join->lock=0; } join->group_fields.delete_elements(); join->tmp_table_param.copy_funcs.delete_elements(); - delete [] join->tmp_table_param.copy_field; + if (join->tmp_table_param.copy_field) // Because of bug in ecc + delete [] join->tmp_table_param.copy_field; join->tmp_table_param.copy_field=0; DBUG_VOID_RETURN; } @@ -2610,7 +2776,7 @@ eq_ref_table(JOIN *join, ORDER *start_order, JOIN_TAB *tab) if (order) { found++; - dbug_assert(!(order->used & map)); + DBUG_ASSERT(!(order->used & map)); order->used|=map; continue; // Used in ORDER BY } @@ -2725,7 +2891,7 @@ remove_const(JOIN *join,ORDER *first_order, COND *cond, bool *simple_order) else if (!(order_tables & not_const_tables)) { DBUG_PRINT("info",("removing: %s", order->item[0]->full_name())); - continue; // skipp const item + continue; // skip const item } else { @@ -2768,7 +2934,7 @@ return_zero_rows(select_result *result,TABLE_LIST *tables,List<Item> &fields, DBUG_ENTER("return_zero_rows"); if (select_options & SELECT_DESCRIBE) - { + { describe_info(current_thd, info); DBUG_RETURN(0); } @@ -2784,17 +2950,17 @@ return_zero_rows(select_result *result,TABLE_LIST *tables,List<Item> &fields, if (having && having->val_int() == 0) send_row=0; } - if (!tables || !(result->send_fields(fields,1))) + if (!(result->send_fields(fields,1))) { if (send_row) result->send_data(fields); - if (tables) // Not from do_select() + if (tables) // Not from do_select() { /* Close open cursors */ for (TABLE_LIST *table=tables; table ; table=table->next) table->table->file->index_end(); - result->send_eof(); // Should be safe } + result->send_eof(); // Should be safe } DBUG_RETURN(0); } @@ -2912,7 +3078,7 @@ propagate_cond_constants(I_List<COND_CMP> *save_list,COND *and_level, { bool and_level= ((Item_cond*) cond)->functype() == Item_func::COND_AND_FUNC; - List_iterator<Item> li(*((Item_cond*) cond)->argument_list()); + List_iterator_fast<Item> li(*((Item_cond*) cond)->argument_list()); Item *item; I_List<COND_CMP> save; while ((item=li++)) @@ -3066,13 +3232,13 @@ remove_eq_conds(COND *cond,Item::cond_result *cond_value) ((Item_func*) cond)->functype() == Item_func::ISNULL_FUNC) { /* - ** Handles this special case for some ODBC applications: - ** The are requesting the row that was just updated with a auto_increment - ** value with this construct: - ** - ** SELECT * from table_name where auto_increment_column IS NULL - ** This will be changed to: - ** SELECT * from table_name where auto_increment_column = LAST_INSERT_ID + Handles this special case for some ODBC applications: + The are requesting the row that was just updated with a auto_increment + value with this construct: + + SELECT * from table_name where auto_increment_column IS NULL + This will be changed to: + SELECT * from table_name where auto_increment_column = LAST_INSERT_ID */ Item_func_isnull *func=(Item_func_isnull*) cond; @@ -3085,6 +3251,7 @@ remove_eq_conds(COND *cond,Item::cond_result *cond_value) (thd->options & OPTION_AUTO_IS_NULL) && thd->insert_id()) { + query_cache_abort(&thd->net); COND *new_cond; if ((new_cond= new Item_func_eq(args[0], new Item_int("last_insert_id()", @@ -3128,7 +3295,7 @@ remove_eq_conds(COND *cond,Item::cond_result *cond_value) } } *cond_value=Item::COND_OK; - return cond; /* Point at next and level */ + return cond; // Point at next and level } /* @@ -3142,7 +3309,7 @@ const_expression_in_where(COND *cond, Item *comp_item, Item **const_item) { bool and_level= (((Item_cond*) cond)->functype() == Item_func::COND_AND_FUNC); - List_iterator<Item> li(*((Item_cond*) cond)->argument_list()); + List_iterator_fast<Item> li(*((Item_cond*) cond)->argument_list()); Item *item; while ((item=li++)) { @@ -3197,7 +3364,7 @@ const_expression_in_where(COND *cond, Item *comp_item, Item **const_item) ** for send_fields ****************************************************************************/ -Field *create_tmp_field(TABLE *table,Item *item, Item::Type type, +Field *create_tmp_field(THD *thd, TABLE *table,Item *item, Item::Type type, Item_result_field ***copy_func, Field **from_field, bool group, bool modify_item) { @@ -3230,7 +3397,7 @@ Field *create_tmp_field(TABLE *table,Item *item, Item::Type type, item->name,table,item_sum->decimals); case INT_RESULT: return new Field_longlong(item_sum->max_length,maybe_null, - item->name,table); + item->name,table,item->unsigned_flag); case STRING_RESULT: if (item_sum->max_length > 255) return new Field_blob(item_sum->max_length,maybe_null, @@ -3239,7 +3406,7 @@ Field *create_tmp_field(TABLE *table,Item *item, Item::Type type, item->name,table,item->binary); } } - current_thd->fatal_error=1; + thd->fatal_error=1; return 0; // Error } case Item::FIELD_ITEM: @@ -3247,7 +3414,8 @@ Field *create_tmp_field(TABLE *table,Item *item, Item::Type type, Field *org_field=((Item_field*) item)->field,*new_field; *from_field=org_field; - if ((new_field= org_field->new_field(table))) // Should always be true + // The following should always be true + if ((new_field= org_field->new_field(&thd->mem_root,table))) { if (modify_item) ((Item_field*) item)->result_field= new_field; @@ -3281,7 +3449,7 @@ Field *create_tmp_field(TABLE *table,Item *item, Item::Type type, break; case INT_RESULT: new_field=new Field_longlong(item->max_length,maybe_null, - item->name,table); + item->name,table, item->unsigned_flag); break; case STRING_RESULT: if (item->max_length > 255) @@ -3307,13 +3475,14 @@ 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, hidden_null_count, hidden_null_pack_length, hidden_field_count, blob_count,group_null_items; bool using_unique_constraint=0; + bool not_all_columns= !(select_options & TMP_TABLE_ALL_COLUMNS); char *tmpname,path[FN_REFLEN]; byte *pos,*group_buff; uchar *null_flags; @@ -3336,7 +3505,7 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields, temp_pool_slot = bitmap_set_next(&temp_pool); if (temp_pool_slot != MY_BIT_NONE) // we got a slot - sprintf(path, "%s%s_%lx_%i", mysql_tmpdir, tmp_file_prefix, + sprintf(path, "%s%s_%lx_%i", mysql_tmpdir, tmp_file_prefix, current_pid, temp_pool_slot); else // if we run out of slots or we are not using tempool sprintf(path,"%s%s%lx_%lx_%x",mysql_tmpdir,tmp_file_prefix,current_pid, @@ -3347,7 +3516,11 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields, if (!param->quick_group) group=0; // Can't use group key else for (ORDER *tmp=group ; tmp ; tmp=tmp->next) + { (*tmp->item)->marker=4; // Store null in key + if ((*tmp->item)->max_length >= MAX_CHAR_WIDTH) + using_unique_constraint=1; + } if (param->group_length >= MAX_BLOB_WIDTH) using_unique_constraint=1; if (group) @@ -3404,24 +3577,27 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields, reclength=blob_count=null_count=hidden_null_count=group_null_items=0; param->using_indirect_summary_function=0; - List_iterator<Item> li(fields); + List_iterator_fast<Item> li(fields); Item *item; Field **tmp_from_field=from_field; while ((item=li++)) { Item::Type type=item->type(); - if (item->with_sum_func && type != Item::SUM_FUNC_ITEM) + if (not_all_columns) { - /* - Mark that the we have ignored an item that refers to a summary - function. We need to know this if someone is going to use - DISTINCT on the result. - */ - param->using_indirect_summary_function=1; - continue; + if (item->with_sum_func && type != Item::SUM_FUNC_ITEM) + { + /* + Mark that the we have ignored an item that refers to a summary + function. We need to know this if someone is going to use + DISTINCT on the result. + */ + param->using_indirect_summary_function=1; + continue; + } + if (item->const_item()) // We don't have to store this + continue; } - if (item->const_item()) // We don't have to store this - continue; if (type == Item::SUM_FUNC_ITEM && !group && !save_sum_fields) { /* Can't calc group yet */ ((Item_sum*) item)->result_field=0; @@ -3431,8 +3607,8 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields, if (!arg->const_item()) { Field *new_field= - create_tmp_field(table,arg,arg->type(),©_func,tmp_from_field, - group != 0,1); + create_tmp_field(thd, table,arg,arg->type(),©_func, + tmp_from_field, group != 0,not_all_columns); if (!new_field) goto err; // Should be OOM tmp_from_field++; @@ -3448,8 +3624,9 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields, } else { - Field *new_field=create_tmp_field(table,item,type,©_func, - tmp_from_field, group != 0,1); + Field *new_field=create_tmp_field(thd, table, item,type, ©_func, + tmp_from_field, group != 0, + not_all_columns); if (!new_field) { if (thd->fatal_error) @@ -3545,6 +3722,10 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields, { if (field->flags & GROUP_FLAG && !using_unique_constraint) { + /* + We have to reserve one byte here for NULL bits, + as this is updated by 'end_update()' + */ *pos++=0; // Null is stored here recinfo->length=1; recinfo->type=FIELD_NORMAL; @@ -3579,14 +3760,14 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields, (field->type() == FIELD_TYPE_STRING || field->type() == FIELD_TYPE_VAR_STRING) && length >= 10 && blob_count) - recinfo->type=FIELD_SKIPP_ENDSPACE; + recinfo->type=FIELD_SKIP_ENDSPACE; else recinfo->type=FIELD_NORMAL; if (!--hidden_field_count) null_count=(null_count+7) & ~7; // move to next byte } - param->copy_field_count=(uint) (copy - param->copy_field); + param->copy_field_end=copy; param->recinfo=recinfo; store_record(table,2); // Make empty default record @@ -3628,19 +3809,21 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields, if (!using_unique_constraint) { group->buff=(char*) group_buff; - if (!(group->field=field->new_field(table))) + if (!(group->field=field->new_field(&thd->mem_root,table))) goto err; /* purecov: inspected */ if (maybe_null) { /* - To be able to group on NULL, we move the null bit to be - just before the column and extend the key to cover the null bit + To be able to group on NULL, we reserve place in group_buff + for the NULL flag just before the column. + The field data is after this flag. + The NULL flag is updated by 'end_update()' and 'end_write()' */ - *group_buff= 0; // Init null byte - key_part_info->offset--; - key_part_info->length++; - group->field->move_field((char*) group_buff+1, (uchar*) group_buff, - 1); + keyinfo->flags|= HA_NULL_ARE_EQUAL; // def. that NULL == NULL + key_part_info->null_bit=field->null_bit; + key_part_info->null_offset= (uint) (field->null_ptr - + (uchar*) table->record[0]); + group->field->move_field((char*) ++group->buff); } else group->field->move_field((char*) group_buff); @@ -3742,14 +3925,13 @@ static bool open_tmp_table(TABLE *table) return(1); } /* VOID(ha_lock(table,F_WRLCK)); */ /* Single thread table */ - (void) table->file->extra(HA_EXTRA_NO_READCHECK); /* Not needed */ (void) table->file->extra(HA_EXTRA_QUICK); /* Faster */ return(0); } static bool create_myisam_tmp_table(TABLE *table,TMP_TABLE_PARAM *param, - uint options) + ulong options) { int error; MI_KEYDEF keydef; @@ -3796,10 +3978,10 @@ static bool create_myisam_tmp_table(TABLE *table,TMP_TABLE_PARAM *param, for (uint i=0; i < keyinfo->key_parts ; i++,seg++) { Field *field=keyinfo->key_part[i].field; - seg->flag=0; - seg->language=MY_CHARSET_CURRENT; - seg->length=keyinfo->key_part[i].length; - seg->start=keyinfo->key_part[i].offset; + seg->flag= 0; + seg->language= MY_CHARSET_CURRENT; + seg->length= keyinfo->key_part[i].length; + seg->start= keyinfo->key_part[i].offset; if (field->flags & BLOB_FLAG) { seg->type= @@ -3820,11 +4002,17 @@ static bool create_myisam_tmp_table(TABLE *table,TMP_TABLE_PARAM *param, keyinfo->key_part[i].length > 4) seg->flag|=HA_SPACE_PACK; } - if (using_unique_constraint && - !(field->flags & NOT_NULL_FLAG)) + if (!(field->flags & NOT_NULL_FLAG)) { seg->null_bit= field->null_bit; seg->null_pos= (uint) (field->null_ptr - (uchar*) table->record[0]); + /* + We are using a GROUP BY on something that contains NULL + In this case we have to tell MyISAM that two NULL should + on INSERT be compared as equal + */ + if (!using_unique_constraint) + keydef.flag|= HA_NULL_ARE_EQUAL; } } } @@ -3910,12 +4098,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])) { @@ -3956,9 +4150,12 @@ bool create_myisam_from_heap(TABLE *table, TMP_TABLE_PARAM *param, int error, } -/***************************************************************************** -** Make a join of all tables and write it on socket or to table -*****************************************************************************/ +/**************************************************************************** + Make a join of all tables and write it on socket or to table + Return: 0 if ok + 1 if error is sent + -1 if error should be sent +****************************************************************************/ static int do_select(JOIN *join,List<Item> *fields,TABLE *table,Procedure *procedure) @@ -4035,22 +4232,22 @@ do_select(JOIN *join,List<Item> *fields,TABLE *table,Procedure *procedure) if (error == -3) error=0; /* select_limit used */ } - if (!table) /* If sending data to client */ + + /* Return 1 if error is sent; -1 if error should be sent */ + if (error < 0) { - if (error < 0) - join->result->send_error(0,NullS); /* purecov: inspected */ - else + join->result->send_error(0,NullS); /* purecov: inspected */ + error=1; // Error sent + } + else + { + error=0; + if (!table) // If sending data to client { join_free(join); // Unlock all cursors if (join->result->send_eof()) - error= -1; + error= 1; // Don't send error } - } - else if (error < 0) - join->result->send_error(0,NullS); /* purecov: inspected */ - - if (error >= 0) - { DBUG_PRINT("info",("%ld records output",join->send_records)); } if (table) @@ -4061,15 +4258,15 @@ do_select(JOIN *join,List<Item> *fields,TABLE *table,Procedure *procedure) my_errno=tmp; error= -1; } - if (table->file->index_end()) + if ((tmp=table->file->index_end())) { my_errno=tmp; error= -1; } - if (error != old_error) + if (error == -1) table->file->print_error(my_errno,MYF(0)); } - DBUG_RETURN(error < 0); + DBUG_RETURN(error); } @@ -4145,10 +4342,8 @@ sub_select(JOIN *join,JOIN_TAB *join_tab,bool end_of_records) info->file->unlock_row(); } } while (!(error=info->read_record(info))); - if (error > 0) // Fatal error - return -1; } - else if (error > 0) + if (error > 0) // Fatal error return -1; if (!found && on_expr) @@ -4239,45 +4434,44 @@ flush_cached_records(JOIN *join,JOIN_TAB *join_tab,bool skipp_last) *****************************************************************************/ static int -join_read_const_tables(JOIN *join) +join_read_const_table(JOIN_TAB *tab, POSITION *pos) { - uint i; int error; - DBUG_ENTER("join_read_const_tables"); - for (i=0 ; i < join->const_tables ; i++) - { - TABLE *form=join->table[i]; - form->null_row=0; - form->status=STATUS_NO_RECORD; - - if (join->join_tab[i].type == JT_SYSTEM) - { - if ((error=join_read_system(join->join_tab+i))) - { // Info for DESCRIBE - join->join_tab[i].info="const row not found"; - join->best_positions[i].records_read=0.0; - if (!form->outer_join || error > 0) - DBUG_RETURN(error); - } - } - else - { - if ((error=join_read_const(join->join_tab+i))) - { - join->join_tab[i].info="unique row not found"; - join->best_positions[i].records_read=0.0; - if (!form->outer_join || error > 0) - DBUG_RETURN(error); - } + DBUG_ENTER("join_read_const_table"); + TABLE *table=tab->table; + table->const_table=1; + table->null_row=0; + table->status=STATUS_NO_RECORD; + + if (tab->type == JT_SYSTEM) + { + if ((error=join_read_system(tab))) + { // Info for DESCRIBE + tab->info="const row not found"; + /* Mark for EXPLAIN that the row was not found */ + pos->records_read=0.0; + if (!table->outer_join || error > 0) + DBUG_RETURN(error); } - if (join->join_tab[i].on_expr && !form->null_row) + } + else + { + if ((error=join_read_const(tab))) { - if ((form->null_row= test(join->join_tab[i].on_expr->val_int() == 0))) - empty_record(form); + tab->info="unique row not found"; + /* Mark for EXPLAIN that the row was not found */ + pos->records_read=0.0; + if (!table->outer_join || error > 0) + DBUG_RETURN(error); } - if (!form->null_row) - form->maybe_null=0; } + if (tab->on_expr && !table->null_row) + { + if ((table->null_row= test(tab->on_expr->val_int() == 0))) + empty_record(table); + } + if (!table->null_row) + table->maybe_null=0; DBUG_RETURN(0); } @@ -4289,7 +4483,8 @@ join_read_system(JOIN_TAB *tab) int error; if (table->status & STATUS_GARBAGE) // If first read { - if ((error=table->file->rnd_first(table->record[0]))) + if ((error=table->file->read_first_row(table->record[0], + table->primary_key))) { if (error != HA_ERR_END_OF_FILE) { @@ -4403,6 +4598,35 @@ join_read_always_key(JOIN_TAB *tab) return 0; } +/* + This function is used when optimizing away ORDER BY in + SELECT * FROM t1 WHERE a=1 ORDER BY a DESC,b DESC +*/ + +static int +join_read_last_key(JOIN_TAB *tab) +{ + int error; + TABLE *table= tab->table; + + if (cp_buffer_from_ref(&tab->ref)) + return -1; + if ((error=table->file->index_read_last(table->record[0], + tab->ref.key_buff, + tab->ref.key_length))) + { + if (error != HA_ERR_KEY_NOT_FOUND) + { + sql_print_error("read_const: Got error %d when reading table %s",error, + table->path); + table->file->print_error(error,MYF(0)); + return 1; + } + return -1; /* purecov: inspected */ + } + return 0; +} + /* ARGSUSED */ static int @@ -4413,7 +4637,7 @@ join_no_more_records(READ_RECORD *info __attribute__((unused))) static int -join_read_next(READ_RECORD *info) +join_read_next_same(READ_RECORD *info) { int error; TABLE *table= info->table; @@ -4436,6 +4660,37 @@ join_read_next(READ_RECORD *info) return 0; } +static int +join_read_prev_same(READ_RECORD *info) +{ + int error; + TABLE *table= info->table; + JOIN_TAB *tab=table->reginfo.join_tab; + + if ((error=table->file->index_prev(table->record[0]))) + { + if (error != HA_ERR_END_OF_FILE) + { + sql_print_error("read_next: Got error %d when reading table %s",error, + table->path); + table->file->print_error(error,MYF(0)); + error= 1; + } + else + { + table->status= STATUS_GARBAGE; + error= -1; + } + } + else if (key_cmp(table, tab->ref.key_buff, tab->ref.key, + tab->ref.key_length)) + { + table->status=STATUS_NOT_FOUND; + error= -1; + } + return error; +} + static int join_init_quick_read_record(JOIN_TAB *tab) @@ -4466,17 +4721,18 @@ join_init_read_record(JOIN_TAB *tab) } static int -join_init_read_first_with_key(JOIN_TAB *tab) +join_read_first(JOIN_TAB *tab) { int error; TABLE *table=tab->table; - if (!table->key_read && (table->used_keys & ((key_map) 1 << tab->index))) + if (!table->key_read && (table->used_keys & ((key_map) 1 << tab->index)) && + !table->no_keyread) { table->key_read=1; table->file->extra(HA_EXTRA_KEYREAD); } tab->table->status=0; - tab->read_record.read_record=join_init_read_next_with_key; + tab->read_record.read_record=join_read_next; tab->read_record.table=table; tab->read_record.file=table->file; tab->read_record.index=tab->index; @@ -4496,8 +4752,9 @@ join_init_read_first_with_key(JOIN_TAB *tab) return 0; } + static int -join_init_read_next_with_key(READ_RECORD *info) +join_read_next(READ_RECORD *info) { int error=info->file->index_next(info->record); if (error) @@ -4514,9 +4771,8 @@ join_init_read_next_with_key(READ_RECORD *info) return 0; } - static int -join_init_read_last_with_key(JOIN_TAB *tab) +join_read_last(JOIN_TAB *tab) { TABLE *table=tab->table; int error; @@ -4526,7 +4782,7 @@ join_init_read_last_with_key(JOIN_TAB *tab) table->file->extra(HA_EXTRA_KEYREAD); } tab->table->status=0; - tab->read_record.read_record=join_init_read_prev_with_key; + tab->read_record.read_record=join_read_prev; tab->read_record.table=table; tab->read_record.file=table->file; tab->read_record.index=tab->index; @@ -4546,8 +4802,9 @@ join_init_read_last_with_key(JOIN_TAB *tab) return 0; } + static int -join_init_read_prev_with_key(READ_RECORD *info) +join_read_prev(READ_RECORD *info) { int error=info->file->index_prev(info->record); if (error) @@ -4564,13 +4821,14 @@ join_init_read_prev_with_key(READ_RECORD *info) return 0; } + static int join_ft_read_first(JOIN_TAB *tab) { int error; TABLE *table= tab->table; -#if 0 +#if NOT_USED_YET if (cp_buffer_from_ref(&tab->ref)) // as ft-key doesn't use store_key's return -1; // see also FT_SELECT::init() #endif @@ -4627,14 +4885,36 @@ 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_TAB *jt=join->join_tab; + if ((join->tables == 1) && !join->tmp_table && !join->sort_and_group + && !join->send_group_parts && !join->having && !jt->select_cond && + !(jt->table->file->table_flags() & HA_NOT_EXACT_COUNT)) + { + /* Join over all rows in table; Return number of found rows */ + join->select_options ^= OPTION_FOUND_ROWS; + jt->table->file->info(HA_STATUS_VARIABLE); + join->send_records = jt->table->file->records; + } + else + { + join->do_send_rows=0; + join->thd->select_limit = HA_POS_ERROR; + DBUG_RETURN(0); + } + } DBUG_RETURN(-3); // Abort nicely + } } else { @@ -4662,12 +4942,12 @@ end_send_group(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)), join->procedure->end_group(); if (idx < (int) join->send_group_parts) { - int error; + int error=0; if (join->procedure) { 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 @@ -4682,15 +4962,21 @@ end_send_group(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)), } if (join->having && join->having->val_int() == 0) error= -1; // Didn't satisfy having - else + else if (join->do_send_rows) error=join->result->send_data(*join->fields) ? 1 : 0; } if (error > 0) DBUG_RETURN(-1); /* purecov: inspected */ + 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; + } if (end_of_records) DBUG_RETURN(0); - if (!error && ++join->send_records >= join->thd->select_limit) - DBUG_RETURN(-3); /* Abort nicely */ } } else @@ -4736,6 +5022,7 @@ end_write(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)), copy_fields(&join->tmp_table_param); copy_funcs(join->tmp_table_param.funcs); +#ifdef TO_BE_DELETED if (!table->uniques) // If not unique handling { /* Copy null values from group to row */ @@ -4746,10 +5033,11 @@ end_write(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)), if (item->maybe_null) { Field *field=item->tmp_table_field(); - field->ptr[-1]= (byte) (field->is_null() ? 0 : 1); + field->ptr[-1]= (byte) (field->is_null() ? 1 : 0); } } } +#endif if (!join->having || join->having->val_int()) { join->found_records++; @@ -4761,8 +5049,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); } } } @@ -4797,8 +5092,9 @@ end_update(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)), { Item *item= *group->item; item->save_org_in_field(group->field); + /* Store in the used key if the field was 0 */ if (item->maybe_null) - group->buff[0]=item->null_value ? 0: 1; // Save reversed value + group->buff[-1]=item->null_value ? 1 : 0; } // table->file->index_init(0); if (!table->file->index_read(table->record[1], @@ -4957,7 +5253,7 @@ end_write_group(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)), /***************************************************************************** ** Remove calculation with tables that aren't yet read. Remove also tests -** against fields that are read through key where the table is not a +** against fields that are read through key where the table is not a ** outer join table. ** We can't remove tests that are made against columns which are stored ** in sorted order. @@ -4976,8 +5272,7 @@ static bool test_if_ref(Item_field *left_item,Item *right_item) { if (right_item->type() == Item::FIELD_ITEM) return (field->eq_def(((Item_field *) right_item)->field)); - if (right_item->const_item() && - (right_item->val_int() || !right_item->null_value)) + if (right_item->const_item() && !(right_item->is_null())) { // We can remove binary fields and numerical fields except float, // as float comparison isn't 100 % secure @@ -5098,9 +5393,11 @@ part_of_refkey(TABLE *table,Field *field) ** Returns: 1 if key is ok. ** 0 if key can't be used ** -1 if reverse key can be used +** used_key_parts is set to key parts used if length != 0 *****************************************************************************/ -static int test_if_order_by_key(ORDER *order, TABLE *table, uint idx) +static int test_if_order_by_key(ORDER *order, TABLE *table, uint idx, + uint *used_key_parts) { KEY_PART_INFO *key_part,*key_part_end; key_part=table->key_info[idx].key_part; @@ -5132,6 +5429,7 @@ static int test_if_order_by_key(ORDER *order, TABLE *table, uint idx) reverse=flag; // Remember if reverse key_part++; } + *used_key_parts= (uint) (key_part - table->key_info[idx].key_part); return reverse; } @@ -5154,17 +5452,20 @@ static uint find_shortest_key(TABLE *table, key_map usable_keys) } -/***************************************************************************** -** If not selecting by given key, create a index how records should be read -** return: 0 ok -** -1 some fatal error -** 1 no records -*****************************************************************************/ +/* + Test if we can skip the ORDER BY by using an index. + + If we can use an index, the JOIN_TAB / tab->select struct + is changed to use the index. -/* Return 1 if we don't have to do file sorting */ + Return: + 0 We have to use filesort to do the sorting + 1 We can use an index. +*/ 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; @@ -5192,10 +5493,55 @@ test_if_skip_sort_order(JOIN_TAB *tab,ORDER *order,ha_rows select_limit) if (ref_key >= 0) { + /* + We come here when there is a REF key. + */ + int order_direction; + uint used_key_parts; /* 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, + &used_key_parts))) + { + if (order_direction == -1) // If ORDER BY ... DESC + { + if (select && select->quick) + { + /* + Don't reverse the sort order, if it's already done. + (In some cases test_if_order_by_key() can be called multiple times + */ + if (!select->quick->reverse_sorted()) + { + // ORDER BY range_key DESC + QUICK_SELECT_DESC *tmp=new QUICK_SELECT_DESC(select->quick, + used_key_parts); + if (!tmp || tmp->error) + { + delete tmp; + DBUG_RETURN(0); // Reverse sort not supported + } + select->quick=tmp; + } + DBUG_RETURN(1); + } + if (tab->ref.key_parts < used_key_parts) + { + /* + SELECT * FROM t1 WHERE a=1 ORDER BY a DESC,b DESC + + Use a traversal function that starts by reading the last row + with key part (A) and then traverse the index backwards. + */ + if (table->file->table_flags() & HA_NOT_READ_PREFIX_LAST) + DBUG_RETURN(1); + tab->read_first_record= join_read_last_key; + tab->read_record.read_record= join_read_prev_same; + /* fall through */ + } + } DBUG_RETURN(1); /* No need to sort */ + } } else { @@ -5214,20 +5560,24 @@ test_if_skip_sort_order(JOIN_TAB *tab,ORDER *order,ha_rows select_limit) for (nr=0; keys ; keys>>=1, nr++) { + uint not_used; if (keys & 1) { int flag; - if ((flag=test_if_order_by_key(order,table,nr))) + if ((flag=test_if_order_by_key(order, table, nr, ¬_used))) { - 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 (table->used_keys & ((key_map) 1 << nr)) + if (!no_changes) { - table->key_read=1; - table->file->extra(HA_EXTRA_KEYREAD); + tab->index=nr; + tab->read_first_record= (flag > 0 ? join_read_first: + join_read_last); + table->file->index_init(nr); + tab->type=JT_NEXT; // Read with index_first(), index_next() + if (table->used_keys & ((key_map) 1 << nr)) + { + table->key_read=1; + table->file->extra(HA_EXTRA_KEYREAD); + } } DBUG_RETURN(1); } @@ -5237,6 +5587,14 @@ test_if_skip_sort_order(JOIN_TAB *tab,ORDER *order,ha_rows select_limit) DBUG_RETURN(0); // Can't use index. } + +/***************************************************************************** + If not selecting by given key, create an index how records should be read + return: 0 ok + -1 some fatal error + 1 no records +*****************************************************************************/ + static int create_sort_index(JOIN_TAB *tab,ORDER *order,ha_rows select_limit) { @@ -5247,7 +5605,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 */ @@ -5280,8 +5638,11 @@ 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); + tab->records=table->found_records; // For SQL_CALC_ROWS delete select; // filesort did select tab->select=0; tab->select_cond=0; @@ -5298,11 +5659,11 @@ err: DBUG_RETURN(-1); } - /* ** Add the HAVING criteria to table->select */ +#ifdef NOT_YET static bool fix_having(JOIN *join, Item **having) { (*having)->update_used_tables(); // Some tables may have been const @@ -5330,6 +5691,7 @@ static bool fix_having(JOIN *join, Item **having) } return 0; } +#endif /***************************************************************************** @@ -5380,7 +5742,6 @@ remove_duplicates(JOIN *join, TABLE *entry,List<Item> &fields, Item *having) DBUG_ENTER("remove_duplicates"); entry->reginfo.lock_type=TL_WRITE; - entry->file->extra(HA_EXTRA_NO_READCHECK); /* Calculate how many saved fields there is in list */ field_count=0; @@ -5566,7 +5927,7 @@ static int remove_dup_with_hash_index(THD *thd, TABLE *table, if ((error=file->delete_row(record))) goto err; continue; - } + } /* copy fields to key buffer */ field_length=field_lengths; @@ -6106,7 +6467,7 @@ count_field_types(TMP_TABLE_PARAM *param, List<Item> &fields, List_iterator<Item> li(fields); Item *field; - param->field_count=param->sum_func_count=param->func_count= + param->field_count=param->sum_func_count=param->func_count= param->hidden_field_count=0; param->quick_group=1; while ((field=li++)) @@ -6200,7 +6561,8 @@ get_sort_by_table(ORDER *a,ORDER *b,TABLE_LIST *tables) static void calc_group_buffer(JOIN *join,ORDER *group) { - uint key_length=0,parts=0; + uint key_length=0, parts=0, null_parts=0; + if (group) join->group= 1; for (; group ; group=group->next) @@ -6221,10 +6583,11 @@ calc_group_buffer(JOIN *join,ORDER *group) key_length+=(*group->item)->max_length; parts++; if ((*group->item)->maybe_null) - key_length++; + null_parts++; } - join->tmp_table_param.group_length=key_length; + join->tmp_table_param.group_length=key_length+null_parts; join->tmp_table_param.group_parts=parts; + join->tmp_table_param.group_null_parts=null_parts; } @@ -6275,7 +6638,7 @@ test_if_group_changed(List<Item_buff> &list) */ bool -setup_copy_fields(TMP_TABLE_PARAM *param,List<Item> &fields) +setup_copy_fields(THD *thd, TMP_TABLE_PARAM *param, List<Item> &fields) { Item *pos; List_iterator<Item> li(fields); @@ -6283,7 +6646,7 @@ setup_copy_fields(TMP_TABLE_PARAM *param,List<Item> &fields) DBUG_ENTER("setup_copy_fields"); if (!(copy=param->copy_field= new Copy_field[param->field_count])) - goto err; + goto err2; param->copy_funcs.empty(); while ((pos=li++)) @@ -6303,7 +6666,7 @@ setup_copy_fields(TMP_TABLE_PARAM *param,List<Item> &fields) /* set up save buffer and change result_field to point at saved value */ Field *field= item->field; - item->result_field=field->new_field(field->table); + item->result_field=field->new_field(&thd->mem_root,field->table); char *tmp=(char*) sql_alloc(field->pack_length()+1); if (!tmp) goto err; @@ -6328,12 +6691,13 @@ setup_copy_fields(TMP_TABLE_PARAM *param,List<Item> &fields) goto err; } } - param->copy_field_count= (uint) (copy - param->copy_field); + param->copy_field_end= copy; DBUG_RETURN(0); err: - delete [] param->copy_field; + delete [] param->copy_field; // This is never 0 param->copy_field=0; +err2: DBUG_RETURN(TRUE); } @@ -6346,17 +6710,16 @@ void copy_fields(TMP_TABLE_PARAM *param) { Copy_field *ptr=param->copy_field; - Copy_field *end=ptr+param->copy_field_count; + Copy_field *end=param->copy_field_end; for ( ; ptr != end; ptr++) (*ptr->do_copy)(ptr); - List_iterator<Item> it(param->copy_funcs); + List_iterator_fast<Item> &it=param->copy_funcs_it; + it.rewind(); Item_copy_string *item; while ((item = (Item_copy_string*) it++)) - { item->copy(); - } } @@ -6614,163 +6977,192 @@ static bool add_ref_to_table_cond(THD *thd, JOIN_TAB *join_tab) ****************************************************************************/ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order, - bool distinct) + bool distinct,const char *message) { List<Item> field_list; Item *item; + List<Item> item_list; THD *thd=join->thd; + MYSQL_LOCK *save_lock; + SELECT_LEX *select_lex = &(join->thd->lex.select_lex); + select_result *result=join->result; 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); - 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", + select_lex->options&= ~(QUERY_NO_INDEX_USED | QUERY_NO_GOOD_INDEX_USED); + thd->offset_limit=0; + if (thd->lex.select == select_lex) + { + 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", NAME_LEN*MAX_KEY)); - item->maybe_null=1; - field_list.push_back(item=new Item_empty_string("key",NAME_LEN)); - item->maybe_null=1; - field_list.push_back(item=new Item_int("key_len",0,3)); - item->maybe_null=1; - field_list.push_back(item=new Item_empty_string("ref", - NAME_LEN*MAX_REF_PARTS)); - item->maybe_null=1; - field_list.push_back(new Item_real("rows",0.0,0,10)); - field_list.push_back(new Item_empty_string("Extra",255)); - if (send_fields(thd,field_list,1)) - return; /* purecov: inspected */ + item->maybe_null=1; + field_list.push_back(item=new Item_empty_string("key",NAME_LEN)); + item->maybe_null=1; + field_list.push_back(item=new Item_int("key_len",0,3)); + item->maybe_null=1; + field_list.push_back(item=new Item_empty_string("ref", + NAME_LEN*MAX_REF_PARTS)); + item->maybe_null=1; + field_list.push_back(new Item_real("rows",0.0,0,10)); + field_list.push_back(new Item_empty_string("Extra",255)); + if (result->send_fields(field_list,1)) + return; + } - char buff[512],*buff_ptr; - String tmp(buff,sizeof(buff)),*packet= &thd->packet; - table_map used_tables=0; - for (uint i=0 ; i < join->tables ; i++) + if (message) { - JOIN_TAB *tab=join->join_tab+i; - TABLE *table=tab->table; - - if (tab->type == JT_ALL && tab->select && tab->select->quick) - tab->type= JT_RANGE; - packet->length(0); - net_store_data(packet,table->table_name); - net_store_data(packet,join_type_str[tab->type]); - tmp.length(0); - key_map bits; - uint j; - for (j=0,bits=tab->keys ; bits ; j++,bits>>=1) - { - if (bits & 1) + item_list.push_back(new Item_empty_string("",0)); + item_list.push_back(new Item_empty_string("",0)); + item_list.push_back(new Item_empty_string("",0)); + item_list.push_back(new Item_empty_string("",0)); + item_list.push_back(new Item_empty_string("",0)); + item_list.push_back(new Item_empty_string("",0)); + item_list.push_back(new Item_empty_string("",0)); + item_list.push_back(new Item_string(message,strlen(message))); + if (result->send_data(item_list)) + result->send_error(0,NullS); + } + else + { + table_map used_tables=0; + for (uint i=0 ; i < join->tables ; i++) + { + JOIN_TAB *tab=join->join_tab+i; + TABLE *table=tab->table; + char buff[512],*buff_ptr=buff; + char buff1[512], buff2[512], bufff[512]; + String tmp1(buff1,sizeof(buff1)); + String tmp2(buff2,sizeof(buff2)); + item_list.empty(); + if (tab->type == JT_ALL && tab->select && tab->select->quick) + tab->type= JT_RANGE; + item_list.push_back(new Item_string(table->table_name,strlen(table->table_name))); + item_list.push_back(new Item_string(join_type_str[tab->type],strlen(join_type_str[tab->type]))); + tmp1.length(0); tmp2.length(0); + key_map bits; + uint j; + for (j=0,bits=tab->keys ; bits ; j++,bits>>=1) { - if (tmp.length()) - tmp.append(','); - tmp.append(table->key_info[j].name); + if (bits & 1) + { + if (tmp1.length()) + tmp1.append(','); + tmp1.append(table->key_info[j].name); + } } - } - if (tmp.length()) - net_store_data(packet,tmp.ptr(),tmp.length()); - else - net_store_null(packet); - if (tab->ref.key_parts) - { - net_store_data(packet,table->key_info[tab->ref.key].name); - net_store_data(packet,(uint32) tab->ref.key_length); - tmp.length(0); - for (store_key **ref=tab->ref.key_copy ; *ref ; ref++) + if (tmp1.length()) + item_list.push_back(new Item_string(tmp1.ptr(),tmp1.length())); + else + item_list.push_back(new Item_null()); + if (tab->ref.key_parts) { - if (tmp.length()) - tmp.append(','); - tmp.append((*ref)->name()); + item_list.push_back(new Item_string(table->key_info[tab->ref.key].name, + strlen(table->key_info[tab->ref.key].name))); + item_list.push_back(new Item_int((int32) tab->ref.key_length)); + for (store_key **ref=tab->ref.key_copy ; *ref ; ref++) + { + if (tmp2.length()) + tmp2.append(','); + tmp2.append((*ref)->name()); + } + item_list.push_back(new Item_string(tmp2.ptr(),tmp2.length())); } - net_store_data(packet,tmp.ptr(),tmp.length()); - } - else if (tab->type == JT_NEXT) - { - net_store_data(packet,table->key_info[tab->index].name); - net_store_data(packet,(uint32) table->key_info[tab->index].key_length); - net_store_null(packet); - } - else if (tab->select && tab->select->quick) - { - net_store_data(packet,table->key_info[tab->select->quick->index].name);; - net_store_data(packet,(uint32) tab->select->quick->max_used_key_length); - net_store_null(packet); - } - else - { - net_store_null(packet); - net_store_null(packet); - net_store_null(packet); - } - sprintf(buff,"%.0f",join->best_positions[i].records_read); - net_store_data(packet,buff); - my_bool key_read=table->key_read; - if (tab->type == JT_NEXT && - ((table->used_keys & ((key_map) 1 << tab->index)))) - key_read=1; - - buff_ptr=buff; - if (tab->info) - net_store_data(packet,tab->info); - else if (tab->select) - { - if (tab->use_quick == 2) + else if (tab->type == JT_NEXT) { - sprintf(buff_ptr,"range checked for each record (index map: %u)", - tab->keys); - buff_ptr=strend(buff_ptr); + item_list.push_back(new Item_string(table->key_info[tab->index].name,strlen(table->key_info[tab->index].name))); + item_list.push_back(new Item_int((int32) table->key_info[tab->index].key_length)); + item_list.push_back(new Item_null()); + } + else if (tab->select && tab->select->quick) + { + item_list.push_back(new Item_string(table->key_info[tab->select->quick->index].name,strlen(table->key_info[tab->select->quick->index].name))); + item_list.push_back(new Item_int((int32) tab->select->quick->max_used_key_length)); + item_list.push_back(new Item_null()); } else - buff_ptr=strmov(buff_ptr,"where used"); - } - if (key_read) - { - if (buff != buff_ptr) { - buff_ptr[0]=';' ; buff_ptr[1]=' '; buff_ptr+=2; + item_list.push_back(new Item_null()); + item_list.push_back(new Item_null()); + item_list.push_back(new Item_null()); } - buff_ptr=strmov(buff_ptr,"Using index"); - } - if (table->reginfo.not_exists_optimize) - { - if (buff != buff_ptr) + sprintf(bufff,"%.0f",join->best_positions[i].records_read); + item_list.push_back(new Item_string(bufff,strlen(bufff))); + my_bool key_read=table->key_read; + if (tab->type == JT_NEXT && + ((table->used_keys & ((key_map) 1 << tab->index)))) + key_read=1; + + if (tab->info) + item_list.push_back(new Item_string(tab->info,strlen(tab->info))); + else if (tab->select) { - buff_ptr[0]=';' ; buff_ptr[1]=' '; buff_ptr+=2; + if (tab->use_quick == 2) + { + sprintf(buff_ptr,"range checked for each record (index map: %u)", + tab->keys); + buff_ptr=strend(buff_ptr); + } + else + buff_ptr=strmov(buff_ptr,"where used"); } - buff_ptr=strmov(buff_ptr,"Not exists"); - } - if (need_tmp_table) - { - need_tmp_table=0; - if (buff != buff_ptr) + if (key_read) { - buff_ptr[0]=';' ; buff_ptr[1]=' '; buff_ptr+=2; + if (buff != buff_ptr) + { + buff_ptr[0]=';' ; buff_ptr[1]=' '; buff_ptr+=2; + } + buff_ptr=strmov(buff_ptr,"Using index"); } - buff_ptr=strmov(buff_ptr,"Using temporary"); - } - if (need_order) - { - need_order=0; - if (buff != buff_ptr) + if (table->reginfo.not_exists_optimize) { - buff_ptr[0]=';' ; buff_ptr[1]=' '; buff_ptr+=2; + if (buff != buff_ptr) + { + buff_ptr[0]=';' ; buff_ptr[1]=' '; buff_ptr+=2; + } + buff_ptr=strmov(buff_ptr,"Not exists"); } - buff_ptr=strmov(buff_ptr,"Using filesort"); - } - if (distinct & test_all_bits(used_tables,thd->used_tables)) - { - if (buff != buff_ptr) + if (need_tmp_table) { - buff_ptr[0]=';' ; buff_ptr[1]=' '; buff_ptr+=2; + need_tmp_table=0; + if (buff != buff_ptr) + { + buff_ptr[0]=';' ; buff_ptr[1]=' '; buff_ptr+=2; + } + buff_ptr=strmov(buff_ptr,"Using temporary"); } - buff_ptr=strmov(buff_ptr,"Distinct"); + if (need_order) + { + need_order=0; + if (buff != buff_ptr) + { + buff_ptr[0]=';' ; buff_ptr[1]=' '; buff_ptr+=2; + } + buff_ptr=strmov(buff_ptr,"Using filesort"); + } + if (distinct & test_all_bits(used_tables,thd->used_tables)) + { + if (buff != buff_ptr) + { + buff_ptr[0]=';' ; buff_ptr[1]=' '; buff_ptr+=2; + } + buff_ptr=strmov(buff_ptr,"Distinct"); + } + item_list.push_back(new Item_string(buff,(uint) (buff_ptr - buff))); + // For next iteration + used_tables|=table->map; + if (result->send_data(item_list)) + result->send_error(0,NullS); } - net_store_data(packet,buff,(uint) (buff_ptr - buff)); - if (my_net_write(&thd->net,(char*) packet->ptr(),packet->length())) - DBUG_VOID_RETURN; /* purecov: inspected */ - - // For next iteration - used_tables|=table->map; } - send_eof(&thd->net); + if (!join->thd->lex.select->next) + { + save_lock=thd->lock; + thd->lock=(MYSQL_LOCK *)0; + result->send_eof(); + thd->lock=save_lock; + } DBUG_VOID_RETURN; } @@ -6781,7 +7173,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..befa1efde53 100644 --- a/sql/sql_select.h +++ b/sql/sql_select.h @@ -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 */ @@ -118,19 +118,22 @@ typedef struct st_position { /* Used in find_best */ class TMP_TABLE_PARAM { public: List<Item> copy_funcs; - Copy_field *copy_field; + List_iterator_fast<Item> copy_funcs_it; + Copy_field *copy_field, *copy_field_end; byte *group_buff; Item_result_field **funcs; MI_COLUMNDEF *recinfo,*start_recinfo; KEY *keyinfo; ha_rows end_write_records; - uint copy_field_count,field_count,sum_func_count,func_count; + uint field_count,sum_func_count,func_count; uint hidden_field_count; - uint group_parts,group_length; + uint group_parts,group_length,group_null_parts; uint quick_group; bool using_indirect_summary_function; - TMP_TABLE_PARAM() :copy_field(0), group_parts(0), group_length(0) + TMP_TABLE_PARAM() + :copy_funcs_it(copy_funcs), copy_field(0), group_parts(0), + group_length(0), group_null_parts(0) {} ~TMP_TABLE_PARAM() { @@ -154,7 +157,8 @@ class JOIN { uint tables,const_tables; uint send_group_parts; bool sort_and_group,first_record,full_join,group, no_field_update; - table_map const_table_map,outer_join; + bool do_send_rows; + table_map const_table_map,found_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]; double best_read; @@ -183,11 +187,11 @@ 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); -bool setup_copy_fields(TMP_TABLE_PARAM *param,List<Item> &fields); +bool setup_copy_fields(THD *thd, TMP_TABLE_PARAM *param,List<Item> &fields); void copy_fields(TMP_TABLE_PARAM *param); void copy_funcs(Item_result_field **func_ptr); bool create_myisam_from_heap(TABLE *table, TMP_TABLE_PARAM *param, int error, @@ -207,7 +211,7 @@ class store_key :public Sql_alloc char *null_ptr; char err; public: - store_key(Field *field_arg, char *ptr, char *null, uint length) + store_key(THD *thd, Field *field_arg, char *ptr, char *null, uint length) :null_ptr(null),err(0) { if (field_arg->type() == FIELD_TYPE_BLOB) @@ -216,7 +220,7 @@ class store_key :public Sql_alloc field_arg->table, field_arg->binary()); else { - to_field=field_arg->new_field(field_arg->table); + to_field=field_arg->new_field(&thd->mem_root,field_arg->table); if (to_field) to_field->move_field(ptr, (uchar*) null, 1); } @@ -232,9 +236,9 @@ class store_key_field: public store_key Copy_field copy_field; const char *field_name; public: - store_key_field(Field *to_field_arg, char *ptr, char *null_ptr_arg, + store_key_field(THD *thd, Field *to_field_arg, char *ptr, char *null_ptr_arg, uint length, Field *from_field, const char *name_arg) - :store_key(to_field_arg,ptr, + :store_key(thd, to_field_arg,ptr, null_ptr_arg ? null_ptr_arg : from_field->maybe_null() ? &err : NullS,length), field_name(name_arg) { @@ -257,9 +261,9 @@ class store_key_item :public store_key protected: Item *item; public: - store_key_item(Field *to_field_arg, char *ptr, char *null_ptr_arg, + store_key_item(THD *thd, Field *to_field_arg, char *ptr, char *null_ptr_arg, uint length, Item *item_arg) - :store_key(to_field_arg,ptr, + :store_key(thd, to_field_arg,ptr, null_ptr_arg ? null_ptr_arg : item_arg->maybe_null ? &err : NullS, length), item(item_arg) {} @@ -276,10 +280,10 @@ class store_key_const_item :public store_key_item { bool inited; public: - store_key_const_item(Field *to_field_arg, char *ptr, + store_key_const_item(THD *thd, Field *to_field_arg, char *ptr, char *null_ptr_arg, uint length, Item *item_arg) - :store_key_item(to_field_arg,ptr, + :store_key_item(thd, to_field_arg,ptr, null_ptr_arg ? null_ptr_arg : item_arg->maybe_null ? &err : NullS, length, item_arg), inited(0) { diff --git a/sql/sql_show.cc b/sql/sql_show.cc index 8cfac1675b0..9fb1e88aeea 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -20,6 +20,7 @@ #include "mysql_priv.h" #include "sql_select.h" // For select_describe #include "sql_acl.h" +#include "repl_failsafe.h" #include <my_dir.h> #ifdef HAVE_BERKELEY_DB @@ -45,6 +46,8 @@ store_create_info(THD *thd, TABLE *table, String *packet); static void append_identifier(THD *thd, String *packet, const char *name); +extern struct st_VioSSLAcceptorFd * ssl_acceptor_fd; + /**************************************************************************** ** Send list of databases ** A database is a directory in the mysql_data_home directory @@ -72,7 +75,7 @@ mysqld_show_dbs(THD *thd,const char *wild) DBUG_RETURN(1); if (mysql_find_files(thd,&files,NullS,mysql_data_home,wild,1)) DBUG_RETURN(1); - List_iterator<char> it(files); + List_iterator_fast<char> it(files); while ((file_name=it++)) { if (!opt_safe_show_db || thd->master_access || @@ -81,7 +84,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,34 +98,31 @@ 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)) && thd->fatal_error) 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); } @@ -157,11 +157,11 @@ int mysqld_show_tables(THD *thd,const char *db,const char *wild) DBUG_RETURN(1); if (mysql_find_files(thd,&files,db,path,wild,0)) DBUG_RETURN(-1); - List_iterator<char> it(files); + List_iterator_fast<char> it(files); 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); } @@ -257,6 +257,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); @@ -296,20 +297,20 @@ int mysqld_extend_show_tables(THD *thd,const char *db,const char *wild) if (mysql_find_files(thd,&files,db,path,wild,0)) DBUG_RETURN(-1); - List_iterator<char> it(files); + List_iterator_fast<char> it(files); while ((file_name=it++)) { 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 @@ -317,8 +318,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) @@ -399,7 +400,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)); } { @@ -431,6 +432,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)); @@ -485,18 +487,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) @@ -505,17 +507,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) { @@ -530,7 +532,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); @@ -545,6 +547,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)); @@ -566,7 +569,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 @@ -632,6 +635,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)); @@ -657,6 +661,8 @@ mysqld_show_keys(THD *thd, TABLE_LIST *table_list) item->maybe_null=1; field_list.push_back(item=new Item_empty_string("Packed",10)); item->maybe_null=1; + field_list.push_back(new Item_empty_string("Null",3)); + field_list.push_back(new Item_empty_string("Index_type",16)); field_list.push_back(new Item_empty_string("Comment",255)); item->maybe_null=1; @@ -673,16 +679,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); + if (table->file->index_flags(i) & HA_READ_ORDER) + 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; @@ -690,21 +698,30 @@ 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); + + /* Check if we have a key part that only uses part of the field */ if (!key_part->field || key_part->length != 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":""); + + /* Null flag */ + uint flags= key_part->field ? key_part->field->flags : 0; + char *pos=(char*) ((flags & NOT_NULL_FLAG) ? "" : "YES"); + net_store_data(packet,convert,(const char*) pos); + net_store_data(packet,convert,table->file->index_type(i)); + /* Comment */ + net_store_data(packet,convert,""); if (my_net_write(&thd->net,(char*) packet->ptr(),packet->length())) DBUG_RETURN(1); /* purecov: inspected */ } @@ -749,27 +766,29 @@ 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())) + if (my_net_write(&thd->net, (char*)packet->ptr(), packet->length())) DBUG_RETURN(-1); VOID(net_flush(&thd->net)); } else { - if(my_write(fd, (const byte*) packet->ptr(), packet->length(), - MYF(MY_WME))) + if (my_write(fd, (const byte*) packet->ptr(), packet->length(), + MYF(MY_WME))) DBUG_RETURN(-1); } - DBUG_RETURN(0); } @@ -998,6 +1017,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)); @@ -1027,10 +1047,13 @@ void mysqld_list_processes(THD *thd,const char *user, bool verbose) thread_info *thd_info=new thread_info; thd_info->thread_id=tmp->thread_id; - thd_info->user=thd->strdup(tmp->user ? tmp->user : (tmp->system_thread ? - "system user" : "unauthenticated user")); - thd_info->host=thd->strdup(tmp->host ? tmp->host : (tmp->ip ? tmp->ip : - (tmp->system_thread ? "none" : "connecting host"))); + thd_info->user=thd->strdup(tmp->user ? tmp->user : + (tmp->system_thread ? + "system user" : "unauthenticated user")); + thd_info->host=thd->strdup(tmp->host ? tmp->host : + (tmp->ip ? tmp->ip : + (tmp->system_thread ? "none" : + "connecting host"))); if ((thd_info->db=tmp->db)) // Safe test thd_info->db=thd->strdup(thd_info->db); thd_info->command=(int) tmp->command; @@ -1062,9 +1085,8 @@ void mysqld_list_processes(THD *thd,const char *user, bool verbose) thd_info->query=0; if (tmp->query) { - uint length=(uint) strlen(tmp->query); - if (length > max_query_length) - length=max_query_length; + /* query_length is always set before tmp->query */ + uint length= min(max_query_length, tmp->query_length); thd_info->query=(char*) thd->memdup(tmp->query,length+1); thd_info->query[length]=0; } @@ -1081,28 +1103,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())) @@ -1124,6 +1146,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)); @@ -1137,7 +1160,7 @@ int mysqld_show(THD *thd, const char *wild, show_var_st *variables) if (!(wild && wild[0] && wild_case_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: @@ -1164,7 +1187,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)); @@ -1172,15 +1195,183 @@ int mysqld_show(THD *thd, const char *wild, show_var_st *variables) case SHOW_QUESTION: net_store_data(&packet2,(uint32) thd->query_id); break; + case SHOW_RPL_STATUS: + net_store_data(&packet2, rpl_status_type[(int)rpl_status]); + break; + case SHOW_SLAVE_RUNNING: + { + LOCK_ACTIVE_MI; + net_store_data(&packet2, (active_mi->slave_running && + active_mi->rli.slave_running) + ? "ON" : "OFF"); + UNLOCK_ACTIVE_MI; + break; + } case SHOW_OPENTABLES: net_store_data(&packet2,(uint32) cached_tables()); break; case SHOW_CHAR_PTR: { char *value= *(char**) variables[i].value; - net_store_data(&packet2,value ? value : ""); + net_store_data(&packet2,convert, value ? value : ""); break; } +#ifdef HAVE_OPENSSL + /* First group - functions relying on CTX */ + case SHOW_SSL_CTX_SESS_ACCEPT: + net_store_data(&packet2,(uint32) + (!ssl_acceptor_fd ? 0 : + SSL_CTX_sess_accept(ssl_acceptor_fd->ssl_context_))); + break; + case SHOW_SSL_CTX_SESS_ACCEPT_GOOD: + net_store_data(&packet2,(uint32) + (!ssl_acceptor_fd ? 0 : + SSL_CTX_sess_accept_good(ssl_acceptor_fd->ssl_context_))); + break; + case SHOW_SSL_CTX_SESS_CONNECT_GOOD: + net_store_data(&packet2,(uint32) + (!ssl_acceptor_fd ? 0 : + SSL_CTX_sess_connect_good(ssl_acceptor_fd->ssl_context_))); + break; + case SHOW_SSL_CTX_SESS_ACCEPT_RENEGOTIATE: + net_store_data(&packet2,(uint32) + (!ssl_acceptor_fd ? 0 : + SSL_CTX_sess_accept_renegotiate(ssl_acceptor_fd->ssl_context_))); + break; + case SHOW_SSL_CTX_SESS_CONNECT_RENEGOTIATE: + net_store_data(&packet2,(uint32) + (!ssl_acceptor_fd ? 0 : + SSL_CTX_sess_connect_renegotiate(ssl_acceptor_fd->ssl_context_))); + break; + case SHOW_SSL_CTX_SESS_CB_HITS: + net_store_data(&packet2,(uint32) + (!ssl_acceptor_fd ? 0 : + SSL_CTX_sess_cb_hits(ssl_acceptor_fd->ssl_context_))); + break; + case SHOW_SSL_CTX_SESS_HITS: + net_store_data(&packet2,(uint32) + (!ssl_acceptor_fd ? 0 : + SSL_CTX_sess_hits(ssl_acceptor_fd->ssl_context_))); + break; + case SHOW_SSL_CTX_SESS_CACHE_FULL: + net_store_data(&packet2,(uint32) + (!ssl_acceptor_fd ? 0 : + SSL_CTX_sess_cache_full(ssl_acceptor_fd->ssl_context_))); + break; + case SHOW_SSL_CTX_SESS_MISSES: + net_store_data(&packet2,(uint32) + (!ssl_acceptor_fd ? 0 : + SSL_CTX_sess_misses(ssl_acceptor_fd->ssl_context_))); + break; + case SHOW_SSL_CTX_SESS_TIMEOUTS: + net_store_data(&packet2,(uint32) + (!ssl_acceptor_fd ? 0 : + SSL_CTX_sess_timeouts(ssl_acceptor_fd->ssl_context_))); + break; + case SHOW_SSL_CTX_SESS_NUMBER: + net_store_data(&packet2,(uint32) + (!ssl_acceptor_fd ? 0 : + SSL_CTX_sess_number(ssl_acceptor_fd->ssl_context_))); + break; + case SHOW_SSL_CTX_SESS_CONNECT: + net_store_data(&packet2,(uint32) + (!ssl_acceptor_fd ? 0 : + SSL_CTX_sess_connect(ssl_acceptor_fd->ssl_context_))); + break; + case SHOW_SSL_CTX_SESS_GET_CACHE_SIZE: + net_store_data(&packet2,(uint32) + (!ssl_acceptor_fd ? 0 : + SSL_CTX_sess_get_cache_size(ssl_acceptor_fd->ssl_context_))); + break; + case SHOW_SSL_CTX_GET_VERIFY_MODE: + net_store_data(&packet2,(uint32) + (!ssl_acceptor_fd ? 0 : + SSL_CTX_get_verify_mode(ssl_acceptor_fd->ssl_context_))); + break; + case SHOW_SSL_CTX_GET_VERIFY_DEPTH: + net_store_data(&packet2,(uint32) + (!ssl_acceptor_fd ? 0 : + SSL_CTX_get_verify_depth(ssl_acceptor_fd->ssl_context_))); + break; + case SHOW_SSL_CTX_GET_SESSION_CACHE_MODE: + if (!ssl_acceptor_fd) + { + net_store_data(&packet2,"NONE" ); + break; + } + switch(SSL_CTX_get_session_cache_mode(ssl_acceptor_fd->ssl_context_)) + { + case SSL_SESS_CACHE_OFF: + net_store_data(&packet2,"OFF" ); + break; + case SSL_SESS_CACHE_CLIENT: + net_store_data(&packet2,"CLIENT" ); + break; + case SSL_SESS_CACHE_SERVER: + net_store_data(&packet2,"SERVER" ); + break; + case SSL_SESS_CACHE_BOTH: + net_store_data(&packet2,"BOTH" ); + break; + case SSL_SESS_CACHE_NO_AUTO_CLEAR: + net_store_data(&packet2,"NO_AUTO_CLEAR" ); + break; + case SSL_SESS_CACHE_NO_INTERNAL_LOOKUP: + net_store_data(&packet2,"NO_INTERNAL_LOOKUP" ); + break; + default: + net_store_data(&packet2,"Unknown"); + break; + } + break; + /* First group - functions relying on SSL */ + case SHOW_SSL_GET_VERSION: + net_store_data(&packet2, thd->net.vio->ssl_ ? + SSL_get_version(thd->net.vio->ssl_) : ""); + break; + case SHOW_SSL_SESSION_REUSED: + net_store_data(&packet2,(uint32) (thd->net.vio->ssl_ ? + SSL_session_reused(thd->net.vio->ssl_) : 0)); + break; + case SHOW_SSL_GET_DEFAULT_TIMEOUT: + net_store_data(&packet2,(uint32) (thd->net.vio->ssl_ ? + SSL_get_default_timeout(thd->net.vio->ssl_):0)); + break; + case SHOW_SSL_GET_VERIFY_MODE: + net_store_data(&packet2,(uint32) (thd->net.vio->ssl_ ? + SSL_get_verify_mode(thd->net.vio->ssl_):0)); + break; + case SHOW_SSL_GET_VERIFY_DEPTH: + net_store_data(&packet2,(uint32) (thd->net.vio->ssl_ ? + SSL_get_verify_depth(thd->net.vio->ssl_):0)); + break; + case SHOW_SSL_GET_CIPHER: + net_store_data(&packet2, thd->net.vio->ssl_ ? + SSL_get_cipher(thd->net.vio->ssl_) : ""); + break; + case SHOW_SSL_GET_CIPHER_LIST: + if (thd->net.vio->ssl_) + { + char buf[1024], *pos; + pos=buf; + for (int i=0 ; i++ ;) + { + const char *p=SSL_get_cipher_list(thd->net.vio->ssl_,i); + if (p == NULL) + break; + pos=strmov(pos, p); + *pos++= ':'; + } + if (pos != buf) + pos--; // Remove last ':' + *pos=0; + net_store_data(&packet2, buf); + } + else + net_store_data(&packet2, ""); + break; + +#endif /* HAVE_OPENSSL */ } if (my_net_write(&thd->net, (char*) packet2.ptr(),packet2.length())) goto err; /* purecov: inspected */ @@ -1198,6 +1389,6 @@ int mysqld_show(THD *thd, const char *wild, show_var_st *variables) } #ifdef __GNUC__ -template class List_iterator<char>; +template class List_iterator_fast<char>; template class List<char>; #endif diff --git a/sql/sql_sort.h b/sql/sql_sort.h new file mode 100644 index 00000000000..62c5f1cb164 --- /dev/null +++ b/sql/sql_sort.h @@ -0,0 +1,55 @@ +/* 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; + bool not_killable; +#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_string.cc b/sql/sql_string.cc index e6cdd089bf1..b0ac378c861 100644 --- a/sql/sql_string.cc +++ b/sql/sql_string.cc @@ -1,19 +1,18 @@ -/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB - - This program file 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, +/* 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 - 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 */ + 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 */ /* This file is originally from the mysql distribution. Coded by monty */ @@ -21,7 +20,7 @@ #pragma implementation // gcc: Class implementation #endif -#include <global.h> +#include <my_global.h> #include <my_sys.h> #include <m_string.h> #include <m_ctype.h> @@ -41,19 +40,16 @@ extern void sql_element_free(void *ptr); bool String::real_alloc(uint32 arg_length) { arg_length=ALIGN_SIZE(arg_length+1); + str_length=0; if (Alloced_length < arg_length) { free(); if (!(Ptr=(char*) my_malloc(arg_length,MYF(MY_WME)))) - { - str_length=0; return TRUE; - } Alloced_length=arg_length; alloced=1; } Ptr[0]=0; - str_length=0; return FALSE; } @@ -362,6 +358,37 @@ skipp: return -1; } +/* + Search after a string without regarding to case + This needs to be replaced when we have character sets per string +*/ + +int String::strstr_case(const String &s,uint32 offset) +{ + if (s.length()+offset <= str_length) + { + if (!s.length()) + return ((int) offset); // Empty string is always found + + register const char *str = Ptr+offset; + register const char *search=s.ptr(); + const char *end=Ptr+str_length-s.length()+1; + const char *search_end=s.ptr()+s.length(); +skipp: + while (str != end) + { + if (my_sort_order[*str++] == my_sort_order[*search]) + { + register char *i,*j; + i=(char*) str; j=(char*) search+1; + while (j != search_end) + if (my_sort_order[*i++] != my_sort_order[*j++]) goto skipp; + return (int) (str-Ptr) -1; + } + } + } + return -1; +} /* ** Search string from end. Offset is offset to the end of string @@ -577,7 +604,7 @@ int wild_case_compare(const char *str,const char *str_end, { do { - if (str == str_end) // Skipp one char if possible + if (str == str_end) // Skip one char if possible return (result); INC_PTR(str,str_end); } while (++wildstr < wildend && *wildstr == wild_one); @@ -668,8 +695,11 @@ int wild_case_compare(const char *str,const char *str_end, int wild_case_compare(String &match,String &wild, char escape) { - return wild_case_compare(match.ptr(),match.ptr()+match.length(), - wild.ptr(), wild.ptr()+wild.length(),escape); + DBUG_ENTER("wild_case_compare"); + DBUG_PRINT("enter",("match='%s', wild='%s', escape='%c'" + ,match.ptr(),wild.ptr(),escape)); + DBUG_RETURN(wild_case_compare(match.ptr(),match.ptr()+match.length(), + wild.ptr(), wild.ptr()+wild.length(),escape)); } /* @@ -679,6 +709,9 @@ int wild_case_compare(String &match,String &wild, char escape) int wild_compare(const char *str,const char *str_end, const char *wildstr,const char *wildend,char escape) { + DBUG_ENTER("wild_compare"); + DBUG_PRINT("enter",("str='%s', str_end='%s', wildstr='%s', wildend='%s', escape='%c'" + ,str,str_end,wildstr,wildend,escape)); int result= -1; // Not found, using wildcards while (wildstr != wildend) { @@ -687,17 +720,21 @@ int wild_compare(const char *str,const char *str_end, if (*wildstr == escape && wildstr+1 != wildend) wildstr++; if (str == str_end || *wildstr++ != *str++) - return(1); + { + DBUG_RETURN(1); + } if (wildstr == wildend) - return (str != str_end); // Match if both are at end + { + DBUG_RETURN(str != str_end); // Match if both are at end + } result=1; // Found an anchor char } if (*wildstr == wild_one) { do { - if (str == str_end) // Skipp one char if possible - return (result); + if (str == str_end) // Skip one char if possible + DBUG_RETURN(result); str++; } while (*++wildstr == wild_one && wildstr != wildend); if (wildstr == wildend) @@ -714,17 +751,22 @@ int wild_compare(const char *str,const char *str_end, if (*wildstr == wild_one) { if (str == str_end) - return (-1); + { + DBUG_RETURN(-1); + } str++; continue; } break; // Not a wild character } if (wildstr == wildend) - return(0); // Ok if wild_many is last + { + DBUG_RETURN(0); // Ok if wild_many is last + } if (str == str_end) - return -1; - + { + DBUG_RETURN(-1); + } char cmp; if ((cmp= *wildstr) == escape && wildstr+1 != wildend) cmp= *++wildstr; @@ -733,22 +775,30 @@ int wild_compare(const char *str,const char *str_end, { while (str != str_end && *str != cmp) str++; - if (str++ == str_end) return (-1); + if (str++ == str_end) + { + DBUG_RETURN(-1); + } { int tmp=wild_compare(str,str_end,wildstr,wildend,escape); if (tmp <= 0) - return (tmp); + { + DBUG_RETURN(tmp); + } } } while (str != str_end && wildstr[0] != wild_many); - return(-1); + DBUG_RETURN(-1); } } - return (str != str_end ? 1 : 0); + DBUG_RETURN(str != str_end ? 1 : 0); } int wild_compare(String &match,String &wild, char escape) { - return wild_compare(match.ptr(),match.ptr()+match.length(), - wild.ptr(), wild.ptr()+wild.length(),escape); + DBUG_ENTER("wild_compare"); + DBUG_PRINT("enter",("match='%s', wild='%s', escape='%c'" + ,match.ptr(),wild.ptr(),escape)); + DBUG_RETURN(wild_compare(match.ptr(),match.ptr()+match.length(), + wild.ptr(), wild.ptr()+wild.length(),escape)); } diff --git a/sql/sql_string.h b/sql/sql_string.h index 31dea9991cc..ad7455ecbf1 100644 --- a/sql/sql_string.h +++ b/sql/sql_string.h @@ -1,19 +1,18 @@ -/* 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, +/* 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 - 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 */ + 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 */ /* This file is originally from the mysql distribution. Coded by monty */ @@ -161,6 +160,7 @@ public: bool append(const char *s,uint32 arg_length=0); bool append(IO_CACHE* file, uint32 arg_length); int strstr(const String &search,uint32 offset=0); // Returns offset to substring or -1 + int strstr_case(const String &s,uint32 offset=0); int strrstr(const String &search,uint32 offset=0); // Returns offset to substring or -1 bool replace(uint32 offset,uint32 arg_length,const String &to); inline bool append(char chr) diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 7b5fc5797c9..4316acfa47d 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -26,6 +26,7 @@ #endif extern HASH open_cache; +static const char *primary_key_name="PRIMARY"; static bool check_if_keyname_exists(const char *name,KEY *start, KEY *end); static char *make_unique_key_name(const char *field_name,KEY *start,KEY *end); @@ -43,12 +44,7 @@ static int copy_data_between_tables(TABLE *from,TABLE *to, int mysql_rm_table(THD *thd,TABLE_LIST *tables, my_bool if_exists) { - char path[FN_REFLEN]; - String wrong_tables; - bool some_tables_deleted=0; - uint error; - db_type table_type; - TABLE_LIST *table; + int error; DBUG_ENTER("mysql_rm_table"); /* mark for close and remove all cached entries */ @@ -72,7 +68,35 @@ int mysql_rm_table(THD *thd,TABLE_LIST *tables, my_bool if_exists) } } - + error=mysql_rm_table_part2(thd,tables,if_exists,0); + + err: + VOID(pthread_cond_broadcast(&COND_refresh)); // Signal to refresh + pthread_mutex_unlock(&LOCK_open); + + pthread_mutex_lock(&thd->mysys_var->mutex); + thd->mysys_var->current_mutex= 0; + thd->mysys_var->current_cond= 0; + pthread_mutex_unlock(&thd->mysys_var->mutex); + + if (error) + DBUG_RETURN(-1); + send_ok(&thd->net); + DBUG_RETURN(0); +} + + +int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists, + bool dont_log_query) +{ + TABLE_LIST *table; + char path[FN_REFLEN]; + String wrong_tables; + db_type table_type; + int error; + bool some_tables_deleted=0; + DBUG_ENTER("mysql_rm_table_part2"); + for (table=tables ; table ; table=table->next) { char *db=table->db ? table->db : thd->db; @@ -137,33 +161,25 @@ int mysql_rm_table(THD *thd,TABLE_LIST *tables, my_bool if_exists) } if (some_tables_deleted) { - mysql_update_log.write(thd, thd->query,thd->query_length); - if (mysql_bin_log.is_open()) + query_cache_invalidate3(thd, tables, 0); + if (!dont_log_query) { - Query_log_event qinfo(thd, thd->query); - mysql_bin_log.write(&qinfo); + 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); + } } } - - error = 0; - err: - VOID(pthread_cond_broadcast(&COND_refresh)); // Signal to refresh - pthread_mutex_unlock(&LOCK_open); - - pthread_mutex_lock(&thd->mysys_var->mutex); - thd->mysys_var->current_mutex= 0; - thd->mysys_var->current_cond= 0; - pthread_mutex_unlock(&thd->mysys_var->mutex); + error = 0; if (wrong_tables.length()) { my_error(ER_BAD_TABLE_ERROR,MYF(0),wrong_tables.c_ptr()); error=1; } - if(error) - DBUG_RETURN(-1); - send_ok(&thd->net); - DBUG_RETURN(0); + DBUG_RETURN(error); } @@ -181,6 +197,52 @@ int quick_rm_table(enum db_type base,const char *db, return ha_delete_table(base,path) || error; } +/* + Sort keys in the following order: + - PRIMARY KEY + - UNIQUE keyws where all column are NOT NULL + - Other UNIQUE keys + - Normal keys + - Fulltext keys + + This will make checking for duplicated keys faster and ensure that + PRIMARY keys are prioritized. +*/ + + +static int sort_keys(KEY *a, KEY *b) +{ + if (a->flags & HA_NOSAME) + { + if (!(b->flags & HA_NOSAME)) + return -1; + if ((a->flags ^ b->flags) & HA_NULL_PART_KEY) + { + /* Sort NOT NULL keys before other keys */ + return (a->flags & HA_NULL_PART_KEY) ? 1 : -1; + } + if (a->name == primary_key_name) + return -1; + if (b->name == primary_key_name) + return 1; + } + else if (b->flags & HA_NOSAME) + return 1; // Prefer b + + if ((a->flags ^ b->flags) & HA_FULLTEXT) + { + return (a->flags & HA_FULLTEXT) ? 1 : -1; + } + /* + Prefer original key order. usable_key_parts contains here + the original key position. + */ + return ((a->usable_key_parts < b->usable_key_parts) ? -1 : + (a->usable_key_parts > b->usable_key_parts) ? 1 : + 0); +} + + /***************************************************************************** * Create a table. * If one creates a temporary table, this is automaticly opened @@ -220,7 +282,7 @@ int mysql_create_table(THD *thd,const char *db, const char *table_name, file=get_new_handler((TABLE*) 0, create_info->db_type); if ((create_info->options & HA_LEX_CREATE_TMP_TABLE) && - (file->option_flag() & HA_NO_TEMP_TABLES)) + (file->table_flags() & HA_NO_TEMP_TABLES)) { my_error(ER_ILLEGAL_HA,MYF(0),table_name); DBUG_RETURN(-1); @@ -319,13 +381,13 @@ int mysql_create_table(THD *thd,const char *db, const char *table_name, DBUG_RETURN(-1); } if (auto_increment && - (file->option_flag() & HA_WRONG_ASCII_ORDER)) + (file->table_flags() & HA_NO_AUTO_INCREMENT)) { my_error(ER_TABLE_CANT_HANDLE_AUTO_INCREMENT,MYF(0)); DBUG_RETURN(-1); } - if (blob_columns && (file->option_flag() & HA_NO_BLOBS)) + if (blob_columns && (file->table_flags() & HA_NO_BLOBS)) { my_error(ER_TABLE_CANT_HANDLE_BLOB,MYF(0)); DBUG_RETURN(-1); @@ -336,10 +398,9 @@ int mysql_create_table(THD *thd,const char *db, const char *table_name, List_iterator<Key> key_iterator(keys); uint key_parts=0,key_count=keys.elements; List<Key> keys_in_order; // Add new keys here - Key *primary_key=0; - bool unique_key=0; + bool primary_key=0,unique_key=0; Key *key; - uint tmp; + uint tmp, key_number; tmp=min(file->max_keys(), MAX_KEY); if (key_count > tmp) { @@ -347,12 +408,7 @@ int mysql_create_table(THD *thd,const char *db, const char *table_name, DBUG_RETURN(-1); } - /* - Check keys; - Put PRIMARY KEY first, then UNIQUE keys and other keys last - This will make checking for duplicated keys faster and ensure that - primary keys are prioritized. - */ + /* Calculate number of key segements */ while ((key=key_iterator++)) { @@ -368,33 +424,6 @@ int mysql_create_table(THD *thd,const char *db, const char *table_name, DBUG_RETURN(-1); } key_parts+=key->columns.elements; - if (key->type == Key::PRIMARY) - { - if (primary_key) - { - my_error(ER_MULTIPLE_PRI_KEY,MYF(0)); - DBUG_RETURN(-1); - } - primary_key=key; - } - else if (key->type == Key::UNIQUE) - { - unique_key=1; - if (keys_in_order.push_front(key)) - DBUG_RETURN(-1); - } - else if (keys_in_order.push_back(key)) - DBUG_RETURN(-1); - } - if (primary_key) - { - if (keys_in_order.push_front(primary_key)) - DBUG_RETURN(-1); - } - else if (!unique_key && (file->option_flag() & HA_REQUIRE_PRIMARY_KEY)) - { - my_error(ER_REQUIRES_PRIMARY_KEY,MYF(0)); - DBUG_RETURN(-1); } key_info_buffer=key_info=(KEY*) sql_calloc(sizeof(KEY)*key_count); @@ -402,8 +431,9 @@ int mysql_create_table(THD *thd,const char *db, const char *table_name, if (!key_info_buffer || ! key_part_info) DBUG_RETURN(-1); // Out of memory - List_iterator<Key> key_iterator_in_order(keys_in_order); - for (; (key=key_iterator_in_order++) ; key_info++) + key_iterator.rewind(); + key_number=0; + for (; (key=key_iterator++) ; key_info++, key_number++) { uint key_length=0; key_part_spec *column; @@ -412,10 +442,13 @@ int mysql_create_table(THD *thd,const char *db, const char *table_name, (key->type == Key::FULLTEXT) ? HA_FULLTEXT : HA_NOSAME; key_info->key_parts=(uint8) key->columns.elements; key_info->key_part=key_part_info; + key_info->usable_key_parts= key_number; + key_info->algorithm=key->algorithm; + /* TODO: Add proper checks if handler supports key_type and algorithm */ if (key->type == Key::FULLTEXT) { - if (file->option_flag() & HA_NO_FULLTEXT_KEY) + if (!(file->table_flags() & HA_CAN_FULLTEXT)) { my_error(ER_TABLE_CANT_HANDLE_FULLTEXT, MYF(0)); DBUG_RETURN(-1); @@ -439,7 +472,7 @@ int mysql_create_table(THD *thd,const char *db, const char *table_name, } if (f_is_blob(sql_field->pack_flag)) { - if (!(file->option_flag() & HA_BLOB_KEY)) + if (!(file->table_flags() & HA_BLOB_KEY)) { my_printf_error(ER_BLOB_USED_AS_KEY,ER(ER_BLOB_USED_AS_KEY),MYF(0), column->field_name); @@ -465,16 +498,17 @@ int mysql_create_table(THD *thd,const char *db, const char *table_name, my_error(ER_PRIMARY_CANT_HAVE_NULL, MYF(0)); DBUG_RETURN(-1); } - if (!(file->option_flag() & HA_NULL_KEY)) + if (!(file->table_flags() & HA_NULL_KEY)) { my_printf_error(ER_NULL_COLUMN_IN_INDEX,ER(ER_NULL_COLUMN_IN_INDEX), MYF(0),column->field_name); DBUG_RETURN(-1); } + key_info->flags|= HA_NULL_PART_KEY; } if (MTYP_TYPENR(sql_field->unireg_check) == Field::NEXT_NUMBER) { - if (column_nr == 0 || (file->option_flag() & HA_AUTO_PART_KEY)) + if (column_nr == 0 || (file->table_flags() & HA_AUTO_PART_KEY)) auto_increment--; // Field is used } key_part_info->fieldnr= field; @@ -494,14 +528,14 @@ int mysql_create_table(THD *thd,const char *db, const char *table_name, } else if (column->length > length || ((f_is_packed(sql_field->pack_flag) || - ((file->option_flag() & HA_NO_PREFIX_CHAR_KEYS) && + ((file->table_flags() & HA_NO_PREFIX_CHAR_KEYS) && (key_info->flags & HA_NOSAME))) && column->length != length)) { my_error(ER_WRONG_SUB_KEY,MYF(0)); DBUG_RETURN(-1); } - if (!(file->option_flag() & HA_NO_PREFIX_CHAR_KEYS)) + if (!(file->table_flags() & HA_NO_PREFIX_CHAR_KEYS)) length=column->length; } else if (length == 0) @@ -530,7 +564,15 @@ int mysql_create_table(THD *thd,const char *db, const char *table_name, if (column_nr == 0) { if (key->type == Key::PRIMARY) - key_name="PRIMARY"; + { + if (primary_key) + { + my_error(ER_MULTIPLE_PRI_KEY,MYF(0)); + DBUG_RETURN(-1); + } + key_name=primary_key_name; + primary_key=1; + } else if (!(key_name = key->name())) key_name=make_unique_key_name(sql_field->field_name, key_info_buffer,key_info); @@ -542,18 +584,29 @@ int mysql_create_table(THD *thd,const char *db, const char *table_name, key_info->name=(char*) key_name; } } + if (!(key_info->flags & HA_NULL_PART_KEY)) + unique_key=1; key_info->key_length=(uint16) key_length; - if (key_length > file->max_key_length() && key->type != Key::FULLTEXT) + uint max_key_length= max(file->max_key_length(), MAX_KEY_LENGTH); + if (key_length > max_key_length && key->type != Key::FULLTEXT) { - my_error(ER_TOO_LONG_KEY,MYF(0),file->max_key_length()); + my_error(ER_TOO_LONG_KEY,MYF(0),max_key_length); DBUG_RETURN(-1); } } + if (!unique_key && !primary_key && + (file->table_flags() & HA_REQUIRE_PRIMARY_KEY)) + { + my_error(ER_REQUIRES_PRIMARY_KEY,MYF(0)); + DBUG_RETURN(-1); + } if (auto_increment > 0) { my_error(ER_WRONG_AUTO_KEY,MYF(0)); DBUG_RETURN(-1); } + /* Sort keys in optimized order */ + qsort((gptr) key_info_buffer, key_count, sizeof(KEY), (qsort_cmp) sort_keys); /* Check if table exists */ if (create_info->options & HA_LEX_CREATE_TMP_TABLE) @@ -670,7 +723,7 @@ TABLE *create_table_from_items(THD *thd, HA_CREATE_INFO *create_info, DBUG_ENTER("create_table_from_items"); /* Add selected items to field list */ - List_iterator<Item> it(*items); + List_iterator_fast<Item> it(*items); Item *item; Field *tmp_field; tmp_table.db_create_options=0; @@ -688,8 +741,11 @@ TABLE *create_table_from_items(THD *thd, HA_CREATE_INFO *create_info, my_error(ER_WRONG_COLUMN_NAME,MYF(0),item->name); DBUG_RETURN(0); } - - Field *field=create_tmp_field(&tmp_table,item,item->type(), + Field *field; + if (item->type() == Item::FUNC_ITEM) + field=item->tmp_table_field(&tmp_table); + else + field=create_tmp_field(thd, &tmp_table, item, item->type(), (Item_result_field***) 0, &tmp_field,0,0); if (!field || !(cr_field=new create_field(field,(item->type() == Item::FIELD_ITEM ? @@ -816,7 +872,8 @@ static int send_check_errmsg(THD* thd, TABLE_LIST* table, return 1; } -static int prepare_for_restore(THD* thd, TABLE_LIST* table) +static int prepare_for_restore(THD* thd, TABLE_LIST* table, + HA_CHECK_OPT *check_opt) { DBUG_ENTER("prepare_for_restore"); @@ -833,52 +890,86 @@ static int prepare_for_restore(THD* thd, TABLE_LIST* table) char* table_name = table->name; char* db = thd->db ? thd->db : table->db; - if (!fn_format(src_path, table_name, backup_dir, reg_ext, 4 + 64)) + if (fn_format_relative_to_data_home(src_path, table_name, backup_dir, + reg_ext)) DBUG_RETURN(-1); // protect buffer overflow sprintf(dst_path, "%s/%s/%s", mysql_real_data_home, db, table_name); - int lock_retcode; - pthread_mutex_lock(&LOCK_open); - if ((lock_retcode = lock_table_name(thd, table)) < 0) - { - pthread_mutex_unlock(&LOCK_open); - DBUG_RETURN(-1); - } - - if (lock_retcode && wait_for_locked_table_names(thd, table)) - { - unlock_table_name(thd, table); - pthread_mutex_unlock(&LOCK_open); + if (lock_and_wait_for_table_name(thd,table)) DBUG_RETURN(-1); - } - pthread_mutex_unlock(&LOCK_open); if (my_copy(src_path, - fn_format(dst_path, dst_path,"", - reg_ext, 4), - MYF(MY_WME))) + fn_format(dst_path, dst_path,"", reg_ext, 4), + MYF(MY_WME))) { unlock_table_name(thd, table); DBUG_RETURN(send_check_errmsg(thd, table, "restore", "Failed copying .frm file")); } - bool save_no_send_ok = thd->net.no_send_ok; - thd->net.no_send_ok = 1; - // generate table will try to send OK which messes up the output - // for the client - - if (generate_table(thd, table, 0)) + if (mysql_truncate(thd, table, 1)) { unlock_table_name(thd, table); - thd->net.no_send_ok = save_no_send_ok; DBUG_RETURN(send_check_errmsg(thd, table, "restore", "Failed generating table from .frm file")); } + } + + // now we should be able to open the partially restored table + // to finish the restore in the handler later on + if (!(table->table = reopen_name_locked_table(thd, table))) + unlock_table_name(thd, table); + DBUG_RETURN(0); +} + +static int prepare_for_repair(THD* thd, TABLE_LIST* table, + HA_CHECK_OPT *check_opt) +{ + DBUG_ENTER("prepare_for_repair"); + + if (!(check_opt->sql_flags & TT_USEFRM)) + { + DBUG_RETURN(0); + } + else + { + + char from[FN_REFLEN],tmp[FN_REFLEN]; + char* db = thd->db ? thd->db : table->db; - thd->net.no_send_ok = save_no_send_ok; + sprintf(from, "%s/%s/%s", mysql_real_data_home, db, table->name); + fn_format(from, from, "", MI_NAME_DEXT, 4); + sprintf(tmp,"%s-%lx_%lx", from, current_pid, thd->thread_id); + + close_cached_table(thd,table->table); + + if (lock_and_wait_for_table_name(thd,table)) + DBUG_RETURN(-1); + + if (my_rename(from, tmp, MYF(MY_WME))) + { + unlock_table_name(thd, table); + DBUG_RETURN(send_check_errmsg(thd, table, "repair", + "Failed renaming .MYD file")); + } + if (mysql_truncate(thd, table, 1)) + { + unlock_table_name(thd, table); + DBUG_RETURN(send_check_errmsg(thd, table, "repair", + "Failed generating table from .frm file")); + } + if (my_rename(tmp, from, MYF(MY_WME))) + { + unlock_table_name(thd, table); + DBUG_RETURN(send_check_errmsg(thd, table, "repair", + "Failed restoring .MYD file")); + } } + // now we should be able to open the partially repaired table + // to finish the repair in the handler later on + if (!(table->table = reopen_name_locked_table(thd, table))) + unlock_table_name(thd, table); DBUG_RETURN(0); } @@ -886,8 +977,9 @@ static int mysql_admin_table(THD* thd, TABLE_LIST* tables, HA_CHECK_OPT* check_opt, const char *operator_name, thr_lock_type lock_type, - bool open_for_modify, bool restore, + bool open_for_modify, uint extra_open_options, + int (*prepare_func)(THD *, TABLE_LIST *, HA_CHECK_OPT *), int (handler::*operator_func) (THD *, HA_CHECK_OPT *)) { @@ -919,18 +1011,13 @@ static int mysql_admin_table(THD* thd, TABLE_LIST* tables, table->table = open_ltable(thd, table, lock_type); thd->open_options&= ~extra_open_options; packet->length(0); - if (restore) + if (prepare_func) { - switch (prepare_for_restore(thd, table)) { - case 1: continue; // error, message written to net - case -1: goto err; // error, message could be written to net - default: ;// should be 0 otherwise + switch ((*prepare_func)(thd, table, check_opt)) { + case 1: continue; // error, message written to net + case -1: goto err; // error, message could be written to net + default: ; // should be 0 otherwise } - - // now we should be able to open the partially restored table - // to finish the restore in the handler later on - if (!(table->table = reopen_name_locked_table(thd, table))) - unlock_table_name(thd, table); } if (!table->table) @@ -957,6 +1044,7 @@ static int mysql_admin_table(THD* thd, TABLE_LIST* tables, sprintf(buff, ER(ER_OPEN_AS_READONLY), table_name); net_store_data(packet, buff); close_thread_tables(thd); + table->table=0; // For query cache if (my_net_write(&thd->net, (char*) thd->packet.ptr(), packet->length())) goto err; @@ -1034,6 +1122,7 @@ static int mysql_admin_table(THD* thd, TABLE_LIST* tables, remove_table_from_cache(thd, table->table->table_cache_key, table->table->real_name); close_thread_tables(thd); + table->table=0; // For query cache if (my_net_write(&thd->net, (char*) packet->ptr(), packet->length())) goto err; @@ -1043,9 +1132,12 @@ static int mysql_admin_table(THD* thd, TABLE_LIST* tables, DBUG_RETURN(0); err: close_thread_tables(thd); // Shouldn't be needed + if (table) + table->table=0; DBUG_RETURN(-1); } + int mysql_backup_table(THD* thd, TABLE_LIST* table_list) { DBUG_ENTER("mysql_backup_table"); @@ -1058,7 +1150,8 @@ int mysql_restore_table(THD* thd, TABLE_LIST* table_list) { DBUG_ENTER("mysql_restore_table"); DBUG_RETURN(mysql_admin_table(thd, table_list, 0, - "restore", TL_WRITE, 1, 1,0, + "restore", TL_WRITE, 1, 0, + &prepare_for_restore, &handler::restore)); } @@ -1066,7 +1159,8 @@ int mysql_repair_table(THD* thd, TABLE_LIST* tables, HA_CHECK_OPT* check_opt) { DBUG_ENTER("mysql_repair_table"); DBUG_RETURN(mysql_admin_table(thd, tables, check_opt, - "repair", TL_WRITE, 1, 0, HA_OPEN_FOR_REPAIR, + "repair", TL_WRITE, 1, HA_OPEN_FOR_REPAIR, + &prepare_for_repair, &handler::repair)); } @@ -1105,7 +1199,7 @@ int mysql_check_table(THD* thd, TABLE_LIST* tables,HA_CHECK_OPT* check_opt) DBUG_ENTER("mysql_check_table"); DBUG_RETURN(mysql_admin_table(thd, tables, check_opt, "check", lock_type, - 0, 0, HA_OPEN_FOR_REPAIR, + 0, HA_OPEN_FOR_REPAIR, 0, &handler::check)); } @@ -1118,16 +1212,19 @@ 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; - uint save_time_stamp,db_create_options; + uint save_time_stamp,db_create_options, used_fields; enum db_type old_db_type,new_db_type; DBUG_ENTER("mysql_alter_table"); @@ -1136,6 +1233,7 @@ int mysql_alter_table(THD *thd,char *new_db, char *new_name, db=table_list->db; if (!new_db) new_db=db; + used_fields=create_info->used_fields; if (!(table=open_ltable(thd,table_list,TL_WRITE_ALLOW_READ))) DBUG_RETURN(-1); @@ -1181,42 +1279,49 @@ int mysql_alter_table(THD *thd,char *new_db, char *new_name, if (create_info->db_type == DB_TYPE_DEFAULT) create_info->db_type=old_db_type; new_db_type=create_info->db_type= ha_checktype(create_info->db_type); - if (create_info->row_type == ROW_TYPE_DEFAULT) + if (create_info->row_type == ROW_TYPE_NOT_USED) 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)) + if (new_name != table_name) { - 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; + 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; + } + 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(HA_POS_ERROR); + 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); @@ -1227,7 +1332,6 @@ int mysql_alter_table(THD *thd,char *new_db, char *new_name, } send_ok(&thd->net); } - DBUG_RETURN(error); } @@ -1257,7 +1361,7 @@ int mysql_alter_table(THD *thd,char *new_db, char *new_name, { /* Reset auto_increment value if it was dropped */ if (MTYP_TYPENR(field->unireg_check) == Field::NEXT_NUMBER && - !(create_info->used_fields & HA_CREATE_USED_AUTO)) + !(used_fields & HA_CREATE_USED_AUTO)) { create_info->auto_increment_value=0; create_info->used_fields|=HA_CREATE_USED_AUTO; @@ -1282,8 +1386,11 @@ int mysql_alter_table(THD *thd,char *new_db, char *new_name, def->field=field; if (def->sql_type == FIELD_TYPE_TIMESTAMP) use_timestamp=1; - create_list.push_back(def); - def_it.remove(); + if (!def->after) + { + create_list.push_back(def); + def_it.remove(); + } } else { // Use old field value @@ -1314,7 +1421,7 @@ int mysql_alter_table(THD *thd,char *new_db, char *new_name, List_iterator<create_field> find_it(create_list); while ((def=def_it++)) // Add new columns { - if (def->change) + if (def->change && ! def->field) { my_error(ER_BAD_FIELD_ERROR,MYF(0),def->change,table_name); DBUG_RETURN(-1); @@ -1443,20 +1550,25 @@ int mysql_alter_table(THD *thd,char *new_db, char *new_name, goto err; } + db_create_options=table->db_create_options & ~(HA_OPTION_PACK_RECORD); (void) sprintf(tmp_name,"%s-%lx_%lx", tmp_file_prefix, current_pid, thd->thread_id); create_info->db_type=new_db_type; - if (!create_info->max_rows) - create_info->max_rows=table->max_rows; - if (!create_info->avg_row_length) - create_info->avg_row_length=table->avg_row_length; - table->file->update_create_info(create_info); if (!create_info->comment) create_info->comment=table->comment; + /* let new create options override the old ones */ - db_create_options=table->db_create_options & ~(HA_OPTION_PACK_RECORD); - if (create_info->table_options & - (HA_OPTION_PACK_KEYS | HA_OPTION_NO_PACK_KEYS)) + if (!(used_fields & HA_CREATE_USED_MIN_ROWS)) + create_info->min_rows=table->min_rows; + if (!(used_fields & HA_CREATE_USED_MAX_ROWS)) + create_info->max_rows=table->max_rows; + if (!(used_fields & HA_CREATE_USED_AVG_ROW_LENGTH)) + create_info->avg_row_length=table->avg_row_length; + + table->file->update_create_info(create_info); + if ((create_info->table_options & + (HA_OPTION_PACK_KEYS | HA_OPTION_NO_PACK_KEYS)) || + (used_fields & HA_CREATE_USED_PACK_KEYS)) db_create_options&= ~(HA_OPTION_PACK_KEYS | HA_OPTION_NO_PACK_KEYS); if (create_info->table_options & (HA_OPTION_CHECKSUM | HA_OPTION_NO_CHECKSUM)) @@ -1470,6 +1582,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 @@ -1608,24 +1767,30 @@ int mysql_alter_table(THD *thd,char *new_db, char *new_name, } if (error) { - // This shouldn't happen. We solve this the safe way by - // closing the locked table. + /* + This shouldn't happen. We solve this the safe way by + closing the locked table. + */ close_cached_table(thd,table); VOID(pthread_mutex_unlock(&LOCK_open)); goto err; } if (thd->lock || new_name != table_name) // True if WIN32 { - // Not table locking or alter table with rename - // free locks and remove old table + /* + Not table locking or alter table with rename + free locks and remove old table + */ close_cached_table(thd,table); VOID(quick_rm_table(old_db_type,db,old_name)); } else { - // Using LOCK TABLES without rename. - // This code is never executed on WIN32! - // Remove old renamed table, reopen table and get new locks + /* + Using LOCK TABLES without rename. + This code is never executed on WIN32! + Remove old renamed table, reopen table and get new locks + */ if (table) { VOID(table->file->extra(HA_EXTRA_FORCE_REOPEN)); // Use new file @@ -1662,6 +1827,8 @@ int mysql_alter_table(THD *thd,char *new_db, char *new_name, } VOID(pthread_cond_broadcast(&COND_refresh)); VOID(pthread_mutex_unlock(&LOCK_open)); + table_list->table=0; // For query cache + query_cache_invalidate3(thd, table_list, 0); end_temporary: sprintf(tmp_name,ER(ER_INSERT_INFO),(ulong) (copied+deleted), @@ -1671,7 +1838,6 @@ end_temporary: DBUG_RETURN(0); err: - (void) ha_commit_rename(thd); // Just for safety DBUG_RETURN(-1); } @@ -1730,8 +1896,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; @@ -1781,7 +1947,7 @@ copy_data_between_tables(TABLE *from,TABLE *to, } end_read_record(&info); free_io_cache(from); - delete [] copy; + delete [] copy; // This is never 0 uint tmp_error; if ((tmp_error=to->file->extra(HA_EXTRA_NO_CACHE))) { diff --git a/sql/sql_test.cc b/sql/sql_test.cc index c4c2855a63e..43c24da85a2 100644 --- a/sql/sql_test.cc +++ b/sql/sql_test.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 */ @@ -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_udf.cc b/sql/sql_udf.cc index 8184ae3b15e..9493f969802 100644 --- a/sql/sql_udf.cc +++ b/sql/sql_udf.cc @@ -1,21 +1,19 @@ -/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB - +/* 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 */ - - /* This implements 'user defined functions' */ /* @@ -52,11 +50,11 @@ extern "C" { FreeLibrary((HMODULE)lib); } - + #elif !defined(OS2) #include <dlfcn.h> #endif - + #include <stdarg.h> #include <hash.h> } @@ -141,7 +139,8 @@ void udf_init() new_thd->version = refresh_version; //current_thd->version; new_thd->current_tablenr = 0; new_thd->open_tables = 0; - new_thd->db = my_strdup("mysql", MYF(0)); + new_thd->db= my_strdup("mysql", MYF(0)); + new_thd->db_length=5; bzero((gptr) &tables,sizeof(tables)); tables.name = tables.real_name = (char*) "func"; diff --git a/sql/sql_udf.h b/sql/sql_udf.h index d0b20f0a734..1ee9c44ce48 100644 --- a/sql/sql_udf.h +++ b/sql/sql_udf.h @@ -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 */ diff --git a/sql/sql_union.cc b/sql/sql_union.cc new file mode 100644 index 00000000000..c8237f3ae9b --- /dev/null +++ b/sql/sql_union.cc @@ -0,0 +1,274 @@ +/* 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 */ + + +/* + UNION of select's + UNION's were introduced by Monty and Sinisa <sinisa@mysql.com> +*/ + + +#include "mysql_priv.h" +#include "sql_select.h" + + +int mysql_union(THD *thd, LEX *lex,select_result *result) +{ + SELECT_LEX *sl, *last_sl, *lex_sl; + ORDER *order; + List<Item> item_list; + TABLE *table; + int describe=(lex->select_lex.options & SELECT_DESCRIBE) ? 1 : 0; + int res; + bool found_rows_for_union=false; + TABLE_LIST result_table_list; + TABLE_LIST *first_table=(TABLE_LIST *)lex->select_lex.table_list.first; + TMP_TABLE_PARAM tmp_table_param; + select_union *union_result; + DBUG_ENTER("mysql_union"); + + /* Fix tables 'to-be-unioned-from' list to point at opened tables */ + last_sl= &lex->select_lex; + for (sl= last_sl; + sl && sl->linkage != NOT_A_SELECT; + last_sl=sl, sl=sl->next) + { + for (TABLE_LIST *cursor= (TABLE_LIST *)sl->table_list.first; + cursor; + cursor=cursor->next) + cursor->table= ((TABLE_LIST*) cursor->table)->table; + } + + /* last_sel now points at the last select where the ORDER BY is stored */ + if (sl) + { + /* + The found SL is an extra SELECT_LEX argument that contains + the ORDER BY and LIMIT parameter for the whole UNION + */ + lex_sl= sl; + order= (ORDER *) lex_sl->order_list.first; + found_rows_for_union = lex->select_lex.options & OPTION_FOUND_ROWS && !describe && sl->select_limit; + if (found_rows_for_union) + lex->select_lex.options ^= OPTION_FOUND_ROWS; +// This is done to eliminate unnecessary slowing down of the first query + if (!order || !describe) + last_sl->next=0; // Remove this extra element + } + else if (!last_sl->braces) + { + lex_sl= last_sl; // ORDER BY is here + order= (ORDER *) lex_sl->order_list.first; + } + else + { + lex_sl=0; + order=0; + } + + if (describe) + { + Item *item; + item_list.push_back(new Item_empty_string("table",NAME_LEN)); + item_list.push_back(new Item_empty_string("type",10)); + item_list.push_back(item=new Item_empty_string("possible_keys", + NAME_LEN*MAX_KEY)); + item->maybe_null=1; + item_list.push_back(item=new Item_empty_string("key",NAME_LEN)); + item->maybe_null=1; + item_list.push_back(item=new Item_int("key_len",0,3)); + item->maybe_null=1; + item_list.push_back(item=new Item_empty_string("ref", + NAME_LEN*MAX_REF_PARTS)); + item->maybe_null=1; + item_list.push_back(new Item_real("rows",0.0,0,10)); + item_list.push_back(new Item_empty_string("Extra",255)); + } + else + { + Item *item; + List_iterator<Item> it(lex->select_lex.item_list); + TABLE_LIST *first_table= (TABLE_LIST*) lex->select_lex.table_list.first; + + /* Create a list of items that will be in the result set */ + while ((item= it++)) + if (item_list.push_back(item)) + DBUG_RETURN(-1); + if (setup_fields(thd,first_table,item_list,0,0,1)) + DBUG_RETURN(-1); + } + + bzero((char*) &tmp_table_param,sizeof(tmp_table_param)); + tmp_table_param.field_count=item_list.elements; + if (!(table=create_tmp_table(thd, &tmp_table_param, item_list, + (ORDER*) 0, !describe & !lex->union_option, + 1, 0, + (lex->select_lex.options | thd->options | + TMP_TABLE_ALL_COLUMNS)))) + DBUG_RETURN(-1); + table->file->extra(HA_EXTRA_WRITE_CACHE); + table->file->extra(HA_EXTRA_IGNORE_DUP_KEY); + bzero((char*) &result_table_list,sizeof(result_table_list)); + result_table_list.db= (char*) ""; + result_table_list.real_name=result_table_list.name=(char*) "union"; + result_table_list.table=table; + + if (!(union_result=new select_union(table))) + { + res= -1; + goto exit; + } + union_result->save_time_stamp=!describe; + + for (sl= &lex->select_lex; sl; sl=sl->next) + { + lex->select=sl; + thd->offset_limit=sl->offset_limit; + thd->select_limit=sl->select_limit+sl->offset_limit; + if (thd->select_limit < sl->select_limit) + thd->select_limit= HA_POS_ERROR; // no limit + if (thd->select_limit == HA_POS_ERROR) + sl->options&= ~OPTION_FOUND_ROWS; + + res=mysql_select(thd, (describe && sl->linkage==NOT_A_SELECT) ? first_table : (TABLE_LIST*) sl->table_list.first, + sl->item_list, + sl->where, + (sl->braces) ? (ORDER *)sl->order_list.first : (ORDER *) 0, + (ORDER*) sl->group_list.first, + sl->having, + (ORDER*) NULL, + sl->options | thd->options | SELECT_NO_UNLOCK | ((describe) ? SELECT_DESCRIBE : 0), + union_result); + if (res) + goto exit; + } + if (union_result->flush()) + { + res= 1; // Error is already sent + goto exit; + } + delete union_result; + + /* Send result to 'result' */ + lex->select = &lex->select_lex; + res =-1; + { + /* Create a list of fields in the temporary table */ + List_iterator<Item> it(item_list); + Field **field; +#if 0 + List<Item_func_match> ftfunc_list; + ftfunc_list.empty(); +#else + thd->lex.select_lex.ftfunc_list.empty(); +#endif + + for (field=table->field ; *field ; field++) + { + (void) it++; + (void) it.replace(new Item_field(*field)); + } + if (!thd->fatal_error) // Check if EOM + { + if (lex_sl) + { + thd->offset_limit=lex_sl->offset_limit; + thd->select_limit=lex_sl->select_limit+lex_sl->offset_limit; + if (thd->select_limit < lex_sl->select_limit) + thd->select_limit= HA_POS_ERROR; // no limit + if (thd->select_limit == HA_POS_ERROR) + thd->options&= ~OPTION_FOUND_ROWS; + } + else + { + thd->offset_limit= 0; + thd->select_limit= thd->default_select_limit; + } + if (describe) + thd->select_limit= HA_POS_ERROR; // no limit + res=mysql_select(thd,&result_table_list, + item_list, NULL, (describe) ? 0 : order, + (ORDER*) NULL, NULL, (ORDER*) NULL, + thd->options, result); + if (found_rows_for_union && !res) + thd->limit_found_rows = (ulonglong)table->file->records; + } + } + +exit: + free_tmp_table(thd,table); + DBUG_RETURN(res); +} + + +/*************************************************************************** +** store records in temporary table for UNION +***************************************************************************/ + +select_union::select_union(TABLE *table_par) + :table(table_par) +{ + bzero((char*) &info,sizeof(info)); + /* + We can always use DUP_IGNORE because the temporary table will only + contain a unique key if we are using not using UNION ALL + */ + info.handle_duplicates=DUP_IGNORE; +} + +select_union::~select_union() +{ +} + + +int select_union::prepare(List<Item> &list) +{ + if (save_time_stamp && list.elements != table->fields) + { + my_message(ER_WRONG_NUMBER_OF_COLUMNS_IN_SELECT, + ER(ER_WRONG_NUMBER_OF_COLUMNS_IN_SELECT),MYF(0)); + return -1; + } + return 0; +} + +bool select_union::send_data(List<Item> &values) +{ + if (thd->offset_limit) + { // using limit offset,count + thd->offset_limit--; + return 0; + } + fill_record(table->field,values); + return write_record(table,&info) ? 1 : 0; +} + +bool select_union::send_eof() +{ + return 0; +} + +bool select_union::flush() +{ + int error; + if ((error=table->file->extra(HA_EXTRA_NO_CACHE))) + { + table->file->print_error(error,MYF(0)); + ::send_error(&thd->net); + return 1; + } + return 0; +} diff --git a/sql/sql_update.cc b/sql/sql_update.cc index e5e246b3962..f8a0d169d5a 100644 --- a/sql/sql_update.cc +++ b/sql/sql_update.cc @@ -1,24 +1,29 @@ /* 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 */ -/* Update of records */ +/* Update of records + + Multi-table updates were introduced by Monty and Sinisa <sinisa@mysql.com> + +*/ #include "mysql_priv.h" #include "sql_acl.h" +#include "sql_select.h" /* Return 0 if row hasn't changed */ @@ -40,8 +45,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) @@ -87,7 +96,7 @@ int mysql_update(THD *thd,TABLE_LIST *table_list,List<Item> &fields, /* Check the fields we are going to modify */ table->grant.want_privilege=want_privilege; - if (setup_fields(thd,table_list,fields,1,0)) + if (setup_fields(thd,table_list,fields,1,0,0)) DBUG_RETURN(-1); /* purecov: inspected */ if (table->timestamp_field) { @@ -100,7 +109,7 @@ int mysql_update(THD *thd,TABLE_LIST *table_list,List<Item> &fields, /* Check values */ table->grant.want_privilege=(SELECT_ACL & ~table->grant.privilege); - if (setup_fields(thd,table_list,values,0,0)) + if (setup_fields(thd,table_list,values,0,0,0)) { table->time_stamp=save_time_stamp; // Restore timestamp pointer DBUG_RETURN(-1); /* purecov: inspected */ @@ -126,7 +135,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; @@ -146,7 +155,7 @@ int mysql_update(THD *thd,TABLE_LIST *table_list,List<Item> &fields, used_key_is_modified=check_if_key_used(table, used_index, fields); else used_key_is_modified=0; - if (used_key_is_modified) + if (used_key_is_modified || order) { /* ** We can't update table directly; We must first search after all @@ -166,8 +175,36 @@ 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"; + thd->proc_info="Searching rows for update"; while (!(error=info.read_record(&info)) && !thd->killed) { @@ -183,7 +220,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); } @@ -205,7 +242,7 @@ int mysql_update(THD *thd,TABLE_LIST *table_list,List<Item> &fields, select->cond=0; } else - { + { select= new SQL_SELECT; select->head=table; } @@ -216,12 +253,10 @@ 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); } } - if (!(test_flags & TEST_READCHECK)) /* For debugging */ - VOID(table->file->extra(HA_EXTRA_NO_READCHECK)); if (handle_duplicates == DUP_IGNORE) table->file->extra(HA_EXTRA_IGNORE_DUP_KEY); init_read_record(&info,thd,table,select,0,1); @@ -229,7 +264,7 @@ int mysql_update(THD *thd,TABLE_LIST *table_list,List<Item> &fields, ha_rows updated=0L,found=0L; thd->count_cuted_fields=1; /* calc cuted fields */ thd->cuted_fields=0L; - thd->proc_info="updating"; + thd->proc_info="Updating"; query_id=thd->query_id; while (!(error=info.read_record(&info)) && !thd->killed) @@ -266,7 +301,6 @@ int mysql_update(THD *thd,TABLE_LIST *table_list,List<Item> &fields, } end_read_record(&info); thd->proc_info="end"; - VOID(table->file->extra(HA_EXTRA_READCHECK)); VOID(table->file->extra(HA_EXTRA_NO_IGNORE_DUP_KEY)); table->time_stamp=save_time_stamp; // Restore auto timestamp pointer using_transactions=table->file->has_transactions(); @@ -289,6 +323,11 @@ int mysql_update(THD *thd,TABLE_LIST *table_list,List<Item> &fields, mysql_unlock_tables(thd, thd->lock); thd->lock=0; } + if (updated) + { + query_cache_invalidate3(thd, table_list, 1); + } + delete select; if (error >= 0) send_error(&thd->net,thd->killed ? ER_SERVER_SHUTDOWN : 0); /* purecov: inspected */ @@ -303,5 +342,461 @@ int mysql_update(THD *thd,TABLE_LIST *table_list,List<Item> &fields, DBUG_PRINT("info",("%d records updated",updated)); } thd->count_cuted_fields=0; /* calc cuted fields */ + free_io_cache(table); + DBUG_RETURN(0); +} + +/*************************************************************************** +** update multiple tables from join +***************************************************************************/ + +multi_update::multi_update(THD *thd_arg, TABLE_LIST *ut, List<Item> &fs, + enum enum_duplicates handle_duplicates, thr_lock_type lock_option_arg, uint num) + : update_tables (ut), thd(thd_arg), updated(0), found(0), fields(fs), lock_option(lock_option_arg), + dupl(handle_duplicates), num_of_tables(num), num_fields(0), num_updated(0) , error(0), do_update(false) +{ + save_time_stamps = (uint *) sql_calloc (sizeof(uint) * num_of_tables); + tmp_tables = (TABLE **)NULL; + int counter=0; + ulong timestamp_query_id; + not_trans_safe=false; + for (TABLE_LIST *dt=ut ; dt ; dt=dt->next,counter++) + { + TABLE *table=ut->table; +// (void) ut->table->file->extra(HA_EXTRA_NO_KEYREAD); + dt->table->used_keys=0; + if (table->timestamp_field) + { + // Don't set timestamp column if this is modified + timestamp_query_id=table->timestamp_field->query_id; + table->timestamp_field->query_id=thd->query_id-1; + if (table->timestamp_field->query_id == thd->query_id) + table->time_stamp=0; + else + table->timestamp_field->query_id=timestamp_query_id; + } + save_time_stamps[counter]=table->time_stamp; + } + error = 1; // In case we do not reach prepare we have to reset timestamps +} + +int +multi_update::prepare(List<Item> &values) +{ + DBUG_ENTER("multi_update::prepare"); + do_update = true; + thd->count_cuted_fields=1; + thd->cuted_fields=0L; + thd->proc_info="updating the main table"; + TABLE_LIST *table_ref; + + if (thd->options & OPTION_SAFE_UPDATES) + { + for (table_ref=update_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); + } + } + } +// Here I have to connect fields with tables and only update tables that need to be updated ... + +// I calculate num_updated and fill-up table_sequence +// Set table_list->shared to true or false, depending on whether table is to be updated or not + Item_field *item; + List_iterator<Item> it(fields); + num_fields=fields.elements; + field_sequence = (uint *) sql_alloc(sizeof(uint)*num_fields); + uint *int_ptr=field_sequence; + while ((item= (Item_field *)it++)) + { + unsigned int counter=0; + for (table_ref=update_tables; table_ref; table_ref=table_ref->next, counter++) + { + if (table_ref->table == item->field->table && !table_ref->shared) + { + num_updated++; + table_ref->shared=1; + if (!not_trans_safe && !table_ref->table->file->has_transactions()) + not_trans_safe=true; + table_ref->table->no_keyread=1; // to be moved if initialize_tables has to be used + break; + } + } + if (!table_ref) + { + error = 1; // A proper error message is due here + DBUG_RETURN(1); + } + else + *int_ptr++=counter; + } + if (!num_updated) + { + error = 1; // A proper error message is due here + DBUG_RETURN(1); + } + +// Here, I have to allocate the array of temporary tables +// I have to treat a case of num_updated=1 differently in send_data() method. + if (num_updated > 1) + { + tmp_tables = (TABLE **) sql_calloc(sizeof(TABLE *) * (num_updated - 1)); + infos = (COPY_INFO *) sql_calloc(sizeof(COPY_INFO) * (num_updated - 1)); + fields_by_tables = (List_item **)sql_calloc(sizeof(List_item *) * num_updated); + unsigned int counter; + List<Item> *temp_fields; + for (table_ref=update_tables, counter = 0; table_ref; table_ref=table_ref->next) + { + if (!table_ref->shared) + continue; +// Here we have to add row offset as an additional field ... + if (!(temp_fields = (List_item *)sql_calloc(sizeof(List_item)))) + { + error = 1; // A proper error message is due here + DBUG_RETURN(1); + } + temp_fields->empty(); + it.rewind(); int_ptr=field_sequence; + while ((item= (Item_field *)it++)) + { + if (*int_ptr++ == counter) + temp_fields->push_back(item); + } + if (counter) + { + Field_string offset(table_ref->table->file->ref_length,false,"offset",table_ref->table,true); + temp_fields->push_front(new Item_field(((Field *)&offset))); +// Here I make tmp tables + int cnt=counter-1; + TMP_TABLE_PARAM tmp_table_param; + bzero((char*) &tmp_table_param,sizeof(tmp_table_param)); + tmp_table_param.field_count=temp_fields->elements; + if (!(tmp_tables[cnt]=create_tmp_table(thd, &tmp_table_param, *temp_fields, + (ORDER*) 0, 1, 0, 0, TMP_TABLE_ALL_COLUMNS))) + { + error = 1; // A proper error message is due here + DBUG_RETURN(1); + } + tmp_tables[cnt]->file->extra(HA_EXTRA_WRITE_CACHE); + tmp_tables[cnt]->file->extra(HA_EXTRA_IGNORE_DUP_KEY); + infos[cnt].handle_duplicates=DUP_IGNORE; + temp_fields->pop(); // because we shall use those for values only ... + } + fields_by_tables[counter]=temp_fields; + counter++; + } + } + error = 0; // Timestamps do not need to be restored, so far ... DBUG_RETURN(0); } + + +void +multi_update::initialize_tables(JOIN *join) +{ +/* We skip it as it only makes a mess ........... + TABLE_LIST *walk; + table_map tables_to_update_from=0; + for (walk= update_tables ; walk ; walk=walk->next) + tables_to_update_from|= walk->table->map; + + walk= update_tables; + for (JOIN_TAB *tab=join->join_tab, *end=join->join_tab+join->tables; + tab < end; + tab++) + { + if (tab->table->map & tables_to_update_from) + { + We are going to update from this table + walk->table=tab->table; + walk=walk->next; + if (tab == join->join_tab) + tab->table->no_keyread=1; + } + } +*/ +} + + +multi_update::~multi_update() +{ + int counter = 0; + for (table_being_updated=update_tables ; + table_being_updated ; + counter++, table_being_updated=table_being_updated->next) + { + TABLE *table=table_being_updated->table; + table->no_keyread=0; + if (error) + table->time_stamp=save_time_stamps[counter]; + } + if (tmp_tables) + for (uint counter = 0; counter < num_updated-1; counter++) + if (tmp_tables[counter]) + free_tmp_table(thd,tmp_tables[counter]); +} + + +bool multi_update::send_data(List<Item> &values) +{ + List<Item> real_values(values); + for (uint counter = 0; counter < fields.elements; counter++) + real_values.pop(); +// We have skipped fields .... + if (num_updated == 1) + { + for (table_being_updated=update_tables ; + table_being_updated ; + table_being_updated=table_being_updated->next) + { + if (!table_being_updated->shared) + continue; + TABLE *table=table_being_updated->table; + /* Check if we are using outer join and we didn't find the row */ + if (table->status & (STATUS_NULL_ROW | STATUS_UPDATED)) + return 0; + table->file->position(table->record[0]); +// Only one table being updated receives a completely different treatment + table->status|= STATUS_UPDATED; + store_record(table,1); + if (fill_record(fields,real_values)) + return 1; + found++; + if (/* compare_record(table, query_id) && */ + !(error=table->file->update_row(table->record[1], table->record[0]))) + updated++; + return error; + } + } + else + { + int secure_counter= -1; + for (table_being_updated=update_tables ; + table_being_updated ; + table_being_updated=table_being_updated->next, secure_counter++) + { + if (!table_being_updated->shared) + continue; + + TABLE *table=table_being_updated->table; + /* Check if we are using outer join and we didn't find the row */ + if (table->status & (STATUS_NULL_ROW | STATUS_UPDATED)) + continue; + table->file->position(table->record[0]); + Item *item; + List_iterator<Item> it(real_values); + List <Item> values_by_table; + uint *int_ptr=field_sequence; + while ((item= (Item *)it++)) + { + if (*int_ptr++ == (uint) (secure_counter + 1)) + values_by_table.push_back(item); + } +// Here I am breaking values as per each table + if (secure_counter < 0) + { + table->status|= STATUS_UPDATED; + store_record(table,1); + if (fill_record(*fields_by_tables[0],values_by_table)) + return 1; + found++; + if (/*compare_record(table, query_id) && */ + !(error=table->file->update_row(table->record[1], table->record[0]))) + updated++; + else + { + table->file->print_error(error,MYF(0)); + if (!error) error=1; + return 1; + } + } + else + { + // Here we insert into each temporary table + values_by_table.push_front(new Item_string((char*) table->file->ref, + table->file->ref_length)); + fill_record(tmp_tables[secure_counter]->field,values_by_table); + error= write_record(tmp_tables[secure_counter], + &(infos[secure_counter])); + if (error) + { + error=-1; + return 1; + } + } + } + } + return 0; +} + +void multi_update::send_error(uint errcode,const char *err) +{ + /* First send error what ever it is ... */ + ::send_error(&thd->net,errcode,err); + + /* reset used flags */ +// update_tables->table->no_keyread=0; + + /* If nothing updated return */ + if (!updated) + return; + /* Below can happen when thread is killed early ... */ + if (!table_being_updated) + table_being_updated=update_tables; + + /* + If rows from the first table only has been updated 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 updates ... + */ + if ((table_being_updated->table->file->has_transactions() && + table_being_updated == update_tables) || !not_trans_safe) + ha_rollback_stmt(thd); + else if (do_update) + VOID(do_updates(true)); +} + + +int multi_update::do_updates (bool from_send_error) +{ + int error = 0, counter = 0; + + if (num_updated == 1) + return 0; + if (from_send_error) + { + /* Found out table number for 'table_being_updated' */ + for (TABLE_LIST *aux=update_tables; + aux != table_being_updated; + aux=aux->next) + counter++; + } + else + table_being_updated = update_tables; + + do_update = false; + for (table_being_updated=table_being_updated->next; + table_being_updated ; + table_being_updated=table_being_updated->next, counter++) + { + if (!table_being_updated->shared) + continue; + + TABLE *table = table_being_updated->table; + TABLE *tmp_table=tmp_tables[counter]; + if (tmp_table->file->extra(HA_EXTRA_NO_CACHE)) + { + error=1; + break; + } + List<Item> list; + Field **ptr=tmp_table->field,*field; + // This is supposed to be something like insert_fields + thd->used_tables|=tmp_table->map; + while ((field = *ptr++)) + { + list.push_back((Item *)new Item_field(field)); + if (field->query_id == thd->query_id) + thd->dupp_field=field; + field->query_id=thd->query_id; + tmp_table->used_keys&=field->part_of_key; + } + tmp_table->used_fields=tmp_table->fields; + error=0; list.pop(); // we get position some other way ... + error = tmp_table->file->rnd_init(1); + if (error) + return error; + while (!(error=tmp_table->file->rnd_next(tmp_table->record[0])) && + (!thd->killed || from_send_error || not_trans_safe)) + { + found++; + error= table->file->rnd_pos(table->record[0], + (byte*) (*(tmp_table->field))->ptr); + if (error) + return error; + table->status|= STATUS_UPDATED; + store_record(table,1); + error= fill_record(*fields_by_tables[counter + 1],list) || + /* compare_record(table, query_id) || */ + table->file->update_row(table->record[1],table->record[0]); + if (error) + { + table->file->print_error(error,MYF(0)); + break; + } + else + updated++; + } + if (error == HA_ERR_END_OF_FILE) + error = 0; + } + return error; +} + + +/* out: 1 if error, 0 if success */ + +bool multi_update::send_eof() +{ + thd->proc_info="updating the reference tables"; + + /* Does updates for the last n - 1 tables, returns 0 if ok */ + int error = do_updates(false); /* do_updates returns 0 if success */ + + /* reset used flags */ +#ifndef NOT_USED + update_tables->table->no_keyread=0; +#endif + if (error == -1) + error = 0; + thd->proc_info="end"; + if (error) + send_error(error,"An error occured in multi-table update"); + + /* + Write the SQL statement to the binlog if we updated + rows and we succeeded, or also in an error case when there + was a non-transaction-safe table involved, since + modifications in it cannot be rolled back. + */ + + if (updated || not_trans_safe) + { + mysql_update_log.write(thd,thd->query,thd->query_length); + Query_log_event qinfo(thd, thd->query); + + /* + mysql_bin_log is not open if binlogging or replication + is not used + */ + + if (mysql_bin_log.is_open() && mysql_bin_log.write(&qinfo) && + !not_trans_safe) + error=1; /* Log write failed: roll back the SQL statement */ + + /* Commit or rollback the current SQL statement */ + VOID(ha_autocommit_or_rollback(thd,error > 0)); + } + else + error=0; // this can happen only if it is end of file error + if (!error) // if the above log write did not fail ... + { + char buff[80]; + sprintf(buff,ER(ER_UPDATE_INFO), (long) found, (long) updated, + (long) thd->cuted_fields); + if (updated) + { + query_cache_invalidate3(thd, update_tables, 1); + } + ::send_ok(&thd->net, + (thd->client_capabilities & CLIENT_FOUND_ROWS) ? found : updated, + thd->insert_id_used ? thd->insert_id() : 0L,buff); + } + thd->count_cuted_fields=0; + return 0; +} diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index c01532e48f7..ba07aab2859 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -1,4 +1,4 @@ -/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB +/* Copyright (C) 2000-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 @@ -21,11 +21,13 @@ #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 "slave.h" #include "sql_acl.h" #include "lex_symbol.h" #include <myisam.h> +#include <myisammrg.h> extern void yyerror(const char*); int yylex(void *yylval); @@ -42,7 +44,7 @@ inline Item *or_or_concat(Item* A, Item* B) %union { int num; ulong ulong_num; - ulonglong ulonglong_num; + ulonglong ulonglong_number; LEX_STRING lex_str; LEX_STRING *lex_str_ptr; LEX_SYMBOL symbol; @@ -54,7 +56,9 @@ inline Item *or_or_concat(Item* A, Item* B) Key::Keytype key_type; enum db_type db_type; enum row_type row_type; + enum ha_rkey_function ha_rkey_mode; enum enum_tx_isolation tx_isolation; + enum Item_cast cast_type; String *string; key_part_spec *key_part; TABLE_LIST *table_list; @@ -72,6 +76,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 @@ -90,7 +101,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize); %token MIN_SYM %token SUM_SYM %token STD_SYM - +%token ABORT_SYM %token ADD %token ALTER %token AFTER_SYM @@ -112,6 +123,8 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize); %token RESET_SYM %token PURGE %token SLAVE +%token IO_THREAD +%token SQL_THREAD %token START_SYM %token STOP_SYM %token TRUNCATE_SYM @@ -124,6 +137,8 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize); %token LOCK_SYM %token LOCKS_SYM %token UNLOCK_SYM +%token BINLOG_SYM +%token EVENTS_SYM %token ACTION %token AGGREGATE_SYM @@ -139,27 +154,37 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize); %token BINARY %token BIT_SYM %token BOOL_SYM +%token BOOLEAN_SYM %token BOTH %token BY +%token CACHE_SYM %token CASCADE +%token CAST_SYM %token CHECKSUM_SYM %token CHECK_SYM +%token CIPHER %token COMMITTED_SYM %token COLUMNS %token COLUMN_SYM %token CONCURRENT %token CONSTRAINT +%token CONVERT_SYM %token DATABASES %token DATA_SYM %token DEFAULT %token DELAYED_SYM %token DELAY_KEY_WRITE_SYM +%token DEMAND_SYM %token DESC %token DESCRIBE +%token DES_KEY_FILE +%token DISABLE_SYM %token DISTINCT %token DYNAMIC_SYM +%token ENABLE_SYM %token ENCLOSED %token ESCAPED +%token DIRECTORY_SYM %token ESCAPE_SYM %token EXISTS %token EXTENDED_SYM @@ -171,8 +196,6 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize); %token FROM %token FULL %token FULLTEXT_SYM -%token GEMINI_SYM -%token GEMINI_SPIN_RETRIES %token GLOBAL_SYM %token GRANT %token GRANTS @@ -186,6 +209,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 @@ -193,6 +217,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize); %token IN_SYM %token ISOLATION %token ISAM_SYM +%token ISSUER %token JOIN_SYM %token KEYS %token KEY_SYM @@ -211,22 +236,31 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize); %token MASTER_USER_SYM %token MASTER_LOG_FILE_SYM %token MASTER_LOG_POS_SYM +%token MASTER_LOG_SEQ_SYM %token MASTER_PASSWORD_SYM %token MASTER_PORT_SYM %token MASTER_CONNECT_RETRY_SYM +%token MASTER_SERVER_ID_SYM +%token RELAY_LOG_FILE_SYM +%token RELAY_LOG_POS_SYM %token MATCH %token MAX_ROWS +%token MAX_CONNECTIONS_PER_HOUR +%token MAX_QUERIES_PER_HOUR +%token MAX_UPDATES_PER_HOUR %token MEDIUM_SYM %token MERGE_SYM %token MIN_ROWS %token MYISAM_SYM %token NATIONAL_SYM %token NATURAL +%token NEW_SYM %token NCHAR_SYM %token NOT %token NO_SYM %token NULL_SYM %token NUM +%token OFF %token ON %token OPEN_SYM %token OPTION @@ -243,6 +277,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize); %token PRIVILEGES %token PROCESS %token PROCESSLIST_SYM +%token QUERY_SYM %token RAID_0_SYM %token RAID_STRIPED_SYM %token RAID_TYPE @@ -255,6 +290,8 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize); %token RELOAD %token RENAME %token REPEATABLE_SYM +%token REQUIRE_SYM +%token RESOURCES %token RESTORE_SYM %token RESTRICT %token REVOKE @@ -265,9 +302,13 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize); %token SERIALIZABLE_SYM %token SESSION_SYM %token SHUTDOWN +%token SQL_CACHE_SYM +%token SQL_NO_CACHE_SYM +%token SSL_SYM %token STARTING %token STATUS_SYM %token STRAIGHT_JOIN +%token SUBJECT_SYM %token TABLES %token TABLE_SYM %token TEMPORARY @@ -288,6 +329,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize); %token UNION_SYM %token UNIQUE_SYM %token USAGE +%token USE_FRM %token USE_SYM %token USING %token VALUES @@ -295,6 +337,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize); %token WHERE %token WITH %token WRITE_SYM +%token X509_SYM %token COMPRESSED_SYM %token BIGINT @@ -320,6 +363,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize); %token PRECISION %token QUICK %token REAL +%token SIGNED_SYM %token SMALLINT %token STRING_SYM %token TEXT_SYM @@ -328,6 +372,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize); %token TINYBLOB %token TINYINT %token TINYTEXT +%token ULONGLONG_NUM %token UNSIGNED %token VARBINARY %token VARCHAR @@ -352,6 +397,8 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize); %token DAY_SECOND_SYM %token DAY_SYM %token DECODE_SYM +%token DES_ENCRYPT_SYM +%token DES_DECRYPT_SYM %token ELSE %token ELT_FUNC %token ENCODE_SYM @@ -369,6 +416,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize); %token IDENTIFIED_SYM %token IF %token INSERT_ID +%token INSERT_METHOD %token INTERVAL_SYM %token LAST_INSERT_ID %token LEFT @@ -425,9 +473,14 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize); %token SQL_WARNINGS %token SQL_AUTO_IS_NULL %token SQL_SAFE_UPDATES +%token SQL_QUERY_CACHE_TYPE_SYM %token SQL_QUOTE_SHOW_CREATE %token SQL_SLAVE_SKIP_COUNTER +%token ISSUER_SYM +%token SUBJECT_SYM +%token CIPHER_SYM + %left SET_VAR %left OR_OR_CONCAT OR %left AND @@ -444,7 +497,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize); %type <lex_str> IDENT TEXT_STRING REAL_NUM FLOAT_NUM NUM LONG_NUM HEX_NUM LEX_HOSTNAME - field_ident select_alias ident ident_or_text + ULONGLONG_NUM field_ident select_alias ident ident_or_text %type <lex_str_ptr> opt_table_alias @@ -457,18 +510,18 @@ 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 + ULONG_NUM raid_types merge_insert_types -%type <ulonglong_num> - ULONGLONG_NUM +%type <ulonglong_number> + ulonglong_num %type <item> literal text_literal insert_ident order_ident @@ -477,7 +530,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize); using_list %type <item_list> - expr_list udf_expr_list when_list ident_list + expr_list udf_expr_list when_list ident_list ident_list_arg %type <key_type> key_type opt_unique_or_fulltext @@ -503,6 +556,10 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize); %type <tx_isolation> tx_isolation isolation_types +%type <ha_rkey_mode> handler_rkey_mode + +%type <cast_type> cast_type + %type <udf_type> udf_func_type %type <symbol> FUNC_ARG0 FUNC_ARG1 FUNC_ARG2 FUNC_ARG3 keyword @@ -519,13 +576,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 @@ -533,7 +590,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_read_or_scan + single_multi table_wild_list table_wild_one opt_wild union union_list + precision union_option +END_OF_INPUT %type <NONE> '-' '+' '*' '/' '%' '(' ')' @@ -546,7 +606,7 @@ query: { THD *thd=current_thd; if (!thd->bootstrap && - (!(thd->lex.options & OPTION_FOUND_COMMENT))) + (!(thd->lex.select_lex.options & OPTION_FOUND_COMMENT))) { send_error(¤t_thd->net,ER_EMPTY_QUERY); YYABORT; @@ -556,7 +616,7 @@ query: thd->lex.sql_command = SQLCOM_EMPTY_QUERY; } } - | verb_clause END_OF_INPUT {} + | verb_clause END_OF_INPUT {}; verb_clause: alter @@ -591,9 +651,10 @@ verb_clause: | slave | show | truncate + | handler | unlock | update - | use + | use; /* change master */ @@ -603,12 +664,12 @@ change: LEX *lex = Lex; lex->sql_command = SQLCOM_CHANGE_MASTER; memset(&lex->mi, 0, sizeof(lex->mi)); - } master_defs + } master_defs; master_defs: master_def | - master_defs ',' master_def + master_defs ',' master_def; master_def: MASTER_HOST_SYM EQ TEXT_STRING @@ -636,7 +697,7 @@ master_def: Lex->mi.port = $3; } | - MASTER_LOG_POS_SYM EQ ULONGLONG_NUM + MASTER_LOG_POS_SYM EQ ulonglong_num { Lex->mi.pos = $3; } @@ -645,7 +706,16 @@ master_def: { Lex->mi.connect_retry = $3; } - + | + RELAY_LOG_FILE_SYM EQ TEXT_STRING + { + Lex->mi.relay_log_name = $3.str; + } + | + RELAY_LOG_POS_SYM EQ ULONG_NUM + { + Lex->mi.relay_log_pos = $3; + }; /* create a table */ @@ -671,87 +741,94 @@ 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: '(' field_list ')' opt_create_table_options create3 {} - | opt_create_table_options create3 {} + | opt_create_table_options create3 {}; 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 {} + select_options select_item_list opt_select_from union {}; opt_as: /* empty */ {} - | AS {} + | AS {}; opt_table_options: /* empty */ { $$= 0; } - | table_options { $$= $1;} + | table_options { $$= $1;}; table_options: table_option { $$=$1; } - | table_option table_options { $$= $1 | $2; } + | table_option table_options { $$= $1 | $2; }; table_option: - TEMPORARY { $$=HA_LEX_CREATE_TMP_TABLE; } + TEMPORARY { $$=HA_LEX_CREATE_TMP_TABLE; }; opt_if_not_exists: /* empty */ { $$= 0; } - | IF NOT EXISTS { $$=HA_LEX_CREATE_IF_NOT_EXISTS; } + | IF NOT EXISTS { $$=HA_LEX_CREATE_IF_NOT_EXISTS; }; opt_create_table_options: /* empty */ - | create_table_options + | create_table_options; create_table_options: create_table_option - | create_table_option create_table_options + | create_table_option create_table_options; create_table_option: TYPE_SYM EQ table_types { Lex->create_info.db_type= $3; } - | MAX_ROWS EQ ULONGLONG_NUM { Lex->create_info.max_rows= $3; } - | MIN_ROWS EQ ULONGLONG_NUM { Lex->create_info.min_rows= $3; } - | AVG_ROW_LENGTH EQ ULONG_NUM { Lex->create_info.avg_row_length=$3; } + | MAX_ROWS EQ ulonglong_num { Lex->create_info.max_rows= $3; Lex->create_info.used_fields|= HA_CREATE_USED_MAX_ROWS;} + | MIN_ROWS EQ ulonglong_num { Lex->create_info.min_rows= $3; Lex->create_info.used_fields|= HA_CREATE_USED_MIN_ROWS;} + | AVG_ROW_LENGTH EQ ULONG_NUM { Lex->create_info.avg_row_length=$3; Lex->create_info.used_fields|= HA_CREATE_USED_AVG_ROW_LENGTH;} | PASSWORD EQ TEXT_STRING { Lex->create_info.password=$3.str; } | COMMENT_SYM EQ TEXT_STRING { Lex->create_info.comment=$3.str; } - | AUTO_INC EQ ULONGLONG_NUM { Lex->create_info.auto_increment_value=$3; Lex->create_info.used_fields|= HA_CREATE_USED_AUTO;} - | PACK_KEYS_SYM EQ ULONG_NUM { Lex->create_info.table_options|= $3 ? HA_OPTION_PACK_KEYS : HA_OPTION_NO_PACK_KEYS; } + | AUTO_INC EQ ulonglong_num { Lex->create_info.auto_increment_value=$3; Lex->create_info.used_fields|= HA_CREATE_USED_AUTO;} + | PACK_KEYS_SYM EQ ULONG_NUM { Lex->create_info.table_options|= $3 ? HA_OPTION_PACK_KEYS : HA_OPTION_NO_PACK_KEYS; Lex->create_info.used_fields|= HA_CREATE_USED_PACK_KEYS;} + | PACK_KEYS_SYM EQ DEFAULT { Lex->create_info.table_options&= ~(HA_OPTION_PACK_KEYS | HA_OPTION_NO_PACK_KEYS); Lex->create_info.used_fields|= HA_CREATE_USED_PACK_KEYS;} | CHECKSUM_SYM EQ ULONG_NUM { Lex->create_info.table_options|= $3 ? HA_OPTION_CHECKSUM : HA_OPTION_NO_CHECKSUM; } | DELAY_KEY_WRITE_SYM EQ ULONG_NUM { Lex->create_info.table_options|= $3 ? HA_OPTION_DELAY_KEY_WRITE : HA_OPTION_NO_DELAY_KEY_WRITE; } | ROW_FORMAT_SYM EQ row_types { Lex->create_info.row_type= $3; } @@ -762,15 +839,18 @@ 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; } + | INSERT_METHOD EQ merge_insert_types { Lex->create_info.merge_insert_method= $3; Lex->create_info.used_fields|= HA_CREATE_USED_INSERT_METHOD;} + | 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; } @@ -778,36 +858,40 @@ table_types: | MERGE_SYM { $$= DB_TYPE_MRG_MYISAM; } | HEAP_SYM { $$= DB_TYPE_HEAP; } | BERKELEY_DB_SYM { $$= DB_TYPE_BERKELEY_DB; } - | INNOBASE_SYM { $$= DB_TYPE_INNOBASE; } - | GEMINI_SYM { $$= DB_TYPE_GEMINI; } + | INNOBASE_SYM { $$= DB_TYPE_INNODB; }; row_types: DEFAULT { $$= ROW_TYPE_DEFAULT; } | FIXED_SYM { $$= ROW_TYPE_FIXED; } | DYNAMIC_SYM { $$= ROW_TYPE_DYNAMIC; } - | COMPRESSED_SYM { $$= ROW_TYPE_COMPRESSED; } + | COMPRESSED_SYM { $$= ROW_TYPE_COMPRESSED; }; raid_types: RAID_STRIPED_SYM { $$= RAID_TYPE_0; } | RAID_0_SYM { $$= RAID_TYPE_0; } - | ULONG_NUM { $$=$1;} + | ULONG_NUM { $$=$1;}; + +merge_insert_types: + NO_SYM { $$= MERGE_INSERT_DISABLED; } + | FIRST_SYM { $$= MERGE_INSERT_TO_FIRST; } + | LAST_SYM { $$= MERGE_INSERT_TO_LAST; }; opt_select_from: /* empty */ - | select_from select_lock_type + | select_from select_lock_type; udf_func_type: /* empty */ { $$ = UDFTYPE_FUNCTION; } - | AGGREGATE_SYM { $$ = UDFTYPE_AGGREGATE; } + | AGGREGATE_SYM { $$ = UDFTYPE_AGGREGATE; }; udf_type: STRING_SYM {$$ = (int) STRING_RESULT; } | REAL {$$ = (int) REAL_RESULT; } - | INT_SYM {$$ = (int) INT_RESULT; } + | INT_SYM {$$ = (int) INT_RESULT; }; field_list: field_list_item - | field_list ',' field_list_item + | field_list ',' field_list_item; field_list_item: @@ -818,8 +902,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 { @@ -828,27 +913,29 @@ field_list_item: | opt_constraint CHECK_SYM '(' expr ')' { Lex->col_list.empty(); /* Alloced by sql_alloc */ - } + }; opt_constraint: /* empty */ - | CONSTRAINT opt_ident + | CONSTRAINT opt_ident; 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; - } + }; type: int_type opt_len field_options { Lex->length=$2; $$=$1; } @@ -898,72 +985,82 @@ 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; - } + }; char: CHAR_SYM {} | NCHAR_SYM {} - | NATIONAL_SYM CHAR_SYM {} + | NATIONAL_SYM CHAR_SYM {}; varchar: char VARYING {} | VARCHAR {} | NATIONAL_SYM VARCHAR {} - | NCHAR_SYM VARCHAR {} + | NCHAR_SYM VARCHAR {}; int_type: INT_SYM { $$=FIELD_TYPE_LONG; } | TINYINT { $$=FIELD_TYPE_TINY; } | SMALLINT { $$=FIELD_TYPE_SHORT; } | MEDIUMINT { $$=FIELD_TYPE_INT24; } - | BIGINT { $$=FIELD_TYPE_LONGLONG; } + | BIGINT { $$=FIELD_TYPE_LONGLONG; }; real_type: REAL { $$= current_thd->sql_mode & MODE_REAL_AS_FLOAT ? FIELD_TYPE_FLOAT : FIELD_TYPE_DOUBLE; } | DOUBLE_SYM { $$=FIELD_TYPE_DOUBLE; } - | DOUBLE_SYM PRECISION { $$=FIELD_TYPE_DOUBLE; } + | DOUBLE_SYM PRECISION { $$=FIELD_TYPE_DOUBLE; }; 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 */ {} - | field_opt_list {} + | field_opt_list {}; field_opt_list: field_opt_list field_option {} - | field_option {} + | field_option {}; field_option: - UNSIGNED { Lex->type|= UNSIGNED_FLAG;} - | ZEROFILL { Lex->type|= UNSIGNED_FLAG | ZEROFILL_FLAG; } + SIGNED_SYM {} + | UNSIGNED { Lex->type|= UNSIGNED_FLAG;} + | ZEROFILL { Lex->type|= UNSIGNED_FLAG | ZEROFILL_FLAG; }; opt_len: /* empty */ { $$=(char*) 0; } /* use default length */ - | '(' NUM ')' { $$=$2.str; } + | '(' NUM ')' { $$=$2.str; }; opt_precision: /* empty */ {} - | '(' NUM ',' NUM ')' { Lex->length=$2.str; Lex->dec=$4.str; } + | precision {}; opt_attribute: /* empty */ {} - | opt_attribute_list {} + | opt_attribute_list {}; opt_attribute_list: opt_attribute_list attribute {} - | attribute + | attribute; attribute: NULL_SYM { Lex->type&= ~ NOT_NULL_FLAG; } @@ -972,40 +1069,40 @@ attribute: | AUTO_INC { Lex->type|= AUTO_INCREMENT_FLAG | NOT_NULL_FLAG; } | PRIMARY_SYM KEY_SYM { Lex->type|= PRI_KEY_FLAG | NOT_NULL_FLAG; } | UNIQUE_SYM { Lex->type|= UNIQUE_FLAG; } - | UNIQUE_SYM KEY_SYM { Lex->type|= UNIQUE_KEY_FLAG; } + | UNIQUE_SYM KEY_SYM { Lex->type|= UNIQUE_KEY_FLAG; }; opt_binary: /* empty */ {} - | BINARY { Lex->type|=BINARY_FLAG; } + | BINARY { Lex->type|=BINARY_FLAG; }; references: REFERENCES table_ident opt_on_delete {} | REFERENCES table_ident '(' key_list ')' opt_on_delete { Lex->col_list.empty(); /* Alloced by sql_alloc */ - } + }; opt_on_delete: /* empty */ {} - | opt_on_delete_list {} + | opt_on_delete_list {}; opt_on_delete_list: opt_on_delete_list opt_on_delete_item {} - | opt_on_delete_item {} + | opt_on_delete_item {}; opt_on_delete_item: ON DELETE_SYM delete_option {} | ON UPDATE_SYM delete_option {} | MATCH FULL {} - | MATCH PARTIAL {} + | MATCH PARTIAL {}; delete_option: RESTRICT {} | CASCADE {} | SET NULL_SYM {} | NO_SYM ACTION {} - | SET DEFAULT {} + | SET DEFAULT {}; key_type: opt_constraint PRIMARY_SYM KEY_SYM { $$= Key::PRIMARY; } @@ -1013,36 +1110,37 @@ key_type: | FULLTEXT_SYM { $$= Key::FULLTEXT; } | FULLTEXT_SYM key_or_index { $$= Key::FULLTEXT; } | opt_constraint UNIQUE_SYM { $$= Key::UNIQUE; } - | opt_constraint UNIQUE_SYM key_or_index { $$= Key::UNIQUE; } + | opt_constraint UNIQUE_SYM key_or_index { $$= Key::UNIQUE; }; key_or_index: KEY_SYM {} - | INDEX {} + | INDEX {}; keys_or_index: KEYS {} | INDEX {} + | INDEXES {}; opt_unique_or_fulltext: /* empty */ { $$= Key::MULTIPLE; } | UNIQUE_SYM { $$= Key::UNIQUE; } - | FULLTEXT_SYM { $$= Key::FULLTEXT; } + | FULLTEXT_SYM { $$= Key::FULLTEXT; }; key_list: key_list ',' key_part order_dir { Lex->col_list.push_back($3); } - | key_part order_dir { Lex->col_list.push_back($1); } + | key_part order_dir { Lex->col_list.push_back($1); }; key_part: ident { $$=new key_part_spec($1.str); } - | ident '(' NUM ')' { $$=new key_part_spec($1.str,(uint) atoi($3.str)); } + | ident '(' NUM ')' { $$=new key_part_spec($1.str,(uint) atoi($3.str)); }; opt_ident: /* empty */ { $$=(char*) 0; } /* Defaultlength */ - | field_ident { $$=$1.str; } + | field_ident { $$=$1.str; }; string_list: text_string { Lex->interval_list.push_back($1); } - | string_list ',' text_string { Lex->interval_list.push_back($3); } + | string_list ',' text_string { Lex->interval_list.push_back($3); }; /* ** Alter table @@ -1062,93 +1160,149 @@ 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->create_info.row_type= ROW_TYPE_NOT_USED; + lex->alter_keys_onoff=LEAVE_AS_IS; + lex->simple_alter=1; } - alter_list + alter_list; alter_list: | alter_list_item - | alter_list ',' alter_list_item + | 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 opt_place | 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; } + opt_place | 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)); } - | 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->alter_list.push_back(new Alter_column($3.str,(Item*) 0)); + lex->simple_alter=0; + } + | RENAME opt_to table_ident + { + LEX *lex=Lex; + lex->select->db=$3->db.str; + lex->name= $3->table.str; + lex->simple_alter=0; + } + | create_table_options { Lex->simple_alter=0; } + | order_clause { Lex->simple_alter=0; }; opt_column: /* empty */ {} - | COLUMN_SYM {} + | COLUMN_SYM {}; opt_ignore: /* empty */ { Lex->duplicates=DUP_ERROR; } - | IGNORE_SYM { Lex->duplicates=DUP_IGNORE; } + | IGNORE_SYM { Lex->duplicates=DUP_IGNORE; }; opt_restrict: /* empty */ {} | RESTRICT {} - | CASCADE {} + | CASCADE {}; opt_place: /* empty */ {} | AFTER_SYM ident { store_position_for_column($2.str); } - | FIRST_SYM { store_position_for_column(first_keyword); } + | FIRST_SYM { store_position_for_column(first_keyword); }; opt_to: /* empty */ {} | TO_SYM {} - | AS {} + | EQ {} + | AS {}; slave: - SLAVE START_SYM + SLAVE START_SYM slave_thread_opts { - Lex->sql_command = SQLCOM_SLAVE_START; - Lex->type = 0; + LEX *lex=Lex; + lex->sql_command = SQLCOM_SLAVE_START; + lex->type = 0; } | - SLAVE STOP_SYM + SLAVE STOP_SYM slave_thread_opts { - Lex->sql_command = SQLCOM_SLAVE_STOP; - Lex->type = 0; + LEX *lex=Lex; + lex->sql_command = SQLCOM_SLAVE_STOP; + lex->type = 0; }; +slave_thread_opts: slave_thread_opt + | slave_thread_opts ',' slave_thread_opt; + +slave_thread_opt: + /*empty*/ {} + | SQL_THREAD + { + Lex->slave_thd_opt|=SLAVE_SQL; + } + | IO_THREAD + { + Lex->slave_thd_opt|=SLAVE_IO; + }; + restore: RESTORE_SYM table_or_tables { @@ -1157,7 +1311,7 @@ restore: table_list FROM TEXT_STRING { Lex->backup_dir = $6.str; - } + }; backup: BACKUP_SYM table_or_tables { @@ -1166,75 +1320,89 @@ backup: table_list TO_SYM TEXT_STRING { Lex->backup_dir = $6.str; - } - + }; 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 - + table_list opt_mi_repair_type; -opt_mi_check_type: +opt_mi_repair_type: /* empty */ { Lex->check_opt.flags = T_MEDIUM; } - | TYPE_SYM EQ mi_check_types {} - | mi_check_types {} + | mi_repair_types {}; -mi_check_types: - mi_check_type {} - | mi_check_type mi_check_types {} +mi_repair_types: + mi_repair_type {} + | mi_repair_type mi_repair_types {}; -mi_check_type: - QUICK { Lex->check_opt.quick = 1; } - | FAST_SYM { Lex->check_opt.flags|= T_FAST; } - | MEDIUM_SYM { Lex->check_opt.flags|= T_MEDIUM; } +mi_repair_type: + QUICK { Lex->check_opt.flags|= T_QUICK; } | EXTENDED_SYM { Lex->check_opt.flags|= T_EXTEND; } - | CHANGED { Lex->check_opt.flags|= T_CHECK_ONLY_CHANGED; } + | USE_FRM { Lex->check_opt.sql_flags|= TT_USEFRM; }; 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 + 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 + table_list opt_mi_check_type; + +opt_mi_check_type: + /* empty */ { Lex->check_opt.flags = T_MEDIUM; } + | mi_check_types {}; + +mi_check_types: + mi_check_type {} + | mi_check_type mi_check_types {}; + +mi_check_type: + QUICK { Lex->check_opt.flags|= T_QUICK; } + | FAST_SYM { Lex->check_opt.flags|= T_FAST; } + | MEDIUM_SYM { Lex->check_opt.flags|= T_MEDIUM; } + | EXTENDED_SYM { Lex->check_opt.flags|= T_EXTEND; } + | CHANGED { Lex->check_opt.flags|= T_CHECK_ONLY_CHANGED; }; 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 + table_list opt_mi_check_type; rename: RENAME table_or_tables { Lex->sql_command=SQLCOM_RENAME_TABLE; } - table_to_table_list + table_to_table_list; table_to_table_list: table_to_table - | table_to_table_list ',' table_to_table + | table_to_table_list ',' table_to_table; table_to_table: table_ident TO_SYM table_ident { if (!add_table_to_list($1,NULL,1,TL_IGNORE) || !add_table_to_list($3,NULL,1,TL_IGNORE)) YYABORT; - } + }; /* Select : retrieve data from table @@ -1242,48 +1410,58 @@ table_to_table: select: - SELECT_SYM + select_init { Lex->sql_command=SQLCOM_SELECT; }; + +select_init: + SELECT_SYM select_part2 { Select->braces=false; } union + | + '(' SELECT_SYM select_part2 ')' { Select->braces=true;} union_opt; + + +select_part2: { LEX *lex=Lex; - lex->sql_command= SQLCOM_SELECT; lex->lock_option=TL_READ; - mysql_init_select(lex); + mysql_init_select(lex); } - select_options select_item_list select_into select_lock_type + select_options select_item_list select_into select_lock_type; select_into: - /* empty */ + limit_clause {} | select_from | opt_into select_from - | select_from opt_into + | select_from opt_into; select_from: - FROM join_table_list where_clause group_clause having_clause opt_order_clause limit_clause procedure_clause + FROM join_table_list where_clause group_clause having_clause opt_order_clause limit_clause procedure_clause; select_options: /* empty*/ - | select_option_list + | select_option_list; select_option_list: select_option_list select_option - | select_option + | select_option; select_option: - STRAIGHT_JOIN { Lex->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; } - | ALL {} + STRAIGHT_JOIN { Select->options|= SELECT_STRAIGHT_JOIN; } + | HIGH_PRIORITY { if (Select != &Lex->select_lex) YYABORT; Lex->lock_option= TL_READ_HIGH_PRIORITY; } + | DISTINCT { Select->options|= SELECT_DISTINCT; } + | SQL_SMALL_RESULT { Select->options|= SELECT_SMALL_RESULT; } + | SQL_BIG_RESULT { Select->options|= SELECT_BIG_RESULT; } + | SQL_BUFFER_RESULT { if (Select != &Lex->select_lex) YYABORT; Select->options|= OPTION_BUFFER_RESULT; } + | SQL_CALC_FOUND_ROWS { if (Select != &Lex->select_lex) YYABORT; Select->options|= OPTION_FOUND_ROWS; } + | SQL_NO_CACHE_SYM { if (Select != &Lex->select_lex) YYABORT; current_thd->safe_to_cache_query=0; } + | SQL_CACHE_SYM { if (Select != &Lex->select_lex) YYABORT; Select->options |= OPTION_TO_QUERY_CACHE; } + | ALL {}; select_lock_type: /* empty */ | FOR_SYM UPDATE_SYM - { Lex->lock_option= TL_WRITE; } + { if (Select != &Lex->select_lex) YYABORT; Lex->lock_option= TL_WRITE; current_thd->safe_to_cache_query=0; } | LOCK_SYM IN_SYM SHARE_SYM MODE_SYM - { Lex->lock_option= TL_READ_WITH_SHARED_LOCKS; } + { if (Select != &Lex->select_lex) YYABORT; Lex->lock_option= TL_READ_WITH_SHARED_LOCKS; current_thd->safe_to_cache_query=0; }; select_item_list: select_item_list ',' select_item @@ -1292,7 +1470,7 @@ select_item_list: { if (add_item_to_list(new Item_field(NULL,NULL,"*"))) YYABORT; - } + }; select_item: @@ -1304,32 +1482,32 @@ select_item: $2->set_name($4.str); else if (!$2->name) $2->set_name($1,(uint) ($3 - $1)); - } + }; remember_name: - { $$=(char*) Lex->tok_start; } + { $$=(char*) Lex->tok_start; }; remember_end: - { $$=(char*) Lex->tok_end; } + { $$=(char*) Lex->tok_end; }; select_item2: table_wild { $$=$1; } /* table.* */ - | expr { $$=$1; } + | expr { $$=$1; }; select_alias: { $$.str=0;} | AS ident { $$=$2; } | AS TEXT_STRING { $$=$2; } | ident { $$=$1; } - | TEXT_STRING { $$=$1; } + | TEXT_STRING { $$=$1; }; optional_braces: /* empty */ {} - | '(' ')' {} + | '(' ')' {}; /* all possible expressions */ expr: expr_expr {$$ = $1; } - | simple_expr {$$ = $1; } + | simple_expr {$$ = $1; }; /* expressions that begin with 'expr' */ expr_expr: @@ -1369,7 +1547,7 @@ expr_expr: | expr '+' INTERVAL_SYM expr interval { $$= new Item_date_add_interval($1,$4,$5,0); } | expr '-' INTERVAL_SYM expr interval - { $$= new Item_date_add_interval($1,$4,$5,1); } + { $$= new Item_date_add_interval($1,$4,$5,1); }; /* expressions that begin with 'expr' that do NOT follow IN_SYM */ no_in_expr: @@ -1406,7 +1584,7 @@ no_in_expr: { $$= new Item_date_add_interval($1,$4,$5,0); } | no_in_expr '-' INTERVAL_SYM expr interval { $$= new Item_date_add_interval($1,$4,$5,1); } - | simple_expr + | simple_expr; /* expressions that begin with 'expr' that does NOT follow AND */ no_and_expr: @@ -1446,14 +1624,23 @@ no_and_expr: { $$= new Item_date_add_interval($1,$4,$5,0); } | no_and_expr '-' INTERVAL_SYM expr interval { $$= new Item_date_add_interval($1,$4,$5,1); } - | simple_expr + | simple_expr; simple_expr: simple_ident | literal - | '@' ident_or_text SET_VAR expr { $$= new Item_func_set_user_var($2,$4); } - | '@' ident_or_text { $$= new Item_func_get_user_var($2); } - | '@' '@' ident_or_text { if (!($$= get_system_var($3))) YYABORT; } + | '@' ident_or_text SET_VAR expr + { $$= new Item_func_set_user_var($2,$4); + current_thd->safe_to_cache_query=0; + } + | '@' ident_or_text + { $$= new Item_func_get_user_var($2); + current_thd->safe_to_cache_query=0; + } + | '@' '@' ident_or_text + { if (!($$= get_system_var($3))) YYABORT; + current_thd->safe_to_cache_query=0; + } | sum_expr | '-' expr %prec NEG { $$= new Item_func_neg($2); } | '~' expr %prec NEG { $$= new Item_func_bit_neg($2); } @@ -1461,15 +1648,17 @@ simple_expr: | '!' expr %prec NEG { $$= new Item_func_not($2); } | '(' expr ')' { $$= $2; } | '{' ident expr '}' { $$= $3; } - | MATCH '(' ident_list ')' AGAINST '(' expr ')' - { Lex->ftfunc_list.push_back( - (Item_func_match *)($$=new Item_func_match(*$3,$7))); } - | MATCH ident_list AGAINST '(' expr ')' - { Lex->ftfunc_list.push_back( - (Item_func_match *)($$=new Item_func_match(*$2,$5))); } + | MATCH ident_list_arg AGAINST '(' expr ')' + { Select->ftfunc_list.push_back((Item_func_match *) + ($$=new Item_func_match_nl(*$2,$5))); } + | MATCH ident_list_arg AGAINST '(' expr IN_SYM BOOLEAN_SYM MODE_SYM ')' + { Select->ftfunc_list.push_back((Item_func_match *) + ($$=new Item_func_match_bool(*$2,$5))); } | BINARY expr %prec NEG { $$= new Item_func_binary($2); } + | CAST_SYM '(' expr AS cast_type ')' { $$= create_func_cast($3, $5); } | CASE_SYM opt_expr WHEN_SYM when_list opt_else END { $$= new Item_func_case(* $4, $2, $5 ); } + | CONVERT_SYM '(' expr ',' cast_type ')' { $$= create_func_cast($3, $5); } | FUNC_ARG0 '(' ')' { $$= ((Item*(*)(void))($1.symbol->create_func))();} | FUNC_ARG1 '(' expr ')' @@ -1491,27 +1680,45 @@ simple_expr: | CONCAT_WS '(' expr ',' expr_list ')' { $$= new Item_func_concat_ws($3, *$5); } | CURDATE optional_braces - { $$= new Item_func_curdate(); } + { $$= new Item_func_curdate(); current_thd->safe_to_cache_query=0; } | CURTIME optional_braces - { $$= new Item_func_curtime(); } + { $$= new Item_func_curtime(); current_thd->safe_to_cache_query=0; } | CURTIME '(' expr ')' - { $$= new Item_func_curtime($3); } + { + $$= new Item_func_curtime($3); + current_thd->safe_to_cache_query=0; + } | DATE_ADD_INTERVAL '(' expr ',' INTERVAL_SYM expr interval ')' { $$= new Item_date_add_interval($3,$6,$7,0); } | DATE_SUB_INTERVAL '(' expr ',' INTERVAL_SYM expr interval ')' { $$= new Item_date_add_interval($3,$6,$7,1); } | DATABASE '(' ')' - { $$= new Item_func_database(); } + { + $$= new Item_func_database(); + current_thd->safe_to_cache_query=0; + } | ELT_FUNC '(' expr ',' expr_list ')' { $$= new Item_func_elt($3, *$5); } | MAKE_SET_SYM '(' expr ',' expr_list ')' { $$= new Item_func_make_set($3, *$5); } - | ENCRYPT '(' expr ')' { $$= new Item_func_encrypt($3); } + | ENCRYPT '(' expr ')' + { + $$= new Item_func_encrypt($3); + current_thd->safe_to_cache_query=0; + } | ENCRYPT '(' expr ',' expr ')' { $$= new Item_func_encrypt($3,$5); } | DECODE_SYM '(' expr ',' TEXT_STRING ')' { $$= new Item_func_decode($3,$5.str); } | ENCODE_SYM '(' expr ',' TEXT_STRING ')' { $$= new Item_func_encode($3,$5.str); } + | DES_DECRYPT_SYM '(' expr ')' + { $$= new Item_func_des_decrypt($3); } + | DES_DECRYPT_SYM '(' expr ',' expr ')' + { $$= new Item_func_des_decrypt($3,$5); } + | DES_ENCRYPT_SYM '(' expr ')' + { $$= new Item_func_des_encrypt($3); } + | DES_ENCRYPT_SYM '(' expr ',' expr ')' + { $$= new Item_func_des_encrypt($3,$5); } | EXPORT_SET '(' expr ',' expr ',' expr ')' { $$= new Item_func_export_set($3, $5, $7); } | EXPORT_SET '(' expr ',' expr ',' expr ',' expr ')' @@ -1524,7 +1731,7 @@ simple_expr: { $$= new Item_func_from_unixtime($3); } | FROM_UNIXTIME '(' expr ',' expr ')' { - $$= new Item_func_date_format(new Item_func_from_unixtime($3),$5,0); + $$= new Item_func_date_format (new Item_func_from_unixtime($3),$5,0); } | FIELD_FUNC '(' expr ',' expr_list ')' { $$= new Item_func_field($3, *$5); } @@ -1543,10 +1750,12 @@ simple_expr: { $$= new Item_int((char*) "last_insert_id()", current_thd->insert_id(),21); + current_thd->safe_to_cache_query=0; } | LAST_INSERT_ID '(' expr ')' { $$= new Item_func_set_last_insert_id($3); + current_thd->safe_to_cache_query=0; } | LEFT '(' expr ',' expr ')' { $$= new Item_func_left($3,$5); } @@ -1563,14 +1772,19 @@ simple_expr: | MONTH_SYM '(' expr ')' { $$= new Item_func_month($3); } | NOW_SYM optional_braces - { $$= new Item_func_now(); } + { $$= new Item_func_now(); current_thd->safe_to_cache_query=0;} | NOW_SYM '(' expr ')' - { $$= new Item_func_now($3); } - | PASSWORD '(' expr ')' { $$= new Item_func_password($3); } + { $$= new Item_func_now($3); current_thd->safe_to_cache_query=0;} + | PASSWORD '(' expr ')' + { + $$= new Item_func_password($3); + } | POSITION_SYM '(' no_in_expr IN_SYM expr ')' { $$ = new Item_func_locate($5,$3); } - | RAND '(' expr ')' { $$= new Item_func_rand($3); } - | RAND '(' ')' { $$= new Item_func_rand(); } + | RAND '(' expr ')' + { $$= new Item_func_rand($3); current_thd->safe_to_cache_query=0;} + | RAND '(' ')' + { $$= new Item_func_rand(); current_thd->safe_to_cache_query=0;} | REPLACE '(' expr ',' expr ',' expr ')' { $$= new Item_func_replace($3,$5,$7); } | RIGHT '(' expr ',' expr ')' @@ -1608,6 +1822,7 @@ simple_expr: $$ = new Item_sum_udf_str($1, *$3); else $$ = new Item_sum_udf_str($1); + current_thd->safe_to_cache_query=0; } | UDA_FLOAT_SUM '(' udf_expr_list ')' { @@ -1615,6 +1830,7 @@ simple_expr: $$ = new Item_sum_udf_float($1, *$3); else $$ = new Item_sum_udf_float($1); + current_thd->safe_to_cache_query=0; } | UDA_INT_SUM '(' udf_expr_list ')' { @@ -1629,6 +1845,7 @@ simple_expr: $$ = new Item_func_udf_str($1, *$3); else $$ = new Item_func_udf_str($1); + current_thd->safe_to_cache_query=0; } | UDF_FLOAT_FUNC '(' udf_expr_list ')' { @@ -1636,6 +1853,7 @@ simple_expr: $$ = new Item_func_udf_float($1, *$3); else $$ = new Item_func_udf_float($1); + current_thd->safe_to_cache_query=0; } | UDF_INT_FUNC '(' udf_expr_list ')' { @@ -1643,15 +1861,21 @@ simple_expr: $$ = new Item_func_udf_int($1, *$3); else $$ = new Item_func_udf_int($1); + current_thd->safe_to_cache_query=0; } | UNIQUE_USERS '(' text_literal ',' NUM ',' NUM ',' expr_list ')' - { $$= new Item_func_unique_users($3,atoi($5.str),atoi($7.str), * $9); } + { + $$= new Item_func_unique_users($3,atoi($5.str),atoi($7.str), * $9); + } | UNIX_TIMESTAMP '(' ')' - { $$= new Item_func_unix_timestamp(); } + { + $$= new Item_func_unix_timestamp(); + current_thd->safe_to_cache_query=0; + } | UNIX_TIMESTAMP '(' expr ')' { $$= new Item_func_unix_timestamp($3); } | USER '(' ')' - { $$= new Item_func_user(); } + { $$= new Item_func_user(); current_thd->safe_to_cache_query=0; } | WEEK_SYM '(' expr ')' { $$= new Item_func_week($3,new Item_int((char*) "0",0,1)); } | WEEK_SYM '(' expr ',' expr ')' @@ -1663,13 +1887,16 @@ simple_expr: | YEARWEEK '(' expr ',' expr ')' { $$= new Item_func_yearweek($3, $5); } | BENCHMARK_SYM '(' ULONG_NUM ',' expr ')' - { $$=new Item_func_benchmark($3,$5); } + { + $$=new Item_func_benchmark($3,$5); + current_thd->safe_to_cache_query=0; + } | EXTRACT_SYM '(' interval FROM expr ')' - { $$=new Item_extract( $3, $5); } + { $$=new Item_extract( $3, $5); }; udf_expr_list: /* empty */ { $$= NULL; } - | expr_list { $$= $1;} + | expr_list { $$= $1;}; sum_expr: AVG_SYM '(' in_sum_expr ')' @@ -1693,62 +1920,78 @@ sum_expr: | STD_SYM '(' in_sum_expr ')' { $$=new Item_sum_std($3); } | SUM_SYM '(' in_sum_expr ')' - { $$=new Item_sum_sum($3); } + { $$=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; - } + }; + +cast_type: + BINARY { $$=ITEM_CAST_BINARY; } + | SIGNED_SYM { $$=ITEM_CAST_SIGNED_INT; } + | SIGNED_SYM INT_SYM { $$=ITEM_CAST_SIGNED_INT; } + | UNSIGNED { $$=ITEM_CAST_UNSIGNED_INT; } + | UNSIGNED INT_SYM { $$=ITEM_CAST_UNSIGNED_INT; } + | DATE_SYM { $$=ITEM_CAST_DATE; } + | TIME_SYM { $$=ITEM_CAST_TIME; } + | DATETIME { $$=ITEM_CAST_DATETIME; }; 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_arg: + ident_list { $$= $1; } + | '(' ident_list ')' { $$= $2; }; 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; } - | expr { $$= $1; } + | expr { $$= $1; }; opt_else: /* empty */ { $$= NULL; } - | ELSE expr { $$= $2; } + | 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: /* empty */ { $$=new Item_string(" ",1); } - | expr { $$=$1; } + | expr { $$=$1; }; join_table_list: '(' join_table_list ')' { $$=$2; } @@ -1758,15 +2001,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 @@ -1774,61 +2023,81 @@ 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 { add_join_natural($6,$1); $1->outer_join|=JOIN_TYPE_RIGHT; $$=$1; } | join_table_list NATURAL JOIN_SYM join_table - { add_join_natural($1,$4); $$=$4; } + { add_join_natural($1,$4); $$=$4; }; normal_join: ',' {} | JOIN_SYM {} - | CROSS JOIN_SYM {} + | 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; } + { add_join_on($7,$9); $7->outer_join|=JOIN_TYPE_LEFT; $$=$7; }; opt_outer: /* empty */ {} - | OUTER {} + | 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; - } + }; interval: DAY_HOUR_SYM { $$=INTERVAL_DAY_HOUR; } @@ -1843,31 +2112,34 @@ interval: | MONTH_SYM { $$=INTERVAL_MONTH; } | SECOND_SYM { $$=INTERVAL_SECOND; } | YEAR_MONTH_SYM { $$=INTERVAL_YEAR_MONTH; } - | YEAR_SYM { $$=INTERVAL_YEAR; } + | YEAR_SYM { $$=INTERVAL_YEAR; }; table_alias: /* empty */ | AS - | EQ + | EQ; opt_table_alias: /* empty */ { $$=0; } | table_alias ident - { $$= (LEX_STRING*) sql_memdup(&$2,sizeof(LEX_STRING)); } + { $$= (LEX_STRING*) sql_memdup(&$2,sizeof(LEX_STRING)); }; 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; } - | /* empty */ { $$= (char*) "\\"; } + | /* empty */ { $$= (char*) "\\"; }; /* @@ -1876,13 +2148,13 @@ opt_escape: group_clause: /* empty */ - | GROUP BY group_list + | GROUP BY group_list; group_list: group_list ',' order_ident order_dir { if (add_group_to_list($3,(bool) $4)) YYABORT; } | order_ident order_dir - { if (add_group_to_list($1,(bool) $2)) YYABORT; } + { if (add_group_to_list($1,(bool) $2)) YYABORT; }; /* Order by statement in select @@ -1890,52 +2162,66 @@ group_list: opt_order_clause: /* empty */ - | order_clause + | order_clause; order_clause: - ORDER_SYM BY order_list + ORDER_SYM BY + { + LEX *lex=Lex; + if (lex->sql_command == SQLCOM_MULTI_UPDATE) + YYABORT; + lex->select->sort_default=1; + } order_list; order_list: order_list ',' order_ident order_dir { if (add_order_to_list($3,(bool) $4)) YYABORT; } | order_ident order_dir - { if (add_order_to_list($1,(bool) $2)) YYABORT; } + { if (add_order_to_list($1,(bool) $2)) YYABORT; }; order_dir: /* empty */ { $$ = 1; } | ASC { $$ =1; } - | DESC { $$ =0; } + | DESC { $$ =0; }; limit_clause: - /* empty */ - { - Lex->select_limit= current_thd->default_select_limit; - Lex->offset_limit= 0L; - } + /* empty */ {} | 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; + LEX *lex=Lex; + if (lex->sql_command == SQLCOM_MULTI_UPDATE) + YYABORT; + lex->select->select_limit= HA_POS_ERROR; } - | LIMIT ULONGLONG_NUM - { Lex->select_limit= (ha_rows) $2; } + | LIMIT ulonglong_num + { Select->select_limit= (ha_rows) $2; }; ULONG_NUM: - 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); } - | FLOAT_NUM { $$= strtoull($1.str,NULL,10); } + NUM { $$= strtoul($1.str,NULL,10); } + | ULONGLONG_NUM { $$= (ulong) strtoull($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); } + | ULONGLONG_NUM { $$= strtoull($1.str,NULL,10); } + | LONG_NUM { $$= (ulonglong) strtoul($1.str,NULL,10); } + | REAL_NUM { $$= strtoull($1.str,NULL,10); } + | FLOAT_NUM { $$= strtoull($1.str,NULL,10); }; procedure_clause: /* empty */ @@ -1947,17 +2233,18 @@ procedure_clause: lex->proc_list.next= (byte**) &lex->proc_list.first; if (add_proc_to_list(new Item_field(NULL,NULL,$2.str))) YYABORT; + current_thd->safe_to_cache_query=0; } - '(' procedure_list ')' + '(' procedure_list ')'; procedure_list: /* empty */ {} - | procedure_list2 {} + | procedure_list2 {}; procedure_list2: procedure_list2 ',' procedure_item - | procedure_item + | procedure_item; procedure_item: remember_name expr @@ -1966,7 +2253,7 @@ procedure_item: YYABORT; if (!$2->name) $2->set_name($1,(uint) ((char*) Lex->tok_end - $1)); - } + }; opt_into: INTO OUTFILE TEXT_STRING @@ -1979,7 +2266,7 @@ opt_into: { if (!(Lex->exchange= new sql_exchange($3.str,1))) YYABORT; - } + }; /* DO statement @@ -1992,7 +2279,7 @@ do: DO_SYM if (!(lex->insert_list = new List_item)) YYABORT; } - values + values; /* Drop : delete tables or index */ @@ -2000,93 +2287,105 @@ do: DO_SYM 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; } + { if (!add_table_to_list($1,NULL,1)) YYABORT; }; if_exists: /* empty */ { $$=0; } - | IF EXISTS { $$= 1; } + | IF EXISTS { $$= 1; }; /* ** Insert : add new data to table */ insert: - INSERT { Lex->sql_command = SQLCOM_INSERT; } insert_lock_option opt_ignore insert2 insert_field_spec + INSERT { Lex->sql_command = SQLCOM_INSERT; } insert_lock_option opt_ignore insert2 insert_field_spec; replace: - REPLACE { Lex->sql_command = SQLCOM_REPLACE; } replace_lock_option insert2 insert_field_spec + REPLACE + { + LEX *lex=Lex; + lex->sql_command = SQLCOM_REPLACE; + lex->duplicates= DUP_REPLACE; + } + replace_lock_option insert2 insert_field_spec; insert_lock_option: /* empty */ { Lex->lock_option= TL_WRITE_CONCURRENT_INSERT; } | LOW_PRIORITY { Lex->lock_option= TL_WRITE_LOW_PRIORITY; } | DELAYED_SYM { Lex->lock_option= TL_WRITE_DELAYED; } - | HIGH_PRIORITY { Lex->lock_option= TL_WRITE; } + | HIGH_PRIORITY { Lex->lock_option= TL_WRITE; }; replace_lock_option: opt_low_priority {} - | DELAYED_SYM { Lex->lock_option= TL_WRITE_DELAYED; } + | DELAYED_SYM { Lex->lock_option= TL_WRITE_DELAYED; }; insert2: INTO insert_table {} - | insert_table {} + | 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 + ident_eq_list; opt_field_spec: /* empty */ { } | '(' fields ')' { } - | '(' ')' { } + | '(' ')' { }; fields: fields ',' insert_ident { Lex->field_list.push_back($3); } - | insert_ident { Lex->field_list.push_back($1); } + | insert_ident { Lex->field_list.push_back($1); }; insert_values: VALUES values_list {} @@ -2098,27 +2397,29 @@ insert_values: lex->lock_option= (using_update_log) ? TL_READ_NO_INSERT : TL_READ; mysql_init_select(lex); } - select_options select_item_list select_from select_lock_type {} + select_options select_item_list select_from select_lock_type + union {}; values_list: values_list ',' no_braces - | no_braces + | no_braces; ident_eq_list: ident_eq_list ',' ident_eq_value | - ident_eq_value + ident_eq_value; 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; - } + }; equal: EQ {} - | SET_VAR {} + | SET_VAR {}; no_braces: '(' @@ -2128,13 +2429,14 @@ 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; - } + }; opt_values: /* empty */ {} - | values + | values; values: values ',' expr @@ -2146,13 +2448,20 @@ values: { if (Lex->insert_list->push_back($1)) YYABORT; - } + }; /* 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 + { + 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; + } + opt_low_priority opt_ignore join_table_list SET update_list where_clause opt_order_clause delete_limit_clause; update_list: update_list ',' simple_ident equal expr @@ -2164,61 +2473,107 @@ update_list: { if (add_item_to_list($1) || add_value_to_list($3)) YYABORT; - } + }; opt_low_priority: /* empty */ { Lex->lock_option= current_thd->update_lock_default; } - | LOW_PRIORITY { Lex->lock_option= TL_WRITE_LOW_PRIORITY; } + | LOW_PRIORITY { Lex->lock_option= TL_WRITE_LOW_PRIORITY; }; /* Delete rows from a table */ 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 + { mysql_init_multi_delete(Lex); } + FROM join_table_list where_clause + | FROM table_wild_list + { mysql_init_multi_delete(Lex); } + USING 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 */ {} - | opt_delete_option opt_delete_options {} + /* empty */ {} + | opt_delete_option opt_delete_options {}; opt_delete_option: - QUICK { Lex->options|= OPTION_QUICK; } - | LOW_PRIORITY { Lex->lock_option= TL_WRITE_LOW_PRIORITY; } + 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 - + | TABLE_SYM; + /* Show things */ -show: SHOW { Lex->wild=0;} show_param +show: SHOW { Lex->wild=0;} show_param; 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) @@ -2226,10 +2581,31 @@ show_param: if (!add_table_to_list($4,NULL,0)) YYABORT; } + | NEW_SYM MASTER_SYM FOR_SYM SLAVE WITH MASTER_LOG_FILE_SYM EQ + TEXT_STRING AND MASTER_LOG_POS_SYM EQ ulonglong_num + AND MASTER_SERVER_ID_SYM EQ + ULONG_NUM + { + Lex->sql_command = SQLCOM_SHOW_NEW_MASTER; + Lex->mi.log_file_name = $8.str; + Lex->mi.pos = $12; + Lex->mi.server_id = $16; + } | 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 + { + LEX *lex=Lex; + lex->sql_command = SQLCOM_SHOW_BINLOG_EVENTS; + lex->select->select_limit= lex->thd->default_select_limit; + lex->select->offset_limit= 0L; + } limit_clause | keys_or_index FROM table_ident opt_db { Lex->sql_command= SQLCOM_SHOW_KEYS; @@ -2247,8 +2623,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; @@ -2262,114 +2642,150 @@ show_param: | SLAVE STATUS_SYM { Lex->sql_command = SQLCOM_SHOW_SLAVE_STAT; - } + }; opt_db: /* empty */ { $$= 0; } - | FROM ident { $$= $2.str; } + | from_or_in ident { $$= $2.str; }; wild: /* empty */ - | LIKE text_string { Lex->wild= $2; } + | LIKE text_string { Lex->wild= $2; }; opt_full: /* empty */ { Lex->verbose=0; } - | FULL { Lex->verbose=1; } + | 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 + { Lex->select_lex.options|= SELECT_DESCRIBE; }; describe_command: DESC - | DESCRIBE + | DESCRIBE; opt_describe_column: /* empty */ {} | text_string { Lex->wild= $1; } - | ident { Lex->wild= new String((const char*) $1.str,$1.length); } + | ident + { Lex->wild= new String((const char*) $1.str,$1.length); }; /* 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 - | flush_option + | flush_option; flush_option: table_or_tables { Lex->type|= REFRESH_TABLES; } opt_table_list | TABLES WITH READ_SYM LOCK_SYM { Lex->type|= REFRESH_TABLES | REFRESH_READ_LOCK; } + | QUERY_SYM CACHE_SYM { Lex->type|= REFRESH_QUERY_CACHE_FREE; } | HOSTS_SYM { Lex->type|= REFRESH_HOSTS; } | PRIVILEGES { Lex->type|= REFRESH_GRANT; } | LOGS_SYM { Lex->type|= REFRESH_LOG; } | STATUS_SYM { Lex->type|= REFRESH_STATUS; } | SLAVE { Lex->type|= REFRESH_SLAVE; } | MASTER_SYM { Lex->type|= REFRESH_MASTER; } + | DES_KEY_FILE { Lex->type|= REFRESH_DES_KEY_FILE; }; opt_table_list: /* empty */ {} - | 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 + | reset_option; reset_option: - SLAVE { Lex->type|= REFRESH_SLAVE; } - | MASTER_SYM { Lex->type|= REFRESH_MASTER; } + SLAVE { Lex->type|= REFRESH_SLAVE; } + | MASTER_SYM { Lex->type|= REFRESH_MASTER; } + | QUERY_SYM CACHE_SYM { Lex->type|= REFRESH_QUERY_CACHE;}; 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; - } + } ; /* kill threads */ kill: KILL_SYM expr { - if ($2->fix_fields(current_thd,0)) - { - send_error(¤t_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 *lex= Lex; + LEX *lex=Lex; lex->sql_command= SQLCOM_LOAD; lex->local_file= $4; - if (!(Lex->exchange= new sql_exchange($6.str,0))) + 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 @@ -2385,60 +2801,69 @@ 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;} - | LOCAL_SYM { $$=1;} + | LOCAL_SYM { $$=1;}; load_data_lock: /* empty */ { Lex->lock_option= current_thd->update_lock_default; } | CONCURRENT { Lex->lock_option= TL_WRITE_CONCURRENT_INSERT ; } - | LOW_PRIORITY { Lex->lock_option= TL_WRITE_LOW_PRIORITY; } + | LOW_PRIORITY { Lex->lock_option= TL_WRITE_LOW_PRIORITY; }; opt_duplicate: /* empty */ { Lex->duplicates=DUP_ERROR; } | REPLACE { Lex->duplicates=DUP_REPLACE; } - | IGNORE_SYM { Lex->duplicates=DUP_IGNORE; } + | IGNORE_SYM { Lex->duplicates=DUP_IGNORE; }; opt_field_term: /* empty */ - | COLUMNS field_term_list + | COLUMNS field_term_list; field_term_list: field_term_list field_term - | field_term + | field_term; 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;} + | ESCAPED BY text_string { Lex->exchange->escaped= $3;}; opt_line_term: /* empty */ - | LINES line_term_list + | LINES line_term_list; line_term_list: line_term_list line_term - | line_term + | line_term; line_term: TERMINATED BY text_string { Lex->exchange->line_term= $3;} - | STARTING BY text_string { Lex->exchange->line_start= $3;} + | STARTING BY text_string { Lex->exchange->line_start= $3;}; opt_ignore_lines: /* empty */ | IGNORE_SYM NUM LINES - { Lex->exchange->skip_lines=atol($2.str); } + { Lex->exchange->skip_lines=atol($2.str); }; /* Common definitions */ text_literal: TEXT_STRING { $$ = new Item_string($1.str,$1.length); } | text_literal TEXT_STRING - { ((Item_string*) $1)->append($2.str,$2.length); } + { ((Item_string*) $1)->append($2.str,$2.length); }; text_string: TEXT_STRING { $$= new String($1.str,$1.length); } @@ -2446,20 +2871,21 @@ text_string: { Item *tmp = new Item_varbinary($1.str,$1.length); $$= tmp ? tmp->val_str((String*) 0) : (String*) 0; - } + }; literal: text_literal { $$ = $1; } | NUM { $$ = new Item_int($1.str, (longlong) atol($1.str),$1.length); } | LONG_NUM { $$ = new Item_int($1.str); } + | ULONGLONG_NUM { $$ = new Item_uint($1.str, $1.length); } | REAL_NUM { $$ = new Item_real($1.str, $1.length); } | FLOAT_NUM { $$ = new Item_float($1.str, $1.length); } | NULL_SYM { $$ = new Item_null(); Lex->next_state=STATE_OPERATOR_OR_IDENT;} - | HEX_NUM { $$ = new Item_varbinary($1.str,$1.length); } + | HEX_NUM { $$ = new Item_varbinary($1.str,$1.length);} | DATE_SYM text_literal { $$ = $2; } | TIME_SYM text_literal { $$ = $2; } - | TIMESTAMP text_literal { $$ = $2; } + | TIMESTAMP text_literal { $$ = $2; }; /********************************************************************** ** Createing different items. @@ -2467,51 +2893,66 @@ literal: insert_ident: simple_ident { $$=$1; } - | table_wild { $$=$1; } + | table_wild { $$=$1; }; table_wild: ident '.' '*' { $$ = new Item_field(NullS,$1.str,"*"); } | ident '.' ident '.' '*' - { $$ = new Item_field((current_thd->client_capabilities & CLIENT_NO_SCHEMA ? NullS : $1.str),$3.str,"*"); } + { $$ = new Item_field((current_thd->client_capabilities & + CLIENT_NO_SCHEMA ? NullS : $1.str),$3.str,"*"); }; order_ident: - expr { $$=$1; } + expr { $$=$1; }; 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: ident { $$=$1;} | ident '.' ident { $$=$3;} /* Skipp schema name in create*/ - | '.' ident { $$=$2;} /* For Delphi */ + | '.' ident { $$=$2;} /* For Delphi */; table_ident: ident { $$=new Table_ident($1); } | ident '.' ident { $$=new Table_ident($1,$3,0);} - | '.' ident { $$=new Table_ident($2);} /* For Delphi */ + | '.' ident { $$=new Table_ident($2);} + /* For Delphi */; 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: ident { $$=$1;} | TEXT_STRING { $$=$1;} - | LEX_HOSTNAME { $$=$1;} + | LEX_HOSTNAME { $$=$1;}; user: ident_or_text @@ -2525,7 +2966,7 @@ user: if (!($$=(LEX_USER*) sql_alloc(sizeof(st_lex_user)))) YYABORT; $$->user = $1; $$->host=$3; - } + }; /* Keyword that we allow for identifiers */ @@ -2541,14 +2982,19 @@ keyword: | BACKUP_SYM {} | BEGIN_SYM {} | BERKELEY_DB_SYM {} + | BINLOG_SYM {} | BIT_SYM {} | BOOL_SYM {} + | BOOLEAN_SYM {} + | CACHE_SYM {} | CHANGED {} | CHECKSUM_SYM {} | CHECK_SYM {} + | CIPHER_SYM {} + | CLOSE_SYM {} | COMMENT_SYM {} - | COMMIT_SYM {} | COMMITTED_SYM {} + | COMMIT_SYM {} | COMPRESSED_SYM {} | CONCURRENT {} | DATA_SYM {} @@ -2556,29 +3002,40 @@ keyword: | DATE_SYM {} | DAY_SYM {} | DELAY_KEY_WRITE_SYM {} + | DEMAND_SYM {} + | DES_KEY_FILE {} + | DIRECTORY_SYM {} | DO_SYM {} | DUMPFILE {} | DYNAMIC_SYM {} | END {} | ENUM {} | ESCAPE_SYM {} + | EVENTS_SYM {} | EXTENDED_SYM {} | FAST_SYM {} + | DISABLE_SYM {} + | ENABLE_SYM {} | FULL {} | FILE_SYM {} | FIRST_SYM {} | FIXED_SYM {} | FLUSH_SYM {} | GRANTS {} - | GEMINI_SYM {} | GLOBAL_SYM {} | HEAP_SYM {} + | HANDLER_SYM {} | HOSTS_SYM {} | HOUR_SYM {} | IDENTIFIED_SYM {} + | INDEXES {} | ISOLATION {} | ISAM_SYM {} + | ISSUER_SYM {} | INNOBASE_SYM {} + | INSERT_METHOD {} + | IO_THREAD {} + | LAST_SYM {} | LEVEL_SYM {} | LOCAL_SYM {} | LOCKS_SYM {} @@ -2592,6 +3049,9 @@ keyword: | MASTER_USER_SYM {} | MASTER_PASSWORD_SYM {} | MASTER_CONNECT_RETRY_SYM {} + | MAX_CONNECTIONS_PER_HOUR {} + | MAX_QUERIES_PER_HOUR {} + | MAX_UPDATES_PER_HOUR {} | MEDIUM_SYM {} | MERGE_SYM {} | MINUTE_SYM {} @@ -2602,22 +3062,30 @@ keyword: | MYISAM_SYM {} | NATIONAL_SYM {} | NCHAR_SYM {} + | NEXT_SYM {} + | NEW_SYM {} | NO_SYM {} + | OFF {} | OPEN_SYM {} | PACK_KEYS_SYM {} | PASSWORD {} + | PREV_SYM {} | PROCESS {} | PROCESSLIST_SYM {} + | QUERY_SYM {} | QUICK {} | RAID_0_SYM {} | RAID_CHUNKS {} | RAID_CHUNKSIZE {} | RAID_STRIPED_SYM {} | RAID_TYPE {} + | RELAY_LOG_FILE_SYM {} + | RELAY_LOG_POS_SYM {} | RELOAD {} | REPAIR {} | REPEATABLE_SYM {} | RESET_SYM {} + | RESOURCES {} | RESTORE_SYM {} | ROLLBACK_SYM {} | ROWS_SYM {} @@ -2626,12 +3094,19 @@ keyword: | SECOND_SYM {} | SERIALIZABLE_SYM {} | SESSION_SYM {} + | SIGNED_SYM {} | SHARE_SYM {} | SHUTDOWN {} + | SLAVE {} + | SQL_CACHE_SYM {} + | SQL_NO_CACHE_SYM {} + | SQL_QUERY_CACHE_TYPE_SYM {} + | SQL_THREAD {} | START_SYM {} | STATUS_SYM {} | STOP_SYM {} | STRING_SYM {} + | SUBJECT_SYM {} | TEMPORARY {} | TEXT_SYM {} | TRANSACTION_SYM {} @@ -2641,62 +3116,67 @@ keyword: | TYPE_SYM {} | UDF_SYM {} | UNCOMMITTED_SYM {} + | USE_FRM {} | VARIABLES {} | WORK_SYM {} - | YEAR_SYM {} - | SLAVE {} + | YEAR_SYM {}; /* Option functions */ set: SET opt_option { - THD *thd=current_thd; - LEX *lex= &thd->lex; + LEX *lex=Lex; 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->select->options=lex->thd->options; + lex->select->select_limit=lex->thd->default_select_limit; + lex->tx_isolation=lex->thd->tx_isolation; + lex->option_type=0; + lex->option_list.empty(); } - option_value_list + option_value_list; opt_option: /* empty */ {} - | OPTION {} + | OPTION {}; option_value_list: option_value - | 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 { @@ -2710,22 +3190,14 @@ option_value: { current_thd->user_time=0; } - | LAST_INSERT_ID equal ULONGLONG_NUM + | LAST_INSERT_ID equal ulonglong_num { current_thd->insert_id($3); } - | INSERT_ID equal ULONGLONG_NUM + | INSERT_ID equal ulonglong_num { current_thd->next_insert_id=$3; } - | GEMINI_SPIN_RETRIES equal ULONG_NUM - { - Lex->gemini_spin_retries= $3; - } - | GEMINI_SPIN_RETRIES equal DEFAULT - { - Lex->gemini_spin_retries= 1; - } | CHAR_SYM SET IDENT { CONVERT *tmp; @@ -2753,6 +3225,7 @@ option_value: $3->user.str,$5)) YYABORT; } + | SQL_QUERY_CACHE_TYPE_SYM equal query_cache_type | '@' ident_or_text equal expr { Item_func_set_user_var *item = new Item_func_set_user_var($2,$4); @@ -2764,13 +3237,46 @@ option_value: } | SQL_SLAVE_SKIP_COUNTER equal ULONG_NUM { - pthread_mutex_lock(&LOCK_slave); - if(slave_running) + LOCK_ACTIVE_MI; + pthread_mutex_lock(&active_mi->rli.run_lock); + if (active_mi->rli.slave_running) send_error(¤t_thd->net, ER_SLAVE_MUST_STOP); else - slave_skip_counter = $3; - pthread_mutex_unlock(&LOCK_slave); + { + pthread_mutex_lock(&active_mi->rli.data_lock); + active_mi->rli.slave_skip_counter = $3; + pthread_mutex_unlock(&active_mi->rli.data_lock); + } + pthread_mutex_unlock(&active_mi->rli.run_lock); + UNLOCK_ACTIVE_MI; } + | 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)); + }; + +query_cache_type: + NUM { current_thd->query_cache_type = set_zone(atoi($1.str),0,3); } + | OFF { current_thd->query_cache_type = 0; } + | ON { current_thd->query_cache_type = 1; } + | DEMAND_SYM { current_thd->query_cache_type = 2; }; text_or_password: TEXT_STRING { $$=$1.str;} @@ -2784,7 +3290,7 @@ text_or_password: make_scrambled_password(buff,$3.str); $$=buff; } - } + }; set_option: SQL_BIG_TABLES { $$= OPTION_BIG_TABLES; } @@ -2807,7 +3313,7 @@ set_option: | SQL_AUTO_IS_NULL { $$= OPTION_AUTO_IS_NULL; } | SQL_SAFE_UPDATES { $$= OPTION_SAFE_UPDATES; } | SQL_BUFFER_RESULT { $$= OPTION_BUFFER_RESULT; } - | SQL_QUOTE_SHOW_CREATE { $$= OPTION_QUOTE_SHOW_CREATE; } + | SQL_QUOTE_SHOW_CREATE { $$= OPTION_QUOTE_SHOW_CREATE; }; set_isolation: @@ -2819,18 +3325,21 @@ 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; } + { Lex->tx_isolation= $1; }; tx_isolation: - TRANSACTION_SYM ISOLATION LEVEL_SYM isolation_types { $$=$4; } + TRANSACTION_SYM ISOLATION LEVEL_SYM isolation_types { $$=$4; }; isolation_types: READ_SYM UNCOMMITTED_SYM { $$= ISO_READ_UNCOMMITTED; } | READ_SYM COMMITTED_SYM { $$= ISO_READ_COMMITTED; } | REPEATABLE_SYM READ_SYM { $$= ISO_REPEATABLE_READ; } - | SERIALIZABLE_SYM { $$= ISO_SERIALIZABLE; } + | SERIALIZABLE_SYM { $$= ISO_SERIALIZABLE; }; /* Lock function */ @@ -2839,63 +3348,126 @@ lock: { Lex->sql_command=SQLCOM_LOCK_TABLES; } - table_lock_list + table_lock_list; table_or_tables: TABLE_SYM - | TABLES + | TABLES; table_lock_list: table_lock - | table_lock_list ',' table_lock + | table_lock_list ',' table_lock; table_lock: table_ident opt_table_alias lock_option - { if (!add_table_to_list($1,$2,0,(thr_lock_type) $3)) YYABORT; } + { if (!add_table_to_list($1,$2,0,(thr_lock_type) $3)) YYABORT; }; lock_option: READ_SYM { $$=TL_READ_NO_INSERT; } | WRITE_SYM { $$=current_thd->update_lock_default; } | LOW_PRIORITY WRITE_SYM { $$=TL_WRITE_LOW_PRIORITY; } - | READ_SYM LOCAL_SYM { $$= TL_READ; } + | READ_SYM LOCAL_SYM { $$= TL_READ; }; unlock: - UNLOCK_SYM table_or_tables { Lex->sql_command=SQLCOM_UNLOCK_TABLES; } + 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 + { + LEX *lex=Lex; + lex->sql_command = SQLCOM_HA_READ; + lex->ha_rkey_mode= HA_READ_KEY_EXACT; /* Avoid purify warnings */ + lex->select->select_limit= 1; + lex->select->offset_limit= 0L; + if (!add_table_to_list($2,0,0)) + YYABORT; + } + handler_read_or_scan 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; + lex->ha_rkey_mode=$1; + if (!(lex->insert_list = new List_item)) + YYABORT; + } '(' values ')' { }; + +handler_rkey_mode: + EQ { $$=HA_READ_KEY_EXACT; } + | GE { $$=HA_READ_KEY_OR_NEXT; } + | LE { $$=HA_READ_KEY_OR_PREV; } + | GT_SYM { $$=HA_READ_AFTER_KEY; } + | LT { $$=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_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; + lex->ssl_type=SSL_TYPE_NONE; + lex->ssl_cipher=lex->x509_subject=lex->x509_issuer=0; + bzero(&(lex->mqh),sizeof(lex->mqh)); } grant_privileges ON opt_table TO_SYM user_list - grant_option + require_clause grant_options; grant_privileges: grant_privilege_list {} | ALL PRIVILEGES { Lex->grant = UINT_MAX;} - | ALL { Lex->grant = UINT_MAX;} + | ALL { Lex->grant = UINT_MAX;}; grant_privilege_list: grant_privilege - | grant_privilege_list ',' grant_privilege + | grant_privilege_list ',' grant_privilege; grant_privilege: SELECT_SYM @@ -2918,54 +3490,92 @@ grant_privilege: | SHUTDOWN { Lex->grant |= SHUTDOWN_ACL;} | PROCESS { Lex->grant |= PROCESS_ACL;} | FILE_SYM { Lex->grant |= FILE_ACL;} - | GRANT OPTION { Lex->grant |= GRANT_ACL;} - + | GRANT OPTION { Lex->grant |= GRANT_ACL;}; + +require_list: require_list_element AND require_list +| require_list_element ; + +require_list_element: SUBJECT_SYM TEXT_STRING + { + LEX *lex=Lex; + if (lex->x509_subject) + { + net_printf(&lex->thd->net,ER_DUP_ARGUMENT, "SUBJECT"); + YYABORT; + } + lex->x509_subject=$2.str; + } + | ISSUER_SYM TEXT_STRING + { + LEX *lex=Lex; + if (lex->x509_issuer) + { + net_printf(&lex->thd->net,ER_DUP_ARGUMENT, "ISSUER"); + YYABORT; + } + lex->x509_issuer=$2.str; + } + | CIPHER_SYM TEXT_STRING + { + LEX *lex=Lex; + if (lex->ssl_cipher) + { + net_printf(&lex->thd->net,ER_DUP_ARGUMENT, "CIPHER"); + YYABORT; + } + lex->ssl_cipher=$2.str; + }; + 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(¤t_thd->net,ER_ILLEGAL_GRANT_FOR_TABLE); + send_error(&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(¤t_thd->net,ER_ILLEGAL_GRANT_FOR_TABLE); + send_error(&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(¤t_thd->net,ER_ILLEGAL_GRANT_FOR_TABLE); + send_error(&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; + }; user_list: grant_user { if (Lex->users_list.push_back($1)) YYABORT;} - | user_list ',' grant_user { if (Lex->users_list.push_back($3)) YYABORT;} + | user_list ',' grant_user { if (Lex->users_list.push_back($3)) YYABORT;}; grant_user: @@ -2986,16 +3596,20 @@ grant_user: | user IDENTIFIED_SYM BY PASSWORD TEXT_STRING { $$=$1; $1->password=$5 ; } | user - { $$=$1; $1->password.str=NullS; } + { $$=$1; $1->password.str=NullS; }; opt_column_list: - /* empty */ { Lex->grant |= Lex->which_columns; } - | '(' column_list ')' + /* empty */ + { + LEX *lex=Lex; + lex->grant |= lex->which_columns; + } + | '(' column_list ')'; column_list: column_list ',' column_list_id - | column_list_id + | column_list_id; column_list_id: ident @@ -3003,31 +3617,115 @@ 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: + +require_clause: /* empty */ + | REQUIRE_SYM require_list + { + Lex->ssl_type=SSL_TYPE_SPECIFIED; + } + | REQUIRE_SYM SSL_SYM + { + Lex->ssl_type=SSL_TYPE_ANY; + } + | REQUIRE_SYM X509_SYM + { + Lex->ssl_type=SSL_TYPE_X509; + }; + +grant_options: /* empty */ {} - | WITH GRANT OPTION { Lex->grant |= GRANT_ACL;} + | WITH grant_option_list; + +grant_option_list: + grant_option_list grant_option {} + | grant_option {}; + +grant_option: + GRANT OPTION { Lex->grant |= GRANT_ACL;} + | MAX_QUERIES_PER_HOUR EQ ULONG_NUM + { + Lex->mqh.questions=$3; + } + | MAX_UPDATES_PER_HOUR EQ ULONG_NUM + { + Lex->mqh.updates=$3; + } + | MAX_CONNECTIONS_PER_HOUR EQ ULONG_NUM + { + Lex->mqh.connections=$3; + } begin: - BEGIN_SYM { Lex->sql_command = SQLCOM_BEGIN;} opt_work + BEGIN_SYM { Lex->sql_command = SQLCOM_BEGIN;} opt_work; opt_work: /* empty */ {} - | WORK_SYM {} + | WORK_SYM {}; commit: - COMMIT_SYM { Lex->sql_command = SQLCOM_COMMIT;} + COMMIT_SYM { Lex->sql_command = SQLCOM_COMMIT;}; rollback: - ROLLBACK_SYM { Lex->sql_command = SQLCOM_ROLLBACK;} + ROLLBACK_SYM { Lex->sql_command = SQLCOM_ROLLBACK;}; + + +/* +** UNIONS : glue selects together +*/ + + +union: + /* empty */ {} + | union_list; + +union_list: + UNION_SYM union_option + { + LEX *lex=Lex; + if (lex->exchange) + { + /* Only the last SELECT can have INTO...... */ + net_printf(&lex->thd->net, ER_WRONG_USAGE,"UNION","INTO"); + YYABORT; + } + if (lex->select->linkage == NOT_A_SELECT || mysql_new_select(lex)) + YYABORT; + lex->select->linkage=UNION_TYPE; + } + select_init; + +union_opt: + union {} + | optional_order_or_limit {}; + +optional_order_or_limit: + /* empty */ {} + | + { + LEX *lex=Lex; + if (!lex->select->braces || mysql_new_select(lex)) + YYABORT; + mysql_init_select(lex); + lex->select->linkage=NOT_A_SELECT; + lex->select->select_limit=lex->thd->default_select_limit; + } + opt_order_clause limit_clause; + +union_option: + /* empty */ {} + | ALL {Lex->union_option=1;}; + + diff --git a/sql/stacktrace.c b/sql/stacktrace.c index f4415571f1b..d86d65f567e 100644 --- a/sql/stacktrace.c +++ b/sql/stacktrace.c @@ -14,7 +14,7 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include <global.h> +#include <my_global.h> #include "stacktrace.h" #include <signal.h> #include <my_pthread.h> @@ -122,8 +122,8 @@ terribly wrong...\n"); return; } #endif /* __alpha__ */ - - if (!stack_bottom) + + if (!stack_bottom || (gptr) stack_bottom > (gptr) &fp) { ulong tmp= min(0x10000,thread_stack); /* Assume that the stack starts at the previous even 65K */ @@ -150,7 +150,7 @@ terribly wrong...\n"); :"=r"(pc) :"r"(pc)); #endif /* __alpha__ */ - + while (fp < (uchar**) stack_bottom) { #ifdef __i386__ @@ -165,7 +165,7 @@ terribly wrong...\n"); { new_fp += 90; } - + if (fp && pc) { pc = find_prev_pc(pc, fp); @@ -195,7 +195,7 @@ terribly wrong...\n"); } fprintf(stderr, "Stack trace seems successful - bottom reached\n"); - + end: fprintf(stderr, "Please read http://www.mysql.com/doc/U/s/Using_stack_trace.html and follow instructions on how to resolve the stack trace. Resolved\n\ stack trace is much more helpful in diagnosing the problem, so please do \n\ diff --git a/sql/structs.h b/sql/structs.h index 36f503312c0..75280b34715 100644 --- a/sql/structs.h +++ b/sql/structs.h @@ -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 */ @@ -67,6 +67,7 @@ typedef struct st_key { uint key_parts; /* How many key_parts */ uint extra_length; uint usable_key_parts; /* Should normally be = key_parts */ + enum ha_key_alg algorithm; KEY_PART_INFO *key_part; char *name; /* Name of key */ ulong *rec_per_key; /* Key part distribution */ @@ -125,7 +126,23 @@ typedef struct { enum SHOW_TYPE { SHOW_LONG,SHOW_CHAR,SHOW_INT,SHOW_CHAR_PTR,SHOW_BOOL, SHOW_MY_BOOL,SHOW_OPENTABLES,SHOW_STARTTIME,SHOW_QUESTION, - SHOW_LONG_CONST, SHOW_INT_CONST, SHOW_HAVE}; + SHOW_LONG_CONST, SHOW_INT_CONST, SHOW_HAVE +#ifdef HAVE_OPENSSL + ,SHOW_SSL_CTX_SESS_ACCEPT, SHOW_SSL_CTX_SESS_ACCEPT_GOOD + ,SHOW_SSL_GET_VERSION, SHOW_SSL_CTX_GET_SESSION_CACHE_MODE + ,SHOW_SSL_CTX_SESS_CB_HITS, SHOW_SSL_CTX_SESS_ACCEPT_RENEGOTIATE + ,SHOW_SSL_CTX_SESS_NUMBER, SHOW_SSL_SESSION_REUSED + ,SHOW_SSL_CTX_SESS_GET_CACHE_SIZE, SHOW_SSL_GET_CIPHER + ,SHOW_SSL_GET_DEFAULT_TIMEOUT, SHOW_SSL_GET_VERIFY_MODE + ,SHOW_SSL_CTX_GET_VERIFY_MODE, SHOW_SSL_GET_VERIFY_DEPTH + ,SHOW_SSL_CTX_GET_VERIFY_DEPTH, SHOW_SSL_CTX_SESS_CONNECT + ,SHOW_SSL_CTX_SESS_CONNECT_RENEGOTIATE, SHOW_SSL_CTX_SESS_CONNECT_GOOD + ,SHOW_SSL_CTX_SESS_HITS, SHOW_SSL_CTX_SESS_MISSES + ,SHOW_SSL_CTX_SESS_TIMEOUTS, SHOW_SSL_CTX_SESS_CACHE_FULL + ,SHOW_SSL_GET_CIPHER_LIST +#endif /* HAVE_OPENSSL */ + ,SHOW_RPL_STATUS, SHOW_SLAVE_RUNNING +}; enum SHOW_COMP_OPTION { SHOW_OPTION_YES, SHOW_OPTION_NO, SHOW_OPTION_DISABLED}; @@ -144,6 +161,17 @@ typedef struct st_lex_user { LEX_STRING user, host, password; } LEX_USER; + +typedef struct user_resources { + uint questions, updates, connections; +} USER_RESOURCES; + +typedef struct user_conn { + char *user, *host; + uint len, connections, conn_per_hour, updates, questions, user_len; + USER_RESOURCES user_resources; + time_t intime; +} USER_CONN; /* Bits in form->update */ #define REG_MAKE_DUPP 1 /* Make a copy of record when read */ #define REG_NEW_RECORD 2 /* Write a new record if not found */ @@ -154,13 +182,14 @@ typedef struct st_lex_user { #define REG_MAY_BE_UPDATED 64 #define REG_AUTO_UPDATE 64 /* Used in D-forms for scroll-tables */ #define REG_OVERWRITE 128 -#define REG_SKIPP_DUPP 256 +#define REG_SKIP_DUP 256 /* Bits in form->status */ #define STATUS_NO_RECORD (1+2) /* Record isn't usably */ #define STATUS_GARBAGE 1 -#define STATUS_NOT_FOUND 2 /* No record in database when neaded */ +#define STATUS_NOT_FOUND 2 /* No record in database when needed */ #define STATUS_NO_PARENT 4 /* Parent record wasn't found */ #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.cc b/sql/table.cc index 66cbe7cfa16..315f8bacf34 100644 --- a/sql/table.cc +++ b/sql/table.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 */ @@ -46,7 +46,8 @@ int openfrm(const char *name, const char *alias, uint db_stat, uint prgflag, reg2 uchar *strpos; int j,error; uint rec_buff_length,n_length,int_length,records,key_parts,keys, - interval_count,interval_parts,read_length,db_create_options; + interval_count,interval_parts,read_length,db_create_options; + uint key_info_length; ulong pos; char index_file[FN_REFLEN], *names,*keynames; uchar head[288],*disk_buff,new_field_pack_flag; @@ -127,8 +128,9 @@ int openfrm(const char *name, const char *alias, uint db_stat, uint prgflag, outparam->min_rows=uint4korr(head+22); /* Read keyinformation */ + key_info_length= (uint) uint2korr(head+28); VOID(my_seek(file,(ulong) uint2korr(head+6),MY_SEEK_SET,MYF(0))); - if (read_string(file,(gptr*) &disk_buff,(uint) uint2korr(head+28))) + if (read_string(file,(gptr*) &disk_buff,key_info_length)) goto err_not_open; /* purecov: inspected */ outparam->keys=keys= disk_buff[0]; outparam->keys_in_use= set_bits(key_map, keys); @@ -187,8 +189,16 @@ int openfrm(const char *name, const char *alias, uint db_stat, uint prgflag, if (keyinfo->flags & HA_NOSAME) set_if_bigger(outparam->max_unique_length,keyinfo->key_length); } - - (void) strmov(keynames= (char *) key_part,(char *) strpos); + keynames=(char*) key_part; + strpos+= (strmov(keynames, (char *) strpos) - keynames)+1; + /* Test if new 4.0 format */ + if ((uint) (strpos - disk_buff) < key_info_length) + { + /* Read key types */ + keyinfo=outparam->key_info; + for (i=0 ; i < keys ; i++, keyinfo++) + keyinfo->algorithm= (enum ha_key_alg) *(strpos++); + } outparam->reclength = uint2korr((head+16)); if (*(head+26) == 1) outparam->system=1; /* one-record-database */ @@ -259,7 +269,7 @@ int openfrm(const char *name, const char *alias, uint db_stat, uint prgflag, outparam->comment=strdup_root(&outparam->mem_root, (char*) head+47); - DBUG_PRINT("form",("i_count: %d i_parts: %d index: %d n_length: %d int_length: %d", interval_count,interval_parts, outparam->keys,n_length,int_length)); + DBUG_PRINT("info",("i_count: %d i_parts: %d index: %d n_length: %d int_length: %d", interval_count,interval_parts, outparam->keys,n_length,int_length)); if (!(field_ptr = (Field **) alloc_root(&outparam->mem_root, @@ -369,7 +379,7 @@ int openfrm(const char *name, const char *alias, uint db_stat, uint prgflag, { uint primary_key=(uint) (find_type((char*) "PRIMARY",&outparam->keynames, 3)-1); - uint ha_option=outparam->file->option_flag(); + uint ha_option=outparam->file->table_flags(); keyinfo=outparam->key_info; key_part=keyinfo->key_part; @@ -442,8 +452,10 @@ int openfrm(const char *name, const char *alias, uint db_stat, uint prgflag, (!(ha_option & HA_KEY_READ_WRONG_STR) && !(keyinfo->flags & HA_FULLTEXT))) field->part_of_key|= ((key_map) 1 << key); - if (field->key_type() != HA_KEYTYPE_TEXT || - !(keyinfo->flags & HA_FULLTEXT)) + if ((field->key_type() != HA_KEYTYPE_TEXT || + !(keyinfo->flags & HA_FULLTEXT)) && + !(outparam->file->index_flags(key) & + HA_WRONG_ASCII_ORDER)) field->part_of_sortkey|= ((key_map) 1 << key); } if (!(key_part->key_part_flag & HA_REVERSE_SORT) && @@ -453,15 +465,20 @@ int openfrm(const char *name, const char *alias, uint db_stat, uint prgflag, if (key == primary_key) { field->flags|= PRI_KEY_FLAG; + /* + If this field is part of the primary key and all keys contains + the primary key, then we can use any key to find this column + */ if (ha_option & HA_PRIMARY_KEY_IN_READ_INDEX) - field->part_of_key|= ((key_map) 1 << primary_key); + field->part_of_key= outparam->keys_in_use; } if (field->key_length() != key_part->length) { key_part->key_part_flag|= HA_PART_KEY; if (field->type() != FIELD_TYPE_BLOB) { // Create a new field - field=key_part->field=field->new_field(outparam); + field=key_part->field=field->new_field(&outparam->mem_root, + outparam); field->field_length=key_part->length; } } @@ -478,8 +495,6 @@ int openfrm(const char *name, const char *alias, uint db_stat, uint prgflag, (outparam->keys_in_use & ((key_map) 1 << primary_key))) { outparam->primary_key=primary_key; - if (outparam->file->option_flag() & HA_PRIMARY_KEY_IN_READ_INDEX) - outparam->ref_primary_key= (key_map) 1 << primary_key; /* If we are using an integer as the primary key then allow the user to refer to it as '_rowid' @@ -821,7 +836,7 @@ fix_type_pointers(const char ***array, TYPELIB *point_to_type, uint types, *type_name= '\0'; /* End string */ ptr=type_name; } - ptr+=2; /* Skipp end mark and last 0 */ + ptr+=2; /* Skip end mark and last 0 */ } else ptr++; @@ -995,6 +1010,7 @@ File create_frm(register my_string name, uint reclength, uchar *fileinfo, void update_create_info_from_table(HA_CREATE_INFO *create_info, TABLE *table) { + DBUG_ENTER("update_create_info_from_table"); create_info->max_rows=table->max_rows; create_info->min_rows=table->min_rows; create_info->table_options=table->db_create_options; @@ -1003,7 +1019,8 @@ void update_create_info_from_table(HA_CREATE_INFO *create_info, TABLE *table) create_info->raid_type=table->raid_type; create_info->raid_chunks=table->raid_chunks; create_info->raid_chunksize=table->raid_chunksize; -} + DBUG_VOID_RETURN; +} int rename_file_ext(const char * from,const char * to,const char * ext) diff --git a/sql/table.h b/sql/table.h index ae785b8a402..209333c24b7 100644 --- a/sql/table.h +++ b/sql/table.h @@ -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 */ @@ -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 */ @@ -98,6 +98,7 @@ struct st_table { my_bool fulltext_searched; my_bool crashed; my_bool is_view; + my_bool no_keyread; Field *next_number_field, /* Set if next_number is activated */ *found_next_number_field, /* Set on open */ *rowid_field; @@ -111,14 +112,14 @@ struct st_table { char *table_name,*real_name,*path; uint key_length; /* Length of key */ uint tablenr,used_fields,null_bytes; - table_map map; + table_map map; /* ID bit of table (1,2,4,8,16...) */ ulong version,flush_version; uchar *null_flags; IO_CACHE *io_cache; /* If sorted trough file*/ byte *record_pointers; /* If sorted in memory */ ha_rows found_records; /* How many records in sort */ ORDER *group; - key_map quick_keys, used_keys, ref_primary_key; + key_map quick_keys, used_keys; ha_rows quick_rows[MAX_KEY]; uint quick_key_parts[MAX_KEY]; key_part_map const_key_parts[MAX_KEY]; @@ -137,13 +138,29 @@ struct st_table { typedef struct st_table_list { struct st_table_list *next; char *db,*name,*real_name; + uint32 db_length, real_name_length; Item *on_expr; /* Used with outer join */ struct st_table_list *natural_join; /* natural join on this table*/ - List<String> *use_index,*ignore_index; + /* ... join ... USE INDEX ... IGNORE INDEX */ + List<String> *use_index,*ignore_index; TABLE *table; GRANT_INFO grant; thr_lock_type lock_type; uint outer_join; /* Which join type */ bool straight; /* optimize with prev table */ - bool updating; /* for replicate-do/ignore table */ + bool updating; /* for replicate-do/ignore table */ + bool shared; /* Used twice in union */ } TABLE_LIST; + +typedef struct st_changed_table_list { + struct st_changed_table_list *next; + char *key, *table_name; + uint32 key_length; +} CHANGED_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/thr_malloc.cc b/sql/thr_malloc.cc index deb304443df..8b9baa6f045 100644 --- a/sql/thr_malloc.cc +++ b/sql/thr_malloc.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 */ @@ -22,7 +22,9 @@ extern "C" { void sql_alloc_error_handler(void) { - current_thd->fatal_error=1; /* purecov: inspected */ + THD *thd=current_thd; + if (thd) // QQ; To be removed + thd->fatal_error=1; /* purecov: inspected */ sql_print_error(ER(ER_OUT_OF_RESOURCES)); } } diff --git a/sql/time.cc b/sql/time.cc index 1d7e055f682..aab886648e3 100644 --- a/sql/time.cc +++ b/sql/time.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 */ @@ -24,7 +24,7 @@ static ulong const days_at_timestart=719528; /* daynr at 1970.01.01 */ uchar *days_in_month= (uchar*) "\037\034\037\036\037\036\037\037\036\037\036\037"; - /* Init some variabels neaded when using my_local_time */ + /* Init some variabels needed when using my_local_time */ /* Currently only my_time_zone is inited */ static long my_time_zone=0; @@ -54,7 +54,7 @@ void init_time(void) This code handles also day light saving time. The idea is to cache the time zone (including daylight saving time) for the next call to make things faster. - + */ long my_gmt_sec(TIME *t) @@ -128,7 +128,7 @@ long calc_daynr(uint year,uint month,uint day) DBUG_ENTER("calc_daynr"); if (year == 0 && month == 0 && day == 0) - DBUG_RETURN(0); /* Skipp errors */ + DBUG_RETURN(0); /* Skip errors */ if (year < 200) { if ((year=year+1900) < 1900+YY_PART_YEAR) @@ -176,7 +176,9 @@ uint calc_week(TIME *l_time, bool with_year, bool sunday_first_day_of_week, ulong first_daynr=calc_daynr(l_time->year,1,1); uint weekday=calc_weekday(first_daynr,sunday_first_day_of_week); *year=l_time->year; - if (l_time->month == 1 && weekday >= 4 && l_time->day <= 7-weekday) + if (l_time->month == 1 && l_time->day <= 7-weekday && + ((!sunday_first_day_of_week && weekday >= 4) || + (sunday_first_day_of_week && weekday != 0))) { /* Last week of the previous year */ if (!with_year) @@ -186,7 +188,8 @@ uint calc_week(TIME *l_time, bool with_year, bool sunday_first_day_of_week, first_daynr-= (days=calc_days_in_year(*year)); weekday= (weekday + 53*7- days) % 7; } - if (weekday >= 4) + if ((sunday_first_day_of_week && weekday != 0) || + (!sunday_first_day_of_week && weekday >= 4)) days= daynr - (first_daynr+ (7-weekday)); else days= daynr - (first_daynr - weekday); @@ -431,7 +434,7 @@ str_to_TIME(const char *str, uint length, TIME *l_time,bool fuzzy_date) DBUG_ENTER("str_to_TIME"); DBUG_PRINT("enter",("str: %.*s",length,str)); - for (; str != end && !isdigit(*str) ; str++) ; // Skipp garbage + for (; str != end && !isdigit(*str) ; str++) ; // Skip garbage if (str == end) DBUG_RETURN(TIMESTAMP_NONE); /* @@ -591,7 +594,7 @@ bool str_to_time(const char *str,uint length,TIME *l_time) date[0]=value; state=1; // Assume next is hours found_days=1; - str++; // Skipp space; + str++; // Skip space; } else if ((end-str) > 1 && *str == ':' && isdigit(str[1])) { @@ -599,7 +602,7 @@ bool str_to_time(const char *str,uint length,TIME *l_time) date[1]=value; state=2; found_hours=1; - str++; // skipp ':' + str++; // skip ':' } else { @@ -620,7 +623,7 @@ bool str_to_time(const char *str,uint length,TIME *l_time) date[state++]=value; if (state == 4 || (end-str) < 2 || *str != ':' || !isdigit(str[1])) break; - str++; // Skipp ':' + str++; // Skip ':' } if (state != 4) diff --git a/sql/udf_example.cc b/sql/udf_example.cc index a91db5ee1cc..a5ec77f88e4 100644 --- a/sql/udf_example.cc +++ b/sql/udf_example.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 */ @@ -110,7 +110,7 @@ #include <stdio.h> #include <string.h> #else -#include <global.h> +#include <my_global.h> #include <my_sys.h> #endif #include <mysql.h> diff --git a/sql/uniques.cc b/sql/uniques.cc new file mode 100644 index 00000000000..3a26f610dc5 --- /dev/null +++ b/sql/uniques.cc @@ -0,0 +1,166 @@ +/* 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" + + +int unique_write_to_file(gptr key, element_count count, Unique *unique) +{ + return my_b_write(&unique->file, (byte*) 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; +} + +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) +{ + my_b_clear(&file); + init_tree(&tree, max_in_memory_size / 16, 0, size, comp_func, 0, NULL, comp_func_fixed_arg); + /* If the following fail's the next add will also fail */ + my_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; +} + + +/* + 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); + + bzero((char*) &sort_param,sizeof(sort_param)); + 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; + sort_param.not_killable=1; + + 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/unireg.cc b/sql/unireg.cc index f7b040adebe..1c35f7a6a08 100644 --- a/sql/unireg.cc +++ b/sql/unireg.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 */ @@ -20,7 +20,7 @@ struct. In the following functions FIELD * is an ordinary field-structure with the following exeptions: - sc_length,typepos,row,kol,dtype,regnr and field nead not to be set. + sc_length,typepos,row,kol,dtype,regnr and field need not to be set. str is a (long) to record position where 0 is the first position. */ @@ -246,7 +246,7 @@ static uchar * pack_screens(List<create_field> &create_fields, static uint pack_keys(uchar *keybuff,uint key_count,KEY *keyinfo) { uint key_parts,length; - uchar *pos,*keyname_pos; + uchar *pos, *keyname_pos, *key_alg_pos; KEY *key,*end; KEY_PART_INFO *key_part,*key_part_end; DBUG_ENTER("pack_keys"); @@ -290,11 +290,18 @@ static uint pack_keys(uchar *keybuff,uint key_count,KEY *keyinfo) } *(pos++)=0; + /* For MySQL 4.0; Store key algoritms last */ + key_alg_pos= pos; + for (key=keyinfo ; key != end ; key++) + { + *(pos++)= (uchar) key->algorithm; + } + keybuff[0]=(uchar) key_count; keybuff[1]=(uchar) key_parts; length=(uint) (keyname_pos-keybuff); int2store(keybuff+2,length); - length=(uint) (pos-keyname_pos); + length=(uint) (key_alg_pos-keyname_pos); int2store(keybuff+4,length); DBUG_RETURN((uint) (pos-keybuff)); } /* pack_keys */ @@ -391,8 +398,8 @@ static bool pack_header(uchar *forminfo, enum db_type table_type, int2store(forminfo+272,int_parts); int2store(forminfo+274,int_length); int2store(forminfo+276,time_stamp_pos); - int2store(forminfo+278,80); /* Columns neaded */ - int2store(forminfo+280,22); /* Rows neaded */ + int2store(forminfo+278,80); /* Columns needed */ + int2store(forminfo+280,22); /* Rows needed */ int2store(forminfo+282,null_fields); DBUG_RETURN(0); } /* pack_header */ diff --git a/sql/unireg.h b/sql/unireg.h index f8f5edd5156..c4d2052d1da 100644 --- a/sql/unireg.h +++ b/sql/unireg.h @@ -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 */ @@ -80,7 +80,7 @@ #define MYF_RW MYF(MY_WME+MY_NABP) /* Vid my_read & my_write */ #define SPECIAL_USE_LOCKS 1 /* Lock used databases */ -#define SPECIAL_NO_NEW_FUNC 2 /* Skipp new functions */ +#define SPECIAL_NO_NEW_FUNC 2 /* Skip new functions */ #define SPECIAL_NEW_FUNC 4 /* New nonstandard functions */ #define SPECIAL_WAIT_IF_LOCKED 8 /* Wait if locked database */ #define SPECIAL_SAME_DB_NAME 16 /* form name = file name */ diff --git a/sql/violite.c b/sql/violite.c deleted file mode 100644 index 37fee6fad3d..00000000000 --- a/sql/violite.c +++ /dev/null @@ -1,443 +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 -#ifdef HAVE_FCNTL_H -#include <fcntl.h> -#endif - -#if !defined(MSDOS) && !defined(__WIN__) && !defined(HAVE_BROKEN_NETINET_INCLUDES) && !defined(__BEOS__) && !defined(__FreeBSD__) -#include <netinet/in_systm.h> -#include <netinet/ip.h> -#if !defined(alpha_linux_port) -#include <netinet/tcp.h> -#endif -#endif - -#if defined(__EMX__) || defined(OS2) -#define ioctlsocket ioctl -#endif /* defined(__EMX__) */ - -#if defined(MSDOS) || defined(__WIN__) -#define O_NONBLOCK 1 /* For emulation of fcntl() */ -#endif -#ifndef EWOULDBLOCK -#define SOCKET_EWOULDBLOCK SOCKET_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__) && !defined(OS2) -#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(vio->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 socket_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 size=%d", vio->sd, size)); -#if defined( __WIN__) || defined(OS2) - if (vio->type == VIO_TYPE_NAMEDPIPE) - { - DWORD length; -#ifdef OS2 - if (!DosRead((HFILE)vio->hPipe, buf, size, &length)) - DBUG_RETURN(-1); -#else - if (!ReadFile(vio->hPipe, buf, size, &length, NULL)) - DBUG_RETURN(-1); -#endif - 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",socket_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 size=%d", vio->sd, size)); -#if defined( __WIN__) || defined(OS2) - if ( vio->type == VIO_TYPE_NAMEDPIPE) - { - DWORD length; -#ifdef OS2 - if (!DosWrite((HFILE)vio->hPipe, (char*) buf, size, &length)) - DBUG_RETURN(-1); -#else - if (!WriteFile(vio->hPipe, (char*) buf, size, &length, NULL)) - DBUG_RETURN(-1); -#endif - 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",socket_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__) && !defined(OS2) -#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 = socket_errno; - return en == SOCKET_EAGAIN || en == SOCKET_EINTR || en == SOCKET_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",socket_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", socket_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 */ |