diff options
Diffstat (limited to 'mysys')
69 files changed, 5759 insertions, 2014 deletions
diff --git a/mysys/ChangeLog b/mysys/ChangeLog index e24fc00b493..7a426106667 100644 --- a/mysys/ChangeLog +++ b/mysys/ChangeLog @@ -91,7 +91,7 @@ Tue Mar 26 15:09:45 1991 Mikael WIDENIUS (monty at panther) Sat Mar 23 10:49:49 1991 Michael Widenius (monty at LYNX) - * Added init of alarm variables to skipp some warnings from gcc. + * Added init of alarm variables to skip some warnings from gcc. Tue Mar 5 16:50:34 1991 Michael Widenius (monty at LYNX) @@ -124,7 +124,7 @@ Mon Aug 27 22:20:38 1990 Michael Widenius (monty at lynx) Sun Apr 1 23:29:47 1990 Monty (monty at monty) * Changed mf_keydisk.c to have separate functions for read and write. - Read can now return pointer to intern key-buffer to skipp + Read can now return pointer to intern key-buffer to skip unessessary memcpy-s. Fri Mar 23 23:03:39 1990 Monty (monty at monty) diff --git a/mysys/Makefile.am b/mysys/Makefile.am index 5dc54817fd7..d4290bbc49b 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 @@ -25,21 +25,21 @@ noinst_HEADERS = mysys_priv.h my_static.h \ my_os2cond.c my_os2dirsrch.c my_os2dirsrch.h \ my_os2dlfcn.c my_os2file64.c my_os2mutex.c \ my_os2thread.c my_os2tls.c -libmysys_a_SOURCES = my_init.c my_getwd.c mf_getdate.c\ - mf_path.c mf_loadpath.c\ +libmysys_a_SOURCES = my_init.c my_getwd.c mf_getdate.c \ + mf_path.c mf_loadpath.c my_file.c \ my_open.c my_create.c my_dup.c my_seek.c my_read.c \ my_pread.c my_write.c \ - mf_keycache.c \ + mf_keycache.c mf_keycaches.c my_crc32.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 \ + my_fopen.c my_fstream.c my_getsystime.c \ my_error.c errors.c my_div.c my_messnc.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_wcomp.c mf_wfile.c my_gethwaddr.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_sync.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 charset-def.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,8 +104,16 @@ 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) +test_gethwaddr$(EXEEXT): my_gethwaddr.c $(LIBRARIES) + $(CP) $(srcdir)/my_gethwaddr.c ./test_gethwaddr.c + $(LINK) $(FLAGS) -DMAIN ./test_gethwaddr.c $(LDADD) $(LIBS) + $(RM) -f ./test_gethwaddr.c + # Don't update the files from bitkeeper %::SCCS/s.% diff --git a/mysys/charset-def.c b/mysys/charset-def.c new file mode 100644 index 00000000000..a573581a8ea --- /dev/null +++ b/mysys/charset-def.c @@ -0,0 +1,127 @@ +/* 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 all compiled character sets into the client + If a client don't want to use all of them, he can define his own + init_compiled_charsets() that only adds those that he wants +*/ + +#ifdef HAVE_CHARSET_ucs2 +extern CHARSET_INFO my_charset_ucs2_general_uca; +extern CHARSET_INFO my_charset_ucs2_icelandic_uca_ci; +extern CHARSET_INFO my_charset_ucs2_latvian_uca_ci; +extern CHARSET_INFO my_charset_ucs2_romanian_uca_ci; +extern CHARSET_INFO my_charset_ucs2_slovenian_uca_ci; +extern CHARSET_INFO my_charset_ucs2_polish_uca_ci; +extern CHARSET_INFO my_charset_ucs2_estonian_uca_ci; +extern CHARSET_INFO my_charset_ucs2_spanish_uca_ci; +extern CHARSET_INFO my_charset_ucs2_swedish_uca_ci; +extern CHARSET_INFO my_charset_ucs2_turkish_uca_ci; +extern CHARSET_INFO my_charset_ucs2_czech_uca_ci; +extern CHARSET_INFO my_charset_ucs2_danish_uca_ci; +extern CHARSET_INFO my_charset_ucs2_lithuanian_uca_ci; +extern CHARSET_INFO my_charset_ucs2_slovak_uca_ci; +extern CHARSET_INFO my_charset_ucs2_spanish2_uca_ci; +#endif + +my_bool init_compiled_charsets(myf flags __attribute__((unused))) +{ + CHARSET_INFO *cs; + + add_compiled_collation(&my_charset_bin); + + add_compiled_collation(&my_charset_latin1); + add_compiled_collation(&my_charset_latin1_bin); + add_compiled_collation(&my_charset_latin1_german2_ci); + +#ifdef HAVE_CHARSET_big5 + add_compiled_collation(&my_charset_big5_chinese_ci); + add_compiled_collation(&my_charset_big5_bin); +#endif + +#ifdef HAVE_CHARSET_cp1250 + add_compiled_collation(&my_charset_cp1250_czech_ci); +#endif + +#ifdef HAVE_CHARSET_latin2 + add_compiled_collation(&my_charset_latin2_czech_ci); +#endif + +#ifdef HAVE_CHARSET_euckr + add_compiled_collation(&my_charset_euckr_korean_ci); + add_compiled_collation(&my_charset_euckr_bin); +#endif + +#ifdef HAVE_CHARSET_gb2312 + add_compiled_collation(&my_charset_gb2312_chinese_ci); + add_compiled_collation(&my_charset_gb2312_bin); +#endif + +#ifdef HAVE_CHARSET_gbk + add_compiled_collation(&my_charset_gbk_chinese_ci); + add_compiled_collation(&my_charset_gbk_bin); +#endif + +#ifdef HAVE_CHARSET_sjis + add_compiled_collation(&my_charset_sjis_japanese_ci); + add_compiled_collation(&my_charset_sjis_bin); +#endif + +#ifdef HAVE_CHARSET_tis620 + add_compiled_collation(&my_charset_tis620_thai_ci); + add_compiled_collation(&my_charset_tis620_bin); +#endif + +#ifdef HAVE_CHARSET_ucs2 + add_compiled_collation(&my_charset_ucs2_general_ci); + add_compiled_collation(&my_charset_ucs2_bin); + add_compiled_collation(&my_charset_ucs2_general_uca); + add_compiled_collation(&my_charset_ucs2_general_uca); + add_compiled_collation(&my_charset_ucs2_icelandic_uca_ci); + add_compiled_collation(&my_charset_ucs2_latvian_uca_ci); + add_compiled_collation(&my_charset_ucs2_romanian_uca_ci); + add_compiled_collation(&my_charset_ucs2_slovenian_uca_ci); + add_compiled_collation(&my_charset_ucs2_polish_uca_ci); + add_compiled_collation(&my_charset_ucs2_estonian_uca_ci); + add_compiled_collation(&my_charset_ucs2_spanish_uca_ci); + add_compiled_collation(&my_charset_ucs2_swedish_uca_ci); + add_compiled_collation(&my_charset_ucs2_turkish_uca_ci); + add_compiled_collation(&my_charset_ucs2_czech_uca_ci); + add_compiled_collation(&my_charset_ucs2_danish_uca_ci); + add_compiled_collation(&my_charset_ucs2_lithuanian_uca_ci); + add_compiled_collation(&my_charset_ucs2_slovak_uca_ci); + add_compiled_collation(&my_charset_ucs2_spanish2_uca_ci); +#endif + +#ifdef HAVE_CHARSET_ujis + add_compiled_collation(&my_charset_ujis_japanese_ci); + add_compiled_collation(&my_charset_ujis_bin); +#endif + +#ifdef HAVE_CHARSET_utf8 + add_compiled_collation(&my_charset_utf8_general_ci); + add_compiled_collation(&my_charset_utf8_bin); +#endif + + /* Copy compiled charsets */ + for (cs=compiled_charsets; cs->name; cs++) + add_compiled_collation(cs); + + return FALSE; +} diff --git a/mysys/charset.c b/mysys/charset.c index 5e5be9e1b42..72f102a2296 100644 --- a/mysys/charset.c +++ b/mysys/charset.c @@ -19,76 +19,306 @@ #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; +/* + 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 collation ID + - Setting server default character set +*/ + +my_bool my_charset_same(CHARSET_INFO *cs1, CHARSET_INFO *cs2) +{ + return ((cs1 == cs2) || !strcmp(cs1->csname,cs2->csname)); +} + + +static my_bool init_state_maps(CHARSET_INFO *cs) +{ + uint i; + uchar *state_map; + uchar *ident_map; + + if (!(cs->state_map= (uchar*) my_once_alloc(256, MYF(MY_WME)))) + return 1; + + if (!(cs->ident_map= (uchar*) my_once_alloc(256, MYF(MY_WME)))) + return 1; + + state_map= cs->state_map; + ident_map= cs->ident_map; + + /* Fill state_map with states to get a faster parser */ + for (i=0; i < 256 ; i++) + { + if (my_isalpha(cs,i)) + state_map[i]=(uchar) MY_LEX_IDENT; + else if (my_isdigit(cs,i)) + state_map[i]=(uchar) MY_LEX_NUMBER_IDENT; +#if defined(USE_MB) && defined(USE_MB_IDENT) + else if (my_mbcharlen(cs, i)>1) + state_map[i]=(uchar) MY_LEX_IDENT; +#endif + else if (!my_isgraph(cs,i)) + state_map[i]=(uchar) MY_LEX_SKIP; + else + state_map[i]=(uchar) MY_LEX_CHAR; + } + state_map[(uchar)'_']=state_map[(uchar)'$']=(uchar) MY_LEX_IDENT; + state_map[(uchar)'\'']=(uchar) MY_LEX_STRING; + state_map[(uchar)'.']=(uchar) MY_LEX_REAL_OR_POINT; + state_map[(uchar)'>']=state_map[(uchar)'=']=state_map[(uchar)'!']= (uchar) MY_LEX_CMP_OP; + state_map[(uchar)'<']= (uchar) MY_LEX_LONG_CMP_OP; + state_map[(uchar)'&']=state_map[(uchar)'|']=(uchar) MY_LEX_BOOL; + state_map[(uchar)'#']=(uchar) MY_LEX_COMMENT; + state_map[(uchar)';']=(uchar) MY_LEX_SEMICOLON; + state_map[(uchar)':']=(uchar) MY_LEX_SET_VAR; + state_map[0]=(uchar) MY_LEX_EOL; + state_map[(uchar)'\\']= (uchar) MY_LEX_ESCAPE; + state_map[(uchar)'/']= (uchar) MY_LEX_LONG_COMMENT; + state_map[(uchar)'*']= (uchar) MY_LEX_END_LONG_COMMENT; + state_map[(uchar)'@']= (uchar) MY_LEX_USER_END; + state_map[(uchar) '`']= (uchar) MY_LEX_USER_VARIABLE_DELIMITER; + state_map[(uchar)'"']= (uchar) MY_LEX_STRING_OR_DELIMITER; -#define MAX_LINE 1024 + /* + Create a second map to make it faster to find identifiers + */ + for (i=0; i < 256 ; i++) + { + ident_map[i]= (uchar) (state_map[i] == MY_LEX_IDENT || + state_map[i] == MY_LEX_NUMBER_IDENT); + } -#define CTYPE_TABLE_SIZE 257 -#define TO_LOWER_TABLE_SIZE 256 -#define TO_UPPER_TABLE_SIZE 256 -#define SORT_ORDER_TABLE_SIZE 256 + /* Special handling of hex and binary strings */ + state_map[(uchar)'x']= state_map[(uchar)'X']= (uchar) MY_LEX_IDENT_OR_HEX; + state_map[(uchar)'b']= state_map[(uchar)'b']= (uchar) MY_LEX_IDENT_OR_BIN; + state_map[(uchar)'n']= state_map[(uchar)'N']= (uchar) MY_LEX_IDENT_OR_NCHAR; + return 0; +} -struct simpleconfig_buf_st { - FILE *f; - char buf[MAX_LINE]; - char *p; -}; -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() */ + if (cs->state & MY_CS_BINSORT) + cs->coll= &my_collation_8bit_bin_handler; + else + cs->coll= &my_collation_8bit_simple_ci_handler; + + cs->cset= &my_charset_8bit_handler; } -static char *name_from_csnum(CS_ID **cs, uint number) + + +static int cs_copy_data(CHARSET_INFO *to, CHARSET_INFO *from) { - CS_ID **c; - if(cs) - for (c = cs; *c; ++c) - if ((*c)->number == number) - return (*c)->name; - return (char*) "?"; /* this mimics find_type() */ + to->number= from->number ? from->number : to->number; + + if (from->csname) + if (!(to->csname= my_once_strdup(from->csname,MYF(MY_WME)))) + goto err; + + if (from->name) + if (!(to->name= my_once_strdup(from->name,MYF(MY_WME)))) + goto err; + + if (from->comment) + if (!(to->comment= my_once_strdup(from->comment,MYF(MY_WME)))) + goto err; + + if (from->ctype) + { + if (!(to->ctype= (uchar*) my_once_memdup((char*) from->ctype, + MY_CS_CTYPE_TABLE_SIZE, + MYF(MY_WME)))) + goto err; + if (init_state_maps(to)) + goto err; + } + if (from->to_lower) + if (!(to->to_lower= (uchar*) my_once_memdup((char*) from->to_lower, + MY_CS_TO_LOWER_TABLE_SIZE, + MYF(MY_WME)))) + goto err; + + if (from->to_upper) + if (!(to->to_upper= (uchar*) my_once_memdup((char*) from->to_upper, + MY_CS_TO_UPPER_TABLE_SIZE, + MYF(MY_WME)))) + goto err; + if (from->sort_order) + { + if (!(to->sort_order= (uchar*) my_once_memdup((char*) from->sort_order, + MY_CS_SORT_ORDER_TABLE_SIZE, + MYF(MY_WME)))) + goto err; + + } + if (from->tab_to_uni) + { + uint sz= MY_CS_TO_UNI_TABLE_SIZE*sizeof(uint16); + if (!(to->tab_to_uni= (uint16*) my_once_memdup((char*)from->tab_to_uni, + sz, MYF(MY_WME)))) + goto err; + } + if (from->tailoring) + if (!(to->tailoring= my_once_strdup(from->tailoring,MYF(MY_WME)))) + goto err; + + return 0; + +err: + return 1; } -static my_bool get_word(struct simpleconfig_buf_st *fb, char *buf) + + +static my_bool simple_cs_is_full(CHARSET_INFO *cs) { - char *endptr=fb->p; + return ((cs->csname && cs->tab_to_uni && cs->ctype && cs->to_upper && + cs->to_lower) && + (cs->number && cs->name && + (cs->sort_order || (cs->state & MY_CS_BINSORT) ))); +} - for (;;) + +static int add_collation(CHARSET_INFO *cs) +{ + if (cs->name && (cs->number || (cs->number=get_collation_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 (cs->primary_number == cs->number) + cs->state |= MY_CS_PRIMARY; + + if (cs->binary_number == cs->number) + cs->state |= MY_CS_BINSORT; + + all_charsets[cs->number]->state|= cs->state; + + if (!(all_charsets[cs->number]->state & MY_CS_COMPILED)) + { + CHARSET_INFO *new= all_charsets[cs->number]; + if (cs_copy_data(all_charsets[cs->number],cs)) + return MY_XML_ERROR; + + if (!strcmp(cs->csname,"ucs2") ) + { +#ifdef HAVE_CHARSET_ucs2 + new->cset= my_charset_ucs2_general_uca.cset; + new->coll= my_charset_ucs2_general_uca.coll; + new->strxfrm_multiply= my_charset_ucs2_general_uca.strxfrm_multiply; + new->min_sort_char= my_charset_ucs2_general_uca.min_sort_char; + new->max_sort_char= my_charset_ucs2_general_uca.max_sort_char; + new->mbminlen= 2; + new->mbmaxlen= 2; + new->state |= MY_CS_AVAILABLE | MY_CS_LOADED; +#endif + } + else + { + simple_cs_init_functions(all_charsets[cs->number]); + new->mbminlen= 1; + new->mbmaxlen= 1; + if (simple_cs_is_full(all_charsets[cs->number])) + { + all_charsets[cs->number]->state |= MY_CS_LOADED; + } + all_charsets[cs->number]->state|= MY_CS_AVAILABLE; + } + } + else + { + /* + We need the below to make get_charset_name() + and get_charset_number() working even if a + character set has not been really incompiled. + The above functions are used for example + in error message compiler extra/comp_err.c. + If a character set was compiled, this information + will get lost and overwritten in add_compiled_collation(). + */ + CHARSET_INFO *dst= all_charsets[cs->number]; + dst->number= cs->number; + if (cs->comment) + if (!(dst->comment= my_once_strdup(cs->comment,MYF(MY_WME)))) + return MY_XML_ERROR; + if (cs->csname) + if (!(dst->csname= my_once_strdup(cs->csname,MYF(MY_WME)))) + return MY_XML_ERROR; + if (cs->name) + if (!(dst->name= my_once_strdup(cs->name,MYF(MY_WME)))) + return MY_XML_ERROR; + } + cs->number= 0; + cs->primary_number= 0; + cs->binary_number= 0; + cs->name= NULL; + cs->state= 0; + cs->sort_order= NULL; + cs->state= 0; } + return MY_XML_OK; +} + - while (*endptr && !isspace(*endptr)) - *buf++= *endptr++; - *buf=0; - fb->p = endptr; +#define MY_MAX_ALLOWED_BUF 1024*1024 +#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; + MY_STAT stat_info; + + if (!my_stat(filename, &stat_info, MYF(myflags)) || + ((len= (uint)stat_info.st_size) > MY_MAX_ALLOWED_BUF) || + !(buf= (char *)my_malloc(len,myflags))) + return TRUE; + + if ((fd=my_open(filename,O_RDONLY,myflags)) < 0) + { + my_free(buf,myflags); + return TRUE; + } + len=read(fd,buf,len); + 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; + char *res; DBUG_ENTER("get_charsets_dir"); if (charsets_dir != NULL) @@ -102,65 +332,23 @@ char *get_charsets_dir(char *buf) strxmov(buf, DEFAULT_CHARSET_HOME, "/", sharedir, "/", CHARSET_DIR, NullS); } - convert_dirname(buf,buf,NullS); - DBUG_PRINT("info",("charsets dir='%s'", buf)); - DBUG_RETURN(strend(buf)); + res= convert_dirname(buf,buf,NullS); + DBUG_PRINT("info",("charsets dir: '%s'", buf)); + DBUG_RETURN(res); } +CHARSET_INFO *all_charsets[256]; +CHARSET_INFO *default_charset_info = &my_charset_latin1; -static my_bool read_charset_index(CS_ID ***charsets, myf myflags) +void add_compiled_collation(CHARSET_INFO *cs) { - struct simpleconfig_buf_st fb; - char buf[MAX_LINE], num_buf[MAX_LINE]; - DYNAMIC_ARRAY cs; - CS_ID *csid; - - strmov(get_charsets_dir(buf), "Index"); - - if ((fb.f = my_fopen(buf, O_RDONLY, myflags)) == NULL) - return TRUE; - fb.buf[0] = '\0'; - fb.p = fb.buf; - - if (my_init_dynamic_array(&cs, sizeof(CS_ID *), 32, 32)) - return TRUE; - - while (!get_word(&fb, buf) && !get_word(&fb, num_buf)) - { - uint csnum; - uint length; - - if (!(csnum = atoi(num_buf))) - { - /* corrupt Index file */ - my_fclose(fb.f,myflags); - return TRUE; - } - - 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; - - insert_dynamic(&cs, (gptr) &csid); - } - my_fclose(fb.f,myflags); - - - 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); + all_charsets[cs->number]= cs; + cs->state|= MY_CS_AVAILABLE; +} - return FALSE; +static void *cs_alloc(uint size) +{ + return my_once_alloc(size, MYF(MY_WME)); } @@ -170,228 +358,126 @@ my_bool STDCALL init_available_charsets(myf myflags) 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+array_elements(all_charsets)-1 ; + cs++) { - my_init_dynamic_array(&cs_info_table, sizeof(CHARSET_INFO*), 16, 8); - error = read_charset_index(&available_charsets, myflags); + if (*cs) + { + if (cs[0]->ctype) + if (init_state_maps(*cs)) + *cs= NULL; + } } + + 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) +uint get_collation_number(const char *name) { - char buf[MAX_LINE]; - while (sz--) + CHARSET_INFO **cs; + init_available_charsets(MYF(0)); + + for (cs= all_charsets; + cs < all_charsets+array_elements(all_charsets)-1 ; + cs++) { - 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) -{ - strxmov(get_charsets_dir(buf), - name_from_csnum(available_charsets, cs_number), ".conf", NullS); + if ( cs[0] && cs[0]->name && + !my_strcasecmp(&my_charset_latin1, cs[0]->name, name)) + return cs[0]->number; + } + return 0; /* this mimics find_type() */ } -static my_bool read_charset_file(uint cs_number, CHARSET_INFO *set, - myf myflags) +uint get_charset_number(const char *charset_name, uint cs_flags) { - 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); + CHARSET_INFO **cs; + init_available_charsets(MYF(0)); + + for (cs= all_charsets; + cs < all_charsets+array_elements(all_charsets)-1 ; + cs++) + { + if ( cs[0] && cs[0]->csname && (cs[0]->state & cs_flags) && + !my_strcasecmp(&my_charset_latin1, cs[0]->csname, charset_name)) + return cs[0]->number; + } + return 0; } -uint get_charset_number(const char *charset_name) -{ - uint number=compiled_charset_number(charset_name); - if (number) - return number; - if (init_available_charsets(MYF(0))) /* If it isn't initialized */ - return 0; - return num_from_csname(available_charsets, charset_name); -} - const char *get_charset_name(uint charset_number) { - const char *name=compiled_charset_name(charset_number); - if (*name != '?') - return name; - 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; -} - -/* - Read charset from file. + CHARSET_INFO *cs; + init_available_charsets(MYF(0)); - NOTES - One never has to deallocate character sets. They will all be deallocated - by my_once_free() when program ends. + cs=all_charsets[charset_number]; + if (cs && (cs->number == charset_number) && cs->name ) + return (char*) cs->name; - If my_once_alloc() fails then this function may 'leak' some memory - which my_once_free() will deallocate, but this is so unlikely to happen - that this can be ignored. - - RETURN - 0 Error - # Pointer to allocated charset structure -*/ - - -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; - cs->strxfrm_multiply=cs->mbmaxlen=1; - if (read_charset_file(cs_number, cs, flags)) - return 0; - - if (!(cs= (CHARSET_INFO*) my_once_alloc(sizeof(CHARSET_INFO), - MYF(MY_WME)))) - return 0; - - *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)); - if (!cs->name || !cs->ctype || !cs->to_lower || !cs->to_upper || - !cs->sort_order) - return 0; - - 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) -{ - 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; + return (char*) "?"; /* this mimics find_type() */ } -static CHARSET_INFO *get_internal_charset_by_name(const char *name, myf flags) +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_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= all_charsets[cs_number])) + { + if (!(cs->state & MY_CS_COMPILED) && !(cs->state & MY_CS_LOADED)) + { + strxmov(get_charsets_dir(buf), cs->csname, ".xml", NullS); + my_read_charset_file(buf,flags); + } + cs= (cs->state & MY_CS_AVAILABLE) ? cs : NULL; + } pthread_mutex_unlock(&THR_LOCK_charset); + if (cs && !(cs->state & MY_CS_READY)) + { + if ((cs->cset->init && cs->cset->init(cs, cs_alloc)) || + (cs->coll->init && cs->coll->init(cs, cs_alloc))) + cs= NULL; + else + cs->state|= MY_CS_READY; + } return cs; } @@ -399,13 +485,20 @@ static CHARSET_INFO *get_internal_charset_by_name(const char *name, myf flags) CHARSET_INFO *get_charset(uint cs_number, myf flags) { CHARSET_INFO *cs; + if (cs_number == default_charset_info->number) + return default_charset_info; + (void) init_available_charsets(MYF(0)); /* If it isn't initialized */ + + if (!cs_number || cs_number >= array_elements(all_charsets)-1) + 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); @@ -413,194 +506,104 @@ CHARSET_INFO *get_charset(uint cs_number, myf flags) return cs; } -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); - if (!new_charset) - { - DBUG_PRINT("error",("Couldn't set default character set")); - DBUG_RETURN(TRUE); /* error */ - } - default_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_collation_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; } -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); - if (!new_charset) - { - DBUG_PRINT("error",("Couldn't set default character set")); - DBUG_RETURN(TRUE); /* error */ - } - - default_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) +CHARSET_INFO *get_charset_by_csname(const char *cs_name, + uint cs_flags, + myf flags) { - uint length= (uint) strlen(name); - const char *pos; - for (pos=s->str ; (pos=strstr(pos,name)) ; pos++) - { - if (! pos[length] || pos[length] == ' ') - return TRUE; /* Already existed */ - } - - return FALSE; -} + uint cs_number; + CHARSET_INFO *cs; + DBUG_ENTER("get_charset_by_csname"); + DBUG_PRINT("enter",("name: '%s'", cs_name)); -static void charset_append(DYNAMIC_STRING *s, const char *name) -{ - if (!charset_in_string(name, s)) + (void) init_available_charsets(MYF(0)); /* If it isn't initialized */ + + cs_number= get_charset_number(cs_name, cs_flags); + cs= cs_number ? get_internal_charset(cs_number, flags) : NULL; + + if (!cs && (flags & MY_WME)) { - dynstr_append(s, name); - dynstr_append(s, " "); + 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); } -} - - -/* - Returns a dynamically-allocated string listing the character sets - requested. - - SYNOPSIS - list_charsets() - want_flags Flags for which character sets to return: - MY_COMPILED_SETS: Return incompiled charsets - MY_INDEX_SETS: - MY_LOADED_SETS: - - NOTES - The caller is responsible for freeing the memory. + DBUG_RETURN(cs); +} - RETURN - A string with available character sets separated by space -*/ -char * list_charsets(myf want_flags) +ulong escape_string_for_mysql(CHARSET_INFO *charset_info, char *to, + const char *from, ulong length) { - DYNAMIC_STRING s; - char *result; - - (void)init_available_charsets(MYF(0)); - init_dynamic_string(&s, NullS, 256, 1024); - - if (want_flags & MY_COMPILED_SETS) + const char *to_start= to; + const char *end; +#ifdef USE_MB + my_bool use_mb_flag= use_mb(charset_info); +#endif + for (end= from + length; from != end; from++) { - CHARSET_INFO *cs; - for (cs = compiled_charsets; cs->number > 0; cs++) +#ifdef USE_MB + int l; + if (use_mb_flag && (l= my_ismbchar(charset_info, from, end))) { - dynstr_append(&s, cs->name); - dynstr_append(&s, " "); + while (l--) + *to++= *from++; + from--; + continue; } - } - - if (want_flags & MY_CONFIG_SETS) - { - CS_ID **charset; - char buf[FN_REFLEN]; - MY_STAT status; - - if ((charset=available_charsets)) - { - for (; *charset; charset++) - { - if (charset_in_string((*charset)->name, &s)) - continue; - get_charset_conf_name((*charset)->number, buf); - if (!my_stat(buf, &status, MYF(0))) - continue; /* conf file doesn't exist */ - dynstr_append(&s, (*charset)->name); - dynstr_append(&s, " "); - } +#endif + switch (*from) { + case 0: /* Must be escaped for 'mysql' */ + *to++= '\\'; + *to++= '0'; + break; + case '\n': /* Must be escaped for logs */ + *to++= '\\'; + *to++= 'n'; + break; + case '\r': + *to++= '\\'; + *to++= 'r'; + break; + case '\\': + *to++= '\\'; + *to++= '\\'; + break; + case '\'': + *to++= '\\'; + *to++= '\''; + break; + case '"': /* Better safe than sorry */ + *to++= '\\'; + *to++= '"'; + break; + case '\032': /* This gives problems on Win32 */ + *to++= '\\'; + *to++= 'Z'; + break; + default: + *to++= *from; } } - - if (want_flags & MY_INDEX_SETS) - { - CS_ID **charset; - for (charset = available_charsets; *charset; charset++) - charset_append(&s, (*charset)->name); - } - - if (want_flags & MY_LOADED_SETS) - { - uint i; - for (i = 0; i < cs_info_table.elements; i++) - charset_append(&s, - dynamic_element(&cs_info_table, i, CHARSET_INFO *)->name); - } - if (s.length) - s.length--; /* Remove end space */ - result= my_strdup_with_length(s.str, s.length, MYF(MY_WME)); - dynstr_free(&s); - - return result; -} - -/**************************************************************************** -* 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); + *to= 0; + return (ulong) (to - to_start); } diff --git a/mysys/charset2html.c b/mysys/charset2html.c new file mode 100644 index 00000000000..96862ff16a1 --- /dev/null +++ b/mysys/charset2html.c @@ -0,0 +1,177 @@ +/* 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 ? cs->sort_order[i] : 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>%02X",ch[i].srt); + + printf("<TD>%s%s%s%s%s%s%s%s", + ch[i].ctp & _MY_U ? "U" : "", + ch[i].ctp & _MY_L ? "L" : "", + ch[i].ctp & _MY_NMR ? "N" : "", + ch[i].ctp & _MY_SPC ? "S" : "", + ch[i].ctp & _MY_PNT ? "P" : "", + ch[i].ctp & _MY_CTR ? "C" : "", + ch[i].ctp & _MY_B ? "B" : "", + ch[i].ctp & _MY_X ? "X" : ""); + + if ((ch[i].uni >= 0x80) && (ch[i].uni <= 0x9F)) + { + /* + Control characters 0x0080..0x009F are dysplayed by some + browers as if they were letters. Don't print them to + avoid confusion. + */ + printf("<TD>ctrl<TD>ctrl<TD>ctrl"); + } + else + { + 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"); +} + +static void print_index() +{ + CHARSET_INFO **cs; + int clr=0; + + get_charset_by_name("",MYF(0)); /* To execute init_available_charsets */ + + printf("All charsets\n"); + printf("<table border=1>\n"); + printf("<tr bgcolor=EEEE99><th>ID<th>Charset<th>Collation<th>Def<th>Bin<th>Com<th>Comment\n"); + for (cs=all_charsets ; cs < all_charsets+256; cs++) + { + if (!cs[0]) + continue; + printf("<tr bgcolor=#%s><td><a href=\"?%s\">%d</a><td>%s<td>%s<td>%s<td>%s<td>%s<td>%s\n", + (clr= !clr) ? "DDDDDD" : "EEEE99", + cs[0]->name,cs[0]->number,cs[0]->csname, + cs[0]->name, + (cs[0]->state & MY_CS_PRIMARY) ? "def " : " ", + (cs[0]->state & MY_CS_BINSORT) ? "bin " : " ", + (cs[0]->state & MY_CS_COMPILED) ? "com " : " ", + cs[0]->comment); + } + printf("</table>\n"); +} + +int main(int argc, char **argv) { + const char *the_set = NULL; + int argcnt = 1; + CHARSET_INFO *cs; + + if (getenv("SCRIPT_NAME")) + { + printf("Content-Type: text/html\r\n\r\n"); + } + 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 (!the_set) + { + print_index(); + return 0; + } + + if (!(cs= get_charset_by_name(the_set, MYF(MY_WME)))) + return 1; + + print_cs(cs); + + return 0; +} diff --git a/mysys/checksum.c b/mysys/checksum.c index 1dd135c7ad9..92ec3d550f1 100644 --- a/mysys/checksum.c +++ b/mysys/checksum.c @@ -19,19 +19,24 @@ #include "my_sys.h" /* - Calculate a long checksum for a memoryblock. Used to verify pack_isam - + Calculate a long checksum for a memoryblock. + SYNOPSIS - checksum() - mem Pointer to memory block - count Count of bytes + my_checksum() + crc start value for crc + pos pointer to memory block + length length of the block */ -ulong checksum(const byte *mem, uint count) +ha_checksum my_checksum(ha_checksum crc, const byte *pos, uint length) { - ulong crc; - for (crc= 0; count-- ; mem++) - crc= ((crc << 1) + *((uchar*) mem)) + - test(crc & ((ulong) 1L << (8*sizeof(ulong)-1))); +#ifdef NOT_USED + const byte *end=pos+length; + for ( ; pos != end ; pos++) + crc=((crc << 8) + *((uchar*) pos)) + (crc >> (8*sizeof(ha_checksum)-8)); return crc; +#else + return (ha_checksum)crc32((uint)crc, (const uchar *)pos, length); +#endif } + diff --git a/mysys/default.c b/mysys/default.c index 81290322223..792233ed10d 100644 --- a/mysys/default.c +++ b/mysys/default.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2000 MySQL AB +/* Copyright (C) 2000-2003 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 @@ -166,6 +166,12 @@ int load_defaults(const char *conf_file, const char **groups, if ((error= search_default_file(&args, &alloc, "", forced_default_file, "", &group)) < 0) goto err; + if (error > 0) + { + fprintf(stderr, "Could not open required defaults file: %s\n", + forced_default_file); + goto err; + } } else if (dirname_length(conf_file)) { @@ -214,9 +220,9 @@ int load_defaults(const char *conf_file, const char **groups, res= (char**) (ptr+sizeof(alloc)); /* copy name + found arguments + command line arguments to new array */ - res[0]=argv[0][0]; + res[0]= argv[0][0]; /* Name MUST be set, even by embedded library */ memcpy((gptr) (res+1), args.buffer, args.elements*sizeof(char*)); - /* Skipp --defaults-file and --defaults-extra-file */ + /* Skip --defaults-file and --defaults-extra-file */ (*argc)-= args_used; (*argv)+= args_used; @@ -224,11 +230,12 @@ int load_defaults(const char *conf_file, const char **groups, if (*argc >= 2 && !strcmp(argv[0][1],"--print-defaults")) { found_print_defaults=1; - --*argc; ++*argv; /* skipp argument */ + --*argc; ++*argv; /* skip argument */ } - memcpy((gptr) (res+1+args.elements), (char*) ((*argv)+1), - (*argc-1)*sizeof(char*)); + if (*argc) + memcpy((gptr) (res+1+args.elements), (char*) ((*argv)+1), + (*argc-1)*sizeof(char*)); res[args.elements+ *argc]=0; /* last null */ (*argc)+=args.elements; @@ -250,6 +257,7 @@ int load_defaults(const char *conf_file, const char **groups, err: fprintf(stderr,"Fatal error in defaults handling. Program aborted\n"); exit(1); + return 0; /* Keep compiler happy */ } @@ -329,7 +337,7 @@ static int 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(&my_charset_latin1,*ptr) ; ptr++ ) ; if (*ptr == '#' || *ptr == ';' || !*ptr) continue; if (*ptr == '[') /* Group name */ @@ -342,7 +350,7 @@ static int search_default_file(DYNAMIC_ARRAY *args, MEM_ROOT *alloc, name,line); goto err; } - for ( ; isspace(end[-1]) ; end--) ; /* Remove end space */ + for ( ; my_isspace(&my_charset_latin1,end[-1]) ; end--) ;/* Remove end space */ end[0]=0; read_values=find_type(ptr,group,3) > 0; continue; @@ -359,8 +367,7 @@ static int search_default_file(DYNAMIC_ARRAY *args, MEM_ROOT *alloc, end= remove_end_comment(ptr); if ((value= strchr(ptr, '='))) end= value; /* Option without argument */ - for ( ; isspace(end[-1]) ; end--) ; - + for ( ; my_isspace(&my_charset_latin1,end[-1]) ; end--) ; if (!value) { if (!(tmp=alloc_root(alloc,(uint) (end-ptr)+3))) @@ -373,13 +380,13 @@ static int 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(&my_charset_latin1,*value); value++) ; value_end=strend(value); /* We don't have to test for value_end >= value as we know there is an '=' before */ - for ( ; isspace(value_end[-1]) ; value_end--) ; + for ( ; my_isspace(&my_charset_latin1,value_end[-1]) ; value_end--) ; if (value_end < value) /* Empty string */ value_end=value; @@ -461,7 +468,8 @@ static char *remove_end_comment(char *ptr) else if (quote == *ptr) quote= 0; } - if (!quote && *ptr == '#') /* We are not inside a string */ + /* We are not inside a string */ + if (!quote && *ptr == '#') { *ptr= 0; return ptr; diff --git a/mysys/errors.c b/mysys/errors.c index 7d755718b16..255b6893c73 100644 --- a/mysys/errors.c +++ b/mysys/errors.c @@ -83,7 +83,7 @@ void init_glob_errs() EE(EE_OPEN_WARNING) = "%d files and %d streams is left open\n"; EE(EE_DISK_FULL) = "Disk is full writing '%s'. Waiting for someone to free space..."; EE(EE_CANT_MKDIR) ="Can't create directory '%s' (Errcode: %d)"; - EE(EE_UNKNOWN_CHARSET)= "Character set is not a compiled character set and is not specified in the %s file"; + EE(EE_UNKNOWN_CHARSET)= "Character set '%s' is not a compiled character set and is not specified in the %s file"; EE(EE_OUT_OF_FILERESOURCES)="Out of resources when opening file '%s' (Errcode: %d)"; EE(EE_CANT_READLINK)= "Can't read value for symlink '%s' (Error %d)"; EE(EE_CANT_SYMLINK)= "Can't create symlink '%s' pointing at '%s' (Error %d)"; diff --git a/mysys/hash.c b/mysys/hash.c index 3afd31a079b..b7be41a9058 100644 --- a/mysys/hash.c +++ b/mysys/hash.c @@ -29,16 +29,27 @@ #define HIGHFIND 4 #define HIGHUSED 8 +typedef struct st_hash_info { + uint next; /* index to next key */ + byte *data; /* data for current entry */ +} HASH_LINK; + 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 int hashcmp(HASH *hash,HASH_LINK *pos,const byte *key,uint length); +static uint calc_hash(HASH *hash,const byte *key,uint length) +{ + ulong nr1=1, nr2=4; + hash->charset->coll->hash_sort(hash->charset,(uchar*) key,length,&nr1,&nr2); + return nr1; +} -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)); @@ -47,7 +58,7 @@ my_bool _hash_init(HASH *hash,uint size,uint key_offset,uint key_length, if (my_init_dynamic_array_ci(&hash->array,sizeof(HASH_LINK),size,0)) { hash->free=0; /* Allow call to hash_free */ - DBUG_RETURN(TRUE); + DBUG_RETURN(1); } hash->key_offset=key_offset; hash->key_length=key_length; @@ -56,10 +67,7 @@ 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; - if (flags & HASH_CASE_INSENSITIVE) - hash->calc_hashnr=calc_hashnr_caseup; - else - hash->calc_hashnr=calc_hashnr; + hash->charset=charset; DBUG_RETURN(0); } @@ -109,87 +117,19 @@ 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); -} - -#ifndef NEW_HASH_FUNCTION - - /* Calc hashvalue for a key */ - -static uint calc_hashnr(const byte *key,uint length) -{ - register uint nr=1, nr2=4; - while (length--) - { - nr^= (((nr & 63)+nr2)*((uint) (uchar) *key++))+ (nr << 8); - nr2+=3; - } - return((uint) nr); + return hash_mask(calc_hash(hash,key,length),buffmax,maxlength); } - /* 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 - -/* - * Fowler/Noll/Vo hash - * - * The basis of the hash algorithm was taken from an idea sent by email to the - * IEEE Posix P1003.2 mailing list from Phong Vo (kpv@research.att.com) and - * Glenn Fowler (gsf@research.att.com). Landon Curt Noll (chongo@toad.com) - * later improved on their algorithm. - * - * The magic is in the interesting relationship between the special prime - * 16777619 (2^24 + 403) and 2^32 and 2^8. - * This works well on both numbers and strings. - */ - -uint calc_hashnr(const byte *key, uint len) -{ - const byte *end=key+len; - uint hash; - for (hash = 0; key < end; key++) - { - hash *= 16777619; - hash ^= (uint) *(uchar*) key; - } - 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 -#ifndef __SUNPRO_C /* SUNPRO can't handle this */ +#if !defined(__SUNPRO_C) && !defined(__USLC__) /* broken compilers */ inline #endif 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 calc_hash(hash,key,length); } @@ -205,8 +145,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 : - hash->key_length), + idx=hash_mask(calc_hash(hash,key,length ? length : hash->key_length), hash->blength,hash->records); do { @@ -276,16 +215,15 @@ static int hashcmp(HASH *hash,HASH_LINK *pos,const byte *key,uint length) { uint rec_keylength; 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) : - memcmp(rec_key,key,rec_keylength)); + return ((length && length != rec_keylength) || + my_strnncoll(hash->charset, (uchar*) rec_key, rec_keylength, + (uchar*) key, length)); } /* Write a hash-key to the hash-index */ -my_bool hash_insert(HASH *info,const byte *record) +my_bool my_hash_insert(HASH *info,const byte *record) { int flag; uint halfbuff,hash_nr,first_index,idx; @@ -518,7 +456,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(calc_hash(hash, 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 d35f2c4f7ae..3de82c05b87 100644 --- a/mysys/mf_dirname.c +++ b/mysys/mf_dirname.c @@ -118,11 +118,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 937904847ee..114e19759c8 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_iocache.c b/mysys/mf_iocache.c index 8fb93dc4780..f16f2b7ab72 100644 --- a/mysys/mf_iocache.c +++ b/mysys/mf_iocache.c @@ -55,7 +55,6 @@ TODO: #include "mysys_err.h" static void my_aiowait(my_aio_result *result); #endif -#include <assert.h> #include <errno.h> #ifdef THREAD @@ -110,11 +109,29 @@ init_functions(IO_CACHE* info, enum cache_type type) } } - /* - ** if cachesize == 0 then use default cachesize (from s-file) - ** if file == -1 then real_open_cached_file() will be called. - ** returns 0 if ok - */ + +/* + Initialize an IO_CACHE object + + SYNOPSOS + init_io_cache() + info cache handler to initialize + file File that should be associated to to the handler + If == -1 then real_open_cached_file() + will be called when it's time to open file. + cachesize Size of buffer to allocate for read/write + If == 0 then use my_default_record_cache_size + type Type of cache + seek_offset Where cache should start reading/writing + use_async_io Set to 1 of we should use async_io (if avaiable) + cache_myflags Bitmap of differnt flags + MY_WME | MY_FAE | MY_NABP | MY_FNABP | + MY_DONT_CHECK_FILESIZE + + RETURN + 0 ok + # error +*/ int init_io_cache(IO_CACHE *info, File file, uint cachesize, enum cache_type type, my_off_t seek_offset, @@ -127,20 +144,20 @@ int init_io_cache(IO_CACHE *info, File file, uint cachesize, (ulong) info, (int) type, (ulong) seek_offset)); info->file= file; - info->type=type; + info->type= 0; /* Don't set it until mutex are created */ info->pos_in_file= seek_offset; info->pre_close = info->pre_read = info->post_read = 0; info->arg = 0; info->alloced_buffer = 0; info->buffer=0; info->seek_not_done= test(file >= 0); + info->disk_writes= 0; #ifdef THREAD info->share=0; #endif - if (!cachesize) - if (! (cachesize= my_default_record_cache_size)) - DBUG_RETURN(1); /* No cache requested */ + if (!cachesize && !(cachesize= my_default_record_cache_size)) + DBUG_RETURN(1); /* No cache requested */ min_cache=use_async_io ? IO_SIZE*4 : IO_SIZE*2; if (type == READ_CACHE || type == SEQ_READ_APPEND) { /* Assume file isn't growing */ @@ -201,6 +218,13 @@ int init_io_cache(IO_CACHE *info, File file, uint cachesize, pthread_mutex_init(&info->append_buffer_lock,MY_MUTEX_INIT_FAST); #endif } +#if defined(SAFE_MUTEX) && defined(THREAD) + else + { + /* Clear mutex so that safe_mutex will notice that it's not initialized */ + bzero((char*) &info->append_buffer_lock, sizeof(info)); + } +#endif if (type == WRITE_CACHE) info->write_end= @@ -211,6 +235,7 @@ int init_io_cache(IO_CACHE *info, File file, uint cachesize, /* End_of_file may be changed by user later */ info->end_of_file= end_of_file; info->error=0; + info->type= type; init_functions(info,type); #ifdef HAVE_AIOWAIT if (use_async_io && ! my_disable_async_io) @@ -774,7 +799,7 @@ int _my_b_async_read(register IO_CACHE *info, byte *Buffer, uint Count) { /* Fix if skipped bytes */ if (info->aio_read_pos + read_length < info->pos_in_file) { - read_length=0; /* Skipp block */ + read_length=0; /* Skip block */ next_pos_in_file=info->pos_in_file; } else @@ -805,7 +830,7 @@ int _my_b_async_read(register IO_CACHE *info, byte *Buffer, uint Count) next_pos_in_file=(info->pos_in_file+ (uint) (info->read_end - info->request_pos)); - /* If reading large blocks, or first read or read with skipp */ + /* If reading large blocks, or first read or read with skip */ if (Count) { if (next_pos_in_file == info->end_of_file) @@ -868,7 +893,7 @@ int _my_b_async_read(register IO_CACHE *info, byte *Buffer, uint Count) if (aioread(info->file,read_buffer,(int) max_length, (my_off_t) next_pos_in_file,MY_SEEK_SET, &info->aio_result.result)) - { /* Skipp async io */ + { /* Skip async io */ my_errno=errno; DBUG_PRINT("error",("got error: %d, aio_result: %d from aioread, async skipped", errno, info->aio_result.result.aio_errno)); @@ -1128,6 +1153,7 @@ int my_b_flush_io_cache(IO_CACHE *info, int need_append_buffer_lock) } info->append_read_pos=info->write_pos=info->write_buffer; + ++info->disk_writes; UNLOCK_APPEND_BUFFER; DBUG_RETURN(info->error); } @@ -1143,6 +1169,22 @@ int my_b_flush_io_cache(IO_CACHE *info, int need_append_buffer_lock) DBUG_RETURN(0); } +/* + Free an IO_CACHE object + + SYNOPSOS + end_io_cache() + info IO_CACHE Handle to free + + NOTES + It's currently safe to call this if one has called init_io_cache() + on the 'info' object, even if init_io_cache() failed. + This function is also safe to call twice with the same handle. + + RETURN + 0 ok + # Error +*/ int end_io_cache(IO_CACHE *info) { @@ -1165,7 +1207,10 @@ int end_io_cache(IO_CACHE *info) #endif if ((pre_close=info->pre_close)) + { (*pre_close)(info); + info->pre_close= 0; + } if (info->alloced_buffer) { info->alloced_buffer=0; diff --git a/mysys/mf_iocache2.c b/mysys/mf_iocache2.c index 344b7ac2251..3755bcdb53d 100644 --- a/mysys/mf_iocache2.c +++ b/mysys/mf_iocache2.c @@ -23,7 +23,6 @@ #include <m_string.h> #include <stdarg.h> #include <m_ctype.h> -#include <assert.h> my_off_t my_b_append_tell(IO_CACHE* info) { @@ -266,8 +265,8 @@ uint my_b_vprintf(IO_CACHE *info, const char* fmt, va_list args) fmt++; /* Found one '%' */ } - /* Skipp if max size is used (to be compatible with printf) */ - while (isdigit(*fmt) || *fmt == '.' || *fmt == '-') + /* Skip if max size is used (to be compatible with printf) */ + while (my_isdigit(&my_charset_latin1, *fmt) || *fmt == '.' || *fmt == '-') fmt++; if (*fmt == 's') /* String parameter */ { diff --git a/mysys/mf_keycache.c b/mysys/mf_keycache.c index 977ca6b11a7..32b3154b8ed 100644 --- a/mysys/mf_keycache.c +++ b/mysys/mf_keycache.c @@ -15,706 +15,2427 @@ 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 handle keyblock cacheing for ISAM and MyISAM tables. + + One cache can handle many files. + It must contain buffers of the same blocksize. init_key_cache() should be used to init cache handler. - */ + + The free list (free_block_list) is a stack like structure. + When a block is freed by free_block(), it is pushed onto the stack. + When a new block is required it is first tried to pop one from the stack. + If the stack is empty, it is tried to get a never-used block from the pool. + If this is empty too, then a block is taken from the LRU ring, flushing it + to disk, if neccessary. This is handled in find_key_block(). + With the new free list, the blocks can have three temperatures: + hot, warm and cold (which is free). This is remembered in the block header + by the enum BLOCK_TEMPERATURE temperature variable. Remembering the + temperature is neccessary to correctly count the number of warm blocks, + which is required to decide when blocks are allowed to become hot. Whenever + a block is inserted to another (sub-)chain, we take the old and new + temperature into account to decide if we got one more or less warm block. + blocks_unused is the sum of never used blocks in the pool and of currently + free blocks. blocks_used is the number of blocks fetched from the pool and + as such gives the maximum number of in-use blocks at any time. +*/ #include "mysys_priv.h" +#include <keycache.h> #include "my_static.h" #include <m_string.h> #include <errno.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 - -/* size of map to be used to find changed files */ - -#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 */ - -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; - - -static SEC_LINK *find_key_block(int file,my_off_t filepos,int *error); -static int flush_all_key_blocks(); - - /* 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; +#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; + +/* descriptor of the page in the key cache block buffer */ +struct st_keycache_page +{ + int file; /* file to which the page belongs to */ + my_off_t filepos; /* position of the page in the file */ +}; + +/* element in the chain of a hash table bucket */ +struct st_hash_link +{ + 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 */ +}; + +/* 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 + +/* block temperature determines in which (sub-)chain the block currently is */ +enum BLOCK_TEMPERATURE { BLOCK_COLD /*free*/ , BLOCK_WARM , BLOCK_HOT }; + +/* key cache block */ +struct st_block_link +{ + 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 */ + enum BLOCK_TEMPERATURE temperature; /* block temperature: cold, warm, hot */ + uint hits_left; /* number of hits left until promotion */ + ulonglong last_hit_time; /* timestamp of the last hit */ + KEYCACHE_CONDVAR *condvar; /* condition variable for 'no readers' event */ +}; + +KEY_CACHE dflt_key_cache_var; +KEY_CACHE *dflt_key_cache= &dflt_key_cache_var; + +#define FLUSH_CACHE 2000 /* sort this many blocks at once */ + +static int flush_all_key_blocks(KEY_CACHE *keycache); +static void link_into_queue(KEYCACHE_WQUEUE *wqueue, + struct st_my_thread_var *thread); +static void unlink_from_queue(KEYCACHE_WQUEUE *wqueue, + struct st_my_thread_var *thread); +static void free_block(KEY_CACHE *keycache, BLOCK_LINK *block); +static void test_key_cache(KEY_CACHE *keycache, + const char *where, my_bool lock); + +#define KEYCACHE_HASH(f, pos) \ +(((ulong) ((pos) >> keycache->key_cache_shift)+ \ + (ulong) (f)) & (keycache->hash_entries-1)) +#define FILE_HASH(f) ((uint) (f) & (CHANGED_BLOCKS_HASH-1)) + +#define DEFAULT_KEYCACHE_DEBUG_LOG "keycache_debug.log" + +#if defined(KEYCACHE_DEBUG) && ! defined(KEYCACHE_DEBUG_LOG) +#define KEYCACHE_DEBUG_LOG DEFAULT_KEYCACHE_DEBUG_LOG +#endif + +#if defined(KEYCACHE_DEBUG_LOG) +static FILE *keycache_debug_log=NULL; +static void keycache_debug_print _VARARGS((const char *fmt,...)); +#define KEYCACHE_DEBUG_OPEN \ + if (!keycache_debug_log) keycache_debug_log=fopen(KEYCACHE_DEBUG_LOG, "w") + +#define KEYCACHE_DEBUG_CLOSE \ + if (keycache_debug_log) { fclose(keycache_debug_log); keycache_debug_log=0; } +#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 *) keycache->block_root)/sizeof(BLOCK_LINK))) +#define HASH_LINK_NUMBER(h) \ + ((uint) (((char*)(h)-(char *) keycache->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); +} + + +/* + Initialize a key cache + + SYNOPSIS + init_key_cache() + keycache pointer to a key cache data structure + key_cache_block_size size of blocks to keep cached data + use_mem total memory to use for the key cache + division_limit division limit (may be zero) + age_threshold age threshold (may be zero) + + RETURN VALUE + number of blocks in the key cache, if successful, + 0 - otherwise. - /* Init of disk_buffert */ - /* Returns blocks in use */ - /* ARGSUSED */ + NOTES. + if keycache->key_cache_inited != 0 we assume that the key cache + is already initialized. This is for now used by myisamchk, but shouldn't + be something that a program should rely on! -int init_key_cache(ulong use_mem) + It's assumed that no two threads call this function simultaneously + referring to the same key cache handle. + +*/ + +int init_key_cache(KEY_CACHE *keycache, uint key_cache_block_size, + ulong use_mem, uint division_limit, + uint age_threshold) { - uint blocks,length; + uint blocks, hash_links, length; + int error; DBUG_ENTER("init_key_cache"); + DBUG_ASSERT(key_cache_block_size >= 512); - if (key_cache_inited && _my_disk_blocks > 0) + KEYCACHE_DEBUG_OPEN; + if (keycache->key_cache_inited && keycache->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) + + keycache->global_cache_w_requests= keycache->global_cache_r_requests= 0; + keycache->global_cache_read= keycache->global_cache_write= 0; + keycache->disk_blocks= -1; + if (! keycache->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)); -#ifndef DBUG_OFF - _my_printed=0; -#endif + keycache->key_cache_inited= 1; + keycache->in_init= 0; + pthread_mutex_init(&keycache->cache_lock, MY_MUTEX_INIT_FAST); + keycache->resize_queue.last_thread= NULL; } - blocks= (uint) (use_mem/(sizeof(SEC_LINK)+sizeof(SEC_LINK*)*5/4+ - key_cache_block_size)); - /* No use to have very few blocks */ - if (blocks >= 8 && _my_disk_blocks < 0) + keycache->key_cache_mem_size= use_mem; + keycache->key_cache_block_size= key_cache_block_size; + keycache->key_cache_shift= my_bit_log2(key_cache_block_size); + DBUG_PRINT("info", ("key_cache_block_size: %u", + key_cache_block_size)); + + 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 && keycache->disk_blocks < 0) { - for (;;) + 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 ((keycache->hash_entries= next_power(blocks)) < blocks * 5/4) + keycache->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= (ALIGN_SIZE(blocks * sizeof(BLOCK_LINK)) + + ALIGN_SIZE(hash_links * sizeof(HASH_LINK)) + + ALIGN_SIZE(sizeof(HASH_LINK*) * + keycache->hash_entries))) + + ((ulong) blocks << keycache->key_cache_shift) > use_mem) + blocks--; + /* Allocate memory for cache page buffers */ + if ((keycache->block_mem= + my_malloc_lock((ulong) blocks * keycache->key_cache_block_size, + MYF(0)))) { - if ((_my_block_root=(SEC_LINK*) my_malloc((uint) length,MYF(0))) != 0) - break; - my_free_lock(_my_block_mem,MYF(0)); + /* + Allocate memory for blocks, hash_links and hash entries; + For each block 2 hash links are allocated + */ + if ((keycache->block_root= (BLOCK_LINK*) my_malloc((uint) length, + MYF(0)))) + break; + my_free_lock(keycache->block_mem, MYF(0)); } if (blocks < 8) - goto err; - blocks=blocks/4*3; + { + my_errno= ENOMEM; + 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)); - } - 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); + keycache->blocks_unused= (ulong) blocks; + keycache->disk_blocks= (int) blocks; + keycache->hash_links= hash_links; + keycache->hash_root= (HASH_LINK**) ((char*) keycache->block_root + + ALIGN_SIZE(blocks*sizeof(BLOCK_LINK))); + keycache->hash_link_root= (HASH_LINK*) ((char*) keycache->hash_root + + ALIGN_SIZE((sizeof(HASH_LINK*) * + keycache->hash_entries))); + bzero((byte*) keycache->block_root, + keycache->disk_blocks * sizeof(BLOCK_LINK)); + bzero((byte*) keycache->hash_root, + keycache->hash_entries * sizeof(HASH_LINK*)); + bzero((byte*) keycache->hash_link_root, + keycache->hash_links * sizeof(HASH_LINK)); + keycache->hash_links_used= 0; + keycache->free_hash_list= NULL; + keycache->blocks_used= keycache->blocks_changed= 0; + + keycache->global_blocks_changed= 0; + keycache->blocks_available=0; /* For debugging */ + + /* The LRU chain is empty after initialization */ + keycache->used_last= NULL; + keycache->used_ins= NULL; + keycache->free_block_list= NULL; + keycache->keycache_time= 0; + keycache->warm_blocks= 0; + keycache->min_warm_blocks= (division_limit ? + blocks * division_limit / 100 + 1 : + blocks); + keycache->age_threshold= (age_threshold ? + blocks * age_threshold / 100 : + blocks); + + keycache->cnt_for_resize_op= 0; + keycache->resize_in_flush= 0; + keycache->can_be_used= 1; + + keycache->waiting_for_hash_link.last_thread= NULL; + keycache->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", + keycache->disk_blocks, keycache->block_root, + keycache->hash_entries, keycache->hash_root, + keycache->hash_links, keycache->hash_link_root)); + bzero((gptr) keycache->changed_blocks, + sizeof(keycache->changed_blocks[0]) * CHANGED_BLOCKS_HASH); + bzero((gptr) keycache->file_blocks, + sizeof(keycache->file_blocks[0]) * CHANGED_BLOCKS_HASH); + } + + keycache->blocks= keycache->disk_blocks > 0 ? keycache->disk_blocks : 0; + DBUG_RETURN((int) keycache->disk_blocks); err: - my_errno=ENOMEM; + error= my_errno; + keycache->disk_blocks= 0; + keycache->blocks= 0; + if (keycache->block_mem) + { + my_free_lock((gptr) keycache->block_mem, MYF(0)); + keycache->block_mem= NULL; + } + if (keycache->block_root) + { + my_free((gptr) keycache->block_root, MYF(0)); + keycache->block_root= NULL; + } + my_errno= error; + keycache->can_be_used= 0; DBUG_RETURN(0); -} /* init_key_cache */ +} /* - Resize the key cache + Resize a 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 + keycache pointer to a key cache data structure + key_cache_block_size size of blocks to keep cached data + use_mem total memory to use for the new key cache + division_limit new division limit (if not zero) + age_threshold new age threshold (if not zero) + + RETURN VALUE + number of blocks in the key cache, if successful, + 0 - otherwise. + + NOTES. + The function first compares the memory size and the block size parameters + with the key cache values. + + If they differ the function free the the memory allocated for the + old key cache blocks by calling the end_key_cache function and + then rebuilds the key cache with new blocks by calling + init_key_cache. + + The function starts the operation only when all other threads + performing operations with the key cache let her to proceed + (when cnt_for_resize=0). */ - -int resize_key_cache(ulong use_mem) +int resize_key_cache(KEY_CACHE *keycache, uint key_cache_block_size, + ulong use_mem, uint division_limit, + uint age_threshold) { - int block; - pthread_mutex_lock(&THR_LOCK_keycache); - if (flush_all_key_blocks()) + int blocks; + struct st_my_thread_var *thread; + KEYCACHE_WQUEUE *wqueue; + DBUG_ENTER("resize_key_cache"); + + if (!keycache->key_cache_inited) + DBUG_RETURN(keycache->disk_blocks); + + if(key_cache_block_size == keycache->key_cache_block_size && + use_mem == keycache->key_cache_mem_size) { - /* TODO: If this happens, we should write a warning in the log file ! */ - pthread_mutex_unlock(&THR_LOCK_keycache); - return 0; + change_key_cache_param(keycache, division_limit, age_threshold); + DBUG_RETURN(keycache->disk_blocks); + } + + keycache_pthread_mutex_lock(&keycache->cache_lock); + + wqueue= &keycache->resize_queue; + thread= my_thread_var; + link_into_queue(wqueue, thread); + + while (wqueue->last_thread->next != thread) + { + keycache_pthread_cond_wait(&thread->suspend, &keycache->cache_lock); } - 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; + + keycache->resize_in_flush= 1; + if (flush_all_key_blocks(keycache)) + { + /* TODO: if this happens, we should write a warning in the log file ! */ + keycache->resize_in_flush= 0; + blocks= 0; + keycache->can_be_used= 0; + goto finish; + } + keycache->resize_in_flush= 0; + keycache->can_be_used= 0; + while (keycache->cnt_for_resize_op) + { + keycache_pthread_cond_wait(&thread->suspend, &keycache->cache_lock); + } + + end_key_cache(keycache, 0); /* Don't free mutex */ + /* The following will work even if use_mem is 0 */ + blocks= init_key_cache(keycache, key_cache_block_size, use_mem, + division_limit, age_threshold); + +finish: + unlink_from_queue(wqueue, thread); + /* Signal for the next resize request to proceeed if any */ + if (wqueue->last_thread) + keycache_pthread_cond_signal(&wqueue->last_thread->next->suspend); + keycache_pthread_mutex_unlock(&keycache->cache_lock); + return blocks; +} + + +/* + Increment counter blocking resize key cache operation +*/ +static inline void inc_counter_for_resize_op(KEY_CACHE *keycache) +{ + keycache->cnt_for_resize_op++; } - /* Remove key_cache from memory */ +/* + Decrement counter blocking resize key cache operation; + Signal the operation to proceed when counter becomes equal zero +*/ +static inline void dec_counter_for_resize_op(KEY_CACHE *keycache) +{ + struct st_my_thread_var *last_thread; + if (!--keycache->cnt_for_resize_op && + (last_thread= keycache->resize_queue.last_thread)) + keycache_pthread_cond_signal(&last_thread->next->suspend); +} + +/* + Change the key cache parameters -void end_key_cache(void) + SYNOPSIS + change_key_cache_param() + keycache pointer to a key cache data structure + division_limit new division limit (if not zero) + age_threshold new age threshold (if not zero) + + RETURN VALUE + none + + NOTES. + Presently the function resets the key cache parameters + concerning midpoint insertion strategy - division_limit and + age_threshold. +*/ + +void change_key_cache_param(KEY_CACHE *keycache, uint division_limit, + uint age_threshold) +{ + DBUG_ENTER("change_key_cache_param"); + + keycache_pthread_mutex_lock(&keycache->cache_lock); + if (division_limit) + keycache->min_warm_blocks= (keycache->disk_blocks * + division_limit / 100 + 1); + if (age_threshold) + keycache->age_threshold= (keycache->disk_blocks * + age_threshold / 100); + keycache_pthread_mutex_unlock(&keycache->cache_lock); + DBUG_VOID_RETURN; +} + + +/* + Remove key_cache from memory + + SYNOPSIS + end_key_cache() + keycache key cache handle + cleanup Complete free (Free also mutex for key cache) + + RETURN VALUE + none +*/ + +void end_key_cache(KEY_CACHE *keycache, my_bool cleanup) { DBUG_ENTER("end_key_cache"); - if (! _my_blocks_changed) + DBUG_PRINT("enter", ("key_cache: %lx", keycache)); + + if (!keycache->key_cache_inited) + DBUG_VOID_RETURN; + + if (keycache->disk_blocks > 0) { - if (_my_disk_blocks > 0) + if (keycache->block_mem) { - my_free_lock((gptr) _my_block_mem,MYF(0)); - my_free((gptr) _my_block_root,MYF(0)); - _my_disk_blocks= -1; + my_free_lock((gptr) keycache->block_mem, MYF(0)); + keycache->block_mem= NULL; + my_free((gptr) keycache->block_root, MYF(0)); + keycache->block_root= NULL; } + keycache->disk_blocks= -1; + /* Reset blocks_changed to be safe if flush_all_key_blocks is called */ + keycache->blocks_changed= 0; } - 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", + keycache->blocks_used, keycache->global_blocks_changed, + keycache->global_cache_w_requests, keycache->global_cache_write, + keycache->global_cache_r_requests, keycache->global_cache_read)); + + if (cleanup) + { + pthread_mutex_destroy(&keycache->cache_lock); + keycache->key_cache_inited= 0; + KEYCACHE_DEBUG_CLOSE; + } 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. + + SYNOPSIS + link_into_queue() + wqueue pointer to the queue structure + thread pointer to the thread to be added to the queue + + RETURN VALUE + none + + NOTES. + Queue is represented by a circular list of the thread structures + The list is double-linked of the type (**prev,*next), accessed by + a pointer to the last element. +*/ + +static 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 + + SYNOPSIS + unlink_from_queue() + wqueue pointer to the queue structure + thread pointer to the thread to be removed from the queue + + RETURN VALUE + none + + NOTES. + See NOTES for link_into_queue +*/ + +static 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 + + SYNOPSIS + add_to_queue() + wqueue pointer to the queue structure + thread pointer to the thread to be added to the queue + + RETURN VALUE + none + + NOTES. + Queue is represented by a circular list of the thread structures + The list is single-linked of the type (*next), accessed by a pointer + to the last element. +*/ + +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 + + SYNOPSIS + realease_queue() + wqueue pointer to the queue structure + thread pointer to the thread to be added to the queue + + RETURN VALUE + none + + NOTES. + See notes for add_to_queue + When removed from the queue each thread is signaled via condition + variable thread->suspend. +*/ + +static 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) +{ + if (block->next_changed) + block->next_changed->prev_changed= block->prev_changed; + *block->prev_changed= block->next_changed; +} + + +/* + Link a block into the chain of dirty/clean blocks +*/ + +static inline void link_changed(BLOCK_LINK *block, BLOCK_LINK **phead) +{ + block->prev_changed= phead; + if ((block->next_changed= *phead)) + (*phead)->prev_changed= &block->next_changed; + *phead= block; +} + + +/* + 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 void link_to_file_list(KEY_CACHE *keycache, + BLOCK_LINK *block, int file, my_bool unlink) +{ + if (unlink) + unlink_changed(block); + link_changed(block, &keycache->file_blocks[FILE_HASH(file)]); + if (block->status & BLOCK_CHANGED) + { + block->status&= ~BLOCK_CHANGED; + keycache->blocks_changed--; + keycache->global_blocks_changed--; + } +} + + +/* + 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(KEY_CACHE *keycache, + BLOCK_LINK *block) +{ + unlink_changed(block); + link_changed(block, + &keycache->changed_blocks[FILE_HASH(block->hash_link->file)]); + block->status|=BLOCK_CHANGED; + keycache->blocks_changed++; + keycache->global_blocks_changed++; +} + + +/* + Link a block to the LRU chain at the beginning or at the end of + one of two parts. + + SYNOPSIS + link_block() + keycache pointer to a key cache data structure + block pointer to the block to link to the LRU chain + hot <-> to link the block into the hot subchain + at_end <-> to link the block at the end of the subchain + + RETURN VALUE + none + + NOTES. + The LRU chain is represented by a curcular list of block structures. + The list is double-linked of the type (**prev,*next) type. + The LRU chain is divided into two parts - hot and warm. + There are two pointers to access the last blocks of these two + parts. The beginning of the warm part follows right after the + end of the hot part. + Only blocks of the warm part can be used for replacement. + The first block from the beginning of this subchain is always + taken for eviction (keycache->last_used->next) + + LRU chain: +------+ H O T +------+ + +----| end |----...<----| beg |----+ + | +------+last +------+ | + v<-link in latest hot (new end) | + | link in latest warm (new end)->^ + | +------+ W A R M +------+ | + +----| beg |---->...----| end |----+ + +------+ +------+ins + first for eviction +*/ + +static void link_block(KEY_CACHE *keycache, BLOCK_LINK *block, my_bool hot, + my_bool at_end) +{ + BLOCK_LINK *ins; + BLOCK_LINK **pins; + + KEYCACHE_DBUG_ASSERT(! (block->hash_link && block->hash_link->requests)); + if (!hot && keycache->waiting_for_block.last_thread) { + /* Signal that in the LRU warm sub-chain an available block has appeared */ + struct st_my_thread_var *last_thread= + keycache->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(&keycache->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, keycache->blocks_available)); +#endif + return; + } + pins= hot ? &keycache->used_ins : &keycache->used_last; + ins= *pins; + if (ins) + { + ins->next_used->prev_used= &block->next_used; + block->next_used= ins->next_used; + block->prev_used= &ins->next_used; + ins->next_used= block; + if (at_end) + *pins= block; + } + else + { + /* The LRU chain is empty */ + keycache->used_last= keycache->used_ins= block->next_used= block; + block->prev_used= &block->next_used; + } + KEYCACHE_THREAD_TRACE("link_block"); +#if defined(KEYCACHE_DEBUG) + keycache->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, keycache->blocks_available)); + KEYCACHE_DBUG_ASSERT((ulong) keycache->blocks_available <= + keycache->blocks_used); +#endif +} + + +/* + Unlink a block from the LRU chain + + SYNOPSIS + unlink_block() + keycache pointer to a key cache data structure + block pointer to the block to unlink from the LRU chain + + RETURN VALUE + none + + NOTES. + See NOTES for link_block +*/ + +static void unlink_block(KEY_CACHE *keycache, BLOCK_LINK *block) +{ + if (block->next_used == block) + /* The list contains only one member */ + keycache->used_last= keycache->used_ins= NULL; + else + { + block->next_used->prev_used= block->prev_used; + *block->prev_used= block->next_used; + if (keycache->used_last == block) + keycache->used_last= STRUCT_PTR(BLOCK_LINK, next_used, block->prev_used); + if (keycache->used_ins == block) + keycache->used_ins=STRUCT_PTR(BLOCK_LINK, next_used, block->prev_used); + } + block->next_used= NULL; + + KEYCACHE_THREAD_TRACE("unlink_block"); +#if defined(KEYCACHE_DEBUG) + keycache->blocks_available--; + KEYCACHE_DBUG_PRINT("unlink_block", + ("unlinked block %u status=%x #requests=%u #available=%u", + BLOCK_NUMBER(block), block->status, + block->requests, keycache->blocks_available)); + KEYCACHE_DBUG_ASSERT(keycache->blocks_available >= 0); +#endif +} + + +/* + Register requests for a block +*/ +static void reg_requests(KEY_CACHE *keycache, BLOCK_LINK *block, int count) { - 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->requests) + /* First request for the block unlinks it */ + unlink_block(keycache, block); + block->requests+=count; +} + + +/* + Unregister request for a block + linking it to the LRU chain if it's the last request + + SYNOPSIS + + unreg_block() + keycache pointer to a key cache data structure + block pointer to the block to link to the LRU chain + at_end <-> to link the block at the end of the LRU chain + + RETURN VALUE + none + + NOTES. + Every linking to the LRU chain decrements by one a special block + counter (if it's positive). If the at_end parameter is TRUE the block is + added either at the end of warm sub-chain or at the end of hot sub-chain. + It is added to the hot subchain if its counter is zero and number of + blocks in warm sub-chain is not less than some low limit (determined by + the division_limit parameter). Otherwise the block is added to the warm + sub-chain. If the at_end parameter is FALSE the block is always added + at beginning of the warm sub-chain. + Thus a warm block can be promoted to the hot sub-chain when its counter + becomes zero for the first time. + At the same time the block at the very beginning of the hot subchain + might be moved to the beginning of the warm subchain if it stays untouched + for a too long time (this time is determined by parameter age_threshold). +*/ + +static inline void unreg_request(KEY_CACHE *keycache, + BLOCK_LINK *block, int at_end) +{ + if (! --block->requests) + { + my_bool hot; + if (block->hits_left) + block->hits_left--; + hot= !block->hits_left && at_end && + keycache->warm_blocks > keycache->min_warm_blocks; + if (hot) + { + if (block->temperature == BLOCK_WARM) + keycache->warm_blocks--; + block->temperature= BLOCK_HOT; + KEYCACHE_DBUG_PRINT("unreg_request", ("#warm_blocks=%u", + keycache->warm_blocks)); + } + link_block(keycache, block, hot, (my_bool)at_end); + block->last_hit_time= keycache->keycache_time; + if (++keycache->keycache_time - keycache->used_ins->last_hit_time > + keycache->age_threshold) + { + block= keycache->used_ins; + unlink_block(keycache, block); + link_block(keycache, block, 0, 0); + if (block->temperature != BLOCK_WARM) + { + keycache->warm_blocks++; + block->temperature= BLOCK_WARM; + } + KEYCACHE_DBUG_PRINT("unreg_request", ("#warm_blocks=%u", + keycache->warm_blocks)); + } + } } +/* + Remove a reader of the page in block +*/ -static inline void relink_into_file_blocks(SEC_LINK *next, int file) +static inline void remove_reader(BLOCK_LINK *block) { - 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; + if (! --block->hash_link->requests && block->condvar) + keycache_pthread_cond_signal(block->condvar); } -static inline void link_changed_to_file(SEC_LINK *next,int file) + +/* + Wait until the last reader of the page in block + signals on its termination +*/ + +static inline void wait_for_readers(KEY_CACHE *keycache, BLOCK_LINK *block) { - 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--; + struct st_my_thread_var *thread= my_thread_var; + while (block->hash_link->requests) + { + block->condvar= &thread->suspend; + keycache_pthread_cond_wait(&thread->suspend, &keycache->cache_lock); + block->condvar= NULL; + } } -static inline void link_file_to_changed(SEC_LINK *next) + +/* + Add a hash link to a bucket in the hash_table +*/ + +static inline void link_hash(HASH_LINK **start, HASH_LINK *hash_link) { - 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; - _my_blocks_changed++; + if (*start) + (*start)->prev= &hash_link->next; + hash_link->next= *start; + hash_link->prev= start; + *start= hash_link; } -#if !defined(DBUG_OFF) && !defined(EXTRA_DEBUG) -#define DBUG_OFF /* This should work */ +/* + Remove a hash link from the hash table +*/ + +static void unlink_hash(KEY_CACHE *keycache, 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 (keycache->waiting_for_hash_link.last_thread) + { + /* Signal that a free hash link has appeared */ + struct st_my_thread_var *last_thread= + keycache->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(&keycache->waiting_for_hash_link, thread); + } + } + while (thread != last_thread); + link_hash(&keycache->hash_root[KEYCACHE_HASH(hash_link->file, + hash_link->diskpos)], + hash_link); + return; + } + hash_link->next= keycache->free_hash_list; + keycache->free_hash_list= hash_link; +} + + +/* + Get the hash link for a page +*/ + +static HASH_LINK *get_hash_link(KEY_CACHE *keycache, + int file, my_off_t filepos) +{ + reg1 HASH_LINK *hash_link, **start; + KEYCACHE_PAGE page; +#if defined(KEYCACHE_DEBUG) + int cnt; #endif -#ifndef DBUG_OFF -static void test_key_cache(const char *where, my_bool lock); + 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= &keycache->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 <= keycache->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(cnt <= keycache->hash_links_used); #endif + } + if (! hash_link) + { + /* There is no hash link in the hash table for the pair (file, filepos) */ + if (keycache->free_hash_list) + { + hash_link= keycache->free_hash_list; + keycache->free_hash_list= hash_link->next; + } + else if (keycache->hash_links_used < keycache->hash_links) + { + hash_link= &keycache->hash_link_root[keycache->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(&keycache->waiting_for_hash_link, thread); + keycache_pthread_cond_wait(&thread->suspend, + &keycache->cache_lock); + 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; +} - /* - ** 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 - */ +/* + 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. + + SYNOPSIS + + find_key_block() + keycache pointer to a key cache data structure + file handler for the file to read page from + filepos position of the page in the file + init_hits_left how initialize the block counter for the page + wrmode <-> get for writing + page_st out {PAGE_READ,PAGE_TO_BE_READ,PAGE_WAIT_TO_BE_READ} + + RETURN VALUE + Pointer to the found block if successful, 0 - otherwise + + NOTES. + For the page from file positioned at filepos the function checks whether + the page is in the key cache specified by the first parameter. + If this is the case it immediately returns the block. + If not, the function first chooses a block for this page. If there is + no not used blocks in the key cache yet, the function takes the block + at the very beginning of the warm sub-chain. It saves the page in that + block if it's dirty before returning the pointer to it. + The function returns in the page_st parameter the following values: + PAGE_READ - if page already in the block, + PAGE_TO_BE_READ - if it is to be read yet by the current thread + WAIT_TO_BE_READ - if it is to be read by another thread + If an error occurs THE BLOCK_ERROR bit is set in the block status. + It might happen that there are no blocks in LRU chain (in warm part) - + all blocks are unlinked for some read/write operations. Then the function + waits until first of this operations links any block back. +*/ + +static BLOCK_LINK *find_key_block(KEY_CACHE *keycache, + File file, my_off_t filepos, + int init_hits_left, + 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(keycache, "start of find_key_block", 0);); +#endif + +restart: + /* Find the hash link for the requested page (file, filepos) */ + hash_link= get_hash_link(keycache, file, filepos); -byte *key_cache_read(File file, my_off_t filepos, byte *buff, uint length, + page_status= -1; + if ((block= hash_link->block) && + block->hash_link == hash_link && (block->status & BLOCK_READ)) + page_status= PAGE_READ; + + if (wrmode && keycache->resize_in_flush) + { + /* This is a write request during the flush phase of a resize operation */ + + if (page_status != PAGE_READ) + { + /* We don't need the page in the cache: we are going to write on disk */ + hash_link->requests--; + unlink_hash(keycache, hash_link); + return 0; + } + if (!(block->status & BLOCK_IN_FLUSH)) + { + hash_link->requests--; + /* + Remove block to invalidate the page in the block buffer + as we are going to write directly on disk. + Although we have an exlusive lock for the updated key part + the control can be yieded by the current thread as we might + have unfinished readers of other key parts in the block + buffer. Still we are guaranteed not to have any readers + of the key part we are writing into until the block is + removed from the cache as we set the BLOCL_REASSIGNED + flag (see the code below that handles reading requests). + */ + free_block(keycache, block); + return 0; + } + /* Wait intil the page is flushed on disk */ + hash_link->requests--; + { + 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, + &keycache->cache_lock); + } + while(thread->next); + } + /* Invalidate page in the block if it has not been done yet */ + if (block->status) + free_block(keycache, block); + return 0; + } + + if (page_status == PAGE_READ && + (block->status & (BLOCK_IN_SWITCH | BLOCK_REASSIGNED))) + { + /* 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(keycache, 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, + &keycache->cache_lock); + } + 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 (keycache->blocks_unused) + { + if (keycache->free_block_list) + { + /* There is a block in the free list. */ + block= keycache->free_block_list; + keycache->free_block_list= block->next_used; + block->next_used= NULL; + } + else + { + /* There are some never used blocks, take first of them */ + block= &keycache->block_root[keycache->blocks_used]; + block->buffer= ADD_TO_PTR(keycache->block_mem, + ((ulong) keycache->blocks_used* + keycache->key_cache_block_size), + byte*); + keycache->blocks_used++; + } + keycache->blocks_unused--; + block->status= 0; + block->length= 0; + block->offset= keycache->key_cache_block_size; + block->requests= 1; + block->temperature= BLOCK_COLD; + block->hits_left= init_hits_left; + block->last_hit_time= 0; + link_to_file_list(keycache, block, file, 0); + block->hash_link= hash_link; + hash_link->block= block; + page_status= PAGE_TO_BE_READ; + KEYCACHE_DBUG_PRINT("find_key_block", + ("got free or 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 (! keycache->used_last) + { + struct st_my_thread_var *thread= my_thread_var; + thread->opt_info= (void *) hash_link; + link_into_queue(&keycache->waiting_for_block, thread); + do + { + keycache_pthread_cond_wait(&thread->suspend, + &keycache->cache_lock); + } + 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= keycache->used_last->next_used; + block->hits_left= init_hits_left; + block->last_hit_time= 0; + reg_requests(keycache, 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(&keycache->cache_lock); + /* + 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->offset, + block->length - block->offset, + block->hash_link->diskpos+ block->offset, + MYF(MY_NABP | MY_WAIT_IF_FULL)); + keycache_pthread_mutex_lock(&keycache->cache_lock); + keycache->global_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(keycache, block); + + /* Remove the hash link for this page from the hash table */ + unlink_hash(keycache, 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(keycache, block, file, + (my_bool)(block->hash_link ? 1 : 0)); + block->status= error? BLOCK_ERROR : 0; + block->length= 0; + block->offset= keycache->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; + } + } + keycache->global_cache_read++; + } + else + { + reg_requests(keycache, 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; + KEYCACHE_DBUG_PRINT("find_key_block", + ("file %u, filepos %lu, page_status %lu", + (uint) file,(ulong) filepos,(uint) page_status)); + +#if !defined(DBUG_OFF) && defined(EXTRA_DEBUG) + DBUG_EXECUTE("check_keycache2", + test_key_cache(keycache, "end of find_key_block",0);); +#endif + KEYCACHE_THREAD_TRACE("find_key_block:end"); + DBUG_RETURN(block); +} + + +/* + Read into a key cache block buffer from disk. + + SYNOPSIS + + read_block() + keycache pointer to a key cache data structure + block block to which buffer the data is to be read + read_length size of data to be read + min_length at least so much data must be read + primary <-> the current thread will read the data + + RETURN VALUE + None + + NOTES. + The function either reads a page data from file to the block buffer, + or waits until another thread reads it. What page to read is determined + by a block parameter - reference to a hash link for this page. + If an error occurs THE BLOCK_ERROR bit is set in the block status. + We do not report error when the size of successfully read + portion is less than read_length, but not less than min_length. +*/ + +static void read_block(KEY_CACHE *keycache, + BLOCK_LINK *block, uint read_length, + uint min_length, my_bool primary) +{ + uint got_length; + + /* On entry cache_lock 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(&keycache->cache_lock); + got_length= my_pread(block->hash_link->file, block->buffer, + read_length, block->hash_link->diskpos, MYF(0)); + keycache_pthread_mutex_lock(&keycache->cache_lock); + 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, + &keycache->cache_lock); + } + 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; + + SYNOPSIS + + key_cache_read() + keycache pointer to a key cache data structure + file handler for the file for the block of data to be read + filepos position of the block of data in the file + level determines the weight of the data + buff buffer to where the data must be placed + length length of the buffer + block_length length of the block in the key cache buffer + return_buffer return pointer to the key cache buffer with the data + + RETURN VALUE + Returns address from where the data is placed if sucessful, 0 - otherwise. + + NOTES. + The function ensures that a block of data of size length from file + positioned at filepos is in the buffers for some key cache blocks. + Then the function either copies the data into the buffer buff, or, + if return_buffer is TRUE, it just returns the pointer to the key cache + buffer with the data. + Filepos must be a multiple of 'block_length', but it doesn't + have to be a multiple of key_cache_block_size; +*/ + +byte *key_cache_read(KEY_CACHE *keycache, + File file, my_off_t filepos, int level, + byte *buff, uint length, uint block_length __attribute__((unused)), int return_buffer __attribute__((unused))) { - reg1 SEC_LINK *next; int error=0; + uint offset= 0; + byte *start= buff; DBUG_ENTER("key_cache_read"); DBUG_PRINT("enter", ("file %u, filepos %lu, length %u", - (uint) file, (ulong) filepos, length)); + (uint) file, (ulong) filepos, length)); -#ifndef THREAD - if (block_length > key_cache_block_size) - return_buffer=0; -#endif - if (_my_disk_blocks > 0) - { /* We have key_cacheing */ - byte *start=buff; + if (keycache->can_be_used) + { + /* Key cache is used */ + reg1 BLOCK_LINK *block; 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; + + /* Read data in key_cache_block_size increments */ do { - _my_cache_r_requests++; - read_length= (length > key_cache_block_size ? key_cache_block_size : - length); - if (!(next=find_key_block(file,filepos,&error))) + keycache_pthread_mutex_lock(&keycache->cache_lock); + if (!keycache->can_be_used) { - pthread_mutex_unlock(&THR_LOCK_keycache); - DBUG_RETURN ((byte*) 0); /* Got a fatal error */ + keycache_pthread_mutex_unlock(&keycache->cache_lock); + goto no_key_cache; } - 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++; + read_length= length > keycache->key_cache_block_size ? + keycache->key_cache_block_size : length; + KEYCACHE_DBUG_ASSERT(read_length > 0); + offset= (uint) (filepos & (keycache->key_cache_block_size-1)); + filepos-= offset; +#ifndef THREAD + if (block_length > keycache->key_cache_block_size || offset) + return_buffer=0; +#endif + + inc_counter_for_resize_op(keycache); + keycache->global_cache_r_requests++; + block=find_key_block(keycache, file, filepos, level, 0, &page_st); + if (block->status != BLOCK_ERROR && page_st != PAGE_READ) + { + /* The requested page is to be read into the block buffer */ + read_block(keycache, block, + keycache->key_cache_block_size, read_length+offset, + (my_bool)(page_st == PAGE_TO_BE_READ)); } -#ifndef THREAD /* buffer may be used a long time */ - if (return_buffer) + else if (! (block->status & BLOCK_ERROR) && + block->length < read_length + offset) { - pthread_mutex_unlock(&THR_LOCK_keycache); - DBUG_RETURN (next->buffer); + /* + 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; } + + if (! ((status= block->status) & BLOCK_ERROR)) + { +#ifndef THREAD + if (! return_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; + { +#if !defined(SERIALIZED_READ_FROM_CACHE) + keycache_pthread_mutex_unlock(&keycache->cache_lock); +#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(&keycache->cache_lock); +#endif + } + } + + remove_reader(block); + /* + Link the block into the LRU chain + if it's the last submitted request for the block + */ + unreg_request(keycache, block, 1); + + dec_counter_for_resize_op(keycache); + + keycache_pthread_mutex_unlock(&keycache->cache_lock); + + if (status & BLOCK_ERROR) + DBUG_RETURN((byte *) 0); + +#ifndef THREAD + /* This is only true if we where able to read everything in one block */ + if (return_buffer) + return (block->buffer); +#endif + buff+= read_length; + filepos+= read_length; + } while ((length-= read_length)); - pthread_mutex_unlock(&THR_LOCK_keycache); DBUG_RETURN(start); } -no_key_cache: - _my_cache_r_requests++; - _my_cache_read++; - if (my_pread(file,(byte*) buff,length,filepos,MYF(MY_NABP))) - error=1; - DBUG_RETURN(error ? (byte*) 0 : buff); -} /* key_cache_read */ +no_key_cache: /* Key cache is not used */ + + /* We can't use mutex here as the key cache may not be initialized */ + keycache->global_cache_r_requests++; + keycache->global_cache_read++; + if (my_pread(file, (byte*) buff, length, filepos+offset, MYF(MY_NABP))) + error= 1; + DBUG_RETURN(error ? (byte*) 0 : start); +} - /* 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 */ +/* + Insert a block of file data from a buffer into key cache -int key_cache_write(File file, my_off_t filepos, byte *buff, uint length, - uint block_length __attribute__((unused)), - int dont_write) + SYNOPSIS + key_cache_insert() + keycache pointer to a key cache data structure + file handler for the file to insert data from + filepos position of the block of data in the file to insert + level determines the weight of the data + buff buffer to read data from + length length of the data in the buffer + + NOTES + This is used by MyISAM to move all blocks from a index file to the key + cache + + RETURN VALUE + 0 if a success, 1 - otherwise. +*/ + +int key_cache_insert(KEY_CACHE *keycache, + File file, my_off_t filepos, int level, + byte *buff, uint length) { - reg1 SEC_LINK *next; - int error=0; - DBUG_ENTER("key_cache_write"); + DBUG_ENTER("key_cache_insert"); DBUG_PRINT("enter", ("file %u, filepos %lu, length %u", - (uint) file, (ulong) filepos, length)); + (uint) file,(ulong) filepos, length)); - if (!dont_write) - { /* Forced write of buffer */ - _my_cache_write++; - if (my_pwrite(file,buff,length,filepos,MYF(MY_NABP | MY_WAIT_IF_FULL))) - 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 */ + if (keycache->can_be_used) + { + /* Key cache is used */ + reg1 BLOCK_LINK *block; 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; - } + int page_st; + int error; - _my_cache_w_requests++; 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 */ + uint offset; + keycache_pthread_mutex_lock(&keycache->cache_lock); + if (!keycache->can_be_used) { - if (next->changed) /* Unlink from changed list */ - link_changed_to_file(next,next->file); + keycache_pthread_mutex_unlock(&keycache->cache_lock); + DBUG_RETURN(0); } - else if (!next->changed) - link_file_to_changed(next); /* Add to changed list */ + read_length= length > keycache->key_cache_block_size ? + keycache->key_cache_block_size : length; + KEYCACHE_DBUG_ASSERT(read_length > 0); + offset= (uint) (filepos & (keycache->key_cache_block_size-1)); + /* Read data into key cache from buff in key_cache_block_size incr. */ + filepos-= offset; + + inc_counter_for_resize_op(keycache); + keycache->global_cache_r_requests++; + block= find_key_block(keycache, file, filepos, level, 0, &page_st); + if (block->status != BLOCK_ERROR && page_st != PAGE_READ) + { + /* The requested page is to be read into the block buffer */ +#if !defined(SERIALIZED_READ_FROM_CACHE) + keycache_pthread_mutex_unlock(&keycache->cache_lock); +#endif + + /* Copy data from buff */ + if (!(read_length & 511)) + bmove512(block->buffer+offset, buff, read_length); + else + memcpy(block->buffer+offset, buff, (size_t) read_length); + +#if !defined(SERIALIZED_READ_FROM_CACHE) + keycache_pthread_mutex_lock(&keycache->cache_lock); +#endif + block->status= BLOCK_READ; + block->length= read_length+offset; + } + + remove_reader(block); + /* + Link the block into the LRU chain + if it's the last submitted request for the block + */ + unreg_request(keycache, block, 1); + + error= (block->status & BLOCK_ERROR); + + dec_counter_for_resize_op(keycache); + + keycache_pthread_mutex_unlock(&keycache->cache_lock); + + if (error) + DBUG_RETURN(1); + + buff+= read_length; + filepos+= read_length; - if (!(read_length & 511)) - bmove512(next->buffer,buff,read_length); - else - memcpy(next->buffer,buff,(size_t) read_length); - buff+=read_length; - filepos+=read_length; } while ((length-= read_length)); - error=0; - pthread_mutex_unlock(&THR_LOCK_keycache); - goto end; } + DBUG_RETURN(0); +} -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 */ +/* + Write a buffer into a cached file. + SYNOPSIS - /* Find block in cache */ - /* IF found sector and error is set then next->changed is cleared */ + key_cache_write() + keycache pointer to a key cache data structure + file handler for the file to write data to + filepos position in the file to write data to + level determines the weight of the data + buff buffer with the data + length length of the buffer + dont_write if is 0 then all dirty pages involved in writing + should have been flushed from key cache + + RETURN VALUE + 0 if a success, 1 - otherwise. + + NOTES. + The function copies the data of size length from buff into buffers + for key cache blocks that are assigned to contain the portion of + the file starting with position filepos. + It ensures that this data is flushed to the file if dont_write is FALSE. + Filepos must be a multiple of 'block_length', but it doesn't + have to be a multiple of key_cache_block_size; +*/ -static SEC_LINK *find_key_block(int file, my_off_t filepos, int *error) +int key_cache_write(KEY_CACHE *keycache, + File file, my_off_t filepos, int level, + byte *buff, uint length, + uint block_length __attribute__((unused)), + int dont_write) { - reg1 SEC_LINK *next,**start; - DBUG_ENTER("find_key_block"); - DBUG_PRINT("enter", ("file %u, filepos %lu", - (uint) file, (ulong) filepos)); + reg1 BLOCK_LINK *block; + int error=0; + DBUG_ENTER("key_cache_write"); + DBUG_PRINT("enter", + ("file %u filepos %lu length %u block_length %u key_block_length: %u", + (uint) file, (ulong) filepos, length, block_length, + keycache ? keycache->key_cache_block_size : 0)); + + if (!dont_write) + { + /* Force writing from buff into disk */ + keycache->global_cache_write++; + 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_keycache2",test_key_cache("start of find_key_block",0);); + DBUG_EXECUTE("check_keycache", + test_key_cache(keycache, "start of key_cache_write", 1);); #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 (keycache->can_be_used) + { + /* Key cache is used */ + uint read_length; + int page_st; - if (next) - { /* Found block */ - if (next != _my_used_last) - { /* Relink used-chain */ - if (next == _my_used_first) - _my_used_first=next->next_used; - else + do + { + uint offset; + keycache_pthread_mutex_lock(&keycache->cache_lock); + if (!keycache->can_be_used) { - next->prev_used->next_used = next->next_used; - next->next_used->prev_used = next->prev_used; + keycache_pthread_mutex_unlock(&keycache->cache_lock); + goto no_key_cache; } - 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 */ - } - else - { /* Reuse old block */ - next= _my_used_first; - if (next->changed) + read_length= length > keycache->key_cache_block_size ? + keycache->key_cache_block_size : length; + KEYCACHE_DBUG_ASSERT(read_length > 0); + offset= (uint) (filepos & (keycache->key_cache_block_size-1)); + /* Write data in key_cache_block_size increments */ + filepos-= offset; + + inc_counter_for_resize_op(keycache); + keycache->global_cache_w_requests++; + block= find_key_block(keycache, file, filepos, level, 1, &page_st); + if (!block) { - 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); + /* It happens only for requests submitted during resize operation */ + dec_counter_for_resize_op(keycache); + keycache_pthread_mutex_unlock(&keycache->cache_lock); + if (dont_write) + { + keycache->global_cache_w_requests++; + keycache->global_cache_write++; + if (my_pwrite(file, (byte*) buff, length, filepos, + MYF(MY_NABP | MY_WAIT_IF_FULL))) + error=1; } - _my_cache_write++; - link_changed_to_file(next,file); + goto next_block; } - else + + if (block->status != BLOCK_ERROR && page_st != PAGE_READ && + (offset || read_length < keycache->key_cache_block_size)) + read_block(keycache, block, + offset + read_length >= keycache->key_cache_block_size? + offset : keycache->key_cache_block_size, + offset,(my_bool)(page_st == PAGE_TO_BE_READ)); + + if (!dont_write) { - if (next->file == -1) - link_into_file_blocks(next,file); - else - relink_into_file_blocks(next,file); + /* buff has been written to disk at start */ + if ((block->status & BLOCK_CHANGED) && + (!offset && read_length >= keycache->key_cache_block_size)) + link_to_file_list(keycache, block, block->hash_link->file, 1); } - 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; + else if (! (block->status & BLOCK_CHANGED)) + link_to_changed_list(keycache, block); - _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; + set_if_smaller(block->offset, offset); + set_if_bigger(block->length, read_length+offset); + + if (! (block->status & BLOCK_ERROR)) + { + if (!(read_length & 511)) + bmove512(block->buffer+offset, buff, read_length); + else + memcpy(block->buffer+offset, buff, (size_t) read_length); + } + + block->status|=BLOCK_READ; + + /* Unregister the request */ + block->hash_link->requests--; + unreg_request(keycache, block, 1); + + if (block->status & BLOCK_ERROR) + { + keycache_pthread_mutex_unlock(&keycache->cache_lock); + error= 1; + break; + } + + dec_counter_for_resize_op(keycache); + + keycache_pthread_mutex_unlock(&keycache->cache_lock); + + next_block: + buff+= read_length; + filepos+= read_length; + offset= 0; + + } while ((length-= read_length)); + goto end; + } + +no_key_cache: + /* Key cache is not used */ + if (dont_write) + { + keycache->global_cache_w_requests++; + keycache->global_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_keycache2",test_key_cache("end of find_key_block",0);); + DBUG_EXECUTE("exec", + test_key_cache(keycache, "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 to the free list. +*/ + +static void free_block(KEY_CACHE *keycache, 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(keycache, block); + unlink_hash(keycache, 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= keycache->key_cache_block_size; + KEYCACHE_THREAD_TRACE("free block"); + KEYCACHE_DBUG_PRINT("free_block", + ("block is freed")); + unreg_request(keycache, block, 0); + block->hash_link= NULL; + + /* Remove the free block from the LRU ring. */ + unlink_block(keycache, block); + if (block->temperature == BLOCK_WARM) + keycache->warm_blocks--; + block->temperature= BLOCK_COLD; + /* Insert the free block in the free list. */ + block->next_used= keycache->free_block_list; + keycache->free_block_list= block; + /* Keep track of the number of currently unused blocks. */ + keycache->blocks_unused++; +} - /* 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(KEY_CACHE *keycache, + File file, BLOCK_LINK **cache, + BLOCK_LINK **end, + enum flush_type type) { - uint last_errno=0; + int error; + int last_errno= 0; + uint count= end-cache; + + /* Don't lock the cache during the flush */ + keycache_pthread_mutex_unlock(&keycache->cache_lock); + /* + 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); - for ( ; count-- ; cache++) + + keycache_pthread_mutex_lock(&keycache->cache_lock); + 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(&keycache->cache_lock); + error= my_pwrite(file, + block->buffer+block->offset, + block->length - block->offset, + block->hash_link->diskpos+ block->offset, + MYF(MY_NABP | MY_WAIT_IF_FULL)); + keycache_pthread_mutex_lock(&keycache->cache_lock); + keycache->global_cache_write++; + if (error) { + block->status|= BLOCK_ERROR; if (!last_errno) - last_errno=errno ? errno : -1; + last_errno= errno ? errno : -1; + } + /* + Let to proceed for possible waiting requests to write to the block page. + It might happen only during an operation to resize the key cache. + */ + if (block->wqueue[COND_FOR_SAVED].last_thread) + release_queue(&block->wqueue[COND_FOR_SAVED]); + /* type will never be FLUSH_IGNORE_CHANGED here */ + if (! (type == FLUSH_KEEP || type == FLUSH_FORCE_WRITE)) + { + keycache->blocks_changed--; + keycache->global_blocks_changed--; + free_block(keycache, block); } + else + { + block->status&= ~BLOCK_IN_FLUSH; + link_to_file_list(keycache, block, file, 1); + unreg_request(keycache, block, 1); + } + } return last_errno; } -static int flush_key_blocks_int(File file, enum flush_type type) +/* + flush all key blocks for a file to disk, but don't do any mutex locks + + flush_key_blocks_int() + keycache pointer to a key cache data structure + file handler for the file to flush to + flush_type type of the flush + + NOTES + This function doesn't do any mutex locks because it needs to be called both + from flush_key_blocks and flush_all_key_blocks (the later one does the + mutex lock in the resize_key_cache() function). + + RETURN + 0 ok + 1 error +*/ + +static int flush_key_blocks_int(KEY_CACHE *keycache, + 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; + BLOCK_LINK *cache_buff[FLUSH_CACHE],**cache; + int last_errno= 0; DBUG_ENTER("flush_key_blocks_int"); DBUG_PRINT("enter",("file: %d blocks_used: %d blocks_changed: %d", - file,_my_blocks_used,_my_blocks_changed)); + file, keycache->blocks_used, keycache->blocks_changed)); - cache=cache_buff; /* If no key cache */ - if (_my_disk_blocks > 0 && +#if !defined(DBUG_OFF) && defined(EXTRA_DEBUG) + DBUG_EXECUTE("check_keycache", + test_key_cache(keycache, "start of flush_key_blocks", 0);); +#endif + + cache= cache_buff; + if (keycache->disk_blocks > 0 && (!my_disable_flush_key_blocks || type != FLUSH_KEEP)) { -#if !defined(DBUG_OFF) && defined(EXTRA_DEBUG) - DBUG_EXECUTE("check_keycache",test_key_cache("start of flush_key_blocks",0);); + /* 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= keycache->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<= keycache->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 */ - end=(pos=cache)+count; - for (used=changed_blocks[(uint) file & CHANGED_BLOCKS_MASK]; - used ; - used=next) + /* Retrieve the blocks and write them to a buffer to be flushed */ +restart: + end= (pos= cache)+count; + for (block= keycache->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 <= keycache->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(keycache, 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(keycache, 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 */ + keycache->blocks_changed--; + keycache->global_blocks_changed--; + free_block(keycache, 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(keycache, 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, + &keycache->cache_lock); + } + while (thread->next); + } +#if defined(KEYCACHE_DEBUG) + cnt++; + KEYCACHE_DBUG_ASSERT(cnt <= keycache->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= keycache->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 <= keycache->blocks_used); +#endif + next= block->next_changed; + if (block->hash_link->file == file && + (! (block->status & BLOCK_CHANGED) + || type == FLUSH_IGNORE_CHANGED)) + { + reg_requests(keycache, block, 1); + free_block(keycache, block); + } } } + } + #ifndef DBUG_OFF - DBUG_EXECUTE("check_keycache",test_key_cache("end of flush_key_blocks",0);); + DBUG_EXECUTE("check_keycache", + test_key_cache(keycache, "end of flush_key_blocks", 0);); #endif - } if (cache != cache_buff) - my_free((gptr) cache,MYF(0)); + 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 for a file to disk SYNOPSIS + flush_key_blocks() - file File descriptor - type Type of flush operation + keycache pointer to a key cache data structure + file handler for the file to flush to + flush_type type of the flush - RETURN VALUES - 0 Ok - 1 Error + RETURN + 0 ok + 1 error */ -int flush_key_blocks(File file, enum flush_type type) +int flush_key_blocks(KEY_CACHE *keycache, + File file, enum flush_type type) { int res; - pthread_mutex_lock(&THR_LOCK_keycache); - res=flush_key_blocks_int(file, type); - pthread_mutex_unlock(&THR_LOCK_keycache); - return res; + DBUG_ENTER("flush_key_blocks"); + DBUG_PRINT("enter", ("keycache: %lx", keycache)); + + if (keycache->disk_blocks <= 0) + DBUG_RETURN(0); + keycache_pthread_mutex_lock(&keycache->cache_lock); + inc_counter_for_resize_op(keycache); + res= flush_key_blocks_int(keycache, file, type); + dec_counter_for_resize_op(keycache); + keycache_pthread_mutex_unlock(&keycache->cache_lock); + DBUG_RETURN(res); } /* Flush all blocks in the key cache to disk - - SYNOPSIS - flush_all_key_blocks() - - NOTE - We must have a lock on THR_LOCK_keycache before calling this function - - RETURN VALUES - 0 Ok - 1 Error */ - -static int flush_all_key_blocks() +static int flush_all_key_blocks(KEY_CACHE *keycache) { - SEC_LINK **block, **end; - for (block= changed_blocks, end= block+CHANGED_BLOCKS_HASH; - block < end; - block++ - ) +#if defined(KEYCACHE_DEBUG) + uint cnt=0; +#endif + while (keycache->blocks_changed > 0) { - while (*block) + BLOCK_LINK *block; + for (block= keycache->used_last->next_used ; ; block=block->next_used) { - if (flush_key_blocks_int((*block)->file, FLUSH_RELEASE)) - return 1; + if (block->hash_link) + { +#if defined(KEYCACHE_DEBUG) + cnt++; + KEYCACHE_DBUG_ASSERT(cnt <= keycache->blocks_used); +#endif + if (flush_key_blocks_int(keycache, block->hash_link->file, + FLUSH_RELEASE)) + return 1; + break; + } + if (block == keycache->used_last) + break; } } return 0; @@ -722,156 +2443,239 @@ static int flush_all_key_blocks() #ifndef DBUG_OFF +/* + Test if disk-cache is ok +*/ +static void test_key_cache(KEY_CACHE *keycache __attribute__((unused)), + const char *where __attribute__((unused)), + my_bool lock __attribute__((unused))) +{ + /* TODO */ +} +#endif - /* Test if disk-cache is ok */ +#if defined(KEYCACHE_TIMEOUT) -static void test_key_cache(const char *where, my_bool lock) -{ - reg1 uint i,error; - ulong found,changed; - SEC_LINK *pos,**prev; +#define KEYCACHE_DUMP_FILE "keycache_dump.txt" +#define MAX_QUEUE_LEN 100 - if (lock) - { - pthread_mutex_lock(&THR_LOCK_keycache); - if (_my_disk_blocks <= 0) /* No active key cache */ + +static void keycache_dump(KEY_CACHE *keycache) +{ + 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 { - pthread_mutex_unlock(&THR_LOCK_keycache); - return; + 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; } - } - found=error=0; - for (i= 0 ; i < _my_hash_blocks ; i++) - { + while (thread != last); - for (pos= *(prev= &_my_hash_root[i]) ; - pos && found < _my_blocks_used+2 ; - found++, pos= *(prev= &pos->next_hash)) + i=0; + thread=last=waiting_for_block.last_thread; + fprintf(keycache_dump_file, "queue of threads waiting for block\n"); + if (thread) + do { - 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)); - } - - 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; - } + 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; } - } - if (found > _my_blocks_used) + while (thread != last); + + for (i=0 ; i< keycache->blocks_used ; i++) { - 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++) + int j; + block= &keycache->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++) { - 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) + KEYCACHE_WQUEUE *wqueue=&block->wqueue[j]; + thread= last= wqueue->last_thread; + fprintf(keycache_dump_file, "queue #%d\n", j); + if (thread) { - 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; + do + { + thread=thread->next; + fprintf(keycache_dump_file, + "thread:%u\n", thread->id); + if (++i == MAX_QUEUE_LEN) + break; + } + while (thread != last); } } } - - found=changed=0; - - if ((pos=_my_used_first)) + fprintf(keycache_dump_file, "LRU chain:"); + block= keycache= used_last; + if (block) { - while (pos != _my_used_last && found < _my_blocks_used+2) + do { - 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; + block= block->next_used; + fprintf(keycache_dump_file, + "block:%u, ", BLOCK_NUMBER(block)); } - found++; - if (pos->changed) - changed++; + while (block != keycache->used_last); } - if (found != _my_blocks_used) + fprintf(keycache_dump_file, "\n"); + + fclose(keycache_dump_file); +} + +#endif /* defined(KEYCACHE_TIMEOUT) */ + +#if defined(KEYCACHE_TIMEOUT) && !defined(__WIN__) + + +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) { - DBUG_PRINT("error",("Found %lu of %lu keyblocks",found,_my_blocks_used)); - error=1; + fprintf(keycache_debug_log,"aborted by keycache timeout\n"); + fclose(keycache_debug_log); + abort(); } +#endif - 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 (rc == ETIMEDOUT) + keycache_dump(); - 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) +#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__) */ + +#if defined(KEYCACHE_DEBUG) + + +static int keycache_pthread_mutex_lock(pthread_mutex_t *mutex) +{ + int rc; + rc= pthread_mutex_lock(mutex); + KEYCACHE_THREAD_TRACE_BEGIN(""); + return rc; +} + + +static void keycache_pthread_mutex_unlock(pthread_mutex_t *mutex) +{ + KEYCACHE_THREAD_TRACE_END(""); + pthread_mutex_unlock(mutex); +} + + +static int keycache_pthread_cond_signal(pthread_cond_t *cond) +{ + int rc; + KEYCACHE_THREAD_TRACE("signal"); + rc= pthread_cond_signal(cond); + return rc; +} + + +static int keycache_pthread_cond_broadcast(pthread_cond_t *cond) +{ + int rc; + KEYCACHE_THREAD_TRACE("signal"); + rc= pthread_cond_broadcast(cond); + return rc; +} + +#if defined(KEYCACHE_DEBUG_LOG) + + +static void keycache_debug_print(const char * fmt,...) +{ + va_list args; + va_start(args,fmt); + if (keycache_debug_log) { - DBUG_PRINT("error",("Found %lu blocks that wasn't in changed blocks", - changed)); - error=1; + VOID(vfprintf(keycache_debug_log, fmt, args)); + VOID(fputc('\n',keycache_debug_log)); } - if (error) - DBUG_PRINT("error",("Found error at %s",where)); - if (lock) - pthread_mutex_unlock(&THR_LOCK_keycache); - return; -} /* test_key_cache */ -#endif + va_end(args); +} +#endif /* defined(KEYCACHE_DEBUG_LOG) */ + +#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) */ diff --git a/mysys/mf_keycaches.c b/mysys/mf_keycaches.c new file mode 100644 index 00000000000..806f83dc7d8 --- /dev/null +++ b/mysys/mf_keycaches.c @@ -0,0 +1,360 @@ +/* Copyright (C) 2003 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 */ + +/* + Handling of multiple key caches + + The idea is to have a thread safe hash on the table name, + with a default key cache value that is returned if the table name is not in + the cache. +*/ + +#include "mysys_priv.h" +#include <keycache.h> +#include <hash.h> +#include <m_string.h> + +/***************************************************************************** + General functions to handle SAFE_HASH objects. + + A SAFE_HASH object is used to store the hash, the mutex and default value + needed by the rest of the key cache code. + This is a separate struct to make it easy to later reuse the code for other + purposes + + All entries are linked in a list to allow us to traverse all elements + and delete selected ones. (HASH doesn't allow any easy ways to do this). +*****************************************************************************/ + +/* + Struct to store a key and pointer to object +*/ + +typedef struct st_safe_hash_entry +{ + byte *key; + uint length; + byte *data; + struct st_safe_hash_entry *next, **prev; +} SAFE_HASH_ENTRY; + + +typedef struct st_safe_hash_with_default +{ +#ifdef THREAD + rw_lock_t mutex; +#endif + HASH hash; + byte *default_value; + SAFE_HASH_ENTRY *root; +} SAFE_HASH; + + +/* + Free a SAFE_HASH_ENTRY + + This function is called by the hash object on delete +*/ + +static void safe_hash_entry_free(SAFE_HASH_ENTRY *entry) +{ + DBUG_ENTER("free_assign_entry"); + my_free((gptr) entry, MYF(0)); + DBUG_VOID_RETURN; +} + + +/* Get key and length for a SAFE_HASH_ENTRY */ + +static byte *safe_hash_entry_get(SAFE_HASH_ENTRY *entry, uint *length, + my_bool not_used __attribute__((unused))) +{ + *length=entry->length; + return (byte*) entry->key; +} + + +/* + Init a SAFE_HASH object + + SYNOPSIS + safe_hash_init() + hash safe_hash handler + elements Expected max number of elements + default_value default value + + NOTES + In case of error we set hash->default_value to 0 to allow one to call + safe_hash_free on an object that couldn't be initialized. + + RETURN + 0 ok + 1 error +*/ + +static my_bool safe_hash_init(SAFE_HASH *hash, uint elements, + byte *default_value) +{ + DBUG_ENTER("safe_hash"); + if (hash_init(&hash->hash, &my_charset_bin, elements, + 0, 0, (hash_get_key) safe_hash_entry_get, + (void (*)(void*)) safe_hash_entry_free, 0)) + { + hash->default_value= 0; + DBUG_RETURN(1); + } + my_rwlock_init(&hash->mutex, 0); + hash->default_value= default_value; + hash->root= 0; + DBUG_RETURN(0); +} + + +/* + Free a SAFE_HASH object + + NOTES + This is safe to call on any object that has been sent to safe_hash_init() +*/ + +static void safe_hash_free(SAFE_HASH *hash) +{ + /* + Test if safe_hash_init succeeded. This will also guard us against multiple + free calls. + */ + if (hash->default_value) + { + hash_free(&hash->hash); + rwlock_destroy(&hash->mutex); + hash->default_value=0; + } +} + +/* + Return the value stored for a key or default value if no key +*/ + +static byte *safe_hash_search(SAFE_HASH *hash, const byte *key, uint length) +{ + byte *result; + DBUG_ENTER("safe_hash_search"); + rw_rdlock(&hash->mutex); + result= hash_search(&hash->hash, key, length); + rw_unlock(&hash->mutex); + if (!result) + result= hash->default_value; + else + result= ((SAFE_HASH_ENTRY*) result)->data; + DBUG_PRINT("exit",("data: %lx", result)); + DBUG_RETURN(result); +} + + +/* + Associate a key with some data + + SYONOPSIS + safe_hash_set() + hash Hash handle + key key (path to table etc..) + length Length of key + data data to to associate with the data + + NOTES + This can be used both to insert a new entry and change an existing + entry. + If one associates a key with the default key cache, the key is deleted + + RETURN + 0 ok + 1 error (Can only be EOM). In this case my_message() is called. +*/ + +static my_bool safe_hash_set(SAFE_HASH *hash, const byte *key, uint length, + byte *data) +{ + SAFE_HASH_ENTRY *entry; + my_bool error= 0; + DBUG_ENTER("safe_hash_set"); + DBUG_PRINT("enter",("key: %.*s data: %lx", length, key, data)); + + rw_wrlock(&hash->mutex); + entry= (SAFE_HASH_ENTRY*) hash_search(&hash->hash, key, length); + + if (data == hash->default_value) + { + /* + The key is to be associated with the default entry. In this case + we can just delete the entry (if it existed) from the hash as a + search will return the default entry + */ + if (!entry) /* nothing to do */ + goto end; + /* unlink entry from list */ + if ((*entry->prev= entry->next)) + entry->next->prev= entry->prev; + hash_delete(&hash->hash, (byte*) entry); + goto end; + } + if (entry) + { + /* Entry existed; Just change the pointer to point at the new data */ + entry->data= data; + } + else + { + if (!(entry= (SAFE_HASH_ENTRY *) my_malloc(sizeof(*entry) + length, + MYF(MY_WME)))) + { + error= 1; + goto end; + } + entry->key= (byte*) (entry +1); + memcpy((char*) entry->key, (char*) key, length); + entry->length= length; + entry->data= data; + /* Link entry to list */ + if ((entry->next= hash->root)) + entry->next->prev= &entry->next; + entry->prev= &hash->root; + hash->root= entry; + if (my_hash_insert(&hash->hash, (byte*) entry)) + { + /* This can only happen if hash got out of memory */ + my_delete((char*) entry, MYF(0)); + error= 1; + goto end; + } + } + +end: + rw_unlock(&hash->mutex); + DBUG_RETURN(error); +} + + +/* + Change all entres with one data value to another data value + + SYONOPSIS + safe_hash_change() + hash Hash handle + old_data Old data + new_data Change all 'old_data' to this + + NOTES + We use the linked list to traverse all elements in the hash as + this allows us to delete elements in the case where 'new_data' is the + default value. +*/ + +static void safe_hash_change(SAFE_HASH *hash, byte *old_data, byte *new_data) +{ + SAFE_HASH_ENTRY *entry, *next; + DBUG_ENTER("safe_hash_set"); + + rw_wrlock(&hash->mutex); + + for (entry= hash->root ; entry ; entry= next) + { + next= entry->next; + if (entry->data == old_data) + { + if (new_data == hash->default_value) + hash_delete(&hash->hash, (byte*) entry); + else + entry->data= new_data; + } + } + + rw_unlock(&hash->mutex); + DBUG_VOID_RETURN; +} + + +/***************************************************************************** + Functions to handle the key cache objects +*****************************************************************************/ + +/* Variable to store all key cache objects */ +static SAFE_HASH key_cache_hash; + + +my_bool multi_keycache_init(void) +{ + return safe_hash_init(&key_cache_hash, 16, (byte*) dflt_key_cache); +} + + +void multi_keycache_free(void) +{ + safe_hash_free(&key_cache_hash); +} + +/* + Get a key cache to be used for a specific table. + + SYNOPSIS + multi_key_cache_get() + key key to find (usually table path) + uint length Length of key. + + NOTES + This function is coded in such a way that we will return the + default key cache even if one never called multi_keycache_init. + This will ensure that it works with old MyISAM clients. + + RETURN + key cache to use +*/ + +KEY_CACHE *multi_key_cache_search(byte *key, uint length) +{ + if (!key_cache_hash.hash.records) + return dflt_key_cache; + return (KEY_CACHE*) safe_hash_search(&key_cache_hash, key, length); +} + + +/* + Assosiate a key cache with a key + + + SYONOPSIS + multi_key_cache_set() + key key (path to table etc..) + length Length of key + key_cache cache to assococite with the table + + NOTES + This can be used both to insert a new entry and change an existing + entry +*/ + + +my_bool multi_key_cache_set(const byte *key, uint length, + KEY_CACHE *key_cache) +{ + return safe_hash_set(&key_cache_hash, key, length, (byte*) key_cache); +} + + +void multi_key_cache_change(KEY_CACHE *old_data, + KEY_CACHE *new_data) +{ + safe_hash_change(&key_cache_hash, (byte*) old_data, (byte*) new_data); +} diff --git a/mysys/mf_loadpath.c b/mysys/mf_loadpath.c index 291ad62e297..a46b43c34d4 100644 --- a/mysys/mf_loadpath.c +++ b/mysys/mf_loadpath.c @@ -28,6 +28,7 @@ my_string my_load_path(my_string to, const char *path, const char *own_path_prefix) { char buff[FN_REFLEN]; + int is_cur; DBUG_ENTER("my_load_path"); DBUG_PRINT("enter",("path: %s prefix: %s",path, own_path_prefix ? own_path_prefix : "")); @@ -35,14 +36,16 @@ my_string my_load_path(my_string to, const char *path, if ((path[0] == FN_HOMELIB && path[1] == FN_LIBCHAR) || test_if_hard_path(path)) VOID(strmov(buff,path)); - else if ((path[0] == FN_CURLIB && path[1] == FN_LIBCHAR) || + else if ((is_cur=(path[0] == FN_CURLIB && path[1] == FN_LIBCHAR)) || (is_prefix((gptr) path,FN_PARENTDIR)) || ! own_path_prefix) { - if (! my_getwd(buff,(uint) (FN_REFLEN-strlen(path)),MYF(0))) - VOID(strcat(buff,path)); + if (is_cur) + is_cur=2; /* Remove current dir */ + if (! my_getwd(buff,(uint) (FN_REFLEN-strlen(path)+is_cur),MYF(0))) + VOID(strcat(buff,path+is_cur)); else - VOID(strmov(buff,path)); + VOID(strmov(buff,path)); /* Return org file name */ } else VOID(strxmov(buff,own_path_prefix,path,NullS)); diff --git a/mysys/mf_pack.c b/mysys/mf_pack.c index 95a1d68507e..9193238708d 100644 --- a/mysys/mf_pack.c +++ b/mysys/mf_pack.c @@ -43,7 +43,7 @@ void pack_dirname(my_string to, const char *from) (void) intern_filename(to,from); /* Change to intern name */ #ifdef FN_DEVCHAR - if ((start=strrchr(to,FN_DEVCHAR)) != 0) /* Skipp device part */ + if ((start=strrchr(to,FN_DEVCHAR)) != 0) /* Skip device part */ start++; else #endif @@ -131,7 +131,7 @@ uint cleanup_dirname(register my_string to, const char *from) from_ptr=(my_string) from; #ifdef FN_DEVCHAR if ((pos=strrchr(from_ptr,FN_DEVCHAR)) != 0) - { /* Skipp device part */ + { /* Skip device part */ length=(uint) (pos-from_ptr)+1; start=strnmov(buff,from_ptr,length); from_ptr+=length; } @@ -146,7 +146,7 @@ uint cleanup_dirname(register my_string to, const char *from) if (*pos == FN_LIBCHAR) { if ((uint) (pos-start) > length && bcmp(pos-length,parent,length) == 0) - { /* If .../../; skipp prev */ + { /* If .../../; skip prev */ pos-=length; if (pos != start) { /* not /../ */ @@ -195,7 +195,7 @@ uint cleanup_dirname(register my_string to, const char *from) pos--; /* Remove dupplicate '/' */ } else if (pos-start > 1 && pos[-1] == FN_CURLIB && pos[-2] == FN_LIBCHAR) - pos-=2; /* Skipp /./ */ + pos-=2; /* Skip /./ */ else if (pos > buff+1 && pos[-1] == FN_HOMELIB && pos[-2] == FN_LIBCHAR) { /* Found ..../~/ */ buff[0]=FN_HOMELIB; @@ -409,7 +409,7 @@ uint system_filename(my_string to, const char *from) libchar_found=0; (void) strmov(buff,from); /* If to == from */ from_pos= buff; - if ((pos=strrchr(from_pos,FN_DEVCHAR))) /* Skipp device part */ + if ((pos=strrchr(from_pos,FN_DEVCHAR))) /* Skip device part */ { pos++; to_pos=strnmov(to,from_pos,(size_s) (pos-from_pos)); @@ -419,7 +419,7 @@ uint system_filename(my_string to, const char *from) to_pos=to; if (from_pos[0] == FN_CURLIB && from_pos[1] == FN_LIBCHAR) - from_pos+=2; /* Skipp './' */ + from_pos+=2; /* Skip './' */ if (strchr(from_pos,FN_LIBCHAR)) { *(to_pos++) = FN_C_BEFORE_DIR; @@ -487,7 +487,7 @@ my_string intern_filename(my_string to, const char *from) convert_dirname(buff,from,NullS); /* change '<>' to '[]' */ from_pos=buff; - if ((pos=strrchr(from_pos,FN_DEVCHAR))) /* Skipp device part */ + if ((pos=strrchr(from_pos,FN_DEVCHAR))) /* Skip device part */ { pos++; to_pos=strnmov(to,from_pos,(size_s) (pos-from_pos)); diff --git a/mysys/mf_soundex.c b/mysys/mf_soundex.c index 9fe54ffafa0..c0c6105a6eb 100644 --- a/mysys/mf_soundex.c +++ b/mysys/mf_soundex.c @@ -38,24 +38,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 && !isalpha(*in_pntr)) + while (*in_pntr && !my_isalpha(cs,*in_pntr)) /* Skip 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 */ @@ -63,7 +64,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,23 +82,23 @@ void soundex(register my_string out_pntr, my_string in_pntr, /* If alpha, map input letter to soundex code. - If not alpha and remove_garbage is set then skipp to next char + If not alpha and remove_garbage is set then skip to next char else return 0 */ -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 extended alpha (country spec) */ + if (my_isalpha(cs,ch)) /* If extended 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..4d244aa7d74 --- /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) || defined(__NETWARE__) +#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) || defined(__NETWARE__) + 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_tempfile.c b/mysys/mf_tempfile.c index ea2bec076d4..0b337a74c19 100644 --- a/mysys/mf_tempfile.c +++ b/mysys/mf_tempfile.c @@ -181,7 +181,7 @@ File create_temp_file(char *to, const char *dir, const char *prefix, for (length=0 ; length < 8 && uniq ; length++) { - *end_pos++= _dig_vec[(int) (uniq & 31)]; + *end_pos++= _dig_vec_upper[(int) (uniq & 31)]; uniq >>= 5; } (void) strmov(end_pos,TMP_EXT); diff --git a/mysys/mf_wfile.c b/mysys/mf_wfile.c index 067e4b7acc5..7d537eaa06a 100644 --- a/mysys/mf_wfile.c +++ b/mysys/mf_wfile.c @@ -39,7 +39,7 @@ WF_PACK *wf_comp(my_string str) WF_PACK *ret; DBUG_ENTER("wf_comp"); - not_pos= -1; /* Skipp space and '!' in front */ + not_pos= -1; /* Skip space and '!' in front */ while (*str == ' ') str++; if (*str == '!') @@ -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_alloc.c b/mysys/my_alloc.c index d03ec5841af..34a03391bc4 100644 --- a/mysys/my_alloc.c +++ b/mysys/my_alloc.c @@ -25,6 +25,8 @@ void init_alloc_root(MEM_ROOT *mem_root, uint block_size, uint pre_alloc_size __attribute__((unused))) { + DBUG_ENTER("init_alloc_root"); + DBUG_PRINT("enter",("root: %lx", mem_root)); mem_root->free= mem_root->used= mem_root->pre_alloc= 0; mem_root->min_malloc= 32; mem_root->block_size= block_size-MALLOC_OVERHEAD-sizeof(USED_MEM)-8; @@ -45,6 +47,7 @@ void init_alloc_root(MEM_ROOT *mem_root, uint block_size, } } #endif + DBUG_VOID_RETURN; } /* @@ -65,7 +68,7 @@ void init_alloc_root(MEM_ROOT *mem_root, uint block_size, */ void reset_root_defaults(MEM_ROOT *mem_root, uint block_size, - uint pre_alloc_size) + uint pre_alloc_size __attribute__((unused))) { mem_root->block_size= block_size-MALLOC_OVERHEAD-sizeof(USED_MEM)-8; #if !(defined(HAVE_purify) && defined(EXTRA_DEBUG)) @@ -117,18 +120,20 @@ gptr alloc_root(MEM_ROOT *mem_root,unsigned int Size) { #if defined(HAVE_purify) && defined(EXTRA_DEBUG) reg1 USED_MEM *next; - Size+=ALIGN_SIZE(sizeof(USED_MEM)); + DBUG_ENTER("alloc_root"); + DBUG_PRINT("enter",("root: %lx", mem_root)); + Size+=ALIGN_SIZE(sizeof(USED_MEM)); if (!(next = (USED_MEM*) my_malloc(Size,MYF(MY_WME)))) { if (mem_root->error_handler) (*mem_root->error_handler)(); - return((gptr) 0); /* purecov: inspected */ + DBUG_RETURN((gptr) 0); /* purecov: inspected */ } next->next= mem_root->used; next->size= Size; mem_root->used= next; - return (gptr) (((char*) next)+ALIGN_SIZE(sizeof(USED_MEM))); + DBUG_RETURN((gptr) (((char*) next)+ALIGN_SIZE(sizeof(USED_MEM)))); #else uint get_size, block_size; gptr point; @@ -217,8 +222,9 @@ void free_root(MEM_ROOT *root, myf MyFlags) { reg1 USED_MEM *next,*old; DBUG_ENTER("free_root"); + DBUG_PRINT("enter",("root: %lx flags: %u", root, (uint) MyFlags)); - if (!root) + if (!root) /* QQ: Should be deleted */ DBUG_VOID_RETURN; /* purecov: inspected */ if (MyFlags & MY_MARK_BLOCKS_FREE) { diff --git a/mysys/my_append.c b/mysys/my_append.c index dc5ed084bb3..c3549c670c3 100644 --- a/mysys/my_append.c +++ b/mysys/my_append.c @@ -18,10 +18,10 @@ #include "mysys_priv.h" #include <sys/stat.h> #include <m_string.h> -#if defined(HAVE_SYS_UTIME_H) -#include <sys/utime.h> -#elif defined(HAVE_UTIME_H) +#if defined(HAVE_UTIME_H) #include <utime.h> +#elif defined(HAVE_SYS_UTIME_H) +#include <sys/utime.h> #elif !defined(HPUX10) struct utimbuf { time_t actime; diff --git a/mysys/my_bitmap.c b/mysys/my_bitmap.c index 8834dda98e1..3a09255b0b0 100644 --- a/mysys/my_bitmap.c +++ b/mysys/my_bitmap.c @@ -16,7 +16,18 @@ /* Handling of uchar arrays as large bitmaps. - We assume that the size of the used bitmap is less than ~(uint) 0 + + API limitations (or, rather asserted safety assumptions, + to encourage correct programming) + + * the size of the used bitmap is less than ~(uint) 0 + * it's a multiple of 8 (for efficiency reasons) + * when arguments are a bitmap and a bit number, the number + must be within bitmap size + * bitmap_set_prefix() is an exception - one can use ~0 to set all bits + * when both arguments are bitmaps, they must be of the same size + * bitmap_intersect() is an exception :) + (for for Bitmap::intersect(ulonglong map2buff)) TODO: Make assembler THREAD safe versions of these using test-and-set instructions @@ -24,60 +35,76 @@ #include "mysys_priv.h" #include <my_bitmap.h> -#include <assert.h> #include <m_string.h> -inline void bitmap_lock(MY_BITMAP* map) + +inline void bitmap_lock(MY_BITMAP *map) { #ifdef THREAD - if (map->thread_safe) - pthread_mutex_lock(&map->mutex); + if (map->mutex) + pthread_mutex_lock(map->mutex); #endif } -inline void bitmap_unlock(MY_BITMAP* map) + +inline void bitmap_unlock(MY_BITMAP *map) { #ifdef THREAD - if (map->thread_safe) - pthread_mutex_unlock(&map->mutex); + if (map->mutex) + pthread_mutex_unlock(map->mutex); #endif } -my_bool bitmap_init(MY_BITMAP *map, uint bitmap_size, my_bool thread_safe) + +my_bool bitmap_init(MY_BITMAP *map, uchar *buf, uint bitmap_size, + my_bool thread_safe) { - if (!(map->bitmap=(uchar*) my_malloc((bitmap_size+7)/8, - MYF(MY_WME | MY_ZEROFILL)))) + DBUG_ENTER("bitmap_init"); + + DBUG_ASSERT((bitmap_size & 7) == 0); + bitmap_size/=8; + if (!(map->bitmap=buf) && + !(map->bitmap= (uchar*) my_malloc(bitmap_size + + (thread_safe ? + sizeof(pthread_mutex_t) : 0), + MYF(MY_WME | MY_ZEROFILL)))) return 1; - DBUG_ASSERT(bitmap_size != ~(uint) 0); + map->bitmap_size=bitmap_size; #ifdef THREAD - if ((map->thread_safe = thread_safe)) - pthread_mutex_init(&map->mutex, MY_MUTEX_INIT_FAST); + if (thread_safe) + { + map->mutex=(pthread_mutex_t *)(map->bitmap+bitmap_size); + pthread_mutex_init(map->mutex, MY_MUTEX_INIT_FAST); + } + else + map->mutex=0; #endif - map->bitmap_size=bitmap_size; - return 0; + DBUG_RETURN(0); } + void bitmap_free(MY_BITMAP *map) { + DBUG_ENTER("bitmap_free"); if (map->bitmap) { - my_free((char*) map->bitmap, MYF(0)); - map->bitmap=0; #ifdef THREAD - if (map->thread_safe) - pthread_mutex_destroy(&map->mutex); + if (map->mutex) + pthread_mutex_destroy(map->mutex); #endif + my_free((char*) map->bitmap, MYF(0)); + map->bitmap=0; } + DBUG_VOID_RETURN; } + void bitmap_set_bit(MY_BITMAP *map, uint bitmap_bit) { - if (bitmap_bit < map->bitmap_size) - { - bitmap_lock(map); - map->bitmap[bitmap_bit / 8] |= (1 << (bitmap_bit & 7)); - bitmap_unlock(map); - } + DBUG_ASSERT(map->bitmap && bitmap_bit < map->bitmap_size*8); + bitmap_lock(map); + bitmap_fast_set_bit(map, bitmap_bit); + bitmap_unlock(map); } @@ -85,9 +112,10 @@ uint bitmap_set_next(MY_BITMAP *map) { uchar *bitmap=map->bitmap; uint bit_found = MY_BIT_NONE; - uint bitmap_size=map->bitmap_size; + uint bitmap_size=map->bitmap_size*8; uint i; + DBUG_ASSERT(map->bitmap); bitmap_lock(map); for (i=0; i < bitmap_size ; i++, bitmap++) { @@ -113,32 +141,191 @@ uint bitmap_set_next(MY_BITMAP *map) void bitmap_clear_bit(MY_BITMAP *map, uint bitmap_bit) { - if (bitmap_bit < map->bitmap_size) + DBUG_ASSERT(map->bitmap && bitmap_bit < map->bitmap_size*8); + bitmap_lock(map); + bitmap_fast_clear_bit(map, bitmap_bit); + bitmap_unlock(map); +} + + +void bitmap_set_prefix(MY_BITMAP *map, uint prefix_size) +{ + uint prefix_bytes, prefix_bits; + + DBUG_ASSERT(map->bitmap && + (prefix_size <= map->bitmap_size*8 || prefix_size == (uint) ~0)); + bitmap_lock(map); + set_if_smaller(prefix_size, map->bitmap_size*8); + if ((prefix_bytes= prefix_size / 8)) + memset(map->bitmap, 0xff, prefix_bytes); + if ((prefix_bits= prefix_size & 7)) + map->bitmap[prefix_bytes++]= (1 << prefix_bits)-1; + if (prefix_bytes < map->bitmap_size) + bzero(map->bitmap+prefix_bytes, map->bitmap_size-prefix_bytes); + bitmap_unlock(map); +} + + +void bitmap_clear_all(MY_BITMAP *map) +{ + bitmap_set_prefix(map, 0); +} + + +void bitmap_set_all(MY_BITMAP *map) +{ + bitmap_set_prefix(map, ~0); +} + + +my_bool bitmap_is_prefix(const MY_BITMAP *map, uint prefix_size) +{ + uint prefix_bits= prefix_size & 7, res= 0; + uchar *m= map->bitmap, *end_prefix= map->bitmap+prefix_size/8, + *end= map->bitmap+map->bitmap_size; + + DBUG_ASSERT(map->bitmap && prefix_size <= map->bitmap_size*8); + + bitmap_lock((MY_BITMAP *)map); + while (m < end_prefix) + if (*m++ != 0xff) + goto ret; + + if (prefix_bits && *m++ != (1 << prefix_bits)-1) + goto ret; + + while (m < end) + if (*m++ != 0) + goto ret; + + res=1; +ret: + bitmap_unlock((MY_BITMAP *)map); + return res; +} + + +my_bool bitmap_is_clear_all(const MY_BITMAP *map) +{ + return bitmap_is_prefix(map, 0); +} + +my_bool bitmap_is_set_all(const MY_BITMAP *map) +{ + return bitmap_is_prefix(map, map->bitmap_size*8); +} + + +my_bool bitmap_is_set(const MY_BITMAP *map, uint bitmap_bit) +{ + DBUG_ASSERT(map->bitmap && bitmap_bit < map->bitmap_size*8); + return bitmap_fast_is_set(map, bitmap_bit); +} + + +my_bool bitmap_is_subset(const MY_BITMAP *map1, const MY_BITMAP *map2) +{ + uint res=0; + uchar *m1=map1->bitmap, *m2=map2->bitmap, *end; + + DBUG_ASSERT(map1->bitmap && map2->bitmap && + map1->bitmap_size==map2->bitmap_size); + bitmap_lock((MY_BITMAP *)map1); + bitmap_lock((MY_BITMAP *)map2); + + end= m1+map1->bitmap_size; + + while (m1 < end) { - bitmap_lock(map); - map->bitmap[bitmap_bit / 8] &= ~ (1 << (bitmap_bit & 7)); - bitmap_unlock(map); + if ((*m1++) & ~(*m2++)) + goto ret; } + + res=1; +ret: + bitmap_unlock((MY_BITMAP *)map2); + bitmap_unlock((MY_BITMAP *)map1); + return res; +} + + +my_bool bitmap_cmp(const MY_BITMAP *map1, const MY_BITMAP *map2) +{ + uint res; + + DBUG_ASSERT(map1->bitmap && map2->bitmap && + map1->bitmap_size==map2->bitmap_size); + bitmap_lock((MY_BITMAP *)map1); + bitmap_lock((MY_BITMAP *)map2); + + res= memcmp(map1->bitmap, map2->bitmap, map1->bitmap_size)==0; + + bitmap_unlock((MY_BITMAP *)map2); + bitmap_unlock((MY_BITMAP *)map1); + return res; } -void bitmap_set_all(MY_BITMAP* map) +void bitmap_intersect(MY_BITMAP *map, const MY_BITMAP *map2) { + uchar *to=map->bitmap, *from=map2->bitmap, *end; + uint len=map->bitmap_size, len2=map2->bitmap_size; + + DBUG_ASSERT(map->bitmap && map2->bitmap); bitmap_lock(map); - memset(map->bitmap, 0xff, (map->bitmap_size+7)/8); + bitmap_lock((MY_BITMAP *)map2); + + end= to+min(len,len2); + + while (to < end) + *to++ &= *from++; + + if (len2 < len) + { + end+=len-len2; + while (to < end) + *to++=0; + } + + bitmap_unlock((MY_BITMAP *)map2); bitmap_unlock(map); } -my_bool bitmap_is_set(MY_BITMAP* map, uint bitmap_bit) + +void bitmap_subtract(MY_BITMAP *map, const MY_BITMAP *map2) { - return (bitmap_bit < map->bitmap_size) ? - (map->bitmap[bitmap_bit / 8] & (1 << (bitmap_bit & 7))) : - 0; + uchar *to=map->bitmap, *from=map2->bitmap, *end; + + DBUG_ASSERT(map->bitmap && map2->bitmap && + map->bitmap_size==map2->bitmap_size); + bitmap_lock(map); + bitmap_lock((MY_BITMAP *)map2); + + end= to+map->bitmap_size; + + while (to < end) + *to++ &= ~(*from++); + + bitmap_unlock((MY_BITMAP *)map2); + bitmap_unlock(map); } -void bitmap_clear_all(MY_BITMAP* map) + +void bitmap_union(MY_BITMAP *map, const MY_BITMAP *map2) { + uchar *to=map->bitmap, *from=map2->bitmap, *end; + + DBUG_ASSERT(map->bitmap && map2->bitmap && + map->bitmap_size==map2->bitmap_size); bitmap_lock(map); - bzero(map->bitmap,(map->bitmap_size+7)/8); + bitmap_lock((MY_BITMAP *)map2); + + end= to+map->bitmap_size; + + while (to < end) + *to++ |= *from++; + + bitmap_unlock((MY_BITMAP *)map2); bitmap_unlock(map); } + diff --git a/mysys/my_chsize.c b/mysys/my_chsize.c index 653ea569172..cf26428d65f 100644 --- a/mysys/my_chsize.c +++ b/mysys/my_chsize.c @@ -89,7 +89,7 @@ int my_chsize(File fd, my_off_t newlength, int filler, myf MyFlags) We should never come here on any modern machine */ VOID(my_seek(fd, newlength, MY_SEEK_SET, MYF(MY_WME+MY_FAE))); - swap(my_off_t, newlength, oldsize); + swap_variables(my_off_t, newlength, oldsize); } #endif diff --git a/mysys/my_compress.c b/mysys/my_compress.c index dd076311188..0e37d2fef9b 100644 --- a/mysys/my_compress.c +++ b/mysys/my_compress.c @@ -68,7 +68,7 @@ byte *my_compress_alloc(const byte *packet, ulong *len, ulong *complen) DBUG_PRINT("note",("Packet got longer on compression; Not compressed")); return 0; } - swap(ulong, *len, *complen); /* *len is now packet length */ + swap_variables(ulong, *len, *complen); /* *len is now packet length */ return compbuf; } diff --git a/mysys/my_copy.c b/mysys/my_copy.c index 84eda781a09..03f3feb54d3 100644 --- a/mysys/my_copy.c +++ b/mysys/my_copy.c @@ -18,10 +18,10 @@ #include "mysys_priv.h" #include <sys/stat.h> #include <m_string.h> -#if defined(HAVE_SYS_UTIME_H) -#include <sys/utime.h> -#elif defined(HAVE_UTIME_H) +#if defined(HAVE_UTIME_H) #include <utime.h> +#elif defined(HAVE_SYS_UTIME_H) +#include <sys/utime.h> #elif !defined(HPUX10) #include <time.h> struct utimbuf { diff --git a/mysys/my_crc32.c b/mysys/my_crc32.c new file mode 100644 index 00000000000..5514b01ede2 --- /dev/null +++ b/mysys/my_crc32.c @@ -0,0 +1,36 @@ +/* Copyright (C) 2003 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" + +#ifndef HAVE_COMPRESS + +/* minimal set of defines for using crc32() from zlib codebase */ +#define _ZLIB_H +#define ZEXPORT +#define Z_NULL 0 +#define OF(args) args +#undef DYNAMIC_CRC_TABLE +typedef uchar Byte; +typedef uchar Bytef; +typedef uint uInt; +typedef ulong uLong; +typedef ulong uLongf; + +#include "../zlib/crc32.c" + +#endif + diff --git a/mysys/my_div.c b/mysys/my_div.c index 777ffe403d7..9141ff4fcc5 100644 --- a/mysys/my_div.c +++ b/mysys/my_div.c @@ -27,7 +27,7 @@ my_string my_filename(File fd) { DBUG_ENTER("my_filename"); - if (fd >= MY_NFILE) + if ((uint) fd >= (uint) my_file_limit) DBUG_RETURN((char*) "UNKNOWN"); if (fd >= 0 && my_file_info[fd].type != UNOPEN) { diff --git a/mysys/my_dup.c b/mysys/my_dup.c index df298780e3e..4b7434e29ea 100644 --- a/mysys/my_dup.c +++ b/mysys/my_dup.c @@ -32,7 +32,7 @@ File my_dup(File file, myf MyFlags) DBUG_ENTER("my_dup"); DBUG_PRINT("my",("file: %d MyFlags: %d", MyFlags)); fd = dup(file); - filename= (((int) file < MY_NFILE) ? + filename= (((uint) file < my_file_limit) ? my_file_info[(int) file].name : "Unknown"); DBUG_RETURN(my_register_filename(fd, filename, FILE_BY_DUP, EE_FILENOTFOUND, MyFlags)); diff --git a/mysys/my_error.c b/mysys/my_error.c index 5ce061212b5..8a377f63c7e 100644 --- a/mysys/my_error.c +++ b/mysys/my_error.c @@ -33,6 +33,12 @@ char NEAR errbuff[NRERRBUFFS][ERRMSGSIZE]; nr Errno MyFlags Flags ... variable list + NOTE + The following subset of printf format is supported: + "%[0-9.-]*l?[sdu]", where all length flags are parsed but ignored. + + Additionally "%.*s" is supported and "%.*[ud]" is correctly parsed but + the length value is ignored. */ int my_error(int nr,myf MyFlags, ...) @@ -43,7 +49,10 @@ int my_error(int nr,myf MyFlags, ...) reg2 char *endpos; char * par; char ebuff[ERRMSGSIZE+20]; + int prec_chars; /* output precision */ + my_bool prec_supplied; DBUG_ENTER("my_error"); + LINT_INIT(prec_chars); /* protected by prec_supplied */ va_start(ap,MyFlags); DBUG_PRINT("my", ("nr: %d MyFlags: %d errno: %d", nr, MyFlags, errno)); @@ -59,7 +68,6 @@ int my_error(int nr,myf MyFlags, ...) if (tpos[0] != '%') { *endpos++= *tpos++; /* Copy ordinary char */ - olen++; continue; } if (*++tpos == '%') /* test if %% */ @@ -68,43 +76,71 @@ int my_error(int nr,myf MyFlags, ...) } else { - /* Skipp if max size is used (to be compatible with printf) */ - while (isdigit(*tpos) || *tpos == '.' || *tpos == '-') - tpos++; - if (*tpos == 'l') /* Skipp 'l' argument */ - tpos++; + /* + Skip size/precision flags to be compatible with printf. + The only size/precision flag supported is "%.*s". + If "%.*u" or "%.*d" are encountered, the precision number is read + from the variable argument list but its value is ignored. + */ + prec_supplied= 0; + if (*tpos== '.') + { + tpos++; + olen--; + if (*tpos == '*') + { + tpos++; + olen--; + prec_chars= va_arg(ap, int); /* get length parameter */ + prec_supplied= 1; + } + } + + if (!prec_supplied) + { + while (my_isdigit(&my_charset_latin1, *tpos) || *tpos == '.' || + *tpos == '-') + tpos++; + + if (*tpos == 'l') /* Skip 'l' argument */ + tpos++; + } + if (*tpos == 's') /* String parameter */ { - par = va_arg(ap, char *); - plen = (uint) strlen(par); + par= va_arg(ap, char *); + plen= (uint) strlen(par); + if (prec_supplied && prec_chars > 0) + plen= min((uint)prec_chars, plen); if (olen + plen < ERRMSGSIZE+2) /* Replace if possible */ { - endpos=strmov(endpos,par); - tpos++; - olen+=plen-2; - continue; + strmake(endpos, par, plen); + endpos+= plen; + tpos++; + olen+= plen-2; + continue; } } else if (*tpos == 'd' || *tpos == 'u') /* Integer parameter */ { register int iarg; - iarg = va_arg(ap, int); + iarg= va_arg(ap, int); if (*tpos == 'd') - plen= (uint) (int2str((long) iarg,endpos, -10) - endpos); + plen= (uint) (int10_to_str((long) iarg, endpos, -10) - endpos); else - plen= (uint) (int2str((long) (uint) iarg,endpos,10)- endpos); + plen= (uint) (int10_to_str((long) (uint) iarg, endpos, 10) - endpos); if (olen + plen < ERRMSGSIZE+2) /* Replace parameter if possible */ { - endpos+=plen; + endpos+= plen; tpos++; - olen+=plen-2; + olen+= plen-2; continue; } } } - *endpos++='%'; /* % used as % or unknown code */ + *endpos++= '%'; /* % used as % or unknown code */ } - *endpos='\0'; /* End of errmessage */ + *endpos= '\0'; /* End of errmessage */ va_end(ap); DBUG_RETURN((*error_handler_hook)(nr, ebuff, MyFlags)); } @@ -120,7 +156,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_file.c b/mysys/my_file.c new file mode 100644 index 00000000000..6a9d39cf944 --- /dev/null +++ b/mysys/my_file.c @@ -0,0 +1,147 @@ +/* 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 "my_static.h" +#include <m_string.h> + +/* + set how many open files we want to be able to handle + + SYNOPSIS + set_maximum_open_files() + max_file_limit Files to open + + NOTES + The request may not fulfilled becasue of system limitations + + RETURN + Files available to open. + May be more or less than max_file_limit! +*/ + +#if defined(HAVE_GETRLIMIT) && defined(RLIMIT_NOFILE) && !defined(HAVE_mit_thread) + +#ifndef RLIM_INFINITY +#define RLIM_INFINITY ((uint) 0xffffffff) +#endif + +static uint set_max_open_files(uint max_file_limit) +{ + struct rlimit rlimit; + uint old_cur; + DBUG_ENTER("set_max_open_files"); + DBUG_PRINT("enter",("files: %u", max_file_limit)); + + if (!getrlimit(RLIMIT_NOFILE,&rlimit)) + { + old_cur= (uint) rlimit.rlim_cur; + DBUG_PRINT("info", ("rlim_cur: %u rlim_max: %u", + (uint) rlimit.rlim_cur, + (uint) rlimit.rlim_max)); + if (rlimit.rlim_cur == RLIM_INFINITY) + rlimit.rlim_cur = max_file_limit; + if (rlimit.rlim_cur >= max_file_limit) + DBUG_RETURN(rlimit.rlim_cur); /* purecov: inspected */ + rlimit.rlim_cur= rlimit.rlim_max= max_file_limit; + if (setrlimit(RLIMIT_NOFILE, &rlimit)) + max_file_limit= old_cur; /* Use original value */ + else + { + rlimit.rlim_cur= 0; /* Safety if next call fails */ + (void) getrlimit(RLIMIT_NOFILE,&rlimit); + DBUG_PRINT("info", ("rlim_cur: %u", (uint) rlimit.rlim_cur)); + if (rlimit.rlim_cur) /* If call didn't fail */ + max_file_limit= (uint) rlimit.rlim_cur; + } + } + DBUG_PRINT("exit",("max_file_limit: %u", max_file_limit)); + DBUG_RETURN(max_file_limit); +} + +#elif defined (OS2) + +static uint set_max_open_files(uint max_file_limit) +{ + LONG cbReqCount; + ULONG cbCurMaxFH0; + APIRET ulrc; + DBUG_ENTER("set_max_open_files"); + + /* get current limit */ + cbReqCount = 0; + DosSetRelMaxFH( &cbReqCount, &cbCurMaxFH0); + + /* set new limit */ + if ((cbReqCount = max_file_limit - cbCurMaxFH0) > 0) + ulrc = DosSetRelMaxFH( &cbReqCount, &cbCurMaxFH); + DBUG_RETURN(cbCurMaxFH0); +} + +#else +static int set_max_open_files(uint max_file_limit) +{ + /* We don't know the limit. Return best guess */ + return min(max_file_limit, OS_FILE_LIMIT); +} +#endif + + +/* + Change number of open files + + SYNOPSIS: + my_set_max_open_files() + files Number of requested files + + RETURN + number of files available for open +*/ + +uint my_set_max_open_files(uint files) +{ + struct st_my_file_info *tmp; + DBUG_ENTER("my_set_max_open_files"); + DBUG_PRINT("enter",("files: %u my_file_limit: %u", files, my_file_limit)); + + files= set_max_open_files(min(files, OS_FILE_LIMIT)); + if (files <= MY_NFILE) + DBUG_RETURN(files); + + if (!(tmp= (struct st_my_file_info*) my_malloc(sizeof(*tmp) * files, + MYF(MY_WME)))) + DBUG_RETURN(MY_NFILE); + + /* Copy any initialized files */ + memcpy((char*) tmp, (char*) my_file_info, sizeof(*tmp) * my_file_limit); + my_free_open_file_info(); /* Free if already allocated */ + my_file_info= tmp; + my_file_limit= files; + DBUG_PRINT("exit",("files: %u", files)); + DBUG_RETURN(files); +} + + +void my_free_open_file_info() +{ + DBUG_ENTER("my_free_file_info"); + if (my_file_info != my_file_info_default) + { + my_free((char*) my_file_info, MYF(0)); + my_file_info= my_file_info_default; + } + DBUG_VOID_RETURN; +} diff --git a/mysys/my_fopen.c b/mysys/my_fopen.c index d3b0b90f9c5..8906a288b11 100644 --- a/mysys/my_fopen.c +++ b/mysys/my_fopen.c @@ -42,7 +42,7 @@ FILE *my_fopen(const char *FileName, int Flags, myf MyFlags) on some OS (SUNOS). Actually the filename save isn't that important so we can ignore if this doesn't work. */ - if ((uint) fileno(fd) >= MY_NFILE) + if ((uint) fileno(fd) >= my_file_limit) { thread_safe_increment(my_stream_opened,&THR_LOCK_open); DBUG_RETURN(fd); /* safeguard */ @@ -91,7 +91,7 @@ int my_fclose(FILE *fd, myf MyFlags) } else my_stream_opened--; - if ((uint) file < MY_NFILE && my_file_info[file].type != UNOPEN) + if ((uint) file < my_file_limit && my_file_info[file].type != UNOPEN) { my_file_info[file].type = UNOPEN; my_free(my_file_info[file].name, MYF(MY_ALLOW_ZERO_PTR)); @@ -123,11 +123,11 @@ FILE *my_fdopen(File Filedes, const char *name, int Flags, myf MyFlags) { pthread_mutex_lock(&THR_LOCK_open); my_stream_opened++; - if (Filedes < MY_NFILE) + if ((uint) Filedes < (uint) my_file_limit) { if (my_file_info[Filedes].type != UNOPEN) { - my_file_opened--; /* File is opened with my_open ! */ + my_file_opened--; /* File is opened with my_open ! */ } else { diff --git a/mysys/my_gethostbyname.c b/mysys/my_gethostbyname.c index 5044a505054..27281f3489d 100644 --- a/mysys/my_gethostbyname.c +++ b/mysys/my_gethostbyname.c @@ -18,7 +18,6 @@ /* Thread safe version of gethostbyname_r() */ #include "mysys_priv.h" -#include <assert.h> #if !defined(MSDOS) && !defined(__WIN__) #include <netdb.h> #endif diff --git a/mysys/my_gethwaddr.c b/mysys/my_gethwaddr.c new file mode 100644 index 00000000000..72f1cb975c4 --- /dev/null +++ b/mysys/my_gethwaddr.c @@ -0,0 +1,130 @@ +/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +/* get hardware address for an interface */ +/* if there are many available, any non-zero one can be used */ + +#include "mysys_priv.h" +#include <m_string.h> + +#ifndef MAIN +static my_bool memcpy_and_test(uchar *to, uchar *from, uint len) +{ + uint i, res=1; + + for (i=0; i < len; i++) + if ((*to++= *from++)) + res=0; + return res; +} + +#ifdef __FreeBSD__ + +#include <net/ethernet.h> +#include <sys/sysctl.h> +#include <net/route.h> +#include <net/if.h> +#include <net/if_dl.h> + +my_bool my_gethwaddr(uchar *to) +{ + size_t len; + uchar *buf, *next, *end, *addr; + struct if_msghdr *ifm; + struct sockaddr_dl *sdl; + int i, res=1, mib[6]={CTL_NET, AF_ROUTE, 0, AF_LINK, NET_RT_IFLIST, 0}; + + if (sysctl(mib, 6, NULL, &len, NULL, 0) == -1) + goto err; + if (!(buf = alloca(len))) + goto err; + if (sysctl(mib, 6, buf, &len, NULL, 0) < 0) + goto err; + + end = buf + len; + + for (next = buf ; res && next < end ; next += ifm->ifm_msglen) + { + ifm = (struct if_msghdr *)next; + if (ifm->ifm_type == RTM_IFINFO) + { + sdl = (struct sockaddr_dl *)(ifm + 1); + addr=LLADDR(sdl); + res=memcpy_and_test(to, addr, ETHER_ADDR_LEN); + } + } + +err: + return res; +} + +#elif __linux__ + +#include <net/if.h> +#include <sys/ioctl.h> +#include <net/ethernet.h> + +my_bool my_gethwaddr(uchar *to) +{ + int fd, res=1; + struct ifreq ifr; + + fd = socket(AF_INET, SOCK_DGRAM, 0); + if (fd < 0) + goto err; + + bzero(&ifr, sizeof(ifr)); + strnmov(ifr.ifr_name, "eth0", sizeof(ifr.ifr_name) - 1); + + do { + if (ioctl(fd, SIOCGIFHWADDR, &ifr) >= 0) + res=memcpy_and_test(to, (uchar *)&ifr.ifr_hwaddr.sa_data, ETHER_ADDR_LEN); + } while (res && (errno == 0 || errno == ENODEV) && ifr.ifr_name[3]++ < '6'); + + close(fd); +err: + return res; +} + +#else +/* just fail */ +my_bool my_gethwaddr(uchar *to __attribute__((unused))) +{ + return 1; +} +#endif + +#else MAIN +int main(int argc __attribute__((unused)),char **argv) +{ + uchar mac[6]; + uint i; + MY_INIT(argv[0]); + if (my_gethwaddr(mac)) + { + printf("my_gethwaddr failed with errno %d\n", errno); + exit(1); + } + for (i=0; i < sizeof(mac); i++) + { + if (i) printf(":"); + printf("%02x", mac[i]); + } + printf("\n"); + return 0; +} +#endif + diff --git a/mysys/my_getopt.c b/mysys/my_getopt.c index 694c4685667..d7a9babe5e7 100644 --- a/mysys/my_getopt.c +++ b/mysys/my_getopt.c @@ -18,7 +18,6 @@ #include <m_string.h> #include <stdlib.h> #include <my_getopt.h> -#include <assert.h> #include <my_sys.h> #include <mysys_err.h> @@ -32,8 +31,9 @@ static longlong getopt_ll(char *arg, const struct my_option *optp, int *err); static ulonglong getopt_ull(char *arg, const struct my_option *optp, int *err); static void init_variables(const struct my_option *options); -static int setval(const struct my_option *opts,char *argument, +static int setval(const struct my_option *opts, gptr *value, char *argument, my_bool set_maximum_value); +static char *check_struct_option(char *cur_arg, char *key_name); /* The following three variables belong to same group and the number and @@ -67,6 +67,14 @@ my_bool my_getopt_print_errors= 1; one. Call function 'get_one_option()' once for each option. */ +static gptr* (*getopt_get_addr)(const char *, uint, const struct my_option *); + +void my_getopt_register_get_addr(gptr* (*func_addr)(const char *, uint, + const struct my_option *)) +{ + getopt_get_addr= func_addr; +} + int handle_options(int *argc, char ***argv, const struct my_option *longopts, my_bool (*get_one_option)(int, @@ -74,10 +82,12 @@ int handle_options(int *argc, char ***argv, char *)) { uint opt_found, argvpos= 0, length, i; - my_bool end_of_options= 0, must_be_var, set_maximum_value, special_used, + my_bool end_of_options= 0, must_be_var, set_maximum_value, option_is_loose; - char *progname= *(*argv), **pos, **pos_end, *optend, *prev_found; + char **pos, **pos_end, *optend, *prev_found, + *opt_str, key_name[FN_REFLEN]; const struct my_option *optp; + gptr *value; int error; LINT_INIT(opt_found); @@ -93,7 +103,6 @@ int handle_options(int *argc, char ***argv, char *argument= 0; must_be_var= 0; set_maximum_value= 0; - special_used= 0; option_is_loose= 0; cur_arg++; /* skip '-' */ @@ -110,7 +119,7 @@ int handle_options(int *argc, char ***argv, { if (my_getopt_print_errors) fprintf(stderr, "%s: Option '-O' requires an argument\n", - progname); + my_progname); return EXIT_ARGUMENT_REQUIRED; } cur_arg= *pos; @@ -128,7 +137,7 @@ int handle_options(int *argc, char ***argv, if (my_getopt_print_errors) fprintf(stderr, "%s: Option '--set-variable' requires an argument\n", - progname); + my_progname); return EXIT_ARGUMENT_REQUIRED; } } @@ -142,7 +151,7 @@ int handle_options(int *argc, char ***argv, if (my_getopt_print_errors) fprintf(stderr, "%s: Option '--set-variable' requires an argument\n", - progname); + my_progname); return EXIT_ARGUMENT_REQUIRED; } cur_arg= *pos; @@ -159,19 +168,20 @@ int handle_options(int *argc, char ***argv, continue; } } - optend= strcend(cur_arg, '='); - length= optend - cur_arg; + opt_str= check_struct_option(cur_arg, key_name); + optend= strcend(opt_str, '='); + length= optend - opt_str; if (*optend == '=') optend++; else - optend=0; + optend= 0; /* Find first the right option. Return error in case of an ambiguous, or unknown option */ optp= longopts; - if (!(opt_found= findopt(cur_arg, length, &optp, &prev_found))) + if (!(opt_found= findopt(opt_str, length, &optp, &prev_found))) { /* Didn't find any matching option. Let's see if someone called @@ -183,18 +193,17 @@ int handle_options(int *argc, char ***argv, must_be_var= 1; /* option is followed by an argument */ for (i= 0; special_opt_prefix[i]; i++) { - if (!getopt_compare_strings(special_opt_prefix[i], cur_arg, + if (!getopt_compare_strings(special_opt_prefix[i], opt_str, special_opt_prefix_lengths[i]) && - cur_arg[special_opt_prefix_lengths[i]] == '-') + opt_str[special_opt_prefix_lengths[i]] == '-') { /* We were called with a special prefix, we can reuse opt_found */ - special_used= 1; - cur_arg+= (special_opt_prefix_lengths[i] + 1); + opt_str+= (special_opt_prefix_lengths[i] + 1); if (i == OPT_LOOSE) option_is_loose= 1; - if ((opt_found= findopt(cur_arg, length - + if ((opt_found= findopt(opt_str, length - (special_opt_prefix_lengths[i] + 1), &optp, &prev_found))) { @@ -203,7 +212,7 @@ int handle_options(int *argc, char ***argv, if (my_getopt_print_errors) fprintf(stderr, "%s: ambiguous option '--%s-%s' (--%s-%s)\n", - progname, special_opt_prefix[i], cur_arg, + my_progname, special_opt_prefix[i], opt_str, special_opt_prefix[i], prev_found); return EXIT_AMBIGUOUS_OPTION; } @@ -237,8 +246,8 @@ int handle_options(int *argc, char ***argv, { if (my_getopt_print_errors) fprintf(stderr, - "%s: %s: unknown variable '%s'\n", progname, - option_is_loose ? "WARNING" : "ERROR", cur_arg); + "%s: %s: unknown variable '%s'\n", my_progname, + option_is_loose ? "WARNING" : "ERROR", opt_str); if (!option_is_loose) return EXIT_UNKNOWN_VARIABLE; } @@ -246,8 +255,8 @@ int handle_options(int *argc, char ***argv, { if (my_getopt_print_errors) fprintf(stderr, - "%s: %s: unknown option '--%s'\n", progname, - option_is_loose ? "WARNING" : "ERROR", cur_arg); + "%s: %s: unknown option '--%s'\n", my_progname, + option_is_loose ? "WARNING" : "ERROR", opt_str); if (!option_is_loose) return EXIT_UNKNOWN_OPTION; } @@ -264,34 +273,50 @@ int handle_options(int *argc, char ***argv, { if (my_getopt_print_errors) fprintf(stderr, "%s: variable prefix '%s' is not unique\n", - progname, cur_arg); + my_progname, opt_str); return EXIT_VAR_PREFIX_NOT_UNIQUE; } else { if (my_getopt_print_errors) fprintf(stderr, "%s: ambiguous option '--%s' (%s, %s)\n", - progname, cur_arg, prev_found, optp->name); + my_progname, opt_str, prev_found, optp->name); return EXIT_AMBIGUOUS_OPTION; } } - if (must_be_var && optp->var_type == GET_NO_ARG) + if ((optp->var_type & GET_TYPE_MASK) == GET_DISABLED) + { + if (my_getopt_print_errors) + fprintf(stderr, + "%s: %s: Option '%s' used, but is disabled\n", my_progname, + option_is_loose ? "WARNING" : "ERROR", opt_str); + if (option_is_loose) + { + (*argc)--; + continue; + } + return EXIT_OPTION_DISABLED; + } + if (must_be_var && (optp->var_type & GET_TYPE_MASK) == GET_NO_ARG) { if (my_getopt_print_errors) fprintf(stderr, "%s: option '%s' cannot take an argument\n", - progname, optp->name); + my_progname, optp->name); return EXIT_NO_ARGUMENT_ALLOWED; } + value= optp->var_type & GET_ASK_ADDR ? + (*getopt_get_addr)(key_name, strlen(key_name), optp) : optp->value; + if (optp->arg_type == NO_ARG) { - if (optend && optp->var_type != GET_BOOL) + if (optend && (optp->var_type & GET_TYPE_MASK) != GET_BOOL) { if (my_getopt_print_errors) fprintf(stderr, "%s: option '--%s' cannot take an argument\n", - progname, optp->name); + my_progname, optp->name); return EXIT_NO_ARGUMENT_ALLOWED; } - if (optp->var_type == GET_BOOL) + if ((optp->var_type & GET_TYPE_MASK) == GET_BOOL) { /* Set bool to 1 if no argument or if the user has used @@ -299,7 +324,7 @@ int handle_options(int *argc, char ***argv, *optend was set to '0' if one used --disable-option */ my_bool tmp= (my_bool) (!optend || *optend == '1'); - *((my_bool*) optp->value)= tmp; + *((my_bool*) value)= tmp; (*argc)--; get_one_option(optp->id, optp, tmp ? (char*) "1" : disabled_my_option); @@ -307,14 +332,15 @@ int handle_options(int *argc, char ***argv, } argument= optend; } - else if (optp->arg_type == OPT_ARG && optp->var_type == GET_BOOL) + else if (optp->arg_type == OPT_ARG && + (optp->var_type & GET_TYPE_MASK) == GET_BOOL) { if (optend == disabled_my_option) - *((my_bool*) optp->value)= (my_bool) 0; + *((my_bool*) value)= (my_bool) 0; else { if (!optend) /* No argument -> enable option */ - *((my_bool*) optp->value)= (my_bool) 1; + *((my_bool*) value)= (my_bool) 1; else argument= optend; } @@ -326,7 +352,7 @@ int handle_options(int *argc, char ***argv, { if (my_getopt_print_errors) fprintf(stderr, "%s: option '--%s' requires an argument\n", - progname, optp->name); + my_progname, optp->name); return EXIT_ARGUMENT_REQUIRED; } argument= *pos; @@ -346,7 +372,16 @@ int handle_options(int *argc, char ***argv, { /* Option recognized. Find next what to do with it */ opt_found= 1; - if (optp->var_type == GET_BOOL && optp->arg_type == NO_ARG) + if ((optp->var_type & GET_TYPE_MASK) == GET_DISABLED) + { + if (my_getopt_print_errors) + fprintf(stderr, + "%s: ERROR: Option '-%c' used, but is disabled\n", + my_progname, optp->id); + return EXIT_OPTION_DISABLED; + } + if ((optp->var_type & GET_TYPE_MASK) == GET_BOOL && + optp->arg_type == NO_ARG) { *((my_bool*) optp->value)= (my_bool) 1; get_one_option(optp->id, optp, argument); @@ -377,7 +412,7 @@ int handle_options(int *argc, char ***argv, if (my_getopt_print_errors) fprintf(stderr, "%s: option '-%c' requires an argument\n", - progname, optp->id); + my_progname, optp->id); return EXIT_ARGUMENT_REQUIRED; } argument= *++pos; @@ -385,11 +420,12 @@ int handle_options(int *argc, char ***argv, /* the other loop will break, because *optend + 1 == 0 */ } } - if ((error= setval(optp, argument, set_maximum_value))) + if ((error= setval(optp, optp->value, argument, + set_maximum_value))) { fprintf(stderr, "%s: Error while setting value '%s' to '%s'\n", - progname, argument, optp->name); + my_progname, argument, optp->name); return error; } get_one_option(optp->id, optp, argument); @@ -400,18 +436,18 @@ int handle_options(int *argc, char ***argv, { if (my_getopt_print_errors) fprintf(stderr, - "%s: unknown option '-%c'\n", progname, *optend); + "%s: unknown option '-%c'\n", my_progname, *optend); return EXIT_UNKNOWN_OPTION; } } (*argc)--; /* option handled (short), decrease argument count */ continue; } - if ((error= setval(optp, argument, set_maximum_value))) + if ((error= setval(optp, value, argument, set_maximum_value))) { fprintf(stderr, "%s: Error while setting value '%s' to '%s'\n", - progname, argument, optp->name); + my_progname, argument, optp->name); return error; } get_one_option(optp->id, optp, argument); @@ -431,6 +467,49 @@ int handle_options(int *argc, char ***argv, return 0; } + +/* + function: check_struct_option + + Arguments: Current argument under processing from argv and a variable + where to store the possible key name. + + Return value: In case option is a struct option, returns a pointer to + the current argument at the position where the struct option (key_name) + ends, the next character after the dot. In case argument is not a struct + option, returns a pointer to the argument. + + key_name will hold the name of the key, or 0 if not found. +*/ + +static char *check_struct_option(char *cur_arg, char *key_name) +{ + char *ptr, *end; + + ptr= strcend(cur_arg + 1, '.'); /* Skip the first character */ + end= strcend(cur_arg, '='); + + /* + If the first dot is after an equal sign, then it is part + of a variable value and the option is not a struct option. + Also, if the last character in the string before the ending + NULL, or the character right before equal sign is the first + dot found, the option is not a struct option. + */ + if (end - ptr > 1) + { + uint len= ptr - cur_arg; + set_if_smaller(len, FN_REFLEN-1); + strmake(key_name, cur_arg, len); + return ++ptr; + } + else + { + key_name[0]= 0; + return cur_arg; + } +} + /* function: setval @@ -438,20 +517,20 @@ int handle_options(int *argc, char ***argv, Will set the option value to given value */ -static int setval(const struct my_option *opts, char *argument, +static int setval(const struct my_option *opts, gptr *value, char *argument, my_bool set_maximum_value) { int err= 0; - if (opts->value && argument) + if (value && argument) { gptr *result_pos= ((set_maximum_value) ? - opts->u_max_value : opts->value); + opts->u_max_value : value); if (!result_pos) return EXIT_NO_PTR_TO_VARIABLE; - switch (opts->var_type) { + switch ((opts->var_type & GET_TYPE_MASK)) { case GET_BOOL: /* If argument differs from 0, enable option, else disable */ *((my_bool*) result_pos)= (my_bool) atoi(argument) != 0; break; @@ -503,7 +582,7 @@ static int findopt(char *optpat, uint length, const struct my_option **opt_res, char **ffname) { - int count; + uint count; struct my_option *opt= (struct my_option *) *opt_res; for (count= 0; opt->name; opt++) @@ -515,7 +594,8 @@ static int findopt(char *optpat, uint length, *ffname= (char *) opt->name; /* We only need to know one prev */ if (!opt->name[length]) /* Exact match */ return 1; - count++; + if (!count || strcmp(*ffname, opt->name)) /* Don't count synonyms */ + count++; } } return count; @@ -628,58 +708,77 @@ ulonglong getopt_ull_limit_value(ulonglong num, const struct my_option *optp) return num; } -/* - function: init_variables +/* + Init one value to it's default values + + SYNOPSIS + init_one_value() + option Option to initialize + value Pointer to variable +*/ + +static void init_one_value(const struct my_option *option, gptr *variable, + longlong value) +{ + switch ((option->var_type & GET_TYPE_MASK)) { + case GET_BOOL: + *((my_bool*) variable)= (my_bool) value; + break; + case GET_INT: + *((int*) variable)= (int) value; + break; + case GET_UINT: + *((uint*) variable)= (uint) value; + break; + case GET_LONG: + *((long*) variable)= (long) value; + break; + case GET_ULONG: + *((ulong*) variable)= (ulong) value; + break; + case GET_LL: + *((longlong*) variable)= (longlong) value; + break; + case GET_ULL: + *((ulonglong*) variable)= (ulonglong) value; + break; + default: /* dummy default to avoid compiler warnings */ + break; + } +} + + +/* initialize all variables to their default values + + SYNOPSIS + init_variables() + options Array of options + + NOTES + We will initialize the value that is pointed to by options->value. + If the value is of type GET_ASK_ADDR, we will also ask for the address + for a value and initialize. */ static void init_variables(const struct my_option *options) { for (; options->name; options++) { + gptr *variable; + /* + We must set u_max_value first as for some variables + options->u_max_value == options->value and in this case we want to + set the value to default value. + */ + if (options->u_max_value) + init_one_value(options, options->u_max_value, options->max_value); if (options->value) - { - switch (options->var_type) { - case GET_BOOL: - if (options->u_max_value) - *((my_bool*) options->u_max_value)= (my_bool) options->max_value; - *((my_bool*) options->value)= (my_bool) options->def_value; - break; - case GET_INT: - if (options->u_max_value) - *((int*) options->u_max_value)= (int) options->max_value; - *((int*) options->value)= (int) options->def_value; - break; - case GET_UINT: - if (options->u_max_value) - *((uint*) options->u_max_value)= (uint) options->max_value; - *((uint*) options->value)= (uint) options->def_value; - break; - case GET_LONG: - if (options->u_max_value) - *((long*) options->u_max_value)= (long) options->max_value; - *((long*) options->value)= (long) options->def_value; - break; - case GET_ULONG: - if (options->u_max_value) - *((ulong*) options->u_max_value)= (ulong) options->max_value; - *((ulong*) options->value)= (ulong) options->def_value; - break; - case GET_LL: - if (options->u_max_value) - *((longlong*) options->u_max_value)= (longlong) options->max_value; - *((longlong*) options->value)= (longlong) options->def_value; - break; - case GET_ULL: - if (options->u_max_value) - *((ulonglong*) options->u_max_value)= (ulonglong) options->max_value; - *((ulonglong*) options->value)= (ulonglong) options->def_value; - break; - default: /* dummy default to avoid compiler warnings */ - break; - } - } + init_one_value(options, options->value, options->def_value); + if (options->var_type & GET_ASK_ADDR && + (variable= (*getopt_get_addr)("", 0, options))) + init_one_value(options, variable, options->def_value); } } @@ -714,13 +813,15 @@ void my_print_help(const struct my_option *options) { printf("--%s", optp->name); col+= 2 + strlen(optp->name); - if (optp->var_type == GET_STR || optp->var_type == GET_STR_ALLOC) + if ((optp->var_type & GET_TYPE_MASK) == GET_STR || + (optp->var_type & GET_TYPE_MASK) == GET_STR_ALLOC) { printf("%s=name%s ", optp->arg_type == OPT_ARG ? "[" : "", optp->arg_type == OPT_ARG ? "]" : ""); col+= (optp->arg_type == OPT_ARG) ? 8 : 6; } - else if (optp->var_type == GET_NO_ARG || optp->var_type == GET_BOOL) + else if ((optp->var_type & GET_TYPE_MASK) == GET_NO_ARG || + (optp->var_type & GET_TYPE_MASK) == GET_BOOL) { putchar(' '); col++; @@ -777,41 +878,44 @@ void my_print_variables(const struct my_option *options) printf("--------------------------------- -----------------------------\n"); for (optp= options; optp->id; optp++) { - if (optp->value) + gptr *value= (optp->var_type & GET_ASK_ADDR ? + (*getopt_get_addr)("", 0, optp) : optp->value); + if (value) { printf("%s", optp->name); length= strlen(optp->name); for (; length < name_space; length++) putchar(' '); - switch (optp->var_type) { + switch ((optp->var_type & GET_TYPE_MASK)) { case GET_STR: case GET_STR_ALLOC: /* fall through */ - printf("%s\n", *((char**) optp->value) ? *((char**) optp->value) : + printf("%s\n", *((char**) value) ? *((char**) value) : "(No default value)"); break; case GET_BOOL: - printf("%s\n", *((my_bool*) optp->value) ? "TRUE" : "FALSE"); + printf("%s\n", *((my_bool*) value) ? "TRUE" : "FALSE"); break; case GET_INT: - printf("%d\n", *((int*) optp->value)); + printf("%d\n", *((int*) value)); break; case GET_UINT: - printf("%d\n", *((uint*) optp->value)); + printf("%d\n", *((uint*) value)); break; case GET_LONG: - printf("%lu\n", *((long*) optp->value)); + printf("%lu\n", *((long*) value)); break; case GET_ULONG: - printf("%lu\n", *((ulong*) optp->value)); + printf("%lu\n", *((ulong*) value)); break; case GET_LL: - printf("%s\n", llstr(*((longlong*) optp->value), buff)); + printf("%s\n", llstr(*((longlong*) value), buff)); break; case GET_ULL: - longlong2str(*((ulonglong*) optp->value), buff, 10); + longlong2str(*((ulonglong*) value), buff, 10); printf("%s\n", buff); break; - default: /* dummy default to avoid compiler warnings */ + default: + printf("(Disabled)\n"); break; } } diff --git a/mysys/my_getsystime.c b/mysys/my_getsystime.c new file mode 100644 index 00000000000..91c977f0b5a --- /dev/null +++ b/mysys/my_getsystime.c @@ -0,0 +1,65 @@ +/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +/* get time since epoc in 100 nanosec units */ +/* thus to get the current time we should use the system function + with the highest possible resolution */ + +#ifdef __NETWARE__ +#include <nks/time.h> +#endif + +#include "mysys_priv.h" +ulonglong my_getsystime() +{ +#ifdef HAVE_CLOCK_GETTIME + struct timespec tp; + clock_gettime(CLOCK_REALTIME, &tp); + return (ulonglong)tp.tv_sec*10000000+(ulonglong)tp.tv_nsec/100; +#elif defined(__WIN__) +#define OFFSET_TO_EPOC ((__int64) 134774 * 24 * 60 * 60 * 1000 * 1000 * 10) + static __int64 offset=0, freq; + LARGE_INTEGER t_cnt; + if (!offset) + { + /* strictly speaking there should be a mutex to protect + initialization section. But my_getsystime() is called from + UUID() code, and UUID() calls are serialized with a mutex anyway + */ + LARGE_INTEGER li; + FILETIME ft; + GetSystemTimeAsFileTime(&ft); + li.LowPart=ft.dwLowDateTime; + li.HighPart=ft.dwHighDateTime; + offset=li.QuadPart-OFFSET_TO_EPOC; + QueryPerformanceFrequency(&li); + freq=li.QuadPart; + QueryPerformanceCounter(&t_cnt); + offset-=t_cnt.QuadPart/freq*10000000+t_cnt.QuadPart%freq*10000000/freq; + } + QueryPerformanceCounter(&t_cnt); + return t_cnt.QuadPart/freq*10000000+t_cnt.QuadPart%freq*10000000/freq+offset; +#elif defined(__NETWARE__) + NXTime_t tm; + NXGetTime(NX_SINCE_1970, NX_NSECONDS, &tm); + return (ulonglong)tm/100; +#else + /* TODO: check for other possibilities for hi-res timestamping */ + struct timeval tv; + gettimeofday(&tv,NULL); + return (ulonglong)tv.tv_sec*10000000+(ulonglong)tv.tv_usec*10; +#endif +} diff --git a/mysys/my_getwd.c b/mysys/my_getwd.c index 63ab17b4c51..fd47c532cff 100644 --- a/mysys/my_getwd.c +++ b/mysys/my_getwd.c @@ -108,8 +108,9 @@ 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; + pos++; /* Skip FN_DEVCHAR */ + drive=(uint) (my_toupper(&my_charset_latin1,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..6003808df25 --- /dev/null +++ b/mysys/my_handler.c @@ -0,0 +1,440 @@ +/* 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, + my_bool skip_end_space) +{ + if (skip_end_space) + return charset_info->coll->strnncollsp(charset_info, a, a_length, + b, b_length); + return charset_info->coll->strnncoll(charset_info, a, a_length, + b, b_length, part_key); +} + + +static int compare_bin(uchar *a, uint a_length, uchar *b, uint b_length, + my_bool part_key, my_bool skip_end_space) +{ + 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; + if (skip_end_space && a_length != b_length) + { + int swap= 0; + /* + We are using space compression. We have to check if longer key + has next character < ' ', in which case it's less than the shorter + key that has an implicite space afterwards. + + This code is identical to the one in + strings/ctype-simple.c:my_strnncollsp_simple + */ + if (a_length < b_length) + { + /* put shorter key in a */ + a_length= b_length; + a= b; + swap= -1; /* swap sign of result */ + } + for (end= a + a_length-length; a < end ; a++) + { + if (*a != ' ') + return ((int) *a - (int) ' ') ^ swap; + } + 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), + !(nextflag & SEARCH_PREFIX)))) + 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 (piks && + (flag= mi_compare_text(keyseg->charset, a, a_length, b, b_length, + (my_bool) ((nextflag & SEARCH_PREFIX) && + next_key_length <= 0), + !(nextflag & SEARCH_PREFIX)))) + 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),1))) + 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),0))) + return ((keyseg->flag & HA_REVERSE_SORT) ? -flag : flag); + a+=length; + b+=length; + } + break; + case HA_KEYTYPE_VARTEXT: + { + int a_length,full_a_length,b_length,full_b_length,pack_length; + get_key_length(a_length,a); + get_key_pack_length(b_length,pack_length,b); + full_a_length= a_length; + full_b_length= b_length; + 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), + (my_bool) ((nextflag & (SEARCH_FIND | + SEARCH_UPDATE)) == + SEARCH_FIND)))) + return ((keyseg->flag & HA_REVERSE_SORT) ? -flag : flag); + a+= full_a_length; + b+= full_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), 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_variables(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_variables(uchar*, a, b); + swap_variables(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_variables(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 dd06ae41dcb..da0e6caf96d 100644 --- a/mysys/my_init.c +++ b/mysys/my_init.c @@ -17,13 +17,8 @@ #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 -#include <sys/resource.h> -/* extern int getrusage(int, struct rusage *); */ -#endif #include <signal.h> #ifdef VMS #include <my_static.c> @@ -53,7 +48,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(&my_charset_latin1, *str)) str++; str2int(str, (*str == '0' ? 8 : 10), /* Octalt or decimalt */ @@ -141,7 +136,7 @@ void my_end(int infoflag) DBUG_PRINT("error",("%s",errbuff[0])); } } - free_charsets(); + my_once_free(); if (infoflag & MY_GIVE_INFO || info_file != stderr) { #ifdef HAVE_GETRUSAGE @@ -181,7 +176,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) @@ -244,8 +238,13 @@ static void my_win_init(void) setlocale(LC_CTYPE, ""); /* To get right sortorder */ - /* Clear the OS system variable TZ and avoid the 100% CPU usage */ +#if defined(_MSC_VER) && (_MSC_VER < 1300) + /* + Clear the OS system variable TZ and avoid the 100% CPU usage + Only for old versions of Visual C++ + */ _putenv( "TZ=" ); +#endif _tzset(); /* apre la chiave HKEY_LOCAL_MACHINES\software\MySQL */ diff --git a/mysys/my_lock.c b/mysys/my_lock.c index 5058c301adb..8f915d6003a 100644 --- a/mysys/my_lock.c +++ b/mysys/my_lock.c @@ -117,10 +117,12 @@ int my_lock(File fd, int locktype, my_off_t start, my_off_t length, #if defined(HAVE_FCNTL) { struct flock lock; - lock.l_type= (short) locktype; - lock.l_whence=0L; - lock.l_start=(long) start; - lock.l_len=(long) length; + + lock.l_type= (short) locktype; + lock.l_whence= SEEK_SET; + lock.l_start= (off_t) start; + lock.l_len= (off_t) length; + if (MyFlags & MY_DONT_WAIT) { if (fcntl(fd,F_SETLK,&lock) != -1) /* Check if we can lock */ diff --git a/mysys/my_malloc.c b/mysys/my_malloc.c index b273363aaf1..df9fe1f9bc4 100644 --- a/mysys/my_malloc.c +++ b/mysys/my_malloc.c @@ -24,26 +24,26 @@ /* My memory allocator */ -gptr my_malloc(unsigned int Size, myf MyFlags) +gptr my_malloc(unsigned int size, myf my_flags) { gptr point; DBUG_ENTER("my_malloc"); - DBUG_PRINT("my",("Size: %u MyFlags: %d",Size, MyFlags)); + DBUG_PRINT("my",("size: %u my_flags: %d",size, my_flags)); - if (!Size) - Size=1; /* Safety */ - if ((point = (char*)malloc(Size)) == NULL) + if (!size) + size=1; /* Safety */ + if ((point = (char*)malloc(size)) == NULL) { my_errno=errno; - if (MyFlags & MY_FAE) + if (my_flags & MY_FAE) error_handler_hook=fatal_error_handler_hook; - if (MyFlags & (MY_FAE+MY_WME)) - my_error(EE_OUTOFMEMORY, MYF(ME_BELL+ME_WAITTANG),Size); - if (MyFlags & MY_FAE) + if (my_flags & (MY_FAE+MY_WME)) + my_error(EE_OUTOFMEMORY, MYF(ME_BELL+ME_WAITTANG),size); + if (my_flags & MY_FAE) exit(1); } - else if (MyFlags & MY_ZEROFILL) - bzero(point,Size); + else if (my_flags & MY_ZEROFILL) + bzero(point,size); DBUG_PRINT("exit",("ptr: %lx",point)); DBUG_RETURN(point); } /* my_malloc */ @@ -64,29 +64,29 @@ void my_no_flags_free(gptr ptr) /* malloc and copy */ -gptr my_memdup(const byte *from, uint length, myf MyFlags) +gptr my_memdup(const byte *from, uint length, myf my_flags) { gptr ptr; - if ((ptr=my_malloc(length,MyFlags)) != 0) + if ((ptr=my_malloc(length,my_flags)) != 0) memcpy((byte*) ptr, (byte*) from,(size_t) length); return(ptr); } -char *my_strdup(const char *from, myf MyFlags) +char *my_strdup(const char *from, myf my_flags) { gptr ptr; uint length=(uint) strlen(from)+1; - if ((ptr=my_malloc(length,MyFlags)) != 0) + if ((ptr=my_malloc(length,my_flags)) != 0) memcpy((byte*) ptr, (byte*) from,(size_t) length); return((my_string) ptr); } -char *my_strdup_with_length(const byte *from, uint length, myf MyFlags) +char *my_strdup_with_length(const byte *from, uint length, myf my_flags) { gptr ptr; - if ((ptr=my_malloc(length+1,MyFlags)) != 0) + if ((ptr=my_malloc(length+1,my_flags)) != 0) { memcpy((byte*) ptr, (byte*) from,(size_t) length); ((char*) ptr)[length]=0; diff --git a/mysys/my_new.cc b/mysys/my_new.cc index 5f2da90bbd1..14423c3afd5 100644 --- a/mysys/my_new.cc +++ b/mysys/my_new.cc @@ -21,7 +21,7 @@ #include "mysys_priv.h" -#ifdef USE_MYSYS_NEW +#ifdef USE_MYSYS_NEW void *operator new (size_t sz) { 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_open.c b/mysys/my_open.c index 97f21724e1c..ca5c0d8683f 100644 --- a/mysys/my_open.c +++ b/mysys/my_open.c @@ -86,7 +86,7 @@ int my_close(File fd, myf MyFlags) if (MyFlags & (MY_FAE | MY_WME)) my_error(EE_BADCLOSE, MYF(ME_BELL+ME_WAITTANG),my_filename(fd),errno); } - if ((uint) fd < MY_NFILE && my_file_info[fd].type != UNOPEN) + if ((uint) fd < my_file_limit && my_file_info[fd].type != UNOPEN) { my_free(my_file_info[fd].name, MYF(0)); #if defined(THREAD) && !defined(HAVE_PREAD) @@ -115,7 +115,7 @@ File my_register_filename(File fd, const char *FileName, enum file_type { if ((int) fd >= 0) { - if ((int) fd >= MY_NFILE) + if ((uint) fd >= my_file_limit) { #if defined(THREAD) && !defined(HAVE_PREAD) (void) my_close(fd,MyFlags); diff --git a/mysys/my_pthread.c b/mysys/my_pthread.c index a8157cc2f91..37517fb8327 100644 --- a/mysys/my_pthread.c +++ b/mysys/my_pthread.c @@ -23,7 +23,6 @@ #include <signal.h> #include <m_string.h> #include <thr_alarm.h> -#include <assert.h> #if (defined(__BSD__) || defined(_BSDI_VERSION)) && !defined(HAVE_mit_thread) #define SCHED_POLICY SCHED_RR @@ -98,25 +97,23 @@ void *my_pthread_getspecific_imp(pthread_key_t key) #undef pthread_exit void my_pthread_exit(void *status) { - NXThreadId_t tid = NXThreadGetId(); + NXThreadId_t tid; NXContext_t ctx; - char name[PATH_MAX] = ""; - - /* Do not call pthread_exit if it is not a LibC thread */ - if (tid != 0) - { - NXThreadGetContext(tid, &ctx); - NXContextGetName(ctx, name, PATH_MAX); - - /* - "MYSQLD.NLM's LibC Reaper" or "MYSQLD.NLM's main thread" - with a debug build of LibC the reaper can have different names - */ - if (!strindex(name, "\'s")) - { - pthread_exit(status); - } - } + char name[NX_MAX_OBJECT_NAME_LEN+1] = ""; + + tid= NXThreadGetId(); + if (tid == NX_INVALID_THREAD_ID || !tid) + return; + if (NXThreadGetContext(tid, &ctx) || + NXContextGetName(ctx, name, sizeof(name)-1)) + return; + + /* + "MYSQLD.NLM's LibC Reaper" or "MYSQLD.NLM's main thread" + with a debug build of LibC the reaper can have different names + */ + if (!strindex(name, "\'s")) + pthread_exit(status); } #endif @@ -141,10 +138,13 @@ int my_sigwait(const sigset_t *set,int *sig) /* localtime_r for SCO 3.2V4.2 */ -#ifndef HAVE_LOCALTIME_R +#if !defined(HAVE_LOCALTIME_R) || !defined(HAVE_GMTIME_R) extern pthread_mutex_t LOCK_localtime_r; +#endif + +#if !defined(HAVE_LOCALTIME_R) struct tm *localtime_r(const time_t *clock, struct tm *res) { struct tm *tmp; @@ -156,6 +156,22 @@ struct tm *localtime_r(const time_t *clock, struct tm *res) } #endif +#if !defined(HAVE_GMTIME_R) +/* + Reentrant version of standard gmtime() function. + Needed on some systems which don't implement it. +*/ + +struct tm *gmtime_r(const time_t *clock, struct tm *res) +{ + struct tm *tmp; + pthread_mutex_lock(&LOCK_localtime_r); + tmp= gmtime(clock); + *res= *tmp; + pthread_mutex_unlock(&LOCK_localtime_r); + return res; +} +#endif /**************************************************************************** ** Replacement of sigwait if the system doesn't have one (like BSDI 3.0) diff --git a/mysys/my_realloc.c b/mysys/my_realloc.c index 49d96c2eb4f..5190fa75dce 100644 --- a/mysys/my_realloc.c +++ b/mysys/my_realloc.c @@ -23,40 +23,41 @@ /* My memory re allocator */ -gptr my_realloc(gptr oldpoint, uint Size, myf MyFlags) +gptr my_realloc(gptr oldpoint, uint size, myf my_flags) { gptr point; DBUG_ENTER("my_realloc"); - DBUG_PRINT("my",("ptr: %lx Size: %u MyFlags: %d",oldpoint, Size, MyFlags)); + DBUG_PRINT("my",("ptr: %lx size: %u my_flags: %d",oldpoint, size, + my_flags)); - if (!oldpoint && (MyFlags & MY_ALLOW_ZERO_PTR)) - DBUG_RETURN(my_malloc(Size,MyFlags)); + if (!oldpoint && (my_flags & MY_ALLOW_ZERO_PTR)) + DBUG_RETURN(my_malloc(size,my_flags)); #ifdef USE_HALLOC - if (!(point = malloc(Size))) + if (!(point = malloc(size))) { - if (MyFlags & MY_FREE_ON_ERROR) - my_free(oldpoint,MyFlags); - if (MyFlags & MY_HOLD_ON_ERROR) + if (my_flags & MY_FREE_ON_ERROR) + my_free(oldpoint,my_flags); + if (my_flags & MY_HOLD_ON_ERROR) DBUG_RETURN(oldpoint); my_errno=errno; - if (MyFlags & MY_FAE+MY_WME) - my_error(EE_OUTOFMEMORY, MYF(ME_BELL+ME_WAITTANG),Size); + if (my_flags & MY_FAE+MY_WME) + my_error(EE_OUTOFMEMORY, MYF(ME_BELL+ME_WAITTANG),size); } else { - memcpy(point,oldpoint,Size); + memcpy(point,oldpoint,size); free(oldpoint); } #else - if ((point = (char*)realloc(oldpoint,Size)) == NULL) + if ((point = (char*)realloc(oldpoint,size)) == NULL) { - if (MyFlags & MY_FREE_ON_ERROR) + if (my_flags & MY_FREE_ON_ERROR) my_free(oldpoint,MyFLAGS); - if (MyFlags & MY_HOLD_ON_ERROR) + if (my_flags & MY_HOLD_ON_ERROR) DBUG_RETURN(oldpoint); my_errno=errno; - if (MyFlags & (MY_FAE+MY_WME)) - my_error(EE_OUTOFMEMORY, MYF(ME_BELL+ME_WAITTANG), Size); + if (my_flags & (MY_FAE+MY_WME)) + my_error(EE_OUTOFMEMORY, MYF(ME_BELL+ME_WAITTANG), size); } #endif DBUG_PRINT("exit",("ptr: %lx",point)); diff --git a/mysys/my_redel.c b/mysys/my_redel.c index 9ba03cd9526..9af360424b0 100644 --- a/mysys/my_redel.c +++ b/mysys/my_redel.c @@ -19,10 +19,10 @@ #include <my_dir.h> #include <m_string.h> #include "mysys_err.h" -#if defined(HAVE_SYS_UTIME_H) -#include <sys/utime.h> -#elif defined(HAVE_UTIME_H) +#if defined(HAVE_UTIME_H) #include <utime.h> +#elif defined(HAVE_SYS_UTIME_H) +#include <sys/utime.h> #elif !defined(HPUX10) struct utimbuf { time_t actime; diff --git a/mysys/my_seek.c b/mysys/my_seek.c index ec24a26b3d9..6af65d70fd0 100644 --- a/mysys/my_seek.c +++ b/mysys/my_seek.c @@ -15,7 +15,6 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "mysys_priv.h" -#include <assert.h> /* Seek to position in file */ /*ARGSUSED*/ diff --git a/mysys/my_static.c b/mysys/my_static.c index b24ef28b7b1..5f034555156 100644 --- a/mysys/my_static.c +++ b/mysys/my_static.c @@ -34,7 +34,9 @@ int NEAR my_umask=0664, NEAR my_umask_dir=0777; #ifndef THREAD int NEAR my_errno=0; #endif -struct my_file_info my_file_info[MY_NFILE]= {{0,UNOPEN}}; +struct st_my_file_info my_file_info_default[MY_NFILE]= {{0,UNOPEN}}; +uint my_file_limit= MY_NFILE; +struct st_my_file_info *my_file_info= my_file_info_default; /* From mf_brkhant */ int NEAR my_dont_interrupt=0; diff --git a/mysys/my_static.h b/mysys/my_static.h index 1a33bcf21f3..51f9fbc922f 100644 --- a/mysys/my_static.h +++ b/mysys/my_static.h @@ -19,7 +19,7 @@ a shared library */ -#include "mysys_priv.h" +C_MODE_START #include <signal.h> #define MAX_SIGNALS 10 /* Max signals under a dont-allow */ @@ -69,6 +69,9 @@ extern byte *sf_min_adress,*sf_max_adress; extern uint sf_malloc_count; extern struct st_irem *sf_malloc_root; +extern struct st_my_file_info my_file_info_default[MY_NFILE]; + #if defined(THREAD) && !defined(__WIN__) extern sigset_t my_signals; /* signals blocked by mf_brkhant */ #endif +C_MODE_END diff --git a/mysys/my_symlink.c b/mysys/my_symlink.c index b366678769e..045802c5e61 100644 --- a/mysys/my_symlink.c +++ b/mysys/my_symlink.c @@ -124,20 +124,22 @@ int my_realpath(char *to, const char *filename, } else { - /* Realpath didn't work; Use original name */ + /* + Realpath didn't work; Use my_load_path() which is a poor substitute + original name but will at least be able to resolve paths that starts + with '.'. + */ DBUG_PRINT("error",("realpath failed with errno: %d", errno)); my_errno=errno; if (MyFlags & MY_WME) my_error(EE_REALPATH, MYF(0), filename, my_errno); - if (to != filename) - strmov(to,filename); + my_load_path(to, filename, NullS); result= -1; } } DBUG_RETURN(result); #else - if (to != filename) - strmov(to,filename); + my_load_path(to, filename, NullS); return 0; #endif } diff --git a/mysys/my_tempnam.c b/mysys/my_tempnam.c index b4f76727ee0..9f765298fb6 100644 --- a/mysys/my_tempnam.c +++ b/mysys/my_tempnam.c @@ -161,7 +161,7 @@ my_string my_tempnam(const char *dir, const char *pfx, for (length=0 ; length < 8 && uniq ; length++) { - *end_pos++= _dig_vec[(int) (uniq & 31)]; + *end_pos++= _dig_vec_upper[(int) (uniq & 31)]; uniq >>= 5; } VOID(strmov(end_pos,TMP_EXT)); diff --git a/mysys/my_thr_init.c b/mysys/my_thr_init.c index 878a861bc94..0ce59bee346 100644 --- a/mysys/my_thr_init.c +++ b/mysys/my_thr_init.c @@ -28,10 +28,10 @@ pthread_key(struct st_my_thread_var*, THR_KEY_mysys); #else pthread_key(struct st_my_thread_var, THR_KEY_mysys); #endif /* USE_TLS */ -pthread_mutex_t THR_LOCK_malloc,THR_LOCK_open,THR_LOCK_keycache, +pthread_mutex_t THR_LOCK_malloc,THR_LOCK_open, THR_LOCK_lock,THR_LOCK_isam,THR_LOCK_myisam,THR_LOCK_heap, THR_LOCK_net, THR_LOCK_charset; -#ifndef HAVE_LOCALTIME_R +#if !defined(HAVE_LOCALTIME_R) || !defined(HAVE_GMTIME_R) pthread_mutex_t LOCK_localtime_r; #endif #ifndef HAVE_GETHOSTBYNAME_R @@ -74,7 +74,6 @@ my_bool my_thread_global_init(void) pthread_mutex_init(&THR_LOCK_malloc,MY_MUTEX_INIT_FAST); pthread_mutex_init(&THR_LOCK_open,MY_MUTEX_INIT_FAST); - pthread_mutex_init(&THR_LOCK_keycache,MY_MUTEX_INIT_FAST); pthread_mutex_init(&THR_LOCK_lock,MY_MUTEX_INIT_FAST); pthread_mutex_init(&THR_LOCK_isam,MY_MUTEX_INIT_SLOW); pthread_mutex_init(&THR_LOCK_myisam,MY_MUTEX_INIT_SLOW); @@ -84,7 +83,7 @@ my_bool my_thread_global_init(void) #if defined( __WIN__) || defined(OS2) win_pthread_init(); #endif -#ifndef HAVE_LOCALTIME_R +#if !defined(HAVE_LOCALTIME_R) || !defined(HAVE_GMTIME_R) pthread_mutex_init(&LOCK_localtime_r,MY_MUTEX_INIT_SLOW); #endif #ifndef HAVE_GETHOSTBYNAME_R @@ -110,14 +109,13 @@ void my_thread_global_end(void) #endif pthread_mutex_destroy(&THR_LOCK_malloc); pthread_mutex_destroy(&THR_LOCK_open); - pthread_mutex_destroy(&THR_LOCK_keycache); pthread_mutex_destroy(&THR_LOCK_lock); pthread_mutex_destroy(&THR_LOCK_isam); pthread_mutex_destroy(&THR_LOCK_myisam); pthread_mutex_destroy(&THR_LOCK_heap); pthread_mutex_destroy(&THR_LOCK_net); pthread_mutex_destroy(&THR_LOCK_charset); -#ifndef HAVE_LOCALTIME_R +#if !defined(HAVE_LOCALTIME_R) || !defined(HAVE_GMTIME_R) pthread_mutex_destroy(&LOCK_localtime_r); #endif #ifndef HAVE_GETHOSTBYNAME_R @@ -205,7 +203,8 @@ void my_thread_end(void) tmp->dbug=0; } #endif -#if !defined(__bsdi__) || defined(HAVE_mit_thread) /* bsdi dumps core here */ +#if !defined(__bsdi__) && !defined(__OpenBSD__) || defined(HAVE_mit_thread) + /* bsdi and openbsd 3.5 dumps core here */ pthread_cond_destroy(&tmp->suspend); #endif pthread_mutex_destroy(&tmp->mutex); diff --git a/mysys/my_vsnprintf.c b/mysys/my_vsnprintf.c deleted file mode 100644 index e49b1d0e729..00000000000 --- a/mysys/my_vsnprintf.c +++ /dev/null @@ -1,124 +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, ...) -{ - int result; - va_list args; - va_start(args,fmt); - result= my_vsnprintf(to, n, fmt, args); - va_end(args); - return result; -} - - -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/mysys_priv.h b/mysys/mysys_priv.h index f79431a0b0b..6abadd48aeb 100644 --- a/mysys/mysys_priv.h +++ b/mysys/mysys_priv.h @@ -21,6 +21,10 @@ #include "system_wrappers.h" #endif +#ifdef HAVE_GETRUSAGE +#include <sys/resource.h> +#endif + #ifdef THREAD #include <my_pthread.h> extern pthread_mutex_t THR_LOCK_malloc, THR_LOCK_open, THR_LOCK_keycache; @@ -29,3 +33,4 @@ extern pthread_mutex_t THR_LOCK_charset; #else #include <my_no_pthread.h> #endif + diff --git a/mysys/queues.c b/mysys/queues.c index 93d4c303f22..ecf1058af41 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,13 +62,32 @@ int init_queue(QUEUE *queue, uint max_elements, uint offset_to_key, DBUG_RETURN(0); } + /* - Reinitialize queue for new usage; + Reinitialize queue for other usage + + 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 + This will delete all elements from the queue. If you don't want this, + use resize_queue() instead. + + 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"); queue->elements=0; @@ -96,6 +134,20 @@ int resize_queue(QUEUE *queue, uint max_elements) } +/* + 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"); @@ -146,7 +198,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; @@ -156,8 +208,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); } @@ -199,8 +250,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/rijndael.c b/mysys/rijndael.c index dd0c45445d5..43cd14101ca 100644 --- a/mysys/rijndael.c +++ b/mysys/rijndael.c @@ -26,7 +26,6 @@ */ #include <my_global.h> -#include <assert.h> #include "rijndael.h" /* diff --git a/mysys/test_charset.c b/mysys/test_charset.c index 15e46f3ff82..419332cb997 100644 --- a/mysys/test_charset.c +++ b/mysys/test_charset.c @@ -21,12 +21,44 @@ #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; char *cs_list; int argcnt = 1; + CHARSET_INFO *cs; my_init(); @@ -39,20 +71,22 @@ int main(int argc, char **argv) { if (argc > argcnt) charsets_dir = argv[argcnt++]; - if (set_default_charset_by_name(the_set, MYF(MY_WME))) + if (!(cs= get_charset_by_name(the_set, MYF(MY_WME)))) return 1; puts("CHARSET INFO:"); - _print_csinfo(default_charset_info); + _print_csinfo(cs); fflush(stdout); - cs_list = list_charsets(MYF(MY_COMPILED_SETS | MY_CONFIG_SETS)); +#define NOT_USED_ANYMORE + 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)); +#endif 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/testhash.c b/mysys/testhash.c index a1d14dc225d..72badffdbcd 100644 --- a/mysys/testhash.c +++ b/mysys/testhash.c @@ -83,7 +83,7 @@ static int do_test() n1=rnd(1000); n2=rnd(100); n3=rnd(min(recant*5,MAX_RECORDS)); record= (char*) my_malloc(reclength,MYF(MY_FAE)); sprintf(record,"%6d:%4d:%8d:Pos: %4d ",n1,n2,n3,write_count); - if (hash_insert(&hash,record)) + if (my_hash_insert(&hash,record)) { printf("Error: %d in write at record: %d\n",my_errno,i); goto err; @@ -199,7 +199,7 @@ static int do_test() record=(byte*) my_malloc(reclength,MYF(MY_FAE)); memcpy(record,recpos,reclength); record[reclength-1]=rnd(5)+1; - if (hash_insert(&hash2,record)) + if (my_hash_insert(&hash2,record)) { printf("Got error when inserting record: %*s",reclength,record); goto err; diff --git a/mysys/thr_alarm.c b/mysys/thr_alarm.c index 54aa4d421f6..84a8e779ae1 100644 --- a/mysys/thr_alarm.c +++ b/mysys/thr_alarm.c @@ -27,7 +27,6 @@ #include <m_string.h> #include <queues.h> #include "thr_alarm.h" -#include <assert.h> #ifdef HAVE_SYS_SELECT_H #include <sys/select.h> /* AIX needs this for fd_set */ diff --git a/mysys/thr_lock.c b/mysys/thr_lock.c index d5236cb1ef9..0e3ccfc0452 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) @@ -314,7 +314,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; } @@ -325,7 +325,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; } @@ -1116,7 +1116,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/thr_mutex.c b/mysys/thr_mutex.c index 3abac2dc737..8ebe5be22e8 100644 --- a/mysys/thr_mutex.c +++ b/mysys/thr_mutex.c @@ -54,12 +54,15 @@ void safe_mutex_global_init(void) int safe_mutex_init(safe_mutex_t *mp, const pthread_mutexattr_t *attr __attribute__((unused)), - const char *file __attribute__((unused)), - uint line __attribute__((unused))) + const char *file, + uint line) { bzero((char*) mp,sizeof(*mp)); pthread_mutex_init(&mp->global,MY_MUTEX_INIT_ERRCHK); pthread_mutex_init(&mp->mutex,attr); + /* Mark that mutex is initialized */ + mp->file= file; + mp->line= line; #ifdef SAFE_MUTEX_DETECT_DESTROY /* @@ -70,7 +73,7 @@ int safe_mutex_init(safe_mutex_t *mp, { struct st_safe_mutex_info_t *info =mp->info; - info->init_file= (char *) file; + info->init_file= file; info->init_line= line; info->prev= NULL; info->next= NULL; @@ -92,6 +95,15 @@ int safe_mutex_init(safe_mutex_t *mp, int safe_mutex_lock(safe_mutex_t *mp,const char *file, uint line) { int error; + if (!mp->file) + { + fprintf(stderr, + "safe_mutex: Trying to lock unitialized mutex at %s, line %d\n", + file, line); + fflush(stderr); + abort(); + } + pthread_mutex_lock(&mp->global); if (mp->count > 0 && pthread_equal(pthread_self(),mp->thread)) { @@ -117,7 +129,7 @@ line %d more than 1 time\n", file,line); abort(); } mp->thread=pthread_self(); - mp->file= (char*) file; + mp->file= file; mp->line=line; pthread_mutex_unlock(&mp->global); return error; @@ -204,7 +216,7 @@ int safe_cond_wait(pthread_cond_t *cond, safe_mutex_t *mp, const char *file, abort(); } mp->thread=pthread_self(); - mp->file= (char*) file; + mp->file= file; mp->line=line; pthread_mutex_unlock(&mp->global); return error; @@ -242,7 +254,7 @@ int safe_cond_timedwait(pthread_cond_t *cond, safe_mutex_t *mp, abort(); } mp->thread=pthread_self(); - mp->file= (char*) file; + mp->file= file; mp->line=line; pthread_mutex_unlock(&mp->global); return error; @@ -252,6 +264,14 @@ int safe_cond_timedwait(pthread_cond_t *cond, safe_mutex_t *mp, int safe_mutex_destroy(safe_mutex_t *mp, const char *file, uint line) { int error=0; + if (!mp->file) + { + fprintf(stderr, + "safe_mutex: Trying to destroy unitialized mutex at %s, line %d\n", + file, line); + fflush(stderr); + abort(); + } if (mp->count != 0) { fprintf(stderr,"safe_mutex: Trying to destroy a mutex that was locked at %s, line %d at %s, line %d\n", @@ -268,6 +288,7 @@ int safe_mutex_destroy(safe_mutex_t *mp, const char *file, uint line) if (pthread_mutex_destroy(&mp->mutex)) error=1; #endif + mp->file= 0; /* Mark destroyed */ #ifdef SAFE_MUTEX_DETECT_DESTROY if (mp->info) diff --git a/mysys/tree.c b/mysys/tree.c index 2b5ea717809..063c8739e58 100644 --- a/mysys/tree.c +++ b/mysys/tree.c @@ -17,31 +17,49 @@ /* 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. +*/ + +/* + NOTE: + tree->compare function should be ALWAYS called as + (*tree->compare)(custom_arg, ELEMENT_KEY(tree,element), key) + and not other way around, as + (*tree->compare)(custom_arg, key, ELEMENT_KEY(tree,element)) + + ft_boolean_search.c (at least) relies on that. */ #include "mysys_priv.h" #include <m_string.h> #include <my_tree.h> +#include "my_base.h" #define BLACK 1 #define RED 0 @@ -87,6 +105,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)))) { @@ -151,8 +170,8 @@ void delete_tree(TREE* tree) void reset_tree(TREE* tree) { + /* do not free mem_root, just mark blocks as free */ free_tree(tree, MYF(MY_MARK_BLOCKS_FREE)); - /* do not my_free() mem_root if applicable, just mark blocks as free */ } @@ -169,12 +188,17 @@ static void delete_tree_element(TREE *tree, TREE_ELEMENT *element) } } - /* Code for insert, search and delete of elements */ - /* parent[0] = & parent[-1][0]->left || - parent[0] = & parent[-1][0]->right */ +/* + insert, search and delete of elements + + The following should be true: + parent[0] = & parent[-1][0]->left || + 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; @@ -184,8 +208,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) { @@ -205,15 +229,14 @@ 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; if (tree->with_delete) element=(TREE_ELEMENT *) my_malloc(alloc_size, MYF(MY_WME)); else - element=(TREE_ELEMENT *) - alloc_root(&tree->mem_root,alloc_size); + element=(TREE_ELEMENT *) alloc_root(&tree->mem_root,alloc_size); if (!element) return(NULL); **parent=element; @@ -231,18 +254,21 @@ TREE_ELEMENT *tree_insert(TREE *tree, void *key, uint key_size) } else memcpy((byte*) element+tree->offset_to_key,key,(size_t) key_size); - element->count=1; /* May give warning in purify */ + element->count=1; /* May give warning in purify */ tree->elements_in_tree++; - rb_insert(tree,parent,element); /* rebalance tree */ + 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; @@ -255,8 +281,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) { @@ -297,13 +323,15 @@ int tree_delete(TREE *tree, void *key) rb_delete_fixup(tree,parent); if (tree->free) (*tree->free)(ELEMENT_KEY(tree,element), free_free, tree->custom_arg); + /* This doesn't include key_size, but better than nothing */ + tree->allocated-= sizeof(TREE_ELEMENT)+tree->size_of_element; my_free((gptr) element,MYF(0)); tree->elements_in_tree--; return 0; } -void *tree_search(TREE *tree, void *key) +void *tree_search(TREE *tree, void *key, void *custom_arg) { int cmp; TREE_ELEMENT *element=tree->root; @@ -312,8 +340,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; @@ -322,6 +350,176 @@ 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; + + while (element != &tree->null_element) + { + if ((cmp= (*tree->compare)(custom_arg, ELEMENT_KEY(tree, element), + key)) == 0) + { + switch (flag) { + case HA_READ_KEY_EXACT: + 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: + 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..9aaf97d143f 100644 --- a/mysys/typelib.c +++ b/mysys/typelib.c @@ -20,15 +20,28 @@ #include <m_string.h> #include <m_ctype.h> -/*************************************************************************** -** Search after a fieldtype. Endspace in x is not compared. -** If part, uniq field is found and full_name == 0 then x is expanded -** to full field. -** full_name has the following bit values: -** If & 1 accept only whole names -** If & 2 don't expand if half field -** If & 4 allow #number# as type -****************************************************************************/ + +/* + Search after a string in a list of strings. Endspace in x is not compared. + + SYNOPSIS + find_type() + x String to find + lib TYPELIB (struct of pointer to values + count) + full_name bitmap of what to do + If & 1 accept only whole names + If & 2 don't expand if half field + If & 4 allow #number# as type + + NOTES + If part, uniq field is found and full_name == 0 then x is expanded + to full field. + + RETURN + -1 Too many matching values + 0 No matching value + >0 Offset+1 in typelib for matched string +*/ int find_type(my_string x, TYPELIB *typelib, uint full_name) { @@ -47,11 +60,13 @@ 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(&my_charset_latin1,*i) == + my_toupper(&my_charset_latin1,*j) ; i++, j++) ; if (! *j) { while (*i == ' ') - i++; /* skipp_end_space */ + i++; /* skip_end_space */ if (! *i) DBUG_RETURN(pos+1); } |