diff options
Diffstat (limited to 'sql')
118 files changed, 7488 insertions, 1823 deletions
diff --git a/sql/Makefile.am b/sql/Makefile.am index 4621443f4d2..e1ed9ad8915 100644 --- a/sql/Makefile.am +++ b/sql/Makefile.am @@ -51,12 +51,12 @@ 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_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 \ log_event.h mini_client.h sql_repl.h slave.h \ - stacktrace.h sql_sort.h + 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 \ @@ -67,14 +67,14 @@ mysqld_SOURCES = sql_lex.cc sql_handler.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 uniques.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_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 \ diff --git a/sql/cache_manager.cc b/sql/cache_manager.cc index 9aec222909a..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 */ @@ -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 3e0fbf18ace..7a06208759c 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 bda0690ae7d..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,7 +44,7 @@ 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; @@ -64,36 +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))); 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,18 +105,18 @@ 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); + 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 2f98d2e0fe8..2a0d0160d00 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 */ @@ -219,14 +219,15 @@ static bool test_if_real(const char *str,int length) ****************************************************************************/ 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,8 +243,8 @@ 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); } } @@ -1049,7 +1050,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 @@ -1058,7 +1059,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]; } } @@ -1129,12 +1130,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 { @@ -1171,7 +1172,7 @@ void Field_medium::store(longlong nr) current_thd->cuted_fields++; } else - int3store(ptr,(ulong) nr); + int3store(ptr,(uint32) nr); } else { @@ -1449,7 +1450,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; } @@ -1461,7 +1462,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]; @@ -1472,7 +1473,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]; @@ -1660,7 +1661,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]; @@ -1675,7 +1676,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]; @@ -1910,7 +1911,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 { @@ -2278,10 +2279,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); @@ -2295,7 +2296,7 @@ void Field_timestamp::store(longlong nr) } else #endif - longstore(ptr,(ulong)timestamp); + longstore(ptr,(uint32) timestamp); } @@ -2596,7 +2597,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; } @@ -2632,19 +2633,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; } @@ -2755,14 +2756,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) { @@ -2934,7 +2935,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) @@ -2944,16 +2945,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 @@ -2962,7 +2963,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) @@ -2987,7 +2988,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; } @@ -2997,25 +2998,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; } @@ -3023,7 +3024,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; @@ -3039,9 +3040,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; } @@ -3175,44 +3176,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; } @@ -3331,7 +3332,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)); } @@ -3522,7 +3523,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)); } @@ -3613,9 +3614,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; @@ -3704,7 +3705,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) @@ -3721,7 +3722,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: @@ -3748,9 +3749,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); @@ -3768,11 +3769,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; @@ -3782,10 +3783,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; @@ -3795,7 +3796,7 @@ ulong Field_blob::get_length(const char *pos) else #endif longget(tmp,pos); - return (ulong) tmp; + return (uint32) tmp; } } return 0; // Impossible @@ -3841,14 +3842,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()); } @@ -3859,7 +3860,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; @@ -3875,7 +3876,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; @@ -3898,8 +3899,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) @@ -3933,11 +3934,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); @@ -3956,9 +3957,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)); @@ -4052,7 +4053,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; @@ -4075,7 +4076,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)); @@ -4140,7 +4141,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; @@ -4163,9 +4164,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; @@ -4278,7 +4279,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; @@ -4548,7 +4549,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++) @@ -4558,7 +4559,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; @@ -4624,7 +4625,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 e2af9853512..a9b257f0c3a 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__ @@ -37,21 +37,22 @@ 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 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) { @@ -157,7 +158,7 @@ public: 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) @@ -442,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) @@ -469,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) {} @@ -567,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, @@ -586,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; } @@ -612,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, @@ -643,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; } @@ -671,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; } @@ -705,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, @@ -760,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, @@ -813,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); @@ -837,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) { @@ -862,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*)); @@ -902,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) @@ -939,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) @@ -1022,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); @@ -1065,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 16eedb94c59..a5f42d5731e 100644 --- a/sql/filesort.cc +++ b/sql/filesort.cc @@ -69,7 +69,7 @@ ha_rows filesort(TABLE *table, SORT_FIELD *sortorder, uint s_length, { int error; ulong memavl; - uint maxbuffer,skr; + uint maxbuffer; BUFFPEK *buffpek; ha_rows records; uchar **sort_keys; @@ -163,7 +163,7 @@ ha_rows filesort(TABLE *table, SORT_FIELD *sortorder, uint s_length, &tempfile, selected_records_file)) == HA_POS_ERROR) goto err; - maxbuffer= my_b_tell(&buffpek_pointers)/sizeof(*buffpek); + maxbuffer= (uint) (my_b_tell(&buffpek_pointers)/sizeof(*buffpek)); if (maxbuffer == 0) // The whole set is in memory { @@ -267,14 +267,14 @@ static BUFFPEK *read_buffpek_from_file(IO_CACHE *buffpek_pointers, uint count) if (tmp) { if (reinit_io_cache(buffpek_pointers,READ_CACHE,0L,0,0) || - my_b_read(buffpek_pointers, (char*) tmp, length)) + my_b_read(buffpek_pointers, (byte*) tmp, length)) { my_free((char*) tmp, MYF(0)); tmp=0; } } DBUG_RETURN(tmp); -} +} @@ -398,10 +398,12 @@ 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, - IO_CACHE *buffpek_pointers, 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"); @@ -419,10 +421,10 @@ static int write_keys(SORTPARAM *param, register uchar **sort_keys, uint count, 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++) + for (end=sort_keys+count ; sort_keys != end ; sort_keys++) if (my_b_write(tempfile,(byte*) *sort_keys,(uint) sort_length)) goto err; - if (my_b_write(buffpek_pointers, (char*) &buffpek, sizeof(buffpek))) + if (my_b_write(buffpek_pointers, (byte*) &buffpek, sizeof(buffpek))) goto err; 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 4e5d6fb3111..7ebdbcd8ba8 100644 --- a/sql/gen_lex_hash.cc +++ b/sql/gen_lex_hash.cc @@ -279,8 +279,17 @@ void print_arrays() for (i=0;i<size;i++) { - ulong order = tab_index_function ((i < how_long_symbols) ? symbols[i].name : sql_functions[i - how_long_symbols].name,function_plus,function_type); + const char *name= ((i < how_long_symbols) ? + symbols[i].name : + sql_functions[i - how_long_symbols].name); + ulong order = tab_index_function(name,function_plus,function_type); order %= function_mod; + /* This should never be true */ + if (prva[order] != max_symbol) + { + fprintf(stderr,"Error: Got duplicate value for symbol '%s'\n",name); + exit(1); + } prva [order] = i; } @@ -331,11 +340,11 @@ static struct option long_options[] = static void usage(int version) { - printf("%s Ver 3.2 Distrib %s, for %s (%s)\n", + printf("%s Ver 3.3 Distrib %s, for %s (%s)\n", my_progname, MYSQL_SERVER_VERSION, SYSTEM_TYPE, MACHINE_TYPE); if (version) return; - puts("Copyright (C) 2000 MySQL AB & MySQL Finland AB, by Sinisa and Monty"); + puts("Copyright (C) 2001 MySQL AB, by Sinisa and Monty"); puts("This software comes with ABSOLUTELY NO WARRANTY. This is free software,\nand you are welcome to modify and redistribute it under the GPL license\n"); puts("This program generates a perfect hashing function for the sql_lex.cc"); printf("Usage: %s [OPTIONS]\n", my_progname); @@ -527,7 +536,7 @@ int main(int argc,char **argv) function_mod=best_mod; function_plus=best_add; make_char_table(best_t1,best_t2,best_type); - printf("/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB\n\ + printf("/* Copyright (C) 2001 MySQL AB\n\ This program is free software; you can redistribute it and/or modify\n\ it under the terms of the GNU General Public License as published by\n\ the Free Software Foundation; either version 2 of the License, or\n\ diff --git a/sql/ha_berkeley.cc b/sql/ha_berkeley.cc index 3cb14b5e72f..614d1b5abf5 100644 --- a/sql/ha_berkeley.cc +++ b/sql/ha_berkeley.cc @@ -1043,7 +1043,7 @@ int ha_berkeley::restore_keys(DB_TXN *trans, key_map changed_keys, break; /* purecov: inspected */ } } - + err: DBUG_ASSERT(error != DB_KEYEXIST); DBUG_RETURN(error); @@ -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) @@ -2176,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 561e06229fa..587d70265fa 100644 --- a/sql/ha_berkeley.h +++ b/sql/ha_berkeley.h @@ -89,8 +89,8 @@ class ha_berkeley: public handler 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_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), changed_rows(0),last_dup_key((uint) -1),version(0),using_ignore(0) @@ -98,6 +98,7 @@ class ha_berkeley: public handler } ~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; } uint max_record_length() const { return HA_MAX_REC_LENGTH; } @@ -107,6 +108,7 @@ class ha_berkeley: public handler uint extra_rec_buf_length() { return BDB_HIDDEN_PRIMARY_KEY_LENGTH; } ha_rows estimate_number_of_rows(); bool fast_key_read() { return 1;} + key_map keys_to_use_for_scanning() { return ~(key_map) 0; } bool has_transactions() { return 1;} int open(const char *name, int mode, uint test_if_locked); @@ -121,6 +123,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_heap.cc b/sql/ha_heap.cc index eff69893502..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, diff --git a/sql/ha_heap.h b/sql/ha_heap.h index 6b7e9c6c626..c8f29dea53c 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,12 @@ 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 key_number) { return "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); } + HA_NULL_KEY | HA_REC_NOT_IN_SEQ | 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; } diff --git a/sql/ha_innobase.cc b/sql/ha_innodb.cc index b96eb86dbc1..8941481a95f 100644 --- a/sql/ha_innobase.cc +++ b/sql/ha_innodb.cc @@ -21,7 +21,7 @@ InnoDB */ - 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; @@ -439,7 +439,7 @@ innobase_parse_data_file_paths_and_sizes(void) && *(str + 1) == 'a' && *(str + 2) == 'w') { str += 3; - + if (srv_data_file_is_raw_partition[i] == 0) { srv_data_file_is_raw_partition[i] = SRV_OLD_RAW; } @@ -536,7 +536,7 @@ innobase_init(void) { int err; bool ret; - char current_lib[2], *default_path; + char current_lib[3], *default_path; DBUG_ENTER("innobase_init"); @@ -580,7 +580,7 @@ innobase_init(void) ret = innobase_parse_data_file_paths_and_sizes(); if (ret == FALSE) { - fprintf(stderr, "InnoDB: syntax error in innodb_data_file_path\n"); + sql_print_error("InnoDB: syntax error in innodb_data_file_path"); DBUG_RETURN(TRUE); } @@ -623,7 +623,7 @@ innobase_init(void) 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); } @@ -765,7 +765,7 @@ innobase_rollback( } srv_conc_exit_innodb(); - + trx_mark_sql_stat_end(trx); DBUG_RETURN(convert_error_code_to_mysql(error)); @@ -928,13 +928,13 @@ ha_innobase::open( if (NULL == (ib_table = dict_table_get(norm_name, NULL))) { - 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)); @@ -974,7 +974,7 @@ ha_innobase::open( ->clust_index_was_generated = TRUE; ref_length = DATA_ROW_ID_LEN + 10; - + DBUG_ASSERT(key_used_on_scan == MAX_KEY); } @@ -1288,7 +1288,7 @@ build_template( } else { /* We are building a temporary table: fetch all columns */ - + templ_type = ROW_MYSQL_WHOLE_ROW; } } @@ -1425,7 +1425,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); @@ -1443,7 +1443,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 @@ -1471,7 +1471,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 @@ -1490,11 +1490,11 @@ ha_innobase::write_row( srv_conc_exit_innodb(); 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); @@ -1505,7 +1505,7 @@ ha_innobase::write_row( prebuilt); if (error != DB_SUCCESS) { srv_conc_exit_innodb(); - + error = convert_error_code_to_mysql( error); goto func_exit; @@ -1523,13 +1523,13 @@ ha_innobase::write_row( user_thd->next_insert_id = auto_inc; } } - + /* Set the 'in_update_remember_pos' flag to FALSE to make sure all columns are fetched in the select done by update_auto_increment */ prebuilt->in_update_remember_pos = FALSE; - + update_auto_increment(); if (auto_inc == 0) { @@ -1543,15 +1543,15 @@ ha_innobase::write_row( srv_conc_exit_innodb(); 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 has called a select, and has reset that flag; row_insert_for_mysql has to @@ -2008,6 +2008,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. */ @@ -2034,17 +2052,14 @@ ha_innobase::change_active_index( prebuilt->index=dict_table_get_index_noninline(prebuilt->table, key->name); 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->name, - prebuilt->table->name); + sql_print_error("Innodb could not find key n:o %u with name %s from dict cache for table %s", keynr, key->name, prebuilt->table->name); return(1); } } else prebuilt->index = dict_table_get_first_index_noninline(prebuilt->table); - assert(prebuilt->search_tuple); + assert(prebuilt->search_tuple != 0); dtuple_set_n_fields(prebuilt->search_tuple, prebuilt->index->n_fields); @@ -2108,7 +2123,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(); @@ -2244,7 +2259,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) { @@ -2331,7 +2346,7 @@ ha_innobase::rnd_pos( if (error) { DBUG_RETURN(error); } - + error = index_read(buf, pos, ref_stored_len, HA_READ_KEY_EXACT); change_active_index(keynr); @@ -2473,11 +2488,11 @@ ha_innobase::external_lock( } if (trx->auto_inc_lock) { - + /* If we had reserved the auto-inc lock for some table in this SQL statement, we release it now */ - + srv_conc_enter_innodb(trx); row_unlock_table_autoinc_for_mysql(trx); srv_conc_exit_innodb(); @@ -2757,12 +2772,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: */ @@ -2812,7 +2827,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 @@ -2846,9 +2861,9 @@ innobase_drop_database( char* ptr; int error; char namebuf[10000]; - + ptr = strend(path) - 2; - + while (ptr >= path && *ptr != '\\' && *ptr != '/') { ptr--; len++; @@ -2859,7 +2874,7 @@ innobase_drop_database( memcpy(namebuf, ptr, len); namebuf[len] = '/'; namebuf[len + 1] = '\0'; - + trx = trx_allocate_for_mysql(); error = row_drop_database_for_mysql(namebuf, trx); @@ -2867,7 +2882,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 @@ -2920,7 +2935,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 @@ -2961,7 +2976,8 @@ ha_innobase::records_in_range( KEY* key; dict_index_t* index; mysql_byte* key_val_buff2 = (mysql_byte*) my_malloc( - table->reclength, + table->reclength + + table->max_key_length + 100, MYF(MY_WME)); dtuple_t* range_start; dtuple_t* range_end; @@ -2976,7 +2992,7 @@ ha_innobase::records_in_range( if (prebuilt->trx) { prebuilt->trx->op_info = (char*) "estimating range size"; } - + active_index = keynr; key = table->key_info + active_index; @@ -3012,7 +3028,7 @@ ha_innobase::records_in_range( if (prebuilt->trx) { prebuilt->trx->op_info = (char*) ""; } - + DBUG_RETURN((ha_rows) n_rows); } @@ -3032,7 +3048,7 @@ ha_innobase::estimate_number_of_rows(void) dict_index_t* index; ulonglong estimate; ulonglong data_file_length; - + if (prebuilt->trx) { prebuilt->trx->op_info = (char*) "estimating upper bound of table size"; @@ -3040,16 +3056,19 @@ ha_innobase::estimate_number_of_rows(void) DBUG_ENTER("info"); - dict_update_statistics(prebuilt->table); - index = dict_table_get_first_index_noninline(prebuilt->table); - + data_file_length = ((ulonglong) index->stat_n_leaf_pages) * UNIV_PAGE_SIZE; - /* Calculate a minimum length for a clustered index record */ - estimate = data_file_length / dict_index_calc_min_rec_len(index); - + /* Calculate a minimum length for a clustered index record and from + that an upper bound for the number of rows. Since we only calculate + new statistics in row0mysql.c when a tablehas grown + by a threshold factor, we must add a safety factor 2 in front + of the formula below. */ + + estimate = 2 * data_file_length / dict_index_calc_min_rec_len(index); + if (prebuilt->trx) { prebuilt->trx->op_info = (char*) ""; } @@ -3096,7 +3115,7 @@ ha_innobase::info( if (prebuilt->trx) { prebuilt->trx->op_info = (char*) "calculating table stats"; } - + ib_table = prebuilt->table; if (flag & HA_STATUS_TIME) { @@ -3146,11 +3165,11 @@ ha_innobase::info( if (rec_per_key == 0) { rec_per_key = 1; } - + table->key_info[i].rec_per_key[j] = rec_per_key; } - + index = dict_table_get_next_index_noninline(index); } } @@ -3170,7 +3189,7 @@ ha_innobase::info( if (prebuilt->trx) { prebuilt->trx->op_info = (char*) ""; } - + DBUG_VOID_RETURN; } @@ -3190,7 +3209,7 @@ ha_innobase::check( { row_prebuilt_t* prebuilt = (row_prebuilt_t*) innobase_prebuilt; ulint ret; - + if (prebuilt->mysql_template == NULL) { /* Build the template; we will use a dummy template in index scans done in checking */ @@ -3203,7 +3222,7 @@ ha_innobase::check( if (ret == DB_SUCCESS) { return(HA_ADMIN_OK); } - + return(HA_ADMIN_CORRUPT); } @@ -3221,7 +3240,7 @@ ha_innobase::update_table_comment( { row_prebuilt_t* prebuilt = (row_prebuilt_t*)innobase_prebuilt; uint length = strlen(comment); - char* str = my_malloc(length + 200, MYF(0)); + char* str = my_malloc(length + 550, MYF(0)); char* pos; if (!str) { @@ -3240,8 +3259,8 @@ ha_innobase::update_table_comment( /* We assume 150 bytes of space to print info */ - dict_print_info_on_foreign_keys(pos, 150, prebuilt->table); - + dict_print_info_on_foreign_keys(pos, 500, prebuilt->table); + return(str); } @@ -3380,7 +3399,7 @@ ha_innobase::get_auto_increment() prebuilt->select_lock_type = LOCK_X; prebuilt->trx->mysql_n_tables_locked += 1; - + error=index_last(table->record[1]); if (error) { diff --git a/sql/ha_innobase.h b/sql/ha_innodb.h index 83e43b1d662..fb10975f30a 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,7 +51,7 @@ 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; @@ -78,11 +79,11 @@ class ha_innobase: public handler 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_NULL_KEY | + HA_NOT_EXACT_COUNT | HA_NO_WRITE_DELAYED | HA_PRIMARY_KEY_IN_READ_INDEX | - HA_DROP_BEFORE_CREATE | + HA_DROP_BEFORE_CREATE | HA_NOT_READ_PREFIX_LAST | HA_NO_PREFIX_CHAR_KEYS), last_dup_key((uint) -1), start_of_scan(0) @@ -91,15 +92,21 @@ 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; } 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; } - /* An InnoDB page must store >= 2 keys: - max key length is therefore set to 7000 - bytes */ - uint max_key_length() const { return 7000; } + /* An InnoDB page must store >= 2 keys; + a secondary key record must also contain the + primary key value: + max key length is therefore set to slightly + less than 1 / 4 of page size which is 16 kB; + but currently MySQL does not work with keys + whose size is > MAX_KEY_LENGTH */ + uint max_key_length() const { return((MAX_KEY_LENGTH <= 3500) ? + MAX_KEY_LENGTH : 3500);} bool fast_key_read() { return 1;} key_map keys_to_use_for_scanning() { return ~(key_map) 0; } bool has_transactions() { return 1;} @@ -116,9 +123,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 1a950ce0a9d..4b8c40f8fe6 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 */ @@ -109,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); @@ -124,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); @@ -236,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, diff --git a/sql/ha_isam.h b/sql/ha_isam.h index 5e01edcf889..4194632ddbe 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 */ @@ -33,11 +33,12 @@ class ha_isam: public handler 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) + 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; } uint max_record_length() const { return HA_MAX_REC_LENGTH; } @@ -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 41fb99fe867..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 */ @@ -110,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); @@ -179,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 { diff --git a/sql/ha_isammrg.h b/sql/ha_isammrg.h index c8eb7dd9f69..1ee0b0e2547 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,9 @@ 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 option_flag() const { return (HA_READ_RND_SAME | HA_KEYPOS_TO_RNDPOS | + HA_NOT_READ_PREFIX_LAST | + HA_REC_NOT_IN_SEQ); } 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 7a8585975cc..78ac9f3b309 100644 --- a/sql/ha_myisam.cc +++ b/sql/ha_myisam.cc @@ -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; @@ -426,7 +432,7 @@ int ha_myisam::backup(THD* thd, HA_CHECK_OPT *check_opt) 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))) @@ -771,6 +777,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); @@ -883,7 +897,7 @@ 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) @@ -967,7 +981,7 @@ void ha_myisam::update_create_info(HA_CREATE_INFO *create_info) } -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; @@ -979,20 +993,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; @@ -1035,7 +1049,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 { @@ -1053,19 +1067,19 @@ 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) @@ -1111,7 +1125,7 @@ int ha_myisam::create(const char *name, register TABLE *form, { recinfo_pos->null_bit=found->null_bit; recinfo_pos->null_pos= (uint) (found->null_ptr- - (uchar*) form->record[0]); + (uchar*) table->record[0]); } else { @@ -1126,20 +1140,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; + 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), - form->keys,keydef, + table->keys,keydef, (uint) (recinfo_pos-recinfo), recinfo, 0, (MI_UNIQUEDEF*) 0, &create_info, @@ -1170,7 +1187,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); @@ -1214,4 +1231,3 @@ int ha_myisam::ft_read(byte * buf) table->status=error ? STATUS_NOT_FOUND: 0; return error; } - diff --git a/sql/ha_myisam.h b/sql/ha_myisam.h index eba2bde7d59..75655a2b505 100644 --- a/sql/ha_myisam.h +++ b/sql/ha_myisam.h @@ -45,20 +45,22 @@ class ha_myisam: public handler 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_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_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; } 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); @@ -69,6 +71,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); @@ -76,9 +79,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_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); } + { + 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 d82c202baa3..63a23fb708f 100644 --- a/sql/ha_myisammrg.cc +++ b/sql/ha_myisammrg.cc @@ -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); 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); @@ -107,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); diff --git a/sql/ha_myisammrg.h b/sql/ha_myisammrg.h index b97baa0703c..2ab3a807543 100644 --- a/sql/ha_myisammrg.h +++ b/sql/ha_myisammrg.h @@ -35,10 +35,10 @@ class ha_myisammrg: public handler 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_HAVE_KEY_READ_ONLY | HA_KEYPOS_TO_RNDPOS | HA_READ_ORDER | HA_LASTKEY_ORDER | HA_READ_NOT_EXACT_KEY | - HA_LONGLONG_KEYS | HA_NULL_KEY | HA_BLOB_KEY); } + 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 9bf9b25f76f..e56bdb916bf 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 */ @@ -33,7 +33,7 @@ #include "ha_berkeley.h" #endif #ifdef HAVE_INNOBASE_DB -#include "ha_innobase.h" +#include "ha_innodb.h" #endif #include <myisampack.h> #include <errno.h> @@ -258,6 +258,8 @@ int ha_commit_trans(THD *thd, THD_TRANS* trans) error=1; } trans->innodb_active_trans=0; + if (trans == &thd->transaction.all) + query_cache.invalidate(Query_cache_table::INNODB); } #endif if (error && trans == &thd->transaction.all && mysql_bin_log.is_open()) @@ -336,7 +338,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) { @@ -423,9 +425,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 diff --git a/sql/handler.h b/sql/handler.h index 98358c3b3e4..aa809b333b4 100644 --- a/sql/handler.h +++ b/sql/handler.h @@ -55,12 +55,11 @@ #define HA_REC_NOT_IN_SEQ 64 /* 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_NOT_READ_PREFIX_LAST 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 */ @@ -76,7 +75,8 @@ #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) +#define HA_CAN_FULLTEXT (HA_NO_PREFIX_CHAR_KEYS*2) +#define HA_CAN_SQL_HANDLER (HA_CAN_FULLTEXT*2) /* Parameters for open() (in register form->filestat) */ /* HA_GET_INFO does an implicit HA_ABORT_IF_LOCKED */ @@ -235,6 +235,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; } @@ -254,6 +255,10 @@ 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 FT_INFO *ft_init_ext(uint mode,uint inx,const byte *key, uint keylen, 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 69301479ec7..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 */ diff --git a/sql/hostname.cc b/sql/hostname.cc index bc812341337..7d4e4a8ca75 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 1f8d653e612..c081fd9dd5f 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 */ diff --git a/sql/item.h b/sql/item.h index a52860528f1..5028f25c6b8 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 */ @@ -64,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(); } @@ -128,7 +128,7 @@ 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(); } @@ -206,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) {} @@ -308,7 +308,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; }; 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 8d2a4d491c4..0c83698e60a 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 */ diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h index 9ed3e86d6e8..c9c7d5654d6 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 */ diff --git a/sql/item_create.cc b/sql/item_create.cc index 55f8bb140a9..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) @@ -131,7 +133,9 @@ Item *create_func_floor(Item* a) Item *create_func_found_rows(void) { - return new Item_int("FOUND_ROWS()",(longlong) current_thd->found_rows(),21); + 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) @@ -141,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) @@ -196,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); @@ -279,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); } @@ -379,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 54d2ff035ea..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); diff --git a/sql/item_func.cc b/sql/item_func.cc index ff6a102fe1f..209fbea1674 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -1421,7 +1421,7 @@ void item_user_lock_release(ULL *ull) char buf[256]; String tmp(buf,sizeof(buf)); tmp.length(0); - tmp.append("SELECT release_lock(\""); + tmp.append("DO RELEASE_LOCK(\""); tmp.append(ull->key,ull->key_length); tmp.append("\")"); thd->query_length=tmp.length(); @@ -1445,7 +1445,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()) { @@ -1498,14 +1498,7 @@ void debug_sync_point(const char* lock_name, uint lock_timeout) 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) lock_timeout; - abstime.ts_nsec=0; -#else - abstime.tv_sec=time((time_t*) 0)+(time_t) lock_timeout; - abstime.tv_nsec=0; -#endif - + set_timespec(abstime,lock_timeout); while (!thd->killed && (error=pthread_cond_timedwait(&ull->cond,&LOCK_user_locks,&abstime)) != ETIME && error != ETIMEDOUT && ull->locked) ; @@ -1593,14 +1586,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 && ull->locked) ; @@ -1861,6 +1847,16 @@ Item_func_set_user_var::val_str(String *str) } +void Item_func_set_user_var::print(String *str) +{ + str->append('('); + str->append(name.str,name.length); + str->append(":=",2); + args[0]->print(str); + str->append(')'); +} + + user_var_entry *Item_func_get_user_var::get_entry() { if (!entry || ! entry->value) @@ -1953,6 +1949,34 @@ enum Item_result Item_func_get_user_var::result_type() const return entry->type; } + +void Item_func_get_user_var::print(String *str) +{ + str->append('@'); + str->append(name.str,name.length); + str->append(')'); +} + +bool Item_func_get_user_var::eq(const Item *item) const +{ + /* Assume we don't have rtti */ + if (this == item) + return 1; // Same item is same. + /* Check if other type is also a get_user_var() object */ +#ifdef FIX_THIS + if (item->eq == &Item_func_get_user_var::eq) + return 0; +#else + if (item->type() != FUNC_ITEM || + ((Item_func*) item)->func_name() != func_name()) + return 0; +#endif + Item_func_get_user_var *other=(Item_func_get_user_var*) item; + return (name.length == other->name.length && + !memcmp(name.str, other->name.str, name.length)); +} + + longlong Item_func_inet_aton::val_int() { uint byte_result = 0; @@ -2021,7 +2045,9 @@ void Item_func_match::init_search(bool no_order) } ft_handler=table->file->ft_init_ext(mode, key, - ft_tmp->ptr(), ft_tmp->length(), join_key && !no_order); + (byte*) ft_tmp->ptr(), + ft_tmp->length(), + join_key && !no_order); if (join_key) { @@ -2172,7 +2198,7 @@ bool Item_func_match::eq(const Item *item) const double Item_func_match::val() { - if (ft_handler==NULL) + if (ft_handler == NULL) return -1.0; if (join_key) @@ -2186,10 +2212,10 @@ double Item_func_match::val() if (key == NO_SUCH_KEY) { String *a=concat->val_str(&value); - if (null_value=(a==0)) + if ((null_value= (a==0))) return 0; return ft_handler->please->find_relevance(ft_handler, - (byte *)a->ptr(), a->length()); + (byte *)a->ptr(), a->length()); } else return ft_handler->please->find_relevance(ft_handler, record, 0); diff --git a/sql/item_func.h b/sql/item_func.h index 2bf272f24ed..f7794028998 100644 --- a/sql/item_func.h +++ b/sql/item_func.h @@ -135,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 @@ -164,6 +169,11 @@ class Item_num_op :public Item_func 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); + } }; @@ -179,8 +189,34 @@ public: String *val_str(String*str); enum Item_result result_type () const { return INT_RESULT; } void fix_length_and_dec() { decimals=0; max_length=21; } + 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() + { decimals=0; 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() + { decimals=0; max_length=args[0]->max_length; unsigned_flag=1; } +}; + + class Item_func_plus :public Item_num_op { public: @@ -486,6 +522,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; @@ -820,6 +864,7 @@ public: enum Item_result result_type () const { return cached_result_type; } bool fix_fields(THD *thd,struct st_table_list *tables); void fix_length_and_dec(); + void print(String *str); const char *func_name() const { return "set_user_var"; } }; @@ -838,13 +883,16 @@ public: longlong val_int(); String *val_str(String* str); void fix_length_and_dec(); + void print(String *str); enum Item_result result_type() const; const char *func_name() const { return "get_user_var"; } bool const_item() const { return const_var_flag; } table_map used_tables() const { return const_var_flag ? 0 : RAND_TABLE_BIT; } + bool eq(const Item *item) const; }; + class Item_func_inet_aton : public Item_int_func { public: @@ -862,18 +910,18 @@ class Item_func_match :public Item_real_func { public: List<Item> fields; - Item *concat; String value; TABLE *table; - uint key, mode; - bool join_key; Item_func_match *master; 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), - key(0), concat(0) {} + fields(a), table(0), master(0), ft_handler(0), + concat(0), key(0), join_key(0) {} ~Item_func_match() { if (!master && ft_handler) @@ -912,3 +960,12 @@ public: 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 08d8fb7c38b..c64fdc7a049 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 */ @@ -75,7 +75,11 @@ String *Item_func_md5::val_str(String *str) my_MD5Init (&context); my_MD5Update (&context,(unsigned char *) sptr->ptr(), sptr->length()); my_MD5Final (digest, &context); - str->alloc(32); // Ensure that memory is free + 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], @@ -201,133 +205,167 @@ void Item_func_concat::fix_length_and_dec() } } -#define bin_to_ascii(c) ((c)>=38?((c)-38+'a'):(c)>=12?((c)-12+'A'):(c)+'.') -#define ascii_to_bin(c) ((c)<=57 ? (c)-46 : (c)<=90 ? (c)-53 : (c)-59) - /* - Function des_encrypt() by tonu@spam.ee - Works only if compiled with OpenSSL library support. - Output always starts with magic char "1" and all - encrypted output is encoded into ASCII-protected - container. - Original input is returned as output if input string - begins with magic "1". Credit card number always begin - with 4,5 or 6. + 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=(8-(original_length % 8))*2+1 + new_length= org_length + (8-(org_length % 8))+1 */ String *Item_func_des_encrypt::val_str(String *str) { - String *res =args[0]->val_str(str); #ifdef HAVE_OPENSSL - des_key_schedule ks1, ks2, ks3; - des_cblock ivec={0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; - struct { - des_cblock key1, key2, key3; // 8 bytes each - } keyblock; + 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() == 0) + if ((res_length=res->length()) == 0) return &empty_string; - if(res->c_ptr()[0]!='1') { // Skip encryption if already encrypted + + 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->c_ptr(), - (int)keystr->length(),1,(uchar *)&keyblock,ivec); - des_set_key_unchecked(&keyblock.key1,ks1); // Here we set all 64-bit keys - des_set_key_unchecked(&keyblock.key2,ks2); // (56 effective) one by one - des_set_key_unchecked(&keyblock.key3,ks3); - /* - The problem: DES algorithm requires original data to be in 8-bytes - chunks. Missing bytes get filled with zeros and result of encryption - can be up to 7 bytes longer than original string. When decrypted, - we do not know the size of original string :( - We add one byte with value 0x0..0x7 to original plaintext marking - change of string length - */ - uchar tail= 7-( res->length() %8); // 0..7 marking real offsets 1..8 - for(int i=0 ; i < tail ; ++i) res->append('*'); - res->append(tail); // Write tail length 0..7 to last pos - str->length(res->length()); - des_ede3_cbc_encrypt( // Real encryption - (const uchar*)(res->c_ptr()), - (uchar*)(str->c_ptr()), - res->length(), ks1, ks2, ks3, &ivec, TRUE); - res->set((const char*)"1",(uint)1); - for(uint i=0 ; i < str->length() ; ++i) - { - res->append(bin_to_ascii((uchar)str->c_ptr()[i] & 0x3f)); - res->append(bin_to_ascii(((uchar)str->c_ptr()[i] >> 5 ) & 0x3f)); - } - } - return res; -#else + (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; -#endif /* HAVE_OPENSSL */ } + String *Item_func_des_decrypt::val_str(String *str) { - String *res =args[0]->val_str(str); #ifdef HAVE_OPENSSL des_key_schedule ks1, ks2, ks3; - des_cblock ivec={0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; - struct { - des_cblock key1, key2, key3; // 8 bytes each - } keyblock; - + 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; - if (res->length() == 0) - return &empty_string; - - if(res->c_ptr()[0]=='1') // Skip decryption if not encrypted + 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 { - str->set((const char*)0,(uint)0); - for(uint i=1 ; i < res->length() ; i+=2) - { - str->append((ascii_to_bin(res->c_ptr()[i])) - | (ascii_to_bin(res->c_ptr()[i+1]) << 5 )); - } - + 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); - int32 mode=0; - if(arg_count == 3 && !args[2]->null_value) - mode=args[2]->val_int(); - /* We make good 24-byte (168 bit) key from given plaintext key with MD5 */ + if (!keystr) + goto error; + + bzero((char*) &ivec,sizeof(ivec)); EVP_BytesToKey(EVP_des_ede3_cbc(),EVP_md5(),NULL, - (uchar *)keystr->c_ptr(), - (int)keystr->length(),1,(uchar *)&keyblock,ivec); - des_set_key_unchecked(&keyblock.key1,ks1); // Here we set all 64-bit keys - des_set_key_unchecked(&keyblock.key2,ks2); // (56 effective) one by one - des_set_key_unchecked(&keyblock.key3,ks3); - res->length(str->length()); - des_ede3_cbc_encrypt( // Real decryption - (const uchar*)(str->c_ptr()), - (uchar*)(res->c_ptr()), - str->length(), - ks1, ks2, ks3, &ivec, FALSE); - uchar tail=(res->c_ptr()[res->length()-1]) & 0x7; - if((res->length() > ((uint)1+tail))) // We should avoid negative length - res->length(res->length()-1-tail); // (can happen with wrong key) - } - return res; -#else + (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; -#endif /* HAVE_OPENSSL */ } - /* -** 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) @@ -1117,6 +1155,7 @@ String *Item_func_password::val_str(String *str) return str; } +#define bin_to_ascii(c) ((c)>=38?((c)-38+'a'):(c)>=12?((c)-12+'A'):(c)+'.') String *Item_func_encrypt::val_str(String *str) { @@ -1248,9 +1287,9 @@ 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)) // Skip pre-space @@ -1274,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; } @@ -1733,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) @@ -1854,7 +1932,7 @@ String* Item_func_inet_ntoa::val_str(String* str) // 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-- ) ; num[3]='.'; while (p-- > buf) diff --git a/sql/item_strfunc.h b/sql/item_strfunc.h index 31c832c8ddb..1279a5099d5 100644 --- a/sql/item_strfunc.h +++ b/sql/item_strfunc.h @@ -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 @@ -228,9 +233,9 @@ class Item_func_des_encrypt :public Item_str_func public: Item_func_des_encrypt(Item *a) :Item_str_func(a) {} Item_func_des_encrypt(Item *a, Item *b): Item_str_func(a,b) {} - Item_func_des_encrypt(Item *a, Item *b, Item *c): Item_str_func(a,b,c) {} String *val_str(String *); - void fix_length_and_dec() { maybe_null=1; max_length = args[0]->max_length; } + void fix_length_and_dec() + { maybe_null=1; max_length = args[0]->max_length+8; } const char *func_name() const { return "des_encrypt"; } }; @@ -240,7 +245,6 @@ class Item_func_des_decrypt :public Item_str_func public: Item_func_des_decrypt(Item *a) :Item_str_func(a) {} Item_func_des_decrypt(Item *a, Item *b): Item_str_func(a,b) {} - Item_func_des_decrypt(Item *a, Item *b, Item *c): Item_str_func(a,b,c) {} 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"; } @@ -298,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 *); @@ -413,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 431d8b56e6a..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 */ @@ -967,7 +967,7 @@ bool Item_sum_count_distinct::setup(THD *thd) 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) { @@ -976,7 +976,7 @@ bool Item_sum_count_distinct::setup(THD *thd) // to make things easier for dump_leaf if we ever have to dump to MyISAM restore_record(table,2); - + if (table->fields == 1) { /* diff --git a/sql/item_sum.h b/sql/item_sum.h index 18b0c3ff577..4840ae8298d 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 */ @@ -159,7 +159,7 @@ class Item_sum_count_distinct :public Item_sum_int // 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 @@ -167,7 +167,7 @@ class Item_sum_count_distinct :public Item_sum_int 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); diff --git a/sql/item_timefunc.cc b/sql/item_timefunc.cc index b198900d24e..8f55a02b020 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=&str_value; // Save result here + str= &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 1343cdad390..32b85e7f028 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; } @@ -307,16 +343,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); }; @@ -330,6 +366,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, @@ -339,7 +384,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; @@ -347,7 +392,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);} @@ -368,3 +413,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 b6fb260bf36..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 */ @@ -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 894fdfb5362..6bee4152e48 100644 --- a/sql/lex.h +++ b/sql/lex.h @@ -76,6 +76,7 @@ static SYMBOL symbols[] = { { "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}, @@ -110,16 +111,19 @@ 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}, { "DOUBLE", SYM(DOUBLE_SYM),0,0}, { "DROP", SYM(DROP),0,0}, { "DUMPFILE", SYM(DUMPFILE),0,0}, @@ -223,6 +227,7 @@ static SYMBOL symbols[] = { { "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}, { "MATCH", SYM(MATCH),0,0}, { "MEDIUMBLOB", SYM(MEDIUMBLOB),0,0}, { "MEDIUMTEXT", SYM(MEDIUMTEXT),0,0}, @@ -247,6 +252,7 @@ static SYMBOL symbols[] = { { "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}, @@ -267,6 +273,7 @@ static SYMBOL symbols[] = { { "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}, @@ -294,6 +301,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}, @@ -305,12 +313,15 @@ 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}, @@ -382,7 +393,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}, @@ -390,6 +403,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)}, @@ -404,8 +418,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),0,0}, - { "DES_DECRYPT", SYM(DES_DECRYPT),0,0}, + { "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}, @@ -489,9 +503,9 @@ static SYMBOL sql_functions[] = { { "TO_DAYS", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_to_days)}, { "TRIM", SYM(TRIM),0,0}, { "UCASE", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_ucase)}, - { "UPPER", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_ucase)}, { "UNIQUE_USERS", SYM(UNIQUE_USERS),0,0}, { "UNIX_TIMESTAMP", SYM(UNIX_TIMESTAMP),0,0}, + { "UPPER", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_ucase)}, { "USER", SYM(USER),0,0}, { "VERSION", SYM(FUNC_ARG0),0,CREATE_FUNC(create_func_version)}, { "WEEK", SYM(WEEK_SYM),0,0}, 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 520821629e3..9f4f23b01e3 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 */ diff --git a/sql/matherr.c b/sql/matherr.c index ed18438cd0c..ea0c15d2feb 100644 --- a/sql/matherr.c +++ b/sql/matherr.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 */ diff --git a/sql/mf_iocache.cc b/sql/mf_iocache.cc index 2a7b25eab2f..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 */ @@ -50,14 +50,14 @@ extern "C" { 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) 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; DBUG_RETURN(1); diff --git a/sql/mini_client.cc b/sql/mini_client.cc index a43e5710e60..a5879081566 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,21 +22,15 @@ in case we decide to make them external at some point */ +#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 <my_global.h> @@ -53,12 +47,7 @@ inline int local_thr_alarm(my_bool *A,int B __attribute__((unused)),ALARM *C __a #include "mysqld_error.h" #include "errmsg.h" -#ifdef EMBEDDED_LIBRARY -#define net_read_timeout net_read_timeout1 -#define net_write_timeout net_write_timeout1 -#endif - -#if defined( OS2) && defined( MYSQL_SERVER) +#if defined( OS2) && defined(MYSQL_SERVER) #undef ER #define ER CER #endif @@ -82,18 +71,17 @@ 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_free_rows(MYSQL_DATA *cur); @@ -808,7 +796,7 @@ mc_mysql_connect(MYSQL *mysql,const char *host, const char *user, #endif /* HAVE_OPENSSL */ int3store(buff+2,max_allowed_packet); - + if (user && user[0]) strmake(buff+5,user,32); else @@ -1046,7 +1034,7 @@ 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) + if (!length) length = strlen(query); if (mc_simple_command(mysql,COM_QUERY,query,length,1)) DBUG_RETURN(-1); @@ -1055,58 +1043,62 @@ int mc_mysql_query(MYSQL *mysql, const char *query, uint length) static int mc_send_file_to_server(MYSQL *mysql, const char *filename) { - int fd, readcount; - char buf[IO_SIZE*15],*tmp_name; + 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"); - fn_format(buf,filename,"","",4); /* Convert to client format */ - if (!(tmp_name=my_strdup(buf,MYF(0)))) + 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; - sprintf(buf,EE(mysql->net.last_errno),tmp_name,errno); - strmake(mysql->net.last_error,buf,sizeof(mysql->net.last_error)-1); - my_net_write(&mysql->net,"",0); net_flush(&mysql->net); - my_free(tmp_name,MYF(0)); - DBUG_RETURN(-1); + 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,sizeof(buf),MYF(0))) > 0) + 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)); - DBUG_PRINT("error",("Lost connection to MySQL server during LOAD DATA of local file")); - (void) my_close(fd,MYF(0)); - my_free(tmp_name,MYF(0)); - DBUG_RETURN(-1); + goto err; } } - (void) my_close(fd,MYF(0)); /* Send empty packet to mark end of file */ if (my_net_write(&mysql->net,"",0) || net_flush(&mysql->net)) { mysql->net.last_errno=CR_SERVER_LOST; sprintf(mysql->net.last_error,ER(mysql->net.last_errno),errno); - my_free(tmp_name,MYF(0)); - DBUG_RETURN(-1); + goto err; } if (readcount < 0) { mysql->net.last_errno=EE_READ; /* the errmsg for not entire file read */ - sprintf(buf,EE(mysql->net.last_errno),tmp_name,errno); - strmake(mysql->net.last_error,buf,sizeof(mysql->net.last_error)-1); - my_free(tmp_name,MYF(0)); - DBUG_RETURN(-1); + my_snprintf(mysql->net.last_error,sizeof(mysql->net.last_error)-1, + tmp_name,errno); + goto err; } - DBUG_RETURN(0); + 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) { @@ -1186,7 +1178,7 @@ static MYSQL_DATA *mc_read_rows(MYSQL *mysql,MYSQL_FIELD *mysql_fields, if ((pkt_len=mc_net_safe_read(mysql)) == packet_error) DBUG_RETURN(0); if (!(result=(MYSQL_DATA*) my_malloc(sizeof(MYSQL_DATA), - MYF(MY_WME | MY_ZEROFILL)))) + MYF(MY_ZEROFILL)))) { net->last_errno=CR_OUT_OF_MEMORY; strmov(net->last_error,ER(net->last_errno)); @@ -1373,7 +1365,7 @@ MYSQL_RES *mc_mysql_store_result(MYSQL *mysql) mysql->status=MYSQL_STATUS_READY; /* server is ready */ if (!(result=(MYSQL_RES*) my_malloc(sizeof(MYSQL_RES)+ sizeof(ulong)*mysql->field_count, - MYF(MY_WME | MY_ZEROFILL)))) + MYF(MY_ZEROFILL)))) { mysql->net.last_errno=CR_OUT_OF_MEMORY; strmov(mysql->net.last_error, ER(mysql->net.last_errno)); @@ -1396,11 +1388,3 @@ MYSQL_RES *mc_mysql_store_result(MYSQL *mysql) 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 1e17d34244e..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 */ @@ -49,4 +49,3 @@ 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 19d38fd9c79..9b4ac502e57 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 */ diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index 12a043250a5..8ccf0de27d6 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -149,7 +149,7 @@ char* query_table_status(THD *thd,const char *db,const char *table_name); #define SELECT_SMALL_RESULT 8 #define SELECT_BIG_RESULT 16 #define OPTION_FOUND_ROWS 32 -#define SELECT_HIGH_PRIORITY 64 /* Intern */ +#define OPTION_TO_QUERY_CACHE 64 #define SELECT_NO_JOIN_CACHE 256 /* Intern */ #define OPTION_BIG_TABLES 512 /* for SQL OPTION */ @@ -180,6 +180,7 @@ char* query_table_status(THD *thd,const char *db,const char *table_name); #define SELECT_NO_UNLOCK (QUERY_NO_GOOD_INDEX_USED*2) #define TMP_TABLE_ALL_COLUMNS (SELECT_NO_UNLOCK*2) + #define MODE_REAL_AS_FLOAT 1 #define MODE_PIPES_AS_CONCAT 2 #define MODE_ANSI_QUOTES 4 @@ -248,7 +249,7 @@ inline THD *_current_thd(void) #include "item.h" #include "sql_class.h" #include "opt_range.h" - +#include "sql_cache.h" 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); @@ -262,7 +263,8 @@ bool mysql_rename_tables(THD *thd, TABLE_LIST *table_list); bool mysql_change_db(THD *thd,const char *name); void mysql_parse(THD *thd,char *inBuf,uint length); void mysql_init_select(LEX *lex); -void mysql_new_select(LEX *lex); +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); @@ -275,15 +277,16 @@ 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); +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 mysql_backup_table(THD* thd, TABLE_LIST* table_list); @@ -330,7 +333,7 @@ int mysql_select(THD *thd,TABLE_LIST *tables,List<Item> &list,COND *conds, ORDER *order, ORDER *group,Item *having,ORDER *proc_param, ulong select_type,select_result *result); int mysql_union(THD *thd,LEX *lex,select_result *result); -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); int mysql_create_table(THD *thd,const char *db, const char *table_name, @@ -395,6 +398,24 @@ 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); @@ -416,7 +437,7 @@ int mysqld_show(THD *thd, const char *wild, show_var_st *variables); /* sql_handler.cc */ int mysql_ha_open(THD *thd, TABLE_LIST *tables); -int mysql_ha_close(THD *thd, TABLE_LIST *tables); +int mysql_ha_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); @@ -492,7 +513,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; @@ -543,6 +564,7 @@ extern ulong select_full_range_join_count,select_full_join_count, slave_open_temp_tables; 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; @@ -578,7 +600,8 @@ extern ulong keybuff_size,sortbuff_size,max_item_sort_length,table_cache_size, 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; + 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; extern bool opt_sql_bin_update, opt_safe_show_db, opt_warnings, @@ -698,7 +721,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); diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 6d99db62f12..b6e33a32b74 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -27,7 +27,7 @@ #include "ha_berkeley.h" #endif #ifdef HAVE_INNOBASE_DB -#include "ha_innobase.h" +#include "ha_innodb.h" #endif #include "ha_myisam.h" #include <nisam.h> @@ -114,8 +114,13 @@ typedef fp_except fp_except_t; inline void reset_floating_point_exceptions() { /* Don't fall for overflow, underflow,divide-by-zero or loss of precision */ - fpsetmask(~(FP_X_INV | FP_X_DNML | FP_X_OFL | FP_X_UFL | - FP_X_DZ | FP_X_IMP)); +#if defined(__i386__) + fpsetmask(~(FP_X_INV | FP_X_DNML | FP_X_OFL | FP_X_UFL | FP_X_DZ | + FP_X_IMP)); +#else + fpsetmask(~(FP_X_INV | FP_X_OFL | FP_X_UFL | FP_X_DZ | + FP_X_IMP)); +#endif } #else #define reset_floating_point_exceptions() @@ -223,6 +228,7 @@ static bool opt_log,opt_update_log,opt_bin_log,opt_slow_log,opt_noacl, bool opt_sql_bin_update = 0, opt_log_slave_updates = 0, opt_safe_show_db=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 @@ -240,6 +246,7 @@ char glob_hostname[FN_REFLEN]; #include "sslopt-vars.h" #ifdef HAVE_OPENSSL +char *des_key_file = 0; struct st_VioSSLAcceptorFd * ssl_acceptor_fd = 0; #endif /* HAVE_OPENSSL */ @@ -266,8 +273,10 @@ ulong keybuff_size,sortbuff_size,max_item_sort_length,table_cache_size, net_interactive_timeout, slow_launch_time = 2L, net_read_timeout,net_write_timeout,slave_open_temp_tables=0, open_files_limit=0, max_binlog_size, record_rnd_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, query_cache_limit=0, query_cache_startup_type=1; volatile ulong cached_thread_count=0; // replication parameters, if master_host is not NULL, we are a slave @@ -360,7 +369,10 @@ pthread_mutex_t LOCK_mysql_create_db, LOCK_Acl, LOCK_open, LOCK_thread_count, LOCK_server_id, LOCK_user_conn, LOCK_slave_list, LOCK_active_mi; -pthread_cond_t COND_refresh,COND_thread_count; +Query_cache query_cache; + +pthread_cond_t COND_refresh,COND_thread_count,COND_binlog_update, + COND_slave_stopped, COND_slave_start; pthread_cond_t COND_thread_cache,COND_flush_thread_cache; pthread_t signal_thread; pthread_attr_t connection_attrib; @@ -369,10 +381,12 @@ enum db_type default_table_type=DB_TYPE_MYISAM; #ifdef __WIN__ #undef getpid #include <process.h> +#if !defined(EMBEDDED_LIBRARY) HANDLE hEventShutdown; #include "nt_servc.h" static NTService Service; // Service object for WinNT #endif +#endif #ifdef OS2 pthread_cond_t eventShutdown; @@ -385,6 +399,7 @@ static void get_options(int argc,char **argv); static char *get_relative_path(const char *path); 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); @@ -436,15 +451,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, @@ -603,8 +610,9 @@ void kill_mysql(void) #ifdef SIGNALS_DONT_BREAK_READ close_server_sock(); /* force accept to wake up */ #endif - + #if defined(__WIN__) +#if !defined(EMBEDDED_LIBRARY) { if (!SetEvent(hEventShutdown)) { @@ -617,22 +625,30 @@ void kill_mysql(void) CloseHandle(hEvent); */ } +#endif #elif defined(OS2) pthread_cond_signal( &eventShutdown); // post semaphore #elif defined(HAVE_PTHREAD_KILL) - if (pthread_kill(signal_thread,SIGTERM)) /* End everything nicely */ - { - DBUG_PRINT("error",("Got error %d from pthread_kill",errno)); /* purecov: inspected */ - } -#else - kill(current_pid,SIGTERM); + 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,SIGTERM); #endif - DBUG_PRINT("quit",("After pthread_kill")); - shutdown_in_progress=1; // Safety if kill didn't work -#ifdef SIGNALS_DONT_BREAK_READ + DBUG_PRINT("quit",("After pthread_kill")); + shutdown_in_progress=1; // Safety if kill didn't work +#ifdef SIGNALS_DONT_BREAK_READ + if (!abort_loop) + { + pthread_t tmp; abort_loop=1; + if (pthread_create(&tmp,&connection_attrib, kill_server_thread, + (void*) 0)) + sql_print_error("Error: Can't create thread to kill server"); + } #endif - DBUG_VOID_RETURN; + DBUG_VOID_RETURN; } @@ -677,7 +693,7 @@ 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))) { my_thread_init(); // Initialize new thread kill_server(0); @@ -730,9 +746,11 @@ void clean_up(bool print_message) DBUG_PRINT("exit",("clean_up")); if (cleanup_done++) return; /* purecov: inspected */ + if (use_slave_mask) + 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(); @@ -1471,7 +1489,7 @@ static void *signal_hand(void *arg __attribute__((unused))) (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; @@ -1549,8 +1567,9 @@ pthread_handler_decl(handle_shutdown,arg) /* 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; } @@ -1745,9 +1764,11 @@ int main(int argc, char **argv) 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 @@ -1796,7 +1817,7 @@ int main(int argc, char **argv) #endif select_thread=pthread_self(); select_thread_in_use=1; - if (use_temp_pool && bitmap_init(&temp_pool,1024)) + if (use_temp_pool && bitmap_init(&temp_pool,1024,1)) unireg_abort(1); /* @@ -1812,12 +1833,13 @@ 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(); init_slave_list(); - + /* Setup log files */ if (opt_log) open_log(&mysql_log, glob_hostname, opt_logname, ".log", LOG_NORMAL); @@ -1928,7 +1950,7 @@ The server will not act as a slave."); } if (!opt_noacl) (void) grant_init(); - if (max_user_connections) + if (max_user_connections || mqh_used) init_max_user_conn(); #ifdef HAVE_DLOPEN @@ -1951,7 +1973,7 @@ 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"); pthread_t hThread; @@ -2048,6 +2070,7 @@ The server will not act as a slave."); sql_print_error("After lock_thread_count"); #endif #else +#if !defined(EMBEDDED_LIBRARY) if (Service.IsNT()) { if(start_mode) @@ -2067,13 +2090,14 @@ The server will not act as a slave."); if(hEventShutdown) CloseHandle(hEventShutdown); } #endif +#endif #ifdef HAVE_OPENSSL my_free((gptr)ssl_acceptor_fd,MYF(MY_ALLOW_ZERO_PTR)); #endif /* HAVE_OPENSSL */ /* Wait until cleanup is done */ (void) pthread_mutex_lock(&LOCK_thread_count); DBUG_PRINT("quit", ("Got thread_count mutex for clean up wait")); - + while (!ready_to_exit) { DBUG_PRINT("quit", ("not yet ready to exit")); @@ -2087,7 +2111,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) @@ -2273,7 +2297,7 @@ static void create_new_thread(THD *thd) (void) pthread_mutex_unlock(&LOCK_thread_count); DBUG_VOID_RETURN; } - + (void) pthread_mutex_unlock(&LOCK_thread_count); } } @@ -2422,18 +2446,21 @@ pthread_handler_decl(handle_connections_sockets,arg __attribute__((unused))) fromhost(&req); if (!hosts_access(&req)) { - // This may be stupid but refuse() includes an exit(0) - // which we surely don't want... - // clean_exit() - same stupid thing ... + /* + This may be stupid but refuse() includes an exit(0) + which we surely don't want... + clean_exit() - same stupid thing ... + */ syslog(deny_severity, "refused connect from %s", eval_client(&req)); if (req.sink) ((void (*)(int))req.sink)(req.fd); - // C++ sucks (the gibberish in front just translates the supplied - // sink function pointer in the req structure from a void (*sink)(); - // to a void(*sink)(int) if you omit the cast, the C++ compiler - // will cry... - + /* + C++ sucks (the gibberish in front just translates the supplied + sink function pointer in the req structure from a void (*sink)(); + to a void(*sink)(int) if you omit the cast, the C++ compiler + will cry... + */ (void) shutdown(new_sock,2); // This looks fine to me... (void) closesocket(new_sock); continue; @@ -2461,7 +2488,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, @@ -2646,7 +2674,8 @@ enum options { 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_RELAY_LOG, OPT_RELAY_LOG_INDEX, OPT_RELAY_LOG_INFO_FILE, + OPT_SLAVE_SKIP_ERRORS, OPT_DES_KEY_FILE }; static struct option long_options[] = { @@ -2674,6 +2703,7 @@ static struct option long_options[] = { {"character-sets-dir", required_argument, 0, (int) OPT_CHARSETS_DIR}, {"datadir", required_argument, 0, 'h'}, {"debug", optional_argument, 0, '#'}, + {"des-key-file", required_argument, 0, (int) OPT_DES_KEY_FILE}, {"default-character-set", required_argument, 0, 'C'}, {"default-table-type", required_argument, 0, (int) OPT_TABLE_TYPE}, {"delay-key-write-for-all-tables", @@ -2799,6 +2829,8 @@ static struct option long_options[] = { {"relay-log-info-file", required_argument, 0, (int) OPT_RELAY_LOG_INFO_FILE}, {"slave-load-tmpdir", required_argument, 0, (int) OPT_SLAVE_LOAD_TMPDIR}, + {"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}, @@ -2871,9 +2903,9 @@ CHANGEABLE_VAR changeable_vars[] = { (long*) &innobase_additional_mem_pool_size, 1*1024*1024L, 512*1024L, ~0L, 0, 1024}, {"innodb_file_io_threads", - (long*) &innobase_file_io_threads, 9, 4, 64, 0, 1}, + (long*) &innobase_file_io_threads, 4, 4, 64, 0, 1}, {"innodb_lock_wait_timeout", - (long*) &innobase_lock_wait_timeout, 1024 * 1024 * 1024, 1, + (long*) &innobase_lock_wait_timeout, 50, 1, 1024 * 1024 * 1024, 0, 1}, {"innodb_thread_concurrency", (long*) &innobase_thread_concurrency, 8, 1, 1000, 0, 1}, @@ -2915,7 +2947,7 @@ CHANGEABLE_VAR changeable_vars[] = { { "max_write_lock_count", (long*) &max_write_lock_count, ~0L, 1, ~0L, 0, 1 }, { "myisam_bulk_insert_tree_size", (long*) &myisam_bulk_insert_tree_size, - 8192*1024, 4, ~0L, 0, 1 }, + 8192*1024, 0, ~0L, 0, 1 }, { "myisam_block_size", (long*) &opt_myisam_block_size, MI_KEY_BLOCK_LENGTH, MI_MIN_KEY_BLOCK_LENGTH, MI_MAX_KEY_BLOCK_LENGTH, 0, MI_MIN_KEY_BLOCK_LENGTH }, @@ -2938,6 +2970,12 @@ CHANGEABLE_VAR changeable_vars[] = { 0, 0, 65535, 0, 1}, { "query_buffer_size", (long*) &query_buff_size, 0, MALLOC_OVERHEAD, (long) ~0, MALLOC_OVERHEAD, IO_SIZE }, + { "query_cache_limit", (long*) &query_cache_limit, + 1024*1024L, 0, ULONG_MAX, 0, 1}, + { "query_cache_size", (long*) &query_cache_size, + 0, 0, ULONG_MAX, 0, 1}, + { "query_cache_startup_type",(long*) &query_cache_startup_type, + 1, 0, 2, 0, 1}, { "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, @@ -2992,7 +3030,7 @@ struct show_var_st init_vars[]= { {"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", ft_boolean_syntax, SHOW_CHAR}, + {"ft_boolean_syntax", (char*) ft_boolean_syntax, SHOW_CHAR}, {"have_bdb", (char*) &have_berkeley_db, SHOW_HAVE}, {"have_innodb", (char*) &have_innodb, SHOW_HAVE}, {"have_isam", (char*) &have_isam, SHOW_HAVE}, @@ -3066,6 +3104,9 @@ struct show_var_st init_vars[]= { {"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}, + {"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}, {"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}, @@ -3099,6 +3140,71 @@ struct show_var_st status_vars[]= { {"Aborted_connects", (char*) &aborted_connects, SHOW_LONG}, {"Bytes_received", (char*) &bytes_received, SHOW_LONG}, {"Bytes_sent", (char*) &bytes_sent, SHOW_LONG}, + {"Com_admin_commands", (char*) &com_other, SHOW_LONG}, + {"Com_alter_table", (char*) (com_stat+(uint) SQLCOM_ALTER_TABLE),SHOW_LONG}, + {"Com_analyze", (char*) (com_stat+(uint) SQLCOM_ANALYZE),SHOW_LONG}, + {"Com_backup_table", (char*) (com_stat+(uint) SQLCOM_BACKUP_TABLE),SHOW_LONG}, + {"Com_begin", (char*) (com_stat+(uint) SQLCOM_BEGIN),SHOW_LONG}, + {"Com_change_db", (char*) (com_stat+(uint) SQLCOM_CHANGE_DB),SHOW_LONG}, + {"Com_change_master", (char*) (com_stat+(uint) SQLCOM_CHANGE_MASTER),SHOW_LONG}, + {"Com_check", (char*) (com_stat+(uint) SQLCOM_CHECK),SHOW_LONG}, + {"Com_commit", (char*) (com_stat+(uint) SQLCOM_COMMIT),SHOW_LONG}, + {"Com_create_db", (char*) (com_stat+(uint) SQLCOM_CREATE_DB),SHOW_LONG}, + {"Com_create_function", (char*) (com_stat+(uint) SQLCOM_CREATE_FUNCTION),SHOW_LONG}, + {"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}, + {"Com_purge", (char*) (com_stat+(uint) SQLCOM_PURGE),SHOW_LONG}, + {"Com_rename_table", (char*) (com_stat+(uint) SQLCOM_RENAME_TABLE),SHOW_LONG}, + {"Com_repair", (char*) (com_stat+(uint) SQLCOM_REPAIR),SHOW_LONG}, + {"Com_replace", (char*) (com_stat+(uint) SQLCOM_REPLACE),SHOW_LONG}, + {"Com_replace_select", (char*) (com_stat+(uint) SQLCOM_REPLACE_SELECT),SHOW_LONG}, + {"Com_reset", (char*) (com_stat+(uint) SQLCOM_RESET),SHOW_LONG}, + {"Com_restore_table", (char*) (com_stat+(uint) SQLCOM_RESTORE_TABLE),SHOW_LONG}, + {"Com_revoke", (char*) (com_stat+(uint) SQLCOM_REVOKE),SHOW_LONG}, + {"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}, + {"Com_show_fields", (char*) (com_stat+(uint) SQLCOM_SHOW_FIELDS),SHOW_LONG}, + {"Com_show_grants", (char*) (com_stat+(uint) SQLCOM_SHOW_GRANTS),SHOW_LONG}, + {"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_stat", (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_stat", (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}, + {"Com_show_variables", (char*) (com_stat+(uint) SQLCOM_SHOW_VARIABLES),SHOW_LONG}, + {"Com_slave_start", (char*) (com_stat+(uint) SQLCOM_SLAVE_START),SHOW_LONG}, + {"Com_slave_stop", (char*) (com_stat+(uint) SQLCOM_SLAVE_STOP),SHOW_LONG}, + {"Com_truncate", (char*) (com_stat+(uint) SQLCOM_TRUNCATE),SHOW_LONG}, + {"Com_unlock_tables", (char*) (com_stat+(uint) SQLCOM_UNLOCK_TABLES),SHOW_LONG}, + {"Com_update", (char*) (com_stat+(uint) SQLCOM_UPDATE),SHOW_LONG}, {"Connections", (char*) &thread_id, SHOW_LONG_CONST}, {"Created_tmp_disk_tables", (char*) &created_tmp_disk_tables,SHOW_LONG}, {"Created_tmp_tables", (char*) &created_tmp_tables, SHOW_LONG}, @@ -3129,8 +3235,17 @@ 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}, - {"Rpl_status", (char*) 0, - SHOW_RPL_STATUS}, + {"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}, + {"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}, @@ -3145,29 +3260,29 @@ struct show_var_st status_vars[]= { {"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}, + {"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}, @@ -3189,7 +3304,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) { @@ -3228,7 +3343,13 @@ static void usage(void) 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\ + table\n"); +#ifdef HAVE_OPENSSL + puts("\ + --des-key-file Load keys for des_encrypt() and des_encrypt\n\ + from given file"); +#endif /* HAVE_OPENSSL */ + puts("\ --enable-locking Enable system locking\n\ --enable-pstack Print a symbolic stack trace on failure\n\ -T, --exit-info Used for debugging; Use at your own risk!\n\ @@ -3421,7 +3542,14 @@ static void get_options(int argc,char **argv) int c,option_index=0; 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 optind = 0; // setup in case getopt() was called previously while ((c=getopt_long(argc,argv,"ab:C:h:#::T::?l::L:O:P:sS::t:u:noVvWI?", @@ -3472,6 +3600,9 @@ static void get_options(int argc,char **argv) case 'P': mysql_port= (unsigned int) atoi(optarg); break; + case OPT_SLAVE_SKIP_ERRORS: + init_slave_skip_errors(optarg); + break; case OPT_SAFEMALLOC_MEM_LIMIT: #if !defined(DBUG_OFF) && defined(SAFEMALLOC) safemalloc_mem_limit = atoi(optarg); @@ -3706,7 +3837,6 @@ static void get_options(int argc,char **argv) 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; @@ -3714,6 +3844,7 @@ static void get_options(int argc,char **argv) my_use_symdir=0; have_symlink=SHOW_OPTION_DISABLED; ha_open_options&= ~HA_OPEN_ABORT_IF_CRASHED; + query_cache_size=0; break; case (int) OPT_SAFE: opt_specialflag|= SPECIAL_SAFE_MODE; @@ -3863,6 +3994,11 @@ static void get_options(int argc,char **argv) charsets_dir = mysql_charsets_dir; break; #include "sslopt-case.h" + case OPT_DES_KEY_FILE: +#ifdef HAVE_OPENSSL + des_key_file=optarg; +#endif + break; case OPT_TX_ISOLATION: { int type; @@ -3944,6 +4080,7 @@ static void get_options(int argc,char **argv) 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; @@ -4168,16 +4305,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; } } diff --git a/sql/net_pkg.cc b/sql/net_pkg.cc index 0b50b34c7bd..64c1b07a493 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,9 +104,9 @@ 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); offset= net->return_errno ? 2 : 0; @@ -119,6 +121,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 +145,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 5e002a0f63e..7a1d25e980d 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 +/* Copyright (C) 2000 MySQL 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 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 library is distributed in the hope that it will be useful, + 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. + 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 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 */ + 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. @@ -42,73 +41,61 @@ #include <signal.h> #include <errno.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 - -/* -** Give error if a too big packet is found -** The server can change this with the -O switch, but because the client -** can't normally do this the client should have a bigger max_allowed_packet. -*/ - -ulong max_allowed_packet=~0L; +ulong 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) -#undef MYSQL_SERVER /* Win32 can't handle interrupts */ +#if defined(__WIN__) || !defined(MYSQL_SERVER) + /* The following is because alarms doesn't work on windows. */ +#define NO_ALARM #endif -#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) != 0) -#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 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 */ + +#include "thr_alarm.h" #define TEST_BLOCKING 8 +#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); -#define MAX_THREE_BYTES 255L*255L*255L /* Init with packet info */ int my_net_init(NET *net, Vio* vio) { - if (!(net->buff=(uchar*) my_malloc(net_buffer_length+ + if (!(net->buff=(uchar*) my_malloc((uint32) net_buffer_length+ NET_HEADER_SIZE + COMP_HEADER_SIZE, MYF(MY_WME)))) return 1; @@ -125,6 +112,7 @@ int my_net_init(NET *net, Vio* vio) 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 */ { @@ -160,7 +148,7 @@ static my_bool net_realloc(NET *net, ulong length) pkt_length = (length+IO_SIZE-1) & ~(IO_SIZE-1); /* We must allocate some extra bytes for the end 0 and to be able to read big compressed blocks */ - if (!(buff=(uchar*) my_realloc((char*) net->buff, pkt_length + + if (!(buff=(uchar*) my_realloc((char*) net->buff, (uint32) pkt_length + NET_HEADER_SIZE + COMP_HEADER_SIZE, MYF(MY_WME)))) { @@ -179,7 +167,7 @@ static my_bool net_realloc(NET *net, ulong length) void net_clear(NET *net) { -#ifndef EXTRA_DEBUG +#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) @@ -187,7 +175,7 @@ void net_clear(NET *net) 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) @@ -241,7 +229,7 @@ my_net_write(NET *net,const char *packet,ulong len) { const ulong z_size = MAX_THREE_BYTES; int3store(buff, z_size); - buff[3]= net->pkt_nr++; + 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; @@ -250,7 +238,7 @@ my_net_write(NET *net,const char *packet,ulong len) } /* Write last packet */ int3store(buff,len); - buff[3]= 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); @@ -280,7 +268,7 @@ net_write_command(NET *net,uchar command,const char *packet,ulong len) do { int3store(buff, MAX_THREE_BYTES); - buff[3]= net->pkt_nr++; + buff[3]= (uchar) net->pkt_nr++; if (net_write_buff(net,(char*) buff, header_size) || net_write_buff(net,packet,len)) return 1; @@ -292,7 +280,7 @@ net_write_command(NET *net,uchar command,const char *packet,ulong len) len=length; /* Data left to be written */ } int3store(buff,length); - buff[3]= net->pkt_nr++; + 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)); } @@ -334,13 +322,18 @@ net_real_write(NET *net,const char *packet,ulong len) long int length; char *pos,*end; thr_alarm_t alarmed; -#if !defined(__WIN__) && !defined(__EMX__) && !defined(OS2) +#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"); +#ifdef MYSQL_SERVER + 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 */ @@ -351,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; @@ -378,18 +371,18 @@ net_real_write(NET *net,const char *packet,ulong len) #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 ((long) (length=vio_write(net->vio,pos,(ulong) (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)) @@ -466,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; @@ -496,11 +488,11 @@ static void my_net_skip_rest(NET *net, ulong remain, thr_alarm_t *alarmed) } return; } - remain -= length; + remain -= (uint32) length; statistic_add(bytes_received,length,&LOCK_bytes_received); } } -#endif /* MYSQL_SERVER */ +#endif /* NO_ALARM */ /* @@ -517,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++) @@ -599,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 @@ -608,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); } @@ -653,16 +645,16 @@ my_real_read(NET *net, ulong *complen) { 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; } } @@ -743,7 +735,7 @@ my_net_read(NET *net) for (;;) { ulong packet_len; - + if (buf_length - start_of_packet >= NET_HEADER_SIZE) { read_length = uint3korr(net->buff+start_of_packet); @@ -770,7 +762,7 @@ my_net_read(NET *net) if (read_length != MAX_THREE_BYTES) /* last package */ { - multi_byte_packet= 0; // No last zero length packet + multi_byte_packet= 0; /* No last zero len packet */ break; } multi_byte_packet= NET_HEADER_SIZE; diff --git a/sql/opt_range.h b/sql/opt_range.h index 07d1216a42f..83eb10235ea 100644 --- a/sql/opt_range.h +++ b/sql/opt_range.h @@ -48,9 +48,9 @@ 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) {} }; diff --git a/sql/opt_sum.cc b/sql/opt_sum.cc index 3dd13cbebb9..69fc5aa88ff 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 */ diff --git a/sql/password.c b/sql/password.c index 1db94e0db8a..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 */ 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 37f79e54cf6..f156fdaf406 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 */ @@ -86,6 +86,7 @@ void init_read_record(READ_RECORD *info,THD *thd, TABLE *table, } 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; diff --git a/sql/repl_failsafe.cc b/sql/repl_failsafe.cc index 705ef58d0e7..eabba9c68fc 100644 --- a/sql/repl_failsafe.cc +++ b/sql/repl_failsafe.cc @@ -191,10 +191,12 @@ void init_slave_list() void end_slave_list() { - pthread_mutex_lock(&LOCK_slave_list); - hash_free(&slave_list); - pthread_mutex_unlock(&LOCK_slave_list); - pthread_mutex_destroy(&LOCK_slave_list); + /* 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) @@ -256,7 +258,7 @@ int translate_master(THD* thd, LEX_MASTER_INFO* mi, char* errmsg) } linfo.index_file_offset = 0; - + search_file_name[0] = 0; @@ -364,7 +366,7 @@ static Slave_log_event* find_slave_event(IO_CACHE* log, 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))) @@ -436,7 +438,7 @@ int update_slave_list(MYSQL* mysql) 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))) { @@ -504,7 +506,7 @@ err: int find_recovery_captain(THD* thd, MYSQL* mysql) { - + return 0; } @@ -849,7 +851,3 @@ err: return error; } - - - - diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index 13923f0a637..091db4e4b39 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 - + 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 */ @@ -58,7 +58,7 @@ class ACL_USER :public ACL_ACCESS { public: acl_host_and_ip host; - uint hostname_length; + uint hostname_length, questions, updates; char *user,*password; ulong salt[2]; #ifdef HAVE_OPENSSL @@ -132,7 +132,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"; @@ -240,6 +241,15 @@ 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) + { + char *ptr = get_field(&mem, table, 21); + user.questions=atoi(ptr); + ptr = get_field(&mem, table, 22); + user.updates=atoi(ptr); + if (user.questions) + mqh_used=true; + } #ifndef TO_BE_REMOVED if (table->fields <= 13) { // Without grant @@ -423,7 +433,7 @@ static int acl_compare(ACL_ACCESS *a,ACL_ACCESS *b) /* Get master privilges for user (priviliges for all tables). Required to connect */ 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, uint *max) { uint user_access=NO_ACCESS; *priv_user=(char*) user; @@ -536,6 +546,7 @@ uint acl_getroot(THD *thd, const char *host, const char *ip, const char *user, #else /* HAVE_OPENSSL */ user_access=acl_user->access; #endif /* HAVE_OPENSSL */ + *max=acl_user->questions; if (!acl_user->user) *priv_user=(char*) ""; // Change to anonymous user /* purecov: inspected */ break; @@ -569,6 +580,7 @@ static void acl_update_user(const char *user, const char *host, const char *ssl_cipher, const char *x509_issuer, const char *x509_subject, + unsigned int mqh, uint privileges) { for (uint i=0 ; i < acl_users.elements ; i++) @@ -582,6 +594,7 @@ 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->questions=mqh; #ifdef HAVE_OPENSSL acl_user->ssl_type=ssl_type; acl_user->ssl_cipher=ssl_cipher; @@ -611,6 +624,7 @@ static void acl_insert_user(const char *user, const char *host, const char *ssl_cipher, const char *x509_issuer, const char *x509_subject, + unsigned int mqh, uint privileges) { ACL_USER acl_user; @@ -618,6 +632,7 @@ 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.questions=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 @@ -698,12 +713,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))) { @@ -943,7 +963,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, @@ -1201,6 +1221,13 @@ static int replace_user_table(THD *thd, TABLE *table, const LEX_USER &combo, } } #endif /* HAVE_OPENSSL */ + if (table->fields>=23 && thd->lex.mqh) + { + char buff[33]; + int len =int2str((long)thd->lex.mqh,buff,10) - buff; + table->field[21]->store(buff,len); + mqh_used=true; + } if (old_row_exists) { /* @@ -1239,6 +1266,7 @@ end: 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, @@ -1246,6 +1274,7 @@ end: thd->lex.ssl_cipher, thd->lex.x509_issuer, thd->lex.x509_subject, + thd->lex.mqh, rights); } table->file->index_end(); @@ -1377,6 +1406,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); @@ -1398,7 +1432,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(); @@ -1990,7 +2030,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"); @@ -2002,6 +2042,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 */ @@ -2098,7 +2144,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"; @@ -2220,8 +2267,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) { diff --git a/sql/sql_acl.h b/sql/sql_acl.h index e6a39f1b269..3b8616a660a 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 */ @@ -61,7 +61,7 @@ uint acl_get(const char *host, const char *ip, const char *bin_ip, const char *user, const char *db); 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, uint *max); bool acl_check_host(const char *host, const char *ip); bool change_password(THD *thd, const char *host, const char *user, char *password); diff --git a/sql/sql_analyse.cc b/sql/sql_analyse.cc index 161e0f9b2e7..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 */ @@ -890,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) @@ -909,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) @@ -928,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 2147f14e160..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 */ diff --git a/sql/sql_base.cc b/sql/sql_base.cc index b913233806f..a8be2d8b8f5 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -300,6 +300,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); @@ -311,6 +312,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 */ @@ -510,7 +512,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; @@ -1166,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; @@ -1301,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); @@ -1499,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); @@ -2195,16 +2195,17 @@ int setup_ftfuncs(THD *thd) int init_ftfuncs(THD *thd, bool no_order) { - List_iterator<Item_func_match> li(thd->lex.select_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_lex.ftfunc_list.elements) { - ifm->init_search(no_order); - } + List_iterator<Item_func_match> li(thd->lex.select_lex.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..73b15ff3cf1 100644 --- a/sql/sql_cache.cc +++ b/sql/sql_cache.cc @@ -1,98 +1,3446 @@ -/* 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: + + - Invalidate queries that use innoDB tables changed in transaction & remove + invalidation by table type + - 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); + - In Query_cache::insert_table eliminate strlen(). To do this we have to + add db_len to the TABLE_LIST and TABLE structures. +*/ + #include "mysql_priv.h" #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))); +} -#define SQL_CACHE_LENGTH 30 // 300 crashes apple gcc. +inline gptr Query_cache_block::data(void) +{ + return (gptr)( ((byte*)this) + headers_len() ); +} -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; +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(); +} -/* Function to return a text string from a LEX struct */ -static byte *cache_key(const byte *record, uint *length, my_bool not_used) +inline Query_cache_table * Query_cache_block::table() { -#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; +#ifndef DBUG_OFF + if (type != TABLE) + query_cache.wreck(__LINE__, "incorrect block type"); #endif - return 0; + return (Query_cache_table *) data(); } -/* At the moment we do not really want to do anything upon delete */ -static void free_cache_entry(void *entry) +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(); } -/* Initialization of the SQL cache hash -- should be called during - the bootstrap stage */ -bool sql_cache_init(void) +inline Query_cache_block_table * Query_cache_block::table(TABLE_COUNTER_TYPE n) { - if (query_buff_size) + 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; +} + + +/* + Following methods work for block read/write locking only in this + particular case and in interaction with structure_guard_mutex. + + 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) { - VOID(hash_init(&sql_cache, 4096, 0, 0, - cache_key, - (void (*)(void*)) free_cache_entry, - 0)); + DBUG_PRINT("info", ("can't lock semaphore")); + DBUG_RETURN(0); } - 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); } -/* Clearing the SQL cache hash -- during shutdown */ -void sql_cache_free(void) +extern "C" +{ +byte *query_cache_query_get_key(const byte *record, uint *length, + my_bool not_used) { - hash_free(&sql_cache); + 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 +*****************************************************************************/ -/* 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) */ +/* + Insert the packet into the query cache. + This should only be called if net->query_cache_query != 0 +*/ -int sql_cache_hit(THD *thd, char *sql, uint length) +void query_cache_insert(NET *net, const char *packet, ulong length) { -#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_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(); - lex_start(thd, (uchar *)sql, length); + DUMP(&query_cache); + BLOCK_LOCK_WR(query_block); + DBUG_PRINT("qcache", ("insert packet %lu bytes long",length)); - if (hash_insert(&sql_cache, (const byte *)ptr)) { - fprintf(stderr, "Out of memory during hash_insert?\n"); + /* + 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; } - 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"); + 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); } - return 0; + 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) +{ + 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 1; + 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(TABLE_LIST *tables_used) +{ + DBUG_ENTER("Query_cache::invalidate (table list)"); + if (query_cache_size > 0) + { + STRUCT_LOCK(&structure_guard_mutex); + if (query_cache_size > 0) + { + DUMP(this); + for ( ; tables_used; tables_used=tables_used->next) + invalidate_table(tables_used); + } + STRUCT_UNLOCK(&structure_guard_mutex); + } + DBUG_VOID_RETURN; +} + +/* + Remove all cached queries that uses the given table +*/ + +void Query_cache::invalidate(TABLE *table) +{ + DBUG_ENTER("Query_cache::invalidate (table)"); + if (query_cache_size > 0) + { + STRUCT_LOCK(&structure_guard_mutex); + if (query_cache_size > 0) + invalidate_table(table); + STRUCT_UNLOCK(&structure_guard_mutex); + } + DBUG_VOID_RETURN; +} + +/* + Remove all cached queries that uses the given table type. +*/ + +void Query_cache::invalidate(Query_cache_table::query_cache_table_type type) +{ + DBUG_ENTER("Query_cache::invalidate (type)"); + if (query_cache_size > 0) + { + STRUCT_LOCK(&structure_guard_mutex); + DUMP(this); + if (query_cache_size > 0) + { + /* invalidate_table reduce list while only root of list remain */ + while (tables_blocks[type] != 0) + invalidate_table(tables_blocks[type]); + } + STRUCT_UNLOCK(&structure_guard_mutex); + } + DBUG_VOID_RETURN; +} + + +/* + Remove all cached queries that uses the given database +*/ + +void Query_cache::invalidate(char *db) +{ + DBUG_ENTER("Query_cache::invalidate (db)"); + if (query_cache_size > 0) + { + STRUCT_LOCK(&structure_guard_mutex); + if (query_cache_size > 0) + { + DUMP(this); + for (int i=0 ; i < (int) Query_cache_table::TYPES_NUMBER; i++) + { + /* invalidate_table reduce list while only root of list remain */ + while (tables_blocks[i] !=0 ) + invalidate_table(tables_blocks[i]); + } + } + STRUCT_UNLOCK(&structure_guard_mutex); + } + DBUG_VOID_RETURN; +} + + +void Query_cache::invalidate_by_MyISAM_filename(const char *filename) +{ + 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]; + uint key_length= filename_2_table_key(key, filename); + 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; +} + + /* Remove all queries from cache */ + +void Query_cache::flush() +{ + 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)); + + steps = (Query_cache_memory_bin_step *) cache; + bins = ((Query_cache_memory_bin *) + (cache + mem_bin_steps * + ALIGN_SIZE(sizeof(Query_cache_memory_bin_step)))); + + 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; + } + 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; + } + } + 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 + 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) +{ + Query_cache_block *table_block; + if ((table_block = ((Query_cache_block*) + hash_search(&tables, (byte*) table->table_cache_key, + table->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, + Query_cache_table::type_convertion(tables_used->table-> + db_type))) + 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]; + uint key_length =filename_2_table_key(key, table->table->filename); + (++block_table)->n= ++n; + if (!insert_table(key_length, key, block_table, + Query_cache_table::type_convertion(DB_TYPE_MYISAM))) + 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, + Query_cache_table::query_cache_table_type type) +{ + 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(); + header->type(type); + double_linked_list_simple_include(table_block, + &tables_blocks[type]); + 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 + strlen(db) + 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[table_block->table()->type()]); + 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[new_block->table()->type()] == block) + tables_blocks[new_block->table()->type()] = 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) +{ + char tablename[FN_REFLEN+2], *filename, *dbname; + uint db_length; + 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", ("--------------------")); + for (int i=0; i < (int) Query_cache_table::TYPES_NUMBER; i++) + { + DBUG_PRINT("qcache", ("--- type %u", i)); + if (tables_blocks[i] != 0) + { + Query_cache_block *table_block = tables_blocks[i]; + 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[i]); + } + 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 (!initialized ) + { + 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->table()->type()], 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 ...")); + for (i=0 ; (int) i < (int) Query_cache_table::TYPES_NUMBER; i++) + { + if ((block = tables_blocks[i])) + { + 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[i]); + } + } + + 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 */ diff --git a/sql/sql_cache.h b/sql/sql_cache.h new file mode 100644 index 00000000000..b1d8eb23198 --- /dev/null +++ b/sql/sql_cache.h @@ -0,0 +1,418 @@ +/* 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 +{ + enum query_cache_table_type {OTHER=0, INNODB=1, TYPES_NUMBER=2}; + inline static query_cache_table_type type_convertion(db_type type) + { + return (type == DB_TYPE_INNODB ? INNODB : OTHER); + } + + char *tbl; + query_cache_table_type tp; + + inline query_cache_table_type type() { return tp; } + inline void type(query_cache_table_type t) { tp = t;} + 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_table::TYPES_NUMBER]; + + 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); + + /* 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(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, + Query_cache_table::query_cache_table_type type); + 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(TABLE_LIST *tables_used); + void invalidate(TABLE *table); + + /* Remove all queries that uses tables of pointed type*/ + void invalidate(Query_cache_table::query_cache_table_type type); + + /* 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 44a1dbc1720..fbab528dc67 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 */ @@ -84,8 +84,8 @@ THD::THD():user_time(0),fatal_error(0),last_insert_id_used(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=handler_tables=0; @@ -122,6 +122,7 @@ THD::THD():user_time(0),fatal_error(0),last_insert_id_used(0), server_status=SERVER_STATUS_AUTOCOMMIT; update_lock_default= low_priority_updates ? TL_WRITE_LOW_PRIORITY : TL_WRITE; options=thd_startup_options; + query_cache_type = (byte) query_cache_startup_type; sql_mode=(uint) opt_sql_mode; inactive_timeout=net_wait_timeout; open_options=ha_open_options; @@ -201,7 +202,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) diff --git a/sql/sql_class.h b/sql/sql_class.h index 87d19381b78..5b155e6b3d4 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 */ @@ -144,14 +144,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 { @@ -311,15 +313,16 @@ public: enum enum_server_command command; uint32 server_id; uint32 file_id; // for LOAD DATA INFILE + uint32 file_id; // for LOAD DATA INFILE const char *where; - 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; } transaction; Item *free_list, *handler_items; @@ -332,35 +335,41 @@ public: Vio* active_vio; pthread_mutex_t active_vio_lock; #endif - 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; - ulong query_id,version, inactive_timeout,options,thread_id; - 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; + 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 + /* + 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; - - // 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 to master + /* + 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. uint32 log_pos; THD(); @@ -452,6 +461,13 @@ public: { 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; + } }; @@ -700,7 +716,7 @@ public: uint num_of_tables; int error; thr_lock_type lock_option; - bool do_delete; + bool do_delete, not_trans_safe; public: multi_delete(THD *thd, TABLE_LIST *dt, thr_lock_type lock_option_arg, uint num_of_tables); @@ -715,4 +731,32 @@ public: 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 3158c8cbf7b..dd8ed634011 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 */ @@ -38,7 +38,7 @@ int mysql_create_db(THD *thd, char *db, uint create_options, bool silent) long result=1; int error = 0; DBUG_ENTER("mysql_create_db"); - + VOID(pthread_mutex_lock(&LOCK_mysql_create_db)); // do not create database if another thread is holding read lock @@ -159,6 +159,7 @@ int mysql_rm_db(THD *thd,char *db,bool if_exists, bool silent) if ((deleted=mysql_rm_known_files(thd, dirp, db, path,0)) >= 0 && thd) { ha_drop_database(path); + query_cache.invalidate(db); if (!silent) { if (!thd->query) @@ -316,19 +317,19 @@ static long mysql_rm_known_files(THD *thd, MY_DIR *dirp, const char *db, 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); @@ -368,6 +369,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 63e003178c2..2a7824eae68 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -1,15 +1,15 @@ /* 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 */ @@ -24,7 +24,7 @@ #include "mysql_priv.h" -#include "ha_innobase.h" +#include "ha_innodb.h" #include "sql_select.h" int mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, ORDER *order, @@ -98,7 +98,6 @@ int mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, ORDER *order, DBUG_RETURN(1); } } - (void) table->file->extra(HA_EXTRA_NO_READCHECK); if (options & OPTION_QUICK) (void) table->file->extra(HA_EXTRA_QUICK); @@ -157,8 +156,7 @@ int mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, ORDER *order, } thd->proc_info="end"; end_read_record(&info); - /* if (order) free_io_cache(table); */ /* QQ Should not be needed */ - (void) table->file->extra(HA_EXTRA_READCHECK); + free_io_cache(table); // Will not do any harm if (options & OPTION_QUICK) (void) table->file->extra(HA_EXTRA_NORMAL); @@ -183,6 +181,8 @@ cleanup: mysql_unlock_tables(thd, thd->lock); thd->lock=0; } + if (deleted) + query_cache.invalidate(table_list); delete select; if (error >= 0) // Fatal error send_error(&thd->net,thd->killed ? ER_SERVER_SHUTDOWN: 0); @@ -214,17 +214,15 @@ multi_delete::multi_delete(THD *thd_arg, TABLE_LIST *dt, do_delete(false) { uint counter=0; + not_trans_safe=false; tempfiles = (Unique **) sql_calloc(sizeof(Unique *) * (num_of_tables-1)); - (void) dt->table->file->extra(HA_EXTRA_NO_READCHECK); /* Don't use key read with MULTI-TABLE-DELETE */ - (void) dt->table->file->extra(HA_EXTRA_NO_KEYREAD); dt->table->used_keys=0; for (dt=dt->next ; dt ; dt=dt->next,counter++) { TABLE *table=dt->table; - (void) dt->table->file->extra(HA_EXTRA_NO_READCHECK); - (void) dt->table->file->extra(HA_EXTRA_NO_KEYREAD); + table->used_keys=0; tempfiles[counter] = new Unique (refposcmp2, (void *) &table->file->ref_length, table->file->ref_length, @@ -264,7 +262,7 @@ multi_delete::initialize_tables(JOIN *join) 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; @@ -277,18 +275,24 @@ multi_delete::initialize_tables(JOIN *join) 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() { - /* Add back EXTRA_READCHECK; In 4.0.1 we shouldn't need this anymore */ for (table_being_deleted=delete_tables ; table_being_deleted ; table_being_deleted=table_being_deleted->next) - (void) table_being_deleted->table->file->extra(HA_EXTRA_READCHECK); + { + 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++) { @@ -312,7 +316,7 @@ bool multi_delete::send_data(List<Item> &values) continue; table->file->position(table->record[0]); - + if (secure_counter < 0) { table->status|= STATUS_DELETED; @@ -337,51 +341,40 @@ bool multi_delete::send_data(List<Item> &values) return 0; } - - -/* Return true if some table is not transaction safe */ - -static bool some_table_is_not_transaction_safe (TABLE_LIST *tl) -{ - for (; tl ; tl=tl->next) - { - if (!(tl->table->file->has_transactions())) - return true; - } - return false; -} - - void multi_delete::send_error(uint errcode,const char *err) { /* First send error what ever it is ... */ ::send_error(&thd->net,errcode,err); - /* reset used flags */ - delete_tables->table->no_keyread=0; - /* If nothing deleted return */ if (!deleted) return; + /* Below can happen when thread is killed early ... */ if (!table_being_deleted) table_being_deleted=delete_tables; /* - If rows from the first table only has been deleted and it is transactional, - just do rollback. + If rows from the first table only has been deleted and it is + transactional, just do rollback. The same if all tables are transactional, regardless of where we are. In all other cases do attempt deletes ... */ if ((table_being_deleted->table->file->has_transactions() && - table_being_deleted == delete_tables) || - !some_table_is_not_transaction_safe(delete_tables->next)) + table_being_deleted == delete_tables) || !not_trans_safe) ha_rollback_stmt(thd); else if (do_delete) VOID(do_deletes(true)); } +/* + 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; @@ -409,60 +402,39 @@ int multi_delete::do_deletes (bool from_send_error) break; } -#if USE_REGENERATE_TABLE - // nice little optimization .... - // but Monty has to fix generate_table... - // This will not work for transactional tables because for other types - // records is not absolute - if (num_of_positions == table->file->records) - { - TABLE_LIST table_list; - bzero((char*) &table_list,sizeof(table_list)); - table_list.name=table->table_name; table_list.real_name=table_being_deleted->real_name; - table_list.table=table; - table_list.grant=table->grant; - table_list.db = table_being_deleted->db; - error=generate_table(thd,&table_list,(TABLE *)0); - if (error <= 0) {error = 1; break;} - deleted += num_of_positions; - continue; - } -#endif /* USE_REGENERATE_TABLE */ - READ_RECORD info; - error=0; init_read_record(&info,thd,table,NULL,0,0); - bool not_trans_safe = some_table_is_not_transaction_safe(delete_tables); while (!(error=info.read_record(&info)) && (!thd->killed || from_send_error || not_trans_safe)) { - error=table->file->delete_row(table->record[0]); - if (error) + if ((error=table->file->delete_row(table->record[0]))) { table->file->print_error(error,MYF(0)); break; } - else - deleted++; + deleted++; } end_read_record(&info); - if (error == -1) + 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"; /* out: 1 if error, 0 if success */ + thd->proc_info="deleting from reference tables"; /* Does deletes for the last n - 1 tables, returns 0 if ok */ - int error = do_deletes(false); /* do_deletes returns 0 if success */ + int error = do_deletes(false); // returns 0 if success /* reset used flags */ - delete_tables->table->no_keyread=0; - if (error == -1) error = 0; thd->proc_info="end"; if (error) { @@ -470,30 +442,27 @@ bool multi_delete::send_eof() 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 && - (!error || some_table_is_not_transaction_safe(delete_tables))) + /* + 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); - 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) && - !some_table_is_not_transaction_safe(delete_tables)) - error=1; /* Log write failed: roll back - the SQL statement */ - + 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.invalidate(delete_tables); ::send_ok(&thd->net,deleted); return 0; } @@ -539,6 +508,7 @@ int mysql_truncate(THD *thd, TABLE_LIST *table_list, bool dont_send_ok) 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); @@ -570,6 +540,7 @@ int mysql_truncate(THD *thd, TABLE_LIST *table_list, bool dont_send_ok) 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.invalidate(table_list); if (!dont_send_ok) { diff --git a/sql/sql_do.cc b/sql/sql_do.cc new file mode 100644 index 00000000000..70124c2d796 --- /dev/null +++ b/sql/sql_do.cc @@ -0,0 +1,34 @@ +/* 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 */ + + +/* Execute DO statement */ + +#include "mysql_priv.h" +#include "sql_acl.h" + +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, 0)) + DBUG_RETURN(-1); + while ((value = li++)) + value->val_int(); + send_ok(&thd->net); + DBUG_RETURN(0); +} diff --git a/sql/sql_handler.cc b/sql/sql_handler.cc index 46cbe5e44d3..0c6b5599519 100644 --- a/sql/sql_handler.cc +++ b/sql/sql_handler.cc @@ -23,17 +23,17 @@ /* 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... */ @@ -43,7 +43,7 @@ thd->open_tables=thd->handler_tables; \ thd->handler_tables=tmp; } -static TABLE **find_table_ptr_by_name(THD *thd, const char *db, +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) @@ -53,12 +53,20 @@ int mysql_ha_open(THD *thd, TABLE_LIST *tables) HANDLER_TABLES_HACK(thd); if (err) return -1; - + + // there can be only one table in *tables + if (!(tables->table->file->option_flag() & 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) +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); @@ -68,14 +76,20 @@ int mysql_ha_close(THD *thd, TABLE_LIST *tables) close_thread_table(thd, ptr); VOID(pthread_mutex_unlock(&LOCK_open)); } - - send_ok(&thd->net); + 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[]= +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, @@ -86,14 +100,14 @@ int mysql_ha_read(THD *thd, TABLE_LIST *tables, if (!table) { my_printf_error(ER_UNKNOWN_TABLE,ER(ER_UNKNOWN_TABLE),MYF(0), - tables->name,"HANDLER"); + 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) @@ -115,8 +129,12 @@ int mysql_ha_read(THD *thd, TABLE_LIST *tables, 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 (uint num_rows=0; num_rows < select_limit; ) { @@ -166,7 +184,7 @@ int mysql_ha_read(THD *thd, TABLE_LIST *tables, if (!(key= (byte*) sql_calloc(ALIGN_SIZE(key_len)))) { send_error(&thd->net,ER_OUTOFMEMORY); - goto err; + goto err; } key_copy(key, table, keyno, key_len); err=table->file->index_read(table->record[0], @@ -176,7 +194,7 @@ int mysql_ha_read(THD *thd, TABLE_LIST *tables, } default: send_error(&thd->net,ER_ILLEGAL_HA); - goto err; + goto err; } if (err) @@ -191,7 +209,7 @@ int mysql_ha_read(THD *thd, TABLE_LIST *tables, goto ok; } if (cond) - { + { err=err; if(!cond->val_int()) continue; @@ -224,6 +242,7 @@ ok: return 0; err: mysql_unlock_tables(thd,lock); +err0: return -1; } @@ -234,19 +253,19 @@ err: **************************************************************************/ /* Note: this function differs from find_locked_table() because we're looking - here for alias, not real table name + 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; + dblen=strlen(db)+1; ptr=&(thd->handler_tables); - + for (TABLE *table=*ptr; table ; table=*ptr) { if (!memcmp(table->table_cache_key, db, dblen) && diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index 70cc5f412a1..0898ad4bffb 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 */ @@ -310,6 +310,8 @@ int mysql_insert(THD *thd,TABLE_LIST *table_list, List<Item> &fields, } } thd->proc_info="end"; + if (info.copied || info.deleted) + query_cache.invalidate(table_list); table->time_stamp=save_time_stamp; // Restore auto timestamp ptr table->next_number_field=0; thd->count_cuted_fields=0; @@ -362,7 +364,7 @@ int write_record(TABLE *table,COPY_INFO *info) { int error; char *key=0; - + info->records++; if (info->handle_duplicates == DUP_REPLACE) { @@ -396,7 +398,7 @@ int write_record(TABLE *table,COPY_INFO *info) error=my_errno; goto err; } - + if (!key) { if (!(key=(char*) my_safe_alloca(table->max_unique_length, @@ -737,9 +739,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; @@ -757,7 +759,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) @@ -986,23 +988,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) @@ -1037,6 +1028,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) { @@ -1225,6 +1217,7 @@ bool delayed_insert::handle_inserts(void) sql_print_error("%s",thd.net.last_error); goto err; } + query_cache.invalidate(table); if (thr_reschedule_write_lock(*thd.lock->locks)) { /* This should never happen */ @@ -1249,6 +1242,7 @@ bool delayed_insert::handle_inserts(void) sql_print_error("%s",thd.net.last_error); goto err; } + query_cache.invalidate(table); pthread_mutex_lock(&mutex); DBUG_RETURN(0); @@ -1334,7 +1328,9 @@ 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.invalidate(table); } @@ -1346,6 +1342,8 @@ 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.invalidate(table); if (error) { diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index 67f0d05d2d3..d61e47d0883 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 */ diff --git a/sql/sql_lex.h b/sql/sql_lex.h index a796eca58f9..6a966336ad7 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 */ @@ -55,8 +55,9 @@ enum enum_sql_command { SQLCOM_RESET, SQLCOM_PURGE, SQLCOM_SHOW_BINLOGS, SQLCOM_SHOW_OPEN_TABLES, SQLCOM_LOAD_MASTER_DATA, SQLCOM_HA_OPEN, SQLCOM_HA_CLOSE, SQLCOM_HA_READ, - SQLCOM_SHOW_SLAVE_HOSTS, SQLCOM_MULTI_DELETE, - SQLCOM_SHOW_BINLOG_EVENTS, SQLCOM_SHOW_NEW_MASTER + SQLCOM_SHOW_SLAVE_HOSTS, SQLCOM_DELETE_MULTI, SQLCOM_MULTI_UPDATE, + SQLCOM_SHOW_BINLOG_EVENTS, SQLCOM_SHOW_NEW_MASTER, SQLCOM_DO, + SQLCOM_END }; enum lex_states { STATE_START, STATE_CHAR, STATE_IDENT, @@ -180,7 +181,7 @@ typedef struct st_lex { 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; + uint grant,grant_tot_col,which_columns, union_option, mqh; thr_lock_type lock_option; bool drop_primary,drop_if_exists,local_file; bool in_comment,ignore_space,verbose,simple_alter, option_type; diff --git a/sql/sql_list.cc b/sql/sql_list.cc index be630dfc038..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 */ diff --git a/sql/sql_list.h b/sql/sql_list.h index 66311b03435..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 */ diff --git a/sql/sql_load.cc b/sql/sql_load.cc index 17d1765495c..abc9fa5a121 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 */ @@ -175,7 +175,7 @@ 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); - + // 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 @@ -292,7 +292,7 @@ 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 (mysql_bin_log.is_open()) @@ -547,7 +547,7 @@ READ_INFO::READ_INFO(File file_par, uint tot_length, String &field_term, */ 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 = @@ -748,7 +748,7 @@ found_eof: /* ** One can't use fixed length with multi-byte charset ** */ - + int READ_INFO::read_fixed_length() { int chr; @@ -872,4 +872,3 @@ bool READ_INFO::find_start_of_fields() } return 0; } - 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 a459ca0d602..3bcade84cd0 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -29,25 +29,42 @@ #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 + extern int yyparse(void); extern "C" pthread_mutex_t THR_LOCK_keycache; #ifdef SOLARIS 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 int check_for_max_user_connections(const char *user, const char *host, uint max); static void decrease_user_connections(const char *user, const char *host); 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(char **filename_ptr, char *table_name); +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 @@ -72,8 +89,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) @@ -99,6 +116,95 @@ inline bool end_active_trans(THD *thd) } +static HASH hash_user_connections; +extern pthread_mutex_t LOCK_user_conn; + +struct user_conn { + char *user; + uint len, connections, questions, max; + time_t intime; +}; + +static byte* get_key_conn(user_conn *buff, uint *length, + my_bool not_used __attribute__((unused))) +{ + *length=buff->len; + return (byte*) buff->user; +} + +#define DEF_USER_COUNT 50 + +static void free_user(struct user_conn *uc) +{ + my_free((char*) uc,MYF(0)); +} + +/* +** Check if maximum queries per hour limit has been reached +** returns 0 if OK. +*/ + +static bool check_mqh(THD *thd, const char *user, const char *host,uint max) +{ + uint temp_len; + char temp_user[USERNAME_LENGTH+HOSTNAME_LENGTH+2]; + struct user_conn *uc; + if (!user) + user=""; + if (!host) + host=""; + temp_len= (uint) (strxnmov(temp_user, sizeof(temp_user), user, "@", host, + NullS) - temp_user); +//This would be MUCH faster if there was already temp_user made in THD !!! May I ?? + (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 queries */ + { + bool my_start = thd->start_time != 0; + time_t check_time = (my_start) ? thd->start_time : time(NULL); + if (check_time - uc->intime >= 3600) + { + uc->questions=(uint)my_start; + uc->intime=check_time; + } + else if (uc->max && ++(uc->questions) > uc->max) + { + (void) pthread_mutex_unlock(&LOCK_user_conn); + send_error(&thd->net,ER_NOT_ALLOWED_COMMAND); // change this to appropriate message + return 1; + } + } + else + { + 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 + (void) pthread_mutex_unlock(&LOCK_user_conn); + return 1; + } + uc->user=(char*) (uc+1); + memcpy(uc->user,temp_user,temp_len+1); + uc->len = temp_len; + uc->connections = 1; + uc->questions=0; + uc->max=max; + uc->intime=current_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 + (void) pthread_mutex_unlock(&LOCK_user_conn); + return 1; + } + } + (void) pthread_mutex_unlock(&LOCK_user_conn); + return 0; +} + /* ** Check if user is ok ** Updates: @@ -109,7 +215,9 @@ static bool check_user(THD *thd,enum_server_command command, const char *user, const char *passwd, const char *db, bool check_count) { NET *net= &thd->net; + uint max=0; thd->db=0; + thd->db_length=0; if (!(thd->user = my_strdup(user, MYF(0)))) { @@ -120,7 +228,7 @@ static bool check_user(THD *thd,enum_server_command command, const char *user, passwd, thd->scramble, &thd->priv_user, protocol_version == 9 || !(thd->client_capabilities & - CLIENT_LONG_PASSWORD)); + CLIENT_LONG_PASSWORD),&max); 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, @@ -151,6 +259,8 @@ static bool check_user(THD *thd,enum_server_command command, const char *user, return(1); } } + if (mqh_used && max && check_mqh(thd,user,thd->host,max)) + return -1; mysql_log.write(thd,command, (thd->priv_user == thd->user ? (char*) "%s@%s on %s" : @@ -160,7 +270,7 @@ static bool check_user(THD *thd,enum_server_command command, const char *user, db ? db : (char*) ""); thd->db_access=0; if (max_user_connections && - check_for_max_user_connections(user, strlen(user), thd->host)) + check_for_max_user_connections(user, thd->host, max)) return -1; if (db && db[0]) { @@ -180,28 +290,6 @@ static bool check_user(THD *thd,enum_server_command command, const char *user, ** 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))) -{ - *length=buff->len; - return (byte*) buff->user; -} - -#define DEF_USER_COUNT 50 - -static void free_user(struct user_conn *uc) -{ - my_free((char*) uc,MYF(0)); -} - void init_max_user_conn(void) { (void) hash_init(&hash_user_connections,DEF_USER_COUNT,0,0, @@ -210,8 +298,7 @@ void init_max_user_conn(void) } -static int check_for_max_user_connections(const char *user, int u_length, - const char *host) +static int check_for_max_user_connections(const char *user, const char *host, uint max) { int error=1; uint temp_len; @@ -252,7 +339,10 @@ static int check_for_max_user_connections(const char *user, int u_length, uc->user=(char*) (uc+1); memcpy(uc->user,temp_user,temp_len+1); uc->len = temp_len; - uc->connections = 1; + uc->connections = 1; + uc->questions=0; + uc->max=max; + uc->intime=current_thd->thr_create_time; if (hash_insert(&hash_user_connections, (byte*) uc)) { my_free((char*) uc,0); @@ -291,7 +381,7 @@ static void decrease_user_connections(const char *user, const char *host) DBUG_ASSERT(uc != 0); // We should always find the user if (!uc) goto end; // Safety; Something went wrong - if (! --uc->connections) + if (! --uc->connections && !mqh_used) { /* Last connection for user; Delete it */ (void) hash_delete(&hash_user_connections,(byte*) uc); @@ -307,7 +397,6 @@ void free_max_user_conn(void) hash_free(&hash_user_connections); } - /* ** check connnetion and get priviliges ** returns 0 on ok, -1 < if error is given > 0 on error. @@ -363,51 +452,35 @@ check_connections(THD *thd) } 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); @@ -426,12 +499,9 @@ 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...")); sslaccept(ssl_acceptor_fd, net->vio, thd->inactive_timeout); @@ -439,8 +509,8 @@ check_connections(THD *thd) 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); } @@ -469,7 +539,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]); @@ -487,9 +557,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); @@ -498,13 +568,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_* @@ -564,12 +634,12 @@ pthread_handler_decl(handle_one_connection,arg) 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_or_ip, - (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); } @@ -635,7 +705,8 @@ pthread_handler_decl(handle_bootstrap,arg) buff[length]=0; thd->current_tablenr=0; thd->query_length=length; - thd->query= thd->memdup(buff,length+1); + thd->query= thd->memdup_w_gap(buff, length+1, thd->db_length+1); + thd->query[length] = '\0'; thd->query_id=query_id++; mysql_parse(thd,thd->query,length); close_thread_tables(thd); // Free tables @@ -693,8 +764,8 @@ 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; - thd->query_length=strlen(tbl_name); if ((error = mysqld_dump_create_info(thd, table, -1))) { my_error(ER_GET_ERRNO, MYF(0)); @@ -715,7 +786,8 @@ err: bool do_command(THD *thd) { char *packet; - uint old_timeout,packet_length; + uint old_timeout; + ulong packet_length; NET *net; enum enum_server_command command; DBUG_ENTER("do_command"); @@ -725,7 +797,7 @@ 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; @@ -745,7 +817,7 @@ bool do_command(THD *thd) command_name[command])); } net->timeout=old_timeout; // Timeout for writing - DBUG_RETURN(dispatch_command(command,thd, packet+1, packet_length)); + DBUG_RETURN(dispatch_command(command,thd, packet+1, (uint) packet_length)); } @@ -770,6 +842,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd, 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)) mysql_log.write(thd,command,"%s",thd->db); break; @@ -783,6 +856,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd, } case COM_TABLE_DUMP: { + thread_safe_increment(com_other,&LOCK_thread_count); slow_command = TRUE; uint db_len = *(uchar*)packet; uint tbl_len = *(uchar*)(packet + db_len + 1); @@ -799,6 +873,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd, } case COM_CHANGE_USER: { + thread_safe_increment(com_other,&LOCK_thread_count); char *user= (char*) packet; char *passwd= strend(user)+1; char *db= strend(passwd)+1; @@ -806,6 +881,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd, /* 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; @@ -822,6 +898,7 @@ bool dispatch_command(enum enum_server_command 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; @@ -835,14 +912,23 @@ bool dispatch_command(enum enum_server_command command, THD *thd, case COM_QUERY: { - char *pos=packet-1+packet_length; // Point at end null - /* Remove garage at end of query */ - while (packet_length > 0 && pos[-1] == ';') + 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 + while (packet_length > 0 && (pos[-1] == ';' || isspace(pos[-1]))) { pos--; packet_length--; } - if (!(thd->query= (char*) thd->memdup((gptr) (packet),packet_length+1))) + /* 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 @@ -850,7 +936,14 @@ bool dispatch_command(enum enum_server_command command, THD *thd, 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 && check_mqh(thd,thd->user,thd->host,0)) + { + error = TRUE; + net->error = 0; + 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")); @@ -864,6 +957,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd, { char *fields; TABLE_LIST table_list; + thread_safe_increment(com_stat[SQLCOM_SHOW_FIELDS],&LOCK_thread_count); bzero((char*) &table_list,sizeof(table_list)); if (!(table_list.db=thd->db)) { @@ -872,8 +966,10 @@ bool dispatch_command(enum enum_server_command command, THD *thd, } thd->free_list=0; table_list.name=table_list.real_name=thd->strdup(packet); - thd->query=fields=thd->strdup(strend(packet)+1); - thd->query_length=strlen(thd->query); + 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 @@ -888,6 +984,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd, } #endif case COM_QUIT: + /* We don't calculate statistics for this command */ mysql_log.write(thd,command,NullS); net->error=0; // Don't give 'abort' message error=TRUE; // End server @@ -895,6 +992,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd, case COM_CREATE_DB: // QQ: To be removed { + 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)) @@ -910,6 +1008,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd, } case COM_DROP_DB: // QQ: To be removed { + 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)) @@ -928,6 +1027,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd, } case COM_BINLOG_DUMP: { + thread_safe_increment(com_other,&LOCK_thread_count); slow_command = TRUE; if (check_access(thd, FILE_ACL, any_db)) break; @@ -952,7 +1052,8 @@ bool dispatch_command(enum enum_server_command command, THD *thd, } case COM_REFRESH: { - uint options=(uchar) packet[0]; + 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); @@ -960,9 +1061,12 @@ bool dispatch_command(enum enum_server_command command, THD *thd, send_error(net,0); else send_eof(net); + if (mqh_used && hash_user_connections.array.buffer == 0) + init_max_user_conn(); break; } case COM_SHUTDOWN: + thread_safe_increment(com_other,&LOCK_thread_count); if (check_access(thd,SHUTDOWN_ACL,any_db)) break; /* purecov: inspected */ DBUG_PRINT("quit",("Got shutdown command")); @@ -984,6 +1088,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd, case COM_STATISTICS: { mysql_log.write(thd,command,NullS); + thread_safe_increment(com_stat[SQLCOM_SHOW_STATUS],&LOCK_thread_count); char buff[200]; ulong uptime = (ulong) (thd->start_time - start_time); sprintf((char*) buff, @@ -1002,9 +1107,11 @@ bool dispatch_command(enum enum_server_command command, THD *thd, break; } case COM_PING: + thread_safe_increment(com_other,&LOCK_thread_count); send_ok(net); // Tell client we are alive break; case COM_PROCESS_INFO: + thread_safe_increment(com_stat[SQLCOM_SHOW_PROCESSLIST],&LOCK_thread_count); if (!thd->priv_user[0] && check_process_priv(thd)) break; mysql_log.write(thd,command,NullS); @@ -1013,11 +1120,13 @@ bool dispatch_command(enum enum_server_command command, THD *thd, break; case COM_PROCESS_KILL: { + thread_safe_increment(com_stat[SQLCOM_KILL],&LOCK_thread_count); ulong id=(ulong) uint4korr(packet); kill_one_thread(thd,id); break; } case COM_DEBUG: + thread_safe_increment(com_other,&LOCK_thread_count); if (check_process_priv(thd)) break; /* purecov: inspected */ mysql_print_status(thd); @@ -1085,6 +1194,28 @@ mysql_execute_command(void) 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)) + DBUG_VOID_RETURN; +#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 = &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 @@ -1094,6 +1225,7 @@ mysql_execute_command(void) !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: { @@ -1164,11 +1296,18 @@ mysql_execute_command(void) } if (!(res=open_and_lock_tables(thd,tables))) + { + query_cache.store_query(thd, tables); res=handle_select(thd, lex, result); + } else delete result; break; } + case SQLCOM_DO: + res=mysql_do(thd, *lex->insert_list); + break; + case SQLCOM_PURGE: { if (check_process_priv(thd)) @@ -1307,8 +1446,10 @@ mysql_execute_command(void) lex->create_info.data_file_name=lex->create_info.index_file_name=0; #else /* Fix names if symlinked tables */ - if (append_file_to_dir(&lex->create_info.data_file_name, tables->name) || - append_file_to_dir(&lex->create_info.index_file_name, tables->name)) + 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; @@ -1319,7 +1460,7 @@ mysql_execute_command(void) 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; @@ -1435,6 +1576,7 @@ mysql_execute_command(void) if (end_active_trans(thd)) res= -1; else + { res= mysql_alter_table(thd, select_lex->db, lex->name, &lex->create_info, tables, lex->create_list, @@ -1442,6 +1584,7 @@ mysql_execute_command(void) (ORDER *) select_lex->order_list.first, lex->drop_primary, lex->duplicates, lex->alter_keys_onoff, lex->simple_alter); + } break; } #endif @@ -1470,6 +1613,7 @@ mysql_execute_command(void) goto error; } } + query_cache.invalidate(tables); if (end_active_trans(thd)) res= -1; else if (mysql_rename_tables(thd,tables)) @@ -1508,6 +1652,7 @@ mysql_execute_command(void) check_table_access(thd,SELECT_ACL | INSERT_ACL, tables)) goto error; /* purecov: inspected */ res = mysql_repair_table(thd, tables, &lex->check_opt); + query_cache.invalidate(tables); break; } case SQLCOM_CHECK: @@ -1516,6 +1661,7 @@ mysql_execute_command(void) check_table_access(thd, SELECT_ACL | EXTRA_ACL , tables)) goto error; /* purecov: inspected */ res = mysql_check_table(thd, tables, &lex->check_opt); + query_cache.invalidate(tables); break; } case SQLCOM_ANALYZE: @@ -1564,19 +1710,68 @@ mysql_execute_command(void) send_error(&thd->net,ER_WRONG_VALUE_COUNT); DBUG_VOID_RETURN; } - 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); + 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 select_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 */ @@ -1624,7 +1819,7 @@ mysql_execute_command(void) 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; @@ -1681,13 +1876,13 @@ mysql_execute_command(void) select_lex->options); break; } - case SQLCOM_MULTI_DELETE: + 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) || @@ -1729,8 +1924,8 @@ mysql_execute_command(void) /* Fix tables-to-be-deleted-from list to point at opened tables */ for (auxi=(TABLE_LIST*) aux_tables ; auxi ; auxi=auxi->next) auxi->table= ((TABLE_LIST*) auxi->table)->table; - if ((result=new multi_delete(thd,aux_tables,lex->lock_option, - table_count)) && ! thd->fatal_error) + 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, @@ -1739,10 +1934,10 @@ mysql_execute_command(void) select_lex->options | thd->options | SELECT_NO_JOIN_CACHE, result); + delete result; } else res= -1; // Error is not sent - delete result; close_thread_tables(thd); break; } @@ -1912,7 +2107,7 @@ mysql_execute_command(void) } case SQLCOM_SET_OPTION: { - uint org_options=thd->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); @@ -2099,6 +2294,8 @@ mysql_execute_command(void) } } } + if (mqh_used && hash_user_connections.array.buffer == 0) + init_max_user_conn(); break; } case SQLCOM_FLUSH: @@ -2167,13 +2364,17 @@ 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; break; + } case SQLCOM_ROLLBACK: thd->server_status&= ~SERVER_STATUS_IN_TRANS; if (!ha_rollback(thd)) @@ -2212,7 +2413,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) @@ -2222,7 +2423,8 @@ check_access(THD *thd,uint want_access,const char *db, uint *save_priv, 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 */ } @@ -2234,16 +2436,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_or_ip, - 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 */ @@ -2257,10 +2460,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_or_ip, - 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 */ } @@ -2277,7 +2481,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; @@ -2292,18 +2497,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) return check_grant(thd,want_access & ~EXTRA_ACL,org_tables, - test(want_access & EXTRA_ACL)); + test(want_access & EXTRA_ACL), no_errors); return FALSE; } @@ -2425,7 +2632,7 @@ mysql_init_query(THD *thd) 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->lex.sql_command=SQLCOM_SELECT; + thd->safe_to_cache_query=1; DBUG_VOID_RETURN; } @@ -2434,9 +2641,10 @@ mysql_init_select(LEX *lex) { SELECT_LEX *select_lex = lex->select; select_lex->where=select_lex->having=0; - select_lex->select_limit=current_thd->default_select_limit; + select_lex->select_limit=lex->thd->default_select_limit; select_lex->offset_limit=0; - select_lex->options=0; select_lex->linkage=UNSPECIFIED_TYPE; + select_lex->options=0; + select_lex->linkage=UNSPECIFIED_TYPE; lex->exchange = 0; lex->proc_list.first=0; select_lex->order_list.elements=select_lex->group_list.elements=0; @@ -2447,16 +2655,33 @@ mysql_init_select(LEX *lex) select_lex->next = (SELECT_LEX *)NULL; } -void +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(); + 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 @@ -2466,12 +2691,20 @@ 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) + { + 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; } @@ -2853,13 +3086,17 @@ TABLE_LIST *add_table_to_list(Table_ident *table, LEX_STRING *alias, if (!alias) /* Alias is case sensitive */ 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 ? table->db.str : (thd->db ? thd->db : (char*) ""); - ptr->real_name=table->table.str; 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->lock_type=flags; ptr->updating=updating; if (use_index) @@ -2872,7 +3109,8 @@ TABLE_LIST *add_table_to_list(Table_ident *table, LEX_STRING *alias, /* check that used name is unique */ if (flags != TL_IGNORE) { - for (TABLE_LIST *tables=(TABLE_LIST*) thd->lex.select->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, tables->db)) @@ -2904,7 +3142,7 @@ static bool create_total_list(THD *thd, LEX *lex, TABLE_LIST **result) 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) { @@ -2968,8 +3206,7 @@ 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) { for (; tables ; tables=tables->next) if (!strcmp(name,tables->real_name) && !strcmp(db,tables->db)) @@ -2977,7 +3214,7 @@ static bool check_dup(THD *thd,const char *db,const char *name, 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; @@ -2998,6 +3235,15 @@ bool reload_acl_and_cache(THD *thd, uint options, TABLE_LIST *tables) if (ha_flush_logs()) result=1; } + 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 + } if (options & (REFRESH_TABLES | REFRESH_READ_LOCK)) { if ((options & REFRESH_READ_LOCK) && thd) @@ -3016,7 +3262,14 @@ bool reload_acl_and_cache(THD *thd, uint options, TABLE_LIST *tables) if (options & REFRESH_MASTER) if (reset_master(thd)) result=1; - if (options & REFRESH_SLAVE) +#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)) @@ -3073,7 +3326,7 @@ static void refresh_status(void) /* If pointer is not a null pointer, append filename to it */ -static bool append_file_to_dir(char **filename_ptr, char *table_name) +static bool append_file_to_dir(THD *thd, char **filename_ptr, char *table_name) { char buff[FN_REFLEN],*ptr, *end; if (!*filename_ptr) @@ -3089,7 +3342,7 @@ static bool append_file_to_dir(char **filename_ptr, char *table_name) /* Fix is using unix filename format on dos */ strmov(buff,*filename_ptr); end=convert_dirname(buff, *filename_ptr, NullS); - if (!(ptr=sql_alloc((uint) (end-buff)+strlen(table_name)+1))) + 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); diff --git a/sql/sql_rename.cc b/sql/sql_rename.cc index 38d861d03d6..e4a277d1434 100644 --- a/sql/sql_rename.cc +++ b/sql/sql_rename.cc @@ -34,7 +34,7 @@ bool mysql_rename_tables(THD *thd, TABLE_LIST *table_list) 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 */ @@ -43,7 +43,7 @@ bool mysql_rename_tables(THD *thd, TABLE_LIST *table_list) 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 +53,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) { diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 660702d4117..31c349d199b 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -35,7 +35,7 @@ const char *join_type_str[]={ "UNKNOWN","system","const","eq_ref","ref", "MAYBE_REF","ALL","range","index","fulltext" }; static bool make_join_statistics(JOIN *join,TABLE_LIST *tables,COND *conds, - DYNAMIC_ARRAY *keyuse); + DYNAMIC_ARRAY *keyuse); static bool update_ref_and_keys(THD *thd, DYNAMIC_ARRAY *keyuse, JOIN_TAB *join_tab, uint tables,COND *conds,table_map table_map); @@ -47,7 +47,8 @@ static void find_best(JOIN *join,table_map rest_tables,uint index, 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); @@ -88,15 +89,18 @@ 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, @@ -179,7 +183,7 @@ mysql_select(THD *thd,TABLE_LIST *tables,List<Item> &fields,COND *conds, ulong select_options,select_result *result) { TABLE *tmp_table; - int error,tmp; + int error, tmp_error, tmp; bool need_tmp,hidden_group_fields; bool simple_order,simple_group,no_order, skip_sort_order; Item::cond_result cond_value; @@ -479,7 +483,9 @@ mysql_select(THD *thd,TABLE_LIST *tables,List<Item> &fields,COND *conds, (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)))) + !(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))) { @@ -672,8 +678,11 @@ mysql_select(THD *thd,TABLE_LIST *tables,List<Item> &fields,COND *conds, /* 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 @@ -746,9 +755,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 */ } @@ -795,7 +806,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 */ } @@ -2152,7 +2163,8 @@ get_best_combination(JOIN *join) 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, + store_key_item *tmp=new store_key_item(thd, + keyinfo->key_part[i].field, (char*)key_buff + maybe_null, maybe_null ? @@ -2166,7 +2178,8 @@ get_best_combination(JOIN *join) tmp->copy(); } else - *ref_key++= get_store_key(keyuse,join->const_table_map, + *ref_key++= get_store_key(join->thd, + keyuse,join->const_table_map, &keyinfo->key_part[i], (char*) key_buff,maybe_null); key_buff+=keyinfo->key_part[i].store_length; @@ -2188,10 +2201,17 @@ get_best_combination(JOIN *join) j->type=JT_REF; /* Must read with repeat */ else if (ref_key == j->ref.key_copy) { /* Should never be reached */ - j->type=JT_CONST; /* purecov: deadcode */ + /* + 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) { - join->const_tables++; /* purecov: deadcode */ + join->const_tables++; join->const_table_map|=form->map; } } @@ -2208,25 +2228,28 @@ get_best_combination(JOIN *join) 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, @@ -2495,7 +2518,7 @@ 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; + tab->read_record.read_record= join_read_next_same; if (table->used_keys & ((key_map) 1 << tab->ref.key) && !table->no_keyread) { @@ -2570,7 +2593,7 @@ make_join_readinfo(JOIN *join,uint options) { // 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->read_first_record= join_read_first; tab->type=JT_NEXT; // Read with index_first / index_next } } @@ -2771,8 +2794,8 @@ static void update_depend_map(JOIN *join, ORDER *order) /* -** simple_order is set to 1 if sort_order only uses fields from head table -** and the head table is not a LEFT JOIN table + simple_order is set to 1 if sort_order only uses fields from head table + and the head table is not a LEFT JOIN table */ static ORDER * @@ -3159,6 +3182,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()", @@ -3271,7 +3295,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) { @@ -3313,7 +3337,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: @@ -3321,7 +3345,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; @@ -3422,7 +3447,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) @@ -3509,8 +3538,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,not_all_columns); + 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++; @@ -3526,7 +3555,7 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields, } else { - Field *new_field=create_tmp_field(table,item,type,©_func, + Field *new_field=create_tmp_field(thd, table, item,type, ©_func, tmp_from_field, group != 0, not_all_columns); if (!new_field) @@ -3624,6 +3653,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; @@ -3707,19 +3740,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); @@ -3821,7 +3856,6 @@ 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); } @@ -3875,10 +3909,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= @@ -3899,11 +3933,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; } } } @@ -4041,9 +4081,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) @@ -4120,15 +4163,21 @@ do_select(JOIN *join,List<Item> *fields,TABLE *table,Procedure *procedure) if (error == -3) error=0; /* select_limit used */ } + + /* Return 1 if error is sent; -1 if error should be sent */ if (error < 0) - join->result->send_error(0,NullS); /* purecov: inspected */ + { + join->result->send_error(0,NullS); /* purecov: inspected */ + error=1; // Error sent + } else { - if (!table) // If sending data to client + 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 } DBUG_PRINT("info",("%ld records output",join->send_records)); } @@ -4145,10 +4194,10 @@ do_select(JOIN *join,List<Item> *fields,TABLE *table,Procedure *procedure) 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); } @@ -4416,8 +4465,11 @@ join_read_const(JOIN_TAB *tab) } store_record(table,1); } - else if (!table->status) // Only happens with left join + else if (!(table->status & ~STATUS_NULL_ROW)) // Only happens with left join + { + table->status=0; restore_record(table,1); // restore old record + } table->null_row=0; return table->status ? -1 : 0; } @@ -4477,6 +4529,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 @@ -4487,7 +4568,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; @@ -4510,6 +4591,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) @@ -4540,7 +4652,7 @@ 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; @@ -4551,7 +4663,7 @@ join_init_read_first_with_key(JOIN_TAB *tab) 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; @@ -4571,8 +4683,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) @@ -4589,9 +4702,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; @@ -4601,7 +4713,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; @@ -4621,8 +4733,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) @@ -4639,6 +4752,7 @@ join_init_read_prev_with_key(READ_RECORD *info) return 0; } + static int join_ft_read_first(JOIN_TAB *tab) { @@ -4713,9 +4827,19 @@ end_send(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)), { if (join->select_options & OPTION_FOUND_ROWS) { - join->do_send_rows=0; - join->thd->select_limit = HA_POS_ERROR; - DBUG_RETURN(0); + 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) + { + join->select_options ^= OPTION_FOUND_ROWS; + join->send_records = jt->records; + } + else + { + join->do_send_rows=0; + join->thd->select_limit = HA_POS_ERROR; + DBUG_RETURN(0); + } } DBUG_RETURN(-3); // Abort nicely } @@ -4827,6 +4951,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 */ @@ -4837,10 +4962,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++; @@ -4895,8 +5021,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], @@ -5254,13 +5381,6 @@ 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 -*****************************************************************************/ - /* Return 1 if we don't have to do file sorting */ static bool @@ -5293,6 +5413,9 @@ 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 */ @@ -5300,11 +5423,11 @@ test_if_skip_sort_order(JOIN_TAB *tab,ORDER *order,ha_rows select_limit, (order_direction = test_if_order_by_key(order,table,ref_key, &used_key_parts))) { - if (order_direction == -1) + if (order_direction == -1) // If ORDER BY ... DESC { if (select && select->quick) { - // ORDER BY ref_key DESC + // ORDER BY range_key DESC QUICK_SELECT_DESC *tmp=new QUICK_SELECT_DESC(select->quick, used_key_parts); if (!tmp || tmp->error) @@ -5319,11 +5442,15 @@ test_if_skip_sort_order(JOIN_TAB *tab,ORDER *order,ha_rows select_limit, { /* SELECT * FROM t1 WHERE a=1 ORDER BY a DESC,b DESC - TODO: - Add a new traversal function to read last matching row and - traverse backwards. + + Use a traversal function that starts by reading the last row + with key part (A) and then traverse the index backwards. */ - DBUG_RETURN(0); + if (table->file->option_flag() & 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 */ @@ -5355,10 +5482,15 @@ test_if_skip_sort_order(JOIN_TAB *tab,ORDER *order,ha_rows select_limit, if (!no_changes) { tab->index=nr; - tab->read_first_record= (flag > 0 ? join_init_read_first_with_key: - join_init_read_last_with_key); + 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); } @@ -5368,6 +5500,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) { @@ -5514,7 +5654,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; @@ -6334,7 +6473,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) @@ -6355,10 +6495,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; } @@ -6409,7 +6550,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); @@ -6437,7 +6578,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; diff --git a/sql/sql_select.h b/sql/sql_select.h index 4fcffae31d2..9eb287c8845 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 */ @@ -127,12 +127,13 @@ class TMP_TABLE_PARAM { ha_rows end_write_records; 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_funcs_it(copy_funcs), copy_field(0), group_parts(0), group_length(0) + :copy_funcs_it(copy_funcs), copy_field(0), group_parts(0), + group_length(0), group_null_parts(0) {} ~TMP_TABLE_PARAM() { @@ -190,7 +191,7 @@ TABLE *create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields, 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, @@ -210,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) @@ -219,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); } @@ -235,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) { @@ -260,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) {} @@ -279,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 1dac8033ad7..8629930cf5c 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -652,6 +652,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; @@ -691,6 +693,8 @@ mysqld_show_keys(THD *thd, TABLE_LIST *table_list) } 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()) @@ -701,8 +705,14 @@ mysqld_show_keys(THD *thd, TABLE_LIST *table_list) else net_store_null(packet); net_store_null(packet); // No pack_information yet - net_store_data(packet,convert, - key_info->flags & HA_FULLTEXT ? "FULLTEXT":""); + + /* Null flag */ + uint flags= key_part->field ? key_part->field->flags : 0; + char *pos=(byte*) ((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 */ } @@ -1054,9 +1064,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; } @@ -1190,65 +1199,85 @@ int mysqld_show(THD *thd, const char *wild, show_var_st *variables) /* First group - functions relying on CTX */ case SHOW_SSL_CTX_SESS_ACCEPT: net_store_data(&packet2,(uint32) - SSL_CTX_sess_accept(ssl_acceptor_fd->ssl_context_)); + (!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_CTX_sess_accept_good(ssl_acceptor_fd->ssl_context_)); + (!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_CTX_sess_connect_good(ssl_acceptor_fd->ssl_context_)); + (!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_CTX_sess_accept_renegotiate(ssl_acceptor_fd->ssl_context_)); + (!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_CTX_sess_connect_renegotiate(ssl_acceptor_fd->ssl_context_)); + (!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_CTX_sess_cb_hits(ssl_acceptor_fd->ssl_context_)); + (!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_CTX_sess_hits(ssl_acceptor_fd->ssl_context_)); + (!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_CTX_sess_cache_full(ssl_acceptor_fd->ssl_context_)); + (!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_CTX_sess_misses(ssl_acceptor_fd->ssl_context_)); + (!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_CTX_sess_timeouts(ssl_acceptor_fd->ssl_context_)); + (!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_CTX_sess_number(ssl_acceptor_fd->ssl_context_)); + (!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_CTX_sess_connect(ssl_acceptor_fd->ssl_context_)); + (!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_CTX_sess_get_cache_size(ssl_acceptor_fd->ssl_context_)); + (!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_CTX_get_verify_mode(ssl_acceptor_fd->ssl_context_)); + (!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_CTX_get_verify_depth(ssl_acceptor_fd->ssl_context_)); + (!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: @@ -1296,23 +1325,28 @@ int mysqld_show(THD *thd, const char *wild, show_var_st *variables) 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_) : ""); + 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_) + if (thd->net.vio->ssl_) { - char buf[1024]=""; - for (int i=0; ; i++) + 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; - if (i != 0) - strcat(buf,":"); - strcat(buf,p); - DBUG_PRINT("info",("cipher to add: %s,%s",p,buf)); + pos=strmov(pos, p); + *pos++= ':'; } + if (pos != buf) + pos--; // Remove last ':' + *pos=0; net_store_data(&packet2, buf); - } else + } + else net_store_data(&packet2, ""); break; diff --git a/sql/sql_string.cc b/sql/sql_string.cc index 4649af918d5..8fe84947ac2 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 */ diff --git a/sql/sql_string.h b/sql/sql_string.h index 00a078640b3..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 */ diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 049fb1c182c..14daf8c1924 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); @@ -158,16 +159,20 @@ int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists, wrong_tables.append(String(table->real_name)); } } - if (some_tables_deleted && !dont_log_query) + if (some_tables_deleted) { - mysql_update_log.write(thd, thd->query,thd->query_length); - if (mysql_bin_log.is_open()) + query_cache.invalidate(tables); + 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; if (wrong_tables.length()) { @@ -192,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 @@ -347,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) { @@ -358,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++)) { @@ -379,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); @@ -413,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; @@ -423,10 +442,11 @@ 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; if (key->type == Key::FULLTEXT) { - if (file->option_flag() & HA_NO_FULLTEXT_KEY) + if (!(file->option_flag() & HA_CAN_FULLTEXT)) { my_error(ER_TABLE_CANT_HANDLE_FULLTEXT, MYF(0)); DBUG_RETURN(-1); @@ -482,6 +502,7 @@ int mysql_create_table(THD *thd,const char *db, const char *table_name, 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) { @@ -541,7 +562,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); @@ -553,18 +582,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->option_flag() & 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) @@ -699,8 +739,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 ? @@ -947,6 +990,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; @@ -1024,6 +1068,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; @@ -1033,9 +1078,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"); @@ -1654,24 +1702,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 @@ -1708,6 +1762,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.invalidate(table_list); end_temporary: sprintf(tmp_name,ER(ER_INSERT_INFO),(ulong) (copied+deleted), diff --git a/sql/sql_test.cc b/sql/sql_test.cc index 651fd52d6c3..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 */ 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 index b2ffb97fa81..0d8a41e9966 100644 --- a/sql/sql_union.cc +++ b/sql/sql_union.cc @@ -27,7 +27,7 @@ int mysql_union(THD *thd, LEX *lex,select_result *result) { - SELECT_LEX *sl, *last_sl=(SELECT_LEX *)NULL, lex_sl; + SELECT_LEX *sl, *last_sl, *lex_sl; ORDER *order; List<Item> item_list; TABLE *table; @@ -38,7 +38,10 @@ int mysql_union(THD *thd, LEX *lex,select_result *result) DBUG_ENTER("mysql_union"); /* Fix tables 'to-be-unioned-from' list to point at opened tables */ - for (sl=&lex->select_lex; sl && sl->linkage != NOT_A_SELECT; last_sl=sl, sl=sl->next) + 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; @@ -46,19 +49,27 @@ int mysql_union(THD *thd, LEX *lex,select_result *result) cursor->table= ((TABLE_LIST*) cursor->table)->table; } + /* last_sel now points at the last select where the ORDER BY is stored */ if (sl) { - lex_sl=*sl; - sl=(SELECT_LEX *)NULL; - if (last_sl) last_sl->next=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; + last_sl->next=0; // Remove this extra element + order= (ORDER *) lex_sl->order_list.first; + } + else if (!last_sl->braces) + { + lex_sl= last_sl; // ORDER BY is here + order= (ORDER *) lex_sl->order_list.first; } else - lex_sl.linkage=UNSPECIFIED_TYPE; - - /* Find last select part as it's here ORDER BY and GROUP BY is stored */ - for (last_sl= &lex->select_lex; - last_sl->next; - last_sl=last_sl->next) ; + { + lex_sl=0; + order=0; + } if (lex->select_lex.options & SELECT_DESCRIBE) { @@ -68,7 +79,8 @@ int mysql_union(THD *thd, LEX *lex,select_result *result) res=mysql_select(thd, (TABLE_LIST*) sl->table_list.first, sl->item_list, sl->where, - (sl->braces) ? (ORDER *) sl->order_list.first : (ORDER *) 0, + ((sl->braces) ? + (ORDER *) sl->order_list.first : (ORDER *) 0), (ORDER*) sl->group_list.first, sl->having, (ORDER*) NULL, @@ -79,8 +91,6 @@ int mysql_union(THD *thd, LEX *lex,select_result *result) DBUG_RETURN(0); } - order = (lex_sl.linkage == UNSPECIFIED_TYPE) ? ( (last_sl->braces) ? (ORDER *) 0 : (ORDER *) last_sl->order_list.first) : (ORDER *) lex_sl.order_list.first; - { Item *item; List_iterator<Item> it(lex->select_lex.item_list); @@ -162,11 +172,11 @@ int mysql_union(THD *thd, LEX *lex,select_result *result) } if (!thd->fatal_error) // Check if EOM { - if (lex_sl.linkage == NOT_A_SELECT && ( lex_sl.select_limit || lex_sl.offset_limit)) + 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->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; diff --git a/sql/sql_update.cc b/sql/sql_update.cc index 0422d9664b6..5776cba7e96 100644 --- a/sql/sql_update.cc +++ b/sql/sql_update.cc @@ -15,10 +15,15 @@ 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 */ @@ -252,8 +257,6 @@ int mysql_update(THD *thd, } } - 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); @@ -298,7 +301,6 @@ int mysql_update(THD *thd, } 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(); @@ -321,6 +323,8 @@ int mysql_update(THD *thd, mysql_unlock_tables(thd, thd->lock); thd->lock=0; } + if (updated) + query_cache.invalidate(table_list); delete select; if (error >= 0) send_error(&thd->net,thd->killed ? ER_SERVER_SHUTDOWN : 0); /* purecov: inspected */ @@ -338,3 +342,444 @@ int mysql_update(THD *thd, 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 I insert into each temporary table + values_by_table.push_front(new Item_string(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], (*(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; +} + + +bool multi_update::send_eof() +{ + thd->proc_info="updating the reference tables"; /* out: 1 if error, 0 if success */ + + /* 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 */ +// update_tables->table->no_keyread=0; + 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.invalidate(update_tables); + ::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 2b62ee03575..32a87f194ac 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 @@ -58,6 +58,7 @@ inline Item *or_or_concat(Item* A, Item* B) 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; @@ -112,6 +113,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize); %token CREATE %token CROSS %token DELETE_SYM +%token DO_SYM %token DROP %token INSERT %token FLUSH_SYM @@ -153,7 +155,9 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize); %token BOOLEAN_SYM %token BOTH %token BY +%token CACHE_SYM %token CASCADE +%token CAST_SYM %token CHECKSUM_SYM %token CHECK_SYM %token CIPHER @@ -162,20 +166,23 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize); %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 DIRECTORY_SYM -%token DISTINCT +%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 @@ -234,6 +241,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize); %token MASTER_SERVER_ID_SYM %token MATCH %token MAX_ROWS +%token MAX_QUERIES_PER_HOUR %token MEDIUM_SYM %token MERGE_SYM %token MIN_ROWS @@ -246,6 +254,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize); %token NO_SYM %token NULL_SYM %token NUM +%token OFF %token ON %token OPEN_SYM %token OPTION @@ -262,6 +271,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 @@ -285,6 +295,8 @@ 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 @@ -343,6 +355,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 @@ -376,8 +389,8 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize); %token DAY_SECOND_SYM %token DAY_SYM %token DECODE_SYM -%token DES_ENCRYPT -%token DES_DECRYPT +%token DES_ENCRYPT_SYM +%token DES_DECRYPT_SYM %token ELSE %token ELT_FUNC %token ENCODE_SYM @@ -452,6 +465,7 @@ 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 @@ -502,7 +516,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize); ulonglong_num %type <item> - literal text_literal insert_ident group_ident order_ident + literal text_literal insert_ident order_ident simple_ident select_item2 expr opt_expr opt_else sum_expr in_sum_expr table_wild opt_pad no_in_expr expr_expr simple_expr no_and_expr using_list @@ -536,6 +550,8 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize); %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 @@ -543,7 +559,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize); %type <lex_user> user grant_user %type <NONE> - query verb_clause create change select drop insert replace insert2 + query verb_clause create change select do drop insert replace insert2 insert_values update delete truncate rename show describe load alter optimize flush reset purge begin commit rollback slave master_def master_defs @@ -561,7 +577,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize); 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 + grant_privilege grant_privilege_list mqh_option flush_options flush_option insert_lock_option replace_lock_option equal optional_braces opt_key_definition key_usage_list2 opt_mi_check_type opt_to mi_check_types normal_join @@ -597,6 +613,7 @@ verb_clause: | create | delete | describe + | do | drop | grant | insert @@ -999,7 +1016,8 @@ field_opt_list: | field_option {} field_option: - UNSIGNED { Lex->type|= UNSIGNED_FLAG;} + SIGNED_SYM {} + | UNSIGNED { Lex->type|= UNSIGNED_FLAG;} | ZEROFILL { Lex->type|= UNSIGNED_FLAG | ZEROFILL_FLAG; } opt_len: @@ -1336,14 +1354,18 @@ table_to_table: } /* -** Select : retrieve data from table + Select : retrieve data from table */ select: - SELECT_SYM select_part2 {Select->braces=false;} union + 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_SYM select_part2 ')' { Select->braces=true;} union_opt + select_part2: { @@ -1379,14 +1401,16 @@ select_option: | SQL_BIG_RESULT { Select->options|= SELECT_BIG_RESULT; } | SQL_BUFFER_RESULT { Select->options|= OPTION_BUFFER_RESULT; } | SQL_CALC_FOUND_ROWS { Select->options|= OPTION_FOUND_ROWS; } + | SQL_NO_CACHE_SYM { current_thd->safe_to_cache_query=0; } + | SQL_CACHE_SYM { Select->options |= OPTION_TO_QUERY_CACHE; } | ALL {} select_lock_type: /* empty */ | FOR_SYM UPDATE_SYM - { Lex->lock_option= TL_WRITE; } - | IN_SYM SHARE_SYM MODE_SYM - { Lex->lock_option= TL_READ_WITH_SHARED_LOCKS; } + { 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; current_thd->safe_to_cache_query=0; } select_item_list: select_item_list ',' select_item @@ -1554,9 +1578,18 @@ no_and_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); } @@ -1571,8 +1604,10 @@ simple_expr: { 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 ')' @@ -1594,33 +1629,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_ENCRYPT '(' expr ')' { $$= new Item_func_des_encrypt($3); } - | DES_DECRYPT '(' expr ')' { $$= new Item_func_des_decrypt($3); } - | DES_ENCRYPT '(' expr ',' expr ')' { $$= new Item_func_des_encrypt($3,$5); } - | DES_DECRYPT '(' expr ',' expr ')' { $$= new Item_func_des_decrypt($3,$5); } - | DES_ENCRYPT '(' expr ',' expr ',' expr ')' { $$= new Item_func_des_encrypt($3,$5,$7); } - | DES_DECRYPT '(' expr ',' expr ',' expr ')' { $$= new Item_func_des_decrypt($3,$5,$7); } + | 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 ')' @@ -1633,7 +1680,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); } @@ -1652,10 +1699,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); } @@ -1672,14 +1721,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 ')' @@ -1717,6 +1771,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 ')' { @@ -1724,6 +1779,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 ')' { @@ -1738,6 +1794,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 ')' { @@ -1745,6 +1802,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 ')' { @@ -1752,15 +1810,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 ')' @@ -1772,7 +1836,10 @@ 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); } @@ -1812,6 +1879,16 @@ 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: { Select->expr_list.push_front(new List<Item>); } expr_list2 @@ -2015,7 +2092,7 @@ opt_escape: /* -** group by statement in select + group by statement in select */ group_clause: @@ -2023,13 +2100,13 @@ group_clause: | GROUP BY group_list group_list: - group_list ',' group_ident - { if (add_group_to_list($3,(bool) 1)) YYABORT; } - | group_ident - { if (add_group_to_list($1,(bool) 1)) YYABORT; } + 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; } /* -** Order by statement in select + Order by statement in select */ opt_order_clause: @@ -2037,7 +2114,13 @@ opt_order_clause: | order_clause order_clause: - ORDER_SYM BY { Select->sort_default=1; } 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 @@ -2047,22 +2130,17 @@ order_list: order_dir: /* empty */ { $$ = 1; } - | ASC { $$ = Select->sort_default=1; } - | DESC { $$ = Select->sort_default=0; } + | ASC { $$ =1; } + | DESC { $$ =0; } limit_clause: - /* empty */ - { - SELECT_LEX *sel=Select; - sel->select_limit= (Lex->sql_command == SQLCOM_HA_READ) ? - 1 : current_thd->default_select_limit; - sel->offset_limit= 0L; - } + /* empty */ {} | LIMIT ULONG_NUM { SELECT_LEX *sel=Select; - sel->select_limit= $2; sel->offset_limit=0L; + sel->select_limit= $2; + sel->offset_limit=0L; } | LIMIT ULONG_NUM ',' ULONG_NUM { @@ -2073,7 +2151,10 @@ limit_clause: delete_limit_clause: /* empty */ { - Select->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 { Select->select_limit= (ha_rows) $2; } @@ -2101,6 +2182,7 @@ 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 ')' @@ -2135,9 +2217,20 @@ opt_into: YYABORT; } +/* + DO statement +*/ +do: DO_SYM + { + LEX *lex=Lex; + lex->sql_command = SQLCOM_DO; + if (!(lex->insert_list = new List_item)) + YYABORT; + } + values /* -** Drop : delete tables or index + Drop : delete tables or index */ drop: @@ -2308,7 +2401,7 @@ values: /* Update rows in a table */ update: - UPDATE_SYM opt_low_priority opt_ignore table_name + UPDATE_SYM { LEX *lex=Lex; lex->sql_command = SQLCOM_UPDATE; @@ -2316,10 +2409,7 @@ update: lex->select->order_list.first=0; lex->select->order_list.next= (byte**) &lex->select->order_list.first; } - SET update_list - where_clause - opt_order_clause - delete_limit_clause + 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 @@ -2354,24 +2444,11 @@ delete: single_multi: FROM table_name where_clause opt_order_clause delete_limit_clause {} | table_wild_list - { - LEX *lex=Lex; - lex->sql_command = SQLCOM_MULTI_DELETE; - mysql_init_select(lex); - lex->select->select_limit=HA_POS_ERROR; - lex->auxilliary_table_list.elements=0; - lex->auxilliary_table_list.first=0; - lex->auxilliary_table_list.next= (byte**) &(lex->auxilliary_table_list.first); - } - FROM - { - LEX *lex=Lex; - lex->auxilliary_table_list=lex->select_lex.table_list; - lex->select->table_list.elements=0; - lex->select->table_list.first=0; - lex->select->table_list.next= (byte**) &(lex->select->table_list.first); - } join_table_list where_clause - + { 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 {} @@ -2470,10 +2547,13 @@ show_param: { Lex->sql_command = SQLCOM_SHOW_SLAVE_HOSTS; } - | BINLOG_SYM EVENTS_SYM binlog_in binlog_from limit_clause + | BINLOG_SYM EVENTS_SYM binlog_in binlog_from { - Lex->sql_command = SQLCOM_SHOW_BINLOG_EVENTS; - } + 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; @@ -2579,12 +2659,14 @@ flush_options: 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 */ {} @@ -2601,8 +2683,9 @@ reset_options: | 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 @@ -2763,9 +2846,6 @@ table_wild: | ident '.' ident '.' '*' { $$ = new Item_field((current_thd->client_capabilities & CLIENT_NO_SCHEMA ? NullS : $1.str),$3.str,"*"); } -group_ident: - order_ident order_dir - order_ident: expr { $$=$1; } @@ -2850,32 +2930,36 @@ keyword: | 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 {} | DATETIME {} | DATE_SYM {} | DAY_SYM {} - | DIRECTORY_SYM {} | DELAY_KEY_WRITE_SYM {} - | DISABLE_SYM {} + | DEMAND_SYM {} + | DES_KEY_FILE {} + | DIRECTORY_SYM {} + | DO_SYM {} | DUMPFILE {} | DYNAMIC_SYM {} - | ENABLE_SYM {} | END {} | ENUM {} | ESCAPE_SYM {} | EVENTS_SYM {} | EXTENDED_SYM {} | FAST_SYM {} + | DISABLE_SYM {} + | ENABLE_SYM {} | FULL {} | FILE_SYM {} | FIRST_SYM {} @@ -2908,6 +2992,7 @@ keyword: | MASTER_USER_SYM {} | MASTER_PASSWORD_SYM {} | MASTER_CONNECT_RETRY_SYM {} + | MAX_QUERIES_PER_HOUR {} | MEDIUM_SYM {} | MERGE_SYM {} | MINUTE_SYM {} @@ -2921,12 +3006,14 @@ keyword: | 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 {} @@ -2945,9 +3032,13 @@ 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 {} | START_SYM {} | STATUS_SYM {} | STOP_SYM {} @@ -3071,6 +3162,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); @@ -3117,6 +3209,11 @@ option_value: 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;} @@ -3234,6 +3331,8 @@ handler: 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; } @@ -3293,9 +3392,10 @@ grant: lex->select->db=0; lex->ssl_type=SSL_TYPE_NONE; lex->ssl_cipher=lex->x509_subject=lex->x509_issuer=0; + lex->mqh=0; } grant_privileges ON opt_table TO_SYM user_list - require_clause grant_option + require_clause grant_option mqh_option grant_privileges: grant_privilege_list {} @@ -3486,6 +3586,15 @@ grant_option: /* empty */ {} | WITH GRANT OPTION { Lex->grant |= GRANT_ACL;} +mqh_option: + /* empty */ {} + | AND WITH MAX_QUERIES_PER_HOUR EQ NUM + { + Lex->mqh=atoi($5.str); + if (Lex->mqh > 65535) + YYABORT; + } + begin: BEGIN_SYM { Lex->sql_command = SQLCOM_BEGIN;} opt_work @@ -3519,12 +3628,11 @@ union_list: net_printf(&lex->thd->net, ER_WRONG_USAGE,"UNION","INTO"); YYABORT; } - if (lex->select->linkage==NOT_A_SELECT) + if (lex->select->linkage == NOT_A_SELECT || mysql_new_select(lex)) YYABORT; - mysql_new_select(lex); lex->select->linkage=UNION_TYPE; } - select + select_init union_opt: union {} @@ -3535,11 +3643,11 @@ optional_order_or_limit: | { LEX *lex=Lex; - if (!lex->select->braces) - YYABORT; - mysql_new_select(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 diff --git a/sql/stacktrace.c b/sql/stacktrace.c index dd7db5548c1..d86d65f567e 100644 --- a/sql/stacktrace.c +++ b/sql/stacktrace.c @@ -122,7 +122,7 @@ terribly wrong...\n"); return; } #endif /* __alpha__ */ - + if (!stack_bottom || (gptr) stack_bottom > (gptr) &fp) { ulong tmp= min(0x10000,thread_stack); @@ -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 e76e628ddda..e0f32ebbe43 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 */ diff --git a/sql/table.cc b/sql/table.cc index eb6d8fbb9cd..9aae9e17e5a 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 */ @@ -120,10 +120,8 @@ int openfrm(const char *name, const char *alias, uint db_stat, uint prgflag, outparam->db_record_offset=1; if (db_create_options & HA_OPTION_LONG_BLOB_PTR) outparam->blob_ptr_size=portable_sizeof_char_ptr; - outparam->db_low_byte_first=test(outparam->db_type == DB_TYPE_MYISAM || - outparam->db_type == DB_TYPE_BERKELEY_DB || - outparam->db_type == DB_TYPE_HEAP); - + /* Set temporaryly a good value for db_low_byte_first */ + outparam->db_low_byte_first=test(outparam->db_type != DB_TYPE_ISAM); error=4; outparam->max_rows=uint4korr(head+18); outparam->min_rows=uint4korr(head+22); @@ -467,7 +465,8 @@ int openfrm(const char *name, const char *alias, uint db_stat, uint prgflag, 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; } } @@ -1008,7 +1007,7 @@ void update_create_info_from_table(HA_CREATE_INFO *create_info, TABLE *table) 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 071c447e660..259c34030b2 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 */ diff --git a/sql/thr_malloc.cc b/sql/thr_malloc.cc index 4017b7c30b8..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 */ diff --git a/sql/time.cc b/sql/time.cc index 4d194a94b8e..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 */ @@ -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) diff --git a/sql/udf_example.cc b/sql/udf_example.cc index 44a1d00437a..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 */ diff --git a/sql/uniques.cc b/sql/uniques.cc index fcee97dbb1a..6b05618bcc7 100644 --- a/sql/uniques.cc +++ b/sql/uniques.cc @@ -127,14 +127,14 @@ bool Unique::get(TABLE *table) 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)))) diff --git a/sql/unireg.cc b/sql/unireg.cc index b0dd3471a64..8c72dfc2855 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 */ @@ -515,16 +515,23 @@ static bool make_empty_rec(File file,enum db_type table_type, uchar *buff,*null_pos; TABLE table; create_field *field; + handler *handler; DBUG_ENTER("make_empty_rec"); /* We need a table to generate columns for default values */ bzero((char*) &table,sizeof(table)); - table.db_low_byte_first=test(table_type == DB_TYPE_MYISAM || - table_type == DB_TYPE_HEAP); - table.blob_ptr_size=portable_sizeof_char_ptr; + handler= get_new_handler((TABLE*) 0, table_type); - if (!(buff=(uchar*) my_malloc((uint) reclength,MYF(MY_WME | MY_ZEROFILL)))) + if (!handler || + !(buff=(uchar*) my_malloc((uint) reclength,MYF(MY_WME | MY_ZEROFILL)))) + { + delete handler; DBUG_RETURN(1); + } + + table.db_low_byte_first= handler->low_byte_first(); + table.blob_ptr_size=portable_sizeof_char_ptr; + firstpos=reclength; null_count=0; if (!(table_options & HA_OPTION_PACK_RECORD)) @@ -574,8 +581,11 @@ static bool make_empty_rec(File file,enum db_type table_type, regfield->reset(); delete regfield; } - bfill((byte*) buff+null_length,firstpos-null_length,255);/* Fill not used startpos */ + + /* Fill not used startpos */ + bfill((byte*) buff+null_length,firstpos-null_length,255); error=(int) my_write(file,(byte*) buff,(uint) reclength,MYF_RW); my_free((gptr) buff,MYF(MY_FAE)); + delete handler; DBUG_RETURN(error); } /* make_empty_rec */ diff --git a/sql/unireg.h b/sql/unireg.h index abba79cbda5..9b220f87918 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 */ |