diff options
85 files changed, 4764 insertions, 987 deletions
diff --git a/Docs/manual.texi b/Docs/manual.texi index c92fb71d60d..9e52ea95850 100644 --- a/Docs/manual.texi +++ b/Docs/manual.texi @@ -8514,6 +8514,14 @@ libc internal error: _rmutex_unlock: rmutex not held Add @code{-mt} to @code{CFLAGS} and @code{CXXFLAGS} and try again. +If you are using the SFW version of gcc (which comes with Solaris 8), +you must add @file{/opt/sfw/lib} to the environment variable +@code{LD_LIBRARY_PATH} before running configure. + +If you are using the gcc available from @code{sunfreeware.com}, you may +have many problems. You should recompile gcc and GNU binutils on the +machine you will be running them from to avoid any problems. + If you get the following error when compiling MySQL with @code{gcc}, it means that your @code{gcc} is not configured for your version of Solaris: @@ -17432,6 +17440,7 @@ information and a description of what it means. * OPTIMIZE TABLE:: @code{OPTIMIZE TABLE} Syntax * ANALYZE TABLE:: @code{ANALYZE TABLE} Syntax * FLUSH:: @code{FLUSH} Syntax +* RESET:: @code{RESET} Syntax * KILL:: @code{KILL} Syntax * SHOW:: @code{SHOW} Syntax @end menu @@ -17519,7 +17528,7 @@ If the table hasn't changed since the last @code{ANALYZE TABLE} command, the table will not be analysed again. -@node FLUSH, KILL, ANALYZE TABLE, Database Administration +@node FLUSH, RESET, ANALYZE TABLE, Database Administration @subsection @code{FLUSH} Syntax @findex FLUSH @@ -17560,7 +17569,9 @@ signal to the @code{mysqld} server. @item @code{PRIVILEGES} @tab Reloads the privileges from the grant tables in the @code{mysql} database. -@item @code{TABLES} @tab Closes all open tables and force all tables in use to be closed. +@item @code{QUERY CACHE} @tab Defragment the query cache to better utilize the memory. This command will not remove any queries from the cache. + +@item @code{TABLES} @tab Closes all open tables and force all tables in use to be closed. This also flushes the query cache. @item @code{[TABLE | TABLES] table_name [,table_name...]} @tab Flushes only the given tables. @@ -17573,12 +17584,34 @@ You can also access each of the commands shown above with the @code{mysqladmin} utility, using the @code{flush-hosts}, @code{flush-logs}, @code{reload}, or @code{flush-tables} commands. -Take also a look at the @code{RESET} command used with -replication. @xref{Replication SQL}. +Take also a look at the @code{RESET} command used with replication. +@xref{RESET}. + +@node RESET, KILL, FLUSH, Database Administration +@subsection @code{RESET} Syntax + +@example +FLUSH flush_option [,flush_option] +@end example + +The @code{RESET} command is used to clear things. It also acts as an stronger +version of the @code{FLUSH} command. @xref{FLUSH}. +@multitable @columnfractions .15 .85 +@item @code{MASTER} +@tab Deletes all binary logs listed in the index file, resetting the binlog +index file to be empty. In pre-3.23.26 versions, @code{FLUSH MASTER} (Master) +@item @code{SLAVE} +@tab Makes the slave forget its replication position in the master +logs. In pre 3.23.26 versions the command was called +@code{FLUSH SLAVE}(Slave) -@node KILL, SHOW, FLUSH, Database Administration +@item @code{QUERY CACHE} +@tab Removes all query results from the query cache. +@end multitable + +@node KILL, SHOW, RESET, Database Administration @subsection @code{KILL} Syntax @findex KILL @@ -18049,6 +18082,9 @@ differ somewhat: | protocol_version | 10 | | record_buffer | 131072 | | query_buffer_size | 0 | +| query_cache_limit | 1048576 | +| query_cache_size | 16768060 | +| query_cache_startup_type | 1 | | safe_show_database | OFF | | server_id | 0 | | skip_locking | ON | @@ -18449,6 +18485,18 @@ buffer to avoid a disk seeks. If not set, then it's set to the value of The initial allocation of the query buffer. If most of your queries are long (like when inserting blobs), you should increase this! +@item @code{query_cache_limit} +Don't cache results that are bigger than this. (Default 1M). + +@item @code{query_cache_size} +The memory allocated to store results from old queries. If this is zero +the query cache is disabled. + +@item @code{query_cache_startup_type} +This may have be set to 0 (cache results but don't retrieve results from +cache), 1 (cache results by defaults) or 2 (only cache @code{SELECT}'s marked +with @code{SQL_CACHE}). + @item @code{safe_show_databases} Don't show databases for which the user doesn't have any database or table privileges. This can improve security if you're concerned about @@ -25663,6 +25711,17 @@ flag again, the @code{SQL_MAX_JOIN_SIZE} variable will be ignored. You can set a default value for this variable by starting @code{mysqld} with @code{-O max_join_size=#}. +@item SQL_QUERY_CACHE_TYPE = [OFF | ON | DEMAND] +@item SQL_QUERY_CACHE_TYPE = [0 | 1 | 2] + +The numbers are standing for the correspoding verbose option. + +@multitable @columnfractions .3 .7 +@item 0 or OFF @tab Cache @code{SELECT} results, but don't retrieve results from cache. +@item 1 or ON @tab Cache all @code{SELECT}'s that are not marked with @code{SQL_NO_CACHE}. +@item 2 or DEMAND @tab Cache only @code{SELECT SQL_CACHE}) queries. +@end multitable + @item SQL_SAFE_UPDATES = 0 | 1 If set to @code{1}, MySQL will abort if an @code{UPDATE} or @code{DELETE} is attempted that doesn't use a key or @code{LIMIT} in the @@ -31001,7 +31060,7 @@ mysql> SELECT id,FLOOR(value/100) FROM tbl_name ORDER BY RAND(); @c help SELECT @example SELECT [STRAIGHT_JOIN] [SQL_SMALL_RESULT] [SQL_BIG_RESULT] [SQL_BUFFER_RESULT] - [HIGH_PRIORITY] + [SQL_CACHE | SQL_NO_CACHE] [HIGH_PRIORITY] [DISTINCT | DISTINCTROW | ALL] select_expression,... [INTO @{OUTFILE | DUMPFILE@} 'file_name' export_options] @@ -31129,9 +31188,8 @@ mysql> select user,max(salary) AS sum from users @end example @item -@code{SQL_SMALL_RESULT}, @code{SQL_BIG_RESULT}, @code{SQL_BUFFER_RESULT}, -@code{STRAIGHT_JOIN}, and @code{HIGH_PRIORITY} are MySQL extensions -to ANSI SQL92. +All options beginning with @code{SQL_}, @code{STRAIGHT_JOIN}, and +@code{HIGH_PRIORITY} are MySQL extensions to ANSI SQL. @item @code{HIGH_PRIORITY} will give the @code{SELECT} higher priority than @@ -31160,6 +31218,14 @@ temporary tables to store the resulting table instead of using sorting. In MySQL Version 3.23 this shouldn't normally be needed. @item +@code{SQL_CACHE} tells MySQL to store the query result in the query cache +even if you are using @code{SQL_QUERY_CACHE_METHOD} 2 (= @code{DEMAND}). + +@item +@code{SQL_NO_CACHE} tells MySL to not store the query result in the +query cache. + +@item @cindex @code{GROUP BY}, extensions to ANSI SQL If you use @code{GROUP BY}, the output rows will be sorted according to the @code{GROUP BY} as if you would have had an @code{ORDER BY} over all the fields @@ -46082,6 +46148,11 @@ Our TODO section contains what we plan to have in 4.0. @xref{TODO MySQL 4.0}. @itemize @bullet @item +A new query cache to cache results from identical @code{SELECT} queries. +@item +Fixed core dump bug on 64 bit machines when it got a wrong communication +packet. +@item @code{MATCH ... AGAINST(... IN BOOLEAN MODE)} can now work without @code{FULLTEXT} index. @item @@ -46275,6 +46346,7 @@ users use this code as the rest of the code and because of this we are not yet 100% confident in this code. @menu +* News-3.23.47:: Changes in release 3.23.47 * News-3.23.46:: Changes in release 3.23.46 * News-3.23.45:: Changes in release 3.23.45 * News-3.23.44:: Changes in release 3.23.44 @@ -46325,10 +46397,22 @@ not yet 100% confident in this code. * News-3.23.0:: Changes in release 3.23.0 @end menu -@node News-3.23.46, News-3.23.45, News-3.23.x, News-3.23.x +@node News-3.23.47, News-3.23.46, News-3.23.x, News-3.23.x +@appendixsubsec Changes in release 3.23.47 +@itemize @bullet +@item +Fixed bug when using @code{t1 LEFT JOIN t2 ON t2.key=constant}. +@item +@code{mysqlconfig} now also work with binary (relocated) distributions. +@end itemize + +@node News-3.23.46, News-3.23.45, News-3.23.47, News-3.23.x @appendixsubsec Changes in release 3.23.46 @itemize @bullet @item +InnoDB and BDB tables will now use index when doing an @code{ORDER BY} +on the whole table. +@item Fixed bug where one got an empty set instead of a DEADLOCK error when using BDB tables. @item diff --git a/build-tags b/build-tags new file mode 100755 index 00000000000..90b957eb3bc --- /dev/null +++ b/build-tags @@ -0,0 +1,11 @@ +#! /bin/sh + +if [ ! -f configure.in ] ; then + echo "$0 must be run from MySQL source root" + exit 1 +fi + +rm -f TAGS +find -not -path \*SCCS\* -and \ + \( -name \*.cc -or -name \*.h -or -name \*.yy -or -name \*.c \) \ + -print -exec etags -o TAGS --append {} \; diff --git a/include/ft_global.h b/include/ft_global.h index 064dd7a6538..e4b63aecbb2 100644 --- a/include/ft_global.h +++ b/include/ft_global.h @@ -49,7 +49,7 @@ extern const char *ft_precompiled_stopwords[]; extern ulong ft_min_word_len; extern ulong ft_max_word_len; extern ulong ft_max_word_len_for_sort; -extern char *ft_boolean_syntax; +extern const char *ft_boolean_syntax; int ft_init_stopwords(const char **); void ft_free_stopwords(void); diff --git a/include/hash.h b/include/hash.h index c98b0851645..b6ca7e354b9 100644 --- a/include/hash.h +++ b/include/hash.h @@ -54,6 +54,7 @@ gptr hash_next(HASH *info,const byte *key,uint length); my_bool hash_insert(HASH *info,const byte *data); my_bool hash_delete(HASH *hash,byte *record); my_bool hash_update(HASH *hash,byte *record,byte *old_key,uint old_key_length); +void hash_replace(HASH *hash, uint idx, byte *new_row); my_bool hash_check(HASH *hash); /* Only in debug library */ #define hash_clear(H) bzero((char*) (H),sizeof(*(H))) diff --git a/include/myisam.h b/include/myisam.h index 36c0de5034f..99e21304108 100644 --- a/include/myisam.h +++ b/include/myisam.h @@ -191,6 +191,8 @@ typedef struct st_columndef /* column information */ #endif } MI_COLUMNDEF; +/* invalidator function reference for Query Cache */ +typedef void (* invalidator_by_filename)(const char * filename); extern my_string myisam_log_filename; /* Name of logfile */ extern uint myisam_block_size; diff --git a/include/myisammrg.h b/include/myisammrg.h index 1ae825b4b94..0230f077814 100644 --- a/include/myisammrg.h +++ b/include/myisammrg.h @@ -95,6 +95,7 @@ extern int myrg_lock_database(MYRG_INFO *file,int lock_type); extern int myrg_create(const char *name, const char **table_names, uint insert_method, my_bool fix_names); extern int myrg_extra(MYRG_INFO *file,enum ha_extra_function function); +extern void myrg_extrafunc(MYRG_INFO *info,invalidator_by_filename inv); extern ha_rows myrg_records_in_range(MYRG_INFO *info,int inx, const byte *start_key,uint start_key_len, enum ha_rkey_function start_search_flag, diff --git a/include/mysql_com.h b/include/mysql_com.h index 048b6b98918..4c6355e9f7c 100644 --- a/include/mysql_com.h +++ b/include/mysql_com.h @@ -78,6 +78,10 @@ enum enum_server_command {COM_SLEEP,COM_QUIT,COM_INIT_DB,COM_QUERY, #define REFRESH_READ_LOCK 16384 /* Lock tables for read */ #define REFRESH_FAST 32768 /* Intern flag */ +/* RESET (remove all queries) from query cache */ +#define REFRESH_QUERY_CACHE 65536 +#define REFRESH_QUERY_CACHE_FREE 0x20000L /* pack query cache */ + #define CLIENT_LONG_PASSWORD 1 /* new more secure passwords */ #define CLIENT_FOUND_ROWS 2 /* Found instead of affected rows */ #define CLIENT_LONG_FLAG 4 /* Get all column flags */ @@ -126,6 +130,7 @@ typedef struct st_net { unsigned char reading_or_writing; char save_char; my_bool no_send_ok; + gptr query_cache_query; } NET; #define packet_error (~(unsigned long) 0) diff --git a/innobase/include/buf0buf.ic b/innobase/include/buf0buf.ic index 49f6dc59503..52bee0eb282 100644 --- a/innobase/include/buf0buf.ic +++ b/innobase/include/buf0buf.ic @@ -211,6 +211,9 @@ buf_block_align( block = buf_pool_get_nth_block(buf_pool, (ptr - frame_zero) >> UNIV_PAGE_SIZE_SHIFT); + ut_a(block >= buf_pool->blocks); + ut_a(block < buf_pool->blocks + buf_pool->max_size); + return(block); } @@ -235,6 +238,9 @@ buf_block_align_low( block = buf_pool_get_nth_block(buf_pool, (ptr - frame_zero) >> UNIV_PAGE_SIZE_SHIFT); + ut_a(block >= buf_pool->blocks); + ut_a(block < buf_pool->blocks + buf_pool->max_size); + return(block); } @@ -253,9 +259,9 @@ buf_frame_align( frame = ut_align_down(ptr, UNIV_PAGE_SIZE); - ut_ad((ulint)frame + ut_a((ulint)frame >= (ulint)(buf_pool_get_nth_block(buf_pool, 0)->frame)); - ut_ad((ulint)frame <= (ulint)(buf_pool_get_nth_block(buf_pool, + ut_a((ulint)frame <= (ulint)(buf_pool_get_nth_block(buf_pool, buf_pool->max_size - 1)->frame)); return(frame); } diff --git a/innobase/include/mtr0log.ic b/innobase/include/mtr0log.ic index c2150660794..0598f1a9536 100644 --- a/innobase/include/mtr0log.ic +++ b/innobase/include/mtr0log.ic @@ -163,6 +163,13 @@ mlog_write_initial_log_record_fast( space = buf_block_get_space(block); offset = buf_block_get_page_no(block); + if (space != 0 || offset > 0x8FFFFFFF) { + fprintf(stderr, + "InnoDB: error: buffer page pointer %lx has nonsensical space id %lu\n" + "InnoDB: or page no %lu\n", (ulint)ptr, space, offset); + ut_a(0); + } + mach_write_to_1(log_ptr, type); log_ptr++; log_ptr += mach_write_compressed(log_ptr, space); diff --git a/innobase/log/log0recv.c b/innobase/log/log0recv.c index 999429cbfcd..1734cfadfff 100644 --- a/innobase/log/log0recv.c +++ b/innobase/log/log0recv.c @@ -1316,7 +1316,9 @@ recv_parse_log_rec( new_ptr = mlog_parse_initial_log_record(ptr, end_ptr, type, space, page_no); - if (!new_ptr) { + /* Check that space id and page_no are sensible */ + + if (!new_ptr || *space != 0 || *page_no > 0x8FFFFFFF) { return(0); } diff --git a/libmysql/net.c b/libmysql/net.c index 5e002a0f63e..5a39b071b4f 100644 --- a/libmysql/net.c +++ b/libmysql/net.c @@ -94,6 +94,7 @@ inline int local_thr_alarm(my_bool *A,int B __attribute__((unused)),ALARM *C __a #ifdef MYSQL_SERVER extern ulong bytes_sent, bytes_received; extern pthread_mutex_t LOCK_bytes_sent , LOCK_bytes_received; +extern void query_cache_insert(NET *net, const char *packet, ulong length); #else #undef statistic_add #define statistic_add(A,B,C) @@ -108,7 +109,7 @@ static int net_write_buff(NET *net,const char *packet,ulong len); 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 +126,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 +162,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)))) { @@ -187,7 +189,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 +243,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 +252,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 +282,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 +294,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)); } @@ -341,6 +343,10 @@ net_real_write(NET *net,const char *packet,ulong len) my_bool net_blocking = vio_is_blocking(net->vio); DBUG_ENTER("net_real_write"); +#ifdef MYSQL_SERVER + query_cache_insert(net, packet, len); +#endif + if (net->error == 2) DBUG_RETURN(-1); /* socket can't be used */ @@ -351,8 +357,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; @@ -389,7 +395,7 @@ net_real_write(NET *net,const char *packet,ulong len) 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)) @@ -473,7 +479,7 @@ net_real_write(NET *net,const char *packet,ulong len) 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,7 +502,7 @@ 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); } } @@ -521,8 +527,8 @@ my_real_read(NET *net, ulong *complen) 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; @@ -599,7 +605,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 +614,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); } @@ -655,14 +661,14 @@ my_real_read(NET *net, ulong *complen) { #ifdef MYSQL_SERVER 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; } } diff --git a/libmysqld/lib_sql.cc b/libmysqld/lib_sql.cc index d241ceaada0..ab82b8de2f2 100644 --- a/libmysqld/lib_sql.cc +++ b/libmysqld/lib_sql.cc @@ -460,7 +460,7 @@ int STDCALL mysql_server_init(int argc, char **argv, char **groups) umask(((~my_umask) & 0666)); table_cache_init(); hostname_cache_init(); - sql_cache_init(); + /*sql_cache_init();*/ randominit(&sql_rand,(ulong) start_time,(ulong) start_time/2); reset_floating_point_exceptions(); init_thr_lock(); diff --git a/myisam/ft_boolean_search.c b/myisam/ft_boolean_search.c index fce6fee7c8d..5e471daa0ed 100644 --- a/myisam/ft_boolean_search.c +++ b/myisam/ft_boolean_search.c @@ -86,7 +86,7 @@ typedef struct st_ft_info { MEM_ROOT mem_root; } FTB; -int FTB_WORD_cmp(void *v, byte *a, byte *b) +int FTB_WORD_cmp(void *v __attribute__((unused)), byte *a, byte *b) { /* ORDER BY docid, ndepth DESC */ int i=CMP_NUM(((FTB_WORD *)a)->docid, ((FTB_WORD *)b)->docid); @@ -109,7 +109,7 @@ void _ftb_parse_query(FTB *ftb, byte **start, byte *end, return; param.prev=' '; - while (res=ft_get_word(start,end,&w,¶m)) + while ((res=ft_get_word(start,end,&w,¶m))) { byte r=param.plusminus; float weight=(param.pmsign ? nwghts : wghts)[(r>5)?5:((r<-5)?-5:r)]; @@ -277,7 +277,7 @@ void _ftb_climb_the_tree(FTB_WORD *ftbw, my_off_t curdoc) int ft_boolean_read_next(FT_INFO *ftb, char *record) { - FTB_EXPR *ftbe, *up; + FTB_EXPR *ftbe; FTB_WORD *ftbw; MI_INFO *info=ftb->info; MI_KEYDEF *keyinfo=info->s->keyinfo+ftb->keynr; diff --git a/myisam/ft_dump.c b/myisam/ft_dump.c index 48c26d1aa79..07d079cea1e 100644 --- a/myisam/ft_dump.c +++ b/myisam/ft_dump.c @@ -209,15 +209,15 @@ static void get_options(int argc, char *argv[]) static void usage(char *argv[]) { - printf(" -Use: %s [-%s] <table_name> <index_no> - --d dump index (incl. data offsets and word weights) --s report global stats --l report length distribution --c calculate per-word stats (counts and global weights) --v be verbose --h this text\n + printf("\n\ +Use: %s [-%s] <table_name> <index_no>\n\ +\n\ +-d Dump index (incl. data offsets and word weights)\n\ +-s Report global stats\n\ +-c Calculate per-word stats (counts and global weights)\n\ +-l Report length distribution\n\ +-v Be verbose\n\ +-h This text\n\ ", *argv, options); exit(1); } diff --git a/myisam/ft_parser.c b/myisam/ft_parser.c index 0d1495da548..c1b1190bcab 100644 --- a/myisam/ft_parser.c +++ b/myisam/ft_parser.c @@ -38,8 +38,9 @@ typedef struct st_ft_docstat { static int FT_WORD_cmp(void* cmp_arg, FT_WORD *w1, FT_WORD *w2) { return _mi_compare_text(default_charset_info, - (uchar*) w1->pos,w1->len, - (uchar*) w2->pos, w2->len,(my_bool)cmp_arg); + (uchar*) w1->pos, w1->len, + (uchar*) w2->pos, w2->len, + (my_bool) (cmp_arg != 0)); } static int walk_and_copy(FT_WORD *word,uint32 count,FT_DOCSTAT *docstat) diff --git a/myisam/ft_static.c b/myisam/ft_static.c index 08ecbdc1009..566b08596a3 100644 --- a/myisam/ft_static.c +++ b/myisam/ft_static.c @@ -21,7 +21,7 @@ ulong ft_min_word_len=4; ulong ft_max_word_len=HA_FT_MAXLEN; ulong ft_max_word_len_for_sort=20; -char *ft_boolean_syntax="+ -><()~*"; +const char *ft_boolean_syntax="+ -><()~*"; const MI_KEYSEG ft_keysegs[FT_SEGS]={ { diff --git a/myisam/ft_update.c b/myisam/ft_update.c index 1bc0ace6c77..3d394eeacac 100644 --- a/myisam/ft_update.c +++ b/myisam/ft_update.c @@ -67,8 +67,9 @@ uint _mi_ft_parse(TREE *parsed, MI_INFO *info, uint keynr, const byte *record) return 0; } -FT_WORD * _mi_ft_parserecord(MI_INFO *info, uint keynr, byte *keybuf, - const byte *record) +FT_WORD * _mi_ft_parserecord(MI_INFO *info, uint keynr, + byte *keybuf __attribute__((unused)), + const byte *record) { TREE ptree; diff --git a/myisam/ftdefs.h b/myisam/ftdefs.h index 147c3f5b5e6..ee35ccbb14a 100644 --- a/myisam/ftdefs.h +++ b/myisam/ftdefs.h @@ -123,6 +123,7 @@ byte ft_simple_get_word(byte **, byte *, FT_WORD *); int ft_parse(TREE *, byte *, int); FT_WORD * ft_linearize(/*MI_INFO *, uint, byte *, */TREE *); FT_WORD * _mi_ft_parserecord(MI_INFO *, uint, byte *, const byte *); +uint _mi_ft_parse(TREE *parsed, MI_INFO *info, uint keynr, const byte *record); const struct _ft_vft _ft_vft_nlq; FT_INFO *ft_init_nlq_search(MI_INFO *, uint, byte *, uint, my_bool); @@ -141,4 +142,3 @@ void ft_boolean_close_search(FT_INFO *); float ft_boolean_get_relevance(FT_INFO *); my_off_t ft_boolean_get_docid(FT_INFO *); void ft_boolean_reinit_search(FT_INFO *); - diff --git a/myisam/mi_delete.c b/myisam/mi_delete.c index 65d245e64f9..2a0cd6f6b6d 100644 --- a/myisam/mi_delete.c +++ b/myisam/mi_delete.c @@ -97,6 +97,12 @@ int mi_delete(MI_INFO *info,const byte *record) myisam_log_command(MI_LOG_DELETE,info,(byte*) lastpos,sizeof(lastpos),0); VOID(_mi_writeinfo(info,WRITEINFO_UPDATE_KEYFILE)); allow_break(); /* Allow SIGHUP & SIGINT */ + if (info->invalidator != 0) + { + DBUG_PRINT("info", ("invalidator... '%s' (delete)", info->filename)); + (*info->invalidator)(info->filename); + info->invalidator=0; + } DBUG_RETURN(0); err: diff --git a/myisam/mi_locking.c b/myisam/mi_locking.c index 68ef52ea819..22ec4ba875c 100644 --- a/myisam/mi_locking.c +++ b/myisam/mi_locking.c @@ -43,8 +43,7 @@ int mi_lock_database(MI_INFO *info, int lock_type) pthread_mutex_lock(&share->intern_lock); if (share->kfile >= 0) /* May only be false on windows */ { - switch (lock_type) - { + switch (lock_type) { case F_UNLCK: if (info->lock_type == F_RDLCK) count= --share->r_locks; @@ -201,6 +200,7 @@ int mi_lock_database(MI_INFO *info, int lock_type) } VOID(_mi_test_if_changed(info)); info->lock_type=lock_type; + info->invalidator=info->s->invalidator; share->w_locks++; share->tot_locks++; break; @@ -319,6 +319,7 @@ int _mi_readinfo(register MI_INFO *info, int lock_type, int check_keybuffer) } if (check_keybuffer) VOID(_mi_test_if_changed(info)); + info->invalidator=info->s->invalidator; } else if (lock_type == F_WRLCK && info->lock_type == F_RDLCK) { diff --git a/myisam/mi_update.c b/myisam/mi_update.c index ac843dbb6bd..bb022c5f4cd 100644 --- a/myisam/mi_update.c +++ b/myisam/mi_update.c @@ -136,6 +136,12 @@ int mi_update(register MI_INFO *info, const byte *oldrec, byte *newrec) myisam_log_record(MI_LOG_UPDATE,info,newrec,info->lastpos,0); VOID(_mi_writeinfo(info,key_changed ? WRITEINFO_UPDATE_KEYFILE : 0)); allow_break(); /* Allow SIGHUP & SIGINT */ + if (info->invalidator != 0) + { + DBUG_PRINT("info", ("invalidator... '%s' (update)", info->filename)); + (*info->invalidator)(info->filename); + info->invalidator=0; + } DBUG_RETURN(0); err: diff --git a/myisam/mi_write.c b/myisam/mi_write.c index f8c19c40b01..d4a17ebf19b 100644 --- a/myisam/mi_write.c +++ b/myisam/mi_write.c @@ -149,6 +149,12 @@ int mi_write(MI_INFO *info, byte *record) info->lastpos=filepos; myisam_log_record(MI_LOG_WRITE,info,record,filepos,0); VOID(_mi_writeinfo(info, WRITEINFO_UPDATE_KEYFILE)); + if (info->invalidator != 0) + { + DBUG_PRINT("info", ("invalidator... '%s' (update)", info->filename)); + (*info->invalidator)(info->filename); + info->invalidator=0; + } allow_break(); /* Allow SIGHUP & SIGINT */ DBUG_RETURN(0); diff --git a/myisam/myisamdef.h b/myisam/myisamdef.h index e8b9c1b83ec..203101a2f48 100644 --- a/myisam/myisamdef.h +++ b/myisam/myisamdef.h @@ -66,8 +66,10 @@ typedef struct st_mi_state_info ulong unique; /* Unique number for this process */ ulong update_count; /* Updated for each write lock */ ulong status; + ulong *rec_per_key_part; my_off_t *key_root; /* Start of key trees */ my_off_t *key_del; /* delete links for trees */ + my_off_t rec_per_key_rows; /* Rows when calculating rec_per_key */ ulong sec_index_changed; /* Updated when new sec_index */ ulong sec_index_used; /* which extra index are in use */ @@ -80,8 +82,6 @@ typedef struct st_mi_state_info uint sortkey; /* sorted by this key (not used) */ uint open_count; uint8 changed; /* Changed since myisamchk */ - my_off_t rec_per_key_rows; /* Rows when calculating rec_per_key */ - ulong *rec_per_key_part; /* the following isn't saved on disk */ uint state_diff_length; /* Should be 0 */ @@ -164,10 +164,25 @@ typedef struct st_mi_isam_share { /* Shared between opens */ char *data_file_name, /* Resolved path names from symlinks */ *index_file_name; byte *file_map; /* mem-map of file if possible */ + MI_DECODE_TREE *decode_trees; + uint16 *decode_tables; + int (*read_record)(struct st_myisam_info*, my_off_t, byte*); + int (*write_record)(struct st_myisam_info*, const byte*); + int (*update_record)(struct st_myisam_info*, my_off_t, const byte*); + int (*delete_record)(struct st_myisam_info*); + int (*read_rnd)(struct st_myisam_info*, byte*, my_off_t, my_bool); + int (*compare_record)(struct st_myisam_info*, const byte *); + ha_checksum (*calc_checksum)(struct st_myisam_info*, const byte *); + int (*compare_unique)(struct st_myisam_info*, MI_UNIQUEDEF *, + const byte *record, my_off_t pos); + invalidator_by_filename invalidator; /* query cache invalidator */ ulong this_process; /* processid */ ulong last_process; /* For table-change-check */ ulong last_version; /* Version on start */ ulong options; /* Options used */ + ulong min_pack_length; /* Theese are used by packed data */ + ulong max_pack_length; + ulong state_diff_length; uint rec_reflength; /* rec_reflength in use now */ File kfile; /* Shared keyfile */ File data_file; /* Shared data file */ @@ -175,29 +190,15 @@ typedef struct st_mi_isam_share { /* Shared between opens */ uint reopen; /* How many times reopened */ uint w_locks,r_locks,tot_locks; /* Number of read/write locks */ uint blocksize; /* blocksize of keyfile */ - ulong min_pack_length; /* Theese are used by packed data */ - ulong max_pack_length; - ulong state_diff_length; + myf write_flag; + int rnd; /* rnd-counter */ + enum data_file_type data_file_type; my_bool changed, /* If changed since lock */ global_changed, /* If changed since open */ not_flushed, temporary,delay_key_write, concurrent_insert, fulltext_index; - myf write_flag; - int rnd; /* rnd-counter */ - MI_DECODE_TREE *decode_trees; - uint16 *decode_tables; - enum data_file_type data_file_type; - int (*read_record)(struct st_myisam_info*, my_off_t, byte*); - int (*write_record)(struct st_myisam_info*, const byte*); - int (*update_record)(struct st_myisam_info*, my_off_t, const byte*); - int (*delete_record)(struct st_myisam_info*); - int (*read_rnd)(struct st_myisam_info*, byte*, my_off_t, my_bool); - int (*compare_record)(struct st_myisam_info*, const byte *); - ha_checksum (*calc_checksum)(struct st_myisam_info*, const byte *); - int (*compare_unique)(struct st_myisam_info*, MI_UNIQUEDEF *, - const byte *record, my_off_t pos); #ifdef THREAD THR_LOCK lock; pthread_mutex_t intern_lock; /* Locking for use with _locking */ @@ -215,16 +216,22 @@ typedef struct st_mi_bit_buff { /* Used for packing of record */ uint error; } MI_BIT_BUFF; - struct st_myisam_info { MYISAM_SHARE *s; /* Shared between open:s */ MI_STATUS_INFO *state,save_state; MI_BLOB *blobs; /* Pointer to blobs */ - int dfile; /* The datafile */ - MI_BIT_BUFF bit_buff; - uint opt_flag; /* Optim. for space/speed */ - uint update; /* If file changed since open */ + MI_BIT_BUFF bit_buff; + /* accumulate indexfile changes between write's */ + TREE *bulk_insert; char *filename; /* parameter to open filename */ + uchar *buff, /* Temp area for key */ + *lastkey,*lastkey2; /* Last used search key */ + byte *rec_buff, /* Tempbuff for recordpack */ + *rec_alloc; /* Malloced area for record */ + uchar *int_keypos, /* Save position for next/previous */ + *int_maxpos; /* -""- */ + int (*read_record)(struct st_myisam_info*, my_off_t, byte*); + invalidator_by_filename invalidator; /* query cache invalidator */ ulong this_unique; /* uniq filenumber or thread */ ulong last_unique; /* last unique number */ ulong this_loop; /* counter for this open */ @@ -233,20 +240,16 @@ struct st_myisam_info { nextpos; /* Position to next record */ my_off_t save_lastpos; my_off_t pos; /* Intern variable */ + my_off_t last_keypage; /* Last key page read */ + my_off_t last_search_keypage; /* Last keypage when searching */ + my_off_t dupp_key_pos; ha_checksum checksum; ulong packed_length,blob_length; /* Length of found, packed record */ + int dfile; /* The datafile */ + uint opt_flag; /* Optim. for space/speed */ + uint update; /* If file changed since open */ uint alloced_rec_buff_length; /* Max recordlength malloced */ - uchar *buff, /* Temp area for key */ - *lastkey,*lastkey2; /* Last used search key */ - byte *rec_buff, /* Tempbuff for recordpack */ - *rec_alloc; /* Malloced area for record */ - uchar *int_keypos, /* Save position for next/previous */ - *int_maxpos; /* -""- */ - uint32 int_keytree_version; /* -""- */ uint int_nod_flag; /* -""- */ - my_off_t last_keypage; /* Last key page read */ - my_off_t last_search_keypage; /* Last keypage when searching */ - my_off_t dupp_key_pos; int lastinx; /* Last used index */ uint lastkey_length; /* Length of key in lastkey */ uint last_rkey_length; /* Last length in mi_rkey() */ @@ -257,16 +260,15 @@ struct st_myisam_info { uint data_changed; /* Somebody has changed data */ uint save_update; /* When using KEY_READ */ int save_lastinx; + uint32 int_keytree_version; /* -""- */ + LIST open_list; + IO_CACHE rec_cache; /* When cacheing records */ + myf lock_wait; /* is 0 or MY_DONT_WAIT */ my_bool was_locked; /* Was locked in panic */ my_bool quick_mode; my_bool page_changed; /* If info->buff can't be used for rnext */ my_bool buff_used; /* If info->buff has to be reread for rnext */ my_bool use_packed_key; /* For MYISAMMRG */ - TREE *bulk_insert; /* accumulate indexfile changes between mi_write's */ - myf lock_wait; /* is 0 or MY_DONT_WAIT */ - int (*read_record)(struct st_myisam_info*, my_off_t, byte*); - LIST open_list; - IO_CACHE rec_cache; /* When cacheing records */ #ifdef THREAD THR_LOCK_DATA lock; #endif diff --git a/myisammrg/myrg_extra.c b/myisammrg/myrg_extra.c index 315abe0f29e..5434d30a50e 100644 --- a/myisammrg/myrg_extra.c +++ b/myisammrg/myrg_extra.c @@ -46,3 +46,14 @@ int myrg_extra(MYRG_INFO *info,enum ha_extra_function function) } DBUG_RETURN(save_error); } + +void myrg_extrafunc(MYRG_INFO *info, invalidator_by_filename inv) +{ + MYRG_TABLE *file; + DBUG_ENTER("myrg_extrafunc"); + + for (file=info->open_tables ; file != info->end_table ; file++) + file->table->s->invalidator = inv; + + DBUG_VOID_RETURN; +} diff --git a/mysql-test/r/flush.result b/mysql-test/r/flush.result index 1fb4fc05136..99d212ee49c 100644 --- a/mysql-test/r/flush.result +++ b/mysql-test/r/flush.result @@ -28,3 +28,8 @@ select * from t1; n 345 drop table t1; +flush query cache; +reset query cache; +show status like "Qcache_queries_in_cache"; +Variable_name Value +Qcache_queries_in_cache 0 diff --git a/mysql-test/r/gcc296.result b/mysql-test/r/gcc296.result new file mode 100644 index 00000000000..7184bfb9cdc --- /dev/null +++ b/mysql-test/r/gcc296.result @@ -0,0 +1,5 @@ +kodoboru obor aobor +0101000000 aaa AAA +0102000000 bbb BBB +0103000000 ccc CCC +0104000000 xxx XXX diff --git a/mysql-test/r/query_cache.result b/mysql-test/r/query_cache.result new file mode 100644 index 00000000000..ac6962bfd9d --- /dev/null +++ b/mysql-test/r/query_cache.result @@ -0,0 +1,38 @@ +reset query cache; +flush status; +drop table if exists t1; +create table t1 (a int not null); +insert into t1 values (1),(2),(3); +select * from t1; +a +1 +2 +3 +select * from t1; +a +1 +2 +3 +select sql_no_cache * from t1; +a +1 +2 +3 +select length(now()) from t1; +length(now()) +19 +19 +19 +show status like "Qcache_queries_in_cache"; +Variable_name Value +Qcache_queries_in_cache 1 +show status like "Qcache_inserts"; +Variable_name Value +Qcache_inserts 1 +show status like "Qcache_hits"; +Variable_name Value +Qcache_hits 1 +drop table t1; +show status like "Qcache_queries_in_cache"; +Variable_name Value +Qcache_queries_in_cache 0 diff --git a/mysql-test/t/flush.test b/mysql-test/t/flush.test index fe219471041..6a09b903873 100644 --- a/mysql-test/t/flush.test +++ b/mysql-test/t/flush.test @@ -66,3 +66,12 @@ connection con2; insert into t1 values (345); select * from t1; drop table t1; + +# +# Test that QUERY CACHE commands doesn't core dump. +# (Normally we don't have a cache active at this point) +# + +flush query cache; +reset query cache; +show status like "Qcache_queries_in_cache"; diff --git a/mysql-test/t/gcc296.test b/mysql-test/t/gcc296.test new file mode 100644 index 00000000000..7c72b57ca54 --- /dev/null +++ b/mysql-test/t/gcc296.test @@ -0,0 +1,17 @@ +#try to crash gcc 2.96 +drop table if exists obory; +CREATE TABLE obory ( + kodoboru varchar(10) default NULL, + obor tinytext, + aobor tinytext, + UNIQUE INDEX kodoboru (kodoboru), + FULLTEXT KEY obor (obor), + FULLTEXT KEY aobor (aobor) +); +INSERT INTO obory VALUES ('0101000000','aaa','AAA'); +INSERT INTO obory VALUES ('0102000000','bbb','BBB'); +INSERT INTO obory VALUES ('0103000000','ccc','CCC'); +INSERT INTO obory VALUES ('0104000000','xxx','XXX'); + +select * from obory; +drop table obory; diff --git a/mysql-test/t/innodb.test b/mysql-test/t/innodb.test index 6bac7bb8059..4d0930c50c6 100644 --- a/mysql-test/t/innodb.test +++ b/mysql-test/t/innodb.test @@ -547,3 +547,19 @@ delete from t1; select * from t1; commit; drop table t1; + +# +# Test of how ORDER BY works when doing it on the whole table +# + +create table t1 (a int not null, b int not null, c int not null, primary key (a),key(b)) type=innodb; +insert into t1 values (3,3,3),(1,1,1),(2,2,2),(4,4,4); +explain select * from t1 order by a; +explain select * from t1 order by b; +explain select * from t1 order by c; +explain select a from t1 order by a; +explain select b from t1 order by b; +explain select a,b from t1 order by b; +explain select a,b from t1; +explain select a,b,c from t1; +drop table t1; diff --git a/mysql-test/t/join_outer.test b/mysql-test/t/join_outer.test index 774f35ae38e..af5f377afb5 100644 --- a/mysql-test/t/join_outer.test +++ b/mysql-test/t/join_outer.test @@ -404,3 +404,15 @@ insert into t2 values (1,1),(1,2); insert into t1 values (1,1),(2,1); SELECT * FROM t1 LEFT JOIN t2 ON (t1.bug_id = t2.bug_id AND t2.who = 2) WHERE (t1.reporter = 2 OR t2.who = 2); drop table t1,t2; + +# +# Test problem with LEFT JOIN + +create table t1 (fooID smallint unsigned auto_increment, primary key (fooID)); +create table t2 (fooID smallint unsigned not null, barID smallint unsigned not null, primary key (fooID,barID)); +insert into t1 (fooID) values (10),(20),(30); +insert into t2 values (10,1),(20,2),(30,3); +explain select * from t2 left join t1 on t1.fooID = t2.fooID and t1.fooID = 30; +select * from t2 left join t1 on t1.fooID = t2.fooID and t1.fooID = 30; +select * from t2 left join t1 ignore index(primary) on t1.fooID = t2.fooID and t1.fooID = 30; +drop table t1,t2; diff --git a/mysql-test/t/myisam.test b/mysql-test/t/myisam.test index 93462534b43..861bc807323 100644 --- a/mysql-test/t/myisam.test +++ b/mysql-test/t/myisam.test @@ -50,3 +50,19 @@ show index from t1; optimize table t1; show index from t1; drop table t1; + +# +# Test of how ORDER BY works when doing it on the whole table +# + +create table t1 (a int not null, b int not null, c int not null, primary key (a),key(b)) type=myisam; +insert into t1 values (3,3,3),(1,1,1),(2,2,2),(4,4,4); +explain select * from t1 order by a; +explain select * from t1 order by b; +explain select * from t1 order by c; +explain select a from t1 order by a; +explain select b from t1 order by b; +explain select a,b from t1 order by b; +explain select a,b from t1; +explain select a,b,c from t1; +drop table t1; diff --git a/mysql-test/t/query_cache-master.opt b/mysql-test/t/query_cache-master.opt new file mode 100644 index 00000000000..7ff962c2216 --- /dev/null +++ b/mysql-test/t/query_cache-master.opt @@ -0,0 +1 @@ +--set-variable=query_cache_size=2M diff --git a/mysql-test/t/query_cache.test b/mysql-test/t/query_cache.test new file mode 100644 index 00000000000..3d1aa70d8b2 --- /dev/null +++ b/mysql-test/t/query_cache.test @@ -0,0 +1,26 @@ +# +# Tests with query cache +# + +# Reset query cache variables. + +reset query cache; +flush status; +drop table if exists t1; +create table t1 (a int not null); +insert into t1 values (1),(2),(3); +select * from t1; +select * from t1; +select sql_no_cache * from t1; +select length(now()) from t1; + +# Only check the variables that are independent of the machine and startup +# options + +show status like "Qcache_queries_in_cache"; +show status like "Qcache_inserts"; +show status like "Qcache_hits"; + +drop table t1; + +show status like "Qcache_queries_in_cache"; diff --git a/mysys/hash.c b/mysys/hash.c index acb5a9b4310..7129b62ba89 100644 --- a/mysys/hash.c +++ b/mysys/hash.c @@ -583,7 +583,8 @@ byte *hash_element(HASH *hash,uint idx) void hash_replace(HASH *hash, uint idx, byte *new_row) { - dynamic_element(&hash->array,idx,HASH_LINK*)->data=new_row; + if (idx != NO_RECORD) /* Safety */ + dynamic_element(&hash->array,idx,HASH_LINK*)->data=new_row; } diff --git a/mysys/mf_keycache.c b/mysys/mf_keycache.c index 87f6ca2717c..330eeeb6f6f 100644 --- a/mysys/mf_keycache.c +++ b/mysys/mf_keycache.c @@ -252,6 +252,9 @@ byte *key_cache_read(File file, my_off_t filepos, byte *buff, uint length, { reg1 SEC_LINK *next; int error=0; + DBUG_ENTER("key_cache_read"); + DBUG_PRINT("enter", ("file %u, filepos %lu, length %u", + (uint) file, (ulong) filepos, length)); #ifndef THREAD if (block_length > key_cache_block_size) @@ -270,14 +273,14 @@ byte *key_cache_read(File file, my_off_t filepos, byte *buff, uint length, if (!(next=find_key_block(file,filepos,&error))) { pthread_mutex_unlock(&THR_LOCK_keycache); - return (byte*) 0; /* Got a fatal error */ + DBUG_RETURN ((byte*) 0); /* Got a fatal error */ } if (error) { /* Didn't find it in cache */ if (my_pread(file,next->buffer,read_length,filepos,MYF(MY_NABP))) { pthread_mutex_unlock(&THR_LOCK_keycache); - return((byte*) 0); + DBUG_RETURN((byte*) 0); } _my_cache_read++; } @@ -285,7 +288,7 @@ byte *key_cache_read(File file, my_off_t filepos, byte *buff, uint length, if (return_buffer) { pthread_mutex_unlock(&THR_LOCK_keycache); - return (next->buffer); + DBUG_RETURN (next->buffer); } #endif if (! (read_length & 511)) @@ -296,13 +299,13 @@ byte *key_cache_read(File file, my_off_t filepos, byte *buff, uint length, filepos+=read_length; } while ((length-= read_length)); pthread_mutex_unlock(&THR_LOCK_keycache); - return(start); + DBUG_RETURN(start); } _my_cache_r_requests++; _my_cache_read++; if (my_pread(file,(byte*) buff,length,filepos,MYF(MY_NABP))) error=1; - return (error ? (byte*) 0 : buff); + DBUG_RETURN(error ? (byte*) 0 : buff); } /* key_cache_read */ @@ -316,12 +319,15 @@ int key_cache_write(File file, my_off_t filepos, byte *buff, uint length, { reg1 SEC_LINK *next; int error=0; + DBUG_ENTER("key_cache_write"); + DBUG_PRINT("enter", ("file %u, filepos %lu, length %u", + (uint) file, (ulong) filepos, length)); if (!dont_write) { /* Forced write of buffer */ _my_cache_write++; if (my_pwrite(file,buff,length,filepos,MYF(MY_NABP | MY_WAIT_IF_FULL))) - return(1); + DBUG_RETURN(1); } #if !defined(DBUG_OFF) && defined(EXTRA_DEBUG) @@ -367,7 +373,7 @@ end: #if !defined(DBUG_OFF) && defined(EXTRA_DEBUG) DBUG_EXECUTE("check_keycache",test_key_cache("end of key_cache_write",1);); #endif - return(error); + DBUG_RETURN(error); } /* key_cache_write */ @@ -377,6 +383,9 @@ end: static SEC_LINK *find_key_block(int file, my_off_t filepos, int *error) { reg1 SEC_LINK *next,**start; + DBUG_ENTER("find_key_block"); + DBUG_PRINT("enter", ("file %u, filepos %lu", + (uint) file, (ulong) filepos)); #if !defined(DBUG_OFF) && defined(EXTRA_DEBUG) DBUG_EXECUTE("check_keycache2",test_key_cache("start of find_key_block",0);); @@ -461,7 +470,7 @@ static SEC_LINK *find_key_block(int file, my_off_t filepos, int *error) #if !defined(DBUG_OFF) && defined(EXTRA_DEBUG) DBUG_EXECUTE("check_keycache2",test_key_cache("end of find_key_block",0);); #endif - return next; + DBUG_RETURN(next); } /* find_key_block */ diff --git a/regex/cclass.h b/regex/cclass.h index 7d9a7ccc7c2..e0f752f38b8 100644 --- a/regex/cclass.h +++ b/regex/cclass.h @@ -15,7 +15,7 @@ #define CCLASS_LAST 12 extern struct cclass { - char *name; - char *chars; - char *multis; + const char *name; + const char *chars; + const char *multis; } cclasses[]; diff --git a/regex/cname.h b/regex/cname.h index 2b6a1a8496f..06865f3e102 100644 --- a/regex/cname.h +++ b/regex/cname.h @@ -1,7 +1,7 @@ /* character-name table */ static struct cname { - char *name; - char code; + const char *name; + const char code; } cnames[] = { {"NUL", '\0'}, {"SOH", '\001'}, diff --git a/regex/main.c b/regex/main.c index eac7293bf45..7844a4d8384 100644 --- a/regex/main.c +++ b/regex/main.c @@ -133,9 +133,9 @@ FILE *in; int i; char erbuf[100]; size_t ne; - char *badpat = "invalid regular expression"; + const char *badpat = "invalid regular expression"; # define SHORT 10 - char *bpname = "REG_BADPAT"; + const char *bpname = "REG_BADPAT"; regex_t re; while (fgets(inbuf, sizeof(inbuf), in) != NULL) { @@ -152,7 +152,7 @@ FILE *in; } for (i = 0; i < nf; i++) if (strcmp(f[i], "\"\"") == 0) - f[i] = ""; + f[i] = (char*) ""; if (nf <= 3) f[3] = NULL; if (nf <= 4) @@ -217,7 +217,7 @@ int opts; /* may not match f1 */ char erbuf[100]; int err; int len; - char *type = (opts & REG_EXTENDED) ? "ERE" : "BRE"; + const char *type = (opts & REG_EXTENDED) ? "ERE" : "BRE"; register int i; char *grump; char f0copy[1000]; @@ -291,7 +291,7 @@ int opts; /* may not match f1 */ nshould = split(f4, should+1, NSHOULD-1, ","); if (nshould == 0) { nshould = 1; - should[1] = ""; + should[1] = (char*) ""; } for (i = 1; i < NSUBS; i++) { grump = check(f2, subs[i], should[i]); @@ -317,7 +317,7 @@ char *s; { register char *p; register int o = (type == 'c') ? copts : eopts; - register char *legal = (type == 'c') ? "bisnmp" : "^$#tl"; + register const char *legal = (type == 'c') ? "bisnmp" : "^$#tl"; for (p = s; *p != '\0'; p++) if (strchr(legal, *p) != NULL) @@ -417,7 +417,7 @@ char *should; should = NULL; if (should != NULL && should[0] == '@') { at = should + 1; - should = ""; + should = (char*) ""; } /* check rm_so and rm_eo for consistency */ @@ -434,7 +434,7 @@ char *should; if (sub.rm_so == -1 && should == NULL) return(NULL); if (sub.rm_so == -1) - return("did not match"); + return((char*) "did not match"); /* check for in range */ if ((int) sub.rm_eo > (int) strlen(str)) { diff --git a/regex/regcomp.c b/regex/regcomp.c index 688b8ce7464..6f8221a706d 100644 --- a/regex/regcomp.c +++ b/regex/regcomp.c @@ -219,7 +219,7 @@ int stop; /* character this ERE should end at */ conc = HERE(); while (MORE() && (c = PEEK()) != '|' && c != stop) p_ere_exp(p); - if(REQUIRE(HERE() != conc, REG_EMPTY)); /* require nonempty */ + if(REQUIRE(HERE() != conc, REG_EMPTY)) {}/* require nonempty */ if (!EAT('|')) break; /* NOTE BREAK OUT */ @@ -266,11 +266,11 @@ register struct parse *p; pos = HERE(); switch (c) { case '(': - if(REQUIRE(MORE(), REG_EPAREN)); + if(REQUIRE(MORE(), REG_EPAREN)) {} p->g->nsub++; subno = (sopno) p->g->nsub; if (subno < NPAREN) - p->pbegin[subno] = HERE(); + p->pbegin[subno] = HERE(); EMIT(OLPAREN, subno); if (!SEE(')')) p_ere(p, ')'); @@ -279,7 +279,7 @@ register struct parse *p; assert(p->pend[subno] != 0); } EMIT(ORPAREN, subno); - if(MUSTEAT(')', REG_EPAREN)); + if(MUSTEAT(')', REG_EPAREN)) {} break; #ifndef POSIX_MISTAKE case ')': /* happens only if no current unmatched ( */ @@ -322,12 +322,12 @@ register struct parse *p; p_bracket(p); break; case '\\': - if(REQUIRE(MORE(), REG_EESCAPE)); + if(REQUIRE(MORE(), REG_EESCAPE)) {} c = GETNEXT(); ordinary(p, c); break; case '{': /* okay as ordinary except if digit follows */ - if(REQUIRE(!MORE() || !isdigit(PEEK()), REG_BADRPT)); + if(REQUIRE(!MORE() || !isdigit(PEEK()), REG_BADRPT)) {} /* FALLTHROUGH */ default: ordinary(p, c); @@ -343,7 +343,7 @@ register struct parse *p; return; /* no repetition, we're done */ NEXT(); - if(REQUIRE(!wascaret, REG_BADRPT)); + if(REQUIRE(!wascaret, REG_BADRPT)) {} switch (c) { case '*': /* implemented as +? */ /* this case does not require the (y|) trick, noKLUDGE */ @@ -370,7 +370,7 @@ register struct parse *p; if (EAT(',')) { if (isdigit(PEEK())) { count2 = p_count(p); - if(REQUIRE(count <= count2, REG_BADBR)); + if(REQUIRE(count <= count2, REG_BADBR)) {} } else /* single number with comma */ count2 = RE_INFINITY; } else /* just a single number */ @@ -379,7 +379,7 @@ register struct parse *p; if (!EAT('}')) { /* error heuristics */ while (MORE() && PEEK() != '}') NEXT(); - if(REQUIRE(MORE(), REG_EBRACE)); + if(REQUIRE(MORE(), REG_EBRACE)) {} SETERROR(REG_BADBR); } break; @@ -402,7 +402,7 @@ static void p_str(p) register struct parse *p; { - if(REQUIRE(MORE(), REG_EMPTY)); + if(REQUIRE(MORE(), REG_EMPTY)) {} while (MORE()) ordinary(p, GETNEXT()); } @@ -445,7 +445,7 @@ register int end2; /* second terminating character */ p->g->neol++; } - if(REQUIRE(HERE() != start, REG_EMPTY)); /* require nonempty */ + if(REQUIRE(HERE() != start, REG_EMPTY)) {} /* require nonempty */ } /* @@ -470,7 +470,7 @@ int starordinary; /* is a leading * an ordinary character? */ assert(MORE()); /* caller should have ensured this */ c = GETNEXT(); if (c == '\\') { - if(REQUIRE(MORE(), REG_EESCAPE)); + if(REQUIRE(MORE(), REG_EESCAPE)) {} c = BACKSL | (unsigned char)GETNEXT(); } switch (c) { @@ -500,7 +500,7 @@ int starordinary; /* is a leading * an ordinary character? */ assert(p->pend[subno] != 0); } EMIT(ORPAREN, subno); - if(REQUIRE(EATTWO('\\', ')'), REG_EPAREN)); + if(REQUIRE(EATTWO('\\', ')'), REG_EPAREN)) {} break; case BACKSL|')': /* should not get here -- must be user */ case BACKSL|'}': @@ -530,7 +530,7 @@ int starordinary; /* is a leading * an ordinary character? */ p->g->backrefs = 1; break; case '*': - if(REQUIRE(starordinary, REG_BADRPT)); + if(REQUIRE(starordinary, REG_BADRPT)) {} /* FALLTHROUGH */ default: ordinary(p, c &~ BACKSL); @@ -548,7 +548,7 @@ int starordinary; /* is a leading * an ordinary character? */ if (EAT(',')) { if (MORE() && isdigit(PEEK())) { count2 = p_count(p); - if(REQUIRE(count <= count2, REG_BADBR)); + if(REQUIRE(count <= count2, REG_BADBR)) {} } else /* single number with comma */ count2 = RE_INFINITY; } else /* just a single number */ @@ -557,7 +557,7 @@ int starordinary; /* is a leading * an ordinary character? */ if (!EATTWO('\\', '}')) { /* error heuristics */ while (MORE() && !SEETWO('\\', '}')) NEXT(); - if(REQUIRE(MORE(), REG_EBRACE)); + if(REQUIRE(MORE(), REG_EBRACE)) {} SETERROR(REG_BADBR); } } else if (c == (unsigned char)'$') /* $ (but not \$) ends it */ @@ -582,7 +582,7 @@ register struct parse *p; ndigits++; } - if(REQUIRE(ndigits > 0 && count <= DUPMAX, REG_BADBR)); + if(REQUIRE(ndigits > 0 && count <= DUPMAX, REG_BADBR)) {} return(count); } @@ -622,7 +622,7 @@ register struct parse *p; p_b_term(p, cs); if (EAT('-')) CHadd(cs, '-'); - if(MUSTEAT(']', REG_EBRACK)); + if(MUSTEAT(']', REG_EBRACK)) {} if (p->error != 0) /* don't mess things up further */ return; @@ -693,21 +693,21 @@ register cset *cs; switch (c) { case ':': /* character class */ NEXT2(); - if(REQUIRE(MORE(), REG_EBRACK)); + if(REQUIRE(MORE(), REG_EBRACK)) {} c = PEEK(); - if(REQUIRE(c != '-' && c != ']', REG_ECTYPE)); + if(REQUIRE(c != '-' && c != ']', REG_ECTYPE)) {} p_b_cclass(p, cs); - if(REQUIRE(MORE(), REG_EBRACK)); - if(REQUIRE(EATTWO(':', ']'), REG_ECTYPE)); + if(REQUIRE(MORE(), REG_EBRACK)) {} + if(REQUIRE(EATTWO(':', ']'), REG_ECTYPE)) {} break; case '=': /* equivalence class */ NEXT2(); - if(REQUIRE(MORE(), REG_EBRACK)); + if(REQUIRE(MORE(), REG_EBRACK)) {} c = PEEK(); - if(REQUIRE(c != '-' && c != ']', REG_ECOLLATE)); + if(REQUIRE(c != '-' && c != ']', REG_ECOLLATE)) {} p_b_eclass(p, cs); - if(REQUIRE(MORE(), REG_EBRACK)); - if(REQUIRE(EATTWO('=', ']'), REG_ECOLLATE)); + if(REQUIRE(MORE(), REG_EBRACK)) {} + if(REQUIRE(EATTWO('=', ']'), REG_ECOLLATE)) {} break; default: /* symbol, ordinary character, or range */ /* xxx revision needed for multichar stuff */ @@ -722,7 +722,7 @@ register cset *cs; } else finish = start; /* xxx what about signed chars here... */ - if(REQUIRE(start <= finish, REG_ERANGE)); + if(REQUIRE(start <= finish, REG_ERANGE)) {} for (i = start; i <= finish; i++) CHadd(cs, i); break; @@ -756,10 +756,10 @@ register cset *cs; return; } - u = cp->chars; + u = (char*) cp->chars; while ((c = *u++) != '\0') CHadd(cs, c); - for (u = cp->multis; *u != '\0'; u += strlen(u) + 1) + for (u = (char*) cp->multis; *u != '\0'; u += strlen(u) + 1) MCadd(p, cs, u); } @@ -790,13 +790,13 @@ register struct parse *p; { register char value; - if(REQUIRE(MORE(), REG_EBRACK)); + if(REQUIRE(MORE(), REG_EBRACK)) {} if (!EATTWO('[', '.')) return(GETNEXT()); /* collating symbol */ value = p_b_coll_elem(p, '.'); - if(REQUIRE(EATTWO('.', ']'), REG_ECOLLATE)); + if(REQUIRE(EATTWO('.', ']'), REG_ECOLLATE)) {} return(value); } @@ -1189,6 +1189,7 @@ register char *cp; cs->multis[cs->smultis - 1] = '\0'; } +#ifdef NOT_USED /* - mcsub - subtract a collating element from a cset == static void mcsub(register cset *cs, register char *cp); @@ -1246,6 +1247,7 @@ register char *cp; return(p); return(NULL); } +#endif /* - mcinvert - invert the list of collating elements in a cset @@ -1256,8 +1258,8 @@ register char *cp; */ static void mcinvert(p, cs) -register struct parse *p; -register cset *cs; + register struct parse *p __attribute__((unused)); + register cset *cs __attribute__((unused)); { assert(cs->multis == NULL); /* xxx */ } @@ -1271,8 +1273,8 @@ register cset *cs; */ static void mccase(p, cs) -register struct parse *p; -register cset *cs; +register struct parse *p __attribute__((unused)); +register cset *cs __attribute__((unused)); { assert(cs->multis == NULL); /* xxx */ } diff --git a/regex/regcomp.ih b/regex/regcomp.ih index 0776e7185cd..4ae45bbf4a9 100644 --- a/regex/regcomp.ih +++ b/regex/regcomp.ih @@ -28,9 +28,11 @@ static int freezeset(register struct parse *p, register cset *cs); static int firstch(register struct parse *p, register cset *cs); static int nch(register struct parse *p, register cset *cs); static void mcadd(register struct parse *p, register cset *cs, register char *cp); +#ifdef NOT_USED static void mcsub(register cset *cs, register char *cp); static int mcin(register cset *cs, register char *cp); static char *mcfind(register cset *cs, register char *cp); +#endif static void mcinvert(register struct parse *p, register cset *cs); static void mccase(register struct parse *p, register cset *cs); static int isinsets(register struct re_guts *g, int c); diff --git a/regex/regerror.c b/regex/regerror.c index 4a28f6e9d24..0a7b7c8da2c 100644 --- a/regex/regerror.c +++ b/regex/regerror.c @@ -28,8 +28,8 @@ */ static struct rerr { int code; - char *name; - char *explain; + const char *name; + const char *explain; } rerrs[] = { {REG_NOMATCH, "REG_NOMATCH", "regexec() failed to match"}, {REG_BADPAT, "REG_BADPAT", "invalid regular expression"}, @@ -83,7 +83,7 @@ size_t errbuf_size; assert(strlen(convbuf) < sizeof(convbuf)); s = convbuf; } else - s = r->explain; + s = (char*) r->explain; } len = strlen(s) + 1; @@ -113,7 +113,7 @@ char *localbuf; if (strcmp(r->name, preg->re_endp) == 0) break; if (r->code == 0) - return("0"); + return((char*) "0"); sprintf(localbuf, "%d", r->code); return(localbuf); diff --git a/regex/reginit.c b/regex/reginit.c index 06a48b16243..18647c386fc 100644 --- a/regex/reginit.c +++ b/regex/reginit.c @@ -63,7 +63,7 @@ void regex_end() { int i; for (i=0; i < CCLASS_LAST ; i++) - free(cclasses[i].chars); + free((char*) cclasses[i].chars); regex_inited=0; } } diff --git a/regex/split.c b/regex/split.c index 188bdb775b9..bd2a53c01e3 100644 --- a/regex/split.c +++ b/regex/split.c @@ -27,7 +27,7 @@ char *sep; /* "" white, "c" single char, "ab" [ab]+ */ continue; p--; trimtrail = 1; - sep = " \t"; /* note, code below knows this is 2 long */ + sep = (char*) " \t"; /* note, code below knows this is 2 long */ sepc = ' '; } else trimtrail = 0; diff --git a/scripts/mysql_config.sh b/scripts/mysql_config.sh index b2a09173760..31e3ed42e0a 100644 --- a/scripts/mysql_config.sh +++ b/scripts/mysql_config.sh @@ -18,11 +18,62 @@ # This script reports various configuration settings that may be needed # when using the MySQL client library. +which () +{ + IFS="${IFS= }"; save_ifs="$IFS"; IFS=':' + for file + do + for dir in $PATH + do + if test -f $dir/$file + then + echo "$dir/$file" + continue 2 + fi + done + echo "which: no $file in ($PATH)" + exit 1 + done + IFS="$save_ifs" +} + +# +# If we can find the given directory relatively to where mysql_config is +# we should use this instead of the incompiled one. +# This is to ensure that this script also works with the binary MySQL +# version + +fix_path () +{ + var=$1 + shift + for filename + do + path=$basedir/$filename + if [ -d "$path" ] ; + then + eval "$var"=$path + return + fi + done +} + +abs_path=`expr \( substr $0 1 1 \) = '/'` +if [ "x$abs_path" = "x1" ] ; then + me=$0 +else + me=`which $0` +fi + +basedir=`echo $me | sed -e 's;/bin/mysql_config;;'` + ldata='@localstatedir@' execdir='@libexecdir@' bindir='@bindir@' pkglibdir='@pkglibdir@' +fix_path pkglibdir lib/mysql lib pkgincludedir='@pkgincludedir@' +fix_path pkgincludedir include/mysql include version='@VERSION@' socket='@MYSQL_UNIX_ADDR@' port='@MYSQL_TCP_PORT@' diff --git a/scripts/mysqlhotcopy.sh b/scripts/mysqlhotcopy.sh index 655e5a2a52b..caf2615fe7a 100644 --- a/scripts/mysqlhotcopy.sh +++ b/scripts/mysqlhotcopy.sh @@ -37,7 +37,7 @@ WARNING: THIS PROGRAM IS STILL IN BETA. Comments/patches welcome. # Documentation continued at end of file -my $VERSION = "1.13"; +my $VERSION = "1.14"; my $opt_tmpdir = $ENV{TMPDIR} || "/tmp"; @@ -120,6 +120,7 @@ GetOptions( \%opt, # 'target' - destination directory of the copy # 'tables' - array-ref to list of tables in the db # 'files' - array-ref to list of files to be copied +# (RAID files look like 'nn/name.MYD') # 'index' - array-ref to list of indexes to be copied # @@ -265,11 +266,23 @@ foreach my $rdb ( @db_desc ) { or die "Cannot open dir '$db_dir': $!"; my %db_files; - map { ( /(.+)\.\w+$/ ? ( $db_files{$_} = $1 ) : () ) } readdir(DBDIR); + my @raid_dir = (); + + while ( defined( my $name = readdir DBDIR ) ) { + if ( $name =~ /^\d\d$/ && -d "$db_dir/$name" ) { + push @raid_dir, $name; + } + else { + $db_files{$name} = $1 if ( $name =~ /(.+)\.\w+$/ ); + } + } + closedir( DBDIR ); + + scan_raid_dir( \%db_files, $db_dir, @raid_dir ); + unless( keys %db_files ) { warn "'$db' is an empty database\n"; } - closedir( DBDIR ); ## filter (out) files specified in t_regex my @db_files; @@ -297,6 +310,8 @@ foreach my $rdb ( @db_desc ) { my @hc_tables = map { "$db.$_" } @dbh_tables; $rdb->{tables} = [ @hc_tables ]; + $rdb->{raid_dirs} = [ get_raid_dirs( $rdb->{files} ) ]; + $hc_locks .= ", " if ( length $hc_locks && @hc_tables ); $hc_locks .= join ", ", map { "$_ READ" } @hc_tables; $hc_tables .= ", " if ( length $hc_tables && @hc_tables ); @@ -370,17 +385,20 @@ if ($opt{method} =~ /^cp\b/) retire_directory( @existing ) if ( @existing ); foreach my $rdb ( @db_desc ) { - my $tgt_dirpath = $rdb->{target}; - if ( $opt{dryrun} ) { - print "mkdir $tgt_dirpath, 0750\n"; - } - elsif ($opt{method} =~ /^scp\b/) { - ## assume it's there? - ## ... - } - else { - mkdir($tgt_dirpath, 0750) - or die "Can't create '$tgt_dirpath': $!\n"; + foreach my $td ( '', @{$rdb->{raid_dirs}} ) { + + my $tgt_dirpath = "$rdb->{target}/$td"; + if ( $opt{dryrun} ) { + print "mkdir $tgt_dirpath, 0750\n"; + } + elsif ($opt{method} =~ /^scp\b/) { + ## assume it's there? + ## ... + } + else { + mkdir($tgt_dirpath, 0750) + or die "Can't create '$tgt_dirpath': $!\n"; + } } } @@ -438,7 +456,7 @@ foreach my $rdb ( @db_desc ) my @files = map { "$datadir/$rdb->{src}/$_" } @{$rdb->{files}}; next unless @files; - eval { copy_files($opt{method}, \@files, $rdb->{target} ); }; + eval { copy_files($opt{method}, \@files, $rdb->{target}, $rdb->{raid_dirs} ); }; push @failed, "$rdb->{src} -> $rdb->{target} failed: $@" if ( $@ ); @@ -531,27 +549,33 @@ exit 0; # --- sub copy_files { - my ($method, $files, $target) = @_; + my ($method, $files, $target, $raid_dirs) = @_; my @cmd; print "Copying ".@$files." files...\n" unless $opt{quiet}; if ($method =~ /^s?cp\b/) { # cp or scp with optional flags - @cmd = ($method); + my @cp = ($method); # add option to preserve mod time etc of copied files # not critical, but nice to have - push @cmd, "-p" if $^O =~ m/^(solaris|linux|freebsd)$/; + push @cp, "-p" if $^O =~ m/^(solaris|linux|freebsd)$/; # add recursive option for scp - push @cmd, "-r" if $^O =~ /m^(solaris|linux|freebsd)$/ && $method =~ /^scp\b/; + push @cp, "-r" if $^O =~ /m^(solaris|linux|freebsd)$/ && $method =~ /^scp\b/; + + my @non_raid = grep { $_ !~ m:\d\d/: } @$files; # add files to copy and the destination directory - push @cmd, @$files, $target; + safe_system( @cp, @non_raid, $target ); + + foreach my $rd ( @$raid_dirs ) { + my @raid = grep { m:$rd/: } @$files; + safe_system( @cp, @raid, "$target/$rd" ) if ( @raid ); + } } else { die "Can't use unsupported method '$method'\n"; } - safe_system (@cmd); } # @@ -682,6 +706,35 @@ sub get_row { return $sth->fetchrow_array(); } +sub scan_raid_dir { + my ( $r_db_files, $data_dir, @raid_dir ) = @_; + + local(*RAID_DIR); + + foreach my $rd ( @raid_dir ) { + + opendir(RAID_DIR, "$data_dir/$rd" ) + or die "Cannot open dir '$data_dir/$rd': $!"; + + while ( defined( my $name = readdir RAID_DIR ) ) { + $r_db_files->{"$rd/$name"} = $1 if ( $name =~ /(.+)\.\w+$/ ); + } + closedir( RAID_DIR ); + } +} + +sub get_raid_dirs { + my ( $r_files ) = @_; + + my %dirs = (); + foreach my $f ( @$r_files ) { + if ( $f =~ m:^(\d\d)/: ) { + $dirs{$1} = 1; + } + } + return sort keys %dirs; +} + __END__ =head1 DESCRIPTION @@ -905,6 +958,7 @@ Tim Bunce Martin Waite - added checkpoint, flushlog, regexp and dryrun options Fixed cleanup of targets when hotcopy fails. Added --record_log_pos. + RAID tables are now copied (don't know if this works over scp). Ralph Corderoy - added synonyms for commands diff --git a/sql-bench/test-connect.sh b/sql-bench/test-connect.sh index 862161e3a03..50778ce572c 100644 --- a/sql-bench/test-connect.sh +++ b/sql-bench/test-connect.sh @@ -27,7 +27,7 @@ use DBI; use Benchmark; -$opt_loop_count=10000; # Change this to make test harder/easier +$opt_loop_count=100000; # Change this to make test harder/easier $str_length=65000; # This is the length of blob strings in PART:5 $max_test=20; # How many times to test if the server is busy @@ -43,9 +43,10 @@ if ($opt_small_test) } $opt_loop_count=min(1000, $opt_loop_count) if ($opt_tcpip); +$small_loop_count=$opt_loop_count/10; # For connect tests print "Testing the speed of connecting to the server and sending of data\n"; -print "All tests are done $opt_loop_count times\n\n"; +print "Connect tests are done $small_loop_count times and other tests $opt_loop_count times\n\n"; ################################# PART:1 ################################### #### @@ -59,7 +60,7 @@ print "Testing connection/disconnect\n"; $loop_time=new Benchmark; $errors=0; -for ($i=0 ; $i < $opt_loop_count ; $i++) +for ($i=0 ; $i < $small_loop_count ; $i++) { print "$i " if (($opt_debug)); for ($j=0; $j < $max_test ; $j++) @@ -80,27 +81,27 @@ for ($i=0 ; $i < $opt_loop_count ; $i++) } $end_time=new Benchmark; print "Warning: $errors connections didn't work without a time delay\n" if ($errors); -print "Time to connect ($opt_loop_count): " . +print "Time to connect ($small_loop_count): " . timestr(timediff($end_time, $loop_time),"all") . "\n\n"; ################################# PART:2 ################################### #### Now we shall do first one connect, then simple select #### (select 1..) and then close connection. This will be -#### done $opt_loop_count times. +#### done $small_loop_count times. if ($limits->{'select_without_from'}) { print "Test connect/simple select/disconnect\n"; $loop_time=new Benchmark; - for ($i=0; $i<$opt_loop_count; $i++) + for ($i=0; $i < $small_loop_count; $i++) { $dbh = DBI->connect($server->{'data_source'}, $opt_user, $opt_password) || die $DBI::errstr; - $sth = $dbh->do("select 1") or die $DBI::errstr; + $sth = $dbh->do("select $i") or die $DBI::errstr; $dbh->disconnect; } $end_time=new Benchmark; - print "Time for connect+select_simple ($opt_loop_count): " . + print "Time for connect+select_simple ($small_loop_count): " . timestr(timediff($end_time, $loop_time),"all") . "\n\n"; } @@ -116,16 +117,33 @@ if ($limits->{'select_without_from'}) { print "Test simple select\n"; $loop_time=new Benchmark; - for ($i=0 ; $i<$opt_loop_count ; $i++) + for ($i=0 ; $i < $opt_loop_count ; $i++) { - $sth = $dbh->do("select 1") or die $DBI::errstr; + $sth = $dbh->do("select $i") or die $DBI::errstr; } $end_time=new Benchmark; print "Time for select_simple ($opt_loop_count): " . timestr(timediff($end_time, $loop_time),"all") . "\n\n"; } -################################# PART:4 ################################### +########################################################################### +#### The same as the previous test, but always execute the same select +#### This is done to test the query cache for real simple selects. + +if ($limits->{'select_without_from'}) +{ + print "Test simple select\n"; + $loop_time=new Benchmark; + for ($i=0 ; $i < $opt_loop_count ; $i++) + { + $sth = $dbh->do("select 10000") or die $DBI::errstr; + } + $end_time=new Benchmark; + print "Time for select_simple_cache ($opt_loop_count): " . + timestr(timediff($end_time, $loop_time),"all") . "\n\n"; +} + +########################################################################## #### First, we'll create a simple table 'bench1' #### Then we shall do $opt_loop_count selects from this table. #### Table will contain very simple data. @@ -152,7 +170,7 @@ print "Testing connect/select 1 row from table/disconnect\n"; $loop_time=new Benchmark; $errors=0; -for ($i=0 ; $i<$opt_loop_count ; $i++) +for ($i=0 ; $i < $small_loop_count ; $i++) { for ($j=0; $j < $max_test ; $j++) { @@ -161,14 +179,14 @@ for ($i=0 ; $i<$opt_loop_count ; $i++) } die $DBI::errstr if ($j == $max_test); - $sth = $dbh->do("select * from bench1") #Select * from table with 1 record + $sth = $dbh->do("select a,i,s,$i from bench1") # Select * from table with 1 record or die $DBI::errstr; $dbh->disconnect; } $end_time=new Benchmark; print "Warning: $errors connections didn't work without a time delay\n" if ($errors); -print "Time to connect+select_1_row ($opt_loop_count): " . +print "Time to connect+select_1_row ($small_loop_count): " . timestr(timediff($end_time, $loop_time),"all") . "\n\n"; # @@ -179,9 +197,9 @@ print "Testing select 1 row from table\n"; $dbh = $server->connect(); $loop_time=new Benchmark; -for ($i=0 ; $i<$opt_loop_count ; $i++) +for ($i=0 ; $i < $opt_loop_count ; $i++) { - $sth = $dbh->do("select * from bench1") # Select * from table with 1 record + $sth = $dbh->do("select a,i,s,$i from bench1") # Select * from table with 1 record or die $DBI::errstr; } @@ -190,8 +208,24 @@ print "Time to select_1_row ($opt_loop_count): " . timestr(timediff($end_time, $loop_time),"all") . "\n\n"; # -# The same test, but with 2 rows. +# Same test (as with one row) but now with a cacheable query # + +$loop_time=new Benchmark; + +for ($i=0 ; $i < $opt_loop_count ; $i++) +{ + $sth = $dbh->do("select a,i,s from bench1") # Select * from table with 1 record + or die $DBI::errstr; +} +$end_time=new Benchmark; +print "Time to select_1_row_cache ($opt_loop_count): " . + timestr(timediff($end_time, $loop_time),"all") . "\n\n"; + +# +# The same test, but with 2 rows (not cacheable). +# + print "Testing select 2 rows from table\n"; $sth = $dbh->do("insert into bench1 values(2,200,'BBB')") @@ -199,9 +233,9 @@ $sth = $dbh->do("insert into bench1 values(2,200,'BBB')") $loop_time=new Benchmark; -for ($i=0 ; $i<$opt_loop_count ; $i++) +for ($i=0 ; $i < $opt_loop_count ; $i++) { - $sth = $dbh->do("select * from bench1") # Select * from table with 2 record + $sth = $dbh->do("select a,i,s,$i from bench1") # Select * from table with 2 record or die $DBI::errstr; } @@ -209,14 +243,18 @@ $end_time=new Benchmark; print "Time to select_2_rows ($opt_loop_count): " . timestr(timediff($end_time, $loop_time),"all") . "\n\n"; +# +# Simple test to test speed of functions. +# + if ($limits->{'functions'}) { print "Test select with aritmetic (+)\n"; $loop_time=new Benchmark; - for ($i=0; $i<$opt_loop_count; $i++) + for ($i=0; $i < $opt_loop_count; $i++) { - $sth = $dbh->do("select a+a+a+a+a+a+a+a+a+a from bench1") or die $DBI::errstr; + $sth = $dbh->do("select a+a+a+a+a+a+a+a+a+$i from bench1") or die $DBI::errstr; } $end_time=new Benchmark; print "Time for select_column+column ($opt_loop_count): " . @@ -254,9 +292,9 @@ if ($opt_fast && defined($server->{vacuum})) $loop_time=new Benchmark; -for ($i=0 ; $i < $opt_loop_count ; $i++) +for ($i=0 ; $i < $small_loop_count ; $i++) { - $sth = $dbh->prepare("select * from bench1"); + $sth = $dbh->prepare("select b,$i from bench1"); if (!$sth->execute || !(@row = $sth->fetchrow_array) || length($row[0]) != $str_length) { @@ -266,7 +304,7 @@ for ($i=0 ; $i < $opt_loop_count ; $i++) } $end_time=new Benchmark; -print "Time to select_big_str ($opt_loop_count): " . +print "Time to select_big_str ($small_loop_count): " . timestr(timediff($end_time, $loop_time),"all") . "\n\n"; $sth = $dbh->do("drop table bench1" . $server->{'drop_attr'}) diff --git a/sql-bench/test-select.sh b/sql-bench/test-select.sh index 62e978a945f..a5ae1da7283 100644 --- a/sql-bench/test-select.sh +++ b/sql-bench/test-select.sh @@ -156,7 +156,7 @@ if ($limits->{'group_functions'}) fetch_all_rows($dbh,"select sum(idn+100),sum(rev_idn-100) from bench1"); } $end_time=new Benchmark; - print "Time for select_query_cache ($opt_loop_count): " . + print "Time for select_cache ($opt_loop_count): " . timestr(timediff($end_time, $loop_time),"all") . "\n\n"; # If the database has a query cache, the following loop should be much @@ -168,7 +168,7 @@ if ($limits->{'group_functions'}) fetch_all_rows($dbh,"select sum(idn+$tests),sum(rev_idn-$tests) from bench1"); } $end_time=new Benchmark; - print "Time for select_query_cache2 ($opt_loop_count): " . + print "Time for select_cache2 ($opt_loop_count): " . timestr(timediff($end_time, $loop_time),"all") . "\n\n"; } diff --git a/sql-bench/test-transactions.sh b/sql-bench/test-transactions.sh index adb76e6f68d..b081b89fb69 100644 --- a/sql-bench/test-transactions.sh +++ b/sql-bench/test-transactions.sh @@ -23,7 +23,7 @@ use DBI; use Benchmark; -use warnings; +#use warnings; $opt_groups=27; # Characters are 'A' -> Z @@ -34,7 +34,8 @@ chomp($pwd = `pwd`); $pwd = "." if ($pwd eq ''); require "$pwd/bench-init.pl" || die "Can't read Configuration file: $!\n"; # Avoid warnings for variables in bench-init.pl -our ($opt_small_test, $opt_small_tables, $opt_debug, $opt_force); +# (Only works with perl 5.6) +#our ($opt_small_test, $opt_small_tables, $opt_debug, $opt_force); if ($opt_small_test || $opt_small_tables) { diff --git a/sql/convert.cc b/sql/convert.cc index 3e0fbf18ace..2645690a71f 100644 --- a/sql/convert.cc +++ b/sql/convert.cc @@ -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/field.cc b/sql/field.cc index 2f98d2e0fe8..fc5feba8eb8 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -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; @@ -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..03fd6d46a78 100644 --- a/sql/field.h +++ b/sql/field.h @@ -16,8 +16,8 @@ /* -** 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()); } @@ -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,7 +587,7 @@ 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, @@ -612,7 +613,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,7 +644,7 @@ 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, @@ -671,7 +672,7 @@ 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, @@ -705,7 +706,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 +761,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 +814,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 +838,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 +864,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 +904,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 +941,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 +1024,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 +1067,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/ha_berkeley.h b/sql/ha_berkeley.h index 561e06229fa..ab1ead5a3e9 100644 --- a/sql/ha_berkeley.h +++ b/sql/ha_berkeley.h @@ -107,6 +107,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); diff --git a/sql/ha_myisam.cc b/sql/ha_myisam.cc index 7a8585975cc..fa130c59a88 100644 --- a/sql/ha_myisam.cc +++ b/sql/ha_myisam.cc @@ -130,7 +130,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 +139,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; diff --git a/sql/ha_myisammrg.cc b/sql/ha_myisammrg.cc index d82c202baa3..999d9fe33ef 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); diff --git a/sql/ha_myisammrg.h b/sql/ha_myisammrg.h index b97baa0703c..7d696162de0 100644 --- a/sql/ha_myisammrg.h +++ b/sql/ha_myisammrg.h @@ -74,4 +74,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..d7c6f3fe93c 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -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()) diff --git a/sql/item.h b/sql/item.h index a52860528f1..355d81d0c6c 100644 --- a/sql/item.h +++ b/sql/item.h @@ -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) {} diff --git a/sql/item_create.cc b/sql/item_create.cc index 55f8bb140a9..5809091275e 100644 --- a/sql/item_create.cc +++ b/sql/item_create.cc @@ -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,6 +145,7 @@ 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); } @@ -279,6 +284,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 +385,12 @@ 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); } diff --git a/sql/item_func.cc b/sql/item_func.cc index fe68d8f47c2..eeae891b793 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -2170,7 +2170,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) @@ -2184,10 +2184,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..23cdf7082cf 100644 --- a/sql/item_func.h +++ b/sql/item_func.h @@ -862,18 +862,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) diff --git a/sql/lex.h b/sql/lex.h index 37fe38b76a1..744898d6b9c 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}, @@ -114,6 +115,7 @@ static SYMBOL symbols[] = { { "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}, @@ -248,6 +250,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}, @@ -268,6 +271,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}, @@ -306,12 +310,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}, diff --git a/sql/log_event.cc b/sql/log_event.cc index 72198038a07..b8c2435e84e 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -193,14 +193,14 @@ void Query_log_event::pack_info(String* packet) char buf[256]; String tmp(buf, sizeof(buf)); tmp.length(0); - if(db && db_len) + if (db && db_len) { tmp.append("use "); tmp.append(db, db_len); tmp.append("; ", 2); } - if(query && q_len) + if (query && q_len) tmp.append(query, q_len); net_store_data(packet, (char*)tmp.ptr(), tmp.length()); } @@ -224,7 +224,7 @@ void Load_log_event::pack_info(String* packet) char buf[256]; String tmp(buf, sizeof(buf)); tmp.length(0); - if(db && db_len) + if (db && db_len) { tmp.append("use "); tmp.append(db, db_len); @@ -234,9 +234,9 @@ void Load_log_event::pack_info(String* packet) tmp.append("LOAD DATA INFILE '"); tmp.append(fname, fname_len); tmp.append("' ", 2); - if(sql_ex.opt_flags && REPLACE_FLAG ) + if (sql_ex.opt_flags && REPLACE_FLAG ) tmp.append(" REPLACE "); - else if(sql_ex.opt_flags && IGNORE_FLAG ) + else if (sql_ex.opt_flags && IGNORE_FLAG ) tmp.append(" IGNORE "); tmp.append("INTO TABLE "); @@ -278,12 +278,11 @@ void Load_log_event::pack_info(String* packet) if (num_fields) { - uint i; const char* field = fields; tmp.append(" ("); - for(i = 0; i < num_fields; i++) + for (uint i = 0; i < num_fields; i++) { - if(i) + if (i) tmp.append(" ,"); tmp.append( field); @@ -304,7 +303,7 @@ void Rotate_log_event::pack_info(String* packet) tmp.append(new_log_ident, ident_len); tmp.append(";pos="); tmp.append(llstr(pos,buf)); - if(flags & LOG_EVENT_FORCED_ROTATE_F) + if (flags & LOG_EVENT_FORCED_ROTATE_F) tmp.append("; forced by master"); net_store_data(packet, tmp.ptr(), tmp.length()); } @@ -414,7 +413,7 @@ int Log_event::read_log_event(IO_CACHE* file, String* packet, // if the read hits eof, we must report it as eof // so the caller will know it can go into cond_wait to be woken up // on the next update to the log - if(!file->error) return LOG_READ_EOF; + if (!file->error) return LOG_READ_EOF; return file->error > 0 ? LOG_READ_TRUNC: LOG_READ_IO; } data_len = uint4korr(buf + EVENT_LEN_OFFSET); @@ -430,7 +429,7 @@ int Log_event::read_log_event(IO_CACHE* file, String* packet, { if (packet->append(file, data_len)) { - if(log_lock) + if (log_lock) pthread_mutex_unlock(log_lock); // here we should never hit eof in a non-error condtion // eof means we are reading the event partially, which should @@ -445,13 +444,13 @@ int Log_event::read_log_event(IO_CACHE* file, String* packet, #endif // MYSQL_CLIENT #ifndef MYSQL_CLIENT -#define UNLOCK_MUTEX if(log_lock) pthread_mutex_unlock(log_lock); +#define UNLOCK_MUTEX if (log_lock) pthread_mutex_unlock(log_lock); #else #define UNLOCK_MUTEX #endif #ifndef MYSQL_CLIENT -#define LOCK_MUTEX if(log_lock) pthread_mutex_lock(log_lock); +#define LOCK_MUTEX if (log_lock) pthread_mutex_lock(log_lock); #else #define LOCK_MUTEX #endif @@ -501,7 +500,7 @@ Log_event* Log_event::read_log_event(IO_CACHE* file, bool old_format) } buf[data_len] = 0; memcpy(buf, head, header_size); - if(my_b_read(file, (byte*) buf + header_size, + if (my_b_read(file, (byte*) buf + header_size, data_len - header_size)) { error = "read error"; @@ -511,7 +510,7 @@ Log_event* Log_event::read_log_event(IO_CACHE* file, bool old_format) res->register_temp_buf(buf); err: UNLOCK_MUTEX; - if(error) + if (error) { sql_print_error(error); my_free(buf, MYF(MY_ALLOW_ZERO_PTR)); @@ -679,7 +678,7 @@ Rotate_log_event::Rotate_log_event(const char* buf, int event_len, // EVENT_LEN_OFFSET int header_size = (old_format) ? OLD_HEADER_LEN : LOG_EVENT_HEADER_LEN; uint ident_offset; - if(event_len < header_size) + if (event_len < header_size) return; buf += header_size; if (old_format) @@ -775,9 +774,9 @@ void Query_log_event::print(FILE* file, bool short_form, char* last_db) bool same_db = 0; - if(db && last_db) + if (db && last_db) { - if(!(same_db = !memcmp(last_db, db, db_len + 1))) + if (!(same_db = !memcmp(last_db, db, db_len + 1))) memcpy(last_db, db, db_len + 1); } @@ -838,7 +837,7 @@ int Intvar_log_event::write_data(IO_CACHE* file) void Intvar_log_event::print(FILE* file, bool short_form, char* last_db) { char llbuff[22]; - if(!short_form) + if (!short_form) { print_header(file); fprintf(file, "\tIntvar\n"); @@ -971,86 +970,83 @@ char* sql_ex_info::init(char* buf,char* buf_end,bool use_new_format) #ifndef MYSQL_CLIENT Load_log_event::Load_log_event(THD* thd, sql_exchange* ex, const char* db_arg, const char* table_name_arg, - List<Item>& fields_arg, enum enum_duplicates handle_dup): - Log_event(thd),thread_id(thd->thread_id), - num_fields(0),fields(0),field_lens(0),field_block_len(0), - table_name(table_name_arg), - db(db_arg), - fname(ex->file_name) - { - time_t end_time; - time(&end_time); - exec_time = (ulong) (end_time - thd->start_time); - db_len = (db) ? (uint32) strlen(db) : 0; - table_name_len = (table_name) ? (uint32) strlen(table_name) : 0; - fname_len = (fname) ? (uint) strlen(fname) : 0; - sql_ex.field_term = (char*) ex->field_term->ptr(); - sql_ex.field_term_len = (uint8) ex->field_term->length(); - sql_ex.enclosed = (char*) ex->enclosed->ptr(); - sql_ex.enclosed_len = (uint8) ex->enclosed->length(); - sql_ex.line_term = (char*) ex->line_term->ptr(); - sql_ex.line_term_len = (uint8) ex->line_term->length(); - sql_ex.line_start = (char*) ex->line_start->ptr(); - sql_ex.line_start_len = (uint8) ex->line_start->length(); - sql_ex.escaped = (char*) ex->escaped->ptr(); - sql_ex.escaped_len = (uint8) ex->escaped->length(); - sql_ex.opt_flags = 0; - sql_ex.cached_new_format = -1; + List<Item>& fields_arg, + enum enum_duplicates handle_dup): + Log_event(thd), fields(0), field_lens(0), + table_name(table_name_arg), db(db_arg), fname(ex->file_name), + thread_id(thd->thread_id), num_fields(0), field_block_len(0) +{ + time_t end_time; + time(&end_time); + exec_time = (ulong) (end_time - thd->start_time); + db_len = (db) ? (uint32) strlen(db) : 0; + table_name_len = (table_name) ? (uint32) strlen(table_name) : 0; + fname_len = (fname) ? (uint) strlen(fname) : 0; + sql_ex.field_term = (char*) ex->field_term->ptr(); + sql_ex.field_term_len = (uint8) ex->field_term->length(); + sql_ex.enclosed = (char*) ex->enclosed->ptr(); + sql_ex.enclosed_len = (uint8) ex->enclosed->length(); + sql_ex.line_term = (char*) ex->line_term->ptr(); + sql_ex.line_term_len = (uint8) ex->line_term->length(); + sql_ex.line_start = (char*) ex->line_start->ptr(); + sql_ex.line_start_len = (uint8) ex->line_start->length(); + sql_ex.escaped = (char*) ex->escaped->ptr(); + sql_ex.escaped_len = (uint8) ex->escaped->length(); + sql_ex.opt_flags = 0; + sql_ex.cached_new_format = -1; - if(ex->dumpfile) - sql_ex.opt_flags |= DUMPFILE_FLAG; - if(ex->opt_enclosed) - sql_ex.opt_flags |= OPT_ENCLOSED_FLAG; - - sql_ex.empty_flags = 0; - - switch(handle_dup) - { - case DUP_IGNORE: sql_ex.opt_flags |= IGNORE_FLAG; break; - case DUP_REPLACE: sql_ex.opt_flags |= REPLACE_FLAG; break; - case DUP_ERROR: break; - } - - - if(!ex->field_term->length()) - sql_ex.empty_flags |= FIELD_TERM_EMPTY; - if(!ex->enclosed->length()) - sql_ex.empty_flags |= ENCLOSED_EMPTY; - if(!ex->line_term->length()) - sql_ex.empty_flags |= LINE_TERM_EMPTY; - if(!ex->line_start->length()) - sql_ex.empty_flags |= LINE_START_EMPTY; - if(!ex->escaped->length()) - sql_ex.empty_flags |= ESCAPED_EMPTY; + if (ex->dumpfile) + sql_ex.opt_flags |= DUMPFILE_FLAG; + if (ex->opt_enclosed) + sql_ex.opt_flags |= OPT_ENCLOSED_FLAG; + + sql_ex.empty_flags = 0; + + switch(handle_dup) { + case DUP_IGNORE: sql_ex.opt_flags |= IGNORE_FLAG; break; + case DUP_REPLACE: sql_ex.opt_flags |= REPLACE_FLAG; break; + case DUP_ERROR: break; + } + + + if (!ex->field_term->length()) + sql_ex.empty_flags |= FIELD_TERM_EMPTY; + if (!ex->enclosed->length()) + sql_ex.empty_flags |= ENCLOSED_EMPTY; + if (!ex->line_term->length()) + sql_ex.empty_flags |= LINE_TERM_EMPTY; + if (!ex->line_start->length()) + sql_ex.empty_flags |= LINE_START_EMPTY; + if (!ex->escaped->length()) + sql_ex.empty_flags |= ESCAPED_EMPTY; - skip_lines = ex->skip_lines; - - List_iterator<Item> li(fields_arg); - field_lens_buf.length(0); - fields_buf.length(0); - Item* item; - while((item = li++)) - { - num_fields++; - uchar len = (uchar) strlen(item->name); - field_block_len += len + 1; - fields_buf.append(item->name, len + 1); - field_lens_buf.append((char*)&len, 1); - } + skip_lines = ex->skip_lines; - field_lens = (const uchar*)field_lens_buf.ptr(); - fields = fields_buf.ptr(); + List_iterator<Item> li(fields_arg); + field_lens_buf.length(0); + fields_buf.length(0); + Item* item; + while((item = li++)) + { + num_fields++; + uchar len = (uchar) strlen(item->name); + field_block_len += len + 1; + fields_buf.append(item->name, len + 1); + field_lens_buf.append((char*)&len, 1); } + field_lens = (const uchar*)field_lens_buf.ptr(); + fields = fields_buf.ptr(); +} + #endif // the caller must do buf[event_len] = 0 before he starts using the // constructed event Load_log_event::Load_log_event(const char* buf, int event_len, bool old_format): - Log_event(buf, old_format),num_fields(0),fields(0), - field_lens(0),field_block_len(0), - table_name(0),db(0),fname(0) + Log_event(buf, old_format),fields(0), + field_lens(0), num_fields(0), field_block_len(0) { if (!event_len) // derived class, will call copy_log_event() itself return; @@ -1112,32 +1108,32 @@ void Load_log_event::print(FILE* file, bool short_form, char* last_db) bool same_db = 0; - if(db && last_db) + if (db && last_db) { - if(!(same_db = !memcmp(last_db, db, db_len + 1))) + if (!(same_db = !memcmp(last_db, db, db_len + 1))) memcpy(last_db, db, db_len + 1); } - if(db && db[0] && !same_db) + if (db && db[0] && !same_db) fprintf(file, "use %s;\n", db); fprintf(file, "LOAD DATA INFILE '%-*s' ", fname_len, fname); - if(sql_ex.opt_flags && REPLACE_FLAG ) + if (sql_ex.opt_flags && REPLACE_FLAG ) fprintf(file," REPLACE "); - else if(sql_ex.opt_flags && IGNORE_FLAG ) + else if (sql_ex.opt_flags && IGNORE_FLAG ) fprintf(file," IGNORE "); fprintf(file, "INTO TABLE %s ", table_name); - if(sql_ex.field_term) + if (sql_ex.field_term) { fprintf(file, " FIELDS TERMINATED BY "); pretty_print_str(file, sql_ex.field_term, sql_ex.field_term_len); } - if(sql_ex.enclosed) + if (sql_ex.enclosed) { - if(sql_ex.opt_flags && OPT_ENCLOSED_FLAG ) + if (sql_ex.opt_flags && OPT_ENCLOSED_FLAG ) fprintf(file," OPTIONALLY "); fprintf(file, " ENCLOSED BY "); pretty_print_str(file, sql_ex.enclosed, sql_ex.enclosed_len); @@ -1161,7 +1157,7 @@ void Load_log_event::print(FILE* file, bool short_form, char* last_db) pretty_print_str(file, sql_ex.line_start, sql_ex.line_start_len); } - if((int)skip_lines > 0) + if ((int)skip_lines > 0) fprintf(file, " IGNORE %ld LINES ", (long) skip_lines); if (num_fields) @@ -1169,9 +1165,9 @@ void Load_log_event::print(FILE* file, bool short_form, char* last_db) uint i; const char* field = fields; fprintf( file, " ("); - for(i = 0; i < num_fields; i++) + for (i = 0; i < num_fields; i++) { - if(i) + if (i) fputc(',', file); fprintf(file, field); @@ -1197,24 +1193,24 @@ void Load_log_event::set_fields(List<Item> &fields) { uint i; const char* field = this->fields; - for(i = 0; i < num_fields; i++) - { - fields.push_back(new Item_field(db, table_name, field)); - field += field_lens[i] + 1; - } + for (i = 0; i < num_fields; i++) + { + fields.push_back(new Item_field(db, table_name, field)); + field += field_lens[i] + 1; + } } Slave_log_event::Slave_log_event(THD* thd_arg,struct st_master_info* mi): Log_event(thd_arg),mem_pool(0),master_host(0) { - if(!mi->inited) + if (!mi->inited) return; pthread_mutex_lock(&mi->lock); master_host_len = strlen(mi->host); master_log_len = strlen(mi->log_file_name); // on OOM, just do not initialize the structure and print the error - if((mem_pool = (char*)my_malloc(get_data_size() + 1, + if ((mem_pool = (char*)my_malloc(get_data_size() + 1, MYF(MY_WME)))) { master_host = mem_pool + SL_MASTER_HOST_OFFSET ; @@ -1243,7 +1239,7 @@ Slave_log_event::~Slave_log_event() void Slave_log_event::print(FILE* file, bool short_form, char* last_db) { char llbuff[22]; - if(short_form) + if (short_form) return; print_header(file); fputc('\n', file); @@ -1275,7 +1271,7 @@ void Slave_log_event::init_from_mem_pool(int data_size) master_host_len = strlen(master_host); // safety master_log = master_host + master_host_len + 1; - if(master_log > mem_pool + data_size) + if (master_log > mem_pool + data_size) { master_host = 0; return; @@ -1287,9 +1283,9 @@ Slave_log_event::Slave_log_event(const char* buf, int event_len): Log_event(buf,0),mem_pool(0),master_host(0) { event_len -= LOG_EVENT_HEADER_LEN; - if(event_len < 0) + if (event_len < 0) return; - if(!(mem_pool = (char*)my_malloc(event_len + 1, MYF(MY_WME)))) + if (!(mem_pool = (char*)my_malloc(event_len + 1, MYF(MY_WME)))) return; memcpy(mem_pool, buf + LOG_EVENT_HEADER_LEN, event_len); mem_pool[event_len] = 0; @@ -1396,7 +1392,7 @@ Append_block_log_event::Append_block_log_event(THD* thd_arg, char* block_arg, Append_block_log_event::Append_block_log_event(const char* buf, int len): Log_event(buf, 0),block(0) { - if((uint)len < APPEND_BLOCK_EVENT_OVERHEAD) + if ((uint)len < APPEND_BLOCK_EVENT_OVERHEAD) return; file_id = uint4korr(buf + LOG_EVENT_HEADER_LEN + AB_FILE_ID_OFFSET); block = (char*)buf + APPEND_BLOCK_EVENT_OVERHEAD; @@ -1448,7 +1444,7 @@ Delete_file_log_event::Delete_file_log_event(THD* thd_arg): Delete_file_log_event::Delete_file_log_event(const char* buf, int len): Log_event(buf, 0),file_id(0) { - if((uint)len < DELETE_FILE_EVENT_OVERHEAD) + if ((uint)len < DELETE_FILE_EVENT_OVERHEAD) return; file_id = uint4korr(buf + LOG_EVENT_HEADER_LEN + AB_FILE_ID_OFFSET); } @@ -1495,7 +1491,7 @@ Execute_load_log_event::Execute_load_log_event(THD* thd_arg): Execute_load_log_event::Execute_load_log_event(const char* buf,int len): Log_event(buf, 0),file_id(0) { - if((uint)len < EXEC_LOAD_EVENT_OVERHEAD) + if ((uint)len < EXEC_LOAD_EVENT_OVERHEAD) return; file_id = uint4korr(buf + LOG_EVENT_HEADER_LEN + EL_FILE_ID_OFFSET); } @@ -1551,8 +1547,10 @@ int Query_log_event::exec_event(struct st_master_info* mi) thd->net.last_error[0] = 0; thd->slave_proxy_id = thread_id; // for temp tables - // sanity check to make sure the master did not get a really bad - // error on the query + /* + sanity check to make sure the master did not get a really bad + error on the query + */ if (!check_expected_error(thd, (expected_error = error_code))) { mysql_parse(thd, thd->query, q_len); @@ -1609,7 +1607,7 @@ int Load_log_event::exec_event(NET* net, struct st_master_info* mi) thd->query = 0; thd->query_error = 0; - if(db_ok(thd->db, replicate_do_db, replicate_ignore_db)) + if (db_ok(thd->db, replicate_do_db, replicate_ignore_db)) { thd->set_time((time_t)when); thd->current_tablenr = 0; @@ -1623,7 +1621,7 @@ int Load_log_event::exec_event(NET* net, struct st_master_info* mi) tables.name = tables.real_name = (char*)table_name; tables.lock_type = TL_WRITE; // the table will be opened in mysql_load - if(table_rules_on && !tables_ok(thd, &tables)) + if (table_rules_on && !tables_ok(thd, &tables)) { if (net) skip_load_data_infile(net); @@ -1632,7 +1630,7 @@ int Load_log_event::exec_event(NET* net, struct st_master_info* mi) { char llbuff[22]; enum enum_duplicates handle_dup = DUP_IGNORE; - if(sql_ex.opt_flags && REPLACE_FLAG) + if (sql_ex.opt_flags && REPLACE_FLAG) handle_dup = DUP_REPLACE; sql_exchange ex((char*)fname, sql_ex.opt_flags && DUMPFILE_FLAG ); @@ -1658,14 +1656,14 @@ int Load_log_event::exec_event(NET* net, struct st_master_info* mi) // about the packet sequence thd->net.pkt_nr = net->pkt_nr; } - if(mysql_load(thd, &ex, &tables, fields, handle_dup, net != 0, + if (mysql_load(thd, &ex, &tables, fields, handle_dup, net != 0, TL_WRITE)) thd->query_error = 1; - if(thd->cuted_fields) + if (thd->cuted_fields) sql_print_error("Slave: load data infile at position %s in log \ '%s' produced %d warning(s)", llstr(mi->pos,llbuff), RPL_LOG_NAME, thd->cuted_fields ); - if(net) + if (net) net->pkt_nr = thd->net.pkt_nr; } } @@ -1680,10 +1678,10 @@ int Load_log_event::exec_event(NET* net, struct st_master_info* mi) thd->net.vio = 0; thd->db = 0;// prevent db from being freed close_thread_tables(thd); - if(thd->query_error) + if (thd->query_error) { int sql_error = thd->net.last_errno; - if(!sql_error) + if (!sql_error) sql_error = ER_UNKNOWN_ERROR; slave_print_error(sql_error, "Slave: Error '%s' running load data infile ", @@ -1693,7 +1691,7 @@ int Load_log_event::exec_event(NET* net, struct st_master_info* mi) } free_root(&thd->mem_root,0); - if(thd->fatal_error) + if (thd->fatal_error) { sql_print_error("Slave: Fatal error running LOAD DATA INFILE "); return 1; @@ -1788,7 +1786,7 @@ int Intvar_log_event::exec_event(struct st_master_info* mi) int Slave_log_event::exec_event(struct st_master_info* mi) { - if(mysql_bin_log.is_open()) + if (mysql_bin_log.is_open()) mysql_bin_log.write(this); return Log_event::exec_event(mi); } diff --git a/sql/log_event.h b/sql/log_event.h index 329d748025d..4426232008b 100644 --- a/sql/log_event.h +++ b/sql/log_event.h @@ -244,42 +244,31 @@ public: virtual bool is_valid() = 0; virtual bool get_cache_stmt() { return 0; } Log_event(const char* buf, bool old_format); -#ifndef MYSQL_CLIENT - Log_event(THD* thd_arg, uint16 flags_arg = 0); -#endif virtual ~Log_event() { free_temp_buf();} void register_temp_buf(char* buf) { temp_buf = buf; } void free_temp_buf() + { + if (temp_buf) { - if (temp_buf) - { - my_free(temp_buf, MYF(0)); - temp_buf = 0; - } + my_free(temp_buf, MYF(0)); + temp_buf = 0; } + } virtual int get_data_size() { return 0;} virtual int get_data_body_offset() { return 0; } int get_event_len() { return cached_event_len ? cached_event_len : (cached_event_len = LOG_EVENT_HEADER_LEN + get_data_size()); } -#ifdef MYSQL_CLIENT - virtual void print(FILE* file, bool short_form = 0, char* last_db = 0) = 0; - void print_timestamp(FILE* file, time_t *ts = 0); - void print_header(FILE* file); -#endif - -#ifndef MYSQL_CLIENT - // if mutex is 0, the read will proceed without mutex - static Log_event* read_log_event(IO_CACHE* file, - pthread_mutex_t* log_lock, - bool old_format); -#else // avoid having to link mysqlbinlog against libpthread - static Log_event* read_log_event(IO_CACHE* file, bool old_format); -#endif + static Log_event* read_log_event(const char* buf, int event_len, const char **error, bool old_format); const char* get_type_str(); #ifndef MYSQL_CLIENT + // if mutex is 0, the read will proceed without mutex + Log_event(THD* thd_arg, uint16 flags_arg = 0); + static Log_event* read_log_event(IO_CACHE* file, + pthread_mutex_t* log_lock, + bool old_format); static int read_log_event(IO_CACHE* file, String* packet, pthread_mutex_t* log_lock); void set_log_seq(THD* thd, MYSQL_LOG* log); @@ -291,6 +280,13 @@ public: { return thd ? thd->db : 0; } +#else + // avoid having to link mysqlbinlog against libpthread + static Log_event* read_log_event(IO_CACHE* file, bool old_format); + + virtual void print(FILE* file, bool short_form = 0, char* last_db = 0) = 0; + void print_timestamp(FILE* file, time_t *ts = 0); + void print_header(FILE* file); #endif }; @@ -303,13 +299,16 @@ protected: public: const char* query; const char* db; - uint32 q_len; // if we already know the length of the query string - // we pass it here, so we would not have to call strlen() - // otherwise, set it to 0, in which case, we compute it with strlen() + /* + If we already know the length of the query string + we pass it here, so we would not have to call strlen() + otherwise, set it to 0, in which case, we compute it with strlen() + */ + uint32 q_len; uint32 db_len; uint16 error_code; ulong thread_id; -#if !defined(MYSQL_CLIENT) +#ifndef MYSQL_CLIENT bool cache_stmt; Query_log_event(THD* thd_arg, const char* query_arg, @@ -318,6 +317,8 @@ public: void pack_info(String* packet); int exec_event(struct st_master_info* mi); bool get_cache_stmt() { return cache_stmt; } +#else + void print(FILE* file, bool short_form = 0, char* last_db = 0); #endif Query_log_event(const char* buf, int event_len, bool old_format); @@ -340,9 +341,6 @@ public: + 2 // error_code ; } -#ifdef MYSQL_CLIENT - void print(FILE* file, bool short_form = 0, char* last_db = 0); -#endif }; class Slave_log_event: public Log_event @@ -352,24 +350,22 @@ protected: void init_from_mem_pool(int data_size); public: char* master_host; - int master_host_len; - uint16 master_port; char* master_log; - int master_log_len; ulonglong master_pos; + int master_host_len; + int master_log_len; + uint16 master_port; -#ifndef MYSQL_CLIENT - Slave_log_event(THD* thd_arg, struct st_master_info* mi); - void pack_info(String* packet); - int exec_event(struct st_master_info* mi); -#endif - Slave_log_event(const char* buf, int event_len); ~Slave_log_event(); int get_data_size(); bool is_valid() { return master_host != 0; } Log_event_type get_type_code() { return SLAVE_EVENT; } -#ifdef MYSQL_CLIENT +#ifndef MYSQL_CLIENT + Slave_log_event(THD* thd_arg, struct st_master_info* mi); + void pack_info(String* packet); + int exec_event(struct st_master_info* mi); +#else void print(FILE* file, bool short_form = 0, char* last_db = 0); #endif int write_data(IO_CACHE* file ); @@ -382,22 +378,21 @@ protected: int copy_log_event(const char *buf, ulong event_len, bool old_format); public: + const char* fields; + const uchar* field_lens; + const char* table_name; + const char* db; + const char* fname; ulong thread_id; uint32 table_name_len; uint32 db_len; uint32 fname_len; uint32 num_fields; - const char* fields; - const uchar* field_lens; uint32 field_block_len; - - const char* table_name; - const char* db; - const char* fname; uint32 skip_lines; sql_ex_info sql_ex; -#if !defined(MYSQL_CLIENT) +#ifndef MYSQL_CLIENT String field_lens_buf; String fields_buf; @@ -408,10 +403,12 @@ public: void pack_info(String* packet); const char* get_db() { return db; } int exec_event(struct st_master_info* mi) - { - return exec_event(thd->slave_net,mi); - } + { + return exec_event(thd->slave_net,mi); + } int exec_event(NET* net, struct st_master_info* mi); +#else + void print(FILE* file, bool short_form = 0, char* last_db = 0); #endif Load_log_event(const char* buf, int event_len, bool old_format); @@ -434,9 +431,6 @@ public: ; } int get_data_body_offset() { return LOAD_EVENT_OVERHEAD; } -#ifdef MYSQL_CLIENT - void print(FILE* file, bool short_form = 0, char* last_db = 0); -#endif }; extern char server_version[SERVER_VERSION_LENGTH]; @@ -447,13 +441,7 @@ public: uint32 created; uint16 binlog_version; char server_version[ST_SERVER_VER_LEN]; -#ifndef MYSQL_CLIENT - Start_log_event() :Log_event((THD*)0),binlog_version(BINLOG_VERSION) - { - created = (uint32) when; - memcpy(server_version, ::server_version, ST_SERVER_VER_LEN); - } -#endif + Start_log_event(const char* buf, bool old_format); ~Start_log_event() {} Log_event_type get_type_code() { return START_EVENT;} @@ -464,10 +452,14 @@ public: return START_HEADER_LEN; } #ifndef MYSQL_CLIENT + Start_log_event() :Log_event((THD*)0),binlog_version(BINLOG_VERSION) + { + created = (uint32) when; + memcpy(server_version, ::server_version, ST_SERVER_VER_LEN); + } void pack_info(String* packet); int exec_event(struct st_master_info* mi); -#endif -#ifdef MYSQL_CLIENT +#else void print(FILE* file, bool short_form = 0, char* last_db = 0); #endif }; @@ -477,11 +469,7 @@ class Intvar_log_event: public Log_event public: ulonglong val; uchar type; -#ifndef MYSQL_CLIENT - Intvar_log_event(THD* thd_arg,uchar type_arg, ulonglong val_arg) - :Log_event(thd_arg),val(val_arg),type(type_arg) - {} -#endif + Intvar_log_event(const char* buf, bool old_format); ~Intvar_log_event() {} Log_event_type get_type_code() { return INTVAR_EVENT;} @@ -490,11 +478,12 @@ public: int write_data(IO_CACHE* file); bool is_valid() { return 1; } #ifndef MYSQL_CLIENT + Intvar_log_event(THD* thd_arg,uchar type_arg, ulonglong val_arg) + :Log_event(thd_arg),val(val_arg),type(type_arg) + {} void pack_info(String* packet); int exec_event(struct st_master_info* mi); -#endif - -#ifdef MYSQL_CLIENT +#else void print(FILE* file, bool short_form = 0, char* last_db = 0); #endif }; @@ -502,10 +491,6 @@ public: class Stop_log_event: public Log_event { public: -#ifndef MYSQL_CLIENT - Stop_log_event() :Log_event((THD*)0) - {} -#endif Stop_log_event(const char* buf, bool old_format):Log_event(buf, old_format) { @@ -513,11 +498,11 @@ public: ~Stop_log_event() {} Log_event_type get_type_code() { return STOP_EVENT;} bool is_valid() { return 1; } -#ifdef MYSQL_CLIENT - void print(FILE* file, bool short_form = 0, char* last_db = 0); -#endif -#ifndef MYSQL_CLIENT +#ifndef MYSQL_CLIENT + Stop_log_event() :Log_event((THD*)0) {} int exec_event(struct st_master_info* mi); +#else + void print(FILE* file, bool short_form = 0, char* last_db = 0); #endif }; @@ -525,19 +510,10 @@ class Rotate_log_event: public Log_event { public: const char* new_log_ident; - uchar ident_len; ulonglong pos; + uint8 ident_len; bool alloced; -#ifndef MYSQL_CLIENT - Rotate_log_event(THD* thd_arg, const char* new_log_ident_arg, - uint ident_len_arg = 0,ulonglong pos_arg = 4) : - Log_event(thd_arg), - new_log_ident(new_log_ident_arg), - ident_len(ident_len_arg ? ident_len_arg : - (uint) strlen(new_log_ident_arg)), pos(pos_arg), - alloced(0) - {} -#endif + Rotate_log_event(const char* buf, int event_len, bool old_format); ~Rotate_log_event() { @@ -548,64 +524,81 @@ public: int get_data_size() { return ident_len + ROTATE_HEADER_LEN;} bool is_valid() { return new_log_ident != 0; } int write_data(IO_CACHE* file); -#ifdef MYSQL_CLIENT - void print(FILE* file, bool short_form = 0, char* last_db = 0); -#endif -#ifndef MYSQL_CLIENT +#ifndef MYSQL_CLIENT + Rotate_log_event(THD* thd_arg, const char* new_log_ident_arg, + uint8 ident_len_arg = 0,ulonglong pos_arg = 4) : + Log_event(thd_arg), new_log_ident(new_log_ident_arg), + pos(pos_arg), + ident_len(ident_len_arg ? ident_len_arg : + (uint8) strlen(new_log_ident_arg)), + alloced(0) + {} void pack_info(String* packet); int exec_event(struct st_master_info* mi); +#else + void print(FILE* file, bool short_form = 0, char* last_db = 0); #endif }; + /* the classes below are for the new LOAD DATA INFILE logging */ class Create_file_log_event: public Load_log_event { protected: - // pretend we are Load event, so we can write out just - // our Load part - used on the slave when writing event out to - // SQL_LOAD-*.info file + /* + Pretend we are Load event, so we can write out just + our Load part - used on the slave when writing event out to + SQL_LOAD-*.info file + */ bool fake_base; public: char* block; uint block_len; uint file_id; -#ifndef MYSQL_CLIENT - Create_file_log_event(THD* thd, sql_exchange* ex, const char* db_arg, - const char* table_name_arg, - List<Item>& fields_arg, - enum enum_duplicates handle_dup, - char* block_arg, uint block_len_arg); -#endif - + Create_file_log_event(const char* buf, int event_len); ~Create_file_log_event() { } Log_event_type get_type_code() - { - return fake_base ? Load_log_event::get_type_code() : CREATE_FILE_EVENT; - } - int get_data_size() { return fake_base ? Load_log_event::get_data_size() : - Load_log_event::get_data_size() + - 4 + 1 + block_len;} - int get_data_body_offset() { return fake_base ? LOAD_EVENT_OVERHEAD: - LOAD_EVENT_OVERHEAD + CREATE_FILE_HEADER_LEN; } + { + return fake_base ? Load_log_event::get_type_code() : CREATE_FILE_EVENT; + } + int get_data_size() + { + return (fake_base ? Load_log_event::get_data_size() : + Load_log_event::get_data_size() + + 4 + 1 + block_len); + } + int get_data_body_offset() + { + return (fake_base ? LOAD_EVENT_OVERHEAD: + LOAD_EVENT_OVERHEAD + CREATE_FILE_HEADER_LEN); + } bool is_valid() { return block != 0; } int write_data_header(IO_CACHE* file); int write_data_body(IO_CACHE* file); - int write_base(IO_CACHE* file); // cut out Create_file extentions and - // write it as Load event - used on the slave + /* + Cut out Create_file extentions and write it as Load event - used on the + slave. + */ + int write_base(IO_CACHE* file); -#ifdef MYSQL_CLIENT - void print(FILE* file, bool short_form = 0, char* last_db = 0); -#endif #ifndef MYSQL_CLIENT + Create_file_log_event(THD* thd, sql_exchange* ex, const char* db_arg, + const char* table_name_arg, + List<Item>& fields_arg, + enum enum_duplicates handle_dup, + char* block_arg, uint block_len_arg); void pack_info(String* packet); int exec_event(struct st_master_info* mi); +#else + void print(FILE* file, bool short_form = 0, char* last_db = 0); #endif }; + class Append_block_log_event: public Log_event { public: @@ -613,12 +606,6 @@ public: uint block_len; uint file_id; -#ifndef MYSQL_CLIENT - Append_block_log_event(THD* thd, char* block_arg, - uint block_len_arg); - int exec_event(struct st_master_info* mi); -#endif - Append_block_log_event(const char* buf, int event_len); ~Append_block_log_event() { @@ -628,23 +615,22 @@ public: bool is_valid() { return block != 0; } int write_data(IO_CACHE* file); -#ifdef MYSQL_CLIENT - void print(FILE* file, bool short_form = 0, char* last_db = 0); -#endif #ifndef MYSQL_CLIENT + Append_block_log_event(THD* thd, char* block_arg, + uint block_len_arg); + int exec_event(struct st_master_info* mi); void pack_info(String* packet); +#else + void print(FILE* file, bool short_form = 0, char* last_db = 0); #endif }; + class Delete_file_log_event: public Log_event { public: uint file_id; -#ifndef MYSQL_CLIENT - Delete_file_log_event(THD* thd); -#endif - Delete_file_log_event(const char* buf, int event_len); ~Delete_file_log_event() { @@ -654,12 +640,12 @@ public: bool is_valid() { return file_id != 0; } int write_data(IO_CACHE* file); -#ifdef MYSQL_CLIENT - void print(FILE* file, bool short_form = 0, char* last_db = 0); -#endif #ifndef MYSQL_CLIENT + Delete_file_log_event(THD* thd); void pack_info(String* packet); int exec_event(struct st_master_info* mi); +#else + void print(FILE* file, bool short_form = 0, char* last_db = 0); #endif }; @@ -668,10 +654,6 @@ class Execute_load_log_event: public Log_event public: uint file_id; -#ifndef MYSQL_CLIENT - Execute_load_log_event(THD* thd); -#endif - Execute_load_log_event(const char* buf, int event_len); ~Execute_load_log_event() { @@ -681,16 +663,13 @@ public: bool is_valid() { return file_id != 0; } int write_data(IO_CACHE* file); -#ifdef MYSQL_CLIENT - void print(FILE* file, bool short_form = 0, char* last_db = 0); -#endif #ifndef MYSQL_CLIENT + Execute_load_log_event(THD* thd); void pack_info(String* packet); int exec_event(struct st_master_info* mi); +#else + void print(FILE* file, bool short_form = 0, char* last_db = 0); #endif }; -#endif - - - +#endif /* _LOG_EVENT_H */ diff --git a/sql/mf_iocache.cc b/sql/mf_iocache.cc index 2a7b25eab2f..860318d5a79 100644 --- a/sql/mf_iocache.cc +++ b/sql/mf_iocache.cc @@ -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/mysql_priv.h b/sql/mysql_priv.h index e0e85145c52..72953cad8e8 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -179,6 +179,8 @@ 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 OPTION_TO_QUERY_CACHE (TMP_TABLE_ALL_COLUMNS*2) + #define MODE_REAL_AS_FLOAT 1 #define MODE_PIPES_AS_CONCAT 2 @@ -248,7 +250,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); @@ -275,7 +277,7 @@ 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); @@ -330,7 +332,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, @@ -539,6 +541,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; @@ -694,7 +697,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 b742b803628..5acec4ead67 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -270,6 +270,7 @@ ulong keybuff_size,sortbuff_size,max_item_sort_length,table_cache_size, open_files_limit=0, max_binlog_size, record_rnd_cache_size; 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 @@ -359,6 +360,8 @@ pthread_mutex_t LOCK_mysql_create_db, LOCK_Acl, LOCK_open, LOCK_thread_count, LOCK_binlog_update, LOCK_slave, LOCK_server_id, LOCK_user_conn, LOCK_slave_list; +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; @@ -732,7 +735,7 @@ void clean_up(bool print_message) return; /* purecov: inspected */ acl_free(1); grant_free(); - sql_cache_free(); + query_cache.resize(0); table_cache_free(); hostname_cache_free(); item_user_lock_free(); @@ -1810,7 +1813,8 @@ 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(); @@ -2953,6 +2957,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, @@ -3007,7 +3017,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}, @@ -3081,6 +3091,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}, @@ -3144,8 +3157,13 @@ 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}, + {"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}, + {"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}, @@ -3160,29 +3178,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}, @@ -3708,12 +3726,11 @@ static void get_options(int argc,char **argv) opt_slow_log=1; opt_slow_logname=optarg; break; - case (int)OPT_SKIP_SLAVE_START: + case (int) OPT_SKIP_SLAVE_START: opt_skip_slave_start = 1; break; case (int) OPT_SKIP_NEW: opt_specialflag|= SPECIAL_NO_NEW_FUNC; - default_table_type=DB_TYPE_ISAM; myisam_delay_key_write=0; myisam_concurrent_insert=0; myisam_recover_options= HA_RECOVER_NONE; @@ -3721,6 +3738,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; diff --git a/sql/net_serv.cc b/sql/net_serv.cc index 5e002a0f63e..5a39b071b4f 100644 --- a/sql/net_serv.cc +++ b/sql/net_serv.cc @@ -94,6 +94,7 @@ inline int local_thr_alarm(my_bool *A,int B __attribute__((unused)),ALARM *C __a #ifdef MYSQL_SERVER extern ulong bytes_sent, bytes_received; extern pthread_mutex_t LOCK_bytes_sent , LOCK_bytes_received; +extern void query_cache_insert(NET *net, const char *packet, ulong length); #else #undef statistic_add #define statistic_add(A,B,C) @@ -108,7 +109,7 @@ static int net_write_buff(NET *net,const char *packet,ulong len); 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 +126,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 +162,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)))) { @@ -187,7 +189,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 +243,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 +252,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 +282,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 +294,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)); } @@ -341,6 +343,10 @@ net_real_write(NET *net,const char *packet,ulong len) my_bool net_blocking = vio_is_blocking(net->vio); DBUG_ENTER("net_real_write"); +#ifdef MYSQL_SERVER + query_cache_insert(net, packet, len); +#endif + if (net->error == 2) DBUG_RETURN(-1); /* socket can't be used */ @@ -351,8 +357,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; @@ -389,7 +395,7 @@ net_real_write(NET *net,const char *packet,ulong len) 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)) @@ -473,7 +479,7 @@ net_real_write(NET *net,const char *packet,ulong len) 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,7 +502,7 @@ 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); } } @@ -521,8 +527,8 @@ my_real_read(NET *net, ulong *complen) 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; @@ -599,7 +605,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 +614,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); } @@ -655,14 +661,14 @@ my_real_read(NET *net, ulong *complen) { #ifdef MYSQL_SERVER 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; } } 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/sql_base.cc b/sql/sql_base.cc index b913233806f..7f6ffd04d98 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -2195,16 +2195,18 @@ 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..9b83a14c6d6 100644 --- a/sql/sql_cache.cc +++ b/sql/sql_cache.cc @@ -1,98 +1,2832 @@ /* 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 */ +/* + 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). + +6. 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. +*/ + #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 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 MUTEX_UNLOCK(M) {DBUG_PRINT("lock", ("mutex unlock 0x%lx",\ + (ulong)(M))); pthread_mutex_unlock(M);} +#define SEM_UNLOCK(M) {DBUG_PRINT("lock", ("sem unlock 0x%lx", (ulong)(M))); \ + sem_post(M); DBUG_PRINT("lock", ("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)->queries_dump();(C)->tables_dump();}) +#else +#define MUTEX_LOCK(M) pthread_mutex_lock(M) +#define SEM_LOCK(M) sem_wait(M) +#define MUTEX_UNLOCK(M) pthread_mutex_unlock(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) - + 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", (ulong) this)); + length = block_length; + used = 0; + type = Query_cache_block::FREE; + n_tables = 0; + DBUG_VOID_RETURN; +} + +void Query_cache_block::destroy() +{ + DBUG_ENTER("Query_cache_block::destroy"); + DBUG_PRINT("qcache", ("destroy block 0x%lx, type %d", + (ulong) this, type)); + type = INCOMPLETE; + DBUG_VOID_RETURN; +} + +inline uint Query_cache_block::headers_len() +{ + return (ALIGN_SIZE(sizeof(Query_cache_block_table)*n_tables) + + ALIGN_SIZE(sizeof(Query_cache_block))); +} + +inline gptr Query_cache_block::data(void) +{ + return (gptr)( ((byte*)this) + headers_len() ); +} -#define SQL_CACHE_LENGTH 30 // 300 crashes apple gcc. +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(); +} -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_table * Query_cache_block::table() +{ +#ifndef DBUG_OFF + if (type != TABLE) + query_cache.wreck(__LINE__, "incorrect block type"); +#endif + return (Query_cache_table *) 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_result * Query_cache_block::result() { -#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 != RESULT && type != RES_CONT && type != RES_BEG && + type != RES_INCOMPLETE) + query_cache.wreck(__LINE__, "incorrect block type"); #endif - return 0; + return (Query_cache_result *) data(); +} + +inline Query_cache_block_table * Query_cache_block::table(TABLE_COUNTER_TYPE n) +{ + return ((Query_cache_block_table *) + (((byte*)this)+ALIGN_SIZE(sizeof(Query_cache_block)) + + n*sizeof(Query_cache_block_table))); } -/* At the moment we do not really want to do anything upon delete */ -static void free_cache_entry(void *entry) + +/***************************************************************************** + * 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; } -/* Initialization of the SQL cache hash -- should be called during - the bootstrap stage */ -bool sql_cache_init(void) + +void Query_cache_query::unlock_n_destroy() { - if (query_buff_size) + DBUG_ENTER("Query_cache_query::unlock_n_destroy"); + /* + The following call is not needed on system where one can destroy an + active semaphore + */ + this->unlock_writing(); + DBUG_PRINT("qcache", ("destroyed & unlocked query for block 0x%lx", + ((byte*)this)-ALIGN_SIZE(sizeof(Query_cache_block)))); + 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("qcache", ("can't lock mutex")); + DBUG_RETURN(0); } - return 0; + DBUG_PRINT("qcache", ("mutex 'lock' 0x%lx locked", (ulong) &lock)); + DBUG_RETURN(1); +} + + +void Query_cache_query::lock_reading() +{ + MUTEX_LOCK(&clients_guard); + if (!clients++) + SEM_LOCK(&lock); + MUTEX_UNLOCK(&clients_guard); +} + + +void Query_cache_query::unlock_writing() +{ + SEM_UNLOCK(&lock); +} + + +void Query_cache_query::unlock_reading() +{ + MUTEX_LOCK(&clients_guard); + if (--clients == 0) + SEM_UNLOCK(&lock); + MUTEX_UNLOCK(&clients_guard); } -/* 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))); +} } -/* 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) */ +/***************************************************************************** + Functions to store things into the query cache +*****************************************************************************/ -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 + + // Quick check on unlocked structure + if (net->query_cache_query != 0) + { + 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; + } + header->result(result); + BLOCK_UNLOCK_WR(query_block); } - 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"); + else + STRUCT_UNLOCK(&query_cache.structure_guard_mutex); + } + DBUG_VOID_RETURN; +} + + +void query_cache_abort(NET *net) +{ + DBUG_ENTER("query_cache_abort"); + +#ifndef DBUG_OFF + // Check if we have called query_cache.wreck() (which disables the cache) + if (query_cache.query_cache_size == 0) + DBUG_VOID_RETURN; +#endif + if (net->query_cache_query != 0) // Quick check on unlocked structure + { + STRUCT_LOCK(&query_cache.structure_guard_mutex); + Query_cache_block *query_block = ((Query_cache_block*) + net->query_cache_query); + if (query_block) // Test if changed by other thread + { + DUMP(&query_cache); + BLOCK_LOCK_WR(query_block); + // The following call will remove the lock on query_block + query_cache.free_query(query_block); + net->query_cache_query=0; } - return 0; + 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); + STRUCT_UNLOCK(&query_cache.structure_guard_mutex); + + Query_cache_query *header = query_block->query(); +#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; + net->query_cache_query=0; + 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_VOID_RETURN; +} + +void query_cache_invalidate_by_MyISAM_filename(const char *filename) +{ + query_cache.invalidate_by_MyISAM_filename(filename); +} + + +/***************************************************************************** + 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), + 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) +{ + /* + TODO: + When will be realized pack() optimize case when + query_cache_size < this->query_cache_size + + Try to copy old cache in new memory + */ + 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) +{ + /* + TODO: + Maybe better convert keywords to upper case when query stored/compared. + (Not important at this stage) + */ + TABLE_COUNTER_TYPE tables; + 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); + + /* + 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); + } + + /* Check if another thread is processing the same query? */ + thd->query[thd->query_length] = (char) flags; + Query_cache_block *competitor = (Query_cache_block *) + hash_search(&queries, thd->query, thd->query_length+1); + 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 */ + thd->query[thd->query_length] = (char) flags; + Query_cache_block *query_block; + query_block= write_block_data(thd->query_length+1, + (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, (char *) query_block); + header->unlock_n_destroy(); + free_memory_block(query_block); + STRUCT_UNLOCK(&structure_guard_mutex); + DBUG_VOID_RETURN; + } + 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: + thd->query[thd->query_length]= 0; // Restore end null + DBUG_VOID_RETURN; +} + + +my_bool +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; + 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 on not in autocommit mode")); + goto err; + } + /* + We can't cache the query if we are using a temporary table because + we don't know if the query is using a temporary table. + + TODO: We could parse the query and then check if the query used + a temporary table. + */ + if (thd->temporary_tables != 0 || !thd->safe_to_cache_query) + { + DBUG_PRINT("qcache", ("SELECT is non-cacheable")); + goto err; + } + + /* Test if the query is a SELECT */ + while (*sql == ' ' || *sql == '\t') + { + sql++; + query_length--; + } + 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 and not in autocommit mode")); + STRUCT_UNLOCK(&structure_guard_mutex); + goto err; + } + DBUG_PRINT("qcache", (" sql %u '%s'", query_length, sql)); + Query_cache_block *query_block; + + /* + 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[query_length] = (char) flags; + query_block = (Query_cache_block *) hash_search(&queries, sql, + query_length+1); + sql[query_length] = '\0'; + + /* Quick abort on unlocked data */ + if (query_block == 0 || + query_block->query()->result() == 0 || + query_block->query()->result()->type != Query_cache_block::RESULT) + { + STRUCT_UNLOCK(&structure_guard_mutex); + DBUG_PRINT("qcache", ("No query in query hash or no results")); + goto err; + } + 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; + } + 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)) + { + DBUG_PRINT("qcache", + ("probably no SELECT access to %s.%s => return to normal processing", + table_list.db, table_list.name)); + BLOCK_UNLOCK_RD(query_block); + STRUCT_UNLOCK(&structure_guard_mutex); + goto err; + } + } + 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(0); + +err: + DBUG_RETURN(1); +} + +/* + 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. + TODO: This is currently used to invalidate InnoDB tables on commit. + We should remove this function and only invalidate tables + used in the transaction. +*/ + +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 && tables_blocks[type] != 0) + { + Query_cache_block *table_block = tables_blocks[type]; + do + { + /* Store next block address defore deleting the current block */ + Query_cache_block *next = table_block->next; + invalidate_table(table_block); +#ifdef TO_BE_DELETED + if (next == table_block) // End of list + break; +#endif + table_block = next; + } while (table_block != 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); + int i = 0; + for(; i < (int) Query_cache_table::TYPES_NUMBER; i++) + { + if (tables_blocks[i] != 0) // Cache not empty + { + Query_cache_block *table_block = tables_blocks[i]; + do + { + /* + Store next block address defore deletetion of current block + */ + Query_cache_block *next = table_block->next; + + invalidate_table_in_db(table_block, db); +#ifdef TO_BE_DELETED + if (table_block == next) + break; +#endif + table_block = next; + } while (table_block != 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, 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); + } + 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() +{ + 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); + 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 + free_memory= 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"); + } +#endif + + /* Becasue we did a flush, all cache memory must be in one this block */ + bins[0].free_blocks->destroy(); + DBUG_PRINT("qcache", ("free memory %lu (should be %lu)", + free_memory , query_cache_size)); + 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)); + *current_block=0; // Mark error + 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... + if (last_block_free_space < data_len && + append_next_free_block(last_block, + max(data_len - last_block_free_space, + QUERY_CACHE_MIN_RESULT_DATA_SIZE))) + 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) + { + // TODO: Try get memory from next free block (if exist) (is it needed?) + 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 not 0 even !success (if write_result_data + allocate small block but failed 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); + 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); +} + +/* + 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) +{ + 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)); + + *result_block = allocate_block(max(min_result_data_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))) + 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,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, table->table_cache_key, + table->key_length)))) + invalidate_table(table_block); +} + +void Query_cache::invalidate_table_in_db(Query_cache_block *table_block, + char *db) +{ + /* + table key consist of data_base_name + '\0' + table_name +'\0'... + => we may use strcmp to compare database names. + */ + if (strcmp(db, (char*)(table_block->table()->db())) == 0) + 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; + /* + TODO: (Low priority) + The following has to be recoded to not test for a specific table + type but instead call a handler function that does this for us. + Something like the following: + + tables_used->table->file->register_used_filenames(callback, + first_argument); + */ + 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, 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(); + /* + TODO: Eliminate strlen() by sending this to the function + To do this we have to add db_len to the TABLE_LIST and TABLE structures. + */ + 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) +{ + 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); + } +} + +/***************************************************************************** + 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; + first = list; + while (first->next != list && first->length < len) + first=first->next; + if (first->length >= len) + block=first; + } + 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_n_unlock_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); + block->length=len; + new_block->pnext = block->pnext; + block->pnext = new_block; + new_block->pprev = block; + new_block->pnext->pprev = new_block; + + insert_into_free_memory_list(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(); + + 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->is_free()) + { + ulong old_len = block->length; + exclude_from_free_memory_list(next_block); + next_block->destroy(); + + 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; + 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) +{ + int i; + DBUG_ENTER("Query_cache::find_bin"); + // Begin small blocks to big (small blocks frequently asked) + for (i=mem_bin_steps - 1; i > 0 && steps[i-1].size < size; i--) ; + if (i == 0) + { + // first bin not subordinate of common rules + DBUG_PRINT("qcache", ("first bin (# 0), size %lu",size)); + DBUG_RETURN(0); + } + uint bin = steps[i].idx - (uint)((size - steps[i].size)/steps[i].increment); + DBUG_PRINT("qcache", ("bin %u step %u, size %lu", bin, i, 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; + 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 + { + point->next = (*list_pointer); + point->prev = (*list_pointer)->prev; + point->prev->next = point; + (*list_pointer)->prev = point; + (*list_pointer) = 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() +{ + STRUCT_LOCK(&structure_guard_mutex); + byte *border = 0; + Query_cache_block *before = 0; + ulong gap = 0; + my_bool ok = 1; + Query_cache_block *block = first_block; + DBUG_ENTER("Query_cache::pack_cache"); + DUMP(this); + + if (first_block) + { + do + { + ok = move_by_type(&border, &before, &gap, block); + block = block->pnext; + } while (ok && block != first_block); + + if (border != 0) + { + Query_cache_block *new_block = (Query_cache_block *) border; + new_block->init(gap); + 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); + } + 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(); + 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; + char *data = (char*) block->data(); + byte *key; + uint key_length; + key=query_cache_table_get_key((byte*) block, &key_length,0); + hash_search(&tables, 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; + memcpy((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 == list_root ? nlist_root : tnext); + nlist_root->prev = (tprev == list_root ? nlist_root: tnext); + tnext->prev = list_root; + tprev->next = list_root; + *border += len; + *before = new_block; + /* 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, key, key_length); + + memcpy((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; + memcpy((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 ); + } + 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; + memcpy((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) +{ + nblock->prev = (prev == oblock ? nblock : prev); //check pointer to himself + nblock->next = (next == oblock ? nblock : next); + prev->next=nblock; + 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; + Query_cache_block *table_block; + 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 +****************************************************************************/ + +#ifndef DBUG_OFF + +void Query_cache::wreck(uint line, const char *message) +{ + 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", ("==================================")); + current_thd->killed = 1; + bins_dump(); + cache_dump(); + DBUG_VOID_RETURN; +} + + +void Query_cache::bins_dump() +{ + uint i; + 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() +{ + 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() +{ + 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); + byte flags = (byte) str[len-1]; + DBUG_PRINT("qcache", ("%u (%u,%u) %.*s",len, + ((flags & QUERY_CACHE_CLIENT_LONG_FLAG_MASK)? 1:0), + (flags & QUERY_CACHE_CHARSET_CONVERT_MASK), len, + str)); + 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)); + + 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() +{ + 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", ("--------------------")); } +#endif /* DBUG_OFF */ diff --git a/sql/sql_cache.h b/sql/sql_cache.h new file mode 100644 index 00000000000..f5f0580d051 --- /dev/null +++ b/sql/sql_cache.h @@ -0,0 +1,391 @@ +/* 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 + +#include <semaphore.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 0 + +/* 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 + +/* 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 + +/* 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 + +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; + +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 / table block / free block + 3. results blocks (only when must become free). + */ + 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); + void invalidate_table(TABLE_LIST *table); + void invalidate_table(TABLE *table); + void invalidate_table_in_db(Query_cache_block *table_block, + char *db); + 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); + 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. + */ + my_bool 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(); + +#ifndef DBUG_OFF + void wreck(uint line, const char *message); + void bins_dump(); + void cache_dump(); + void queries_dump(); + void tables_dump(); +#endif + 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); +}; + +extern Query_cache query_cache; + +#endif diff --git a/sql/sql_class.cc b/sql/sql_class.cc index d76461b6faf..d293a62903d 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -84,7 +84,7 @@ 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_start_used=safe_to_cache_query=0; query_length=col_access=0; query_error=0; next_insert_id=last_insert_id=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; diff --git a/sql/sql_class.h b/sql/sql_class.h index 698a90c1a28..5d20508e728 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -126,14 +126,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 { @@ -234,35 +236,35 @@ class delayed_insert; class THD :public ilink { public: - NET net; - LEX lex; - MEM_ROOT mem_root; - HASH user_vars; - String packet; /* Room for 1 row */ - struct sockaddr_in remote; - struct rand_struct rand; - char *query,*thread_stack; - char *host,*user,*priv_user,*db,*ip; - const char *proc_info, *host_or_ip; - uint client_capabilities,sql_mode,max_packet_length; - uint master_access,db_access; - TABLE *open_tables,*temporary_tables, *handler_tables; + NET net; + LEX lex; + MEM_ROOT mem_root; + HASH user_vars; + String packet; /* Room for 1 row */ + struct sockaddr_in remote; + struct rand_struct rand; + char *query,*thread_stack; + char *host,*user,*priv_user,*db,*ip; + const char *proc_info, *host_or_ip; + uint client_capabilities,sql_mode,max_packet_length; + uint master_access,db_access; + TABLE *open_tables,*temporary_tables, *handler_tables; MYSQL_LOCK *lock,*locked_tables; - ULL *ull; + ULL *ull; struct st_my_thread_var *mysys_var; enum enum_server_command command; - uint32 server_id; - uint32 log_seq; - uint32 file_id; // for LOAD DATA INFILE + uint32 server_id; + uint32 log_seq; + 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; @@ -275,34 +277,40 @@ 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; enum_tx_isolation tx_isolation, session_tx_isolation; char scramble[9]; + uint8 query_cache_type; // type of query cache processing bool slave_thread; bool set_query_id,locked,count_cuted_fields,some_tables_deleted; bool no_errors, allow_sum_func, password, fatal_error; bool query_start_used,last_insert_id_used,insert_id_used; bool system_thread,in_lock_tables,global_read_lock; bool query_error, bootstrap, cleanup_done; + bool safe_to_cache_query; bool volatile killed; + /* + If we do a purge of binary logs, log index info of the threads + that are currently reading it needs to be adjusted. To do that + each thread that is using LOG_INFO needs to adjust the pointer to it + */ LOG_INFO* current_linfo; - // if we do a purge of binary logs, log index info of the threads - // that are currently reading it needs to be adjusted. To do that - // each thread that is using LOG_INFO needs to adjust the pointer to it - - ulong slave_proxy_id; // in slave thread we need to know in behalf of which - // thread the query is being run to replicate temp tables properly - - 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. THD(); ~THD(); void cleanup(void); @@ -324,10 +332,10 @@ public: { pthread_mutex_lock(&active_vio_lock); if(active_vio) - { - vio_close(active_vio); - active_vio = 0; - } + { + vio_close(active_vio); + active_vio = 0; + } pthread_mutex_unlock(&active_vio_lock); } #endif diff --git a/sql/sql_db.cc b/sql/sql_db.cc index 3158c8cbf7b..b7f078e7b2b 100644 --- a/sql/sql_db.cc +++ b/sql/sql_db.cc @@ -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) diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index 63e003178c2..bb04712aaee 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -183,6 +183,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); @@ -539,6 +541,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 +573,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_insert.cc b/sql/sql_insert.cc index 70cc5f412a1..548e7c9062f 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -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; @@ -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) @@ -1335,6 +1337,8 @@ void select_insert::send_error(uint errcode,const char *err) table->file->extra(HA_EXTRA_NO_CACHE); table->file->activate_all_index(thd); ha_rollback(thd); + if (info.copied || info.deleted) + query_cache.invalidate(table); } @@ -1346,6 +1350,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_parse.cc b/sql/sql_parse.cc index 96185a174b5..8eef80bc985 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); 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 @@ -160,7 +177,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)) return -1; if (db && db[0]) { @@ -210,8 +227,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) { int error=1; uint temp_len; @@ -363,51 +379,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 +426,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 +436,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 +466,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]); @@ -693,8 +690,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 +712,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 +723,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 +743,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)); } @@ -842,6 +840,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd, pos--; packet_length--; } + thd->query_length= packet_length; if (!(thd->query= (char*) thd->memdup((gptr) (packet),packet_length+1))) break; thd->query[packet_length]=0; @@ -872,8 +871,8 @@ 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); + thd->query=fields=thd->strdup(strend(packet)+1); mysql_log.write(thd,command,"%s %s",table_list.real_name,fields); remove_escape(table_list.real_name); // This can't have wildcards @@ -952,7 +951,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd, } case COM_REFRESH: { - uint options=(uchar) packet[0]; + ulong options= (ulong) (uchar) packet[0]; if (check_access(thd,RELOAD_ACL,any_db)) break; mysql_log.write(thd,command,NullS); @@ -1164,7 +1163,10 @@ 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; @@ -1299,8 +1301,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; @@ -1311,7 +1315,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; @@ -1420,6 +1424,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, @@ -1427,6 +1432,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 @@ -1455,6 +1461,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)) @@ -1493,6 +1500,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: @@ -1501,6 +1509,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: @@ -1609,7 +1618,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; @@ -1897,7 +1906,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); @@ -2152,13 +2161,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)) @@ -2451,12 +2464,21 @@ 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)) + { + thd->safe_to_cache_query=1; + 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; } @@ -2953,8 +2975,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)) @@ -2962,7 +2983,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; @@ -2982,6 +3003,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) @@ -3052,7 +3082,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) @@ -3068,7 +3098,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_select.cc b/sql/sql_select.cc index 660702d4117..cfc40bdfdc3 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -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); @@ -795,7 +796,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 +2153,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 +2168,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 +2191,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 +2218,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, @@ -2771,8 +2784,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 +3172,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 +3285,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 +3327,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 +3335,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; @@ -3509,8 +3524,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 +3541,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) @@ -3707,7 +3722,7 @@ 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) { @@ -4416,8 +4431,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; } @@ -5255,7 +5273,7 @@ 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 +** If not selecting by given key, create an index how records should be read ** return: 0 ok ** -1 some fatal error ** 1 no records @@ -5359,6 +5377,11 @@ test_if_skip_sort_order(JOIN_TAB *tab,ORDER *order,ha_rows select_limit, join_init_read_last_with_key); table->file->index_init(nr); tab->type=JT_NEXT; // Read with index_first(), index_next() + if (table->used_keys & ((key_map) 1 << nr)) + { + table->key_read=1; + table->file->extra(HA_EXTRA_KEYREAD); + } } DBUG_RETURN(1); } @@ -6409,7 +6432,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 +6460,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..1e62f1b7809 100644 --- a/sql/sql_select.h +++ b/sql/sql_select.h @@ -190,7 +190,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 +210,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 +219,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 +235,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 +260,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 +279,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 19c3d89caaf..16790ac1621 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -1054,9 +1054,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; } diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 049fb1c182c..2f9fa17faf1 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -158,13 +158,17 @@ 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); + } } } @@ -700,7 +704,7 @@ TABLE *create_table_from_items(THD *thd, HA_CREATE_INFO *create_info, DBUG_RETURN(0); } - Field *field=create_tmp_field(&tmp_table,item,item->type(), + Field *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 +951,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 +1029,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 +1039,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 +1663,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 +1723,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_update.cc b/sql/sql_update.cc index 0422d9664b6..4cf5366a8ba 100644 --- a/sql/sql_update.cc +++ b/sql/sql_update.cc @@ -321,6 +321,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 */ diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 01ba468175e..ab74ee74171 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -153,6 +153,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize); %token BOOLEAN_SYM %token BOTH %token BY +%token CACHE_SYM %token CASCADE %token CHECKSUM_SYM %token CHECK_SYM @@ -167,6 +168,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize); %token DEFAULT %token DELAYED_SYM %token DELAY_KEY_WRITE_SYM +%token DEMAND_SYM %token DESC %token DESCRIBE %token DIRECTORY_SYM @@ -246,6 +248,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 +265,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 +289,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 @@ -452,6 +458,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 @@ -1341,7 +1348,7 @@ table_to_table: select: - SELECT_SYM select_part2 {Select->braces=false;} union + SELECT_SYM select_part2 { Select->braces=false; } union | '(' SELECT_SYM select_part2 ')' {Select->braces=true;} union_opt @@ -1379,14 +1386,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; } + { Lex->lock_option= TL_WRITE; current_thd->safe_to_cache_query=0; } | IN_SYM SHARE_SYM MODE_SYM - { Lex->lock_option= TL_READ_WITH_SHARED_LOCKS; } + { 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 +1563,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); } @@ -1594,22 +1612,32 @@ 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); } @@ -1633,7 +1661,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 +1680,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 +1702,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 +1752,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 +1760,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 +1775,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 +1783,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 +1791,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 +1817,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); } @@ -2101,6 +2149,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 ')' @@ -2580,6 +2629,7 @@ 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; } @@ -2602,8 +2652,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 @@ -2851,6 +2902,7 @@ keyword: | BIT_SYM {} | BOOL_SYM {} | BOOLEAN_SYM {} + | CACHE_SYM {} | CHANGED {} | CHECKSUM_SYM {} | CHECK_SYM {} @@ -2867,6 +2919,7 @@ keyword: | DAY_SYM {} | DIRECTORY_SYM {} | DELAY_KEY_WRITE_SYM {} + | DEMAND_SYM {} | DISABLE_SYM {} | DUMPFILE {} | DYNAMIC_SYM {} @@ -2922,12 +2975,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 {} @@ -2949,6 +3004,9 @@ keyword: | SHARE_SYM {} | SHUTDOWN {} | SLAVE {} + | SQL_CACHE_SYM {} + | SQL_NO_CACHE_SYM {} + | SQL_QUERY_CACHE_TYPE_SYM {} | START_SYM {} | STATUS_SYM {} | STOP_SYM {} @@ -3072,6 +3130,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); @@ -3112,6 +3171,13 @@ option_value: item)); } +query_cache_type: + '0' { current_thd->query_cache_type = 0; } + | OFF { current_thd->query_cache_type = 0; } + | '1' { current_thd->query_cache_type = 1; } + | ON { current_thd->query_cache_type = 1; } + | '2' { current_thd->query_cache_type = 2; } + | DEMAND_SYM { current_thd->query_cache_type = 2; } text_or_password: TEXT_STRING { $$=$1.str;} diff --git a/sql/table.cc b/sql/table.cc index eb6d8fbb9cd..11c30f12ca2 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -467,7 +467,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; } } |