diff options
Diffstat (limited to 'mysys')
-rw-r--r-- | mysys/Makefile.am | 20 | ||||
-rw-r--r-- | mysys/charset.c | 759 | ||||
-rw-r--r-- | mysys/charset2html.c | 129 | ||||
-rw-r--r-- | mysys/default.c | 10 | ||||
-rw-r--r-- | mysys/hash.c | 55 | ||||
-rw-r--r-- | mysys/list.c | 2 | ||||
-rw-r--r-- | mysys/mf_casecnv.c | 297 | ||||
-rw-r--r-- | mysys/mf_dirname.c | 6 | ||||
-rw-r--r-- | mysys/mf_format.c | 6 | ||||
-rw-r--r-- | mysys/mf_iocache2.c | 2 | ||||
-rw-r--r-- | mysys/mf_keycache.c | 2229 | ||||
-rw-r--r-- | mysys/mf_soundex.c | 21 | ||||
-rw-r--r-- | mysys/mf_tempdir.c | 83 | ||||
-rw-r--r-- | mysys/mf_wfile.c | 7 | ||||
-rw-r--r-- | mysys/mulalloc.c | 14 | ||||
-rw-r--r-- | mysys/my_error.c | 4 | ||||
-rw-r--r-- | mysys/my_getwd.c | 3 | ||||
-rw-r--r-- | mysys/my_handler.c | 411 | ||||
-rw-r--r-- | mysys/my_init.c | 5 | ||||
-rw-r--r-- | mysys/my_once.c | 20 | ||||
-rw-r--r-- | mysys/my_symlink.c | 3 | ||||
-rw-r--r-- | mysys/my_vsnprintf.c | 118 | ||||
-rw-r--r-- | mysys/queues.c | 72 | ||||
-rw-r--r-- | mysys/test_charset.c | 41 | ||||
-rw-r--r-- | mysys/test_xml.c | 105 | ||||
-rw-r--r-- | mysys/thr_lock.c | 8 | ||||
-rw-r--r-- | mysys/tree.c | 228 | ||||
-rw-r--r-- | mysys/typelib.c | 4 |
28 files changed, 3182 insertions, 1480 deletions
diff --git a/mysys/Makefile.am b/mysys/Makefile.am index a927fa5e3a0..5b1c859cb2a 100644 --- a/mysys/Makefile.am +++ b/mysys/Makefile.am @@ -17,7 +17,7 @@ MYSQLDATAdir = $(localstatedir) MYSQLSHAREdir = $(pkgdatadir) MYSQLBASEdir= $(prefix) -INCLUDES = @MT_INCLUDES@ -I$(srcdir)/../include -I../include -I.. -I$(srcdir) +INCLUDES = @MT_INCLUDES@ -I$(top_srcdir)/include -I$(srcdir) pkglib_LIBRARIES = libmysys.a LDADD = libmysys.a ../dbug/libdbug.a \ ../strings/libmystrings.a @@ -31,7 +31,7 @@ libmysys_a_SOURCES = my_init.c my_getwd.c mf_getdate.c\ my_pread.c my_write.c \ mf_keycache.c \ mf_iocache.c mf_iocache2.c mf_cache.c mf_tempfile.c \ - my_lock.c mf_brkhant.c my_alarm.c \ + mf_tempdir.c my_lock.c mf_brkhant.c my_alarm.c \ my_malloc.c my_realloc.c my_once.c mulalloc.c \ my_alloc.c safemalloc.c my_new.cc \ my_fopen.c my_fstream.c \ @@ -39,7 +39,7 @@ libmysys_a_SOURCES = my_init.c my_getwd.c mf_getdate.c\ mf_format.c mf_same.c mf_dirname.c mf_fn_ext.c \ my_symlink.c my_symlink2.c \ mf_pack.c mf_unixpath.c mf_strip.c \ - mf_casecnv.c mf_soundex.c mf_wcomp.c mf_wfile.c \ + mf_soundex.c mf_wcomp.c mf_wfile.c \ mf_qsort.c mf_qsort2.c mf_sort.c \ ptr_cmp.c mf_radix.c queues.c \ tree.c list.c hash.c array.c string.c typelib.c \ @@ -49,18 +49,18 @@ libmysys_a_SOURCES = my_init.c my_getwd.c mf_getdate.c\ my_quick.c my_lockmem.c my_static.c \ my_getopt.c my_mkdir.c \ default.c my_compress.c checksum.c raid.cc \ - my_net.c my_semaphore.c my_port.c my_sleep.c \ - my_vsnprintf.c charset.c my_bitmap.c my_bit.c md5.c \ + my_net.c my_semaphore.c my_port.c my_sleep.c \ + charset.c my_bitmap.c my_bit.c md5.c \ my_gethostbyname.c rijndael.c my_aes.c sha1.c \ - my_netware.c + my_handler.c my_netware.c EXTRA_DIST = thr_alarm.c thr_lock.c my_pthread.c my_thr_init.c \ thr_mutex.c thr_rwlock.c libmysys_a_LIBADD = @THREAD_LOBJECTS@ -# test_fn removed 980815 since it not up to date test_dir -noinst_PROGRAMS = @THREAD_LPROGRAMS@ +noinst_PROGRAMS = charset2html @THREAD_LPROGRAMS@ # test_dir_DEPENDENCIES= $(LIBRARIES) # testhash_DEPENDENCIES= $(LIBRARIES) # test_charset_DEPENDENCIES= $(LIBRARIES) +# charset2html_DEPENDENCIES= $(LIBRARIES) EXTRA_PROGRAMS = DEFS = -DDEFAULT_BASEDIR=\"$(prefix)\" \ -DDATADIR="\"$(MYSQLDATAdir)\"" \ @@ -92,6 +92,7 @@ test_vsnprintf$(EXEEXT): my_vsnprintf.c $(LIBRARIES) $(CP) $(srcdir)/my_vsnprintf.c test_vsnprintf.c $(LINK) $(FLAGS) -DMAIN ./test_vsnprintf.c $(LDADD) $(LIBS) $(RM) -f test_vsnprintf.c + test_io_cache$(EXEEXT): mf_iocache.c $(LIBRARIES) $(CP) $(srcdir)/mf_iocache.c test_io_cache.c $(LINK) $(FLAGS) -DMAIN ./test_io_cache.c $(LDADD) $(LIBS) @@ -103,6 +104,9 @@ test_dir$(EXEEXT): test_dir.c $(LIBRARIES) test_charset$(EXEEXT): test_charset.c $(LIBRARIES) $(LINK) $(FLAGS) -DMAIN $(srcdir)/test_charset.c $(LDADD) $(LIBS) +charset2html$(EXEEXT): charset2html.c $(LIBRARIES) + $(LINK) $(FLAGS) -DMAIN $(srcdir)/charset2html.c $(LDADD) $(LIBS) + testhash$(EXEEXT): testhash.c $(LIBRARIES) $(LINK) $(FLAGS) -DMAIN $(srcdir)/testhash.c $(LDADD) $(LIBS) diff --git a/mysys/charset.c b/mysys/charset.c index 235fcb08023..1145a989958 100644 --- a/mysys/charset.c +++ b/mysys/charset.c @@ -19,76 +19,286 @@ #include <m_ctype.h> #include <m_string.h> #include <my_dir.h> +#include <my_xml.h> -typedef struct cs_id_st { - char *name; - uint number; -} CS_ID; -const char *charsets_dir = NULL; -static DYNAMIC_ARRAY cs_info_table; -static CS_ID **available_charsets; -static int charset_initialized=0; +/* -#define MAX_LINE 1024 + The code below implements this functionality: + + - Initializing charset related structures + - Loading dynamic charsets + - Searching for a proper CHARSET_INFO + using charset name, collation name or collatio ID + - Setting server default character set +*/ -#define CTYPE_TABLE_SIZE 257 -#define TO_LOWER_TABLE_SIZE 256 -#define TO_UPPER_TABLE_SIZE 256 -#define SORT_ORDER_TABLE_SIZE 256 -struct simpleconfig_buf_st { - FILE *f; - char buf[MAX_LINE]; - char *p; -}; +static void set_max_sort_char(CHARSET_INFO *cs) +{ + uchar max_char; + uint i; + + if (!cs->sort_order) + return; + + max_char=cs->sort_order[(uchar) cs->max_sort_char]; + for (i= 0; i < 256; i++) + { + if ((uchar) cs->sort_order[i] > max_char) + { + max_char=(uchar) cs->sort_order[i]; + cs->max_sort_char= (char) i; + } + } +} + -static uint num_from_csname(CS_ID **cs, const char *name) +static void simple_cs_init_functions(CHARSET_INFO *cs) { - CS_ID **c; - for (c = cs; *c; ++c) - if (!strcmp((*c)->name, name)) - return (*c)->number; - return 0; /* this mimics find_type() */ + + cs->strnxfrm = my_strnxfrm_simple; + cs->strnncoll = my_strnncoll_simple; + cs->strnncollsp = my_strnncollsp_simple; + cs->like_range = my_like_range_simple; + cs->wildcmp = my_wildcmp_8bit; + cs->mb_wc = my_mb_wc_8bit; + cs->wc_mb = my_wc_mb_8bit; + cs->caseup_str = my_caseup_str_8bit; + cs->casedn_str = my_casedn_str_8bit; + cs->caseup = my_caseup_8bit; + cs->casedn = my_casedn_8bit; + cs->tosort = my_tosort_8bit; + cs->strcasecmp = my_strcasecmp_8bit; + cs->strncasecmp = my_strncasecmp_8bit; + cs->hash_caseup = my_hash_caseup_simple; + cs->hash_sort = my_hash_sort_simple; + cs->snprintf = my_snprintf_8bit; + cs->long10_to_str= my_long10_to_str_8bit; + cs->longlong10_to_str= my_longlong10_to_str_8bit; + cs->fill = my_fill_8bit; + cs->strntol = my_strntol_8bit; + cs->strntoul = my_strntoul_8bit; + cs->strntoll = my_strntoll_8bit; + cs->strntoull = my_strntoull_8bit; + cs->strntod = my_strntod_8bit; + cs->scan = my_scan_8bit; + cs->mbmaxlen = 1; + cs->numchars = my_numchars_8bit; + cs->charpos = my_charpos_8bit; } -static char *name_from_csnum(CS_ID **cs, uint number) + +typedef struct { - CS_ID **c; - if(cs) - for (c = cs; *c; ++c) - if ((*c)->number == number) - return (*c)->name; - return (char*) "?"; /* this mimics find_type() */ + int nchars; + MY_UNI_IDX uidx; +} uni_idx; + +#define PLANE_SIZE 0x100 +#define PLANE_NUM 0x100 +#define PLANE_NUMBER(x) (((x)>>8) % PLANE_NUM) + +static int pcmp(const void * f, const void * s) +{ + const uni_idx *F= (const uni_idx*) f; + const uni_idx *S= (const uni_idx*) s; + int res; + + if (!(res=((S->nchars)-(F->nchars)))) + res=((F->uidx.from)-(S->uidx.to)); + return res; +} + + +static my_bool create_fromuni(CHARSET_INFO *cs) +{ + uni_idx idx[PLANE_NUM]; + int i,n; + + /* Clear plane statistics */ + bzero(idx,sizeof(idx)); + + /* Count number of characters in each plane */ + for (i=0; i< 0x100; i++) + { + uint16 wc=cs->tab_to_uni[i]; + int pl= PLANE_NUMBER(wc); + + if (wc || !i) + { + if (!idx[pl].nchars) + { + idx[pl].uidx.from=wc; + idx[pl].uidx.to=wc; + }else + { + idx[pl].uidx.from=wc<idx[pl].uidx.from?wc:idx[pl].uidx.from; + idx[pl].uidx.to=wc>idx[pl].uidx.to?wc:idx[pl].uidx.to; + } + idx[pl].nchars++; + } + } + + /* Sort planes in descending order */ + qsort(&idx,PLANE_NUM,sizeof(uni_idx),&pcmp); + + for (i=0; i < PLANE_NUM; i++) + { + int ch,numchars; + + /* Skip empty plane */ + if (!idx[i].nchars) + break; + + numchars=idx[i].uidx.to-idx[i].uidx.from+1; + idx[i].uidx.tab=(unsigned char*)my_once_alloc(numchars * + sizeof(*idx[i].uidx.tab), + MYF(MY_WME)); + bzero(idx[i].uidx.tab,numchars*sizeof(*idx[i].uidx.tab)); + + for (ch=1; ch < PLANE_SIZE; ch++) + { + uint16 wc=cs->tab_to_uni[ch]; + if (wc >= idx[i].uidx.from && wc <= idx[i].uidx.to && wc) + { + int ofs= wc - idx[i].uidx.from; + idx[i].uidx.tab[ofs]= ch; + } + } + } + + /* Allocate and fill reverse table for each plane */ + n=i; + cs->tab_from_uni= (MY_UNI_IDX*) my_once_alloc(sizeof(MY_UNI_IDX)*(n+1), + MYF(MY_WME)); + for (i=0; i< n; i++) + cs->tab_from_uni[i]= idx[i].uidx; + + /* Set end-of-list marker */ + bzero(&cs->tab_from_uni[i],sizeof(MY_UNI_IDX)); + return FALSE; } -static my_bool get_word(struct simpleconfig_buf_st *fb, char *buf) + +static void simple_cs_copy_data(CHARSET_INFO *to, CHARSET_INFO *from) { - char *endptr=fb->p; + to->number= from->number ? from->number : to->number; + to->state|= from->state; + + if (from->csname) + to->csname= my_once_strdup(from->csname,MYF(MY_WME)); + + if (from->name) + to->name= my_once_strdup(from->name,MYF(MY_WME)); + + if (from->ctype) + to->ctype= (uchar*) my_once_memdup((char*) from->ctype, + MY_CS_CTYPE_TABLE_SIZE, MYF(MY_WME)); + if (from->to_lower) + to->to_lower= (uchar*) my_once_memdup((char*) from->to_lower, + MY_CS_TO_LOWER_TABLE_SIZE, MYF(MY_WME)); + if (from->to_upper) + to->to_upper= (uchar*) my_once_memdup((char*) from->to_upper, + MY_CS_TO_UPPER_TABLE_SIZE, MYF(MY_WME)); + if (from->sort_order) + { + to->sort_order= (uchar*) my_once_memdup((char*) from->sort_order, + MY_CS_SORT_ORDER_TABLE_SIZE, + MYF(MY_WME)); + set_max_sort_char(to); + } + if (from->tab_to_uni) + { + uint sz= MY_CS_TO_UNI_TABLE_SIZE*sizeof(uint16); + to->tab_to_uni= (uint16*) my_once_memdup((char*)from->tab_to_uni, sz, + MYF(MY_WME)); + create_fromuni(to); + } +} - for (;;) + +static my_bool simple_cs_is_full(CHARSET_INFO *cs) +{ + return ((cs->csname && cs->tab_to_uni && cs->ctype && cs->to_upper && + cs->to_lower) && + (cs->number && cs->name && cs->sort_order)); +} + + +static int add_collation(CHARSET_INFO *cs) +{ + if (cs->name && (cs->number || (cs->number=get_charset_number(cs->name)))) { - while (isspace(*endptr)) - ++endptr; - if (*endptr && *endptr != '#') /* Not comment */ - break; /* Found something */ - if ((fgets(fb->buf, sizeof(fb->buf), fb->f)) == NULL) - return TRUE; /* end of file */ - endptr = fb->buf; + if (!all_charsets[cs->number]) + { + if (!(all_charsets[cs->number]= + (CHARSET_INFO*) my_once_alloc(sizeof(CHARSET_INFO),MYF(0)))) + return MY_XML_ERROR; + bzero((void*)all_charsets[cs->number],sizeof(CHARSET_INFO)); + } + + if (!(all_charsets[cs->number]->state & MY_CS_COMPILED)) + { + simple_cs_copy_data(all_charsets[cs->number],cs); + if (simple_cs_is_full(all_charsets[cs->number])) + { + simple_cs_init_functions(all_charsets[cs->number]); + all_charsets[cs->number]->state |= MY_CS_LOADED; + } + } + cs->number= 0; + cs->name= NULL; + cs->state= 0; + cs->sort_order= NULL; + cs->state= 0; } + return MY_XML_OK; +} - while (!isspace(*endptr)) - *buf++= *endptr++; - *buf=0; - fb->p = endptr; +#define MAX_BUF 1024*16 +#define MY_CHARSET_INDEX "Index.xml" + +const char *charsets_dir= NULL; +static int charset_initialized=0; + + +static my_bool my_read_charset_file(const char *filename, myf myflags) +{ + char *buf; + int fd; + uint len; + + if (!(buf= (char *)my_malloc(MAX_BUF,myflags))) + return FALSE; + + if ((fd=my_open(filename,O_RDONLY,myflags)) < 0) + { + my_free(buf,myflags); + return TRUE; + } + len=read(fd,buf,MAX_BUF); + my_close(fd,myflags); + + if (my_parse_charset_xml(buf,len,add_collation)) + { +#ifdef NOT_YET + printf("ERROR at line %d pos %d '%s'\n", + my_xml_error_lineno(&p)+1, + my_xml_error_pos(&p), + my_xml_error_string(&p)); +#endif + } + + my_free(buf, myflags); return FALSE; } char *get_charsets_dir(char *buf) { - const char *sharedir = SHAREDIR; + const char *sharedir= SHAREDIR; DBUG_ENTER("get_charsets_dir"); if (charsets_dir != NULL) @@ -103,269 +313,181 @@ char *get_charsets_dir(char *buf) NullS); } convert_dirname(buf,buf,NullS); - DBUG_PRINT("info",("charsets dir='%s'", buf)); + DBUG_PRINT("info",("charsets dir: '%s'", buf)); DBUG_RETURN(strend(buf)); } +CHARSET_INFO *all_charsets[256]; +CHARSET_INFO *default_charset_info = &my_charset_latin1; +CHARSET_INFO *system_charset_info = &my_charset_latin1; + +#define MY_ADD_CHARSET(x) all_charsets[(x)->number]=(x) -static my_bool read_charset_index(CS_ID ***charsets, myf myflags) + +static my_bool init_compiled_charsets(myf flags __attribute__((unused))) { - struct simpleconfig_buf_st fb; - char buf[MAX_LINE], num_buf[MAX_LINE]; - DYNAMIC_ARRAY cs; - CS_ID *csid; + CHARSET_INFO *cs; - strmov(get_charsets_dir(buf), "Index"); + MY_ADD_CHARSET(&my_charset_latin1); + + MY_ADD_CHARSET(&my_charset_bin); - if ((fb.f = my_fopen(buf, O_RDONLY, myflags)) == NULL) - return TRUE; - fb.buf[0] = '\0'; - fb.p = fb.buf; +#ifdef HAVE_CHARSET_big5 + MY_ADD_CHARSET(&my_charset_big5); +#endif - if (my_init_dynamic_array(&cs, sizeof(CS_ID *), 32, 32)) - return TRUE; +#ifdef HAVE_CHARSET_czech + MY_ADD_CHARSET(&my_charset_czech); +#endif - while (!get_word(&fb, buf) && !get_word(&fb, num_buf)) - { - uint csnum; - uint length; +#ifdef HAVE_CHARSET_euc_kr + MY_ADD_CHARSET(&my_charset_euc_kr); +#endif - if (!(csnum = atoi(num_buf))) - { - /* corrupt Index file */ - my_fclose(fb.f,myflags); - return TRUE; - } +#ifdef HAVE_CHARSET_gb2312 + MY_ADD_CHARSET(&my_charset_gb2312); +#endif - if (!(csid = (CS_ID*) my_once_alloc(sizeof(CS_ID), myflags)) || - !(csid->name= - (char*) my_once_alloc(length= (uint) strlen(buf)+1, myflags))) - { - my_fclose(fb.f,myflags); - return TRUE; - } - memcpy(csid->name,buf,length); - csid->number = csnum; +#ifdef HAVE_CHARSET_gbk + MY_ADD_CHARSET(&my_charset_gbk); +#endif - insert_dynamic(&cs, (gptr) &csid); - } - my_fclose(fb.f,myflags); +#ifdef HAVE_CHARSET_latin1_de + MY_ADD_CHARSET(&my_charset_latin1_de); +#endif +#ifdef HAVE_CHARSET_sjis + MY_ADD_CHARSET(&my_charset_sjis); +#endif - if (!(*charsets = - (CS_ID **) my_once_alloc((cs.elements + 1) * sizeof(CS_ID *), myflags))) - return TRUE; - /* unwarranted chumminess with dynamic_array implementation? */ - memcpy((byte *) *charsets, cs.buffer, cs.elements * sizeof(CS_ID *)); - (*charsets)[cs.elements] = NULL; - delete_dynamic(&cs); +#ifdef HAVE_CHARSET_tis620 + MY_ADD_CHARSET(&my_charset_tis620); +#endif + +#ifdef HAVE_CHARSET_ucs2 + MY_ADD_CHARSET(&my_charset_ucs2); +#endif + +#ifdef HAVE_CHARSET_ujis + MY_ADD_CHARSET(&my_charset_ujis); +#endif + +#ifdef HAVE_CHARSET_utf8 + MY_ADD_CHARSET(&my_charset_utf8); +#endif +#ifdef HAVE_CHARSET_win1250ch + MY_ADD_CHARSET(&my_charset_win1250ch); +#endif + + /* Copy compiled charsets */ + for (cs=compiled_charsets; cs->name; cs++) + { + all_charsets[cs->number]=cs; + } + return FALSE; } - #ifdef __NETWARE__ my_bool STDCALL init_available_charsets(myf myflags) #else static my_bool init_available_charsets(myf myflags) #endif { - my_bool error=0; + char fname[FN_REFLEN]; + my_bool error=FALSE; /* We have to use charset_initialized to not lock on THR_LOCK_charset inside get_internal_charset... - */ + */ if (!charset_initialized) { - /* - To make things thread safe we are not allowing other threads to interfere - while we may changing the cs_info_table - */ + CHARSET_INFO **cs; + /* + To make things thread safe we are not allowing other threads to interfere + while we may changing the cs_info_table + */ pthread_mutex_lock(&THR_LOCK_charset); - if (!cs_info_table.buffer) /* If not initialized */ + + bzero(&all_charsets,sizeof(all_charsets)); + init_compiled_charsets(myflags); + + /* Copy compiled charsets */ + for (cs=all_charsets; cs < all_charsets+255 ; cs++) { - my_init_dynamic_array(&cs_info_table, sizeof(CHARSET_INFO*), 16, 8); - error = read_charset_index(&available_charsets, myflags); + if (*cs) + set_max_sort_char(*cs); } + + strmov(get_charsets_dir(fname), MY_CHARSET_INDEX); + error= my_read_charset_file(fname,myflags); charset_initialized=1; pthread_mutex_unlock(&THR_LOCK_charset); } - if(!available_charsets || !available_charsets[0]) - error = TRUE; return error; } void free_charsets(void) { - delete_dynamic(&cs_info_table); charset_initialized=0; } -static my_bool fill_array(uchar *array, int sz, struct simpleconfig_buf_st *fb) -{ - char buf[MAX_LINE]; - while (sz--) - { - if (get_word(fb, buf)) - { - DBUG_PRINT("error",("get_word failed, expecting %d more words", sz + 1)); - return 1; - } - *array++ = (uchar) strtol(buf, NULL, 16); - } - return 0; -} - - -static void get_charset_conf_name(uint cs_number, char *buf) +static void get_charset_conf_name(const char *cs_name, char *buf) { - strxmov(get_charsets_dir(buf), - name_from_csnum(available_charsets, cs_number), ".conf", NullS); -} - - -static my_bool read_charset_file(uint cs_number, CHARSET_INFO *set, - myf myflags) -{ - struct simpleconfig_buf_st fb; - char buf[FN_REFLEN]; - my_bool result; - DBUG_ENTER("read_charset_file"); - DBUG_PRINT("enter",("cs_number: %d", cs_number)); - - if (cs_number <= 0) - DBUG_RETURN(TRUE); - - get_charset_conf_name(cs_number, buf); - DBUG_PRINT("info",("file name: %s", buf)); - - if ((fb.f = my_fopen(buf, O_RDONLY, myflags)) == NULL) - DBUG_RETURN(TRUE); - - fb.buf[0] = '\0'; /* Init for get_word */ - fb.p = fb.buf; - - result=FALSE; - if (fill_array(set->ctype, CTYPE_TABLE_SIZE, &fb) || - fill_array(set->to_lower, TO_LOWER_TABLE_SIZE, &fb) || - fill_array(set->to_upper, TO_UPPER_TABLE_SIZE, &fb) || - fill_array(set->sort_order, SORT_ORDER_TABLE_SIZE, &fb)) - result=TRUE; - - my_fclose(fb.f, MYF(0)); - DBUG_RETURN(result); + strxmov(get_charsets_dir(buf), cs_name, ".conf", NullS); } uint get_charset_number(const char *charset_name) { - uint number=compiled_charset_number(charset_name); - if (number) - return number; + CHARSET_INFO **cs; if (init_available_charsets(MYF(0))) /* If it isn't initialized */ return 0; - return num_from_csname(available_charsets, charset_name); + + for (cs= all_charsets; cs < all_charsets+255; ++cs) + { + if ( cs[0] && cs[0]->name && !strcmp(cs[0]->name, charset_name)) + return cs[0]->number; + } + return 0; /* this mimics find_type() */ } + const char *get_charset_name(uint charset_number) { - const char *name=compiled_charset_name(charset_number); - if (*name != '?') - return name; + CHARSET_INFO *cs; if (init_available_charsets(MYF(0))) /* If it isn't initialized */ return "?"; - return name_from_csnum(available_charsets, charset_number); -} - - -static CHARSET_INFO *find_charset(CHARSET_INFO **table, uint cs_number, - size_t tablesz) -{ - uint i; - for (i = 0; i < tablesz; ++i) - if (table[i]->number == cs_number) - return table[i]; - return NULL; -} -static CHARSET_INFO *find_charset_by_name(CHARSET_INFO **table, - const char *name, size_t tablesz) -{ - uint i; - for (i = 0; i < tablesz; ++i) - if (!strcmp(table[i]->name,name)) - return table[i]; - return NULL; + cs=all_charsets[charset_number]; + if (cs && (cs->number == charset_number) && cs->name ) + return (char*) cs->name; + + return (char*) "?"; /* this mimics find_type() */ } -static CHARSET_INFO *add_charset(uint cs_number, const char *cs_name, myf flags) -{ - CHARSET_INFO tmp_cs,*cs; - uchar tmp_ctype[CTYPE_TABLE_SIZE]; - uchar tmp_to_lower[TO_LOWER_TABLE_SIZE]; - uchar tmp_to_upper[TO_UPPER_TABLE_SIZE]; - uchar tmp_sort_order[SORT_ORDER_TABLE_SIZE]; - - /* Don't allocate memory if we are not sure we can find the char set */ - cs= &tmp_cs; - bzero((char*) cs, sizeof(*cs)); - cs->ctype=tmp_ctype; - cs->to_lower=tmp_to_lower; - cs->to_upper=tmp_to_upper; - cs->sort_order=tmp_sort_order; - if (read_charset_file(cs_number, cs, flags)) - return NULL; - - cs = (CHARSET_INFO*) my_once_alloc(sizeof(CHARSET_INFO), - MYF(MY_WME)); - *cs=tmp_cs; - cs->name = (char *) my_once_alloc((uint) strlen(cs_name)+1, MYF(MY_WME)); - cs->ctype = (uchar*) my_once_alloc(CTYPE_TABLE_SIZE, MYF(MY_WME)); - cs->to_lower = (uchar*) my_once_alloc(TO_LOWER_TABLE_SIZE, MYF(MY_WME)); - cs->to_upper = (uchar*) my_once_alloc(TO_UPPER_TABLE_SIZE, MYF(MY_WME)); - cs->sort_order=(uchar*) my_once_alloc(SORT_ORDER_TABLE_SIZE, MYF(MY_WME)); - cs->number = cs_number; - memcpy((char*) cs->name, (char*) cs_name, strlen(cs_name) + 1); - memcpy((char*) cs->ctype, (char*) tmp_ctype, sizeof(tmp_ctype)); - memcpy((char*) cs->to_lower, (char*) tmp_to_lower, sizeof(tmp_to_lower)); - memcpy((char*) cs->to_upper, (char*) tmp_to_upper, sizeof(tmp_to_upper)); - memcpy((char*) cs->sort_order, (char*) tmp_sort_order, - sizeof(tmp_sort_order)); - insert_dynamic(&cs_info_table, (gptr) &cs); - return cs; -} static CHARSET_INFO *get_internal_charset(uint cs_number, myf flags) { + char buf[FN_REFLEN]; CHARSET_INFO *cs; /* To make things thread safe we are not allowing other threads to interfere while we may changing the cs_info_table */ pthread_mutex_lock(&THR_LOCK_charset); - if (!(cs = find_charset((CHARSET_INFO**) cs_info_table.buffer, cs_number, - cs_info_table.elements))) - if (!(cs = find_compiled_charset(cs_number))) - cs=add_charset(cs_number, get_charset_name(cs_number), flags); - pthread_mutex_unlock(&THR_LOCK_charset); - return cs; -} + cs= all_charsets[cs_number]; -static CHARSET_INFO *get_internal_charset_by_name(const char *name, myf flags) -{ - CHARSET_INFO *cs; - /* - To make things thread safe we are not allowing other threads to interfere - while we may changing the cs_info_table - */ - pthread_mutex_lock(&THR_LOCK_charset); - if (!(cs = find_charset_by_name((CHARSET_INFO**) cs_info_table.buffer, name, - cs_info_table.elements))) - if (!(cs = find_compiled_charset_by_name(name))) - cs=add_charset(get_charset_number(name), name, flags); + if (cs && !(cs->state & (MY_CS_COMPILED | MY_CS_LOADED))) + { + strxmov(get_charsets_dir(buf), cs->csname, ".xml", NullS); + my_read_charset_file(buf,flags); + cs= (cs->state & MY_CS_LOADED) ? cs : NULL; + } pthread_mutex_unlock(&THR_LOCK_charset); return cs; } @@ -375,12 +497,16 @@ CHARSET_INFO *get_charset(uint cs_number, myf flags) { CHARSET_INFO *cs; (void) init_available_charsets(MYF(0)); /* If it isn't initialized */ + + if (!cs_number) + return NULL; + cs=get_internal_charset(cs_number, flags); if (!cs && (flags & MY_WME)) { char index_file[FN_REFLEN], cs_string[23]; - strmov(get_charsets_dir(index_file), "Index"); + strmov(get_charsets_dir(index_file),MY_CHARSET_INDEX); cs_string[0]='#'; int10_to_str(cs_number, cs_string+1, 10); my_error(EE_UNKNOWN_CHARSET, MYF(ME_BELL), cs_string, index_file); @@ -393,48 +519,85 @@ my_bool set_default_charset(uint cs, myf flags) CHARSET_INFO *new_charset; DBUG_ENTER("set_default_charset"); DBUG_PRINT("enter",("character set: %d",(int) cs)); - new_charset = get_charset(cs, flags); + + new_charset= get_charset(cs, flags); if (!new_charset) { DBUG_PRINT("error",("Couldn't set default character set")); DBUG_RETURN(TRUE); /* error */ } - default_charset_info = new_charset; + default_charset_info= new_charset; + system_charset_info= new_charset; + DBUG_RETURN(FALSE); } CHARSET_INFO *get_charset_by_name(const char *cs_name, myf flags) { + uint cs_number; CHARSET_INFO *cs; (void) init_available_charsets(MYF(0)); /* If it isn't initialized */ - cs=get_internal_charset_by_name(cs_name, flags); + + cs_number=get_charset_number(cs_name); + cs= cs_number ? get_internal_charset(cs_number,flags) : NULL; if (!cs && (flags & MY_WME)) { char index_file[FN_REFLEN]; - strmov(get_charsets_dir(index_file), "Index"); + strmov(get_charsets_dir(index_file),MY_CHARSET_INDEX); my_error(EE_UNKNOWN_CHARSET, MYF(ME_BELL), cs_name, index_file); } return cs; } + +CHARSET_INFO *get_charset_by_csname(const char *cs_name, myf flags) +{ + CHARSET_INFO *cs=NULL; + CHARSET_INFO **css; + (void) init_available_charsets(MYF(0)); /* If it isn't initialized */ + + for (css= all_charsets; css < all_charsets+255; ++css) + { + if ( css[0] && (css[0]->state & MY_CS_PRIMARY) && + css[0]->csname && !strcmp(css[0]->csname, cs_name)) + { + cs= css[0]->number ? get_internal_charset(css[0]->number,flags) : NULL; + break; + } + } + + if (!cs && (flags & MY_WME)) + { + char index_file[FN_REFLEN]; + strmov(get_charsets_dir(index_file),MY_CHARSET_INDEX); + my_error(EE_UNKNOWN_CHARSET, MYF(ME_BELL), cs_name, index_file); + } + + return cs; +} + + my_bool set_default_charset_by_name(const char *cs_name, myf flags) { CHARSET_INFO *new_charset; DBUG_ENTER("set_default_charset_by_name"); DBUG_PRINT("enter",("character set: %s", cs_name)); - new_charset = get_charset_by_name(cs_name, flags); + + new_charset= get_charset_by_name(cs_name, flags); if (!new_charset) { DBUG_PRINT("error",("Couldn't set default character set")); DBUG_RETURN(TRUE); /* error */ } - default_charset_info = new_charset; + default_charset_info= new_charset; + system_charset_info= new_charset; DBUG_RETURN(FALSE); } + /* Only append name if it doesn't exist from before */ static my_bool charset_in_string(const char *name, DYNAMIC_STRING *s) @@ -446,13 +609,14 @@ static my_bool charset_in_string(const char *name, DYNAMIC_STRING *s) if (! pos[length] || pos[length] == ' ') return TRUE; /* Already existed */ } - return FALSE; } + static void charset_append(DYNAMIC_STRING *s, const char *name) { - if (!charset_in_string(name, s)) { + if (!charset_in_string(name, s)) + { dynstr_append(s, name); dynstr_append(s, " "); } @@ -462,7 +626,7 @@ static void charset_append(DYNAMIC_STRING *s, const char *name) /* Returns a dynamically-allocated string listing the character sets requested. The caller is responsible for freeing the memory. */ -char * list_charsets(myf want_flags) +char *list_charsets(myf want_flags) { DYNAMIC_STRING s; char *p; @@ -470,92 +634,57 @@ char * list_charsets(myf want_flags) (void)init_available_charsets(MYF(0)); init_dynamic_string(&s, NullS, 256, 1024); - if (want_flags & MY_COMPILED_SETS) + if (want_flags & MY_CS_COMPILED) { - CHARSET_INFO *cs; - for (cs = compiled_charsets; cs->number > 0; cs++) + CHARSET_INFO **cs; + for (cs= all_charsets; cs < all_charsets+255; cs++) { - dynstr_append(&s, cs->name); - dynstr_append(&s, " "); + if (cs[0]) + { + dynstr_append(&s, cs[0]->name); + dynstr_append(&s, " "); + } } } - if (want_flags & MY_CONFIG_SETS) + if (want_flags & MY_CS_CONFIG) { - CS_ID **c; + CHARSET_INFO **cs; char buf[FN_REFLEN]; MY_STAT status; - if((c=available_charsets)) - for (; *c; ++c) - { - if (charset_in_string((*c)->name, &s)) - continue; - get_charset_conf_name((*c)->number, buf); - if (!my_stat(buf, &status, MYF(0))) - continue; /* conf file doesn't exist */ - dynstr_append(&s, (*c)->name); - dynstr_append(&s, " "); - } + for (cs=all_charsets; cs < all_charsets+255; cs++) + { + if (!cs[0] || !cs[0]->name || charset_in_string(cs[0]->name, &s)) + continue; + get_charset_conf_name(cs[0]->name, buf); + if (!my_stat(buf, &status, MYF(0))) + continue; /* conf file doesn't exist */ + dynstr_append(&s, cs[0]->name); + dynstr_append(&s, " "); + } } - if (want_flags & MY_INDEX_SETS) + if (want_flags & (MY_CS_INDEX|MY_CS_LOADED)) { - CS_ID **c; - for (c = available_charsets; *c; ++c) - charset_append(&s, (*c)->name); + CHARSET_INFO **cs; + for (cs= all_charsets; cs < all_charsets + 255; cs++) + if (cs[0] && cs[0]->name && (cs[0]->state & want_flags) ) + charset_append(&s, cs[0]->name); } - - if (want_flags & MY_LOADED_SETS) + + if (s.length) + { + s.str[s.length - 1]= '\0'; /* chop trailing space */ + p= my_strdup(s.str, MYF(MY_WME)); + } + else { - uint i; - for (i = 0; i < cs_info_table.elements; i++) - charset_append(&s, - dynamic_element(&cs_info_table, i, CHARSET_INFO *)->name); + p= my_strdup("", MYF(MY_WME)); } - s.str[s.length - 1] = '\0'; /* chop trailing space */ - p = my_strdup(s.str, MYF(MY_WME)); dynstr_free(&s); - + return p; } -/**************************************************************************** -* Code for debugging. -****************************************************************************/ - -static void _print_array(uint8 *data, uint size) -{ - uint i; - for (i = 0; i < size; ++i) - { - if (i == 0 || i % 16 == size % 16) printf(" "); - printf(" %02x", data[i]); - if ((i+1) % 16 == size % 16) printf("\n"); - } -} - -/* _print_csinfo is called from test_charset.c */ -void _print_csinfo(CHARSET_INFO *cs) -{ - printf("%s #%d\n", cs->name, cs->number); - printf("ctype:\n"); _print_array(cs->ctype, 257); - printf("to_lower:\n"); _print_array(cs->to_lower, 256); - printf("to_upper:\n"); _print_array(cs->to_upper, 256); - printf("sort_order:\n"); _print_array(cs->sort_order, 256); - printf("collate: %3s (%d, %p, %p, %p, %p, %p)\n", - cs->strxfrm_multiply ? "yes" : "no", - cs->strxfrm_multiply, - cs->strcoll, - cs->strxfrm, - cs->strnncoll, - cs->strnxfrm, - cs->like_range); - printf("multi-byte: %3s (%d, %p, %p, %p)\n", - cs->mbmaxlen ? "yes" : "no", - cs->mbmaxlen, - cs->ismbchar, - cs->ismbhead, - cs->mbcharlen); -} diff --git a/mysys/charset2html.c b/mysys/charset2html.c new file mode 100644 index 00000000000..0d6450a8116 --- /dev/null +++ b/mysys/charset2html.c @@ -0,0 +1,129 @@ +/* Copyright (C) 2000 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + Written by Alexander Barkov to check what + a charset is in your favorite web browser +*/ + +#include <my_global.h> +#include <m_ctype.h> +#include <my_sys.h> +#include <mysql_version.h> + +#include <stdio.h> + +typedef struct char_info_st +{ + int cod; + int srt; + int uni; + int low; + int upp; + int ctp; +} MY_CH; + +static int chcmp(const void *vf, const void *vs) +{ + const MY_CH *f=vf; + const MY_CH *s=vs; + + return f->srt-s->srt ? f->srt-s->srt : f->uni-s->uni; +} + +static void print_cs(CHARSET_INFO *cs) +{ + uint i; + int srt; + int clr=0; + MY_CH ch[256]; + + printf("<HTML>\n"); + printf("<HEAD>\n"); + printf("</HEAD>\n"); + printf("<BODY><PRE>\n"); + printf("Charset %s\n",cs->name); + + printf("<TABLE>\n"); + printf("<TR><TH>Code<TH>Uni<TH>Sort<TH>Ctype<TH>Ch<TH>Lo<TH>Up</TR>"); + + for (i=0; i<256; i++) + { + ch[i].cod=i; + ch[i].srt=cs->sort_order[i]; + ch[i].uni=cs->tab_to_uni[i]; + ch[i].low=cs->tab_to_uni[cs->to_lower[i]]; + ch[i].upp=cs->tab_to_uni[cs->to_upper[i]]; + ch[i].ctp=cs->ctype[i+1]; + } + + qsort(ch,256,sizeof(MY_CH),&chcmp); + srt=ch[0].srt; + + for (i=0; i<256; i++) + { + clr = (srt!=ch[i].srt) ? !clr : clr; + + printf("<TR bgcolor=#%s>",clr ? "DDDDDD" : "EEEE99"); + printf("<TD>%02X",ch[i].cod); + printf("<TD>%04X",ch[i].uni); + printf("<TD>%d",ch[i].srt); + + printf("<TD>%s%s%s%s%s%s%s%s", + ch[i].ctp & _U ? "U" : "", + ch[i].ctp & _L ? "L" : "", + ch[i].ctp & _NMR ? "N" : "", + ch[i].ctp & _SPC ? "S" : "", + ch[i].ctp & _PNT ? "P" : "", + ch[i].ctp & _CTR ? "C" : "", + ch[i].ctp & _B ? "B" : "", + ch[i].ctp & _X ? "X" : ""); + + printf("<TD>&#%d;",ch[i].uni); + printf("<TD>&#%d;",ch[i].low); + printf("<TD>&#%d;",ch[i].upp); + printf("</TR>\n"); + srt=ch[i].srt; + } + printf("</TABLE>\n"); + printf("</PRE></BODY>\n"); + printf("</HTML>\n"); +} + + +int main(int argc, char **argv) { + const char *the_set = MYSQL_CHARSET; + int argcnt = 1; + + my_init(); + + if (argc > argcnt && argv[argcnt][0] == '-' && argv[argcnt][1] == '#') + DBUG_PUSH(argv[argcnt++]+2); + + if (argc > argcnt) + the_set = argv[argcnt++]; + + if (argc > argcnt) + charsets_dir = argv[argcnt++]; + + if (set_default_charset_by_name(the_set, MYF(MY_WME))) + return 1; + + print_cs(default_charset_info); + + return 0; +} diff --git a/mysys/default.c b/mysys/default.c index c354e1744bc..06557f73d06 100644 --- a/mysys/default.c +++ b/mysys/default.c @@ -249,7 +249,7 @@ static my_bool search_default_file(DYNAMIC_ARRAY *args, MEM_ROOT *alloc, { line++; /* Ignore comment and empty lines */ - for (ptr=buff ; isspace(*ptr) ; ptr++ ) ; + for (ptr=buff ; my_isspace(system_charset_info,*ptr) ; ptr++ ) ; if (*ptr == '#' || *ptr == ';' || !*ptr) continue; if (*ptr == '[') /* Group name */ @@ -262,7 +262,7 @@ static my_bool search_default_file(DYNAMIC_ARRAY *args, MEM_ROOT *alloc, name,line); goto err; } - for ( ; isspace(end[-1]) ; end--) ; /* Remove end space */ + for ( ; my_isspace(system_charset_info,end[-1]) ; end--) ;/* Remove end space */ end[0]=0; read_values=find_type(ptr,group,3) > 0; continue; @@ -278,7 +278,7 @@ static my_bool search_default_file(DYNAMIC_ARRAY *args, MEM_ROOT *alloc, continue; if (!(end=value=strchr(ptr,'='))) end=strend(ptr); /* Option without argument */ - for ( ; isspace(end[-1]) ; end--) ; + for ( ; my_isspace(system_charset_info,end[-1]) ; end--) ; if (!value) { if (!(tmp=alloc_root(alloc,(uint) (end-ptr)+3))) @@ -291,9 +291,9 @@ static my_bool search_default_file(DYNAMIC_ARRAY *args, MEM_ROOT *alloc, { /* Remove pre- and end space */ char *value_end; - for (value++ ; isspace(*value); value++) ; + for (value++ ; my_isspace(system_charset_info,*value); value++) ; value_end=strend(value); - for ( ; isspace(value_end[-1]) ; value_end--) ; + for ( ; my_isspace(system_charset_info,value_end[-1]) ; value_end--) ; if (value_end < value) /* Empty string */ value_end=value; if (!(tmp=alloc_root(alloc,(uint) (end-ptr)+3 + diff --git a/mysys/hash.c b/mysys/hash.c index 3afd31a079b..7707e7019c9 100644 --- a/mysys/hash.c +++ b/mysys/hash.c @@ -31,14 +31,15 @@ static uint hash_mask(uint hashnr,uint buffmax,uint maxlength); static void movelink(HASH_LINK *array,uint pos,uint next_link,uint newlink); -static uint calc_hashnr(const byte *key,uint length); -static uint calc_hashnr_caseup(const byte *key,uint length); +static uint calc_hashnr(CHARSET_INFO *cs,const byte *key,uint length); static int hashcmp(HASH *hash,HASH_LINK *pos,const byte *key,uint length); -my_bool _hash_init(HASH *hash,uint size,uint key_offset,uint key_length, - hash_get_key get_key, - void (*free_element)(void*),uint flags CALLER_INFO_PROTO) +my_bool +_hash_init(HASH *hash,CHARSET_INFO *charset, + uint size,uint key_offset,uint key_length, + hash_get_key get_key, + void (*free_element)(void*),uint flags CALLER_INFO_PROTO) { DBUG_ENTER("hash_init"); DBUG_PRINT("enter",("hash: %lx size: %d",hash,size)); @@ -56,8 +57,9 @@ my_bool _hash_init(HASH *hash,uint size,uint key_offset,uint key_length, hash->get_key=get_key; hash->free=free_element; hash->flags=flags; + hash->charset=charset; if (flags & HASH_CASE_INSENSITIVE) - hash->calc_hashnr=calc_hashnr_caseup; + hash->calc_hashnr=charset->hash_caseup; else hash->calc_hashnr=calc_hashnr; DBUG_RETURN(0); @@ -109,14 +111,16 @@ static uint hash_rec_mask(HASH *hash,HASH_LINK *pos,uint buffmax, { uint length; byte *key= (byte*) hash_key(hash,pos->data,&length,0); - return hash_mask((*hash->calc_hashnr)(key,length),buffmax,maxlength); + return hash_mask((*hash->calc_hashnr)(hash->charset,key,length), + buffmax,maxlength); } #ifndef NEW_HASH_FUNCTION /* Calc hashvalue for a key */ -static uint calc_hashnr(const byte *key,uint length) +static uint calc_hashnr(CHARSET_INFO *cs __attribute__((unused)), + const byte *key,uint length) { register uint nr=1, nr2=4; while (length--) @@ -127,19 +131,6 @@ static uint calc_hashnr(const byte *key,uint length) return((uint) nr); } - /* Calc hashvalue for a key, case indepenently */ - -static uint calc_hashnr_caseup(const byte *key,uint length) -{ - register uint nr=1, nr2=4; - while (length--) - { - nr^= (((nr & 63)+nr2)*((uint) (uchar) toupper(*key++)))+ (nr << 8); - nr2+=3; - } - return((uint) nr); -} - #else /* @@ -155,7 +146,7 @@ static uint calc_hashnr_caseup(const byte *key,uint length) * This works well on both numbers and strings. */ -uint calc_hashnr(const byte *key, uint len) +uint calc_hashnr(CHARSET_INFO *cs, const byte *key, uint len) { const byte *end=key+len; uint hash; @@ -167,18 +158,6 @@ uint calc_hashnr(const byte *key, uint len) return (hash); } -uint calc_hashnr_caseup(const byte *key, uint len) -{ - const byte *end=key+len; - uint hash; - for (hash = 0; key < end; key++) - { - hash *= 16777619; - hash ^= (uint) (uchar) toupper(*key); - } - return (hash); -} - #endif @@ -189,7 +168,7 @@ unsigned int rec_hashnr(HASH *hash,const byte *record) { uint length; byte *key= (byte*) hash_key(hash,record,&length,0); - return (*hash->calc_hashnr)(key,length); + return (*hash->calc_hashnr)(hash->charset,key,length); } @@ -205,7 +184,7 @@ gptr hash_search(HASH *hash,const byte *key,uint length) flag=1; if (hash->records) { - idx=hash_mask((*hash->calc_hashnr)(key,length ? length : + idx=hash_mask((*hash->calc_hashnr)(hash->charset,key,length ? length : hash->key_length), hash->blength,hash->records); do @@ -278,7 +257,7 @@ static int hashcmp(HASH *hash,HASH_LINK *pos,const byte *key,uint length) byte *rec_key= (byte*) hash_key(hash,pos->data,&rec_keylength,1); return (length && length != rec_keylength) || (hash->flags & HASH_CASE_INSENSITIVE ? - my_casecmp(rec_key,key,rec_keylength) : + my_strncasecmp(hash->charset, rec_key,key,rec_keylength) : memcmp(rec_key,key,rec_keylength)); } @@ -518,7 +497,7 @@ my_bool hash_update(HASH *hash,byte *record,byte *old_key,uint old_key_length) /* Search after record with key */ - idx=hash_mask((*hash->calc_hashnr)(old_key,(old_key_length ? + idx=hash_mask((*hash->calc_hashnr)(hash->charset, old_key,(old_key_length ? old_key_length : hash->key_length)), blength,records); diff --git a/mysys/list.c b/mysys/list.c index ac9e1b979a0..17028e8e183 100644 --- a/mysys/list.c +++ b/mysys/list.c @@ -55,7 +55,7 @@ LIST *list_delete(LIST *root, LIST *element) } -void list_free(LIST *root, pbool free_data) +void list_free(LIST *root, uint free_data) { LIST *next; while (root) diff --git a/mysys/mf_casecnv.c b/mysys/mf_casecnv.c deleted file mode 100644 index c037cbd0f16..00000000000 --- a/mysys/mf_casecnv.c +++ /dev/null @@ -1,297 +0,0 @@ -/* Copyright (C) 2000 MySQL AB - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ - -/* - Functions to convert to lover_case and to upper_case in scandinavia. - - case_sort converts a character string to a representaion that can - be compared by strcmp to find with is alfabetical bigger. - (lower- and uppercase letters is compared as the same) -*/ - -#include "mysys_priv.h" -#include <m_ctype.h> -#ifndef SCO -#include <m_string.h> -#endif - -/* - Upcase string - - SYNOPSIS - str IN/OUT String to upcase - - RETURN VALUE - none - DESCRIPTION - Function changes input parameter so all chars it consist from - are replaced with matching one in upper case. - String should be writable with exception read-only empty string - constant is handled correctly. -*/ - - -void caseup_str(my_string str) -{ -#ifdef USE_MB - if (use_mb(default_charset_info)) - { - register uint32 l; - register char *end=str+strlen(str); - while (*str) - { - if ((l=my_ismbchar(default_charset_info, str,end))) str+=l; - else *str=toupper(*str),++str; - } - } - else -#endif - while (*str!=0) /* iterate till the end of string */ - { - *str= toupper(*str); - str++; - } -} /* caseup_str */ - - -/* - Downcase string - - SYNOPSIS - str IN/OUT String to downcase - - RETURN VALUE - none - DESCRIPTION - Function changes input parameter so all chars it consist from - are replaced with matching one in lower case. - String should be writable with exception read-only empty string - constant is handled correctly. -*/ - - -void casedn_str(my_string str) -{ -#ifdef USE_MB - if (use_mb(default_charset_info)) - { - register uint32 l; - register char *end=str+strlen(str); - while (*str) - { - if ((l=my_ismbchar(default_charset_info, str,end))) str+=l; - else *str=tolower(*str),++str; - } - } - else -#endif - while (*str!=0) /* iterate till the end of string */ - { - *str= tolower(*str); - str++; - } -} /* casedn_str */ - - - /* to uppercase */ - -void caseup(my_string str, uint length) -{ -#ifdef USE_MB - if (use_mb(default_charset_info)) - { - register uint32 l; - register char *end=str+length; - while (str<end) - { - if ((l=my_ismbchar(default_charset_info, str,end))) str+=l; - else *str=toupper(*str),++str; - } - } - else -#endif - for ( ; length>0 ; length--, str++) - *str= toupper(*str); -} /* caseup */ - - /* to lowercase */ - -void casedn(my_string str, uint length) -{ -#ifdef USE_MB - if (use_mb(default_charset_info)) - { - register uint32 l; - register char *end=str+length; - while (str<end) - { - if ((l=my_ismbchar(default_charset_info, str,end))) str+=l; - else *str=tolower(*str),++str; - } - } - else -#endif - for ( ; length>0 ; length--, str++) - *str= tolower(*str); -} /* casedn */ - - /* to sort-string that can be compared to get text in order */ - -void case_sort(my_string str, uint length) -{ - for ( ; length>0 ; length--, str++) - *str= (char) my_sort_order[(uchar) *str]; -} /* case_sort */ - - /* find string in another with no case_sensivity */ - -/* ToDo: This function should be modified to support multibyte charset. - However it is not used untill 3.23.5. - Wei He (hewei@mail.ied.ac.cn) -*/ - -my_string my_strcasestr(const char *str, const char *search) -{ - uchar *i,*j,*pos; - - pos=(uchar*) str; -skipp: - while (*pos != '\0') - { - if (toupper((uchar) *pos++) == toupper((uchar) *search)) - { - i=(uchar*) pos; j=(uchar*) search+1; - while (*j) - if (toupper(*i++) != toupper(*j++)) goto skipp; - return ((char*) pos-1); - } - } - return ((my_string) 0); -} /* strcstr */ - - - /* compare strings without regarding to case */ - -int my_strcasecmp(const char *s, const char *t) -{ -#ifdef USE_MB - if (use_mb(default_charset_info)) - { - register uint32 l; - register const char *end=s+strlen(s); - while (s<end) - { - if ((l=my_ismbchar(default_charset_info, s,end))) - { - while (l--) - if (*s++ != *t++) return 1; - } - else if (my_ismbhead(default_charset_info, *t)) return 1; - else if (toupper((uchar) *s++) != toupper((uchar) *t++)) return 1; - } - return *t; - } - else -#endif - { - while (toupper((uchar) *s) == toupper((uchar) *t++)) - if (!*s++) return 0; - return ((int) toupper((uchar) s[0]) - (int) toupper((uchar) t[-1])); - } -} - - -int my_casecmp(const char *s, const char *t, uint len) -{ -#ifdef USE_MB - if (use_mb(default_charset_info)) - { - register uint32 l; - register const char *end=s+len; - while (s<end) - { - if ((l=my_ismbchar(default_charset_info, s,end))) - { - while (l--) - if (*s++ != *t++) return 1; - } - else if (my_ismbhead(default_charset_info, *t)) return 1; - else if (toupper((uchar) *s++) != toupper((uchar) *t++)) return 1; - } - return 0; - } - else -#endif - { - while (len-- != 0 && toupper(*s++) == toupper(*t++)) ; - return (int) len+1; - } -} - - -int my_strsortcmp(const char *s, const char *t) -{ -#ifdef USE_STRCOLL - if (use_strcoll(default_charset_info)) - return my_strcoll(default_charset_info, (uchar *)s, (uchar *)t); - else -#endif - { - while (my_sort_order[(uchar) *s] == my_sort_order[(uchar) *t++]) - if (!*s++) return 0; - return ((int) my_sort_order[(uchar) s[0]] - - (int) my_sort_order[(uchar) t[-1]]); - } -} - -int my_sortcmp(const char *s, const char *t, uint len) -{ -#ifdef USE_STRCOLL - if (use_strcoll(default_charset_info)) - return my_strnncoll(default_charset_info, - (uchar *)s, len, (uchar *)t, len); - else -#endif - { - while (len--) - { - if (my_sort_order[(uchar) *s++] != my_sort_order[(uchar) *t++]) - return ((int) my_sort_order[(uchar) s[-1]] - - (int) my_sort_order[(uchar) t[-1]]); - } - return 0; - } -} - -int my_sortncmp(const char *s, uint s_len, const char *t, uint t_len) -{ -#ifdef USE_STRCOLL - if (use_strcoll(default_charset_info)) - return my_strnncoll(default_charset_info, - (uchar *)s, s_len, (uchar *)t, t_len); - else -#endif - { - uint len= min(s_len,t_len); - while (len--) - { - if (my_sort_order[(uchar) *s++] != my_sort_order[(uchar) *t++]) - return ((int) my_sort_order[(uchar) s[-1]] - - (int) my_sort_order[(uchar) t[-1]]); - } - return (int) (s_len - t_len); - } -} diff --git a/mysys/mf_dirname.c b/mysys/mf_dirname.c index 6523abcdb12..320917c8f3e 100644 --- a/mysys/mf_dirname.c +++ b/mysys/mf_dirname.c @@ -108,11 +108,5 @@ char *convert_dirname(char *to, const char *from, const char *from_end) *to++=FN_LIBCHAR; *to=0; } -#ifdef FN_UPPER_CASE - caseup_str(to_org); -#endif -#ifdef FN_LOWER_CASE - casedn_str(to_org); -#endif return to; /* Pointer to end of dir */ } /* convert_dirname */ diff --git a/mysys/mf_format.c b/mysys/mf_format.c index ab1904da162..e68f7df9d83 100644 --- a/mysys/mf_format.c +++ b/mysys/mf_format.c @@ -91,12 +91,6 @@ my_string fn_format(my_string to, const char *name, const char *dir, name=buff; } pos=strmake(strmov(to,dev),name,length); -#ifdef FN_UPPER_CASE - caseup_str(to); -#endif -#ifdef FN_LOWER_CASE - casedn_str(to); -#endif (void) strmov(pos,ext); /* Don't convert extension */ } /* diff --git a/mysys/mf_iocache2.c b/mysys/mf_iocache2.c index 344b7ac2251..8a7dfc7be09 100644 --- a/mysys/mf_iocache2.c +++ b/mysys/mf_iocache2.c @@ -267,7 +267,7 @@ uint my_b_vprintf(IO_CACHE *info, const char* fmt, va_list args) /* Found one '%' */ } /* Skipp if max size is used (to be compatible with printf) */ - while (isdigit(*fmt) || *fmt == '.' || *fmt == '-') + while (my_isdigit(system_charset_info, *fmt) || *fmt == '.' || *fmt == '-') fmt++; if (*fmt == 's') /* String parameter */ { diff --git a/mysys/mf_keycache.c b/mysys/mf_keycache.c index 6a037f13f05..9d45ec8d539 100644 --- a/mysys/mf_keycache.c +++ b/mysys/mf_keycache.c @@ -15,855 +15,1856 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* - This functions is handle keyblock cacheing for NISAM, MISAM and PISAM - databases. - One cache can handle many files. Every different blocksize has it owns - set of buffers that are allocated from block_mem. + These functions are to handle keyblock cacheing + for NISAM, MISAM and PISAM databases. + One cache can handle many files. + It must contain buffers of the same blocksize. init_key_cache() should be used to init cache handler. - */ +*/ #include "mysys_priv.h" #include "my_static.h" #include <m_string.h> #include <errno.h> +#include <assert.h> +#include <stdarg.h> + +/* + Some compilation flags have been added specifically for this module + to control the following: + - not to let a thread to yield the control when reading directly + from key cache, which might improve performance in many cases; + to enable this add: + #define SERIALIZED_READ_FROM_CACHE + - to set an upper bound for number of threads simultaneously + using the key cache; this setting helps to determine an optimal + size for hash table and improve performance when the number of + blocks in the key cache much less than the number of threads + accessing it; + to set this number equal to <N> add + #define MAX_THREADS <N> + - to substitute calls of pthread_cond_wait for calls of + pthread_cond_timedwait (wait with timeout set up); + this setting should be used only when you want to trap a deadlock + situation, which theoretically should not happen; + to set timeout equal to <T> seconds add + #define KEYCACHE_TIMEOUT <T> + - to enable the module traps and to send debug information from + key cache module to a special debug log add: + #define KEYCACHE_DEBUG + the name of this debug log file <LOG NAME> can be set through: + #define KEYCACHE_DEBUG_LOG <LOG NAME> + if the name is not defined, it's set by default; + if the KEYCACHE_DEBUG flag is not set up and we are in a debug + mode, i.e. when ! defined(DBUG_OFF), the debug information from the + module is sent to the regular debug log. + + Example of the settings: + #define SERIALIZED_READ_FROM_CACHE + #define MAX_THREADS 100 + #define KEYCACHE_TIMEOUT 1 + #define KEYCACHE_DEBUG + #define KEYCACHE_DEBUG_LOG "my_key_cache_debug.log" +*/ #if defined(MSDOS) && !defined(M_IC80386) - /* We nead much memory */ +/* we nead much memory */ #undef my_malloc_lock #undef my_free_lock -#define my_malloc_lock(A,B) halloc((long) (A/IO_SIZE),IO_SIZE) -#define my_free_lock(A,B) hfree(A) -#endif +#define my_malloc_lock(A,B) halloc((long) (A/IO_SIZE),IO_SIZE) +#define my_free_lock(A,B) hfree(A) +#endif /* defined(MSDOS) && !defined(M_IC80386) */ + +#define STRUCT_PTR(TYPE, MEMBER, a) \ + (TYPE *) ((char *) (a) - offsetof(TYPE, MEMBER)) + +/* types of condition variables */ +#define COND_FOR_REQUESTED 0 +#define COND_FOR_SAVED 1 +#define COND_FOR_READERS 2 + +typedef pthread_cond_t KEYCACHE_CONDVAR; +typedef struct st_keycache_wqueue +{ /* info about requests in a waiting queue */ + struct st_my_thread_var *last_thread; /* circular list of waiting threads */ +} KEYCACHE_WQUEUE; + +typedef struct st_keycache_page +{ /* descriptor of the page in the key cache block buffer */ + int file; /* file to which the page belongs to */ + my_off_t filepos; /* position of the page in the file */ +} KEYCACHE_PAGE; + +typedef struct st_hash_link +{ /* element in the chain of a hash table bucket */ + struct st_hash_link *next, **prev; /* to connect links in the same bucket */ + struct st_block_link *block; /* reference to the block for the page: */ + File file; /* from such a file */ + my_off_t diskpos; /* with such an offset */ + uint requests; /* number of requests for the page */ +} HASH_LINK; /* offset is always alighed for key_cache_block_size */ + +/* simple states of a block */ +#define BLOCK_ERROR 1 /* an error occured when performing disk i/o */ +#define BLOCK_READ 2 /* the is page in the block buffer */ +#define BLOCK_IN_SWITCH 4 /* block is preparing to read new page */ +#define BLOCK_REASSIGNED 8 /* block does not accept requests for old page */ +#define BLOCK_IN_FLUSH 16 /* block is in flush operation */ +#define BLOCK_CHANGED 32 /* block buffer contains a dirty page */ + +/* page status, returned by find_key_block */ +#define PAGE_READ 0 +#define PAGE_TO_BE_READ 1 +#define PAGE_WAIT_TO_BE_READ 2 + +typedef struct st_block_link +{ /* key cache block */ + struct st_block_link + *next_used, **prev_used; /* to connect links in the LRU chain (ring) */ + struct st_block_link + *next_changed, **prev_changed; /* for lists of file dirty/clean blocks */ + struct st_hash_link *hash_link; /* backward ptr to referring hash_link */ + KEYCACHE_WQUEUE wqueue[2]; /* queues on waiting requests for new/old pages */ + uint requests; /* number of requests for the block */ + byte *buffer; /* buffer for the block page */ + uint offset; /* beginning of modified data in the buffer */ + uint length; /* end of data in the buffer */ + uint status; /* state of the block */ + KEYCACHE_CONDVAR *condvar; /* condition variable for 'no readers' event */ +} BLOCK_LINK; -/* size of map to be used to find changed files */ +static int flush_all_key_blocks(); +static void test_key_cache(const char *where, my_bool lock); -#define CHANGED_BLOCKS_HASH 128 /* Must be power of 2 */ -#define CHANGED_BLOCKS_MASK (CHANGED_BLOCKS_HASH-1) -#define FLUSH_CACHE 2000 /* Sort this many blocks at once */ +uint key_cache_block_size= /* size of the page buffer of a cache block */ + DEFAULT_KEYCACHE_BLOCK_SIZE; +static uint key_cache_shift; + +#define CHANGED_BLOCKS_HASH 128 /* must be power of 2 */ +#define FLUSH_CACHE 2000 /* sort this many blocks at once */ + +static KEYCACHE_WQUEUE + waiting_for_hash_link; /* queue of requests waiting for a free hash link */ +static KEYCACHE_WQUEUE + waiting_for_block; /* queue of requests waiting for a free block */ + +static HASH_LINK **_my_hash_root; /* arr. of entries into hash table buckets */ +static uint _my_hash_entries; /* max number of entries in the hash table */ +static HASH_LINK *_my_hash_link_root; /* memory for hash table links */ +static int _my_hash_links; /* max number of hash links */ +static int _my_hash_links_used; /* number of hash links currently used */ +static HASH_LINK *_my_free_hash_list; /* list of free hash links */ +static BLOCK_LINK *_my_block_root; /* memory for block links */ +static int _my_disk_blocks; /* max number of blocks in the cache */ +static byte HUGE_PTR *_my_block_mem; /* memory for block buffers */ +static BLOCK_LINK *_my_used_last; /* ptr to the last block of the LRU chain */ +ulong _my_blocks_used, /* number of currently used blocks */ + _my_blocks_changed; /* number of currently dirty blocks */ +#if defined(KEYCACHE_DEBUG) +static +ulong _my_blocks_available; /* number of blocks available in the LRU chain */ +#endif /* defined(KEYCACHE_DEBUG) */ +ulong _my_cache_w_requests,_my_cache_write, /* counters */ + _my_cache_r_requests,_my_cache_read; /* for statistics */ +static BLOCK_LINK + *changed_blocks[CHANGED_BLOCKS_HASH]; /* hash table for file dirty blocks */ +static BLOCK_LINK + *file_blocks[CHANGED_BLOCKS_HASH]; /* hash table for other file blocks */ + /* that are not free */ +#ifndef DBUG_OFF +static my_bool _my_printed; +#endif -typedef struct sec_link { - struct sec_link *next_hash,**prev_hash;/* Blocks linked acc. to hash-value */ - struct sec_link *next_used,*prev_used; - struct sec_link *next_changed,**prev_changed; - File file; - my_off_t diskpos; - byte *buffer; - my_bool changed; -} SEC_LINK; +#define KEYCACHE_HASH(f, pos) \ + (((ulong) ((pos) >> key_cache_shift)+(ulong) (f)) & (_my_hash_entries-1)) +#define FILE_HASH(f) ((uint) (f) & (CHANGED_BLOCKS_HASH-1)) +#define DEFAULT_KEYCACHE_DEBUG_LOG "keycache_debug.log" -static SEC_LINK *find_key_block(int file,my_off_t filepos,int *error); -static int flush_all_key_blocks(); +#if defined(KEYCACHE_DEBUG) && ! defined(KEYCACHE_DEBUG_LOG) +#define KEYCACHE_DEBUG_LOG DEFAULT_KEYCACHE_DEBUG_LOG +#endif - /* static variables in this file */ -static SEC_LINK *_my_block_root,**_my_hash_root, - *_my_used_first,*_my_used_last; -static int _my_disk_blocks; -static uint _my_disk_blocks_used, _my_hash_blocks; -static uint key_cache_shift; -ulong _my_blocks_used,_my_blocks_changed; -ulong _my_cache_w_requests,_my_cache_write,_my_cache_r_requests, - _my_cache_read; -uint key_cache_block_size=DEFAULT_KEYCACHE_BLOCK_SIZE; -static byte HUGE_PTR *_my_block_mem; -static SEC_LINK *changed_blocks[CHANGED_BLOCKS_HASH]; -static SEC_LINK *file_blocks[CHANGED_BLOCKS_HASH]; -#ifndef DBUG_OFF -static my_bool _my_printed; +#if defined(KEYCACHE_DEBUG_LOG) +static FILE *keycache_debug_log=NULL; +static void keycache_debug_print _VARARGS((const char *fmt,...)); +#define KEYCACHE_DEBUG_OPEN \ + keycache_debug_log=fopen(KEYCACHE_DEBUG_LOG, "w") + +#define KEYCACHE_DEBUG_CLOSE \ + if (keycache_debug_log) fclose(keycache_debug_log) +#else +#define KEYCACHE_DEBUG_OPEN +#define KEYCACHE_DEBUG_CLOSE +#endif /* defined(KEYCACHE_DEBUG_LOG) */ + +#if defined(KEYCACHE_DEBUG_LOG) && defined(KEYCACHE_DEBUG) +#define KEYCACHE_DBUG_PRINT(l, m) \ + { if (keycache_debug_log) fprintf(keycache_debug_log, "%s: ", l); \ + keycache_debug_print m; } + +#define KEYCACHE_DBUG_ASSERT(a) \ + { if (! (a) && keycache_debug_log) fclose(keycache_debug_log); \ + assert(a); } +#else +#define KEYCACHE_DBUG_PRINT(l, m) DBUG_PRINT(l, m) +#define KEYCACHE_DBUG_ASSERT(a) DBUG_ASSERT(a) +#endif /* defined(KEYCACHE_DEBUG_LOG) && defined(KEYCACHE_DEBUG) */ + +#if defined(KEYCACHE_DEBUG) || !defined(DBUG_OFF) +static long keycache_thread_id; +#define KEYCACHE_THREAD_TRACE(l) \ + KEYCACHE_DBUG_PRINT(l,("|thread %ld",keycache_thread_id)) + +#define KEYCACHE_THREAD_TRACE_BEGIN(l) \ + { struct st_my_thread_var *thread_var =my_thread_var; \ + keycache_thread_id=my_thread_var->id; \ + KEYCACHE_DBUG_PRINT(l,("[thread %ld",keycache_thread_id)) } + +#define KEYCACHE_THREAD_TRACE_END(l) \ + KEYCACHE_DBUG_PRINT(l,("]thread %ld",keycache_thread_id)) +#else +#define KEYCACHE_THREAD_TRACE_BEGIN(l) +#define KEYCACHE_THREAD_TRACE_END(l) +#define KEYCACHE_THREAD_TRACE(l) +#endif /* defined(KEYCACHE_DEBUG) || !defined(DBUG_OFF) */ + +#define BLOCK_NUMBER(b) \ + ((uint) (((char*)(b) - (char *) _my_block_root) / sizeof(BLOCK_LINK))) +#define HASH_LINK_NUMBER(h) \ + ((uint) (((char*)(h) - (char *) _my_hash_link_root) / sizeof(HASH_LINK))) + +#if (defined(KEYCACHE_TIMEOUT) && !defined(__WIN__)) || defined(KEYCACHE_DEBUG) +static int keycache_pthread_cond_wait(pthread_cond_t *cond, + pthread_mutex_t *mutex); +#else +#define keycache_pthread_cond_wait pthread_cond_wait #endif +#if defined(KEYCACHE_DEBUG) +static int keycache_pthread_mutex_lock(pthread_mutex_t *mutex); +static void keycache_pthread_mutex_unlock(pthread_mutex_t *mutex); +static int keycache_pthread_cond_signal(pthread_cond_t *cond); +static int keycache_pthread_cond_broadcast(pthread_cond_t *cond); +#else +#define keycache_pthread_mutex_lock pthread_mutex_lock +#define keycache_pthread_mutex_unlock pthread_mutex_unlock +#define keycache_pthread_cond_signal pthread_cond_signal +#define keycache_pthread_cond_broadcast pthread_cond_broadcast +#endif /* defined(KEYCACHE_DEBUG) */ + +static uint next_power(uint value) +{ + uint old_value=1; + while (value) + { + old_value=value; + value&= value-1; + } + return (old_value << 1); +} - /* Init of disk_buffert */ - /* Returns blocks in use */ - /* ARGSUSED */ +/* + Initialize the key cache, + return number of blocks in it +*/ int init_key_cache(ulong use_mem) { - uint blocks,length; + uint blocks, hash_links, length; + int error; + DBUG_ENTER("init_key_cache"); - + + KEYCACHE_DEBUG_OPEN; if (key_cache_inited && _my_disk_blocks > 0) { - DBUG_PRINT("warning",("key cache already in use")); /* purecov: inspected */ - DBUG_RETURN(0); /* purecov: inspected */ + DBUG_PRINT("warning",("key cache already in use")); + DBUG_RETURN(0); } if (! key_cache_inited) { key_cache_inited=TRUE; _my_disk_blocks= -1; key_cache_shift=my_bit_log2(key_cache_block_size); - DBUG_PRINT("info",("key_cache_block_size: %u key_cache_shift: %u", - key_cache_block_size, key_cache_shift)); + DBUG_PRINT("info",("key_cache_block_size: %u", + key_cache_block_size)); #ifndef DBUG_OFF _my_printed=0; #endif } - - blocks= (uint) (use_mem/(sizeof(SEC_LINK)+sizeof(SEC_LINK*)*5/4+ - key_cache_block_size)); - /* No use to have very few blocks */ + + _my_cache_w_requests=_my_cache_r_requests=_my_cache_read=_my_cache_write=0; + + _my_block_mem=NULL; + _my_block_root=NULL; + + blocks= (uint) (use_mem/(sizeof(BLOCK_LINK)+2*sizeof(HASH_LINK)+ + sizeof(HASH_LINK*)*5/4+key_cache_block_size)); + /* It doesn't make sense to have too few blocks (less than 8) */ if (blocks >= 8 && _my_disk_blocks < 0) { for (;;) { - /* Set my_hash_blocks to the next bigger 2 power */ - _my_hash_blocks=(uint) 1 << (my_bit_log2(blocks*5/4)+1); - while ((length=(uint) blocks*sizeof(SEC_LINK)+ - sizeof(SEC_LINK*)*_my_hash_blocks)+ - ((ulong) blocks << key_cache_shift) > - use_mem) - blocks--; - if ((_my_block_mem=my_malloc_lock((ulong) blocks << key_cache_shift, - MYF(0)))) + /* Set _my_hash_entries to the next bigger 2 power */ + if ((_my_hash_entries=next_power(blocks)) < blocks*5/4) + _my_hash_entries<<=1; + hash_links=2*blocks; +#if defined(MAX_THREADS) + if (hash_links < MAX_THREADS + blocks - 1) + hash_links=MAX_THREADS + blocks - 1; +#endif + while ((length=blocks*sizeof(BLOCK_LINK)+hash_links*sizeof(HASH_LINK)+ + sizeof(HASH_LINK*)*_my_hash_entries)+ + ((ulong) blocks << key_cache_shift) > + use_mem) + blocks--; + /* Allocate memory for cache page buffers */ + if ((_my_block_mem=my_malloc_lock((ulong) blocks*key_cache_block_size, + MYF(0)))) + { + /* + Allocate memory for blocks, hash_links and hash entries; + For each block 2 hash links are allocated + */ + if ((_my_block_root=(BLOCK_LINK*) my_malloc((uint) length,MYF(0)))) + break; + my_free_lock(_my_block_mem,MYF(0)); + } + if (blocks < 8) { - if ((_my_block_root=(SEC_LINK*) my_malloc((uint) length,MYF(0))) != 0) - break; - my_free_lock(_my_block_mem,MYF(0)); + my_errno=ENOMEM; + goto err; } - if (blocks < 8) - goto err; blocks=blocks/4*3; } _my_disk_blocks=(int) blocks; - _my_hash_root= (SEC_LINK**) (_my_block_root+blocks); - bzero((byte*) _my_hash_root,_my_hash_blocks*sizeof(SEC_LINK*)); - _my_used_first=_my_used_last=0; - _my_blocks_used=_my_disk_blocks_used=_my_blocks_changed=0; - _my_cache_w_requests=_my_cache_r_requests=_my_cache_read=_my_cache_write=0; - DBUG_PRINT("exit",("disk_blocks: %d block_root: %lx _my_hash_blocks: %d hash_root: %lx", - _my_disk_blocks,_my_block_root,_my_hash_blocks, - _my_hash_root)); + _my_hash_links=hash_links; + _my_hash_root=(HASH_LINK**) (_my_block_root+blocks); + _my_hash_link_root=(HASH_LINK*) (_my_hash_root+_my_hash_entries); + bzero((byte*) _my_block_root,_my_disk_blocks*sizeof(BLOCK_LINK)); + bzero((byte*) _my_hash_root,_my_hash_entries*sizeof(HASH_LINK*)); + bzero((byte*) _my_hash_link_root,_my_hash_links*sizeof(HASH_LINK)); + _my_hash_links_used=0; + _my_free_hash_list=NULL; + _my_blocks_used=_my_blocks_changed=0; +#if defined(KEYCACHE_DEBUG) + _my_blocks_available=0; +#endif + /* The LRU chain is empty after initialization */ + _my_used_last=NULL; + + waiting_for_hash_link.last_thread=NULL; + waiting_for_block.last_thread=NULL; + DBUG_PRINT("exit", + ("disk_blocks: %d block_root: %lx hash_entries: %d hash_root: %lx \ + hash_links: %d hash_link_root %lx", + _my_disk_blocks,_my_block_root,_my_hash_entries,_my_hash_root, + _my_hash_links,_my_hash_link_root)); } bzero((gptr) changed_blocks,sizeof(changed_blocks[0])*CHANGED_BLOCKS_HASH); bzero((gptr) file_blocks,sizeof(file_blocks[0])*CHANGED_BLOCKS_HASH); + DBUG_RETURN((int) blocks); - + err: - my_errno=ENOMEM; + error=my_errno; + if (_my_block_mem) + my_free_lock((gptr) _my_block_mem,MYF(0)); + if (_my_block_mem) + my_free((gptr) _my_block_root,MYF(0)); + my_errno=error; DBUG_RETURN(0); -} /* init_key_cache */ +} /* - Resize the key cache - - SYNOPSIS - resize_key_cache() - use_mem Bytes to use for new key cache - - RETURN VALUES - 0 Error - # number of blocks in key cache + Resize the key cache */ - - int resize_key_cache(ulong use_mem) { - int block; - pthread_mutex_lock(&THR_LOCK_keycache); + int blocks; + keycache_pthread_mutex_lock(&THR_LOCK_keycache); if (flush_all_key_blocks()) { - /* TODO: If this happens, we should write a warning in the log file ! */ - pthread_mutex_unlock(&THR_LOCK_keycache); + /* TODO: if this happens, we should write a warning in the log file ! */ + keycache_pthread_mutex_unlock(&THR_LOCK_keycache); return 0; } end_key_cache(); - /* The following will work even if memory is 0 */ - block=init_key_cache(use_mem); - pthread_mutex_unlock(&THR_LOCK_keycache); - return block; + /* the following will work even if memory is 0 */ + blocks=init_key_cache(use_mem); + keycache_pthread_mutex_unlock(&THR_LOCK_keycache); + return blocks; } - /* Remove key_cache from memory */ - +/* + Remove key_cache from memory +*/ void end_key_cache(void) { DBUG_ENTER("end_key_cache"); - if (! _my_blocks_changed) + if (_my_disk_blocks > 0) { - if (_my_disk_blocks > 0) + if (_my_block_mem) { my_free_lock((gptr) _my_block_mem,MYF(0)); my_free((gptr) _my_block_root,MYF(0)); - _my_disk_blocks= -1; } + _my_disk_blocks= -1; } + KEYCACHE_DEBUG_CLOSE; key_cache_inited=0; - _my_hash_blocks=_my_blocks_used=0; DBUG_PRINT("status", - ("used: %d changed: %d w_requests: %ld writes: %ld r_requests: %ld reads: %ld", - _my_blocks_used,_my_blocks_changed,_my_cache_w_requests, - _my_cache_write,_my_cache_r_requests,_my_cache_read)); + ("used: %d changed: %d w_requests: %ld \ + writes: %ld r_requests: %ld reads: %ld", + _my_blocks_used,_my_blocks_changed,_my_cache_w_requests, + _my_cache_write,_my_cache_r_requests,_my_cache_read)); DBUG_VOID_RETURN; } /* end_key_cache */ -static inline void link_into_file_blocks(SEC_LINK *next, int file) +/* + Link a thread into double-linked queue of waiting threads +*/ +static inline void link_into_queue(KEYCACHE_WQUEUE *wqueue, + struct st_my_thread_var *thread) +{ + struct st_my_thread_var *last; + if (! (last=wqueue->last_thread)) + { /* Queue is empty */ + thread->next=thread; + thread->prev=&thread->next; + } + else + { + thread->prev=last->next->prev; + last->next->prev=&thread->next; + thread->next=last->next; + last->next=thread; + } + wqueue->last_thread=thread; +} + +/* + Unlink a thread from double-linked queue of waiting threads +*/ +static inline void unlink_from_queue(KEYCACHE_WQUEUE *wqueue, + struct st_my_thread_var *thread) +{ + KEYCACHE_DBUG_PRINT("unlink_from_queue", ("thread %ld", thread->id)); + if (thread->next == thread) + /* The queue contains only one member */ + wqueue->last_thread=NULL; + else + { + thread->next->prev=thread->prev; + *thread->prev=thread->next; + if (wqueue->last_thread == thread) + wqueue->last_thread=STRUCT_PTR(struct st_my_thread_var, next, + thread->prev); + } + thread->next=NULL; +} + + +/* + Add a thread to single-linked queue of waiting threads +*/ +static inline void add_to_queue(KEYCACHE_WQUEUE *wqueue, + struct st_my_thread_var *thread) +{ + struct st_my_thread_var *last; + if (! (last=wqueue->last_thread)) + thread->next=thread; + else + { + thread->next=last->next; + last->next=thread; + } + wqueue->last_thread=thread; +} + + +/* + Remove all threads from queue signaling them to proceed +*/ +static inline void release_queue(KEYCACHE_WQUEUE *wqueue) +{ + struct st_my_thread_var *last=wqueue->last_thread; + struct st_my_thread_var *next=last->next; + struct st_my_thread_var *thread; + do + { + thread=next; + keycache_pthread_cond_signal(&thread->suspend); + KEYCACHE_DBUG_PRINT("release_queue: signal", ("thread %ld", thread->id)); + next=thread->next; + thread->next=NULL; + } + while (thread != last); + wqueue->last_thread=NULL; +} + + +/* + Unlink a block from the chain of dirty/clean blocks +*/ +static inline void unlink_changed(BLOCK_LINK *block) { - reg1 SEC_LINK **ptr= &file_blocks[(uint) file & CHANGED_BLOCKS_MASK]; - next->prev_changed= ptr; - if ((next->next_changed= *ptr)) - (*ptr)->prev_changed= &next->next_changed; - *ptr=next; + if (block->next_changed) + block->next_changed->prev_changed=block->prev_changed; + *block->prev_changed=block->next_changed; } -static inline void relink_into_file_blocks(SEC_LINK *next, int file) +/* + Link a block into the chain of dirty/clean blocks +*/ +static inline void link_changed(BLOCK_LINK *block, BLOCK_LINK **phead) { - reg1 SEC_LINK **ptr= &file_blocks[(uint) file & CHANGED_BLOCKS_MASK]; - if (next->next_changed) - next->next_changed->prev_changed=next->prev_changed; - *next->prev_changed=next->next_changed; - next->prev_changed= ptr; - if ((next->next_changed= *ptr)) - (*ptr)->prev_changed= &next->next_changed; - *ptr=next; + block->prev_changed=phead; + if ((block->next_changed=*phead)) + (*phead)->prev_changed= &block->next_changed; + *phead=block; } -static inline void link_changed_to_file(SEC_LINK *next,int file) + +/* + Unlink a block from the chain of dirty/clean blocks, if it's asked for, + and link it to the chain of clean blocks for the specified file +*/ +static inline void link_to_file_list(BLOCK_LINK *block,int file, + my_bool unlink) { - reg1 SEC_LINK **ptr= &file_blocks[(uint) file & CHANGED_BLOCKS_MASK]; - if (next->next_changed) - next->next_changed->prev_changed=next->prev_changed; - *next->prev_changed=next->next_changed; - next->prev_changed= ptr; - if ((next->next_changed= *ptr)) - (*ptr)->prev_changed= &next->next_changed; - *ptr=next; - next->changed=0; - _my_blocks_changed--; + if (unlink) + unlink_changed(block); + link_changed(block,&file_blocks[FILE_HASH(file)]); + if (block->status & BLOCK_CHANGED) + { + block->status&=~BLOCK_CHANGED; + _my_blocks_changed--; + } } -static inline void link_file_to_changed(SEC_LINK *next) + +/* + Unlink a block from the chain of clean blocks for the specified + file and link it to the chain of dirty blocks for this file +*/ +static inline void link_to_changed_list(BLOCK_LINK *block) { - reg1 SEC_LINK **ptr= &changed_blocks[(uint) next->file & CHANGED_BLOCKS_MASK]; - if (next->next_changed) - next->next_changed->prev_changed=next->prev_changed; - *next->prev_changed=next->next_changed; - next->prev_changed= ptr; - if ((next->next_changed= *ptr)) - (*ptr)->prev_changed= &next->next_changed; - *ptr=next; - next->changed=1; + unlink_changed(block); + link_changed(block,&changed_blocks[FILE_HASH(block->hash_link->file)]); + block->status|=BLOCK_CHANGED; _my_blocks_changed++; } -#if !defined(DBUG_OFF) && !defined(EXTRA_DEBUG) -#define DBUG_OFF /* This should work */ +/* + Link a block to the LRU chain at the beginning or at the end +*/ +static void link_block(BLOCK_LINK *block, my_bool at_end) +{ + KEYCACHE_DBUG_ASSERT(! (block->hash_link && block->hash_link->requests)); + if (waiting_for_block.last_thread) { + /* Signal that in the LRU chain an available block has appeared */ + struct st_my_thread_var *last_thread=waiting_for_block.last_thread; + struct st_my_thread_var *first_thread=last_thread->next; + struct st_my_thread_var *next_thread=first_thread; + HASH_LINK *hash_link= (HASH_LINK *) first_thread->opt_info; + struct st_my_thread_var *thread; + do + { + thread=next_thread; + next_thread=thread->next; + /* + We notify about the event all threads that ask + for the same page as the first thread in the queue + */ + if ((HASH_LINK *) thread->opt_info == hash_link) + { + keycache_pthread_cond_signal(&thread->suspend); + unlink_from_queue(&waiting_for_block, thread); + block->requests++; + } + } + while (thread != last_thread); + hash_link->block=block; + KEYCACHE_THREAD_TRACE("link_block: after signaling"); +#if defined(KEYCACHE_DEBUG) + KEYCACHE_DBUG_PRINT("link_block", + ("linked,unlinked block %u status=%x #requests=%u #available=%u", + BLOCK_NUMBER(block),block->status, + block->requests,_my_blocks_available)); #endif + return; + } + if (_my_used_last) + { + _my_used_last->next_used->prev_used=&block->next_used; + block->next_used=_my_used_last->next_used; + block->prev_used=&_my_used_last->next_used; + _my_used_last->next_used=block; + if (at_end) + _my_used_last=block; + } + else + { + /* The LRU chain is empty */ + _my_used_last=block->next_used=block; + block->prev_used=&block->next_used; + } + KEYCACHE_THREAD_TRACE("link_block"); +#if defined(KEYCACHE_DEBUG) + _my_blocks_available++; + KEYCACHE_DBUG_PRINT("link_block", + ("linked block %u:%1u status=%x #requests=%u #available=%u", + BLOCK_NUMBER(block),at_end,block->status, + block->requests,_my_blocks_available)); + KEYCACHE_DBUG_ASSERT(_my_blocks_available <= _my_blocks_used); +#endif +} -#ifndef DBUG_OFF -static void test_key_cache(const char *where, my_bool lock); + +/* + Unlink a block from the LRU chain +*/ +static inline void unlink_block(BLOCK_LINK *block) +{ + if (block->next_used == block) + /* The list contains only one member */ + _my_used_last=NULL; + else + { + block->next_used->prev_used=block->prev_used; + *block->prev_used=block->next_used; + if (_my_used_last == block) + _my_used_last=STRUCT_PTR(BLOCK_LINK, next_used, block->prev_used); + } + block->next_used=NULL; + + KEYCACHE_THREAD_TRACE("unlink_block"); +#if defined(KEYCACHE_DEBUG) + _my_blocks_available--; + KEYCACHE_DBUG_PRINT("unlink_block", + ("unlinked block %u status=%x #requests=%u #available=%u", + BLOCK_NUMBER(block),block->status, + block->requests,_my_blocks_available)); + KEYCACHE_DBUG_ASSERT(_my_blocks_available >= 0); +#endif +} + + +/* + Register requests for a block +*/ +static inline void reg_requests(BLOCK_LINK *block, int count) +{ + if (! block->requests) + /* First request for the block unlinks it */ + unlink_block(block); + block->requests+=count; +} + + +/* + Unregister request for a block + linking it to the LRU chain if it's the last request +*/ +static inline void unreg_request(BLOCK_LINK *block, int at_end) +{ + if (! --block->requests) + link_block(block, at_end); +} + +/* + Remove a reader of the page in block +*/ +static inline void remove_reader(BLOCK_LINK *block) +{ + if (! --block->hash_link->requests && block->condvar) + keycache_pthread_cond_signal(block->condvar); +} + + +/* + Wait until the last reader of the page in block + signals on its termination +*/ +static inline void wait_for_readers(BLOCK_LINK *block) +{ + struct st_my_thread_var *thread=my_thread_var; + while (block->hash_link->requests) + { + block->condvar=&thread->suspend; + keycache_pthread_cond_wait(&thread->suspend,&THR_LOCK_keycache); + block->condvar=NULL; + } +} + + +/* + add a hash link to a bucket in the hash_table +*/ +static inline void link_hash(HASH_LINK **start, HASH_LINK *hash_link) +{ + if (*start) + (*start)->prev=&hash_link->next; + hash_link->next=*start; + hash_link->prev=start; + *start=hash_link; +} + + +/* + Remove a hash link from the hash table +*/ +static inline void unlink_hash(HASH_LINK *hash_link) +{ + KEYCACHE_DBUG_PRINT("unlink_hash", ("file %u, filepos %lu #requests=%u", + (uint) hash_link->file,(ulong) hash_link->diskpos, hash_link->requests)); + KEYCACHE_DBUG_ASSERT(hash_link->requests == 0); + if ((*hash_link->prev=hash_link->next)) + hash_link->next->prev=hash_link->prev; + hash_link->block=NULL; + if (waiting_for_hash_link.last_thread) + { /* Signal that A free hash link appeared */ + struct st_my_thread_var *last_thread=waiting_for_hash_link.last_thread; + struct st_my_thread_var *first_thread=last_thread->next; + struct st_my_thread_var *next_thread=first_thread; + KEYCACHE_PAGE *first_page= (KEYCACHE_PAGE *) (first_thread->opt_info); + struct st_my_thread_var *thread; + + hash_link->file=first_page->file; + hash_link->diskpos=first_page->filepos; + do + { + KEYCACHE_PAGE *page; + thread=next_thread; + page= (KEYCACHE_PAGE *) thread->opt_info; + next_thread=thread->next; + /* + We notify about the event all threads that ask + for the same page as the first thread in the queue + */ + if (page->file == hash_link->file && page->filepos == hash_link->diskpos) + { + keycache_pthread_cond_signal(&thread->suspend); + unlink_from_queue(&waiting_for_hash_link, thread); + } + } + while (thread != last_thread); + link_hash(&_my_hash_root[KEYCACHE_HASH(hash_link->file, + hash_link->diskpos)], hash_link); + return; + } + hash_link->next=_my_free_hash_list; + _my_free_hash_list=hash_link; +} + +/* + Get the hash link for a page +*/ +static inline HASH_LINK *get_hash_link(int file, my_off_t filepos) +{ + reg1 HASH_LINK *hash_link, **start; + KEYCACHE_PAGE page; +#if defined(KEYCACHE_DEBUG) + int cnt; +#endif + + KEYCACHE_DBUG_PRINT("get_hash_link", ("file %u, filepos %lu", + (uint) file,(ulong) filepos)); + +restart: + /* + Find the bucket in the hash table for the pair (file, filepos); + start contains the head of the bucket list, + hash_link points to the first member of the list + */ + hash_link=*(start=&_my_hash_root[KEYCACHE_HASH(file, filepos)]); +#if defined(KEYCACHE_DEBUG) + cnt=0; +#endif + /* Look for an element for the pair (file, filepos) in the bucket chain */ + while (hash_link && + (hash_link->diskpos != filepos || hash_link->file != file)) + { + hash_link= hash_link->next; +#if defined(KEYCACHE_DEBUG) + cnt++; + if (! (cnt <= _my_hash_links_used)) + { + int i; + for (i=0, hash_link=*start ; + i < cnt ; i++, hash_link=hash_link->next) + { + KEYCACHE_DBUG_PRINT("get_hash_link", ("file %u, filepos %lu", + (uint) hash_link->file,(ulong) hash_link->diskpos)); + } + } + KEYCACHE_DBUG_ASSERT(n <= _my_hash_links_used); +#endif + } + if (! hash_link) + { /* There is no hash link in the hash table for the pair (file, filepos) */ + if (_my_free_hash_list) + { + hash_link=_my_free_hash_list; + _my_free_hash_list=hash_link->next; + } + else if (_my_hash_links_used < _my_hash_links) + { + hash_link= &_my_hash_link_root[_my_hash_links_used++]; + } + else + { /* Wait for a free hash link */ + struct st_my_thread_var *thread=my_thread_var; + KEYCACHE_DBUG_PRINT("get_hash_link", ("waiting")); + page.file=file; page.filepos=filepos; + thread->opt_info= (void *) &page; + link_into_queue(&waiting_for_hash_link, thread); + keycache_pthread_cond_wait(&thread->suspend,&THR_LOCK_keycache); + thread->opt_info=NULL; + goto restart; + } + hash_link->file=file; + hash_link->diskpos=filepos; + link_hash(start, hash_link); + } + /* Register the request for the page */ + hash_link->requests++; + + return hash_link; +} + + +/* + Get a block for the file page requested by a keycache read/write operation; + If the page is not in the cache return a free block, if there is none + return the lru block after saving its buffer if the page is dirty +*/ +static BLOCK_LINK *find_key_block(int file, my_off_t filepos, + int wrmode, int *page_st) +{ + HASH_LINK *hash_link; + BLOCK_LINK *block; + int error=0; + int page_status; + + DBUG_ENTER("find_key_block"); + KEYCACHE_THREAD_TRACE("find_key_block:begin"); + DBUG_PRINT("enter", ("file %u, filepos %lu, wrmode %lu", + (uint) file,(ulong) filepos,(uint) wrmode)); + KEYCACHE_DBUG_PRINT("find_key_block", ("file %u, filepos %lu, wrmode %lu", + (uint) file,(ulong) filepos,(uint) wrmode)); +#if !defined(DBUG_OFF) && defined(EXTRA_DEBUG) + DBUG_EXECUTE("check_keycache2",test_key_cache("start of find_key_block",0);); #endif + +restart: + /* Find the hash link for the requested page (file, filepos) */ + hash_link=get_hash_link(file, filepos); + + page_status=-1; + if ((block=hash_link->block) && + block->hash_link == hash_link && (block->status & BLOCK_READ)) + page_status=PAGE_READ; + + if (page_status == PAGE_READ && (block->status & BLOCK_IN_SWITCH)) + { /* This is a request for a page to be removed from cache */ + + KEYCACHE_DBUG_PRINT("find_key_block", + ("request for old page in block %u",BLOCK_NUMBER(block))); + /* + Only reading requests can proceed until the old dirty page is flushed, + all others are to be suspended, then resubmitted + */ + if (!wrmode && !(block->status & BLOCK_REASSIGNED)) + reg_requests(block,1); + else + { + hash_link->requests--; + KEYCACHE_DBUG_PRINT("find_key_block", + ("request waiting for old page to be saved")); + { + struct st_my_thread_var *thread=my_thread_var; + /* Put the request into the queue of those waiting for the old page */ + add_to_queue(&block->wqueue[COND_FOR_SAVED], thread); + /* Wait until the request can be resubmitted */ + do + { + keycache_pthread_cond_wait(&thread->suspend, &THR_LOCK_keycache); + } + while(thread->next); + } + KEYCACHE_DBUG_PRINT("find_key_block", + ("request for old page resubmitted")); + /* Resubmit the request */ + goto restart; + } + } + else + { /* This is a request for a new page or for a page not to be removed */ + if (! block) + { /* No block is assigned for the page yet */ + if (_my_blocks_used < (uint) _my_disk_blocks) + { /* There are some never used blocks, take first of them */ + hash_link->block=block= &_my_block_root[_my_blocks_used]; + block->buffer=ADD_TO_PTR(_my_block_mem, + ((ulong) _my_blocks_used*key_cache_block_size), + byte*); + block->status=0; + block->length=0; + block->offset=key_cache_block_size; + block->requests=1; + _my_blocks_used++; + link_to_file_list(block, file, 0); + block->hash_link=hash_link; + page_status=PAGE_TO_BE_READ; + KEYCACHE_DBUG_PRINT("find_key_block", + ("got never used block %u",BLOCK_NUMBER(block))); + } + else + { /* There are no never used blocks, use a block from the LRU chain */ + /* + Wait until a new block is added to the LRU chain; + several threads might wait here for the same page, + all of them must get the same block + */ + + if (! _my_used_last) + { + struct st_my_thread_var *thread=my_thread_var; + thread->opt_info=(void *) hash_link; + link_into_queue(&waiting_for_block, thread); + do + { + keycache_pthread_cond_wait(&thread->suspend,&THR_LOCK_keycache); + } + while (thread->next); + thread->opt_info=NULL; + } + block=hash_link->block; + if (! block) + { + /* + Take the first block from the LRU chain + unlinking it from the chain + */ + block=_my_used_last->next_used; + reg_requests(block,1); + hash_link->block=block; + } + + if (block->hash_link != hash_link && ! (block->status & BLOCK_IN_SWITCH) ) + { /* this is a primary request for a new page */ + block->status|=BLOCK_IN_SWITCH; + + KEYCACHE_DBUG_PRINT("find_key_block", + ("got block %u for new page",BLOCK_NUMBER(block))); + + if (block->status & BLOCK_CHANGED) + { /* The block contains a dirty page - push it out of the cache */ + + KEYCACHE_DBUG_PRINT("find_key_block",("block is dirty")); + + keycache_pthread_mutex_unlock(&THR_LOCK_keycache); + /* + The call is thread safe because only the current + thread might change the block->hash_link value + */ + error=my_pwrite(block->hash_link->file,block->buffer, + block->length,block->hash_link->diskpos, + MYF(MY_NABP | MY_WAIT_IF_FULL)); + keycache_pthread_mutex_lock(&THR_LOCK_keycache); + _my_cache_write++; + } + + block->status|=BLOCK_REASSIGNED; + if (block->hash_link) + { + /* + Wait until all pending read requests + for this page are executed + (we could have avoided this waiting, if we had read + a page in the cache in a sweep, without yielding control) + */ + wait_for_readers(block); + + /* Remove the hash link for this page from the hash table */ + unlink_hash(block->hash_link); + /* All pending requests for this page must be resubmitted */ + if (block->wqueue[COND_FOR_SAVED].last_thread) + release_queue(&block->wqueue[COND_FOR_SAVED]); + } + link_to_file_list(block, file, block->hash_link ? 1 : 0); + block->status=error? BLOCK_ERROR : 0; + block->length=0; + block->offset=key_cache_block_size; + block->hash_link=hash_link; + page_status=PAGE_TO_BE_READ; + + KEYCACHE_DBUG_ASSERT(block->hash_link->block == block); + KEYCACHE_DBUG_ASSERT(hash_link->block->hash_link == hash_link); + } + else + { + /* This is for secondary requests for a new page only */ + page_status = block->hash_link == hash_link && + (block->status & BLOCK_READ) ? + PAGE_READ : PAGE_WAIT_TO_BE_READ; + } + } + + _my_cache_read++; + } + else + { + reg_requests(block,1); + page_status = block->hash_link == hash_link && + (block->status & BLOCK_READ) ? + PAGE_READ : PAGE_WAIT_TO_BE_READ; + } + } + + KEYCACHE_DBUG_ASSERT(page_status != -1); + *page_st=page_status; + +#if !defined(DBUG_OFF) && defined(EXTRA_DEBUG) + DBUG_EXECUTE("check_keycache2",test_key_cache("end of find_key_block",0);); +#endif + KEYCACHE_THREAD_TRACE("find_key_block:end"); + DBUG_RETURN(block); +} - /* - ** read a key_buffer - ** filepos must point at a even key_cache_block_size block - ** if return_buffer is set then the intern buffer is returned if - ** it can be used - ** Returns adress to where data is read - */ +/* + Read into a key cache block buffer from disk; + do not to report error when the size of successfully read + portion is less than read_length, but not less than min_length +*/ +static void read_block(BLOCK_LINK *block, uint read_length, + uint min_length, my_bool primary) +{ + uint got_length; + + /* On entry THR_LOCK_keycache is locked */ + + KEYCACHE_THREAD_TRACE("read_block"); + if (primary) + { /* + This code is executed only by threads + that submitted primary requests + */ + + KEYCACHE_DBUG_PRINT("read_block", + ("page to be read by primary request")); + + /* Page is not in buffer yet, is to be read from disk */ + keycache_pthread_mutex_unlock(&THR_LOCK_keycache); + got_length=my_pread(block->hash_link->file,block->buffer, + read_length,block->hash_link->diskpos,MYF(0)); + keycache_pthread_mutex_lock(&THR_LOCK_keycache); + if (got_length < min_length) + block->status|=BLOCK_ERROR; + else + { + block->status=BLOCK_READ; + block->length=got_length; + } + KEYCACHE_DBUG_PRINT("read_block", + ("primary request: new page in cache")); + /* Signal that all pending requests for this page now can be processed */ + if (block->wqueue[COND_FOR_REQUESTED].last_thread) + release_queue(&block->wqueue[COND_FOR_REQUESTED]); + } + else + { /* + This code is executed only by threads + that submitted secondary requests + */ + KEYCACHE_DBUG_PRINT("read_block", + ("secondary request waiting for new page to be read")); + { + struct st_my_thread_var *thread=my_thread_var; + /* Put the request into a queue and wait until it can be processed */ + add_to_queue(&block->wqueue[COND_FOR_REQUESTED],thread); + do + { + keycache_pthread_cond_wait(&thread->suspend,&THR_LOCK_keycache); + } + while (thread->next); + } + KEYCACHE_DBUG_PRINT("read_block", + ("secondary request: new page in cache")); + } +} + + +/* + Read a block of data from a cached file into a buffer; + if return_buffer is set then the cache buffer is returned if + it can be used; + filepos must be a multiple of 'block_length', but it doesn't + have to be a multiple of key_cache_block_size; + returns adress from where data is read +*/ byte *key_cache_read(File file, my_off_t filepos, byte *buff, uint length, - uint block_length __attribute__((unused)), - int return_buffer __attribute__((unused))) + uint block_length __attribute__((unused)), + int return_buffer __attribute__((unused))) { - 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) - return_buffer=0; -#endif + (uint) file,(ulong) filepos,length)); + if (_my_disk_blocks > 0) - { /* We have key_cacheing */ + { /* Key cache is used */ + reg1 BLOCK_LINK *block; + uint offset= (uint) (filepos & (key_cache_block_size-1)); byte *start=buff; uint read_length; - pthread_mutex_lock(&THR_LOCK_keycache); - if (_my_disk_blocks <= 0) /* Resize failed */ - { - pthread_mutex_unlock(&THR_LOCK_keycache); - goto no_key_cache; - } + uint status; + int page_st; + +#ifndef THREAD + if (block_length > key_cache_block_size || offset) + return_buffer=0; +#endif + + /* Read data in key_cache_block_size increments */ + filepos-= offset; do { + read_length= length > key_cache_block_size ? + key_cache_block_size : length; + KEYCACHE_DBUG_ASSERT(read_length > 0); + keycache_pthread_mutex_lock(&THR_LOCK_keycache); _my_cache_r_requests++; - read_length= (length > key_cache_block_size ? key_cache_block_size : - length); - if (!(next=find_key_block(file,filepos,&error))) + block=find_key_block(file,filepos,0,&page_st); + if (page_st != PAGE_READ) { - pthread_mutex_unlock(&THR_LOCK_keycache); - DBUG_RETURN ((byte*) 0); /* Got a fatal error */ + /* The requested page is to be read into the block buffer */ + read_block(block,key_cache_block_size,read_length+offset, + page_st == PAGE_TO_BE_READ); } - 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); - DBUG_RETURN((byte*) 0); - } - _my_cache_read++; + else if (! (block->status & BLOCK_ERROR) && + block->length < read_length + offset) + { + /* + Impossible if nothing goes wrong: + this could only happen if we are using a file with + small key blocks and are trying to read outside the file + */ + my_errno=-1; + block->status|=BLOCK_ERROR; } -#ifndef THREAD /* buffer may be used a long time */ - if (return_buffer) + + if (! ((status=block->status) & BLOCK_ERROR)) { - pthread_mutex_unlock(&THR_LOCK_keycache); - DBUG_RETURN (next->buffer); +#ifndef THREAD + if (! return_buffer) +#endif + { +#if !defined(SERIALIZED_READ_FROM_CACHE) + keycache_pthread_mutex_unlock(&THR_LOCK_keycache); +#endif + + /* Copy data from the cache buffer */ + if (!(read_length & 511)) + bmove512(buff,block->buffer+offset,read_length); + else + memcpy(buff,block->buffer+offset,(size_t) read_length); + +#if !defined(SERIALIZED_READ_FROM_CACHE) + keycache_pthread_mutex_lock(&THR_LOCK_keycache); +#endif + } } + + remove_reader(block); + /* + Link the block into the LRU chain + if it's the last submitted request for the block + */ + unreg_request(block,1); + + keycache_pthread_mutex_unlock(&THR_LOCK_keycache); + + if (status & BLOCK_ERROR) + DBUG_RETURN((byte *) 0); + +#ifndef THREAD + if (return_buffer) + return (block->buffer); #endif - if (! (read_length & 511)) - bmove512(buff,next->buffer,read_length); - else - memcpy(buff,next->buffer,(size_t) read_length); + buff+=read_length; filepos+=read_length; + offset=0; + } while ((length-= read_length)); - pthread_mutex_unlock(&THR_LOCK_keycache); DBUG_RETURN(start); } - -no_key_cache: - _my_cache_r_requests++; - _my_cache_read++; + + /* Key cache is not used */ + statistic_increment(_my_cache_r_requests,&THR_LOCK_keycache); + statistic_increment(_my_cache_read,&THR_LOCK_keycache); if (my_pread(file,(byte*) buff,length,filepos,MYF(MY_NABP))) error=1; - DBUG_RETURN(error ? (byte*) 0 : buff); -} /* key_cache_read */ - + DBUG_RETURN(error? (byte*) 0 : buff); +} - /* write a key_buffer */ - /* We don't have to use pwrite because of write locking */ - /* buff must point at a even key_cache_block_size block */ +/* + Write a buffer into disk; + filepos must be a multiple of 'block_length', but it doesn't + have to be a multiple of key cache block size; + if !dont_write then all dirty pages involved in writing should + have been flushed from key cache before the function starts +*/ int key_cache_write(File file, my_off_t filepos, byte *buff, uint length, - uint block_length __attribute__((unused)), - int dont_write) + uint block_length __attribute__((unused)), + int dont_write) { - reg1 SEC_LINK *next; + reg1 BLOCK_LINK *block; int error=0; + DBUG_ENTER("key_cache_write"); - DBUG_PRINT("enter", ("file %u, filepos %lu, length %u", - (uint) file, (ulong) filepos, length)); + DBUG_PRINT("enter", ("file %u, filepos %lu, length %u block_length %u", + (uint) file,(ulong) filepos,length,block_length)); if (!dont_write) - { /* Forced write of buffer */ - _my_cache_write++; + { /* Force writing from buff into disk */ + statistic_increment(_my_cache_write, &THR_LOCK_keycache); if (my_pwrite(file,buff,length,filepos,MYF(MY_NABP | MY_WAIT_IF_FULL))) DBUG_RETURN(1); } - + #if !defined(DBUG_OFF) && defined(EXTRA_DEBUG) DBUG_EXECUTE("check_keycache",test_key_cache("start of key_cache_write",1);); #endif + if (_my_disk_blocks > 0) - { /* We have key_cacheing */ + { /* Key cache is used */ uint read_length; - pthread_mutex_lock(&THR_LOCK_keycache); - if (_my_disk_blocks <= 0) /* If resize failed */ - { - pthread_mutex_unlock(&THR_LOCK_keycache); - goto no_key_cache; - } - - _my_cache_w_requests++; + uint offset= (uint) (filepos & (key_cache_block_size-1)); + int page_st; + + /* Write data in key_cache_block_size increments */ + filepos-= offset; do { - read_length= length > key_cache_block_size ? key_cache_block_size : length; - if (!(next=find_key_block(file,filepos,&error))) - goto end; /* Fatal error */ - if (!dont_write) /* If we wrote buff at start */ + read_length= length > key_cache_block_size ? + key_cache_block_size : length; + KEYCACHE_DBUG_ASSERT(read_length > 0); + keycache_pthread_mutex_lock(&THR_LOCK_keycache); + _my_cache_w_requests++; + block=find_key_block(file, filepos, 1, &page_st); + if (page_st != PAGE_READ && + (offset || read_length < key_cache_block_size)) + read_block(block, + offset + read_length >= key_cache_block_size? + offset : key_cache_block_size, + offset,page_st == PAGE_TO_BE_READ); + + if (!dont_write) + { /* buff has been written to disk at start */ + if ((block->status & BLOCK_CHANGED) && + (!offset && read_length >= key_cache_block_size)) + link_to_file_list(block, block->hash_link->file, 1); + } + else if (! (block->status & BLOCK_CHANGED)) + link_to_changed_list(block); + + set_if_smaller(block->offset,offset) + set_if_bigger(block->length,read_length+offset); + + if (! (block->status & BLOCK_ERROR)) { - if (next->changed) /* Unlink from changed list */ - link_changed_to_file(next,next->file); + if (!(read_length & 511)) + bmove512(block->buffer+offset,buff,read_length); + else + memcpy(block->buffer+offset,buff,(size_t) read_length); } - else if (!next->changed) - link_file_to_changed(next); /* Add to changed list */ - - if (!(read_length & 511)) - bmove512(next->buffer,buff,read_length); - else - memcpy(next->buffer,buff,(size_t) read_length); + + block->status|=BLOCK_READ; + + /* Unregister the request */ + block->hash_link->requests--; + unreg_request(block,1); + + if (block->status & BLOCK_ERROR) + { + keycache_pthread_mutex_unlock(&THR_LOCK_keycache); + error=1; + break; + } + + keycache_pthread_mutex_unlock(&THR_LOCK_keycache); + buff+=read_length; filepos+=read_length; + offset=0; + } while ((length-= read_length)); - error=0; - pthread_mutex_unlock(&THR_LOCK_keycache); - goto end; - } - -no_key_cache: - if (dont_write) - { /* We must write, no cache */ - _my_cache_w_requests++; - _my_cache_write++; - if (my_pwrite(file,(byte*) buff,length,filepos, - MYF(MY_NABP | MY_WAIT_IF_FULL))) - error=1; - } - -end: -#if !defined(DBUG_OFF) && defined(EXTRA_DEBUG) - DBUG_EXECUTE("check_keycache",test_key_cache("end of key_cache_write",1);); -#endif - DBUG_RETURN(error); -} /* key_cache_write */ - - - /* Find block in cache */ - /* IF found sector and error is set then next->changed is cleared */ - -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);); -#endif - - *error=0; - next= *(start= &_my_hash_root[((ulong) (filepos >> key_cache_shift)+(ulong) file) & - (_my_hash_blocks-1)]); - while (next && (next->diskpos != filepos || next->file != file)) - next= next->next_hash; - - if (next) - { /* Found block */ - if (next != _my_used_last) - { /* Relink used-chain */ - if (next == _my_used_first) - _my_used_first=next->next_used; - else - { - next->prev_used->next_used = next->next_used; - next->next_used->prev_used = next->prev_used; - } - next->prev_used=_my_used_last; - _my_used_last->next_used=next; - } } else - { /* New block */ - if (_my_disk_blocks_used+1 <= (uint) _my_disk_blocks) - { /* There are unused blocks */ - next= &_my_block_root[_my_blocks_used++]; /* Link in hash-chain */ - next->buffer=ADD_TO_PTR(_my_block_mem, - ((ulong) _my_disk_blocks_used << key_cache_shift), - byte*); - /* link first in file_blocks */ - next->changed=0; - link_into_file_blocks(next,file); - _my_disk_blocks_used++; - if (!_my_used_first) - _my_used_first=next; - if (_my_used_last) - _my_used_last->next_used=next; /* Last in used-chain */ + { + /* Key cache is not used */ + if (dont_write) + { + statistic_increment(_my_cache_w_requests, &THR_LOCK_keycache); + statistic_increment(_my_cache_write, &THR_LOCK_keycache); + if (my_pwrite(file,(byte*) buff,length,filepos,MYF(MY_NABP | MY_WAIT_IF_FULL))) + error=1; } - else - { /* Reuse old block */ - next= _my_used_first; - if (next->changed) - { - if (my_pwrite(next->file,next->buffer,key_cache_block_size, - next->diskpos, - MYF(MY_NABP | MY_WAIT_IF_FULL))) - { - *error=1; - return((SEC_LINK*) 0); - } - _my_cache_write++; - link_changed_to_file(next,file); - } - else - { - if (next->file == -1) - link_into_file_blocks(next,file); - else - relink_into_file_blocks(next,file); - } - if (next->prev_hash) /* If in hash-link */ - if ((*next->prev_hash=next->next_hash) != 0) /* Remove from link */ - next->next_hash->prev_hash= next->prev_hash; + } - _my_used_last->next_used=next; - _my_used_first=next->next_used; - } - if (*start) /* Link in first in h.-chain */ - (*start)->prev_hash= &next->next_hash; - next->next_hash= *start; next->prev_hash=start; *start=next; - next->prev_used=_my_used_last; - next->file=file; - next->diskpos=filepos; - *error=1; /* Block wasn't in memory */ - } - _my_used_last=next; #if !defined(DBUG_OFF) && defined(EXTRA_DEBUG) - DBUG_EXECUTE("check_keycache2",test_key_cache("end of find_key_block",0);); + DBUG_EXECUTE("exec",test_key_cache("end of key_cache_write",1);); #endif - DBUG_RETURN(next); -} /* find_key_block */ + DBUG_RETURN(error); +} -static void free_block(SEC_LINK *used) +/* + Free block: remove reference to it from hash table, + remove it from the chain file of dirty/clean blocks + and add it at the beginning of the LRU chain +*/ +static void free_block(BLOCK_LINK *block) { - used->file= -1; - used->changed=0; - if (used != _my_used_first) /* Relink used-chain */ + KEYCACHE_THREAD_TRACE("free block"); + KEYCACHE_DBUG_PRINT("free_block", + ("block %u to be freed",BLOCK_NUMBER(block))); + if (block->hash_link) { - if (used == _my_used_last) - _my_used_last=used->prev_used; - else - { - used->prev_used->next_used = used->next_used; - used->next_used->prev_used = used->prev_used; - } - used->next_used=_my_used_first; - used->next_used->prev_used=used; - _my_used_first=used; + block->status|=BLOCK_REASSIGNED; + wait_for_readers(block); + unlink_hash(block->hash_link); } - if ((*used->prev_hash=used->next_hash)) /* Relink hash-chain */ - used->next_hash->prev_hash= used->prev_hash; - if (used->next_changed) /* Relink changed/file list */ - used->next_changed->prev_changed=used->prev_changed; - *used->prev_changed=used->next_changed; - used->prev_hash=0; used->next_hash=0; /* Safety */ + + unlink_changed(block); + block->status=0; + block->length=0; + block->offset=key_cache_block_size; + KEYCACHE_THREAD_TRACE("free block"); + KEYCACHE_DBUG_PRINT("free_block", + ("block is freed")); + unreg_request(block,0); + block->hash_link=NULL; } - /* Flush all changed blocks to disk. Free used blocks if requested */ - -static int cmp_sec_link(SEC_LINK **a, SEC_LINK **b) +static int cmp_sec_link(BLOCK_LINK **a, BLOCK_LINK **b) { - return (((*a)->diskpos < (*b)->diskpos) ? -1 : - ((*a)->diskpos > (*b)->diskpos) ? 1 : 0); + return (((*a)->hash_link->diskpos < (*b)->hash_link->diskpos) ? -1 : + ((*a)->hash_link->diskpos > (*b)->hash_link->diskpos) ? 1 : 0); } -static int flush_cached_blocks(File file, SEC_LINK **cache, uint count) +/* + Flush a portion of changed blocks to disk, + free used blocks if requested +*/ +static int flush_cached_blocks(File file, BLOCK_LINK **cache, + BLOCK_LINK **end, + enum flush_type type) { - uint last_errno=0; - qsort((byte*) cache, count, sizeof(*cache), (qsort_cmp) cmp_sec_link); - for ( ; count-- ; cache++) + int error; + int last_errno=0; + uint count=end-cache; + + /* Don't lock the cache during the flush */ + keycache_pthread_mutex_unlock(&THR_LOCK_keycache); + /* + As all blocks referred in 'cache' are marked by BLOCK_IN_FLUSH + we are guarunteed no thread will change them + */ + qsort((byte*) cache,count,sizeof(*cache),(qsort_cmp) cmp_sec_link); + + keycache_pthread_mutex_lock(&THR_LOCK_keycache); + for ( ; cache != end ; cache++) { - if (my_pwrite(file,(*cache)->buffer,key_cache_block_size, - (*cache)->diskpos, - MYF(MY_NABP | MY_WAIT_IF_FULL))) + BLOCK_LINK *block= *cache; + + KEYCACHE_DBUG_PRINT("flush_cached_blocks", + ("block %u to be flushed", BLOCK_NUMBER(block))); + keycache_pthread_mutex_unlock(&THR_LOCK_keycache); + error=my_pwrite(file,block->buffer+block->offset,block->length, + block->hash_link->diskpos,MYF(MY_NABP | MY_WAIT_IF_FULL)); + keycache_pthread_mutex_lock(&THR_LOCK_keycache); + _my_cache_write++; + if (error) { + block->status|= BLOCK_ERROR; if (!last_errno) - last_errno=errno ? errno : -1; + last_errno=errno ? errno : -1; + } + /* type will never be FLUSH_IGNORE_CHANGED here */ + if (! (type == FLUSH_KEEP || type == FLUSH_FORCE_WRITE)) + { + _my_blocks_changed--; + free_block(block); + } + else + { + block->status&=~BLOCK_IN_FLUSH; + link_to_file_list(block,file,1); + unreg_request(block,1); } + } return last_errno; } -static int flush_key_blocks_int(File file, enum flush_type type) +/* + Flush all blocks for a file to disk +*/ +int flush_key_blocks(File file, enum flush_type type) { - int error=0,last_errno=0; - uint count=0; - SEC_LINK *cache_buff[FLUSH_CACHE],**cache,**pos,**end; - SEC_LINK *used,*next; - DBUG_ENTER("flush_key_blocks_int"); + int last_errno=0; + BLOCK_LINK *cache_buff[FLUSH_CACHE],**cache; + DBUG_ENTER("flush_key_blocks"); DBUG_PRINT("enter",("file: %d blocks_used: %d blocks_changed: %d", - file,_my_blocks_used,_my_blocks_changed)); - - cache=cache_buff; /* If no key cache */ - if (_my_disk_blocks > 0 && - (!my_disable_flush_key_blocks || type != FLUSH_KEEP)) - { + file,_my_blocks_used,_my_blocks_changed)); + #if !defined(DBUG_OFF) && defined(EXTRA_DEBUG) DBUG_EXECUTE("check_keycache",test_key_cache("start of flush_key_blocks",0);); #endif + + keycache_pthread_mutex_lock(&THR_LOCK_keycache); + + cache=cache_buff; + if (_my_disk_blocks > 0 && + (!my_disable_flush_key_blocks || type != FLUSH_KEEP)) + { /* Key cache exists and flush is not disabled */ + int error=0; + uint count=0; + BLOCK_LINK **pos,**end; + BLOCK_LINK *first_in_switch=NULL; + BLOCK_LINK *block, *next; +#if defined(KEYCACHE_DEBUG) + uint cnt=0; +#endif + if (type != FLUSH_IGNORE_CHANGED) { - /* Count how many key blocks we have to cache to be able to - write everything with so few seeks as possible */ - - for (used=changed_blocks[(uint) file & CHANGED_BLOCKS_MASK]; - used ; - used=used->next_changed) + /* + Count how many key blocks we have to cache to be able + to flush all dirty pages with minimum seek moves + */ + for (block=changed_blocks[FILE_HASH(file)] ; + block ; + block=block->next_changed) { - if (used->file == file) - count++; - } - /* Only allocate a new buffer if its bigger than the one we have */ - if (count > FLUSH_CACHE) - { - if (!(cache=(SEC_LINK**) my_malloc(sizeof(SEC_LINK*)*count,MYF(0)))) + if (block->hash_link->file == file) { - cache=cache_buff; /* Fall back to safe buffer */ - count=FLUSH_CACHE; + count++; + KEYCACHE_DBUG_ASSERT(count<=_my_blocks_used); } } + /* Allocate a new buffer only if its bigger than the one we have */ + if (count > FLUSH_CACHE && + !(cache=(BLOCK_LINK**) my_malloc(sizeof(BLOCK_LINK*)*count,MYF(0)))) + { + cache=cache_buff; + count=FLUSH_CACHE; + } } - - /* Go through the keys and write them to buffer to be flushed */ + + /* Retrieve the blocks and write them to a buffer to be flushed */ +restart: end=(pos=cache)+count; - for (used=changed_blocks[(uint) file & CHANGED_BLOCKS_MASK]; - used ; - used=next) + for (block=changed_blocks[FILE_HASH(file)] ; + block ; + block=next) { - next=used->next_changed; - if (used->file == file) +#if defined(KEYCACHE_DEBUG) + cnt++; + KEYCACHE_DBUG_ASSERT(cnt <= _my_blocks_used); +#endif + next=block->next_changed; + if (block->hash_link->file == file) { - if (type != FLUSH_IGNORE_CHANGED) - { - if (pos == end) - { - if ((error=flush_cached_blocks(file, cache, count))) - last_errno=error; - pos=cache; - } - *pos++=used; - _my_cache_write++; - } - if (type != FLUSH_KEEP && type != FLUSH_FORCE_WRITE) - { - /* This will not destroy position or data */ - _my_blocks_changed--; - free_block(used); - } - else - link_changed_to_file(used,file); + /* + Mark the block with BLOCK_IN_FLUSH in order not to let + other threads to use it for new pages and interfere with + our sequence ot flushing dirty file pages + */ + block->status|= BLOCK_IN_FLUSH; + + if (! (block->status & BLOCK_IN_SWITCH)) + { /* + We care only for the blocks for which flushing was not + initiated by other threads as a result of page swapping + */ + reg_requests(block,1); + if (type != FLUSH_IGNORE_CHANGED) + { /* It's not a temporary file */ + if (pos == end) + { /* + This happens only if there is not enough + memory for the big block + */ + if ((error=flush_cached_blocks(file,cache,end,type))) + last_errno=error; + /* + Restart the scan as some other thread might have changed + the changed blocks chain: the blocks that were in switch + state before the flush started have to be excluded + */ + goto restart; + } + *pos++=block; + } + else + { + /* It's a temporary file */ + _my_blocks_changed--; + free_block(block); + } + } + else + { /* Link the block into a list of blocks 'in switch' */ + unlink_changed(block); + link_changed(block,&first_in_switch); + } } } if (pos != cache) { - if ((error=flush_cached_blocks(file, cache, (uint) (pos-cache)))) - last_errno=error; + if ((error=flush_cached_blocks(file,cache,pos,type))) + last_errno=error; + } + /* Wait until list of blocks in switch is empty */ + while (first_in_switch) + { +#if defined(KEYCACHE_DEBUG) + cnt=0; +#endif + block=first_in_switch; + { + struct st_my_thread_var *thread=my_thread_var; + add_to_queue(&block->wqueue[COND_FOR_SAVED], thread); + do + { + keycache_pthread_cond_wait(&thread->suspend,&THR_LOCK_keycache); + } + while (thread->next); + } +#if defined(KEYCACHE_DEBUG) + cnt++; + KEYCACHE_DBUG_ASSERT(cnt <= _my_blocks_used); +#endif } /* The following happens very seldom */ - if (type != FLUSH_KEEP && type != FLUSH_FORCE_WRITE) + if (! (type == FLUSH_KEEP || type == FLUSH_FORCE_WRITE)) { - for (used=file_blocks[(uint) file & CHANGED_BLOCKS_MASK]; - used ; - used=next) +#if defined(KEYCACHE_DEBUG) + cnt=0; +#endif + for (block=file_blocks[FILE_HASH(file)] ; + block ; + block=next) { - next=used->next_changed; - if (used->file == file && (!used->changed || - type == FLUSH_IGNORE_CHANGED)) - free_block(used); +#if defined(KEYCACHE_DEBUG) + cnt++; + KEYCACHE_DBUG_ASSERT(cnt <= _my_blocks_used); +#endif + next=block->next_changed; + if (block->hash_link->file == file && + (! (block->status & BLOCK_CHANGED) + || type == FLUSH_IGNORE_CHANGED)) + { + reg_requests(block,1); + free_block(block); + } } } + } + + keycache_pthread_mutex_unlock(&THR_LOCK_keycache); + #ifndef DBUG_OFF - DBUG_EXECUTE("check_keycache",test_key_cache("end of flush_key_blocks",0);); + DBUG_EXECUTE("check_keycache", + test_key_cache("end of flush_key_blocks",0);); #endif - } if (cache != cache_buff) my_free((gptr) cache,MYF(0)); if (last_errno) - errno=last_errno; /* Return first error */ + errno=last_errno; /* Return first error */ DBUG_RETURN(last_errno != 0); } -/* - Flush all blocks for a specific file to disk +/* + Flush all blocks in the key cache to disk +*/ +static int flush_all_key_blocks() +{ +#if defined(KEYCACHE_DEBUG) + uint cnt=0; +#endif + while (_my_blocks_changed > 0) + { + BLOCK_LINK *block; + for (block=_my_used_last->next_used ; ; block=block->next_used) + { + if (block->hash_link) + { +#if defined(KEYCACHE_DEBUG) + cnt++; + KEYCACHE_DBUG_ASSERT(cnt <= _my_blocks_used); +#endif + if (flush_key_blocks(block->hash_link->file, FLUSH_RELEASE)) + return 1; + break; + } + if (block == _my_used_last) + break; + } + } + return 0; +} - SYNOPSIS - flush_all_key_blocks() - file File descriptor - type Type of flush operation - RETURN VALUES - 0 Ok - 1 Error +#ifndef DBUG_OFF +/* + Test if disk-cache is ok */ +static void test_key_cache(const char *where __attribute__((unused)), + my_bool lock __attribute__((unused))) +{ + /* TODO */ +} +#endif -int flush_key_blocks(File file, enum flush_type type) +#if defined(KEYCACHE_TIMEOUT) + +#define KEYCACHE_DUMP_FILE "keycache_dump.txt" +#define MAX_QUEUE_LEN 100 + + +static void keycache_dump() { - int res; - pthread_mutex_lock(&THR_LOCK_keycache); - res=flush_key_blocks_int(file, type); - pthread_mutex_unlock(&THR_LOCK_keycache); - return res; + FILE *keycache_dump_file=fopen(KEYCACHE_DUMP_FILE, "w"); + struct st_my_thread_var *thread_var =my_thread_var; + struct st_my_thread_var *last; + struct st_my_thread_var *thread; + BLOCK_LINK *block; + HASH_LINK *hash_link; + KEYCACHE_PAGE *page; + uint i; + + fprintf(keycache_dump_file, "thread:%u\n", thread->id); + + i=0; + thread=last=waiting_for_hash_link.last_thread; + fprintf(keycache_dump_file, "queue of threads waiting for hash link\n"); + if (thread) + do + { + thread=thread->next; + page= (KEYCACHE_PAGE *) thread->opt_info; + fprintf(keycache_dump_file, + "thread:%u, (file,filepos)=(%u,%lu)\n", + thread->id,(uint) page->file,(ulong) page->filepos); + if (++i == MAX_QUEUE_LEN) + break; + } + while (thread != last); + + i=0; + thread=last=waiting_for_block.last_thread; + fprintf(keycache_dump_file, "queue of threads waiting for block\n"); + if (thread) + do + { + thread=thread->next; + hash_link= (HASH_LINK *) thread->opt_info; + fprintf(keycache_dump_file, + "thread:%u hash_link:%u (file,filepos)=(%u,%lu)\n", + thread->id, (uint) HASH_LINK_NUMBER(hash_link), + (uint) hash_link->file,(ulong) hash_link->diskpos); + if (++i == MAX_QUEUE_LEN) + break; + } + while (thread != last); + + for (i=0 ; i< _my_blocks_used ; i++) + { + int j; + block=&_my_block_root[i]; + hash_link=block->hash_link; + fprintf(keycache_dump_file, + "block:%u hash_link:%d status:%x #requests=%u waiting_for_readers:%d\n", + i, (int) (hash_link ? HASH_LINK_NUMBER(hash_link) : -1), + block->status, block->requests, block->condvar ? 1 : 0); + for (j=0 ; j < 2; j++) + { + KEYCACHE_WQUEUE *wqueue=&block->wqueue[j]; + thread=last=wqueue->last_thread; + fprintf(keycache_dump_file, "queue #%d\n", j); + if (thread) + do + { + thread=thread->next; + fprintf(keycache_dump_file, + "thread:%u\n", thread->id); + if (++i == MAX_QUEUE_LEN) + break; + } + while (thread != last); + } + } + fprintf(keycache_dump_file, "LRU chain:"); + block=_my_used_last; + if (block) + do + { + block=block->next_used; + fprintf(keycache_dump_file, + "block:%u, ", BLOCK_NUMBER(block)); + } + while (block != _my_used_last); + fprintf(keycache_dump_file, "\n"); + + fclose(keycache_dump_file); } +#endif /* defined(KEYCACHE_TIMEOUT) */ -/* - Flush all blocks in the key cache to disk +#if defined(KEYCACHE_TIMEOUT) && !defined(__WIN__) - SYNOPSIS - flush_all_key_blocks() - NOTE - We must have a lock on THR_LOCK_keycache before calling this function +static int keycache_pthread_cond_wait(pthread_cond_t *cond, + pthread_mutex_t *mutex) +{ + int rc; + struct timeval now; /* time when we started waiting */ + struct timespec timeout; /* timeout value for the wait function */ + struct timezone tz; +#if defined(KEYCACHE_DEBUG) + int cnt=0; +#endif + + /* Get current time */ + gettimeofday(&now, &tz); + /* Prepare timeout value */ + timeout.tv_sec = now.tv_sec + KEYCACHE_TIMEOUT; + timeout.tv_nsec = now.tv_usec * 1000; /* timeval uses microseconds. */ + /* timespec uses nanoseconds. */ + /* 1 nanosecond = 1000 micro seconds. */ + KEYCACHE_THREAD_TRACE_END("started waiting"); +#if defined(KEYCACHE_DEBUG) + cnt++; + if (cnt % 100 == 0) + fprintf(keycache_debug_log, "waiting...\n"); + fflush(keycache_debug_log); +#endif + rc = pthread_cond_timedwait(cond, mutex, &timeout); + KEYCACHE_THREAD_TRACE_BEGIN("finished waiting"); +#if defined(KEYCACHE_DEBUG) + if (rc == ETIMEDOUT) + { + fprintf(keycache_debug_log,"aborted by keycache timeout\n"); + fclose(keycache_debug_log); + abort(); + } +#endif + + if (rc == ETIMEDOUT) + keycache_dump(); + +#if defined(KEYCACHE_DEBUG) + KEYCACHE_DBUG_ASSERT(rc != ETIMEDOUT); +#else + assert(rc != ETIMEDOUT); +#endif + return rc; +} +#else +#if defined(KEYCACHE_DEBUG) +static int keycache_pthread_cond_wait(pthread_cond_t *cond, + pthread_mutex_t *mutex) +{ + int rc; + KEYCACHE_THREAD_TRACE_END("started waiting"); + rc = pthread_cond_wait(cond, mutex); + KEYCACHE_THREAD_TRACE_BEGIN("finished waiting"); + return rc; +} +#endif +#endif /* defined(KEYCACHE_TIMEOUT) && !defined(__WIN__) */ - RETURN VALUES - 0 Ok - 1 Error -*/ +#if defined(KEYCACHE_DEBUG) -static int flush_all_key_blocks() +static int keycache_pthread_mutex_lock(pthread_mutex_t *mutex) { - int error=0; - while (_my_blocks_changed > 0) - if (flush_key_blocks_int(_my_used_first->file, FLUSH_RELEASE)) - error=1; - return error; + int rc; + rc=pthread_mutex_lock(mutex); + KEYCACHE_THREAD_TRACE_BEGIN(""); + return rc; } -#ifndef DBUG_OFF +static void keycache_pthread_mutex_unlock(pthread_mutex_t *mutex) +{ + KEYCACHE_THREAD_TRACE_END(""); + pthread_mutex_unlock(mutex); +} - /* Test if disk-cache is ok */ -static void test_key_cache(const char *where, my_bool lock) +static int keycache_pthread_cond_signal(pthread_cond_t *cond) { - reg1 uint i,error; - ulong found,changed; - SEC_LINK *pos,**prev; + int rc; + KEYCACHE_THREAD_TRACE("signal"); + rc=pthread_cond_signal(cond); + return rc; +} - if (lock) - { - pthread_mutex_lock(&THR_LOCK_keycache); - if (_my_disk_blocks <= 0) /* No active key cache */ - { - pthread_mutex_unlock(&THR_LOCK_keycache); - return; - } - } - found=error=0; - for (i= 0 ; i < _my_hash_blocks ; i++) - { - for (pos= *(prev= &_my_hash_root[i]) ; - pos && found < _my_blocks_used+2 ; - found++, pos= *(prev= &pos->next_hash)) - { - if (prev != pos->prev_hash) - { - error=1; - DBUG_PRINT("error", - ("hash: %d pos: %lx : prev: %lx != pos->prev: %lx", - i,(ulong) pos,(ulong) prev,(ulong) pos->prev_hash)); - } +static int keycache_pthread_cond_broadcast(pthread_cond_t *cond) +{ + int rc; + KEYCACHE_THREAD_TRACE("signal"); + rc=pthread_cond_broadcast(cond); + return rc; +} - if (((pos->diskpos >> key_cache_shift)+pos->file) % _my_hash_blocks != i) - { - DBUG_PRINT("error",("hash: %d pos: %lx : Wrong disk_buffer %ld", - i,(ulong) pos,(ulong) pos->diskpos)); - error=1; - } - } - } - if (found > _my_blocks_used) - { - DBUG_PRINT("error",("Found too many hash_pointers")); - error=1; - } - if (error && !_my_printed) - { /* Write all hash-pointers */ - _my_printed=1; - for (i=0 ; i < _my_hash_blocks ; i++) - { - DBUG_PRINT("loop",("hash: %d _my_hash_root: %lx",i,&_my_hash_root[i])); - pos= _my_hash_root[i]; found=0; - while (pos && found < 10) - { - DBUG_PRINT("loop",("pos: %lx prev: %lx next: %lx file: %d disk_buffer: %ld", (ulong) pos, (ulong) pos->prev_hash, (ulong) pos->next_hash, (ulong) pos->file, (ulong) pos->diskpos)); - found++; pos= pos->next_hash; - } - } - } +#if defined(KEYCACHE_DEBUG_LOG) - found=changed=0; - if ((pos=_my_used_first)) - { - while (pos != _my_used_last && found < _my_blocks_used+2) - { - found++; - if (pos->changed) - changed++; - if (pos->next_used->prev_used != pos) - { - DBUG_PRINT("error",("pos: %lx next_used: %lx next_used->prev: %lx", - (ulong) pos, - (ulong) pos->next_used, - (ulong) pos->next_used->prev_hash)); - error=1; - } - pos=pos->next_used; - } - found++; - if (pos->changed) - changed++; - } - if (found != _my_blocks_used) +static void keycache_debug_print(const char * fmt,...) +{ + va_list args; + va_start(args,fmt); + if (keycache_debug_log) { - DBUG_PRINT("error",("Found %lu of %lu keyblocks",found,_my_blocks_used)); - error=1; + VOID(vfprintf(keycache_debug_log, fmt, args)); + VOID(fputc('\n',keycache_debug_log)); } + va_end(args); +} +#endif /* defined(KEYCACHE_DEBUG_LOG) */ - for (i= 0 ; i < CHANGED_BLOCKS_HASH ; i++) - { - found=0; - prev= &changed_blocks[i]; - for (pos= *prev ; pos && found < _my_blocks_used+2; pos=pos->next_changed) - { - found++; - if (pos->prev_changed != prev) - { - DBUG_PRINT("error",("changed_block list %d doesn't point backwards properly",i)); - error=1; - } - prev= &pos->next_changed; - if (((uint) pos->file & CHANGED_BLOCKS_MASK) != i) - { - DBUG_PRINT("error",("Wrong file %d in changed blocks: %d",pos->file,i)); - error=1; - } - changed--; - } - if (pos) - { - DBUG_PRINT("error",("changed_blocks %d has recursive link",i)); - error=1; - } +#if defined(KEYCACHE_DEBUG_LOG) + + +void keycache_debug_log_close(void) +{ + if (keycache_debug_log) + fclose(keycache_debug_log); +} +#endif /* defined(KEYCACHE_DEBUG_LOG) */ + +#endif /* defined(KEYCACHE_DEBUG) */ - found=0; - prev= &file_blocks[i]; - for (pos= *prev ; pos && found < _my_blocks_used+2; pos=pos->next_changed) - { - found++; - if (pos->prev_changed != prev) - { - DBUG_PRINT("error",("file_block list %d doesn't point backwards properly",i)); - error=1; - } - prev= &pos->next_changed; - if (((uint) pos->file & CHANGED_BLOCKS_MASK) != i) - { - DBUG_PRINT("error",("Wrong file %d in file_blocks: %d",pos->file,i)); - error=1; - } - } - if (pos) - { - DBUG_PRINT("error",("File_blocks %d has recursive link",i)); - error=1; - } - } - if (changed != 0) - { - DBUG_PRINT("error",("Found %lu blocks that wasn't in changed blocks", - changed)); - error=1; - } - if (error) - DBUG_PRINT("error",("Found error at %s",where)); - if (lock) - pthread_mutex_unlock(&THR_LOCK_keycache); - return; -} /* test_key_cache */ -#endif diff --git a/mysys/mf_soundex.c b/mysys/mf_soundex.c index 4f7aa7da601..459e304dfd7 100644 --- a/mysys/mf_soundex.c +++ b/mysys/mf_soundex.c @@ -34,24 +34,25 @@ #include <m_ctype.h> #include "my_static.h" -static char get_scode(char **ptr,pbool remove_garbage); +static char get_scode(CHARSET_INFO * cs, char **ptr,pbool remove_garbage); /* outputed string is 4 byte long */ /* out_pntr can be == in_pntr */ -void soundex(register my_string out_pntr, my_string in_pntr, +void soundex(CHARSET_INFO * cs,register my_string out_pntr, my_string in_pntr, pbool remove_garbage) { char ch,last_ch; reg3 my_string end; + register uchar *map=cs->to_upper; if (remove_garbage) { - while (*in_pntr && isspace(*in_pntr)) /* Skipp pre-space */ + while (*in_pntr && my_isspace(cs,*in_pntr)) /* Skipp pre-space */ in_pntr++; } - *out_pntr++ = toupper(*in_pntr); /* Copy first letter */ - last_ch = get_scode(&in_pntr,0); /* code of the first letter */ + *out_pntr++ = map[(uchar)*in_pntr]; /* Copy first letter */ + last_ch = get_scode(cs,&in_pntr,0); /* code of the first letter */ /* for the first 'double-letter */ /* check. */ end=out_pntr+3; /* Loop on input letters until */ @@ -59,7 +60,7 @@ void soundex(register my_string out_pntr, my_string in_pntr, /* letter code count = 3 */ in_pntr++; - while (out_pntr < end && (ch = get_scode(&in_pntr,remove_garbage)) != 0) + while (out_pntr < end && (ch = get_scode(cs,&in_pntr,remove_garbage)) != 0) { in_pntr++; if ((ch != '0') && (ch != last_ch)) /* if not skipped or double */ @@ -81,19 +82,19 @@ void soundex(register my_string out_pntr, my_string in_pntr, else return 0 */ -static char get_scode(char **ptr, pbool remove_garbage) +static char get_scode(CHARSET_INFO * cs,char **ptr, pbool remove_garbage) { uchar ch; if (remove_garbage) { - while (**ptr && !isalpha(**ptr)) + while (**ptr && !my_isalpha(cs,**ptr)) (*ptr)++; } - ch=toupper(**ptr); + ch=my_toupper(cs,**ptr); if (ch < 'A' || ch > 'Z') { - if (isalpha(ch)) /* If exetended alfa (country spec) */ + if (my_isalpha(cs,ch)) /* If exetended alfa (country spec) */ return '0'; /* threat as vokal */ return 0; /* Can't map */ } diff --git a/mysys/mf_tempdir.c b/mysys/mf_tempdir.c new file mode 100644 index 00000000000..1ae034af67d --- /dev/null +++ b/mysys/mf_tempdir.c @@ -0,0 +1,83 @@ +/* Copyright (C) 2000 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#include "mysys_priv.h" +#include <m_string.h> + +#if defined( __WIN__) || defined(OS2) +#define DELIM ';' +#else +#define DELIM ':' +#endif + +my_bool init_tmpdir(MY_TMPDIR *tmpdir, const char *pathlist) +{ + char *end, *copy; + char buff[FN_REFLEN]; + DYNAMIC_ARRAY t_arr; + pthread_mutex_init(&tmpdir->mutex, MY_MUTEX_INIT_FAST); + if (my_init_dynamic_array(&t_arr, sizeof(char*), 1, 5)) + return TRUE; + if (!pathlist || !pathlist[0]) + { + /* Get default temporary directory */ + pathlist=getenv("TMPDIR"); /* Use this if possible */ +#if defined( __WIN__) || defined(OS2) + if (!pathlist) + pathlist=getenv("TEMP"); + if (!pathlist) + pathlist=getenv("TMP"); +#endif + if (!pathlist || !pathlist[0]) + pathlist=(char*) P_tmpdir; + } + do + { + end=strcend(pathlist, DELIM); + convert_dirname(buff, pathlist, end); + if (!(copy=my_strdup(buff, MYF(MY_WME)))) + return TRUE; + if (insert_dynamic(&t_arr, (gptr)©)) + return TRUE; + pathlist=end+1; + } + while (*end); + freeze_size(&t_arr); + tmpdir->list=(char **)t_arr.buffer; + tmpdir->max=t_arr.elements-1; + tmpdir->cur=0; + return FALSE; +} + +char *my_tmpdir(MY_TMPDIR *tmpdir) +{ + char *dir; + pthread_mutex_lock(&tmpdir->mutex); + dir=tmpdir->list[tmpdir->cur]; + tmpdir->cur= (tmpdir->cur == tmpdir->max) ? 0 : tmpdir->cur+1; + pthread_mutex_unlock(&tmpdir->mutex); + return dir; +} + +void free_tmpdir(MY_TMPDIR *tmpdir) +{ + uint i; + for (i=0; i<=tmpdir->max; i++) + my_free(tmpdir->list[i], MYF(0)); + my_free((gptr)tmpdir->list, MYF(0)); + pthread_mutex_destroy(&tmpdir->mutex); +} + diff --git a/mysys/mf_wfile.c b/mysys/mf_wfile.c index e9e12c72755..3c10de1f238 100644 --- a/mysys/mf_wfile.c +++ b/mysys/mf_wfile.c @@ -54,13 +54,6 @@ WF_PACK *wf_comp(my_string str) for (pos=str ; *pos ; pos++) ant+= test(*pos == ' ' || *pos == ','); -#ifdef FN_UPPER_CASE - caseup(str,(int) (pos-str)); -#endif -#ifdef FN_LOWER_CASE - casedn(str,(int) (pos-str)); -#endif - if ((ret= (WF_PACK*) my_malloc((uint) ant*(sizeof(my_string*)+2)+ sizeof(WF_PACK)+ (uint) strlen(str)+1, MYF(MY_WME))) diff --git a/mysys/mulalloc.c b/mysys/mulalloc.c index 6b025bca699..e1eb1c00602 100644 --- a/mysys/mulalloc.c +++ b/mysys/mulalloc.c @@ -19,16 +19,18 @@ /* Malloc many pointers at the same time + Only ptr1 can be free'd, and doing this will free all + the memory allocated. ptr2, etc all point inside big allocated + memory area. SYNOPSIS my_multi_malloc() - myFlags Flags - ... Multiple arguments terminated by null ptr - - ptr, length - ptr, length + myFlags Flags + ptr1, length1 Multiple arguments terminated by null ptr + ptr2, length2 ... + ... NULL -*/ +*/ gptr my_multi_malloc(myf myFlags, ...) { diff --git a/mysys/my_error.c b/mysys/my_error.c index 5ce061212b5..cd41589f366 100644 --- a/mysys/my_error.c +++ b/mysys/my_error.c @@ -69,7 +69,7 @@ int my_error(int nr,myf MyFlags, ...) else { /* Skipp if max size is used (to be compatible with printf) */ - while (isdigit(*tpos) || *tpos == '.' || *tpos == '-') + while (my_isdigit(system_charset_info, *tpos) || *tpos == '.' || *tpos == '-') tpos++; if (*tpos == 'l') /* Skipp 'l' argument */ tpos++; @@ -120,7 +120,7 @@ int my_error(int nr,myf MyFlags, ...) ... variable list */ -int my_printf_error (uint error, const char *format, myf MyFlags, ...) +int my_printf_error(uint error, const char *format, myf MyFlags, ...) { va_list args; char ebuff[ERRMSGSIZE+20]; diff --git a/mysys/my_getwd.c b/mysys/my_getwd.c index 63ab17b4c51..adf131c5fd0 100644 --- a/mysys/my_getwd.c +++ b/mysys/my_getwd.c @@ -109,7 +109,8 @@ int my_setwd(const char *dir, myf MyFlags) uint drive,drives; pos++; /* Skipp FN_DEVCHAR */ - drive=(uint) (toupper(dir[0])-'A'+1); drives= (uint) -1; + drive=(uint) (my_toupper(system_charset_info,dir[0])-'A'+1); + drives= (uint) -1; if ((pos-(byte*) dir) == 2 && drive > 0 && drive < 32) { #ifdef OS2 diff --git a/mysys/my_handler.c b/mysys/my_handler.c new file mode 100644 index 00000000000..2fd7f1fcdee --- /dev/null +++ b/mysys/my_handler.c @@ -0,0 +1,411 @@ +/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + MA 02111-1307, USA */ + +#include "my_handler.h" + +int mi_compare_text(CHARSET_INFO *charset_info, uchar *a, uint a_length, + uchar *b, uint b_length, my_bool part_key) +{ + if (part_key && b_length < a_length) + a_length=b_length; + return my_strnncoll(charset_info, a, a_length, b, b_length); +} + +static int compare_bin(uchar *a, uint a_length, uchar *b, uint b_length, + my_bool part_key) +{ + uint length= min(a_length,b_length); + uchar *end= a+ length; + int flag; + + while (a < end) + if ((flag= (int) *a++ - (int) *b++)) + return flag; + if (part_key && b_length < a_length) + return 0; + return (int) (a_length-b_length); +} + + +/* + Compare two keys + + SYNOPSIS + ha_key_cmp() + keyseg Key segments of key to compare + a First key to compare, in format from _mi_pack_key() + This is normally key specified by user + b Second key to compare. This is always from a row + key_length Length of key to compare. This can be shorter than + a to just compare sub keys + next_flag How keys should be compared + If bit SEARCH_FIND is not set the keys includes the row + position and this should also be compared + + NOTES + Number-keys can't be splited + + RETURN VALUES + <0 If a < b + 0 If a == b + >0 If a > b +*/ + +#define FCMP(A,B) ((int) (A) - (int) (B)) + +int ha_key_cmp(register HA_KEYSEG *keyseg, register uchar *a, + register uchar *b, uint key_length, uint nextflag, + uint *diff_pos) +{ + int flag; + int16 s_1,s_2; + int32 l_1,l_2; + uint32 u_1,u_2; + float f_1,f_2; + double d_1,d_2; + uint next_key_length; + + *diff_pos=0; + for ( ; (int) key_length >0 ; key_length=next_key_length, keyseg++) + { + uchar *end; + uint piks=! (keyseg->flag & HA_NO_SORT); + (*diff_pos)++; + + /* Handle NULL part */ + if (keyseg->null_bit) + { + key_length--; + if (*a != *b && piks) + { + flag = (int) *a - (int) *b; + return ((keyseg->flag & HA_REVERSE_SORT) ? -flag : flag); + } + b++; + if (!*a++) /* If key was NULL */ + { + if (nextflag == (SEARCH_FIND | SEARCH_UPDATE)) + nextflag=SEARCH_SAME; /* Allow duplicate keys */ + else if (nextflag & SEARCH_NULL_ARE_NOT_EQUAL) + { + /* + This is only used from mi_check() to calculate cardinality. + It can't be used when searching for a key as this would cause + compare of (a,b) and (b,a) to return the same value. + */ + return -1; + } + next_key_length=key_length; + continue; /* To next key part */ + } + } + end= a+ min(keyseg->length,key_length); + next_key_length=key_length-keyseg->length; + + switch ((enum ha_base_keytype) keyseg->type) { + case HA_KEYTYPE_TEXT: /* Ascii; Key is converted */ + if (keyseg->flag & HA_SPACE_PACK) + { + int a_length,b_length,pack_length; + get_key_length(a_length,a); + get_key_pack_length(b_length,pack_length,b); + next_key_length=key_length-b_length-pack_length; + + if (piks && + (flag=mi_compare_text(keyseg->charset,a,a_length,b,b_length, + (my_bool) ((nextflag & SEARCH_PREFIX) && + next_key_length <= 0)))) + return ((keyseg->flag & HA_REVERSE_SORT) ? -flag : flag); + a+=a_length; + b+=b_length; + break; + } + else + { + uint length=(uint) (end-a), a_length=length, b_length=length; + if (!(nextflag & SEARCH_PREFIX)) + { + while (a_length && a[a_length-1] == ' ') + a_length--; + while (b_length && b[b_length-1] == ' ') + b_length--; + } + if (piks && + (flag= mi_compare_text(keyseg->charset, a, a_length, b, b_length, + (my_bool) ((nextflag & SEARCH_PREFIX) && + next_key_length <= 0)))) + return ((keyseg->flag & HA_REVERSE_SORT) ? -flag : flag); + a=end; + b+=length; + } + break; + case HA_KEYTYPE_BINARY: + if (keyseg->flag & HA_SPACE_PACK) + { + int a_length,b_length,pack_length; + get_key_length(a_length,a); + get_key_pack_length(b_length,pack_length,b); + next_key_length=key_length-b_length-pack_length; + + if (piks && + (flag=compare_bin(a,a_length,b,b_length, + (my_bool) ((nextflag & SEARCH_PREFIX) && + next_key_length <= 0)))) + return ((keyseg->flag & HA_REVERSE_SORT) ? -flag : flag); + a+=a_length; + b+=b_length; + break; + } + else + { + uint length=keyseg->length; + if (piks && + (flag=compare_bin(a,length,b,length, + (my_bool) ((nextflag & SEARCH_PREFIX) && + next_key_length <= 0)))) + return ((keyseg->flag & HA_REVERSE_SORT) ? -flag : flag); + a+=length; + b+=length; + } + break; + case HA_KEYTYPE_VARTEXT: + { + int a_length,b_length,pack_length; + get_key_length(a_length,a); + get_key_pack_length(b_length,pack_length,b); + next_key_length=key_length-b_length-pack_length; + + if (piks && + (flag= mi_compare_text(keyseg->charset,a,a_length,b,b_length, + (my_bool) ((nextflag & SEARCH_PREFIX) && + next_key_length <= 0)))) + return ((keyseg->flag & HA_REVERSE_SORT) ? -flag : flag); + a+=a_length; + b+=b_length; + break; + } + break; + case HA_KEYTYPE_VARBINARY: + { + int a_length,b_length,pack_length; + get_key_length(a_length,a); + get_key_pack_length(b_length,pack_length,b); + next_key_length=key_length-b_length-pack_length; + + if (piks && + (flag=compare_bin(a,a_length,b,b_length, + (my_bool) ((nextflag & SEARCH_PREFIX) && + next_key_length <= 0)))) + return ((keyseg->flag & HA_REVERSE_SORT) ? -flag : flag); + a+=a_length; + b+=b_length; + break; + } + break; + case HA_KEYTYPE_INT8: + { + int i_1= (int) *((signed char*) a); + int i_2= (int) *((signed char*) b); + if (piks && (flag = CMP_NUM(i_1,i_2))) + return ((keyseg->flag & HA_REVERSE_SORT) ? -flag : flag); + a= end; + b++; + break; + } + case HA_KEYTYPE_SHORT_INT: + s_1= mi_sint2korr(a); + s_2= mi_sint2korr(b); + if (piks && (flag = CMP_NUM(s_1,s_2))) + return ((keyseg->flag & HA_REVERSE_SORT) ? -flag : flag); + a= end; + b+= 2; /* sizeof(short int); */ + break; + case HA_KEYTYPE_USHORT_INT: + { + uint16 us_1,us_2; + us_1= mi_sint2korr(a); + us_2= mi_sint2korr(b); + if (piks && (flag = CMP_NUM(us_1,us_2))) + return ((keyseg->flag & HA_REVERSE_SORT) ? -flag : flag); + a= end; + b+=2; /* sizeof(short int); */ + break; + } + case HA_KEYTYPE_LONG_INT: + l_1= mi_sint4korr(a); + l_2= mi_sint4korr(b); + if (piks && (flag = CMP_NUM(l_1,l_2))) + return ((keyseg->flag & HA_REVERSE_SORT) ? -flag : flag); + a= end; + b+= 4; /* sizeof(long int); */ + break; + case HA_KEYTYPE_ULONG_INT: + u_1= mi_sint4korr(a); + u_2= mi_sint4korr(b); + if (piks && (flag = CMP_NUM(u_1,u_2))) + return ((keyseg->flag & HA_REVERSE_SORT) ? -flag : flag); + a= end; + b+= 4; /* sizeof(long int); */ + break; + case HA_KEYTYPE_INT24: + l_1=mi_sint3korr(a); + l_2=mi_sint3korr(b); + if (piks && (flag = CMP_NUM(l_1,l_2))) + return ((keyseg->flag & HA_REVERSE_SORT) ? -flag : flag); + a= end; + b+= 3; + break; + case HA_KEYTYPE_UINT24: + l_1=mi_uint3korr(a); + l_2=mi_uint3korr(b); + if (piks && (flag = CMP_NUM(l_1,l_2))) + return ((keyseg->flag & HA_REVERSE_SORT) ? -flag : flag); + a= end; + b+= 3; + break; + case HA_KEYTYPE_FLOAT: + mi_float4get(f_1,a); + mi_float4get(f_2,b); + if (piks && (flag = CMP_NUM(f_1,f_2))) + return ((keyseg->flag & HA_REVERSE_SORT) ? -flag : flag); + a= end; + b+= 4; /* sizeof(float); */ + break; + case HA_KEYTYPE_DOUBLE: + mi_float8get(d_1,a); + mi_float8get(d_2,b); + if (piks && (flag = CMP_NUM(d_1,d_2))) + return ((keyseg->flag & HA_REVERSE_SORT) ? -flag : flag); + a= end; + b+= 8; /* sizeof(double); */ + break; + case HA_KEYTYPE_NUM: /* Numeric key */ + { + int swap_flag= 0; + int alength,blength; + + if (keyseg->flag & HA_REVERSE_SORT) + { + swap(uchar*,a,b); + swap_flag=1; /* Remember swap of a & b */ + end= a+ (int) (end-b); + } + if (keyseg->flag & HA_SPACE_PACK) + { + alength= *a++; blength= *b++; + end=a+alength; + next_key_length=key_length-blength-1; + } + else + { + alength= (int) (end-a); + blength=keyseg->length; + /* remove pre space from keys */ + for ( ; alength && *a == ' ' ; a++, alength--) ; + for ( ; blength && *b == ' ' ; b++, blength--) ; + } + if (piks) + { + if (*a == '-') + { + if (*b != '-') + return -1; + a++; b++; + swap(uchar*,a,b); + swap(int,alength,blength); + swap_flag=1-swap_flag; + alength--; blength--; + end=a+alength; + } + else if (*b == '-') + return 1; + while (alength && (*a == '+' || *a == '0')) + { + a++; alength--; + } + while (blength && (*b == '+' || *b == '0')) + { + b++; blength--; + } + if (alength != blength) + return (alength < blength) ? -1 : 1; + while (a < end) + if (*a++ != *b++) + return ((int) a[-1] - (int) b[-1]); + } + else + { + b+=(end-a); + a=end; + } + + if (swap_flag) /* Restore pointers */ + swap(uchar*,a,b); + break; + } +#ifdef HAVE_LONG_LONG + case HA_KEYTYPE_LONGLONG: + { + longlong ll_a,ll_b; + ll_a= mi_sint8korr(a); + ll_b= mi_sint8korr(b); + if (piks && (flag = CMP_NUM(ll_a,ll_b))) + return ((keyseg->flag & HA_REVERSE_SORT) ? -flag : flag); + a= end; + b+= 8; + break; + } + case HA_KEYTYPE_ULONGLONG: + { + ulonglong ll_a,ll_b; + ll_a= mi_uint8korr(a); + ll_b= mi_uint8korr(b); + if (piks && (flag = CMP_NUM(ll_a,ll_b))) + return ((keyseg->flag & HA_REVERSE_SORT) ? -flag : flag); + a= end; + b+= 8; + break; + } +#endif + case HA_KEYTYPE_END: /* Ready */ + goto end; /* diff_pos is incremented */ + } + } + (*diff_pos)++; +end: + if (!(nextflag & SEARCH_FIND)) + { + uint i; + if (nextflag & (SEARCH_NO_FIND | SEARCH_LAST)) /* Find record after key */ + return (nextflag & (SEARCH_BIGGER | SEARCH_LAST)) ? -1 : 1; + flag=0; + for (i=keyseg->length ; i-- > 0 ; ) + { + if (*a++ != *b++) + { + flag= FCMP(a[-1],b[-1]); + break; + } + } + if (nextflag & SEARCH_SAME) + return (flag); /* read same */ + if (nextflag & SEARCH_BIGGER) + return (flag <= 0 ? -1 : 1); /* read next */ + return (flag < 0 ? -1 : 1); /* read previous */ + } + return 0; +} /* ha_key_cmp */ diff --git a/mysys/my_init.c b/mysys/my_init.c index 61d9f4a4b2b..ec7cae46d53 100644 --- a/mysys/my_init.c +++ b/mysys/my_init.c @@ -17,7 +17,6 @@ #include "mysys_priv.h" #include "my_static.h" #include "mysys_err.h" -#include "m_ctype.h" #include <m_string.h> #include <m_ctype.h> #ifdef HAVE_GETRUSAGE @@ -53,7 +52,7 @@ my_bool my_init_done=0; static ulong atoi_octal(const char *str) { long int tmp; - while (*str && isspace(*str)) + while (*str && my_isspace(system_charset_info, *str)) str++; str2int(str, (*str == '0' ? 8 : 10), /* Octalt or decimalt */ @@ -132,6 +131,7 @@ void my_end(int infoflag) } } free_charsets(); + my_once_free(); if (infoflag & MY_GIVE_INFO || info_file != stderr) { #ifdef HAVE_GETRUSAGE @@ -171,7 +171,6 @@ Voluntary context switches %ld, Involuntary context switches %ld\n", } #ifdef THREAD DBUG_POP(); /* Must be done before my_thread_end */ - my_once_free(); my_thread_end(); my_thread_global_end(); #if defined(SAFE_MUTEX) diff --git a/mysys/my_once.c b/mysys/my_once.c index 1250ce24994..a4201810b03 100644 --- a/mysys/my_once.c +++ b/mysys/my_once.c @@ -23,6 +23,7 @@ #include "mysys_priv.h" #include "my_static.h" #include "mysys_err.h" +#include <m_string.h> /* Alloc for things we don't nead to free @@ -78,6 +79,25 @@ gptr my_once_alloc(unsigned int Size, myf MyFlags) } /* my_once_alloc */ +char *my_once_strdup(const char *src,myf myflags) +{ + uint len=strlen(src)+1; + char *dst=my_once_alloc(len, myflags); + if (dst) + memcpy(dst, src, len); + return dst; +} + + +char *my_once_memdup(const char *src, uint len, myf myflags) +{ + char *dst=my_once_alloc(len, myflags); + if (dst) + memcpy(dst, src, len); + return dst; +} + + /* Deallocate everything used by my_once_alloc diff --git a/mysys/my_symlink.c b/mysys/my_symlink.c index e287930ff06..abef0096e28 100644 --- a/mysys/my_symlink.c +++ b/mysys/my_symlink.c @@ -103,7 +103,8 @@ int my_symlink(const char *content, const char *linkname, myf MyFlags) #define BUFF_LEN FN_LEN #endif -int my_realpath(char *to, const char *filename, myf MyFlags) +int my_realpath(char *to, const char *filename, + myf MyFlags __attribute__((unused))) { #if defined(HAVE_REALPATH) && !defined(HAVE_purify) && !defined(HAVE_BROKEN_REALPATH) int result=0; diff --git a/mysys/my_vsnprintf.c b/mysys/my_vsnprintf.c deleted file mode 100644 index 7490ab4a9f2..00000000000 --- a/mysys/my_vsnprintf.c +++ /dev/null @@ -1,118 +0,0 @@ -/* Copyright (C) 2000 MySQL AB - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ - -#include "mysys_priv.h" -#include "mysys_err.h" -#include <m_string.h> -#include <stdarg.h> -#include <m_ctype.h> -#include <assert.h> - -int my_snprintf(char* to, size_t n, const char* fmt, ...) -{ - va_list args; - va_start(args,fmt); - return my_vsnprintf(to, n, fmt, args); -} - -int my_vsnprintf(char *to, size_t n, const char* fmt, va_list ap) -{ - char *start=to, *end=to+n-1; - for (; *fmt ; fmt++) - { - if (fmt[0] != '%') - { - if (to == end) /* End of buffer */ - break; - *to++= *fmt; /* Copy ordinary char */ - continue; - } - /* Skip if max size is used (to be compatible with printf) */ - fmt++; - while (isdigit(*fmt) || *fmt == '.' || *fmt == '-') - fmt++; - if (*fmt == 'l') - fmt++; - if (*fmt == 's') /* String parameter */ - { - reg2 char *par = va_arg(ap, char *); - uint plen,left_len = (uint)(end-to); - if (!par) par = (char*)"(null)"; - plen = (uint) strlen(par); - if (left_len <= plen) - plen = left_len - 1; - to=strnmov(to,par,plen); - continue; - } - else if (*fmt == 'd' || *fmt == 'u') /* Integer parameter */ - { - register int iarg; - if ((uint) (end-to) < 16) - break; - iarg = va_arg(ap, int); - if (*fmt == 'd') - to=int10_to_str((long) iarg,to, -10); - else - to=int10_to_str((long) (uint) iarg,to,10); - continue; - } - /* We come here on '%%', unknown code or too long parameter */ - if (to == end) - break; - *to++='%'; /* % used as % or unknown code */ - } - DBUG_ASSERT(to <= end); - *to='\0'; /* End of errmessage */ - return (uint) (to - start); -} - -#ifdef MAIN -#define OVERRUN_SENTRY 250 -static void my_printf(const char * fmt, ...) -{ - char buf[33]; - int n; - va_list ar; - va_start(ar, fmt); - buf[sizeof(buf)-1]=OVERRUN_SENTRY; - n = my_vsnprintf(buf, sizeof(buf)-1,fmt, ar); - printf(buf); - printf("n=%d, strlen=%d\n", n, strlen(buf)); - if (buf[sizeof(buf)-1] != OVERRUN_SENTRY) - { - fprintf(stderr, "Buffer overrun\n"); - abort(); - } - va_end(ar); -} - -int main() -{ - - my_printf("Hello\n"); - my_printf("Hello int, %d\n", 1); - my_printf("Hello string '%s'\n", "I am a string"); - my_printf("Hello hack hack hack hack hack hack hack %d\n", 1); - my_printf("Hello %d hack %d\n", 1, 4); - my_printf("Hello %d hack hack hack hack hack %d\n", 1, 4); - my_printf("Hello '%s' hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh\n", "hack"); - my_printf("Hello hhhhhhhhhhhhhh %d sssssssssssssss\n", 1); - my_printf("Hello %u\n", 1); - my_printf("conn %ld to: '%-.64s' user: '%-.32s' host:\ - `%-.64s' (%-.64s)", 1, 0,0,0,0); - return 0; -} -#endif diff --git a/mysys/queues.c b/mysys/queues.c index fe642131d74..f077b38ca0b 100644 --- a/mysys/queues.c +++ b/mysys/queues.c @@ -24,7 +24,26 @@ #include <queues.h> -/* Init queue */ +/* + Init queue + + SYNOPSIS + init_queue() + queue Queue to initialise + max_elements Max elements that will be put in queue + offset_to_key Offset to key in element stored in queue + Used when sending pointers to compare function + max_at_top Set to 1 if you want biggest element on top. + compare Compare function for elements, takes 3 arguments. + first_cmp_arg First argument to compare function + + NOTES + Will allocate max_element pointers for queue array + + RETURN + 0 ok + 1 Could not allocate memory +*/ int init_queue(QUEUE *queue, uint max_elements, uint offset_to_key, pbool max_at_top, int (*compare) (void *, byte *, byte *), @@ -43,15 +62,32 @@ int init_queue(QUEUE *queue, uint max_elements, uint offset_to_key, DBUG_RETURN(0); } + /* - Reinitialize queue for new usage; Note that you can't currently resize - the number of elements! If you need this, fix it :) + Reinitialize queue for other usage (deletes all elements) + + SYNOPSIS + reinit_queue() + queue Queue to initialise + max_elements Max elements that will be put in queue + offset_to_key Offset to key in element stored in queue + Used when sending pointers to compare function + max_at_top Set to 1 if you want biggest element on top. + compare Compare function for elements, takes 3 arguments. + first_cmp_arg First argument to compare function + + NOTES + You can't currently resize the number of elements! If you need this, + fix it :) + + RETURN + 0 ok + EE_OUTOFMEMORY Wrong max_elements */ - int reinit_queue(QUEUE *queue, uint max_elements, uint offset_to_key, - pbool max_at_top, int (*compare) (void *, byte *, byte *), - void *first_cmp_arg) + pbool max_at_top, int (*compare) (void *, byte *, byte *), + void *first_cmp_arg) { DBUG_ENTER("reinit_queue"); if (queue->max_elements < max_elements) @@ -66,6 +102,21 @@ int reinit_queue(QUEUE *queue, uint max_elements, uint offset_to_key, DBUG_RETURN(0); } + +/* + Delete queue + + SYNOPSIS + delete_queue() + queue Queue to delete + + IMPLEMENTATION + Just free allocated memory. + + NOTES + Can be called safely multiple times +*/ + void delete_queue(QUEUE *queue) { DBUG_ENTER("delete_queue"); @@ -116,7 +167,7 @@ byte *queue_remove(register QUEUE *queue, uint idx) return 0; #endif { - byte *element=queue->root[++idx]; /* Intern index starts from 1 */ + byte *element=queue->root[++idx]; /* Intern index starts from 1 */ queue->root[idx]=queue->root[queue->elements--]; _downheap(queue,idx); return element; @@ -126,8 +177,7 @@ byte *queue_remove(register QUEUE *queue, uint idx) /* Fix when element on top has been replaced */ #ifndef queue_replaced -void queue_replaced(queue) -QUEUE *queue; +void queue_replaced(QUEUE *queue) { _downheap(queue,1); } @@ -169,8 +219,8 @@ void _downheap(register QUEUE *queue, uint idx) static int queue_fix_cmp(QUEUE *queue, void **a, void **b) { return queue->compare(queue->first_cmp_arg, - (char*) (*a)+queue->offset_to_key, - (char*) (*b)+queue->offset_to_key); + (byte*) (*a)+queue->offset_to_key, + (byte*) (*b)+queue->offset_to_key); } /* diff --git a/mysys/test_charset.c b/mysys/test_charset.c index 15e46f3ff82..d031007a1da 100644 --- a/mysys/test_charset.c +++ b/mysys/test_charset.c @@ -21,7 +21,38 @@ #include <stdio.h> -extern void _print_csinfo(CHARSET_INFO *cs); +static void _print_array(uint8 *data, uint size) +{ + uint i; + for (i = 0; i < size; ++i) + { + if (i == 0 || i % 16 == size % 16) printf(" "); + printf(" %02x", data[i]); + if ((i+1) % 16 == size % 16) printf("\n"); + } +} + +static void _print_csinfo(CHARSET_INFO *cs) +{ + printf("%s #%d\n", cs->name, cs->number); + printf("ctype:\n"); _print_array(cs->ctype, 257); + printf("to_lower:\n"); _print_array(cs->to_lower, 256); + printf("to_upper:\n"); _print_array(cs->to_upper, 256); + printf("sort_order:\n"); _print_array(cs->sort_order, 256); + printf("collate: %3s (%d, %p, %p, %p)\n", + cs->strxfrm_multiply ? "yes" : "no", + cs->strxfrm_multiply, + cs->strnncoll, + cs->strnxfrm, + cs->like_range); + printf("multi-byte: %3s (%d, %p, %p, %p)\n", + cs->mbmaxlen > 1 ? "yes" : "no", + cs->mbmaxlen, + cs->ismbchar, + cs->ismbhead, + cs->mbcharlen); +} + int main(int argc, char **argv) { const char *the_set = MYSQL_CHARSET; @@ -46,13 +77,13 @@ int main(int argc, char **argv) { _print_csinfo(default_charset_info); fflush(stdout); - cs_list = list_charsets(MYF(MY_COMPILED_SETS | MY_CONFIG_SETS)); + cs_list = list_charsets(MYF(MY_CS_COMPILED | MY_CS_CONFIG)); printf("LIST OF CHARSETS (compiled + *.conf):\n%s\n", cs_list); - free(cs_list); + my_free(cs_list,MYF(0)); - cs_list = list_charsets(MYF(MY_INDEX_SETS | MY_LOADED_SETS)); + cs_list = list_charsets(MYF(MY_CS_INDEX | MY_CS_LOADED)); printf("LIST OF CHARSETS (index + loaded):\n%s\n", cs_list); - free(cs_list); + my_free(cs_list,MYF(0)); return 0; } diff --git a/mysys/test_xml.c b/mysys/test_xml.c new file mode 100644 index 00000000000..2a679906cbf --- /dev/null +++ b/mysys/test_xml.c @@ -0,0 +1,105 @@ +/* Copyright (C) 2000 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <fcntl.h> +#include "my_xml.h" + +static void mstr(char *str,const char *src,uint l1,uint l2) +{ + l1 = l1<l2 ? l1 : l2; + memcpy(str,src,l1); + str[l1]='\0'; +} + +static int dstr(MY_XML_PARSER *st,const char *attr, uint len) +{ + char str[1024]; + + mstr(str,attr,len,sizeof(str)-1); + printf("VALUE '%s'\n",str); + return MY_XML_OK; +} + +static int bstr(MY_XML_PARSER *st,const char *attr, uint len) +{ + char str[1024]; + + mstr(str,attr,len,sizeof(str)-1); + printf("ENTER %s\n",str); + return MY_XML_OK; +} + + +static int estr(MY_XML_PARSER *st,const char *attr, uint len) +{ + char str[1024]; + + mstr(str,attr,len,sizeof(str)-1); + printf("LEAVE %s\n",str); + return MY_XML_OK; +} + +static void usage(const char *prog) +{ + printf("Usage:\n"); + printf("%s xmlfile\n",prog); +} + +int main(int ac, char **av) +{ + char str[1024*64]=""; + const char *fn; + int f; + uint len; + MY_XML_PARSER p; + + if (ac<2) + { + usage(av[0]); + return 0; + } + + fn=av[1]?av[1]:"test.xml"; + if ((f=open(fn,O_RDONLY))<0) + { + fprintf(stderr,"Err '%s'\n",fn); + return 1; + } + + len=read(f,str,sizeof(str)-1); + str[len]='\0'; + + my_xml_parser_create(&p); + + my_xml_set_enter_handler(&p,bstr); + my_xml_set_value_handler(&p,dstr); + my_xml_set_leave_handler(&p,estr); + + if (MY_XML_OK!=(f=my_xml_parse(&p,str,len))) + { + printf("ERROR at line %d pos %d '%s'\n", + my_xml_error_lineno(&p)+1, + my_xml_error_pos(&p), + my_xml_error_string(&p)); + } + + my_xml_parser_free(&p); + + return 0; +} diff --git a/mysys/thr_lock.c b/mysys/thr_lock.c index 0288c7c1cbe..c796bd1956a 100644 --- a/mysys/thr_lock.c +++ b/mysys/thr_lock.c @@ -91,7 +91,7 @@ enum thr_lock_type thr_upgraded_concurrent_insert_lock = TL_WRITE; #define MAX_LOCKS 100 -static LIST *thread_list; /* List of threads in use */ +LIST *thr_lock_thread_list; /* List of threads in use */ ulong max_write_lock_count= ~(ulong) 0L; static inline pthread_cond_t *get_cond(void) @@ -307,7 +307,7 @@ void thr_lock_init(THR_LOCK *lock) pthread_mutex_lock(&THR_LOCK_lock); /* Add to locks in use */ lock->list.data=(void*) lock; - thread_list=list_add(thread_list,&lock->list); + thr_lock_thread_list=list_add(thr_lock_thread_list,&lock->list); pthread_mutex_unlock(&THR_LOCK_lock); DBUG_VOID_RETURN; } @@ -318,7 +318,7 @@ void thr_lock_delete(THR_LOCK *lock) DBUG_ENTER("thr_lock_delete"); VOID(pthread_mutex_destroy(&lock->mutex)); pthread_mutex_lock(&THR_LOCK_lock); - thread_list=list_delete(thread_list,&lock->list); + thr_lock_thread_list=list_delete(thr_lock_thread_list,&lock->list); pthread_mutex_unlock(&THR_LOCK_lock); DBUG_VOID_RETURN; } @@ -1061,7 +1061,7 @@ void thr_print_locks(void) pthread_mutex_lock(&THR_LOCK_lock); puts("Current locks:"); - for (list=thread_list ; list && count++ < MAX_THREADS ; list=rest(list)) + for (list=thr_lock_thread_list ; list && count++ < MAX_THREADS ; list=rest(list)) { THR_LOCK *lock=(THR_LOCK*) list->data; VOID(pthread_mutex_lock(&lock->mutex)); diff --git a/mysys/tree.c b/mysys/tree.c index ea5cf79f084..a3b69ebff5e 100644 --- a/mysys/tree.c +++ b/mysys/tree.c @@ -17,31 +17,39 @@ /* Code for handling red-black (balanced) binary trees. key in tree is allocated accrding to following: - 1) If free_element function is given to init_tree or size < 0 then tree - will not allocate keys and only a pointer to each key is saved in tree. - key_sizes must be 0 to init and search. + + 1) If size < 0 then tree will not allocate keys and only a pointer to + each key is saved in tree. + compare and search functions uses and returns key-pointer + + 2) If size == 0 then there are two options: + - key_size != 0 to tree_insert: The key will be stored in the tree. + - key_size == 0 to tree_insert: A pointer to the key is stored. compare and search functions uses and returns key-pointer. - 2) if key_size is given to init_tree then each node will continue the + + 3) if key_size is given to init_tree then each node will continue the key and calls to insert_key may increase length of key. if key_size > sizeof(pointer) and key_size is a multiple of 8 (double allign) then key will be put on a 8 alligned adress. Else the key will be on adress (element+1). This is transparent for user compare and search functions uses a pointer to given key-argument. - 3) If init_tree - keysize is 0 then key_size must be given to tree_insert - and tree_insert will alloc space for key. - compare and search functions uses a pointer to given key-argument. + + - If you use a free function for tree-elements and you are freeing + the element itself, you should use key_size = 0 to init_tree and + tree_search The actual key in TREE_ELEMENT is saved as a pointer or after the TREE_ELEMENT struct. If one uses only pointers in tree one can use tree_set_pointer() to change address of data. - Copyright Monty Program KB. - By monty. + + Implemented by monty. */ #include "mysys_priv.h" #include <m_string.h> #include <my_tree.h> +#include "my_base.h" #define BLACK 1 #define RED 0 @@ -87,6 +95,7 @@ void init_tree(TREE *tree, uint default_alloc_size, uint memory_limit, tree->custom_arg = custom_arg; tree->null_element.colour=BLACK; tree->null_element.left=tree->null_element.right=0; + tree->flag= 0; if (!free_element && size >= 0 && ((uint) size <= sizeof(void*) || ((uint) size & (sizeof(void*)-1)))) { @@ -169,7 +178,8 @@ static void delete_tree_element(TREE *tree, TREE_ELEMENT *element) parent[0] = & parent[-1][0]->right */ -TREE_ELEMENT *tree_insert(TREE *tree, void *key, uint key_size) +TREE_ELEMENT *tree_insert(TREE *tree, void *key, uint key_size, + void* custom_arg) { int cmp; TREE_ELEMENT *element,***parent; @@ -179,8 +189,8 @@ TREE_ELEMENT *tree_insert(TREE *tree, void *key, uint key_size) for (;;) { if (element == &tree->null_element || - (cmp=(*tree->compare)(tree->custom_arg, - ELEMENT_KEY(tree,element),key)) == 0) + (cmp = (*tree->compare)(custom_arg, ELEMENT_KEY(tree,element), + key)) == 0) break; if (cmp < 0) { @@ -200,7 +210,7 @@ TREE_ELEMENT *tree_insert(TREE *tree, void *key, uint key_size) && tree->allocated > tree->memory_limit) { reset_tree(tree); - return tree_insert(tree, key, key_size); + return tree_insert(tree, key, key_size, custom_arg); } key_size+=tree->size_of_element; @@ -231,13 +241,16 @@ TREE_ELEMENT *tree_insert(TREE *tree, void *key, uint key_size) rb_insert(tree,parent,element); /* rebalance tree */ } else + { + if (tree->flag & TREE_NO_DUPS) + return(NULL); element->count++; + } DBUG_EXECUTE("check_tree", test_rb_tree(tree->root);); return element; } - -int tree_delete(TREE *tree, void *key) +int tree_delete(TREE *tree, void *key, void *custom_arg) { int cmp,remove_colour; TREE_ELEMENT *element,***parent, ***org_parent, *nod; @@ -250,8 +263,8 @@ int tree_delete(TREE *tree, void *key) { if (element == &tree->null_element) return 1; /* Was not in tree */ - if ((cmp=(*tree->compare)(tree->custom_arg, - ELEMENT_KEY(tree,element),key)) == 0) + if ((cmp = (*tree->compare)(custom_arg, ELEMENT_KEY(tree,element), + key)) == 0) break; if (cmp < 0) { @@ -298,7 +311,7 @@ int tree_delete(TREE *tree, void *key) } -void *tree_search(TREE *tree, void *key) +void *tree_search(TREE *tree, void *key, void *custom_arg) { int cmp; TREE_ELEMENT *element=tree->root; @@ -307,8 +320,8 @@ void *tree_search(TREE *tree, void *key) { if (element == &tree->null_element) return (void*) 0; - if ((cmp=(*tree->compare)(tree->custom_arg, - ELEMENT_KEY(tree,element),key)) == 0) + if ((cmp = (*tree->compare)(custom_arg, ELEMENT_KEY(tree,element), + key)) == 0) return ELEMENT_KEY(tree,element); if (cmp < 0) element=element->right; @@ -317,6 +330,181 @@ void *tree_search(TREE *tree, void *key) } } +void *tree_search_key(TREE *tree, const void *key, + TREE_ELEMENT **parents, TREE_ELEMENT ***last_pos, + enum ha_rkey_function flag, void *custom_arg) +{ + int cmp; + TREE_ELEMENT *element= tree->root; + TREE_ELEMENT **last_left_step_parent= NULL, **last_right_step_parent= NULL; + TREE_ELEMENT **last_equal_element= NULL; + +/* + TODO: support for HA_READ_KEY_OR_PREV, HA_READ_PREFIX flags if needed. +*/ + + *parents = &tree->null_element; + while (element != &tree->null_element) + { + *++parents= element; + if ((cmp= (*tree->compare)(custom_arg, ELEMENT_KEY(tree, element), + key)) == 0) + { + switch (flag) { + case HA_READ_KEY_EXACT: + case HA_READ_KEY_OR_NEXT: + case HA_READ_BEFORE_KEY: + last_equal_element= parents; + cmp= 1; + break; + case HA_READ_AFTER_KEY: + cmp= -1; + break; + case HA_READ_PREFIX_LAST: + case HA_READ_PREFIX_LAST_OR_PREV: + last_equal_element= parents; + cmp= -1; + break; + default: + return NULL; + } + } + if (cmp < 0) /* element < key */ + { + last_right_step_parent= parents; + element= element->right; + } + else + { + last_left_step_parent= parents; + element= element->left; + } + } + switch (flag) { + case HA_READ_KEY_EXACT: + case HA_READ_PREFIX_LAST: + *last_pos= last_equal_element; + break; + case HA_READ_KEY_OR_NEXT: + *last_pos= last_equal_element ? last_equal_element : last_left_step_parent; + break; + case HA_READ_AFTER_KEY: + *last_pos= last_left_step_parent; + break; + case HA_READ_PREFIX_LAST_OR_PREV: + *last_pos= last_equal_element ? last_equal_element : last_right_step_parent; + break; + case HA_READ_BEFORE_KEY: + *last_pos= last_right_step_parent; + break; + default: + return NULL; + } + return *last_pos ? ELEMENT_KEY(tree, **last_pos) : NULL; +} + +/* + Search first (the most left) or last (the most right) tree element +*/ +void *tree_search_edge(TREE *tree, TREE_ELEMENT **parents, + TREE_ELEMENT ***last_pos, int child_offs) +{ + TREE_ELEMENT *element= tree->root; + + *parents= &tree->null_element; + while (element != &tree->null_element) + { + *++parents= element; + element= ELEMENT_CHILD(element, child_offs); + } + *last_pos= parents; + return **last_pos != &tree->null_element ? + ELEMENT_KEY(tree, **last_pos) : NULL; +} + +void *tree_search_next(TREE *tree, TREE_ELEMENT ***last_pos, int l_offs, + int r_offs) +{ + TREE_ELEMENT *x= **last_pos; + + if (ELEMENT_CHILD(x, r_offs) != &tree->null_element) + { + x= ELEMENT_CHILD(x, r_offs); + *++*last_pos= x; + while (ELEMENT_CHILD(x, l_offs) != &tree->null_element) + { + x= ELEMENT_CHILD(x, l_offs); + *++*last_pos= x; + } + return ELEMENT_KEY(tree, x); + } + else + { + TREE_ELEMENT *y= *--*last_pos; + while (y != &tree->null_element && x == ELEMENT_CHILD(y, r_offs)) + { + x= y; + y= *--*last_pos; + } + return y == &tree->null_element ? NULL : ELEMENT_KEY(tree, y); + } +} + +/* + Expected that tree is fully balanced + (each path from root to leaf has the same length) +*/ +ha_rows tree_record_pos(TREE *tree, const void *key, + enum ha_rkey_function flag, void *custom_arg) +{ + int cmp; + TREE_ELEMENT *element= tree->root; + double left= 1; + double right= tree->elements_in_tree; + ha_rows last_equal_pos= HA_POS_ERROR; + + while (element != &tree->null_element) + { + if ((cmp= (*tree->compare)(custom_arg, ELEMENT_KEY(tree, element), + key)) == 0) + { + switch (flag) { + case HA_READ_KEY_EXACT: + last_equal_pos= (ha_rows) ((left + right) / 2); + cmp= 1; + break; + case HA_READ_BEFORE_KEY: + cmp= 1; + break; + case HA_READ_AFTER_KEY: + cmp= -1; + break; + default: + return HA_POS_ERROR; + } + } + if (cmp < 0) /* element < key */ + { + element= element->right; + left= (left + right) / 2; + } + else + { + element= element->left; + right= (left + right) / 2; + } + } + switch (flag) { + case HA_READ_KEY_EXACT: + return last_equal_pos; + case HA_READ_BEFORE_KEY: + return (ha_rows) right; + case HA_READ_AFTER_KEY: + return (ha_rows) left; + default: + return HA_POS_ERROR; + } +} int tree_walk(TREE *tree, tree_walk_action action, void *argument, TREE_WALK visit) { diff --git a/mysys/typelib.c b/mysys/typelib.c index e2c8eade5c8..e524f903b5d 100644 --- a/mysys/typelib.c +++ b/mysys/typelib.c @@ -47,7 +47,9 @@ int find_type(my_string x, TYPELIB *typelib, uint full_name) find=0; for (pos=0 ; (j=typelib->type_names[pos]) ; pos++) { - for (i=x ; *i && toupper(*i) == toupper(*j) ; i++, j++) ; + for (i=x ; + *i && my_toupper(system_charset_info,*i) == + my_toupper(system_charset_info,*j) ; i++, j++) ; if (! *j) { while (*i == ' ') |