diff options
author | unknown <monty@hundin.mysql.fi> | 2001-12-02 14:34:01 +0200 |
---|---|---|
committer | unknown <monty@hundin.mysql.fi> | 2001-12-02 14:34:01 +0200 |
commit | 7c999bb03223114a4d07f23db2842363d32d1859 (patch) | |
tree | 8e30a64d5c7aaa0ba8554c4c316cc9b76a6724ca | |
parent | 95e94560eac0ee72bbc566517ca70f14128a01e8 (diff) | |
download | mariadb-git-7c999bb03223114a4d07f23db2842363d32d1859.tar.gz |
Query cache.
Remove some warnings
Docs/manual.texi:
Solaris and gcc
include/ft_global.h:
Remove warnings
include/myisam.h:
Query cache
include/myisammrg.h:
Query cache
include/mysql_com.h:
Query cache
libmysqld/lib_sql.cc:
Query cache
myisam/ft_boolean_search.c:
Remove warnings
myisam/ft_dump.c:
Remove warnings
myisam/ft_parser.c:
Remove warnings
myisam/ft_static.c:
Remove warnings
myisam/ft_update.c:
Remove warnings
myisam/ftdefs.h:
Remove warnings
myisam/mi_delete.c:
Query cache
myisam/mi_locking.c:
Query cache
myisam/mi_update.c:
Query cache
myisam/myisamdef.h:
Optimize for Ia64
myisammrg/myrg_extra.c:
Query cache
mysys/mf_keycache.c:
DBUG statements
regex/cclass.h:
Remove warnings
regex/cname.h:
Remove warnings
regex/main.c:
Remove warnings
regex/regcomp.c:
Remove warnings
regex/regcomp.ih:
Remove warnings
regex/regerror.c:
Remove warnings
regex/reginit.c:
Remove warnings
regex/split.c:
Remove warnings
sql-bench/test-connect.sh:
Make tests query-cache safe.
sql-bench/test-transactions.sh:
Fix for old perl versions
sql/convert.cc:
Query cache
sql/ha_myisammrg.cc:
Query cache
sql/ha_myisammrg.h:
Query cache
sql/handler.cc:
Query cache
sql/item_create.cc:
Query cache
sql/item_func.cc:
Remove warnings
sql/item_func.h:
Remove warnings
sql/lex.h:
Query cache
sql/mysql_priv.h:
Query cache
sql/mysqld.cc:
Query cache
sql/net_serv.cc:
Query cache
sql/sql_cache.cc:
Query cache
sql/sql_class.cc:
Query cache
sql/sql_class.h:
Query cache
sql/sql_db.cc:
Query cache
sql/sql_delete.cc:
Query cache
sql/sql_insert.cc:
Query cache
sql/sql_parse.cc:
Query cache
sql/sql_select.cc:
Query cache
sql/sql_table.cc:
Query cache
sql/sql_update.cc:
Query cache
sql/sql_yacc.yy:
Query cache
51 files changed, 3363 insertions, 292 deletions
diff --git a/Docs/manual.texi b/Docs/manual.texi index 1e64a53ffbc..7b9479861b9 100644 --- a/Docs/manual.texi +++ b/Docs/manual.texi @@ -8528,6 +8528,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: 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/myisam.h b/include/myisam.h index 36c0de5034f..352b87c82c2 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) (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..16f603516f9 100644 --- a/include/mysql_com.h +++ b/include/mysql_com.h @@ -78,6 +78,9 @@ 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 */ +#define REFRESH_QUERY_CACHE 65536 /* flush query cache */ +#define REFRESH_QUERY_CACHE_FREE 0x10000L /* 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 +129,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/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 0055842c24b..e7baad64d61 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)]; @@ -280,7 +280,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 940164f89c5..0d5fa7c8fd6 100644 --- a/myisam/ft_dump.c +++ b/myisam/ft_dump.c @@ -184,14 +184,14 @@ 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 --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\ +-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/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..912eeb1f9e6 100644 --- a/myisammrg/myrg_extra.c +++ b/myisammrg/myrg_extra.c @@ -46,3 +46,13 @@ 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/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/sql-bench/test-connect.sh b/sql-bench/test-connect.sh index 862161e3a03..2670c7fdf29 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 $opt_small_loop_count 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_query_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; } @@ -199,9 +217,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; } @@ -214,9 +232,9 @@ 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 +272,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 +284,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-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/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_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/mysql_priv.h b/sql/mysql_priv.h index e0e85145c52..5830c946372 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); @@ -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; diff --git a/sql/mysqld.cc b/sql/mysqld.cc index c4af3366806..1cb06c8a461 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_limit, SHOW_LONG}, + {"query_cache_size", (char*) &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", (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_refused", (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}, diff --git a/sql/net_serv.cc b/sql/net_serv.cc index 5e002a0f63e..ac622bca254 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) @@ -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 */ { @@ -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 */ diff --git a/sql/sql_cache.cc b/sql/sql_cache.cc index 09d436c0c9c..38db67ba38f 100644 --- a/sql/sql_cache.cc +++ b/sql/sql_cache.cc @@ -1,15 +1,15 @@ /* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB - + This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. - + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - + You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ @@ -18,81 +18,2503 @@ #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 + +#ifdef EXTRA_DEBUG +#define MUTEX_LOCK(M) { DBUG_PRINT("info", ("mutex lock 0x%lx", (ulong)(M))); \ + pthread_mutex_lock(M);} +#define SEM_LOCK(M) { int val = 0; sem_getvalue (M, &val); \ + DBUG_PRINT("info", ("sem lock 0x%lx (%d)", (ulong)(M), val)); \ + sem_wait(M); DBUG_PRINT("info", ("sem lock ok")); } +#define MUTEX_UNLOCK(M) {DBUG_PRINT("info", ("mutex unlock 0x%lx",\ + (ulong)(M))); pthread_mutex_unlock(M);} +#define SEM_UNLOCK(M) {DBUG_PRINT("info", ("sem unlock 0x%lx", (ulong)(M))); \ + sem_post(M); DBUG_PRINT("info", ("sem unlock ok")); } +#define STRUCT_LOCK(M) {DBUG_PRINT("info", ("%d struct lock...",__LINE__)); \ + pthread_mutex_lock(M);DBUG_PRINT("info", ("struct lock OK"));} +#define STRUCT_UNLOCK(M) { \ + DBUG_PRINT("info", ("%d struct unlock...",__LINE__)); \ + pthread_mutex_unlock(M);DBUG_PRINT("info", ("struct unlock OK"));} +#define BLOCK_LOCK_WR(B) {DBUG_PRINT("info", ("%d LOCK_WR 0x%lx",\ + __LINE__,(ulong)(B))); \ + B->query()->lock_writing();} +#define BLOCK_LOCK_RD(B) {DBUG_PRINT("info", ("%d LOCK_RD 0x%lx",\ + __LINE__,(ulong)(B))); \ + B->query()->lock_reading();} +#define BLOCK_UNLOCK_WR(B) { \ + DBUG_PRINT("info", ("%d UNLOCK_WR 0x%lx",\ + __LINE__,(ulong)(B)));B->query()->unlock_writing();} +#define BLOCK_UNLOCK_RD(B) { \ + DBUG_PRINT("info", ("%d UNLOCK_RD 0x%lx",\ + __LINE__,(ulong)(B)));B->query()->unlock_reading();} +#define DUMP(C) {C->bins_dump();C->cache_dump();\ + 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) - + ALIGN_SIZE(sizeof(Query_cache_block_table))*n - + ALIGN_SIZE(sizeof(Query_cache_block))); +}; + +/***************************************************************************** + * Query_cache_block method(s) + *****************************************************************************/ + +void Query_cache_block::init(ulong block_length) +{ + DBUG_ENTER("Query_cache_block::init"); + DBUG_PRINT("info", ("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("info", ("destroy block 0x%lx, type %d", + (ulong)this, type)); + type = INCOMPLETE; + DBUG_VOID_RETURN; +} + +inline uint Query_cache_block::headers_len() +{ + return (ALIGN_SIZE(sizeof(Query_cache_block_table))*n_tables + + ALIGN_SIZE(sizeof(Query_cache_block))); +} + +inline gptr Query_cache_block::data(void) +{ + return (gptr)( ((byte*)this) + headers_len() ); +} + +inline Query_cache_query * Query_cache_block::query() +{ +#ifndef DBUG_OFF + if (type != QUERY) + query_cache.wreck(__LINE__, "incorrect block type"); +#endif + return (Query_cache_query *) data(); +} + +inline Query_cache_table * Query_cache_block::table() +{ +#ifndef DBUG_OFF + if (type != TABLE) + query_cache.wreck(__LINE__, "incorrect block type"); +#endif + return (Query_cache_table *) data(); +} + +inline Query_cache_result * Query_cache_block::result() +{ +#ifndef DBUG_OFF + if (type != RESULT && type != RES_CONT && type != RES_BEG && + type != RES_INCOMPLETE) + query_cache.wreck(__LINE__, "incorrect block type"); +#endif + return (Query_cache_result *) data(); +} + +inline Query_cache_block_table * Query_cache_block::table(TABLE_COUNTER_TYPE n) +{ + return ((Query_cache_block_table *) + (((byte*)this)+ALIGN_SIZE(sizeof(Query_cache_block)) + + n*ALIGN_SIZE(sizeof(Query_cache_block_table)))); +} + + +/***************************************************************************** + * Query_cache_table method(s) + *****************************************************************************/ + +byte * Query_cache_table::cache_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))); +} + +void Query_cache_table::free_cache(void *entry) +{ + //NOP +} + +/***************************************************************************** + * 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("info", ("inited & locked query for block 0x%lx", + ((byte*) this)-ALIGN_SIZE(sizeof(Query_cache_block)))); + DBUG_VOID_RETURN; +} + +void Query_cache_query::unlock_n_destroy() +{ + DBUG_ENTER("Query_cache_query::unlock_n_destroy"); + this->unlock_writing(); + DBUG_PRINT("info", ("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 rwad/write locking only in this + particular case and in interaction with structure_guard_mutex. + + Lock for write prevents any other locking. + 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 wait while block become unlocked, in addition + block locking mean that query now used and we not need to + remove it +*/ + +my_bool Query_cache_query::try_lock_writing() +{ + DBUG_ENTER("Query_cache_block::try_lock_writing"); + if (sem_trywait(&lock)!=0 || clients != 0) + { + DBUG_PRINT("info", ("can't lock mutex")); + DBUG_RETURN(0); + } + DBUG_PRINT("info", ("mutex 'lock' 0x%lx locked", (ulong) &lock)); + DBUG_RETURN(1); +} + + +void Query_cache_query::lock_reading() +{ + MUTEX_LOCK(&clients_guard); + clients++; + if (clients == 1) SEM_LOCK(&lock); + MUTEX_UNLOCK(&clients_guard); +} + + +void Query_cache_query::unlock_writing() +{ + SEM_UNLOCK(&lock); +} + + +void Query_cache_query::unlock_reading() +{ + MUTEX_LOCK(&clients_guard); + clients--; + if (clients == 0) SEM_UNLOCK(&lock); + MUTEX_UNLOCK(&clients_guard); + +} + + +byte * Query_cache_query::cache_key( const byte *record, uint *length, + my_bool not_used) +{ + Query_cache_block * query_block = (Query_cache_block *) record; + *length = (query_block->used - query_block->headers_len() - + ALIGN_SIZE(sizeof(Query_cache_query))); + return (((byte *)query_block->data()) + + ALIGN_SIZE(sizeof(Query_cache_query))); +} + + +void Query_cache_query::free_cache(void *entry) +{ + //NOP +} + +/***************************************************************************** + * Query cache store functions + *****************************************************************************/ + +void query_cache_insert(NET *net, const char *packet, ulong length) +{ + DBUG_ENTER("query_cache_insert"); + +#ifndef DBUG_OFF + // Debugging method wreck may cause this + 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); + if (net->query_cache_query != 0) + { + Query_cache_block * query_block = (Query_cache_block*) + net->query_cache_query; + DUMP((&query_cache)); + BLOCK_LOCK_WR(query_block); + DBUG_PRINT("info", ("insert packet %lu bytes long",length)); + Query_cache_query * header = query_block->query(); + + Query_cache_block * result = header->result(); + /* + STRUCT_UNLOCK(&query_cache.structure_guard_mutex); will be done by + query_cache.append_result_data if success (if no success we need + query_cache.structure_guard_mutex locked to free query) + */ + if (!query_cache.append_result_data( result, length, (gptr) packet, + query_block, result)) + { + DBUG_PRINT("warning", ("Can't append data")); + header->result(result); + DBUG_PRINT("info", ("free query 0x%lx", (ulong)query_block)); + query_cache.free_query(query_block); + // append_result_data no success => we need unlock + STRUCT_UNLOCK(&query_cache.structure_guard_mutex); + DBUG_VOID_RETURN; + } + header->result(result); + BLOCK_UNLOCK_WR(query_block); + } + else + STRUCT_UNLOCK(&query_cache.structure_guard_mutex); + } + DBUG_VOID_RETURN; +} + +void query_cache_abort(NET *net) +{ + DBUG_ENTER("query_cache_abort"); + +#ifndef DBUG_OFF + // debuging method wreck may cause this + 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); + DUMP((&query_cache)); + Query_cache_block * query_block = ((Query_cache_block*) + net->query_cache_query); + if (query_block) + { + BLOCK_LOCK_WR(query_block); + query_cache.free_query(query_block); + net->query_cache_query=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 + // debuging method wreck may couse this + 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); + if (net->query_cache_query != 0) + { + Query_cache_block * query_block = (Query_cache_block*) + net->query_cache_query; + 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) + { +#endif + header->found_rows(current_thd->limit_found_rows); + header->result()->type = Query_cache_block::RESULT; +#ifndef DBUG_OFF + } + else + { + DBUG_PRINT("error", ("end of data whith no result. query '%s'", + header->query())); + query_cache.wreck(__LINE__, ""); + DBUG_VOID_RETURN; + } +#endif + 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(char * filename) +{ + query_cache.invalidate_by_MyISAM_filename(filename); +} + +/***************************************************************************** + * Query_cache methods + *****************************************************************************/ + +/***************************************************************************** + * interface 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), + 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), + queries_in_cache(0), hits(0), inserts(0), refused(0), + initialized(0) +{ + if (min_allocation_unit < ALIGN_SIZE(sizeof(Query_cache_block)) + + ALIGN_SIZE(sizeof(Query_cache_block_table)) + + ALIGN_SIZE(sizeof(Query_cache_query)) + 3) + { + min_allocation_unit=ALIGN_SIZE(sizeof(Query_cache_block)) + + ALIGN_SIZE(sizeof(Query_cache_block_table)) + + ALIGN_SIZE(sizeof(Query_cache_query)) + 3; + } + this->min_allocation_unit = min_allocation_unit; + if (min_result_data_size < min_allocation_unit) + 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 + */ + /* + TODO: try to copy old cache in new mamory + */ + DBUG_ENTER("Query_cache::resize"); + DBUG_PRINT("info", ("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 may be better convert keywords to upper case when query + stored/compared + */ + DBUG_ENTER("Query_cache::store_query"); + if (query_cache_size == 0) + DBUG_VOID_RETURN; + + LEX * lex = &thd->lex; + NET * net = &thd->net; + + TABLE_COUNTER_TYPE tables = 0; + + if ((tables = is_cachable(thd, thd->query_length, + thd->query, lex, tables_used))){ + STRUCT_LOCK(&structure_guard_mutex); + if (query_cache_size == 0) + DBUG_VOID_RETURN; -#define SQL_CACHE_LENGTH 30 // 300 crashes apple gcc. + DUMP(this); -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; + /* + prepare flags: + most significant bit - CLIENT_LONG_FLAG, + other - charset number (0 no charset convertion) + */ + byte flags = (thd->client_capabilities & CLIENT_LONG_FLAG ? 0x80 : 0); + if (thd->convert_set != 0) + { + flags |= (byte) thd->convert_set->number(); +#ifndef DBUG_OFF + if ( (thd->convert_set->number() & QUERY_CACHE_CHARSET_CONVERT_MASK) != + thd->convert_set->number()) + { + wreck(__LINE__, + "charset number bigger than QUERY_CACHE_CHARSET_CONVERT_MASK"); + } +#endif + } + + /* check: Is it another thread who process 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); + thd->query[thd->query_length] = '\0'; + DBUG_PRINT("info", ("competitor 0x%lx, flags %x", (ulong)competitor, + flags)); + + if (competitor == 0) + { + thd->query[thd->query_length] = (char)flags; + Query_cache_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); + thd->query[thd->query_length] = '\0'; + if (query_block != 0) + { + DBUG_PRINT("info", ("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("info", ("insertion in query hash")); + header->unlock_n_destroy(); + free_memory_block(query_block); + STRUCT_UNLOCK(&structure_guard_mutex); + DBUG_VOID_RETURN; + } + if (!register_all_tables(query_block, tables_used, tables)) + { + refused++; + DBUG_PRINT("warning", ("tables list incliding filed")); + 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 + { + refused++; + // We have not enough memory to store query => do nothing + STRUCT_UNLOCK(&structure_guard_mutex); + DBUG_PRINT("warning", ("Can't allocate query")); + } + } + else + { + refused++; + // Another thread already procass same query => do nothing + DBUG_PRINT("info", ("Another thread process same query")); + STRUCT_UNLOCK(&structure_guard_mutex); + } + } + else + refused++; + DBUG_VOID_RETURN; +} -/* Function to return a text string from a LEX struct */ -static byte *cache_key(const byte *record, uint *length, my_bool not_used) +my_bool Query_cache::send_result_to_client( + THD *thd, char *sql, uint query_length) { -#ifdef QQ - LEX *lex=(LEX*) record; - *length = lex->sql_query_length; - // *length = strlen(lex->ptr); - return (byte*) lex->sql_query_text; - // return (byte*) lex->ptr; + + DBUG_ENTER("Query_cache::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("info", ("query cache disabled on not in autocommit mode")); + DBUG_RETURN(1); + } + + char *begin = sql; + while(*begin == ' ' || *begin == '\t') begin++; + if ( toupper(begin[0])!='S' || + toupper(begin[1])!='E' || + toupper(begin[2])!='L') + { + DBUG_PRINT("info", ("Not look like SELECT")); + DBUG_RETURN(1); + } + if (thd->temporary_tables != 0 || !thd->safe_to_cache_query ) + { + DBUG_PRINT("info", ("SELECT is non-cachable")); + DBUG_RETURN(1); + } + + STRUCT_LOCK(&structure_guard_mutex); + if (query_cache_size == 0) + { + DBUG_PRINT("info", ("query cache disabled on not in autocommit mode")); + DBUG_RETURN(1); + } + DBUG_PRINT("info", (" sql %u '%s'", query_length, sql)); + Query_cache_block *query_block = 0; + + /* + prepare flags: + most significant bit - CLIENT_LONG_FLAG, + other - charset number (0 no charset convertion) + */ + byte flags = (thd->client_capabilities & CLIENT_LONG_FLAG ? 0x80 : 0); + if (thd->convert_set != 0) + { + flags |= (byte) thd->convert_set->number(); +#ifndef DBUG_OFF + if ( (thd->convert_set->number() & QUERY_CACHE_CHARSET_CONVERT_MASK) != + thd->convert_set->number()) + { + wreck(__LINE__, + "charset number bigger than QUERY_CACHE_CHARSET_CONVERT_MASK"); + } #endif - return 0; + } + + 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("info", ("No query in query hash or no results")); + DBUG_RETURN(1); + } + DBUG_PRINT("info", ("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); + STRUCT_UNLOCK(&structure_guard_mutex); + + Query_cache_query * query = query_block->query(); + Query_cache_block * first_result_block = query->result(); + Query_cache_block * result_block = first_result_block; + if (result_block == 0 || + result_block->type != Query_cache_block::RESULT) + { + DBUG_PRINT("info", ("query found, but no data or data incomplete")); + DBUG_RETURN(1); //no data in query + } + DBUG_PRINT("info", ("Query have result 0x%lx", (ulong)query)); + + //check access; + TABLE_COUNTER_TYPE t = 0; + for(; t < query_block->n_tables; t++) + { + TABLE_LIST table_list; + table_list.next = 0; + table_list.use_index = table_list.ignore_index = 0; + table_list.table = 0; + table_list.grant.grant_table = 0; + table_list.grant.version = table_list.grant.privilege = + table_list.grant.want_privilege = 0; + table_list.outer_join = 0; + table_list.straight = 0; + table_list.updating = 0; + table_list.shared = 0; + + Query_cache_table * table = query_block->table(t)->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("info", + ("probably no SELECT access to %s.%s =>\ + return to normal processing", + table_list.db, table_list.name)); + DBUG_RETURN(1); //no access + } + } + + move_to_query_list_end(query_block); + + hits++; + do + { + DBUG_PRINT("info", ("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(); + net_real_write(&thd->net, result->data(), + result_block->used - + result_block->headers_len() - + ALIGN_SIZE(sizeof(Query_cache_result))); + 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); +} + +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; } -/* At the moment we do not really want to do anything upon delete */ -static void free_cache_entry(void *entry) +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; } -/* Initialization of the SQL cache hash -- should be called during - the bootstrap stage */ -bool sql_cache_init(void) +void Query_cache::invalidate(Query_cache_table::query_cache_table_type type) { - if (query_buff_size) + DBUG_ENTER("Query_cache::invalidate (type)"); + if (query_cache_size > 0) { - VOID(hash_init(&sql_cache, 4096, 0, 0, - cache_key, - (void (*)(void*)) free_cache_entry, - 0)); + STRUCT_LOCK(&structure_guard_mutex); + 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 deletetion of current block + */ + Query_cache_block *next = table_block->next; + + invalidate_table(table_block); + + if (next == table_block) + break; + + table_block = next; + } while (table_block != tables_blocks[type]); + + } + STRUCT_UNLOCK(&structure_guard_mutex); } - return 0; + DBUG_VOID_RETURN; } -/* Clearing the SQL cache hash -- during shutdown */ -void sql_cache_free(void) +void Query_cache::invalidate(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); + + if (table_block == next) + break; + + table_block = next; + } while (table_block != tables_blocks[i]); + } + } + } + STRUCT_UNLOCK(&structure_guard_mutex); + } + DBUG_VOID_RETURN; +} + +void Query_cache::invalidate_by_MyISAM_filename(char * filename) +{ + DBUG_ENTER("Query_cache::invalidate_by_MyISAM_filename"); + if (query_cache_size > 0) + { + STRUCT_LOCK(&structure_guard_mutex); + if (query_cache_size > 0) + { + char key[MAX_DBKEY_LENGTH]; + uint key_length = + Query_cache::filename_2_table_key(key, filename); + + 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; +} +void Query_cache::flush() { - hash_free(&sql_cache); + DBUG_ENTER("Query_cache::flush"); + if (query_cache_size > 0) + { + DUMP(this); + STRUCT_LOCK(&structure_guard_mutex); + flush_cache(); + DUMP(this); + STRUCT_UNLOCK(&structure_guard_mutex); + } + DBUG_VOID_RETURN; } -/* Finds whether the SQL command is already in the cache, at any case - establishes correct LEX structure in the THD (either from - cache or a new one) */ +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; +} -int sql_cache_hit(THD *thd, char *sql, uint length) +void Query_cache::destroy() { -#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::destroy"); + free_cache(1); + pthread_mutex_destroy(&structure_guard_mutex); + initialized = 0; + DBUG_VOID_RETURN; +} + +#ifndef DBUG_OFF - lex_start(thd, (uchar *)sql, length); +void Query_cache::wreck(uint line, const char * message) +{ + DBUG_ENTER("Query_cache::wreck"); + query_cache_size = 0; + 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; +} - if (hash_insert(&sql_cache, (const byte *)ptr)) { - fprintf(stderr, "Out of memory during hash_insert?\n"); +void Query_cache::bins_dump() +{ + DBUG_PRINT("info", ("mem_bin_num=%u, mem_bin_steps=%u", + mem_bin_num, mem_bin_steps)); + DBUG_PRINT("info", ("-------------------------")); + DBUG_PRINT("info", (" size idx step")); + DBUG_PRINT("info", ("-------------------------")); + uint i = 0; + for(; i < mem_bin_steps; i++) + { + DBUG_PRINT("info", ("%10lu %3d %10lu", steps[i].size, steps[i].idx, + steps[i].increment)); + } + DBUG_PRINT("info", ("-------------------------")); + DBUG_PRINT("info", (" size num")); + DBUG_PRINT("info", ("-------------------------")); + i = 0; + for(; i < mem_bin_num; i++) + { + DBUG_PRINT("info", ("%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("info", ("\\-- %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 ); } - 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"); + } + DBUG_PRINT("info", ("-------------------------")); +} + +void Query_cache::cache_dump() +{ + DBUG_PRINT("info", ("-------------------------------------")); + DBUG_PRINT("info", (" length used t nt")); + DBUG_PRINT("info", ("-------------------------------------")); + Query_cache_block * i = first_block; + do + { + DBUG_PRINT("info", + ("%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("info", ("-------------------------------------")); +} +void Query_cache::queries_dump() +{ + DBUG_PRINT("info", ("------------------")); + DBUG_PRINT("info", (" QUERIES")); + DBUG_PRINT("info", ("------------------")); + if (queries_blocks != 0) + { + Query_cache_block * i = queries_blocks; + do + { + uint len; + char * str = (char*) Query_cache_query::cache_key((byte*) i, &len, 0); + byte flags = (byte) str[len-1]; + str[len-1] = 0; //safe only under structure_guard_mutex locked + DBUG_PRINT("info", ("%u (%u,%u) %s",len, + ((flags & QUERY_CACHE_CLIENT_LONG_FLAG_MASK)?1:0), + (flags & QUERY_CACHE_CHARSET_CONVERT_MASK), str)); + str[len-1] = (char)flags; + DBUG_PRINT("info", ("-b- 0x%lx 0x%lx 0x%lx 0x%lx 0x%lx", (ulong)i, + (ulong)i->next, (ulong)i->prev, (ulong)i->pnext, + (ulong)i->pprev)); + TABLE_COUNTER_TYPE t = 0; + for (; t < i->n_tables; t++) + { + Query_cache_table * table = i->table(t)->parent; + DBUG_PRINT("info", ("-t- '%s' '%s'", table->db(), table->table())); + } + Query_cache_query * header = i->query(); + if (header->result()) + { + Query_cache_block * result_block = header->result(); + Query_cache_block * result_beg = result_block; + do + { + DBUG_PRINT("info", ("-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 ); + } + i = i->next; + } while ( i != queries_blocks ); + } + else + { + DBUG_PRINT("info", ("no queries in list")); + } + DBUG_PRINT("info", ("------------------")); +} + +void Query_cache::tables_dump() +{ + DBUG_PRINT("info", ("--------------------")); + DBUG_PRINT("info", ("TABLES")); + DBUG_PRINT("info", ("--------------------")); + int i = 0; + for(; i < (int) Query_cache_table::TYPES_NUMBER; i++) + { + DBUG_PRINT("info", ("--- 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("info", ("'%s' '%s'", table->db(), table->table())); + table_block = table_block->next; + } while ( table_block != tables_blocks[i]); } - return 0; + else + DBUG_PRINT("info", ("no tables in list")); } + DBUG_PRINT("info", ("--------------------")); +} + #endif - return 1; + +/***************************************************************************** + * 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() +{ + DBUG_ENTER("Query_cache::init_cache"); + if (!initialized) + { + DBUG_PRINT("info", ("first time init")); + init(); + } + ulong additional_data_size = 0, + approx_additional_data_size = sizeof(Query_cache) + + sizeof(gptr)*(def_query_hash_size+def_query_hash_size); + + ulong max_mem_bin_size = 0; + if (query_cache_size < approx_additional_data_size) + { + additional_data_size = 0; + approx_additional_data_size = 0; + make_disabled(); + } + else + { + query_cache_size -= approx_additional_data_size; + + //Count memory bins number. + + /* + The idea is inherited from GNU malloc with some add-ons. + Free memory blocks are stored in bins according to their sizes. + The bins are stored in size-descending order. + The bins are approximately logarithmically separated by size. + + Opposite to GNU malloc bin splitting is not fixed but calculated + depending on cache size. Spliting calculating stored in cache and + then used for bin finding. + + 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 + */ + max_mem_bin_size = + query_cache_size >> QUERY_CACHE_MEM_BIN_FIRST_STEP_PWR2; + uint mem_bin_count = 1 + QUERY_CACHE_MEM_BIN_PARTS_INC; + mem_bin_count = (uint) (mem_bin_count * QUERY_CACHE_MEM_BIN_PARTS_MUL); + mem_bin_num = 1; + mem_bin_steps = 1; + ulong mem_bin_size = max_mem_bin_size >> QUERY_CACHE_MEM_BIN_STEP_PWR2; + ulong 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); + } + ulong 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 * + 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) + { + additional_data_size = 0; + approx_additional_data_size = 0; + make_disabled(); + } + else + query_cache_size -= additional_data_size; + + STRUCT_LOCK(&structure_guard_mutex); + if (query_cache_size <= min_allocation_unit) + { + DBUG_PRINT("info", + (" query_cache_size <= min_allocation_unit => cache disabled")); + make_disabled(); + } + else + { + if ( (cache = (byte *) + my_malloc_lock(query_cache_size+additional_data_size, MYF(0)))==0 ) + { + DBUG_PRINT("warning", + ("can't allocate query cache memory => cache disabled")); + make_disabled(); + } + else + { + DBUG_PRINT("info", + ("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; + + free_memory = query_cache_size; + + /* prepare bins */ + + bins[0].init(max_mem_bin_size); + steps[0].init(max_mem_bin_size,0,0); + uint mem_bin_count = 1 + QUERY_CACHE_MEM_BIN_PARTS_INC; + mem_bin_count = (uint) (mem_bin_count * QUERY_CACHE_MEM_BIN_PARTS_MUL); + uint num = 1; + ulong mem_bin_size = max_mem_bin_size >> QUERY_CACHE_MEM_BIN_STEP_PWR2; + uint step = 1; + while (mem_bin_size > min_allocation_unit) + { + ulong inc = (steps[step-1].size - mem_bin_size) / mem_bin_count; + + unsigned long size = mem_bin_size; + uint i = mem_bin_count; + for(; i > 0; i--) + { + bins[num+i-1].init(size); + size += inc; + } + num += mem_bin_count; + steps[step].init(mem_bin_size, num-1, inc); + 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); + } + ulong 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; + for(; i > 0; i--) + { + bins[num+i-1].init(size); + size += inc; + } + + insert_into_free_memory_list(first_block); + + DUMP(this); + + VOID(hash_init(&queries,def_query_hash_size, 0, 0, + Query_cache_query::cache_key, + (void (*)(void*))Query_cache_query::free_cache, + 0)); + VOID(hash_init(&tables,def_table_hash_size, 0, 0, + Query_cache_table::cache_key, + (void (*)(void*))Query_cache_table::free_cache, + 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); +} + +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; + 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 + + bins[0].free_blocks->destroy(); /* all cache memory must be + in one this block */ + DBUG_PRINT("info", ("free memory %lu (should be %lu)", + free_memory , query_cache_size)); + free_memory = 0; + + my_free((gptr)cache, MYF(MY_ALLOW_ZERO_PTR)); + first_block = 0; + bins = 0; + cache = 0; + query_cache_size = 0; + hash_free(&queries); + hash_free(&tables); + queries_in_cache = 0; + if (!destruction) + STRUCT_UNLOCK(&structure_guard_mutex); + } + DBUG_VOID_RETURN; +} + +/***************************************************************************** + * free block data + *****************************************************************************/ + +void Query_cache::flush_cache() +{ + while(queries_blocks != 0){ + BLOCK_LOCK_WR(queries_blocks); + free_query(queries_blocks); + } +} + +my_bool Query_cache::free_old_query() +{ + DBUG_ENTER("Query_cache::free_old_query"); + if (queries_blocks == 0) + { + DBUG_RETURN(0); + } + /* + try_lock_writing used to prevent clinch 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 *i = queries_blocks; + do + { + Query_cache_query * header = i->query(); + if (header->result() != 0 && + header->result()->type == Query_cache_block::RESULT && + i->query()->try_lock_writing()) + { + query_block = i; + break; + } + i = i->next; + } while ( i != queries_blocks ); + } + + if (query_block != 0) + { + free_query(query_block); + DBUG_RETURN(1); + } + else + DBUG_RETURN(0); +} + +/* query_block must be lock_writing() */ +void Query_cache::free_query(Query_cache_block * query_block) +{ + DBUG_ENTER("Query_cache::free_query"); + DBUG_PRINT("info", ("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) + { + query->writer()->query_cache_query = 0; + query->writer(0); + } + double_linked_list_exclude(query_block, queries_blocks); + TABLE_COUNTER_TYPE i = 0; + for(; i < query_block->n_tables; i++) + { + unlink_table(query_block->table(i)); + } + Query_cache_block *result_block = query->result(); + 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) +{ + DBUG_ENTER("Query_cache::write_block_data"); + ulong all_headers_len = ALIGN_SIZE(sizeof(Query_cache_block)) + + ntab*ALIGN_SIZE(sizeof(Query_cache_block_table)) + + header_len; + DBUG_PRINT("info", ("data: %ld, header: %ld, all header: %ld", + data_len, header_len, all_headers_len)); + ulong len = data_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); +} + +my_bool +Query_cache::append_result_data(Query_cache_block * &result, + ulong data_len, gptr data, + Query_cache_block * query_block, + Query_cache_block * first_data_block) +{ + DBUG_ENTER("Query_cache::uppend_result_data"); + DBUG_PRINT("info", ("append %lu bytes to 0x%lx query")); + if (query_block->query()->add(data_len) > query_cache_limit) + { + DBUG_PRINT("info", ("size limit reached %lu > %lu", + query_block->query()->length(), + query_cache_limit)); + result=0; + DBUG_RETURN(0); + } + if (first_data_block == 0) + { + DBUG_PRINT("info", ("allocated first result data block 0x%xl", data_len)); + /* + STRUCT_UNLOCK(&structure_guard_mutex); will be done by + query_cache.append_result_data if success; + */ + DBUG_RETURN(write_result_data(result, data_len, data, query_block, + Query_cache_block::RES_BEG)); + } + else + { + result = first_data_block; + } + Query_cache_block * last_block = first_data_block->prev; + + DBUG_PRINT("info", ("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; + + //write 'tail' of data, that can't be appended to last block + + //try join blocks if physicaly 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("info", ("allocate new block for %lu bytes", + data_len-last_block_free_space)); + Query_cache_block *new_block = 0; + /* + STRUCT_UNLOCK(&structure_guard_mutex); will be done by + query_cache.append_result_data + */ + 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 filed 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); + + // append last block (if it is possible) + if (last_block_free_space > 0) + { + ulong to_copy = min(data_len,last_block_free_space); + DBUG_PRINT("info", ("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("info", ("data_len %lu",data_len)); + + //reserve block(s) for filling + 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("info", ("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 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("info", ("success %d", (int) success)); + DBUG_RETURN(success); +} + +my_bool Query_cache::allocate_data_chain(Query_cache_block * &result_block, + ulong data_len, + Query_cache_block * query_block) +{ + DBUG_ENTER("Query_cache::allocate_data_chain"); + + ulong all_headers_len = ALIGN_SIZE(sizeof(Query_cache_block)) + + ALIGN_SIZE(sizeof(Query_cache_result)); + ulong len = data_len + all_headers_len; + DBUG_PRINT("info", ("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) + { + result_block->n_tables = 0; + result_block->used = 0; + result_block->type = Query_cache_block::RES_INCOMPLETE; + result_block->next = result_block->prev = result_block; + Query_cache_result * header = result_block->result(); + header->parent(query_block); + + Query_cache_block * next_block = 0; + if (result_block->length < len) + { + /* + allocated less memory then we need (no big memory blocks) => + to be continue + */ + Query_cache_block * next_block; + if ((success = allocate_data_chain(next_block, + len - result_block->length, + query_block))) + double_linked_list_join(result_block, next_block); + } + if (success) + { + result_block->used = min(len, result_block->length); + + DBUG_PRINT("info", ("Block len %lu used %lu", + result_block->length, result_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 + *****************************************************************************/ + +void Query_cache::invalidate_table(TABLE_LIST *table_list) +{ + if (table_list->table != 0) + invalidate_table(table_list->table); + else + { + char key[MAX_DBKEY_LENGTH], *key_ptr; + uint key_length; + Query_cache_block *table_block; + key_length=(uint) (strmov(strmov(key,table_list->db)+1, + table_list->real_name) -key)+ 1; + key_ptr = key; + + // we dont store temporary tables => no key_length+=4 ... + if ((table_block = (Query_cache_block*) + hash_search(&tables,key_ptr,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. + */ + 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) +{ + DBUG_PRINT("info", ("register tables block 0x%lx, n %d, header %x", + (ulong)block, (int) tables, + (int)ALIGN_SIZE(sizeof(Query_cache_block)) )); + + TABLE_COUNTER_TYPE n = 0; + TABLE_LIST * i = tables_used; + for(; i != 0; i=i->next, n++) + { + DBUG_PRINT("info", + ("table %s, db %s, openinfo at 0x%lx, keylen %u, key at 0x%lx", + i->real_name, i->db, (ulong) i->table, + i->table->key_length, + (ulong) i->table->table_cache_key)); + Query_cache_block_table * block_table = block->table(n); + block_table->n=n; + if (!insert_table(i->table->key_length, + i->table->table_cache_key, block_table, + Query_cache_table::type_convertion(i->table->db_type))) + break; + if (i->table->db_type == DB_TYPE_MRG_MYISAM) + { + ha_myisammrg * handler = (ha_myisammrg *)i->table->file; + MYRG_INFO *file = handler->myrg_info(); + MYRG_TABLE *table = file->open_tables; + for(;table != file->end_table ; table++) + { + char key[MAX_DBKEY_LENGTH]; + uint key_length = + Query_cache::filename_2_table_key(key, table->table->filename); + n++; + Query_cache_block_table * block_table = block->table(n); + block_table->n=n; + if (!insert_table(key_length, key, block_table, + Query_cache_table::type_convertion(DB_TYPE_MYISAM))) + goto err; + } + } + } +err: + if (i != 0) + { + n--; + DBUG_PRINT("info", ("filed at table %d", (int)n)); + TABLE_COUNTER_TYPE idx = 0; + for(i = tables_used; + idx < n; + idx++) + { + unlink_table(block->table(n)); + } + } + return(i == 0); +} + +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("info", ("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("info", ("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("info", ("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("info", ("Can't insert table to hash")); + // write_block_data return locked block + free_memory_block(table_block); + DBUG_RETURN(0); + } + char * db = header->db(); + header->table(db + strlen(db) + 1); + } + + Query_cache_block_table * list_root = table_block->table(0); + node->next = list_root->next; list_root->next = node; + node->next->prev = node; node->prev = list_root; + node->parent = table_block->table(); + DBUG_RETURN(1); +} + +void Query_cache::unlink_table(Query_cache_block_table * node) +{ + node->prev->next = node->next; node->next->prev = node->prev; + Query_cache_block_table * neighbour = node->next; + if (neighbour->next == neighbour) + { + // list is empty (neighbour 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("info", ("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("info", ("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);}; + + Query_cache_block *block = get_free_block(len, not_less, min); + while( block == 0 && free_old_query()) + { + block = get_free_block(len, not_less, min); + } + + if (block!=0) + { + 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) +{ + DBUG_ENTER("Query_cache::get_free_block"); + Query_cache_block *block = 0, *first = 0; + DBUG_PRINT("info",("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("info",("try bins whith more bigger blocks")); + //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("info",("try smaller blocks")); + if (first != 0 && first->length > min) + block = first; + else + { + uint i = start + 1; + while( i < mem_bin_num && bins[i].number == 0) + i++; + if (i < mem_bin_num && bins[i].number > 0 && + bins[i].free_blocks->prev->length >= min) + block = bins[i].free_blocks->prev; + } + } + if (block != 0) + exclude_from_free_memory_list(block); + + DBUG_PRINT("info",("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("info",("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("info", ("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) +{ + DBUG_ENTER("Query_cache::join_free_blocks"); + DBUG_PRINT("info", + ("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); + + Query_cache_block * second_block = first_block->pnext; + // may be was not free block + second_block->type = Query_cache_block::FREE;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) +{ + DBUG_ENTER("Query_cache::append_next_free_block"); + DBUG_PRINT("enter", ("block 0x%lx, add_size %lu", (ulong) block, + add_size)); + Query_cache_block * next_block = block->pnext; + if (next_block->is_free()) + { + exclude_from_free_memory_list(next_block); + ulong old_len = block->length; + + 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); + } + else + { + DBUG_PRINT("exit", ("block was not appended")); + 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("info",("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]); + bins[idx].number++; + DBUG_PRINT("info",("insert block 0x%lx, bin[%d] 0x%lx", + (ulong) free_block, idx, + (ulong) &(bins[idx]))); + DBUG_VOID_RETURN; +} + +uint Query_cache::find_bin(ulong size) +{ + DBUG_ENTER("Query_cache::find_bin"); + //begin small blocks to big (small blocks frequently asked) + int i = mem_bin_steps - 1; + for(; i > 0 && steps[i-1].size < size; i--); + if (i == 0) + { + // first bin not subordinate of common rules + DBUG_PRINT("info", ("first bin (# 0), size %lu",size)); + DBUG_RETURN(0); + } + uint bin = steps[i].idx - (uint)((size - steps[i].size)/steps[i].increment); + DBUG_PRINT("info", ("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 + frequently than big one + */ + + 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("info", ("inserted into empty list")); + } + else + { + Query_cache_block *point = list; + if (point->length >= new_block->length) + { + point = point->prev; + list = new_block; + } + else + { + 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("info", ("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("info", ("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 cachable return numder tables in query + (query whithout tables tot chached) +*/ +TABLE_COUNTER_TYPE Query_cache::is_cachable(THD *thd, + uint query_len, char *query, + LEX *lex, TABLE_LIST* tables_used) +{ + TABLE_COUNTER_TYPE tables = 0; + DBUG_ENTER("Query_cache::is_cachable"); + 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) + { + DBUG_PRINT("info", ("options %lx %lx, type %u", + OPTION_TO_QUERY_CACHE, + lex->select->options, + (int) thd->query_cache_type)); + my_bool has_transactions = 0; + TABLE_LIST * i = tables_used; + for(; i != 0; i=i->next) + { + tables++; + DBUG_PRINT("info", ("table %s, db %s, type %u", i->real_name, + i->db, i->table->db_type)); + has_transactions = (has_transactions || + i->table->file->has_transactions()); + if (i->table->db_type == DB_TYPE_MRG_ISAM) + { + DBUG_PRINT("info", ("select not cachable: used MRG_ISAM table(s)")); + DBUG_RETURN(0); + } + if (i->table->db_type == DB_TYPE_MRG_MYISAM) + { + ha_myisammrg * handler = (ha_myisammrg *)i->table->file; + MYRG_INFO *file = handler->myrg_info(); + MYRG_TABLE *table = file->open_tables; + for(;table != file->end_table ; table++) + { + tables++; + DBUG_PRINT("loop", (" + 1 table (mrg_MyISAM)")); + } + } + } + + if ((thd->options & (OPTION_NOT_AUTO_COMMIT | OPTION_BEGIN)) && + has_transactions) + { + DBUG_PRINT("info", ("not in autocommin mode")); + DBUG_RETURN(0); + } + else + { + DBUG_PRINT("info", ("select have %d tables", tables)); + DBUG_RETURN(tables); + } + } + DBUG_PRINT("info", + ("not interest query: %d or not cachable, \ +options %lx %lx, type %u", + (int) lex->sql_command, + OPTION_TO_QUERY_CACHE, + lex->select->options, + (int) thd->query_cache_type)); + DBUG_RETURN(0); +} + +/***************************************************************************** + * packing + *****************************************************************************/ + +void Query_cache::pack_cache() +{ + DBUG_ENTER("Query_cache::pack_cache"); + STRUCT_LOCK(&structure_guard_mutex); + DUMP(this); + byte * border = 0; + Query_cache_block * before = 0; + ulong gap = 0; + my_bool ok = 1; + Query_cache_block * i = first_block; + do + { + ok = move_by_type(border, before, gap, i); + i = i->pnext; + } while (ok && i != 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 * i) +{ + DBUG_ENTER("Query_cache::move_by_type"); + my_bool ok = 1; + switch(i->type) + { + case Query_cache_block::FREE: + { + DBUG_PRINT("info", ("block 0x%lx FREE", (ulong) i)); + if (border == 0) + { + border = (byte *) i; + before = i->pprev; + DBUG_PRINT("info", ("gap begining here")); + } + exclude_from_free_memory_list(i); + gap +=i->length; + i->pprev->pnext=i->pnext; + i->pnext->pprev=i->pprev; + i->destroy(); + DBUG_PRINT("info", ("added to gap (%lu)", gap)); + break; + } + case Query_cache_block::TABLE: + { + DBUG_PRINT("info", ("block 0x%lx TABLE", (ulong) i)); + if (border == 0) break; + ulong len = i->length, + used = i->used; + Query_cache_block_table * list_root = i->table(0); + Query_cache_block_table + *tprev = list_root->prev, + *tnext = list_root->next; + Query_cache_block + *prev = i->prev, + *next = i->next, + *pprev = i->pprev, + *pnext = i->pnext, + *new_block =(Query_cache_block *) border; + char * data = (char*)i->data(); + hash_delete(&tables, (byte *)i); + i->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(i, new_block, next, prev, pnext, pprev); + if (tables_blocks[new_block->table()->type()] == i) + { + 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; + hash_insert(&tables, (const byte *)new_block); + DBUG_PRINT("info", ("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("info", ("block 0x%lx QUERY", (ulong) i)); + if (border == 0) break; + BLOCK_LOCK_WR(i); + ulong len = i->length, + used = i->used; + TABLE_COUNTER_TYPE n_tables = i->n_tables; + Query_cache_block + *prev = i->prev, + *next = i->next, + *pprev = i->pprev, + *pnext = i->pnext, + *new_block =(Query_cache_block *) border; + char * data = (char*)i->data(); + Query_cache_block * first_result_block = ((Query_cache_query *) + i->data())->result(); + hash_delete(&queries, (byte *)i); + memcpy((char*)new_block->table(0), (char*)i->table(0), + n_tables * ALIGN_SIZE(sizeof(Query_cache_block_table))); + i->query()->unlock_n_destroy(); + i->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(i, new_block, next, prev, pnext, pprev); + if (queries_blocks == i) + queries_blocks = new_block; + TABLE_COUNTER_TYPE j=0; + for(; 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("info", ("after cicle 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; + } + hash_insert(&queries, (const byte *)new_block); + DBUG_PRINT("info", ("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("info", ("block 0x%lx RES* (%d)", (ulong) i, (int)i->type)); + if (border == 0) break; + Query_cache_block + *query_block = i->result()->parent(), + *next = i->next, + *prev = i->prev; + Query_cache_block::block_type type = i->type; + BLOCK_LOCK_WR(query_block); + ulong len = i->length, + used = i->used; + Query_cache_block + *pprev = i->pprev, + *pnext = i->pnext, + *new_block =(Query_cache_block *) border; + char * data = (char*)i->data(); + i->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(i, new_block, next, prev, pnext, pprev); + new_block->result()->parent(query_block); + Query_cache_query * query = query_block->query(); + if (query->result() == i) + 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("info", ("moved %lu bytes to 0x%lx, new gap at 0x%lx", + len, (ulong) new_block, (ulong) border)); + break; + } + default: + DBUG_PRINT("error", ("unexpectet block type %d, block 0x%lx", + (int)i->type, (ulong) i)); + 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) +{ + //TODO + DBUG_ENTER("Query_cache::join_results"); + my_bool has_moving = 0; + Query_cache_block *query_block = 0; + STRUCT_LOCK(&structure_guard_mutex); + if (queries_blocks != 0) + { + Query_cache_block *i = queries_blocks; + do + { + Query_cache_query * header = i->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(i); + 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("info", ("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(i); + 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(i); + } + } + i = i->next; + } while ( i != queries_blocks ); + } + STRUCT_UNLOCK(&structure_guard_mutex); + DBUG_RETURN(has_moving); +} + +uint Query_cache::filename_2_table_key (char * key, char *filename) +{ + DBUG_ENTER("Query_cache::filename_2_table_key"); + char tablename[FN_REFLEN]; + char dbbuff[FN_REFLEN]; + char *dbname; + Query_cache_block *table_block; + fn_format(tablename,filename,"","",3); + + int path_len = (strrchr(filename, '/') - filename); + strncpy(dbbuff, filename, path_len); + dbbuff[path_len] = '\0'; + dbname = strrchr(dbbuff, '/') + 1; + + DBUG_PRINT("info", ("table %s.%s", dbname, tablename)); + + DBUG_RETURN( (uint) (strmov(strmov(key, dbname) + 1, + tablename) -key) + 1); } diff --git a/sql/sql_cache.h b/sql/sql_cache.h new file mode 100644 index 00000000000..0b786fb1560 --- /dev/null +++ b/sql/sql_cache.h @@ -0,0 +1,402 @@ +/* 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); + static void free_cache(void *entry); +}; + + +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))); + } + + static byte * cache_key(const byte *record, uint *length, + my_bool not_used); + static void free_cache(void *entry); +}; + + +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" { + 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(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 +{ + protected: + 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 + uint mem_bin_num, mem_bin_steps; // See at init_cache & find_bin + + /* options */ + ulong query_cache_size, query_cache_limit, + min_allocation_unit, min_result_data_size; + uint def_query_hash_size, def_table_hash_size; + + /* statistics */ +public: + ulong free_memory, queries_in_cache, hits, inserts, refused; +protected: + my_bool initialized; + + /* + Locked when searched or changed global query or + tables lists or hashes. When operate inside + query structure locked own query block mutex + LOCK SEQUENCE (to prevent clinch): + 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; + + HASH queries, tables; + + /* 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, char *filename); + + /* Following function work properly only when structure_guard_mutex 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, + Query_cache_block * first_data_block); + 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 cachable return numder tables in query + (query without tables not cached) + */ + TABLE_COUNTER_TYPE is_cachable(THD *thd, uint 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(char * filename); + + /* Remove all queries from cache */ + void flush(); + + /* Join result in cache in 1 block (if result length > join_limit) */ + 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..0b596c01f9d 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..e036da982f0 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 { @@ -292,7 +294,10 @@ public: 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; + //type of query cache processing + byte query_cache_type; 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 @@ -324,10 +329,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..5c43cacd85b 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; @@ -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..ef06eaf9145 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -1164,7 +1164,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; @@ -1420,6 +1423,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 +1431,8 @@ mysql_execute_command(void) (ORDER *) select_lex->order_list.first, lex->drop_primary, lex->duplicates, lex->alter_keys_onoff, lex->simple_alter); + query_cache.invalidate(tables); + } 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: @@ -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; } @@ -2982,6 +3004,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(); + options &= ~REFRESH_QUERY_CACHE; //don't flush all cache, just free memory + } + if (options & (REFRESH_TABLES | REFRESH_QUERY_CACHE)) + { + query_cache.flush(); + } if (options & (REFRESH_TABLES | REFRESH_READ_LOCK)) { if ((options & REFRESH_READ_LOCK) && thd) diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 660702d4117..2673c746bbc 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -3159,6 +3159,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()", diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 049fb1c182c..ae450680fbb 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); + } } } @@ -1708,6 +1712,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; // Table is closed + 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;} |