diff options
Diffstat (limited to 'mysys')
83 files changed, 8133 insertions, 2431 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 68d5da5afe8..90a28a9ce4b 100644 --- a/mysys/Makefile.am +++ b/mysys/Makefile.am @@ -17,7 +17,9 @@ MYSQLDATAdir = $(localstatedir) MYSQLSHAREdir = $(pkgdatadir) MYSQLBASEdir= $(prefix) -INCLUDES = @MT_INCLUDES@ -I$(srcdir)/../include -I../include -I.. -I$(srcdir) +INCLUDES = @MT_INCLUDES@ \ + @ZLIB_INCLUDES@ -I$(top_builddir)/include \ + -I$(top_srcdir)/include -I$(srcdir) pkglib_LIBRARIES = libmysys.a LDADD = libmysys.a ../dbug/libdbug.a \ ../strings/libmystrings.a @@ -25,21 +27,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 +51,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_port.c my_sleep.c \ - my_vsnprintf.c charset.c my_bitmap.c my_bit.c md5.c \ + my_net.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 my_windac.c my_access.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 +94,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 +106,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..1fa87b715a8 --- /dev/null +++ b/mysys/charset-def.c @@ -0,0 +1,186 @@ +/* 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_UCA_COLLATIONS + +#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; +extern CHARSET_INFO my_charset_ucs2_roman_uca_ci; +extern CHARSET_INFO my_charset_ucs2_persian_uca_ci; +#endif + +#ifdef HAVE_CHARSET_utf8 +extern CHARSET_INFO my_charset_utf8_general_uca_ci; +extern CHARSET_INFO my_charset_utf8_icelandic_uca_ci; +extern CHARSET_INFO my_charset_utf8_latvian_uca_ci; +extern CHARSET_INFO my_charset_utf8_romanian_uca_ci; +extern CHARSET_INFO my_charset_utf8_slovenian_uca_ci; +extern CHARSET_INFO my_charset_utf8_polish_uca_ci; +extern CHARSET_INFO my_charset_utf8_estonian_uca_ci; +extern CHARSET_INFO my_charset_utf8_spanish_uca_ci; +extern CHARSET_INFO my_charset_utf8_swedish_uca_ci; +extern CHARSET_INFO my_charset_utf8_turkish_uca_ci; +extern CHARSET_INFO my_charset_utf8_czech_uca_ci; +extern CHARSET_INFO my_charset_utf8_danish_uca_ci; +extern CHARSET_INFO my_charset_utf8_lithuanian_uca_ci; +extern CHARSET_INFO my_charset_utf8_slovak_uca_ci; +extern CHARSET_INFO my_charset_utf8_spanish2_uca_ci; +extern CHARSET_INFO my_charset_utf8_roman_uca_ci; +extern CHARSET_INFO my_charset_utf8_persian_uca_ci; +#ifdef HAVE_UTF8_GENERAL_CS +extern CHARSET_INFO my_charset_utf8_general_cs; +#endif +#endif + +#endif /* HAVE_UCA_COLLATIONS */ + +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_cp932 + add_compiled_collation(&my_charset_cp932_japanese_ci); + add_compiled_collation(&my_charset_cp932_bin); +#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); +#ifdef HAVE_UCA_COLLATIONS + 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); + add_compiled_collation(&my_charset_ucs2_roman_uca_ci); + add_compiled_collation(&my_charset_ucs2_persian_uca_ci); +#endif +#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); +#ifdef HAVE_UTF8_GENERAL_CS + add_compiled_collation(&my_charset_utf8_general_cs); +#endif +#ifdef HAVE_UCA_COLLATIONS + add_compiled_collation(&my_charset_utf8_general_uca_ci); + add_compiled_collation(&my_charset_utf8_icelandic_uca_ci); + add_compiled_collation(&my_charset_utf8_latvian_uca_ci); + add_compiled_collation(&my_charset_utf8_romanian_uca_ci); + add_compiled_collation(&my_charset_utf8_slovenian_uca_ci); + add_compiled_collation(&my_charset_utf8_polish_uca_ci); + add_compiled_collation(&my_charset_utf8_estonian_uca_ci); + add_compiled_collation(&my_charset_utf8_spanish_uca_ci); + add_compiled_collation(&my_charset_utf8_swedish_uca_ci); + add_compiled_collation(&my_charset_utf8_turkish_uca_ci); + add_compiled_collation(&my_charset_utf8_czech_uca_ci); + add_compiled_collation(&my_charset_utf8_danish_uca_ci); + add_compiled_collation(&my_charset_utf8_lithuanian_uca_ci); + add_compiled_collation(&my_charset_utf8_slovak_uca_ci); + add_compiled_collation(&my_charset_utf8_spanish2_uca_ci); + add_compiled_collation(&my_charset_utf8_roman_uca_ci); + add_compiled_collation(&my_charset_utf8_persian_uca_ci); +#endif +#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..f0ac61ceed5 100644 --- a/mysys/charset.c +++ b/mysys/charset.c @@ -19,76 +19,337 @@ #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 +*/ -#define MAX_LINE 1024 +my_bool my_charset_same(CHARSET_INFO *cs1, CHARSET_INFO *cs2) +{ + return ((cs1 == cs2) || !strcmp(cs1->csname,cs2->csname)); +} -#define CTYPE_TABLE_SIZE 257 -#define TO_LOWER_TABLE_SIZE 256 -#define TO_UPPER_TABLE_SIZE 256 -#define SORT_ORDER_TABLE_SIZE 256 -struct simpleconfig_buf_st { - FILE *f; - char buf[MAX_LINE]; - char *p; -}; +static uint +get_collation_number_internal(const char *name) +{ + CHARSET_INFO **cs; + for (cs= all_charsets; + cs < all_charsets+array_elements(all_charsets)-1 ; + cs++) + { + if ( cs[0] && cs[0]->name && + !my_strcasecmp(&my_charset_latin1, cs[0]->name, name)) + return cs[0]->number; + } + return 0; +} -static uint num_from_csname(CS_ID **cs, const char *name) + +static my_bool init_state_maps(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() */ + 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_isspace(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; + + /* + 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); + } + + /* 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; } -static char *name_from_csnum(CS_ID **cs, uint number) + +static void simple_cs_init_functions(CHARSET_INFO *cs) { - CS_ID **c; - if(cs) - for (c = cs; *c; ++c) - if ((*c)->number == number) - return (*c)->name; - return (char*) "?"; /* 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 my_bool get_word(struct simpleconfig_buf_st *fb, char *buf) + + +static int cs_copy_data(CHARSET_INFO *to, CHARSET_INFO *from) { - char *endptr=fb->p; + to->number= from->number ? from->number : to->number; - for (;;) + 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) { - 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 (!(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; +} - while (*endptr && !isspace(*endptr)) - *buf++= *endptr++; - *buf=0; - fb->p = endptr; + +static my_bool simple_cs_is_full(CHARSET_INFO *cs) +{ + return ((cs->csname && cs->tab_to_uni && cs->ctype && cs->to_upper && + cs->to_lower) && + (cs->number && cs->name && + (cs->sort_order || (cs->state & MY_CS_BINSORT) ))); +} + + +static int add_collation(CHARSET_INFO *cs) +{ + if (cs->name && (cs->number || + (cs->number=get_collation_number_internal(cs->name)))) + { + 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") ) + { +#if defined(HAVE_CHARSET_ucs2) && defined(HAVE_UCA_COLLATIONS) + 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 + { + uchar *sort_order= all_charsets[cs->number]->sort_order; + 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; + + /* + Check if case sensitive sort order: A < a < B. + We need MY_CS_FLAG for regex library, and for + case sensitivity flag for 5.0 client protocol, + to support isCaseSensitive() method in JDBC driver + */ + if (sort_order && sort_order['A'] < sort_order['a'] && + sort_order['a'] < sort_order['B']) + all_charsets[cs->number]->state|= MY_CS_CSSORT; + } + } + 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; +} + + +#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, tmp_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) + goto error; + tmp_len=my_read(fd, buf, len, myflags); + my_close(fd,myflags); + if (tmp_len != len) + goto error; + + 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; + +error: + my_free(buf, myflags); + return TRUE; } 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 +363,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,227 +389,117 @@ 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 */ + if (!charset_initialized) { - my_init_dynamic_array(&cs_info_table, sizeof(CHARSET_INFO*), 16, 8); - error = read_charset_index(&available_charsets, myflags); + 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++) + { + 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; } - 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--) - { - 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; + init_available_charsets(MYF(0)); + return get_collation_number_internal(name); } -static void get_charset_conf_name(uint cs_number, char *buf) +uint get_charset_number(const char *charset_name, uint cs_flags) { - strxmov(get_charsets_dir(buf), - name_from_csnum(available_charsets, cs_number), ".conf", NullS); -} - - -static my_bool read_charset_file(uint cs_number, CHARSET_INFO *set, - myf myflags) -{ - struct simpleconfig_buf_st fb; - char buf[FN_REFLEN]; - my_bool result; - DBUG_ENTER("read_charset_file"); - DBUG_PRINT("enter",("cs_number: %d", cs_number)); - - if (cs_number <= 0) - DBUG_RETURN(TRUE); - - get_charset_conf_name(cs_number, buf); - DBUG_PRINT("info",("file name: %s", buf)); - - if ((fb.f = my_fopen(buf, O_RDONLY, myflags)) == NULL) - DBUG_RETURN(TRUE); - - fb.buf[0] = '\0'; /* Init for get_word */ - fb.p = fb.buf; - - result=FALSE; - if (fill_array(set->ctype, CTYPE_TABLE_SIZE, &fb) || - fill_array(set->to_lower, TO_LOWER_TABLE_SIZE, &fb) || - fill_array(set->to_upper, TO_UPPER_TABLE_SIZE, &fb) || - fill_array(set->sort_order, SORT_ORDER_TABLE_SIZE, &fb)) - result=TRUE; - - my_fclose(fb.f, MYF(0)); - DBUG_RETURN(result); + 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; + } + 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; + } pthread_mutex_unlock(&THR_LOCK_charset); return cs; } @@ -399,13 +508,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 +529,187 @@ 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"); - my_error(EE_UNKNOWN_CHARSET, MYF(ME_BELL), cs_name, index_file); + strmov(get_charsets_dir(index_file),MY_CHARSET_INDEX); + my_error(EE_UNKNOWN_COLLATION, 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)) + /* + If the next character appears to begin a multi-byte character, we + escape that first byte of that apparent multi-byte character. (The + character just looks like a multi-byte character -- if it were actually + a multi-byte character, it would have been passed through in the test + above.) + + Without this check, we can create a problem by converting an invalid + multi-byte character into a valid one. For example, 0xbf27 is not + a valid GBK character, but 0xbf5c is. (0x27 = ', 0x5c = \) + */ + if (use_mb_flag && (l= my_mbcharlen(charset_info, *from)) > 1) { - 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, " "); - } + *to++= '\\'; + *to++= *from; + continue; + } +#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; + *to= 0; + return (ulong) (to - to_start); } -/**************************************************************************** -* Code for debugging. -****************************************************************************/ +#ifdef BACKSLASH_MBTAIL +static CHARSET_INFO *fs_cset_cache= NULL; -static void _print_array(uint8 *data, uint size) +CHARSET_INFO *fs_character_set() { - uint i; - for (i = 0; i < size; ++i) + if (!fs_cset_cache) { - if (i == 0 || i % 16 == size % 16) printf(" "); - printf(" %02x", data[i]); - if ((i+1) % 16 == size % 16) printf("\n"); + char buf[10]= "cp"; + GetLocaleInfo(LOCALE_SYSTEM_DEFAULT, LOCALE_IDEFAULTANSICODEPAGE, + buf+2, sizeof(buf)-3); + /* + We cannot call get_charset_by_name here + because fs_character_set() is executed before + LOCK_THD_charset mutex initialization, which + is used inside get_charset_by_name. + As we're now interested in cp932 only, + let's just detect it using strcmp(). + */ + fs_cset_cache= !strcmp(buf, "cp932") ? + &my_charset_cp932_japanese_ci : &my_charset_bin; } + return fs_cset_cache; } +#endif + + +/** + @brief Find compatible character set with ctype. + + @param[in] original_cs Original character set + + @note + 128 my_charset_ucs2_general_uca ->192 my_charset_utf8_general_uca_ci + 129 my_charset_ucs2_icelandic_uca_ci ->193 my_charset_utf8_icelandic_uca_ci + 130 my_charset_ucs2_latvian_uca_ci ->194 my_charset_utf8_latvian_uca_ci + 131 my_charset_ucs2_romanian_uca_ci ->195 my_charset_utf8_romanian_uca_ci + 132 my_charset_ucs2_slovenian_uca_ci ->196 my_charset_utf8_slovenian_uca_ci + 133 my_charset_ucs2_polish_uca_ci ->197 my_charset_utf8_polish_uca_ci + 134 my_charset_ucs2_estonian_uca_ci ->198 my_charset_utf8_estonian_uca_ci + 135 my_charset_ucs2_spanish_uca_ci ->199 my_charset_utf8_spanish_uca_ci + 136 my_charset_ucs2_swedish_uca_ci ->200 my_charset_utf8_swedish_uca_ci + 137 my_charset_ucs2_turkish_uca_ci ->201 my_charset_utf8_turkish_uca_ci + 138 my_charset_ucs2_czech_uca_ci ->202 my_charset_utf8_czech_uca_ci + 139 my_charset_ucs2_danish_uca_ci ->203 my_charset_utf8_danish_uca_ci + 140 my_charset_ucs2_lithuanian_uca_ci->204 my_charset_utf8_lithuanian_uca_ci + 141 my_charset_ucs2_slovak_uca_ci ->205 my_charset_utf8_slovak_uca_ci + 142 my_charset_ucs2_spanish2_uca_ci ->206 my_charset_utf8_spanish2_uca_ci + 143 my_charset_ucs2_roman_uca_ci ->207 my_charset_utf8_roman_uca_ci + 144 my_charset_ucs2_persian_uca_ci ->208 my_charset_utf8_persian_uca_ci + + @return Compatible character set or NULL. +*/ -/* _print_csinfo is called from test_charset.c */ -void _print_csinfo(CHARSET_INFO *cs) +CHARSET_INFO *get_compatible_charset_with_ctype(CHARSET_INFO *original_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); + CHARSET_INFO *compatible_cs= 0; + DBUG_ENTER("get_compatible_charset_with_ctype"); + if (!strcmp(original_cs->csname, "ucs2") && + (compatible_cs= get_charset(original_cs->number + 64, MYF(0))) && + (!compatible_cs->ctype || + strcmp(original_cs->name + 4, compatible_cs->name + 4))) + compatible_cs= 0; + DBUG_RETURN(compatible_cs); } 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 911f516dad8..fadf6efbc5b 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 @@ -73,12 +73,42 @@ static int search_default_file(DYNAMIC_ARRAY *args,MEM_ROOT *alloc, static int search_default_file_with_ext(DYNAMIC_ARRAY *args, MEM_ROOT *alloc, const char *dir, const char *ext, const char *config_file, - TYPELIB *group); + TYPELIB *group, int recursion_level); static char *remove_end_comment(char *ptr); /* + Gets --defaults-file and --defaults-extra-file options from command line. + + SYNOPSIS + get_defaults_files() + argc Pointer to argc of original program + argv Pointer to argv of original program + defaults --defaults-file option + extra_defaults --defaults-extra-file option + + RETURN + defaults and extra_defaults will be set to appropriate items + of argv array, or to NULL if there are no such options +*/ + +void get_defaults_files(int argc, char **argv, + char **defaults, char **extra_defaults) +{ + *defaults=0; + *extra_defaults=0; + if (argc >= 2) + { + if (is_prefix(argv[1],"--defaults-file=")) + *defaults= argv[1]; + else if (is_prefix(argv[1],"--defaults-extra-file=")) + *extra_defaults= argv[1]; + } +} + + +/* Read options from configurations files SYNOPSIS @@ -111,10 +141,10 @@ static char *remove_end_comment(char *ptr); int load_defaults(const char *conf_file, const char **groups, - int *argc, char ***argv) + int *argc, char ***argv) { DYNAMIC_ARRAY args; - const char **dirs, *forced_default_file; + const char **dirs, *forced_default_file, *forced_extra_defaults; TYPELIB group; my_bool found_print_defaults=0; uint args_used=0; @@ -143,21 +173,15 @@ int load_defaults(const char *conf_file, const char **groups, DBUG_RETURN(0); } - /* Check if we want to force the use a specific default file */ - forced_default_file=0; - if (*argc >= 2) - { - if (is_prefix(argv[0][1],"--defaults-file=")) - { - forced_default_file=strchr(argv[0][1],'=')+1; - args_used++; - } - else if (is_prefix(argv[0][1],"--defaults-extra-file=")) - { - defaults_extra_file=strchr(argv[0][1],'=')+1; - args_used++; - } - } + get_defaults_files(*argc, *argv, + (char **)&forced_default_file, + (char **)&forced_extra_defaults); + if (forced_default_file) + forced_default_file= strchr(forced_default_file,'=')+1; + if (forced_extra_defaults) + defaults_extra_file= strchr(forced_extra_defaults,'=')+1; + + args_used+= (forced_default_file ? 1 : 0) + (forced_extra_defaults ? 1 : 0); group.count=0; group.name= "defaults"; @@ -170,10 +194,13 @@ int load_defaults(const char *conf_file, const char **groups, if (forced_default_file) { if ((error= search_default_file_with_ext(&args, &alloc, "", "", - forced_default_file, - &group))) + forced_default_file, + &group, 0)) < 0) + goto err; + if (error > 0) { - fprintf(stderr, "Error reading '%s' file.\n", forced_default_file); + fprintf(stderr, "Could not open required defaults file: %s\n", + forced_default_file); goto err; } } @@ -211,11 +238,8 @@ int load_defaults(const char *conf_file, const char **groups, else if (defaults_extra_file) { if (search_default_file(&args, &alloc, NullS, defaults_extra_file, - &group)) - { - fprintf(stderr, "Error reading '%s' file.\n", defaults_extra_file); + &group) < 0) goto err; /* Fatal error */ - } } } } @@ -229,9 +253,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; @@ -239,11 +263,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; @@ -265,6 +290,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 */ } @@ -286,7 +312,7 @@ static int search_default_file(DYNAMIC_ARRAY *args, MEM_ROOT *alloc, { int error; if ((error= search_default_file_with_ext(args, alloc, dir, *ext, - config_file, group)) < 0) + config_file, group, 0)) < 0) return error; } return 0; @@ -294,16 +320,68 @@ static int search_default_file(DYNAMIC_ARRAY *args, MEM_ROOT *alloc, /* + Skip over keyword and get argument after keyword + + SYNOPSIS + get_argument() + keyword Include directive keyword + kwlen Length of keyword + ptr Pointer to the keword in the line under process + line line number + + RETURN + 0 error + # Returns pointer to the argument after the keyword. +*/ + +static char *get_argument(const char *keyword, uint kwlen, + char *ptr, char *name, uint line) +{ + char *end; + + /* Skip over "include / includedir keyword" and following whitespace */ + + for (ptr+= kwlen - 1; + my_isspace(&my_charset_latin1, ptr[0]); + ptr++) + {} + + /* + Trim trailing whitespace from directory name + The -1 below is for the newline added by fgets() + Note that my_isspace() is true for \r and \n + */ + for (end= ptr + strlen(ptr) - 1; + my_isspace(&my_charset_latin1, *(end - 1)); + end--) + {} + end[0]= 0; /* Cut off end space */ + + /* Print error msg if there is nothing after !include* directive */ + if (end <= ptr) + { + fprintf(stderr, + "error: Wrong '!%s' directive in config file: %s at line %d\n", + keyword, name, line); + return 0; + } + return ptr; +} + + +/* Open a configuration file (if exists) and read given options from it - + SYNOPSIS search_default_file_with_ext() args Store pointer to found options here alloc Allocate strings in this object dir directory to read - config_file Name of configuration file ext Extension for configuration file + config_file Name of configuration file group groups to read + recursion_level the level of recursion, got while processing + "!include" or "!includedir" RETURN 0 Success @@ -315,12 +393,18 @@ static int search_default_file(DYNAMIC_ARRAY *args, MEM_ROOT *alloc, static int search_default_file_with_ext(DYNAMIC_ARRAY *args, MEM_ROOT *alloc, const char *dir, const char *ext, const char *config_file, - TYPELIB *group) + TYPELIB *group, int recursion_level) { - char name[FN_REFLEN+10],buff[4096],*ptr,*end,*value,*tmp; + char name[FN_REFLEN + 10], buff[4096], *ptr, *end, *value, *tmp, **tmp_ext; + static const char includedir_keyword[]= "includedir"; + static const char include_keyword[]= "include"; + const int max_recursion_level= 10; FILE *fp; - uint line=0; - my_bool read_values=0,found_group=0; + uint line= 0; + my_bool read_values= 0, found_group= 0; + uint i; + MY_DIR *search_dir; + FILEINFO *search_file; if ((dir ? strlen(dir) : 0 )+strlen(config_file) >= FN_REFLEN-3) return 0; /* Ignore wrong paths */ @@ -349,22 +433,101 @@ static int search_default_file_with_ext(DYNAMIC_ARRAY *args, MEM_ROOT *alloc, if ((stat_info.st_mode & S_IWOTH) && (stat_info.st_mode & S_IFMT) == S_IFREG) { - fprintf(stderr, "warning: World-writeable config file %s is ignored\n", + fprintf(stderr, "warning: World-writable config file %s is ignored\n", name); return 0; } } #endif - if (!(fp = my_fopen(fn_format(name,name,"","",4),O_RDONLY,MYF(0)))) + if (!(fp= my_fopen(fn_format(name, name, "", "", 4), O_RDONLY, MYF(0)))) return 0; /* Ignore wrong files */ - while (fgets(buff,sizeof(buff)-1,fp)) + while (fgets(buff, sizeof(buff) - 1, fp)) { 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; + + /* Configuration File Directives */ + if ((*ptr == '!')) + { + if (recursion_level >= max_recursion_level) + { + for (end= ptr + strlen(ptr) - 1; + my_isspace(&my_charset_latin1, *(end - 1)); + end--) + {} + end[0]= 0; + fprintf(stderr, + "Warning: skipping '%s' directive as maximum include" + "recursion level was reached in file %s at line %d\n", + ptr, name, line); + continue; + } + + /* skip over `!' and following whitespace */ + for (++ptr; my_isspace(&my_charset_latin1, ptr[0]); ptr++) + {} + + if ((!strncmp(ptr, includedir_keyword, + sizeof(includedir_keyword) - 1)) && + my_isspace(&my_charset_latin1, ptr[sizeof(includedir_keyword) - 1])) + { + if (!(ptr= get_argument(includedir_keyword, + sizeof(includedir_keyword), + ptr, name, line))) + goto err; + + if (!(search_dir= my_dir(ptr, MYF(MY_WME)))) + goto err; + + for (i= 0; i < (uint) search_dir->number_off_files; i++) + { + search_file= search_dir->dir_entry + i; + ext= fn_ext(search_file->name); + + /* check extension */ + for (tmp_ext= (char**) f_extensions; *tmp_ext; *tmp_ext++) + { + if (!strcmp(ext, *tmp_ext)) + break; + } + + if (*tmp_ext) + { + if (!(tmp= alloc_root(alloc, 2 + strlen(search_file->name) + + strlen(ptr)))) + goto err; + + fn_format(tmp, search_file->name, ptr, "", + MY_UNPACK_FILENAME | MY_SAFE_PATH); + + search_default_file_with_ext(args, alloc, "", "", tmp, group, + recursion_level + 1); + } + } + + my_dirend(search_dir); + } + else if ((!strncmp(ptr, include_keyword, sizeof(include_keyword) - 1)) && + my_isspace(&my_charset_latin1, ptr[sizeof(include_keyword)-1])) + { + if (!(ptr= get_argument(include_keyword, + sizeof(include_keyword), ptr, + name, line))) + goto err; + + search_default_file_with_ext(args, alloc, "", "", ptr, group, + recursion_level + 1); + } + + continue; + } + if (*ptr == '[') /* Group name */ { found_group=1; @@ -375,7 +538,7 @@ static int search_default_file_with_ext(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; @@ -392,8 +555,7 @@ static int search_default_file_with_ext(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))) @@ -406,18 +568,20 @@ static int search_default_file_with_ext(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; /* remove quotes around argument */ - if ((*value == '\"' || *value == '\'') && *value == value_end[-1]) + if ((*value == '\"' || *value == '\'') && /* First char is quote */ + (value + 1 < value_end ) && /* String is longer than 1 */ + *value == value_end[-1] ) /* First char is equal to last char */ { value++; value_end--; @@ -494,7 +658,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 e21609f6e94..05436c9a212 100644 --- a/mysys/errors.c +++ b/mysys/errors.c @@ -49,6 +49,7 @@ const char * NEAR globerrs[GLOBERRS]= "Can't create symlink '%s' pointing at '%s' (Error %d)", "Error on realpath() on '%s' (Error %d)", "Can't sync file '%s' to disk (Errcode: %d)", + "Collation '%s' is not a compiled collation and is not specified in the '%s' file", }; void init_glob_errs(void) @@ -83,11 +84,12 @@ 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)"; EE(EE_REALPATH)= "Error on realpath() on '%s' (Error %d)"; EE(EE_SYNC)= "Can't sync file '%s' to disk (Errcode: %d)"; + EE(EE_UNKNOWN_COLLATION)= "Collation '%s' is not a compiled collation and is not specified in the %s file"; } #endif diff --git a/mysys/hash.c b/mysys/hash.c index 3ee575a9c98..1ba80dc6fd3 100644 --- a/mysys/hash.c +++ b/mysys/hash.c @@ -29,54 +29,112 @@ #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 int hashcmp(const HASH *hash, HASH_LINK *pos, const byte *key, + uint length); +static uint calc_hash(const 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)); + DBUG_PRINT("enter",("hash: 0x%lx size: %d",hash,size)); hash->records=0; 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; hash->blength=1; - hash->current_record= NO_RECORD; /* For the future */ 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); } -void hash_free(HASH *hash) +/* + Call hash->free on all elements in hash. + + SYNOPSIS + hash_free_elements() + hash hash table + + NOTES: + Sets records to 0 +*/ + +static inline void hash_free_elements(HASH *hash) { - DBUG_ENTER("hash_free"); if (hash->free) { - uint i,records; HASH_LINK *data=dynamic_element(&hash->array,0,HASH_LINK*); - for (i=0,records=hash->records ; i < records ; i++) - (*hash->free)(data[i].data); - hash->free=0; + HASH_LINK *end= data + hash->records; + while (data < end) + (*hash->free)((data++)->data); } - delete_dynamic(&hash->array); hash->records=0; +} + + +/* + Free memory used by hash. + + SYNOPSIS + hash_free() + hash the hash to delete elements of + + NOTES: Hash can't be reused wuthing calling hash_init again. +*/ + +void hash_free(HASH *hash) +{ + DBUG_ENTER("hash_free"); + DBUG_PRINT("enter",("hash: 0x%lxd",hash)); + + hash_free_elements(hash); + hash->free= 0; + delete_dynamic(&hash->array); + DBUG_VOID_RETURN; +} + + +/* + Delete all elements from the hash (the hash itself is to be reused). + + SYNOPSIS + my_hash_reset() + hash the hash to delete elements of +*/ + +void my_hash_reset(HASH *hash) +{ + DBUG_ENTER("my_hash_reset"); + DBUG_PRINT("enter",("hash: 0x%lxd",hash)); + + hash_free_elements(hash); + reset_dynamic(&hash->array); + /* Set row pointers so that the hash can be reused at once */ + hash->blength= 1; DBUG_VOID_RETURN; } @@ -88,7 +146,8 @@ void hash_free(HASH *hash) */ static inline char* -hash_key(HASH *hash,const byte *record,uint *length,my_bool first) +hash_key(const HASH *hash, const byte *record, uint *length, + my_bool first) { if (hash->get_key) return (*hash->get_key)(record,length,first); @@ -104,110 +163,53 @@ static uint hash_mask(uint hashnr,uint buffmax,uint maxlength) return (hashnr & ((buffmax >> 1) -1)); } -static uint hash_rec_mask(HASH *hash,HASH_LINK *pos,uint buffmax, - uint maxlength) +static uint hash_rec_mask(const HASH *hash, HASH_LINK *pos, + uint buffmax, uint maxlength) { 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); -} - - /* 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); + return hash_mask(calc_hash(hash,key,length),buffmax,maxlength); } -#endif /* for compilers which can not handle inline */ +static #if !defined(__USLC__) && !defined(__sgi) -static inline +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); } - /* Search after a record based on a key */ - /* Sets info->current_ptr to found record */ +gptr hash_search(const HASH *hash, const byte *key, uint length) +{ + HASH_SEARCH_STATE state; + return hash_first(hash, key, length, &state); +} -gptr hash_search(HASH *hash,const byte *key,uint length) +/* + Search after a record based on a key + + NOTE + Assigns the number of the found record to HASH_SEARCH_STATE state +*/ + +gptr hash_first(const HASH *hash, const byte *key, uint length, + HASH_SEARCH_STATE *current_record) { HASH_LINK *pos; uint flag,idx; - DBUG_ENTER("hash_search"); + DBUG_ENTER("hash_first"); 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 { @@ -215,7 +217,7 @@ gptr hash_search(HASH *hash,const byte *key,uint length) if (!hashcmp(hash,pos,key,length)) { DBUG_PRINT("exit",("found key at %d",idx)); - hash->current_record= idx; + *current_record= idx; DBUG_RETURN (pos->data); } if (flag) @@ -227,31 +229,32 @@ gptr hash_search(HASH *hash,const byte *key,uint length) } while ((idx=pos->next) != NO_RECORD); } - hash->current_record= NO_RECORD; + *current_record= NO_RECORD; DBUG_RETURN(0); } /* Get next record with identical key */ /* Can only be called if previous calls was hash_search */ -gptr hash_next(HASH *hash,const byte *key,uint length) +gptr hash_next(const HASH *hash, const byte *key, uint length, + HASH_SEARCH_STATE *current_record) { HASH_LINK *pos; uint idx; - if (hash->current_record != NO_RECORD) + if (*current_record != NO_RECORD) { HASH_LINK *data=dynamic_element(&hash->array,0,HASH_LINK*); - for (idx=data[hash->current_record].next; idx != NO_RECORD ; idx=pos->next) + for (idx=data[*current_record].next; idx != NO_RECORD ; idx=pos->next) { pos=data+idx; if (!hashcmp(hash,pos,key,length)) { - hash->current_record= idx; + *current_record= idx; return pos->data; } } - hash->current_record=NO_RECORD; + *current_record= NO_RECORD; } return 0; } @@ -271,22 +274,40 @@ static void movelink(HASH_LINK *array,uint find,uint next_link,uint newlink) return; } - /* Compare a key in a record to a whole key. Return 0 if identical */ +/* + Compare a key in a record to a whole key. Return 0 if identical + + SYNOPSIS + hashcmp() + hash hash table + pos position of hash record to use in comparison + key key for comparison + length length of key + + NOTES: + If length is 0, comparison is done using the length of the + record being compared against. + + RETURN + < 0 key of record < key + = 0 key of record == key + > 0 key of record > key + */ -static int hashcmp(HASH *hash,HASH_LINK *pos,const byte *key,uint length) +static int hashcmp(const 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, rec_keylength)); } /* 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; @@ -300,7 +321,6 @@ my_bool hash_insert(HASH *info,const byte *record) if (!(empty=(HASH_LINK*) alloc_dynamic(&info->array))) return(TRUE); /* No more memory */ - info->current_record= NO_RECORD; data=dynamic_element(&info->array,0,HASH_LINK*); halfbuff= info->blength >> 1; @@ -443,7 +463,6 @@ my_bool hash_delete(HASH *hash,byte *record) } if ( --(hash->records) < hash->blength >> 1) hash->blength>>=1; - hash->current_record= NO_RECORD; lastpos=data+hash->records; /* Remove link to record */ @@ -519,7 +538,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); @@ -536,7 +555,6 @@ my_bool hash_update(HASH *hash,byte *record,byte *old_key,uint old_key_length) if ((idx=pos->next) == NO_RECORD) DBUG_RETURN(1); /* Not found in links */ } - hash->current_record= NO_RECORD; org_link= *pos; empty=idx; @@ -554,6 +572,25 @@ my_bool hash_update(HASH *hash,byte *record,byte *old_key,uint old_key_length) previous->next=pos->next; /* unlink pos */ /* Move data to correct position */ + if (new_index == empty) + { + /* + At this point record is unlinked from the old chain, thus it holds + random position. By the chance this position is equal to position + for the first element in the new chain. That means updated record + is the only record in the new chain. + */ + if (empty != idx) + { + /* + Record was moved while unlinking it from the old chain. + Copy data to a new position. + */ + data[empty]= org_link; + } + data[empty].next= NO_RECORD; + DBUG_RETURN(0); + } pos=data+new_index; new_pos_index=hash_rec_mask(hash,pos,blength,records); if (new_index != new_pos_index) @@ -586,10 +623,10 @@ byte *hash_element(HASH *hash,uint idx) isn't changed */ -void hash_replace(HASH *hash, uint idx, byte *new_row) +void hash_replace(HASH *hash, HASH_SEARCH_STATE *current_record, byte *new_row) { - if (idx != NO_RECORD) /* Safety */ - dynamic_element(&hash->array,idx,HASH_LINK*)->data=new_row; + if (*current_record != NO_RECORD) /* Safety */ + dynamic_element(&hash->array, *current_record, HASH_LINK*)->data= new_row; } @@ -627,7 +664,7 @@ my_bool hash_check(HASH *hash) if ((rec_link=hash_rec_mask(hash,hash_info,blength,records)) != i) { DBUG_PRINT("error", - ("Record in wrong link at %d: Start %d Record: %lx Record-link %d", idx,i,hash_info->data,rec_link)); + ("Record in wrong link at %d: Start %d Record: 0x%lx Record-link %d", idx,i,hash_info->data,rec_link)); error=1; } else diff --git a/mysys/list.c b/mysys/list.c index ac9e1b979a0..480c1080a45 100644 --- a/mysys/list.c +++ b/mysys/list.c @@ -28,7 +28,7 @@ LIST *list_add(LIST *root, LIST *element) { DBUG_ENTER("list_add"); - DBUG_PRINT("enter",("root: %lx element: %lx", root, element)); + DBUG_PRINT("enter",("root: 0x%lx element: %lx", root, element)); if (root) { if (root->prev) /* If add in mid of list */ @@ -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) @@ -109,7 +109,7 @@ int list_walk(LIST *list, list_walk_action action, gptr argument) { if ((error = (*action)(list->data,argument))) return error; - list=rest(list); + list=list_rest(list); } return 0; } 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..45bf4d56c31 100644 --- a/mysys/mf_dirname.c +++ b/mysys/mf_dirname.c @@ -22,6 +22,9 @@ uint dirname_length(const char *name) { register my_string pos,gpos; +#ifdef BASKSLASH_MBTAIL + CHARSET_INFO *fs= fs_character_set(); +#endif #ifdef FN_DEVCHAR if ((pos=(char*)strrchr(name,FN_DEVCHAR)) == 0) #endif @@ -29,12 +32,22 @@ uint dirname_length(const char *name) gpos= pos++; for ( ; *pos ; pos++) /* Find last FN_LIBCHAR */ + { +#ifdef BASKSLASH_MBTAIL + uint l; + if (use_mb(fs) && (l= my_ismbchar(fs, pos, pos + 3))) + { + pos+= l - 1; + continue; + } +#endif if (*pos == FN_LIBCHAR || *pos == '/' #ifdef FN_C_AFTER_DIR || *pos == FN_C_AFTER_DIR || *pos == FN_C_AFTER_DIR_2 #endif ) gpos=pos; + } return ((uint) (uint) (gpos+1-(char*) name)); } @@ -85,6 +98,9 @@ uint dirname_part(my_string to, const char *name) char *convert_dirname(char *to, const char *from, const char *from_end) { char *to_org=to; +#ifdef BACKSLASH_MBTAIL + CHARSET_INFO *fs= fs_character_set(); +#endif /* We use -2 here, becasue we need place for the last FN_LIBCHAR */ if (!from_end || (from_end - from) > FN_REFLEN-2) @@ -103,7 +119,22 @@ char *convert_dirname(char *to, const char *from, const char *from_end) *to++= FN_C_AFTER_DIR; #endif else - *to++= *from; + { +#ifdef BACKSLASH_MBTAIL + uint l; + if (use_mb(fs) && (l= my_ismbchar(fs, from, from + 3))) + { + memmove(to, from, l); + to+= l; + from+= l - 1; + to_org= to; /* Don't look inside mbchar */ + } + else +#endif + { + *to++= *from; + } + } } *to=0; } @@ -118,11 +149,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..d1ca1108d02 100644 --- a/mysys/mf_format.c +++ b/mysys/mf_format.c @@ -42,7 +42,7 @@ my_string fn_format(my_string to, const char *name, const char *dir, /* Use given directory */ convert_dirname(dev,dir,NullS); /* Fix to this OS */ } - else if ((flag & MY_RELATIVE_PATH) && !test_if_hard_path(name)) + else if ((flag & MY_RELATIVE_PATH) && !test_if_hard_path(dev)) { /* Put 'dir' before the given path */ strmake(buff,dev,sizeof(buff)-1); @@ -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 a7937da0cc2..a91002d3b4c 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 @@ -71,7 +70,6 @@ static void my_aiowait(my_aio_result *result); #define IO_ROUND_UP(X) (((X)+IO_SIZE-1) & ~(IO_SIZE-1)) #define IO_ROUND_DN(X) ( (X) & ~(IO_SIZE-1)) - /* Setup internal pointers inside IO_CACHE @@ -131,11 +129,29 @@ init_functions(IO_CACHE* info) setup_io_cache(info); } - /* - ** 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, @@ -144,24 +160,24 @@ int init_io_cache(IO_CACHE *info, File file, uint cachesize, uint min_cache; my_off_t end_of_file= ~(my_off_t) 0; DBUG_ENTER("init_io_cache"); - DBUG_PRINT("enter",("cache: %lx type: %d pos: %ld", + DBUG_PRINT("enter",("cache: 0x%lx type: %d pos: %ld", (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 */ @@ -183,11 +199,11 @@ int init_io_cache(IO_CACHE *info, File file, uint cachesize, if (type != READ_NET && type != WRITE_NET) { /* Retry allocating memory in smaller blocks until we get one */ + cachesize=(uint) ((ulong) (cachesize + min_cache-1) & + (ulong) ~(min_cache-1)); for (;;) { uint buffer_block; - cachesize=(uint) ((ulong) (cachesize + min_cache-1) & - (ulong) ~(min_cache-1)); if (cachesize < min_cache) cachesize = min_cache; buffer_block = cachesize; @@ -206,7 +222,8 @@ int init_io_cache(IO_CACHE *info, File file, uint cachesize, } if (cachesize == min_cache) DBUG_RETURN(2); /* Can't alloc cache */ - cachesize= (uint) ((long) cachesize*3/4); /* Try with less memory */ + /* Try with less memory */ + cachesize= (uint) ((ulong) cachesize*3/4 & (ulong)~(min_cache-1)); } } @@ -222,6 +239,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= @@ -232,6 +256,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); #ifdef HAVE_AIOWAIT if (use_async_io && ! my_disable_async_io) @@ -286,7 +311,7 @@ my_bool reinit_io_cache(IO_CACHE *info, enum cache_type type, pbool clear_cache) { DBUG_ENTER("reinit_io_cache"); - DBUG_PRINT("enter",("cache: %lx type: %d seek_offset: %lu clear_cache: %d", + DBUG_PRINT("enter",("cache: 0x%lx type: %d seek_offset: %lu clear_cache: %d", (ulong) info, type, (ulong) seek_offset, (int) clear_cache)); @@ -372,11 +397,31 @@ my_bool reinit_io_cache(IO_CACHE *info, enum cache_type type, /* - Read buffered. Returns 1 if can't read requested characters - This function is only called from the my_b_read() macro - when there isn't enough characters in the buffer to - satisfy the request. - Returns 0 we succeeded in reading all data + Read buffered. + + SYNOPSIS + _my_b_read() + info IO_CACHE pointer + Buffer Buffer to retrieve count bytes from file + Count Number of bytes to read into Buffer + + NOTE + This function is only called from the my_b_read() macro when there + isn't enough characters in the buffer to satisfy the request. + + WARNING + + When changing this function, be careful with handling file offsets + (end-of_file, pos_in_file). Do not cast them to possibly smaller + types than my_off_t unless you can be sure that their value fits. + Same applies to differences of file offsets. + + When changing this function, check _my_b_read_r(). It might need the + same change. + + RETURN + 0 we succeeded in reading all data + 1 Error: can't read requested characters */ int _my_b_read(register IO_CACHE *info, byte *Buffer, uint Count) @@ -395,16 +440,29 @@ int _my_b_read(register IO_CACHE *info, byte *Buffer, uint Count) /* pos_in_file always point on where info->buffer was read */ pos_in_file=info->pos_in_file+(uint) (info->read_end - info->buffer); + + /* + Whenever a function which operates on IO_CACHE flushes/writes + some part of the IO_CACHE to disk it will set the property + "seek_not_done" to indicate this to other functions operating + on the IO_CACHE. + */ if (info->seek_not_done) - { /* File touched, do seek */ - VOID(my_seek(info->file,pos_in_file,MY_SEEK_SET,MYF(0))); + { + if (my_seek(info->file,pos_in_file,MY_SEEK_SET,MYF(0)) + == MY_FILEPOS_ERROR) + { + info->error= -1; + DBUG_RETURN(1); + } info->seek_not_done=0; } + diff_length=(uint) (pos_in_file & (IO_SIZE-1)); if (Count >= (uint) (IO_SIZE+(IO_SIZE-diff_length))) { /* Fill first intern buffer */ uint read_length; - if (info->end_of_file == pos_in_file) + if (info->end_of_file <= pos_in_file) { /* End of file */ info->error=(int) left_length; DBUG_RETURN(1); @@ -455,152 +513,578 @@ int _my_b_read(register IO_CACHE *info, byte *Buffer, uint Count) DBUG_RETURN(0); } + #ifdef THREAD -/* Prepare IO_CACHE for shared use */ -void init_io_cache_share(IO_CACHE *info, IO_CACHE_SHARE *s, uint num_threads) +/* + Prepare IO_CACHE for shared use. + + SYNOPSIS + init_io_cache_share() + read_cache A read cache. This will be copied for + every thread after setup. + cshare The share. + write_cache If non-NULL a write cache that is to be + synchronized with the read caches. + num_threads Number of threads sharing the cache + including the write thread if any. + + DESCRIPTION + + The shared cache is used so: One IO_CACHE is initialized with + init_io_cache(). This includes the allocation of a buffer. Then a + share is allocated and init_io_cache_share() is called with the io + cache and the share. Then the io cache is copied for each thread. So + every thread has its own copy of IO_CACHE. But the allocated buffer + is shared because cache->buffer is the same for all caches. + + One thread reads data from the file into the buffer. All threads + read from the buffer, but every thread maintains its own set of + pointers into the buffer. When all threads have used up the buffer + contents, one of the threads reads the next block of data into the + buffer. To accomplish this, each thread enters the cache lock before + accessing the buffer. They wait in lock_io_cache() until all threads + joined the lock. The last thread entering the lock is in charge of + reading from file to buffer. It wakes all threads when done. + + Synchronizing a write cache to the read caches works so: Whenever + the write buffer needs a flush, the write thread enters the lock and + waits for all other threads to enter the lock too. They do this when + they have used up the read buffer. When all threads are in the lock, + the write thread copies the write buffer to the read buffer and + wakes all threads. + + share->running_threads is the number of threads not being in the + cache lock. When entering lock_io_cache() the number is decreased. + When the thread that fills the buffer enters unlock_io_cache() the + number is reset to the number of threads. The condition + running_threads == 0 means that all threads are in the lock. Bumping + up the number to the full count is non-intuitive. But increasing the + number by one for each thread that leaves the lock could lead to a + solo run of one thread. The last thread to join a lock reads from + file to buffer, wakes the other threads, processes the data in the + cache and enters the lock again. If no other thread left the lock + meanwhile, it would think it's the last one again and read the next + block... + + The share has copies of 'error', 'buffer', 'read_end', and + 'pos_in_file' from the thread that filled the buffer. We may not be + able to access this information directly from its cache because the + thread may be removed from the share before the variables could be + copied by all other threads. Or, if a write buffer is synchronized, + it would change its 'pos_in_file' after waking the other threads, + possibly before they could copy its value. + + However, the 'buffer' variable in the share is for a synchronized + write cache. It needs to know where to put the data. Otherwise it + would need access to the read cache of one of the threads that is + not yet removed from the share. + + RETURN + void +*/ + +void init_io_cache_share(IO_CACHE *read_cache, IO_CACHE_SHARE *cshare, + IO_CACHE *write_cache, uint num_threads) { - DBUG_ASSERT(info->type == READ_CACHE); - pthread_mutex_init(&s->mutex, MY_MUTEX_INIT_FAST); - pthread_cond_init (&s->cond, 0); - s->total=s->count=num_threads-1; - s->active=0; - info->share=s; - info->read_function=_my_b_read_r; - info->current_pos= info->current_end= 0; + DBUG_ENTER("init_io_cache_share"); + DBUG_PRINT("io_cache_share", ("read_cache: 0x%lx share: 0x%lx " + "write_cache: 0x%lx threads: %u", + read_cache, cshare, write_cache, num_threads)); + + DBUG_ASSERT(num_threads > 1); + DBUG_ASSERT(read_cache->type == READ_CACHE); + DBUG_ASSERT(!write_cache || (write_cache->type == WRITE_CACHE)); + + pthread_mutex_init(&cshare->mutex, MY_MUTEX_INIT_FAST); + pthread_cond_init(&cshare->cond, 0); + pthread_cond_init(&cshare->cond_writer, 0); + + cshare->running_threads= num_threads; + cshare->total_threads= num_threads; + cshare->error= 0; /* Initialize. */ + cshare->buffer= read_cache->buffer; + cshare->read_end= NULL; /* See function comment of lock_io_cache(). */ + cshare->pos_in_file= 0; /* See function comment of lock_io_cache(). */ + cshare->source_cache= write_cache; /* Can be NULL. */ + + read_cache->share= cshare; + read_cache->read_function= _my_b_read_r; + read_cache->current_pos= NULL; + read_cache->current_end= NULL; + + if (write_cache) + write_cache->share= cshare; + + DBUG_VOID_RETURN; } + /* - Remove a thread from shared access to IO_CACHE - Every thread should do that on exit for not - to deadlock other threads + Remove a thread from shared access to IO_CACHE. + + SYNOPSIS + remove_io_thread() + cache The IO_CACHE to be removed from the share. + + NOTE + + Every thread must do that on exit for not to deadlock other threads. + + The last thread destroys the pthread resources. + + A writer flushes its cache first. + + RETURN + void */ -void remove_io_thread(IO_CACHE *info) + +void remove_io_thread(IO_CACHE *cache) { - IO_CACHE_SHARE *s=info->share; + IO_CACHE_SHARE *cshare= cache->share; + uint total; + DBUG_ENTER("remove_io_thread"); - pthread_mutex_lock(&s->mutex); - s->total--; - if (! s->count--) - pthread_cond_signal(&s->cond); - pthread_mutex_unlock(&s->mutex); + /* If the writer goes, it needs to flush the write cache. */ + if (cache == cshare->source_cache) + flush_io_cache(cache); + + pthread_mutex_lock(&cshare->mutex); + DBUG_PRINT("io_cache_share", ("%s: 0x%lx", + (cache == cshare->source_cache) ? + "writer" : "reader", cache)); + + /* Remove from share. */ + total= --cshare->total_threads; + DBUG_PRINT("io_cache_share", ("remaining threads: %u", total)); + + /* Detach from share. */ + cache->share= NULL; + + /* If the writer goes, let the readers know. */ + if (cache == cshare->source_cache) + { + DBUG_PRINT("io_cache_share", ("writer leaves")); + cshare->source_cache= NULL; + } + + /* If all threads are waiting for me to join the lock, wake them. */ + if (!--cshare->running_threads) + { + DBUG_PRINT("io_cache_share", ("the last running thread leaves, wake all")); + pthread_cond_signal(&cshare->cond_writer); + pthread_cond_broadcast(&cshare->cond); + } + + pthread_mutex_unlock(&cshare->mutex); + + if (!total) + { + DBUG_PRINT("io_cache_share", ("last thread removed, destroy share")); + pthread_cond_destroy (&cshare->cond_writer); + pthread_cond_destroy (&cshare->cond); + pthread_mutex_destroy(&cshare->mutex); + } + + DBUG_VOID_RETURN; } -static int lock_io_cache(IO_CACHE *info, my_off_t pos) -{ - int total; - IO_CACHE_SHARE *s=info->share; - pthread_mutex_lock(&s->mutex); - if (!s->count) +/* + Lock IO cache and wait for all other threads to join. + + SYNOPSIS + lock_io_cache() + cache The cache of the thread entering the lock. + pos File position of the block to read. + Unused for the write thread. + + DESCRIPTION + + Wait for all threads to finish with the current buffer. We want + all threads to proceed in concert. The last thread to join + lock_io_cache() will read the block from file and all threads start + to use it. Then they will join again for reading the next block. + + The waiting threads detect a fresh buffer by comparing + cshare->pos_in_file with the position they want to process next. + Since the first block may start at position 0, we take + cshare->read_end as an additional condition. This variable is + initialized to NULL and will be set after a block of data is written + to the buffer. + + RETURN + 1 OK, lock in place, go ahead and read. + 0 OK, unlocked, another thread did the read. +*/ + +static int lock_io_cache(IO_CACHE *cache, my_off_t pos) +{ + IO_CACHE_SHARE *cshare= cache->share; + DBUG_ENTER("lock_io_cache"); + + /* Enter the lock. */ + pthread_mutex_lock(&cshare->mutex); + cshare->running_threads--; + DBUG_PRINT("io_cache_share", ("%s: 0x%lx pos: %lu running: %u", + (cache == cshare->source_cache) ? + "writer" : "reader", cache, (ulong) pos, + cshare->running_threads)); + + if (cshare->source_cache) { - s->count=s->total; - return 1; + /* A write cache is synchronized to the read caches. */ + + if (cache == cshare->source_cache) + { + /* The writer waits until all readers are here. */ + while (cshare->running_threads) + { + DBUG_PRINT("io_cache_share", ("writer waits in lock")); + pthread_cond_wait(&cshare->cond_writer, &cshare->mutex); + } + DBUG_PRINT("io_cache_share", ("writer awoke, going to copy")); + + /* Stay locked. Leave the lock later by unlock_io_cache(). */ + DBUG_RETURN(1); + } + + /* The last thread wakes the writer. */ + if (!cshare->running_threads) + { + DBUG_PRINT("io_cache_share", ("waking writer")); + pthread_cond_signal(&cshare->cond_writer); + } + + /* + Readers wait until the data is copied from the writer. Another + reason to stop waiting is the removal of the write thread. If this + happens, we leave the lock with old data in the buffer. + */ + while ((!cshare->read_end || (cshare->pos_in_file < pos)) && + cshare->source_cache) + { + DBUG_PRINT("io_cache_share", ("reader waits in lock")); + pthread_cond_wait(&cshare->cond, &cshare->mutex); + } + + /* + If the writer was removed from the share while this thread was + asleep, we need to simulate an EOF condition. The writer cannot + reset the share variables as they might still be in use by readers + of the last block. When we awake here then because the last + joining thread signalled us. If the writer is not the last, it + will not signal. So it is safe to clear the buffer here. + */ + if (!cshare->read_end || (cshare->pos_in_file < pos)) + { + DBUG_PRINT("io_cache_share", ("reader found writer removed. EOF")); + cshare->read_end= cshare->buffer; /* Empty buffer. */ + cshare->error= 0; /* EOF is not an error. */ + } } + else + { + /* + There are read caches only. The last thread arriving in + lock_io_cache() continues with a locked cache and reads the block. + */ + if (!cshare->running_threads) + { + DBUG_PRINT("io_cache_share", ("last thread joined, going to read")); + /* Stay locked. Leave the lock later by unlock_io_cache(). */ + DBUG_RETURN(1); + } - total=s->total; - s->count--; - while (!s->active || s->active->pos_in_file < pos) - pthread_cond_wait(&s->cond, &s->mutex); + /* + All other threads wait until the requested block is read by the + last thread arriving. Another reason to stop waiting is the + removal of a thread. If this leads to all threads being in the + lock, we have to continue also. The first of the awaken threads + will then do the read. + */ + while ((!cshare->read_end || (cshare->pos_in_file < pos)) && + cshare->running_threads) + { + DBUG_PRINT("io_cache_share", ("reader waits in lock")); + pthread_cond_wait(&cshare->cond, &cshare->mutex); + } - if (s->total < total && - (!s->active || s->active->pos_in_file < pos)) - return 1; + /* If the block is not yet read, continue with a locked cache and read. */ + if (!cshare->read_end || (cshare->pos_in_file < pos)) + { + DBUG_PRINT("io_cache_share", ("reader awoke, going to read")); + /* Stay locked. Leave the lock later by unlock_io_cache(). */ + DBUG_RETURN(1); + } - pthread_mutex_unlock(&s->mutex); - return 0; + /* Another thread did read the block already. */ + } + DBUG_PRINT("io_cache_share", ("reader awoke, going to process %u bytes", + cshare->read_end ? (uint) + (cshare->read_end - cshare->buffer) : 0)); + + /* + Leave the lock. Do not call unlock_io_cache() later. The thread that + filled the buffer did this and marked all threads as running. + */ + pthread_mutex_unlock(&cshare->mutex); + DBUG_RETURN(0); } -static void unlock_io_cache(IO_CACHE *info) + +/* + Unlock IO cache. + + SYNOPSIS + unlock_io_cache() + cache The cache of the thread leaving the lock. + + NOTE + This is called by the thread that filled the buffer. It marks all + threads as running and awakes them. This must not be done by any + other thread. + + Do not signal cond_writer. Either there is no writer or the writer + is the only one who can call this function. + + The reason for resetting running_threads to total_threads before + waking all other threads is that it could be possible that this + thread is so fast with processing the buffer that it enters the lock + before even one other thread has left it. If every awoken thread + would increase running_threads by one, this thread could think that + he is again the last to join and would not wait for the other + threads to process the data. + + RETURN + void +*/ + +static void unlock_io_cache(IO_CACHE *cache) { - pthread_cond_broadcast(&info->share->cond); - pthread_mutex_unlock(&info->share->mutex); + IO_CACHE_SHARE *cshare= cache->share; + DBUG_ENTER("unlock_io_cache"); + DBUG_PRINT("io_cache_share", ("%s: 0x%lx pos: %lu running: %u", + (cache == cshare->source_cache) ? + "writer" : "reader", + cache, (ulong) cshare->pos_in_file, + cshare->total_threads)); + + cshare->running_threads= cshare->total_threads; + pthread_cond_broadcast(&cshare->cond); + pthread_mutex_unlock(&cshare->mutex); + DBUG_VOID_RETURN; } /* Read from IO_CACHE when it is shared between several threads. - It works as follows: when a thread tries to read from a file - (that is, after using all the data from the (shared) buffer), - it just hangs on lock_io_cache(), wating for other threads. - When the very last thread attempts a read, lock_io_cache() - returns 1, the thread does actual IO and unlock_io_cache(), - which signals all the waiting threads that data is in the buffer. + + SYNOPSIS + _my_b_read_r() + cache IO_CACHE pointer + Buffer Buffer to retrieve count bytes from file + Count Number of bytes to read into Buffer + + NOTE + This function is only called from the my_b_read() macro when there + isn't enough characters in the buffer to satisfy the request. + + IMPLEMENTATION + + It works as follows: when a thread tries to read from a file (that + is, after using all the data from the (shared) buffer), it just + hangs on lock_io_cache(), waiting for other threads. When the very + last thread attempts a read, lock_io_cache() returns 1, the thread + does actual IO and unlock_io_cache(), which signals all the waiting + threads that data is in the buffer. + + WARNING + + When changing this function, be careful with handling file offsets + (end-of_file, pos_in_file). Do not cast them to possibly smaller + types than my_off_t unless you can be sure that their value fits. + Same applies to differences of file offsets. (Bug #11527) + + When changing this function, check _my_b_read(). It might need the + same change. + + RETURN + 0 we succeeded in reading all data + 1 Error: can't read requested characters */ -int _my_b_read_r(register IO_CACHE *info, byte *Buffer, uint Count) +int _my_b_read_r(register IO_CACHE *cache, byte *Buffer, uint Count) { my_off_t pos_in_file; - uint length,diff_length,read_len; + uint length, diff_length, left_length; + IO_CACHE_SHARE *cshare= cache->share; DBUG_ENTER("_my_b_read_r"); - if ((read_len=(uint) (info->read_end-info->read_pos))) + if ((left_length= (uint) (cache->read_end - cache->read_pos))) { - DBUG_ASSERT(Count >= read_len); /* User is not using my_b_read() */ - memcpy(Buffer,info->read_pos, (size_t) (read_len)); - Buffer+=read_len; - Count-=read_len; + DBUG_ASSERT(Count >= left_length); /* User is not using my_b_read() */ + memcpy(Buffer, cache->read_pos, (size_t) (left_length)); + Buffer+= left_length; + Count-= left_length; } while (Count) { int cnt, len; - pos_in_file= info->pos_in_file + (uint)(info->read_end - info->buffer); + pos_in_file= cache->pos_in_file + (cache->read_end - cache->buffer); diff_length= (uint) (pos_in_file & (IO_SIZE-1)); length=IO_ROUND_UP(Count+diff_length)-diff_length; - length=(length <= info->read_length) ? - length + IO_ROUND_DN(info->read_length - length) : - length - IO_ROUND_UP(length - info->read_length) ; - if (info->type != READ_FIFO && - (length > (uint) (info->end_of_file - pos_in_file))) - length= (uint) (info->end_of_file - pos_in_file); + length= ((length <= cache->read_length) ? + length + IO_ROUND_DN(cache->read_length - length) : + length - IO_ROUND_UP(length - cache->read_length)); + if (cache->type != READ_FIFO && + (length > (cache->end_of_file - pos_in_file))) + length= (uint) (cache->end_of_file - pos_in_file); if (length == 0) { - info->error=(int) read_len; + cache->error= (int) left_length; DBUG_RETURN(1); } - if (lock_io_cache(info, pos_in_file)) + if (lock_io_cache(cache, pos_in_file)) { - info->share->active=info; - if (info->seek_not_done) /* File touched, do seek */ - VOID(my_seek(info->file,pos_in_file,MY_SEEK_SET,MYF(0))); - len=(int)my_read(info->file,info->buffer, length, info->myflags); - info->read_end=info->buffer + (len == -1 ? 0 : len); - info->error=(len == (int)length ? 0 : len); - info->pos_in_file=pos_in_file; - unlock_io_cache(info); + /* With a synchronized write/read cache we won't come here... */ + DBUG_ASSERT(!cshare->source_cache); + /* + ... unless the writer has gone before this thread entered the + lock. Simulate EOF in this case. It can be distinguished by + cache->file. + */ + if (cache->file < 0) + len= 0; + else + { + /* + Whenever a function which operates on IO_CACHE flushes/writes + some part of the IO_CACHE to disk it will set the property + "seek_not_done" to indicate this to other functions operating + on the IO_CACHE. + */ + if (cache->seek_not_done) + { + if (my_seek(cache->file,pos_in_file,MY_SEEK_SET,MYF(0)) + == MY_FILEPOS_ERROR) + { + cache->error= -1; + unlock_io_cache(cache); + DBUG_RETURN(1); + } + } + len= (int) my_read(cache->file, cache->buffer, length, cache->myflags); + } + DBUG_PRINT("io_cache_share", ("read %d bytes", len)); + + cache->read_end= cache->buffer + (len == -1 ? 0 : len); + cache->error= (len == (int)length ? 0 : len); + cache->pos_in_file= pos_in_file; + + /* Copy important values to the share. */ + cshare->error= cache->error; + cshare->read_end= cache->read_end; + cshare->pos_in_file= pos_in_file; + + /* Mark all threads as running and wake them. */ + unlock_io_cache(cache); } else { - info->error= info->share->active->error; - info->read_end= info->share->active->read_end; - info->pos_in_file= info->share->active->pos_in_file; - len= (info->error == -1 ? -1 : info->read_end-info->buffer); + /* + With a synchronized write/read cache readers always come here. + Copy important values from the share. + */ + cache->error= cshare->error; + cache->read_end= cshare->read_end; + cache->pos_in_file= cshare->pos_in_file; + + len= ((cache->error == -1) ? -1 : cache->read_end - cache->buffer); } - info->read_pos=info->buffer; - info->seek_not_done=0; + cache->read_pos= cache->buffer; + cache->seek_not_done= 0; if (len <= 0) { - info->error=(int) read_len; + DBUG_PRINT("io_cache_share", ("reader error. len %d left %u", + len, left_length)); + cache->error= (int) left_length; DBUG_RETURN(1); } - cnt=((uint) len > Count) ? (int) Count : len; - memcpy(Buffer,info->read_pos, (size_t)cnt); - Count -=cnt; - Buffer+=cnt; - read_len+=cnt; - info->read_pos+=cnt; + cnt= ((uint) len > Count) ? (int) Count : len; + memcpy(Buffer, cache->read_pos, (size_t) cnt); + Count -= cnt; + Buffer+= cnt; + left_length+= cnt; + cache->read_pos+= cnt; } DBUG_RETURN(0); } -#endif /* - Do sequential read from the SEQ_READ_APPEND cache - we do this in three stages: + Copy data from write cache to read cache. + + SYNOPSIS + copy_to_read_buffer() + write_cache The write cache. + write_buffer The source of data, mostly the cache buffer. + write_length The number of bytes to copy. + + NOTE + The write thread will wait for all read threads to join the cache + lock. Then it copies the data over and wakes the read threads. + + RETURN + void +*/ + +static void copy_to_read_buffer(IO_CACHE *write_cache, + const byte *write_buffer, uint write_length) +{ + IO_CACHE_SHARE *cshare= write_cache->share; + + DBUG_ASSERT(cshare->source_cache == write_cache); + /* + write_length is usually less or equal to buffer_length. + It can be bigger if _my_b_write() is called with a big length. + */ + while (write_length) + { + uint copy_length= min(write_length, write_cache->buffer_length); + int __attribute__((unused)) rc; + + rc= lock_io_cache(write_cache, write_cache->pos_in_file); + /* The writing thread does always have the lock when it awakes. */ + DBUG_ASSERT(rc); + + memcpy(cshare->buffer, write_buffer, copy_length); + + cshare->error= 0; + cshare->read_end= cshare->buffer + copy_length; + cshare->pos_in_file= write_cache->pos_in_file; + + /* Mark all threads as running and wake them. */ + unlock_io_cache(write_cache); + + write_buffer+= copy_length; + write_length-= copy_length; + } +} +#endif /*THREAD*/ + + +/* + Do sequential read from the SEQ_READ_APPEND cache. + + We do this in three stages: - first read from info->buffer - then if there are still data to read, try the file descriptor - afterwards, if there are still data to read, try append buffer + + RETURNS + 0 Success + 1 Failed to read */ int _my_b_seq_read(register IO_CACHE *info, byte *Buffer, uint Count) @@ -628,7 +1112,13 @@ int _my_b_seq_read(register IO_CACHE *info, byte *Buffer, uint Count) With read-append cache we must always do a seek before we read, because the write could have moved the file pointer astray */ - VOID(my_seek(info->file,pos_in_file,MY_SEEK_SET,MYF(0))); + if (my_seek(info->file,pos_in_file,MY_SEEK_SET,MYF(0)) + == MY_FILEPOS_ERROR) + { + info->error= -1; + unlock_append_buffer(info); + return (1); + } info->seek_not_done=0; diff_length=(uint) (pos_in_file & (IO_SIZE-1)); @@ -744,6 +1234,21 @@ read_append_buffer: #ifdef HAVE_AIOWAIT +/* + Read from the IO_CACHE into a buffer and feed asynchronously + from disk when needed. + + SYNOPSIS + _my_b_async_read() + info IO_CACHE pointer + Buffer Buffer to retrieve count bytes from file + Count Number of bytes to read into Buffer + + RETURN VALUE + -1 An error has occurred; my_errno is set. + 0 Success + 1 An error has occurred; IO_CACHE to error state. +*/ int _my_b_async_read(register IO_CACHE *info, byte *Buffer, uint Count) { uint length,read_length,diff_length,left_length,use_length,org_Count; @@ -795,7 +1300,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 @@ -826,7 +1331,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) @@ -834,13 +1339,20 @@ int _my_b_async_read(register IO_CACHE *info, byte *Buffer, uint Count) info->error=(int) (read_length+left_length); return 1; } - VOID(my_seek(info->file,next_pos_in_file,MY_SEEK_SET,MYF(0))); + + if (my_seek(info->file,next_pos_in_file,MY_SEEK_SET,MYF(0)) + == MY_FILEPOS_ERROR) + { + info->error= -1; + return (1); + } + read_length=IO_SIZE*2- (uint) (next_pos_in_file & (IO_SIZE-1)); if (Count < read_length) { /* Small block, read to cache */ if ((read_length=my_read(info->file,info->request_pos, read_length, info->myflags)) == (uint) -1) - return info->error= -1; + return info->error= -1; use_length=min(Count,read_length); memcpy(Buffer,info->request_pos,(size_t) use_length); info->read_pos=info->request_pos+Count; @@ -889,7 +1401,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)); @@ -927,7 +1439,15 @@ int _my_b_get(IO_CACHE *info) return (int) (uchar) buff; } - /* Returns != 0 if error on write */ +/* + Write a byte buffer to IO_CACHE and flush to disk + if IO_CACHE is full. + + RETURN VALUE + 1 On error on write + 0 On success + -1 On error; my_errno contains error code. +*/ int _my_b_write(register IO_CACHE *info, const byte *Buffer, uint Count) { @@ -944,18 +1464,46 @@ int _my_b_write(register IO_CACHE *info, const byte *Buffer, uint Count) Buffer+=rest_length; Count-=rest_length; info->write_pos+=rest_length; + if (flush_io_cache(info)) return 1; if (Count >= IO_SIZE) { /* Fill first intern buffer */ length=Count & (uint) ~(IO_SIZE-1); if (info->seek_not_done) - { /* File touched, do seek */ - VOID(my_seek(info->file,info->pos_in_file,MY_SEEK_SET,MYF(0))); + { + /* + Whenever a function which operates on IO_CACHE flushes/writes + some part of the IO_CACHE to disk it will set the property + "seek_not_done" to indicate this to other functions operating + on the IO_CACHE. + */ + if (my_seek(info->file,info->pos_in_file,MY_SEEK_SET,MYF(0))) + { + info->error= -1; + return (1); + } info->seek_not_done=0; } if (my_write(info->file,Buffer,(uint) length,info->myflags | MY_NABP)) return info->error= -1; + +#ifdef THREAD + /* + In case of a shared I/O cache with a writer we normally do direct + write cache to read cache copy. Simulate this here by direct + caller buffer to read cache copy. Do it after the write so that + the cache readers actions on the flushed part can go in parallel + with the write of the extra stuff. copy_to_read_buffer() + synchronizes writer and readers so that after this call the + readers can act on the extra stuff while the writer can go ahead + and prepare the next output. copy_to_read_buffer() relies on + info->pos_in_file. + */ + if (info->share) + copy_to_read_buffer(info, Buffer, length); +#endif + Count-=length; Buffer+=length; info->pos_in_file+=length; @@ -976,6 +1524,14 @@ int my_b_append(register IO_CACHE *info, const byte *Buffer, uint Count) { uint rest_length,length; +#ifdef THREAD + /* + Assert that we cannot come here with a shared cache. If we do one + day, we might need to add a call to copy_to_read_buffer(). + */ + DBUG_ASSERT(!info->share); +#endif + lock_append_buffer(info); rest_length=(uint) (info->write_end - info->write_pos); if (Count <= rest_length) @@ -1036,6 +1592,14 @@ int my_block_write(register IO_CACHE *info, const byte *Buffer, uint Count, uint length; int error=0; +#ifdef THREAD + /* + Assert that we cannot come here with a shared cache. If we do one + day, we might need to add a call to copy_to_read_buffer(). + */ + DBUG_ASSERT(!info->share); +#endif + if (pos < info->pos_in_file) { /* Of no overlap, write everything without buffering */ @@ -1112,6 +1676,17 @@ int my_b_flush_io_cache(IO_CACHE *info, int need_append_buffer_lock) if ((length=(uint) (info->write_pos - info->write_buffer))) { +#ifdef THREAD + /* + In case of a shared I/O cache with a writer we do direct write + cache to read cache copy. Do it before the write here so that + the readers can work in parallel with the write. + copy_to_read_buffer() relies on info->pos_in_file. + */ + if (info->share) + copy_to_read_buffer(info, info->write_buffer, length); +#endif + pos_in_file=info->pos_in_file; /* If we have append cache, we always open the file with @@ -1149,6 +1724,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); } @@ -1164,6 +1740,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) { @@ -1173,20 +1765,17 @@ int end_io_cache(IO_CACHE *info) #ifdef THREAD /* - if IO_CACHE is shared between several threads, only one - thread needs to call end_io_cache() - just as init_io_cache() - should be called only once and then memcopy'ed + Every thread must call remove_io_thread(). The last one destroys + the share elements. */ - if (info->share) - { - pthread_cond_destroy (&info->share->cond); - pthread_mutex_destroy(&info->share->mutex); - info->share=0; - } + DBUG_ASSERT(!info->share || !info->share->total_threads); #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 0f8eb7e6f65..88b5051c52b 100644 --- a/mysys/mf_keycache.c +++ b/mysys/mf_keycache.c @@ -15,865 +15,2772 @@ 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; -ulonglong _my_cache_w_requests, _my_cache_write; -ulonglong _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"); \ + (void) setvbuf(keycache_debug_log, NULL, _IOLBF, BUFSIZ); \ + } + +#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= 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 - /* Init of disk_buffert */ - /* Returns blocks in use */ - /* ARGSUSED */ +#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); +#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 +#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); +} -int init_key_cache(ulong use_mem) + +/* + 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. + + 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! + + 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)); + keycache->block_mem= 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: 0x%lx hash_entries: %d\ + hash_root: 0x%lx hash_links: %d hash_link_root: 0x%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 + 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(KEY_CACHE *keycache, uint key_cache_block_size, + ulong use_mem, uint division_limit, + uint age_threshold) +{ + 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) + { + 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); + } - RETURN VALUES - 0 Error - # number of blocks in key cache + 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_DBUG_PRINT("resize_key_cache: wait", + ("suspend thread %ld", thread->id)); + 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_DBUG_PRINT("resize_key_cache: signal", + ("thread %ld", wqueue->last_thread->next->id)); + 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++; +} -int resize_key_cache(ulong use_mem) +/* + 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) { - int block; - pthread_mutex_lock(&THR_LOCK_keycache); - if (flush_all_key_blocks()) + struct st_my_thread_var *last_thread; + if (!--keycache->cnt_for_resize_op && + (last_thread= keycache->resize_queue.last_thread)) { - /* TODO: If this happens, we should write a warning in the log file ! */ - pthread_mutex_unlock(&THR_LOCK_keycache); - return 0; + KEYCACHE_DBUG_PRINT("dec_counter_for_resize_op: signal", + ("thread %ld", last_thread->next->id)); + keycache_pthread_cond_signal(&last_thread->next->suspend); } - 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; } +/* + Change the key cache parameters + + 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) - /* Remove key_cache from memory */ + RETURN VALUE + none +*/ -void end_key_cache(void) +void end_key_cache(KEY_CACHE *keycache, my_bool cleanup) { DBUG_ENTER("end_key_cache"); - if (! _my_blocks_changed) + DBUG_PRINT("enter", ("key_cache: 0x%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; + } + + DBUG_PRINT("status", ("used: %d changed: %d w_requests: %lu " + "writes: %lu r_requests: %lu reads: %lu", + keycache->blocks_used, keycache->global_blocks_changed, + (ulong) keycache->global_cache_w_requests, + (ulong) keycache->global_cache_write, + (ulong) keycache->global_cache_r_requests, + (ulong) keycache->global_cache_read)); + + if (cleanup) + { + pthread_mutex_destroy(&keycache->cache_lock); + keycache->key_cache_inited= keycache->can_be_used= 0; + KEYCACHE_DEBUG_CLOSE; } - key_cache_inited=0; - _my_hash_blocks=_my_blocks_used=0; - DBUG_PRINT("status", - ("used: %d changed: %d w_requests: %lu writes: %lu " - "r_requests: %lu reads: %lu", - _my_blocks_used, _my_blocks_changed, - (ulong) _my_cache_w_requests, (ulong) _my_cache_write, - (ulong) _my_cache_r_requests, (ulong) _my_cache_read)); DBUG_VOID_RETURN; } /* end_key_cache */ -static inline void link_into_file_blocks(SEC_LINK *next, int file) +/* + Link a thread into double-linked queue of waiting threads. + + 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) { - 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; + 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_DBUG_PRINT("release_queue: signal", ("thread %ld", thread->id)); + keycache_pthread_cond_signal(&thread->suspend); + next=thread->next; + thread->next= NULL; + } + while (thread != last); + wqueue->last_thread= NULL; } -static inline void relink_into_file_blocks(SEC_LINK *next, int file) +/* + Unlink a block from the chain of dirty/clean blocks +*/ + +static inline void unlink_changed(BLOCK_LINK *block) { - reg1 SEC_LINK **ptr= &file_blocks[(uint) file & CHANGED_BLOCKS_MASK]; - 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->next_changed) + block->next_changed->prev_changed= block->prev_changed; + *block->prev_changed= block->next_changed; } -static inline void link_changed_to_file(SEC_LINK *next,int file) + +/* + Link a block into the chain of dirty/clean blocks +*/ + +static inline void link_changed(BLOCK_LINK *block, BLOCK_LINK **phead) { - reg1 SEC_LINK **ptr= &file_blocks[(uint) file & CHANGED_BLOCKS_MASK]; - if (next->next_changed) - next->next_changed->prev_changed=next->prev_changed; - *next->prev_changed=next->next_changed; - next->prev_changed= ptr; - if ((next->next_changed= *ptr)) - (*ptr)->prev_changed= &next->next_changed; - *ptr=next; - next->changed=0; - _my_blocks_changed--; + block->prev_changed= phead; + if ((block->next_changed= *phead)) + (*phead)->prev_changed= &block->next_changed; + *phead= block; } -static inline void link_file_to_changed(SEC_LINK *next) + +/* + 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) { - 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 (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--; + } } -#if !defined(DBUG_OFF) && !defined(EXTRA_DEBUG) -#define DBUG_OFF /* This should work */ +/* + 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_DBUG_PRINT("link_block: signal", ("thread %ld", thread->id)); + 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 +} -#ifndef DBUG_OFF -static void test_key_cache(const char *where, my_bool lock); + +/* + 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) +{ + 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_request() + 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 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; + keycache->keycache_time++; + + block= keycache->used_ins; + /* Check if we should link a hot block to the warm block */ + if (block && keycache->keycache_time - block->last_hit_time > + keycache->age_threshold) + { + 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 remove_reader(BLOCK_LINK *block) +{ + if (! --block->hash_link->requests && block->condvar) + keycache_pthread_cond_signal(block->condvar); +} + + +/* + Wait until the last reader of the page in block + signals on its termination +*/ + +static inline void wait_for_readers(KEY_CACHE *keycache, BLOCK_LINK *block) +{ + struct st_my_thread_var *thread= my_thread_var; + while (block->hash_link->requests) + { + KEYCACHE_DBUG_PRINT("wait_for_readers: wait", + ("suspend thread %ld block %u", + thread->id, BLOCK_NUMBER(block))); + block->condvar= &thread->suspend; + keycache_pthread_cond_wait(&thread->suspend, &keycache->cache_lock); + block->condvar= NULL; + } +} + + +/* + Add a hash link to a bucket in the hash_table +*/ + +static inline void link_hash(HASH_LINK **start, HASH_LINK *hash_link) +{ + if (*start) + (*start)->prev= &hash_link->next; + hash_link->next= *start; + hash_link->prev= start; + *start= hash_link; +} + + +/* + Remove a hash link from the hash table +*/ + +static void unlink_hash(KEY_CACHE *keycache, HASH_LINK *hash_link) +{ + KEYCACHE_DBUG_PRINT("unlink_hash", ("fd: %u pos_ %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_DBUG_PRINT("unlink_hash: signal", ("thread %ld", thread->id)); + 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 + + KEYCACHE_DBUG_PRINT("get_hash_link", ("fd: %u pos: %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", ("fd: %u pos: %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_DBUG_PRINT("get_hash_link: wait", + ("suspend thread %ld", thread->id)); + 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; +} + + +/* + 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", ("fd: %u pos %lu wrmode: %lu", + (uint) file, (ulong) filepos, (uint) wrmode)); + KEYCACHE_DBUG_PRINT("find_key_block", ("fd: %u pos: %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); + + 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_DBUG_PRINT("find_key_block: wait", + ("suspend thread %ld", thread->id)); + 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 " + "wrmode: %d block->status: %d", + BLOCK_NUMBER(block), wrmode, block->status)); + /* + 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_DBUG_PRINT("find_key_block: wait", + ("suspend thread %ld", thread->id)); + 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_DBUG_PRINT("find_key_block: wait", + ("suspend thread %ld", thread->id)); + 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 */ + KEYCACHE_DBUG_PRINT("find_key_block", + ("block->hash_link: %p hash_link: %p " + "block->status: %u", block->hash_link, + hash_link, block->status )); + 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); + KEYCACHE_DBUG_PRINT("find_key_block", + ("block->hash_link: %p hash_link: %p " + "block->status: %u", block->hash_link, + hash_link, block->status )); + 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", + ("fd: %u pos %lu block->status %u page_status %lu", + (uint) file, (ulong) filepos, block->status, + (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; - /* - ** 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 - */ + /* 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); + /* + Here other threads may step in and register as secondary readers. + They will register in block->wqueue[COND_FOR_REQUESTED]. + */ + 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_DBUG_PRINT("read_block: wait", + ("suspend thread %ld", thread->id)); + 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(File file, my_off_t filepos, byte *buff, uint length, +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)); + DBUG_PRINT("enter", ("fd: %u pos: %lu length: %u", + (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; + + offset= (uint) (filepos & (keycache->key_cache_block_size-1)); + /* 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++; + filepos-= offset; + read_length= length; + set_if_smaller(read_length, keycache->key_cache_block_size-offset); + KEYCACHE_DBUG_ASSERT(read_length > 0); + +#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+offset; + offset= 0; + } while ((length-= read_length)); - pthread_mutex_unlock(&THR_LOCK_keycache); DBUG_RETURN(start); } -no_key_cache: - _my_cache_r_requests++; - _my_cache_read++; - 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 */ -int key_cache_write(File file, my_off_t filepos, byte *buff, uint length, - uint block_length __attribute__((unused)), - int dont_write) -{ - reg1 SEC_LINK *next; - int error=0; - DBUG_ENTER("key_cache_write"); - DBUG_PRINT("enter", ("file %u, filepos %lu, length %u", - (uint) file, (ulong) filepos, length)); +/* + Insert a block of file data from a buffer into key cache - 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); - } + 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. +*/ -#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 */ +int key_cache_insert(KEY_CACHE *keycache, + File file, my_off_t filepos, int level, + byte *buff, uint length) +{ + DBUG_ENTER("key_cache_insert"); + DBUG_PRINT("enter", ("fd: %u pos: %lu length: %u", + (uint) file,(ulong) filepos, length)); + + 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; + uint offset; - _my_cache_w_requests++; + offset= (uint) (filepos & (keycache->key_cache_block_size-1)); 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 */ + keycache_pthread_mutex_lock(&keycache->cache_lock); + if (!keycache->can_be_used) + { + keycache_pthread_mutex_unlock(&keycache->cache_lock); + DBUG_RETURN(0); + } + /* Read data into key cache from buff in key_cache_block_size incr. */ + filepos-= offset; + read_length= length; + set_if_smaller(read_length, keycache->key_cache_block_size-offset); + KEYCACHE_DBUG_ASSERT(read_length > 0); + + 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) { - if (next->changed) /* Unlink from changed list */ - link_changed_to_file(next,next->file); + /* The requested page is to be read into the block buffer */ +#if !defined(SERIALIZED_READ_FROM_CACHE) + keycache_pthread_mutex_unlock(&keycache->cache_lock); + /* + Here other threads may step in and register as secondary readers. + They will register in block->wqueue[COND_FOR_REQUESTED]. + */ +#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); + /* Here we are alone again. */ +#endif + block->status= BLOCK_READ; + block->length= read_length+offset; + KEYCACHE_DBUG_PRINT("key_cache_insert", + ("primary request: new page in cache")); + /* Signal that all pending requests for this now can be processed. */ + if (block->wqueue[COND_FOR_REQUESTED].last_thread) + release_queue(&block->wqueue[COND_FOR_REQUESTED]); } - else if (!next->changed) - link_file_to_changed(next); /* Add to changed list */ - if (!(read_length & 511)) - bmove512(next->buffer,buff,read_length); - else - memcpy(next->buffer,buff,(size_t) read_length); - buff+=read_length; - filepos+=read_length; + 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+offset; + offset= 0; + } 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", + ("fd: %u pos: %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; + uint offset; - if (next) - { /* Found block */ - if (next != _my_used_last) - { /* Relink used-chain */ - if (next == _my_used_first) - _my_used_first=next->next_used; - else + offset= (uint) (filepos & (keycache->key_cache_block_size-1)); + do + { + 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) + /* Write data in key_cache_block_size increments */ + filepos-= offset; + read_length= length; + set_if_smaller(read_length, keycache->key_cache_block_size-offset); + KEYCACHE_DBUG_ASSERT(read_length > 0); + + 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; + 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); +} + +/* + 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(SEC_LINK *used) +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, hash_link %p", + BLOCK_NUMBER(block), block->hash_link)); + 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; + /* + While waiting for readers to finish, new readers might request the + block. But since we set block->status|= BLOCK_REASSIGNED, they + will wait on block->wqueue[COND_FOR_SAVED]. They must be signalled + later. + */ + 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++; + + /* All pending requests for this page must be resubmitted. */ + if (block->wqueue[COND_FOR_SAVED].last_thread) + release_queue(&block->wqueue[COND_FOR_SAVED]); +} - /* 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_DBUG_PRINT("flush_key_blocks_int: wait", + ("suspend thread %ld", thread->id)); + 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: 0x%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 +*/ + +static int flush_all_key_blocks(KEY_CACHE *keycache) +{ +#if defined(KEYCACHE_DEBUG) + uint cnt=0; +#endif + while (keycache->blocks_changed > 0) + { + BLOCK_LINK *block; + for (block= keycache->used_last->next_used ; ; block=block->next_used) + { + 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; +} + + +/* + Reset the counters of a key cache. SYNOPSIS - flush_all_key_blocks() + reset_key_cache_counters() + name the name of a key cache + key_cache pointer to the key kache to be reset - NOTE - We must have a lock on THR_LOCK_keycache before calling this function + DESCRIPTION + This procedure is used by process_key_caches() to reset the counters of all + currently used key caches, both the default one and the named ones. - RETURN VALUES - 0 Ok - 1 Error + RETURN + 0 on success (always because it can't fail) */ - -static int flush_all_key_blocks() +int reset_key_cache_counters(const char *name, KEY_CACHE *key_cache) { - SEC_LINK **block, **end; - for (block= changed_blocks, end= block+CHANGED_BLOCKS_HASH; - block < end; - block++ - ) + DBUG_ENTER("reset_key_cache_counters"); + if (!key_cache->key_cache_inited) { - while (*block) - { - if (flush_key_blocks_int((*block)->file, FLUSH_RELEASE)) - return 1; - } + DBUG_PRINT("info", ("Key cache %s not initialized.", name)); + DBUG_RETURN(0); } - return 0; + DBUG_PRINT("info", ("Resetting counters for key cache %s.", name)); + + key_cache->global_blocks_changed= 0; /* Key_blocks_not_flushed */ + key_cache->global_cache_r_requests= 0; /* Key_read_requests */ + key_cache->global_cache_read= 0; /* Key_reads */ + key_cache->global_cache_w_requests= 0; /* Key_write_requests */ + key_cache->global_cache_write= 0; /* Key_writes */ + DBUG_RETURN(0); } #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; +} + + +#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..38fef31fdd4 --- /dev/null +++ b/mysys/mf_keycaches.c @@ -0,0 +1,364 @@ +/* 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: 0x%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: 0x%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_free((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) + { + if ((*entry->prev= entry->next)) + entry->next->prev= entry->prev; + 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_search() + 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..049aa59a578 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 @@ -124,6 +124,9 @@ uint cleanup_dirname(register my_string to, const char *from) reg4 my_string start; char parent[5], /* for "FN_PARENTDIR" */ buff[FN_REFLEN+1],*end_parentdir; +#ifdef BACKSLASH_MBTAIL + CHARSET_INFO *fs= fs_character_set(); +#endif DBUG_ENTER("cleanup_dirname"); DBUG_PRINT("enter",("from: '%s'",from)); @@ -131,7 +134,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; } @@ -141,12 +144,21 @@ uint cleanup_dirname(register my_string to, const char *from) length=(uint) (strmov(parent+1,FN_PARENTDIR)-parent); for (pos=start ; (*pos= *from_ptr++) != 0 ; pos++) { +#ifdef BACKSLASH_MBTAIL + uint l; + if (use_mb(fs) && (l= my_ismbchar(fs, from_ptr - 1, from_ptr + 2))) + { + for (l-- ; l ; *++pos= *from_ptr++, l--); + start= pos + 1; /* Don't look inside multi-byte char */ + continue; + } +#endif if (*pos == '/') *pos = FN_LIBCHAR; 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 +207,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; @@ -226,7 +238,7 @@ void symdirget(char *dir) { char buff[FN_REFLEN]; char *pos=strend(dir); - if (dir[0] && pos[-1] != FN_DEVCHAR && access(dir, F_OK)) + if (dir[0] && pos[-1] != FN_DEVCHAR && my_access(dir, F_OK)) { File file; uint length; @@ -409,7 +421,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 +431,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 +499,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 c84222ba77f..a15bda4da6d 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_access.c b/mysys/my_access.c new file mode 100644 index 00000000000..237312b5c9b --- /dev/null +++ b/mysys/my_access.c @@ -0,0 +1,122 @@ +/* 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> + +#ifdef __WIN__ + +/* + Check a file or path for accessability. + + SYNOPSIS + file_access() + path Path to file + amode Access method + + DESCRIPTION + This function wraps the normal access method because the access + available in MSVCRT> +reports that filenames such as LPT1 and + COM1 are valid (they are but should not be so for us). + + RETURN VALUES + 0 ok + -1 error (We use -1 as my_access is mapped to access on other platforms) +*/ + +int my_access(const char *path, int amode) +{ + WIN32_FILE_ATTRIBUTE_DATA fileinfo; + BOOL result; + + result= GetFileAttributesEx(path, GetFileExInfoStandard, &fileinfo); + if (! result || + (fileinfo.dwFileAttributes & FILE_ATTRIBUTE_READONLY) && (amode & F_OK)) + { + my_errno= errno= EACCES; + return -1; + } + return 0; +} + +#endif /* __WIN__ */ + +#if defined(MSDOS) || defined(__WIN__) || defined(__EMX__) + +/* + List of file names that causes problem on windows + + NOTE that one can also not have file names of type CON.TXT +*/ + +static const char *reserved_names[]= +{ + "CON", "PRN", "AUX", "NUL", "COM1", "COM2", "COM3", "COM4", "COM5", "COM6", + "COM7", "COM8", "COM9", "LPT1", "LPT2", "LPT3", "LPT4", "LPT5", "LPT6", + "LPT7", "LPT8", "LPT9", "CLOCK$", + NullS +}; + +#define MAX_RESERVED_NAME_LENGTH 6 + +/* + Check if a path will access a reserverd file name that may cause problems + + SYNOPSIS + check_if_legal_filename + path Path to file + + RETURN + 0 ok + 1 reserved file name +*/ + +int check_if_legal_filename(const char *path) +{ + const char *end; + const char **reserved_name; + DBUG_ENTER("check_if_legal_filename"); + + path+= dirname_length(path); /* To start of filename */ + if (!(end= strchr(path, FN_EXTCHAR))) + end= strend(path); + if (path == end || (uint) (end - path) > MAX_RESERVED_NAME_LENGTH) + DBUG_RETURN(0); /* Simplify inner loop */ + + for (reserved_name= reserved_names; *reserved_name; reserved_name++) + { + const char *reserved= *reserved_name; /* never empty */ + const char *name= path; + + do + { + if (*reserved != my_toupper(&my_charset_latin1, *name)) + break; + if (++name == end && !reserved[1]) + DBUG_RETURN(1); /* Found wrong path */ + } while (*++reserved); + } + DBUG_RETURN(0); +} +#endif + + +#ifdef OS2 +int check_if_legal_filename(const char *path) +{ + return 0; +} +#endif /* OS2 */ diff --git a/mysys/my_alloc.c b/mysys/my_alloc.c index d03ec5841af..d52a8efeed2 100644 --- a/mysys/my_alloc.c +++ b/mysys/my_alloc.c @@ -22,12 +22,35 @@ #undef EXTRA_DEBUG #define EXTRA_DEBUG + +/* + Initialize memory root + + SYNOPSIS + init_alloc_root() + mem_root - memory root to initialize + block_size - size of chunks (blocks) used for memory allocation + (It is external size of chunk i.e. it should include + memory required for internal structures, thus it + should be no less than ALLOC_ROOT_MIN_BLOCK_SIZE) + pre_alloc_size - if non-0, then size of block that should be + pre-allocated during memory root initialization. + + DESCRIPTION + This function prepares memory root for further use, sets initial size of + chunk for memory allocation and pre-allocates first block if specified. + Altough error can happen during execution of this function if pre_alloc_size + is non-0 it won't be reported. Instead it will be reported as error in first + alloc_root() on this memory root. +*/ 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: 0x%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; + mem_root->block_size= block_size - ALLOC_ROOT_MIN_BLOCK_SIZE; mem_root->error_handler= 0; mem_root->block_num= 4; /* We shift this with >>2 */ mem_root->first_block_usage= 0; @@ -45,15 +68,16 @@ void init_alloc_root(MEM_ROOT *mem_root, uint block_size, } } #endif + DBUG_VOID_RETURN; } /* SYNOPSIS reset_root_defaults() mem_root memory root to change defaults of - block_size new value of block size. Must be - greater than ~68 bytes (the exact value depends on - platform and compilation flags) + block_size new value of block size. Must be greater or equal + than ALLOC_ROOT_MIN_BLOCK_SIZE (this value is about + 68 bytes and depends on platform and compilation flags) pre_alloc_size new size of preallocated block. If not zero, must be equal to or greater than block size, otherwise means 'no prealloc'. @@ -65,9 +89,11 @@ 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; + DBUG_ASSERT(alloc_root_inited(mem_root)); + + mem_root->block_size= block_size - ALLOC_ROOT_MIN_BLOCK_SIZE; #if !(defined(HAVE_purify) && defined(EXTRA_DEBUG)) if (pre_alloc_size) { @@ -117,24 +143,32 @@ 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: 0x%lx", mem_root)); + + DBUG_ASSERT(alloc_root_inited(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_PRINT("exit",("ptr: 0x%lx", (((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; reg1 USED_MEM *next= 0; reg2 USED_MEM **prev; + DBUG_ASSERT(alloc_root_inited(mem_root)); + Size= ALIGN_SIZE(Size); if ((*(prev= &mem_root->free)) != NULL) { @@ -169,7 +203,7 @@ gptr alloc_root(MEM_ROOT *mem_root,unsigned int Size) next->left= get_size-ALIGN_SIZE(sizeof(USED_MEM)); *prev=next; } - + point= (gptr) ((char*) next+ (next->size-next->left)); /*TODO: next part may be unneded due to mem_root->first_block_usage counter*/ if ((next->left-= Size) < mem_root->min_malloc) @@ -183,6 +217,12 @@ gptr alloc_root(MEM_ROOT *mem_root,unsigned int Size) #endif } +#ifdef SAFEMALLOC +#define TRASH(X) bfill(((char*)(X) + ((X)->size-(X)->left)), (X)->left, 0xa5) +#else +#define TRASH /* no-op */ +#endif + /* Mark all data in blocks free for reusage */ static inline void mark_blocks_free(MEM_ROOT* root) @@ -193,14 +233,20 @@ static inline void mark_blocks_free(MEM_ROOT* root) /* iterate through (partially) free blocks, mark them free */ last= &root->free; for (next= root->free; next; next= *(last= &next->next)) + { next->left= next->size - ALIGN_SIZE(sizeof(USED_MEM)); + TRASH(next); + } /* Combine the free and the used list */ *last= next=root->used; /* now go through the used blocks and mark them free */ for (; next; next= next->next) + { next->left= next->size - ALIGN_SIZE(sizeof(USED_MEM)); + TRASH(next); + } /* Now everything is set; Indicate that nothing is used anymore */ root->used= 0; @@ -217,8 +263,9 @@ void free_root(MEM_ROOT *root, myf MyFlags) { reg1 USED_MEM *next,*old; DBUG_ENTER("free_root"); + DBUG_PRINT("enter",("root: 0x%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) { @@ -245,6 +292,7 @@ void free_root(MEM_ROOT *root, myf MyFlags) { root->free=root->pre_alloc; root->free->left=root->pre_alloc->size-ALIGN_SIZE(sizeof(USED_MEM)); + TRASH(root->pre_alloc); root->free->next=0; } root->block_num= 4; 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 9662f219418..2af4edbf1a5 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,74 @@ #include "mysys_priv.h" #include <my_bitmap.h> -#include <assert.h> #include <m_string.h> static 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 } static 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); } @@ -88,6 +113,7 @@ uint bitmap_set_next(MY_BITMAP *map) uint bitmap_size=map->bitmap_size; uint i; + DBUG_ASSERT(map->bitmap); bitmap_lock(map); for (i=0; i < bitmap_size ; i++, bitmap++) { @@ -113,32 +139,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; } -void bitmap_set_all(MY_BITMAP* map) +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_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..149a5c81e08 100644 --- a/mysys/my_chsize.c +++ b/mysys/my_chsize.c @@ -44,7 +44,9 @@ int my_chsize(File fd, my_off_t newlength, int filler, myf MyFlags) DBUG_PRINT("my",("fd: %d length: %lu MyFlags: %d",fd,(ulong) newlength, MyFlags)); - oldsize = my_seek(fd, 0L, MY_SEEK_END, MYF(MY_WME+MY_FAE)); + if ((oldsize = my_seek(fd, 0L, MY_SEEK_END, MYF(MY_WME+MY_FAE))) == newlength) + DBUG_RETURN(0); + DBUG_PRINT("info",("old_size: %ld", (ulong) oldsize)); if (oldsize > newlength) @@ -88,8 +90,12 @@ int my_chsize(File fd, my_off_t newlength, int filler, myf MyFlags) Fill space between requested length and true length with 'filler' 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); + if (my_seek(fd, newlength, MY_SEEK_SET, MYF(MY_WME+MY_FAE)) + == MY_FILEPOS_ERROR) + { + goto err; + } + 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_conio.c b/mysys/my_conio.c new file mode 100644 index 00000000000..e381f9f23ef --- /dev/null +++ b/mysys/my_conio.c @@ -0,0 +1,217 @@ +/* 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" + +#ifdef __WIN__ + +static HANDLE my_coninpfh= 0; /* console input */ + +/* + functions my_pthread_auto_mutex_lock & my_pthread_auto_mutex_free + are experimental at this moment, they are intended to bring + ability of protecting code sections without necessity to explicitly + initialize synchronization object in one of threads + + if found useful they are to be exported in mysys +*/ + +/* + int my_pthread_auto_mutex_lock(HANDLE* ph, const char* name, + int id, int time) + + NOTES + creates a mutex with given name and tries to lock it time msec. + mutex name is appended with id to allow system wide or process wide + locks. Handle to created mutex returned in ph argument. + + RETURN + 0 thread owns mutex + <>0 error + +*/ +static +int my_pthread_auto_mutex_lock(HANDLE* ph, const char* name, int id, int time) +{ + int res; + char tname[FN_REFLEN]; + + sprintf(tname, "%s-%08X", name, id); + + *ph= CreateMutex(NULL, FALSE, tname); + if (*ph == NULL) + return GetLastError(); + + res= WaitForSingleObject(*ph, time); + + if (res == WAIT_TIMEOUT) + return ERROR_SEM_TIMEOUT; + + if (res == WAIT_FAILED) + return GetLastError(); + + return 0; +} + +/* + int my_pthread_auto_mutex_free(HANDLE* ph) + + + NOTES + releases a mutex. + + RETURN + 0 thread released mutex + <>0 error + +*/ +static +int my_pthread_auto_mutex_free(HANDLE* ph) +{ + if (*ph) + { + ReleaseMutex(*ph); + CloseHandle(*ph); + *ph= NULL; + } + + return 0; +} + + +#define pthread_auto_mutex_decl(name) \ + HANDLE __h##name= NULL; + +#define pthread_auto_mutex_lock(name, proc, time) \ + my_pthread_auto_mutex_lock(&__h##name, #name, (proc), (time)) + +#define pthread_auto_mutex_free(name) \ + my_pthread_auto_mutex_free(&__h##name) + + +/* + char* my_cgets(char *string, unsigned long clen, unsigned long* plen) + + NOTES + Replaces _cgets from libc to support input of more than 255 chars. + Reads from the console via ReadConsole into buffer which + should be at least clen characters. + Actual length of string returned in plen. + + WARNING + my_cgets() does NOT check the pushback character buffer (i.e., _chbuf). + Thus, my_cgets() will not return any character that is pushed back by + the _ungetch() call. + + RETURN + string pointer ok + NULL Error + +*/ +char* my_cgets(char *buffer, unsigned long clen, unsigned long* plen) +{ + ULONG state; + char *result; + CONSOLE_SCREEN_BUFFER_INFO csbi; + + pthread_auto_mutex_decl(my_conio_cs); + + /* lock the console for the current process*/ + if (pthread_auto_mutex_lock(my_conio_cs, GetCurrentProcessId(), INFINITE)) + { + /* can not lock console */ + pthread_auto_mutex_free(my_conio_cs); + return NULL; + } + + /* init console input */ + if (my_coninpfh == 0) + { + /* same handle will be used until process termination */ + my_coninpfh= CreateFile("CONIN$", GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, OPEN_EXISTING, 0, NULL); + } + + if (my_coninpfh == INVALID_HANDLE_VALUE) + { + /* unlock the console */ + pthread_auto_mutex_free(my_conio_cs); + return(NULL); + } + + GetConsoleMode((HANDLE)my_coninpfh, &state); + SetConsoleMode((HANDLE)my_coninpfh, ENABLE_LINE_INPUT | + ENABLE_PROCESSED_INPUT | ENABLE_ECHO_INPUT); + + GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi); + + /* + there is no known way to determine allowed buffer size for input + though it is known it should not be more than 64K + so we cut 64K and try first size of screen buffer + if it is still to large we cut half of it and try again + later we may want to cycle from min(clen, 65535) to allowed size + with small decrement to determine exact allowed buffer + */ + clen= min(clen, 65535); + do + { + clen= min(clen, (unsigned long)csbi.dwSize.X*csbi.dwSize.Y); + if (!ReadConsole((HANDLE)my_coninpfh, (LPVOID)buffer, clen - 1, plen, NULL)) + { + result= NULL; + clen>>= 1; + } + else + { + result= buffer; + break; + } + } + while (GetLastError() == ERROR_NOT_ENOUGH_MEMORY); + + + if (result != NULL) + { + if (buffer[*plen - 2] == '\r') + { + *plen= *plen - 2; + } + else + { + if (buffer[*plen - 1] == '\r') + { + char tmp[3]; + int tmplen= sizeof(tmp); + + *plen= *plen - 1; + /* read /n left in the buffer */ + ReadConsole((HANDLE)my_coninpfh, (LPVOID)tmp, tmplen, &tmplen, NULL); + } + } + buffer[*plen]= '\0'; + } + + SetConsoleMode((HANDLE)my_coninpfh, state); + /* unlock the console */ + pthread_auto_mutex_free(my_conio_cs); + + return result; +} + +#endif /* __WIN__ */ 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..db1beb58263 --- /dev/null +++ b/mysys/my_crc32.c @@ -0,0 +1,23 @@ +/* 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 +#undef DYNAMIC_CRC_TABLE +#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..0f8ffb7c05f 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,84 @@ 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. + */ + if (*tpos == '-') + { + tpos++; + olen--; + } + prec_supplied= 0; + if (*tpos== '.') + { + tpos++; + olen--; + if (*tpos == '*') + { + tpos++; + olen--; + prec_chars= va_arg(ap, int); /* get length parameter */ + prec_supplied= 1; + } + else + { + for (prec_chars= 0; my_isdigit(&my_charset_latin1, *tpos); tpos++, olen--) + { + prec_supplied= 1; + prec_chars= prec_chars * 10 + *tpos - '0'; + } + } + } + + 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 +169,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..f07beec9f39 100644 --- a/mysys/my_fopen.c +++ b/mysys/my_fopen.c @@ -19,42 +19,67 @@ #include <errno.h> #include "mysys_err.h" -static void make_ftype(my_string to,int flag); +static void make_ftype(my_string to,int flag); - /* Open a file as stream */ +/* + Open a file as stream -FILE *my_fopen(const char *FileName, int Flags, myf MyFlags) - /* Path-name of file */ - /* Read | write .. */ - /* Special flags */ + SYNOPSIS + my_fopen() + FileName Path-name of file + Flags Read | write | append | trunc (like for open()) + MyFlags Flags for handling errors + + RETURN + 0 Error + # File handler +*/ + +FILE *my_fopen(const char *filename, int flags, myf MyFlags) { FILE *fd; char type[5]; DBUG_ENTER("my_fopen"); - DBUG_PRINT("my",("Name: '%s' Flags: %d MyFlags: %d", - FileName, Flags, MyFlags)); - - make_ftype(type,Flags); - if ((fd = fopen(FileName, type)) != 0) + DBUG_PRINT("my",("Name: '%s' flags: %d MyFlags: %d", + filename, flags, MyFlags)); + /* + if we are not creating, then we need to use my_access to make sure + the file exists since Windows doesn't handle files like "com1.sym" + very well + */ +#ifdef __WIN__ + if (check_if_legal_filename(filename)) + { + errno= EACCES; + fd= 0; + } + else +#endif + { + make_ftype(type,flags); + fd = fopen(filename, type); + } + + if (fd != 0) { /* The test works if MY_NFILE < 128. The problem is that fileno() is char 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 */ } pthread_mutex_lock(&THR_LOCK_open); if ((my_file_info[fileno(fd)].name = (char*) - my_strdup(FileName,MyFlags))) + my_strdup(filename,MyFlags))) { my_stream_opened++; my_file_info[fileno(fd)].type = STREAM_BY_FOPEN; pthread_mutex_unlock(&THR_LOCK_open); - DBUG_PRINT("exit",("stream: %lx",fd)); + DBUG_PRINT("exit",("stream: 0x%lx",fd)); DBUG_RETURN(fd); } pthread_mutex_unlock(&THR_LOCK_open); @@ -65,9 +90,9 @@ FILE *my_fopen(const char *FileName, int Flags, myf MyFlags) my_errno=errno; DBUG_PRINT("error",("Got error %d on open",my_errno)); if (MyFlags & (MY_FFNF | MY_FAE | MY_WME)) - my_error((Flags & O_RDONLY) || (Flags == O_RDONLY ) ? EE_FILENOTFOUND : + my_error((flags & O_RDONLY) || (flags == O_RDONLY ) ? EE_FILENOTFOUND : EE_CANTCREATEFILE, - MYF(ME_BELL+ME_WAITTANG), FileName,my_errno); + MYF(ME_BELL+ME_WAITTANG), filename, my_errno); DBUG_RETURN((FILE*) 0); } /* my_fopen */ @@ -78,7 +103,7 @@ int my_fclose(FILE *fd, myf MyFlags) { int err,file; DBUG_ENTER("my_fclose"); - DBUG_PRINT("my",("stream: %lx MyFlags: %d",fd, MyFlags)); + DBUG_PRINT("my",("stream: 0x%lx MyFlags: %d",fd, MyFlags)); pthread_mutex_lock(&THR_LOCK_open); file=fileno(fd); @@ -91,7 +116,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 +148,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 { @@ -138,36 +163,61 @@ FILE *my_fdopen(File Filedes, const char *name, int Flags, myf MyFlags) pthread_mutex_unlock(&THR_LOCK_open); } - DBUG_PRINT("exit",("stream: %lx",fd)); + DBUG_PRINT("exit",("stream: 0x%lx",fd)); DBUG_RETURN(fd); } /* my_fdopen */ - /* Make a filehandler-open-typestring from ordinary inputflags */ +/* + Make a fopen() typestring from a open() type bitmap + + SYNOPSIS + make_ftype() + to String for fopen() is stored here + flag Flag used by open() + + IMPLEMENTATION + This routine attempts to find the best possible match + between a numeric option and a string option that could be + fed to fopen. There is not a 1 to 1 mapping between the two. + + NOTE + On Unix, O_RDONLY is usually 0 + + MAPPING + r == O_RDONLY + w == O_WRONLY|O_TRUNC|O_CREAT + a == O_WRONLY|O_APPEND|O_CREAT + r+ == O_RDWR + w+ == O_RDWR|O_TRUNC|O_CREAT + a+ == O_RDWR|O_APPEND|O_CREAT +*/ static void make_ftype(register my_string to, register int flag) { -#if FILE_BINARY /* If we have binary-files */ - reg3 int org_flag=flag; -#endif - flag&= ~FILE_BINARY; /* remove binary bit */ - if (flag == O_RDONLY) - *to++= 'r'; - else if (flag == O_WRONLY) - *to++= 'w'; - else - { /* Add '+' after theese */ - if (flag == O_RDWR) + /* check some possible invalid combinations */ + DBUG_ASSERT((flag & (O_TRUNC | O_APPEND)) != (O_TRUNC | O_APPEND)); + DBUG_ASSERT((flag & (O_WRONLY | O_RDWR)) != (O_WRONLY | O_RDWR)); + + if ((flag & (O_RDONLY|O_WRONLY)) == O_WRONLY) + *to++= (flag & O_APPEND) ? 'a' : 'w'; + else if (flag & O_RDWR) + { + /* Add '+' after theese */ + if (flag & (O_TRUNC | O_CREAT)) + *to++= 'w'; + else if (flag & O_APPEND) + *to++= 'a'; + else *to++= 'r'; - else if (flag & O_APPEND) - *to++= 'a'; - else - *to++= 'w'; /* Create file */ - *to++= '+'; - } -#if FILE_BINARY /* If we have binary-files */ - if (org_flag & FILE_BINARY) + *to++= '+'; + } + else + *to++= 'r'; + +#if FILE_BINARY /* If we have binary-files */ + if (flag & FILE_BINARY) *to++='b'; -#endif +#endif *to='\0'; } /* make_ftype */ diff --git a/mysys/my_fstream.c b/mysys/my_fstream.c index 0ad789e98ac..5b17e3ff51c 100644 --- a/mysys/my_fstream.c +++ b/mysys/my_fstream.c @@ -39,7 +39,7 @@ uint my_fread(FILE *stream, byte *Buffer, uint Count, myf MyFlags) { uint readbytes; DBUG_ENTER("my_fread"); - DBUG_PRINT("my",("stream: %lx Buffer: %lx Count: %u MyFlags: %d", + DBUG_PRINT("my",("stream: 0x%lx Buffer: 0x%lx Count: %u MyFlags: %d", stream, Buffer, Count, MyFlags)); if ((readbytes = (uint) fread(Buffer,sizeof(char),(size_t) Count,stream)) @@ -80,7 +80,7 @@ uint my_fwrite(FILE *stream, const byte *Buffer, uint Count, myf MyFlags) uint errors; #endif DBUG_ENTER("my_fwrite"); - DBUG_PRINT("my",("stream: %lx Buffer: %lx Count: %u MyFlags: %d", + DBUG_PRINT("my",("stream: 0x%lx Buffer: 0x%lx Count: %u MyFlags: %d", stream, Buffer, Count, MyFlags)); #if !defined(NO_BACKGROUND) && defined(USE_MY_STREAM) @@ -152,7 +152,7 @@ my_off_t my_fseek(FILE *stream, my_off_t pos, int whence, myf MyFlags __attribute__((unused))) { DBUG_ENTER("my_fseek"); - DBUG_PRINT("my",("stream: %lx pos: %lu whence: %d MyFlags: %d", + DBUG_PRINT("my",("stream: 0x%lx pos: %lu whence: %d MyFlags: %d", stream, pos, whence, MyFlags)); DBUG_RETURN(fseek(stream, (off_t) pos, whence) ? MY_FILEPOS_ERROR : (my_off_t) ftell(stream)); @@ -166,7 +166,7 @@ my_off_t my_ftell(FILE *stream, myf MyFlags __attribute__((unused))) { off_t pos; DBUG_ENTER("my_ftell"); - DBUG_PRINT("my",("stream: %lx MyFlags: %d",stream, MyFlags)); + DBUG_PRINT("my",("stream: 0x%lx MyFlags: %d",stream, MyFlags)); pos=ftell(stream); DBUG_PRINT("exit",("ftell: %lu",(ulong) pos)); DBUG_RETURN((my_off_t) pos); 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..222abe81933 --- /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 71f8819756a..075956c7af9 100644 --- a/mysys/my_getopt.c +++ b/mysys/my_getopt.c @@ -17,7 +17,6 @@ #include <my_global.h> #include <m_string.h> #include <stdlib.h> -#include <assert.h> #include <my_sys.h> #include <mysys_err.h> #include <my_getopt.h> @@ -35,8 +34,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 @@ -59,13 +59,18 @@ char *disabled_my_option= (char*) "0"; my_bool my_getopt_print_errors= 1; -static void default_reporter(enum loglevel level __attribute__((unused)), +static void default_reporter(enum loglevel level, const char *format, ...) { va_list args; va_start(args, format); + if (level == WARNING_LEVEL) + fprintf(stderr, "%s", "Warning: "); + else if (level == INFORMATION_LEVEL) + fprintf(stderr, "%s", "Info: "); vfprintf(stderr, format, args); va_end(args); + fflush(stderr); } /* @@ -78,15 +83,25 @@ static void default_reporter(enum loglevel level __attribute__((unused)), 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_get_one_option get_one_option) { 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); @@ -102,7 +117,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 '-' */ @@ -120,7 +134,7 @@ int handle_options(int *argc, char ***argv, if (my_getopt_print_errors) my_getopt_error_reporter(ERROR_LEVEL, "%s: Option '-O' requires an argument\n", - progname); + my_progname); return EXIT_ARGUMENT_REQUIRED; } cur_arg= *pos; @@ -138,7 +152,7 @@ int handle_options(int *argc, char ***argv, if (my_getopt_print_errors) my_getopt_error_reporter(ERROR_LEVEL, "%s: Option '--set-variable' requires an argument\n", - progname); + my_progname); return EXIT_ARGUMENT_REQUIRED; } } @@ -152,7 +166,7 @@ int handle_options(int *argc, char ***argv, if (my_getopt_print_errors) my_getopt_error_reporter(ERROR_LEVEL, "%s: Option '--set-variable' requires an argument\n", - progname); + my_progname); return EXIT_ARGUMENT_REQUIRED; } cur_arg= *pos; @@ -169,19 +183,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 @@ -193,18 +208,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))) { @@ -213,7 +227,7 @@ int handle_options(int *argc, char ***argv, if (my_getopt_print_errors) my_getopt_error_reporter(ERROR_LEVEL, "%s: ambiguous option '--%s-%s' (--%s-%s)\n", - progname, special_opt_prefix[i], + my_progname, special_opt_prefix[i], cur_arg, special_opt_prefix[i], prev_found); return EXIT_AMBIGUOUS_OPTION; @@ -250,7 +264,7 @@ int handle_options(int *argc, char ***argv, my_getopt_error_reporter(option_is_loose ? WARNING_LEVEL : ERROR_LEVEL, "%s: unknown variable '%s'\n", - progname, cur_arg); + my_progname, cur_arg); if (!option_is_loose) return EXIT_UNKNOWN_VARIABLE; } @@ -260,7 +274,7 @@ int handle_options(int *argc, char ***argv, my_getopt_error_reporter(option_is_loose ? WARNING_LEVEL : ERROR_LEVEL, "%s: unknown option '--%s'\n", - progname, cur_arg); + my_progname, cur_arg); if (!option_is_loose) return EXIT_UNKNOWN_OPTION; } @@ -278,7 +292,7 @@ int handle_options(int *argc, char ***argv, if (my_getopt_print_errors) my_getopt_error_reporter(ERROR_LEVEL, "%s: variable prefix '%s' is not unique\n", - progname, cur_arg); + my_progname, opt_str); return EXIT_VAR_PREFIX_NOT_UNIQUE; } else @@ -286,30 +300,46 @@ int handle_options(int *argc, char ***argv, if (my_getopt_print_errors) my_getopt_error_reporter(ERROR_LEVEL, "%s: ambiguous option '--%s' (%s, %s)\n", - progname, cur_arg, prev_found, + 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) my_getopt_error_reporter(ERROR_LEVEL, "%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) my_getopt_error_reporter(ERROR_LEVEL, "%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 @@ -317,7 +347,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); @@ -325,14 +355,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; } @@ -345,7 +376,7 @@ int handle_options(int *argc, char ***argv, if (my_getopt_print_errors) my_getopt_error_reporter(ERROR_LEVEL, "%s: option '--%s' requires an argument\n", - progname, optp->name); + my_progname, optp->name); return EXIT_ARGUMENT_REQUIRED; } argument= *pos; @@ -365,7 +396,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); @@ -396,7 +436,7 @@ int handle_options(int *argc, char ***argv, if (my_getopt_print_errors) my_getopt_error_reporter(ERROR_LEVEL, "%s: option '-%c' requires an argument\n", - progname, optp->id); + my_progname, optp->id); return EXIT_ARGUMENT_REQUIRED; } argument= *++pos; @@ -404,11 +444,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))) { my_getopt_error_reporter(ERROR_LEVEL, "%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); @@ -420,18 +461,18 @@ int handle_options(int *argc, char ***argv, if (my_getopt_print_errors) my_getopt_error_reporter(ERROR_LEVEL, "%s: unknown option '-%c'\n", - progname, *optend); + 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))) { my_getopt_error_reporter(ERROR_LEVEL, "%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); @@ -451,6 +492,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 @@ -458,20 +542,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; @@ -523,7 +607,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++) @@ -535,7 +619,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; @@ -609,10 +694,10 @@ static longlong getopt_ll(char *arg, const struct my_option *optp, int *err) ulonglong block_size= (optp->block_size ? (ulonglong) optp->block_size : 1L); num= eval_num_suffix(arg, err, (char*) optp->name); - if (num > 0 && (ulonglong) num > (ulonglong) (ulong) optp->max_value && + if (num > 0 && (ulonglong) num > (ulonglong) optp->max_value && optp->max_value) /* if max value is not set -> no upper limit */ - num= (longlong) (ulong) optp->max_value; - num= ((num - (longlong) optp->sub_size) / block_size); + num= (ulonglong) optp->max_value; + num= ((num - optp->sub_size) / block_size); num= (longlong) (num * block_size); return max(num, optp->min_value); } @@ -648,58 +733,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); } } @@ -734,13 +838,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++; @@ -797,41 +903,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..5663ceaa60e 100644 --- a/mysys/my_getwd.c +++ b/mysys/my_getwd.c @@ -45,7 +45,7 @@ int my_getwd(my_string buf, uint size, myf MyFlags) { my_string pos; DBUG_ENTER("my_getwd"); - DBUG_PRINT("my",("buf: %lx size: %d MyFlags %d", buf,size,MyFlags)); + DBUG_PRINT("my",("buf: 0x%lx size: %d MyFlags %d", buf,size,MyFlags)); #if ! defined(MSDOS) if (curr_dir[0]) /* Current pos is saved here */ @@ -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 @@ -191,3 +192,28 @@ int test_if_hard_path(register const char *dir_name) return FALSE; #endif } /* test_if_hard_path */ + + +/* + Test if a name contains an (absolute or relative) path. + + SYNOPSIS + has_path() + name The name to test. + + RETURN + TRUE name contains a path. + FALSE name does not contain a path. +*/ + +my_bool has_path(const char *name) +{ + return test(strchr(name, FN_LIBCHAR)) +#if FN_LIBCHAR != '/' + || test(strchr(name,'/')) +#endif +#ifdef FN_DEVCHAR + || test(strchr(name, FN_DEVCHAR)) +#endif + ; +} diff --git a/mysys/my_handler.c b/mysys/my_handler.c new file mode 100644 index 00000000000..156e7892580 --- /dev/null +++ b/mysys/my_handler.c @@ -0,0 +1,550 @@ +/* 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_global.h> +#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= 1; + /* + 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 (*a < ' ') ? -swap : swap; + } + return 0; + } + return (int) (a_length-b_length); +} + + +/* + Compare two keys + + SYNOPSIS + ha_key_cmp() + keyseg Array of 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 + diff_pos OUT Number of first keypart where values differ, counting + from one. + diff_pos[1] OUT (b + diff_pos[1]) points to first value in tuple b + that is different from corresponding value in tuple a. + + EXAMPLES + Example1: if the function is called for tuples + ('aaa','bbb') and ('eee','fff'), then + diff_pos[0] = 1 (as 'aaa' != 'eee') + diff_pos[1] = 0 (offset from beggining of tuple b to 'eee' keypart). + + Example2: if the index function is called for tuples + ('aaa','bbb') and ('aaa','fff'), + diff_pos[0] = 2 (as 'aaa' != 'eee') + diff_pos[1] = 3 (offset from beggining of tuple b to 'fff' keypart, + here we assume that first key part is CHAR(3) NOT NULL) + + 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; + uchar *orig_b= b; + + *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)++; + diff_pos[1]= (uint)(b - orig_b); + + /* 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), + (my_bool)!(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), + (my_bool)!(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); + /* + The following may give a compiler warning about floating point + comparison not being safe, but this is ok in this context as + we are bascily doing sorting + */ + 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); + /* + The following may give a compiler warning about floating point + comparison not being safe, but this is ok in this context as + we are bascily doing sorting + */ + 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 */ + + +/* + Find the first NULL value in index-suffix values tuple + + SYNOPSIS + ha_find_null() + keyseg Array of keyparts for key suffix + a Key suffix value tuple + + DESCRIPTION + Find the first NULL value in index-suffix values tuple. + TODO Consider optimizing this fuction or its use so we don't search for + NULL values in completely NOT NULL index suffixes. + + RETURN + First key part that has NULL as value in values tuple, or the last key part + (with keyseg->type==HA_TYPE_END) if values tuple doesn't contain NULLs. +*/ + +HA_KEYSEG *ha_find_null(HA_KEYSEG *keyseg, uchar *a) +{ + for (; (enum ha_base_keytype) keyseg->type != HA_KEYTYPE_END; keyseg++) + { + uchar *end; + if (keyseg->null_bit) + { + if (!*a++) + return keyseg; + } + end= a+ keyseg->length; + + switch ((enum ha_base_keytype) keyseg->type) { + case HA_KEYTYPE_TEXT: + case HA_KEYTYPE_BINARY: + if (keyseg->flag & HA_SPACE_PACK) + { + int a_length; + get_key_length(a_length, a); + a += a_length; + break; + } + else + a= end; + break; + case HA_KEYTYPE_VARTEXT: + case HA_KEYTYPE_VARBINARY: + { + int a_length; + get_key_length(a_length, a); + a+= a_length; + break; + } + case HA_KEYTYPE_NUM: + if (keyseg->flag & HA_SPACE_PACK) + { + int alength= *a++; + end= a+alength; + } + a= end; + break; + case HA_KEYTYPE_INT8: + case HA_KEYTYPE_SHORT_INT: + case HA_KEYTYPE_USHORT_INT: + case HA_KEYTYPE_LONG_INT: + case HA_KEYTYPE_ULONG_INT: + case HA_KEYTYPE_INT24: + case HA_KEYTYPE_UINT24: +#ifdef HAVE_LONG_LONG + case HA_KEYTYPE_LONGLONG: + case HA_KEYTYPE_ULONGLONG: +#endif + case HA_KEYTYPE_FLOAT: + case HA_KEYTYPE_DOUBLE: + a= end; + break; + } + } + return keyseg; +} + diff --git a/mysys/my_init.c b/mysys/my_init.c index dd06ae41dcb..d68c76bef1b 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> @@ -47,16 +42,16 @@ static void netware_init(); #define netware_init() #endif - -my_bool my_init_done=0; +my_bool my_init_done= 0; +uint mysys_usage_id= 0; /* Incremented for each my_init() */ 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 */ + (*str == '0' ? 8 : 10), /* Octalt or decimalt */ 0, INT_MAX, &tmp); return (ulong) tmp; } @@ -79,6 +74,9 @@ my_bool my_init(void) if (my_init_done) return 0; my_init_done=1; + mysys_usage_id++; + my_umask= 0660; /* Default umask for new files */ + my_umask_dir= 0700; /* Default umask for new directories */ #if defined(THREAD) && defined(SAFE_MUTEX) safe_mutex_global_init(); /* Must be called early */ #endif @@ -129,10 +127,23 @@ my_bool my_init(void) void my_end(int infoflag) { - FILE *info_file; - if (!(info_file=DBUG_FILE)) - info_file=stderr; - if (infoflag & MY_CHECK_ERROR || info_file != stderr) + /* + this code is suboptimal to workaround a bug in + Sun CC: Sun C++ 5.6 2004/06/02 for x86, and should not be + optimized until this compiler is not in use anymore + */ + FILE *info_file= DBUG_FILE; + my_bool print_info= (info_file != stderr); + DBUG_ENTER("my_end"); + if (!info_file) + { + info_file= stderr; + print_info= 0; + } + + DBUG_PRINT("info",("Shutting down: print_info: %d", print_info)); + if ((infoflag & MY_CHECK_ERROR) || print_info) + { /* Test if some file is left open */ if (my_file_opened | my_stream_opened) { @@ -142,10 +153,16 @@ void my_end(int infoflag) } } free_charsets(); - if (infoflag & MY_GIVE_INFO || info_file != stderr) + my_once_free(); + + if ((infoflag & MY_GIVE_INFO) || print_info) { #ifdef HAVE_GETRUSAGE struct rusage rus; +#ifdef HAVE_purify + /* Purify assumes that rus is uninitialized after getrusage call */ + bzero((char*) &rus, sizeof(rus)); +#endif if (!getrusage(RUSAGE_SELF, &rus)) fprintf(info_file,"\n\ User time %.2f, System time %.2f\n\ @@ -181,7 +198,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 +260,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_lib.c b/mysys/my_lib.c index 055e00d2efc..1908c70f407 100644 --- a/mysys/my_lib.c +++ b/mysys/my_lib.c @@ -427,6 +427,18 @@ MY_DIR *my_dir(const char *path, myf MyFlags) do { #ifdef __BORLANDC__ + attrib= find.ff_attrib; +#else + attrib= find.attrib; + /* + Do not show hidden and system files which Windows sometimes create. + Note. Because Borland's findfirst() is called with the third + argument = 0 hidden/system files are excluded from the search. + */ + if (attrib & (_A_HIDDEN | _A_SYSTEM)) + continue; +#endif +#ifdef __BORLANDC__ if (!(finfo.name= strdup_root(names_storage, find.ff_name))) goto error; #else @@ -442,11 +454,10 @@ MY_DIR *my_dir(const char *path, myf MyFlags) bzero(finfo.mystat, sizeof(MY_STAT)); #ifdef __BORLANDC__ finfo.mystat->st_size=find.ff_fsize; - mode=MY_S_IREAD; attrib=find.ff_attrib; #else finfo.mystat->st_size=find.size; - mode=MY_S_IREAD; attrib=find.attrib; #endif + mode=MY_S_IREAD; if (!(attrib & _A_RDONLY)) mode|=MY_S_IWRITE; if (attrib & _A_SUBDIR) @@ -613,7 +624,7 @@ MY_STAT *my_stat(const char *path, MY_STAT *stat_area, myf my_flags) { int m_used; DBUG_ENTER("my_stat"); - DBUG_PRINT("my", ("path: '%s', stat_area: %lx, MyFlags: %d", path, + DBUG_PRINT("my", ("path: '%s', stat_area: 0x%lx, MyFlags: %d", path, (byte *) stat_area, my_flags)); if ((m_used= (stat_area == NULL))) diff --git a/mysys/my_lock.c b/mysys/my_lock.c index add274bdcec..b8307f366c0 100644 --- a/mysys/my_lock.c +++ b/mysys/my_lock.c @@ -35,7 +35,14 @@ #include <nks/fsio.h> #endif - /* Lock a part of a file */ +/* + Lock a part of a file + + RETURN VALUE + 0 Success + -1 An error has occured and 'my_errno' is set + to indicate the actual error code. +*/ int my_lock(File fd, int locktype, my_off_t start, my_off_t length, myf MyFlags) @@ -104,10 +111,22 @@ int my_lock(File fd, int locktype, my_off_t start, my_off_t length, #elif defined(HAVE_LOCKING) /* Windows */ { - my_bool error; + my_bool error= FALSE; pthread_mutex_lock(&my_file_info[fd].mutex); - if (MyFlags & MY_SEEK_NOT_DONE) - VOID(my_seek(fd,start,MY_SEEK_SET,MYF(MyFlags & ~MY_SEEK_NOT_DONE))); + if (MyFlags & MY_SEEK_NOT_DONE) + { + if( my_seek(fd,start,MY_SEEK_SET,MYF(MyFlags & ~MY_SEEK_NOT_DONE)) + == MY_FILEPOS_ERROR ) + { + /* + If my_seek fails my_errno will already contain an error code; + just unlock and return error code. + */ + DBUG_PRINT("error",("my_errno: %d (%d)",my_errno,errno)); + pthread_mutex_unlock(&my_file_info[fd].mutex); + DBUG_RETURN(-1); + } + } error= locking(fd,locktype,(ulong) length) && errno != EINVAL; pthread_mutex_unlock(&my_file_info[fd].mutex); if (!error) @@ -117,10 +136,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= 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 */ @@ -143,7 +164,17 @@ int my_lock(File fd, int locktype, my_off_t start, my_off_t length, } #else if (MyFlags & MY_SEEK_NOT_DONE) - VOID(my_seek(fd,start,MY_SEEK_SET,MYF(MyFlags & ~MY_SEEK_NOT_DONE))); + { + if (my_seek(fd,start,MY_SEEK_SET,MYF(MyFlags & ~MY_SEEK_NOT_DONE)) + == MY_FILEPOS_ERROR) + { + /* + If an error has occured in my_seek then we will already + have an error code in my_errno; Just return error code. + */ + DBUG_RETURN(-1); + } + } if (lockf(fd,locktype,length) != -1) DBUG_RETURN(0); #endif /* HAVE_FCNTL */ diff --git a/mysys/my_lwrite.c b/mysys/my_lwrite.c index cfdbd5d4576..02c56a69ba4 100644 --- a/mysys/my_lwrite.c +++ b/mysys/my_lwrite.c @@ -23,7 +23,7 @@ uint32 my_lwrite(int Filedes, const byte *Buffer, uint32 Count, myf MyFlags) { uint32 writenbytes; DBUG_ENTER("my_lwrite"); - DBUG_PRINT("my",("Fd: %d Buffer: %lx Count: %ld MyFlags: %d", + DBUG_PRINT("my",("Fd: %d Buffer: 0x%lx Count: %ld MyFlags: %d", Filedes, Buffer, Count, MyFlags)); DBUG_PRINT("error", ("Deprecated my_lwrite() function should not be used.")); diff --git a/mysys/my_malloc.c b/mysys/my_malloc.c index b273363aaf1..3f601a42dc9 100644 --- a/mysys/my_malloc.c +++ b/mysys/my_malloc.c @@ -24,27 +24,27 @@ /* 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); - DBUG_PRINT("exit",("ptr: %lx",point)); + else if (my_flags & MY_ZEROFILL) + bzero(point,size); + DBUG_PRINT("exit",("ptr: 0x%lx",point)); DBUG_RETURN(point); } /* my_malloc */ @@ -55,7 +55,7 @@ gptr my_malloc(unsigned int Size, myf MyFlags) void my_no_flags_free(gptr ptr) { DBUG_ENTER("my_free"); - DBUG_PRINT("my",("ptr: %lx",ptr)); + DBUG_PRINT("my",("ptr: 0x%lx",ptr)); if (ptr) free(ptr); DBUG_VOID_RETURN; @@ -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..7cf40b57403 100644 --- a/mysys/my_open.c +++ b/mysys/my_open.c @@ -46,6 +46,16 @@ File my_open(const char *FileName, int Flags, myf MyFlags) DBUG_PRINT("my",("Name: '%s' Flags: %d MyFlags: %d", FileName, Flags, MyFlags)); #if defined(MSDOS) || defined(__WIN__) || defined(__EMX__) || defined(OS2) + /* + Check that we don't try to open or create a file name that may + cause problems for us in the future (like PRN) + */ + if (check_if_legal_filename(FileName)) + { + errno= EACCES; + DBUG_RETURN(my_register_filename(-1, FileName, FILE_BY_OPEN, + EE_FILENOTFOUND, MyFlags)); + } if (Flags & O_SHARE) fd = sopen((my_string) FileName, (Flags & ~O_SHARE) | O_BINARY, SH_DENYNO, MY_S_IREAD | MY_S_IWRITE); @@ -86,7 +96,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 +125,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_pread.c b/mysys/my_pread.c index 70990ad12a6..f378d548731 100644 --- a/mysys/my_pread.c +++ b/mysys/my_pread.c @@ -29,7 +29,7 @@ uint my_pread(File Filedes, byte *Buffer, uint Count, my_off_t offset, uint readbytes; int error; DBUG_ENTER("my_pread"); - DBUG_PRINT("my",("Fd: %d Seek: %lu Buffer: %lx Count: %u MyFlags: %d", + DBUG_PRINT("my",("Fd: %d Seek: %lu Buffer: 0x%lx Count: %u MyFlags: %d", Filedes, (ulong) offset, Buffer, Count, MyFlags)); for (;;) @@ -86,7 +86,7 @@ uint my_pwrite(int Filedes, const byte *Buffer, uint Count, my_off_t offset, uint writenbytes,errors; ulong written; DBUG_ENTER("my_pwrite"); - DBUG_PRINT("my",("Fd: %d Seek: %lu Buffer: %lx Count: %d MyFlags: %d", + DBUG_PRINT("my",("Fd: %d Seek: %lu Buffer: 0x%lx Count: %d MyFlags: %d", Filedes, (ulong) offset,Buffer, Count, MyFlags)); errors=0; written=0L; diff --git a/mysys/my_pthread.c b/mysys/my_pthread.c index fd716448e43..50c0a82d2d8 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 @@ -31,8 +30,7 @@ #define SCHED_POLICY SCHED_OTHER #endif -uint thd_lib_detected; -uint thr_client_alarm; +uint thd_lib_detected= 0; #ifndef my_pthread_setprio void my_pthread_setprio(pthread_t thread_id,int prior) @@ -101,25 +99,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 @@ -144,10 +140,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; @@ -159,6 +158,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) @@ -303,8 +318,6 @@ void sigwait_handle_sig(int sig) pthread_mutex_unlock(&LOCK_sigwait); } -extern pthread_t alarm_thread; - void *sigwait_thread(void *set_arg) { sigset_t *set=(sigset_t*) set_arg; @@ -393,23 +406,6 @@ int sigwait(sigset_t *setp, int *sigp) #endif /* DONT_USE_SIGSUSPEND */ #endif /* HAVE_SIGWAIT */ -/***************************************************************************** -** Implement pthread_signal for systems that can't use signal() with threads -** Currently this is only used with BSDI 3.0 -*****************************************************************************/ - -#ifdef USE_PTHREAD_SIGNAL - -int pthread_signal(int sig, void (*func)()) -{ - struct sigaction sact; - sact.sa_flags= 0; - sact.sa_handler= func; - sigemptyset(&sact.sa_mask); - sigaction(sig, &sact, (struct sigaction*) 0); - return 0; -} -#endif /**************************************************************************** The following functions fixes that all pthread functions should work diff --git a/mysys/my_read.c b/mysys/my_read.c index ff47a7c67f9..8b88e483fef 100644 --- a/mysys/my_read.c +++ b/mysys/my_read.c @@ -38,7 +38,7 @@ uint my_read(File Filedes, byte *Buffer, uint Count, myf MyFlags) { uint readbytes, save_count; DBUG_ENTER("my_read"); - DBUG_PRINT("my",("Fd: %d Buffer: %lx Count: %u MyFlags: %d", + DBUG_PRINT("my",("Fd: %d Buffer: 0x%lx Count: %u MyFlags: %d", Filedes, Buffer, Count, MyFlags)); save_count= Count; diff --git a/mysys/my_realloc.c b/mysys/my_realloc.c index 49d96c2eb4f..c8edb172890 100644 --- a/mysys/my_realloc.c +++ b/mysys/my_realloc.c @@ -23,42 +23,43 @@ /* 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: 0x%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)); + DBUG_PRINT("exit",("ptr: 0x%lx",point)); DBUG_RETURN(point); } /* my_realloc */ 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..805a5654ffc 100644 --- a/mysys/my_seek.c +++ b/mysys/my_seek.c @@ -15,10 +15,33 @@ 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*/ +/* + Seek to a position in a file. + + ARGUMENTS + File fd The file descriptor + my_off_t pos The expected position (absolute or relative) + int whence A direction parameter and one of + {SEEK_SET, SEEK_CUR, SEEK_END} + myf MyFlags MY_THREADSAFE must be set in case my_seek may be mixed + with my_pread/my_pwrite calls and fd is shared among + threads. + + DESCRIPTION + The my_seek function is a wrapper around the system call lseek and + repositions the offset of the file descriptor fd to the argument + offset according to the directive whence as follows: + SEEK_SET The offset is set to offset bytes. + SEEK_CUR The offset is set to its current location plus offset bytes + SEEK_END The offset is set to the size of the file plus offset bytes + + RETURN VALUE + my_off_t newpos The new position in the file. + MY_FILEPOS_ERROR An error was encountered while performing + the seek. my_errno is set to indicate the + actual error. +*/ my_off_t my_seek(File fd, my_off_t pos, int whence, myf MyFlags __attribute__((unused))) @@ -30,7 +53,16 @@ my_off_t my_seek(File fd, my_off_t pos, int whence, whence, MyFlags)); DBUG_ASSERT(pos != MY_FILEPOS_ERROR); /* safety check */ - newpos=lseek(fd, pos, whence); +#if defined(THREAD) && !defined(HAVE_PREAD) + if (MyFlags & MY_THREADSAFE) + { + pthread_mutex_lock(&my_file_info[fd].mutex); + newpos= lseek(fd, pos, whence); + pthread_mutex_unlock(&my_file_info[fd].mutex); + } + else +#endif + newpos= lseek(fd, pos, whence); if (newpos == (os_off_t) -1) { my_errno=errno; diff --git a/mysys/my_sleep.c b/mysys/my_sleep.c index 3de2d2abd13..31eaf7eeb96 100644 --- a/mysys/my_sleep.c +++ b/mysys/my_sleep.c @@ -23,6 +23,8 @@ void my_sleep(ulong m_seconds) { #ifdef __NETWARE__ delay(m_seconds/1000+1); +#elif defined(__WIN__) + Sleep(m_seconds/1000+1); /* Sleep() has millisecond arg */ #elif defined(OS2) DosSleep(m_seconds/1000+1); #elif defined(HAVE_SELECT) 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..7be3fcd36f0 100644 --- a/mysys/my_symlink.c +++ b/mysys/my_symlink.c @@ -26,9 +26,11 @@ /* Reads the content of a symbolic link If the file is not a symbolic link, return the original file name in to. - Returns: 0 if table was a symlink, - 1 if table was a normal file - -1 on error. + + RETURN + 0 If filename was a symlink, (to will be set to value of symlink) + 1 If filename was a normal file (to will be set to filename) + -1 on error. */ int my_readlink(char *to, const char *filename, myf MyFlags) @@ -58,6 +60,7 @@ int my_readlink(char *to, const char *filename, myf MyFlags) } else to[length]=0; + DBUG_PRINT("exit" ,("result: %d", result)); DBUG_RETURN(result); #endif /* HAVE_READLINK */ } @@ -124,20 +127,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_symlink2.c b/mysys/my_symlink2.c index 4d58699412a..7aa4e359bb9 100644 --- a/mysys/my_symlink2.c +++ b/mysys/my_symlink2.c @@ -32,6 +32,7 @@ File my_create_with_symlink(const char *linkname, const char *filename, int tmp_errno; /* Test if we should create a link */ int create_link; + char abs_linkname[FN_REFLEN]; DBUG_ENTER("my_create_with_symlink"); if (my_disable_symlinks) @@ -42,7 +43,11 @@ File my_create_with_symlink(const char *linkname, const char *filename, filename= linkname; } else - create_link= (linkname && strcmp(linkname,filename)); + { + if (linkname) + my_realpath(abs_linkname, linkname, MYF(0)); + create_link= (linkname && strcmp(abs_linkname,filename)); + } if (!(MyFlags & MY_DELETE_OLD)) { 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 f4fd96b769d..42f025c39f3 100644 --- a/mysys/my_thr_init.c +++ b/mysys/my_thr_init.c @@ -29,10 +29,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 @@ -42,7 +42,7 @@ pthread_mutex_t LOCK_gethostbyname_r; pthread_mutexattr_t my_fast_mutexattr; #endif #ifdef PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP -pthread_mutexattr_t my_errchk_mutexattr; +pthread_mutexattr_t my_errorcheck_mutexattr; #endif static uint get_thread_lib(void); @@ -60,33 +60,40 @@ static uint get_thread_lib(void); my_bool my_thread_global_init(void) { -#if defined(SIGALRM) || defined(SIGUSR1) - /* On Windows, these signals are not defined, but this whole part is not needed. */ thd_lib_detected= get_thread_lib(); - if (thd_lib_detected == THD_LIB_LT) - thr_client_alarm= SIGALRM; - else - thr_client_alarm= SIGUSR1; -#endif if (pthread_key_create(&THR_KEY_mysys,0)) { fprintf(stderr,"Can't initialize threads: error %d\n",errno); return 1; } + #ifdef PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP + /* + Set mutex type to "fast" a.k.a "adaptive" + + The mutex kind determines what happens if a thread attempts to lock + a mutex it already owns with pthread_mutex_lock(3). If the mutex + is of the ``fast'' kind, pthread_mutex_lock(3) simply suspends + the calling thread forever. If the mutex is of the ``error checking'' + kind, pthread_mutex_lock(3) returns immediately with the error + code EDEADLK. + */ pthread_mutexattr_init(&my_fast_mutexattr); - pthread_mutexattr_setkind_np(&my_fast_mutexattr,PTHREAD_MUTEX_ADAPTIVE_NP); + pthread_mutexattr_settype(&my_fast_mutexattr, + PTHREAD_MUTEX_ADAPTIVE_NP); #endif #ifdef PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP - pthread_mutexattr_init(&my_errchk_mutexattr); - pthread_mutexattr_setkind_np(&my_errchk_mutexattr, - PTHREAD_MUTEX_ERRORCHECK_NP); + /* + Set mutex type to "errorcheck" a.k.a "adaptive" + */ + pthread_mutexattr_init(&my_errorcheck_mutexattr); + pthread_mutexattr_settype(&my_errorcheck_mutexattr, + PTHREAD_MUTEX_ERRORCHECK); #endif 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); @@ -96,7 +103,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 @@ -118,18 +125,17 @@ void my_thread_global_end(void) pthread_mutexattr_destroy(&my_fast_mutexattr); #endif #ifdef PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP - pthread_mutexattr_destroy(&my_errchk_mutexattr); + pthread_mutexattr_destroy(&my_errorcheck_mutexattr); #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 @@ -217,7 +223,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 e7cc780060c..00000000000 --- a/mysys/my_vsnprintf.c +++ /dev/null @@ -1,180 +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 <my_global.h> -#include <m_string.h> -#include <stdarg.h> -#include <m_ctype.h> - -/* - Limited snprintf() implementations - - IMPLEMENTION: - Supports following formats: - %#[l]d - %#[l]u - %#[l]x - %#.#s Note first # is ignored - - RETURN - length of result string -*/ - -int my_vsnprintf(char *to, size_t n, const char* fmt, va_list ap) -{ - char *start=to, *end=to+n-1; - uint length, width, pre_zero, have_long; - - for (; *fmt ; fmt++) - { - if (fmt[0] != '%') - { - if (to == end) /* End of buffer */ - break; - *to++= *fmt; /* Copy ordinary char */ - continue; - } - fmt++; /* skip '%' */ - /* Read max fill size (only used with %d and %u) */ - if (*fmt == '-') - fmt++; - length= width= pre_zero= have_long= 0; - for (;isdigit(*fmt); fmt++) - { - length=length*10+ (uint) (*fmt-'0'); - if (!length) - pre_zero= 1; /* first digit was 0 */ - } - if (*fmt == '.') - for (fmt++;isdigit(*fmt); fmt++) - width=width*10+ (uint) (*fmt-'0'); - else - width= ~0; - if (*fmt == 'l') - { - fmt++; - have_long= 1; - } - if (*fmt == 's') /* String parameter */ - { - reg2 char *par = va_arg(ap, char *); - uint plen,left_len = (uint)(end-to)+1; - if (!par) par = (char*)"(null)"; - plen = (uint) strlen(par); - set_if_smaller(plen,width); - if (left_len <= plen) - plen = left_len - 1; - to=strnmov(to,par,plen); - continue; - } - else if (*fmt == 'd' || *fmt == 'u'|| *fmt== 'x') /* Integer parameter */ - { - register long larg; - uint res_length, to_length; - char *store_start= to, *store_end; - char buff[32]; - - if ((to_length= (uint) (end-to)) < 16 || length) - store_start= buff; - if (have_long) - larg = va_arg(ap, long); - else - if (*fmt == 'd') - larg = va_arg(ap, int); - else - larg= (long) (uint) va_arg(ap, int); - if (*fmt == 'd') - store_end= int10_to_str(larg, store_start, -10); - else - if (*fmt== 'u') - store_end= int10_to_str(larg, store_start, 10); - else - store_end= int2str(larg, store_start, 16); - if ((res_length= (uint) (store_end - store_start)) > to_length) - break; /* num doesn't fit in output */ - /* If %#d syntax was used, we have to pre-zero/pre-space the string */ - if (store_start == buff) - { - length= min(length, to_length); - if (res_length < length) - { - uint diff= (length- res_length); - bfill(to, diff, pre_zero ? '0' : ' '); - to+= diff; - } - bmove(to, store_start, res_length); - } - to+= res_length; - continue; - } - /* We come here on '%%', unknown code or too long parameter */ - if (to == end) - break; - *to++='%'; /* % used as % or unknown code */ - } - *to='\0'; /* End of errmessage */ - return (uint) (to - start); -} - -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; -} - -#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 ((uchar) 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("Hex: %lx '%6lx'\n", 32, 65); - my_printf("conn %ld to: '%-.64s' user: '%-.32s' host:\ - `%-.64s' (%-.64s)", 1, 0,0,0,0); - return 0; -} -#endif diff --git a/mysys/my_windac.c b/mysys/my_windac.c new file mode 100644 index 00000000000..2c1027e4aa6 --- /dev/null +++ b/mysys/my_windac.c @@ -0,0 +1,224 @@ +/* Copyright (C) 2000-2005 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" +#ifdef __WIN__ + +/* Windows NT/2000 discretionary access control utility functions. */ + +/* + Check if the operating system is built on NT technology. + + RETURN + 0 Windows 95/98/Me + 1 otherwise +*/ + +static my_bool is_nt() +{ + return GetVersion() < 0x80000000; +} + +/* + Auxilary structure to store pointers to the data which we need to keep + around while SECURITY_ATTRIBUTES is in use. +*/ + +typedef struct st_my_security_attr +{ + PSID everyone_sid; + PACL dacl; +} My_security_attr; + + +/* + Allocate and initialize SECURITY_ATTRIBUTES setting up access + rights for the owner and group `Everybody'. + + SYNOPSIS + my_security_attr_create() + psa [OUT] pointer to store the pointer to SA in + perror [OUT] pointer to store error message if there was an + error + owner_rights [IN] access rights for the owner + everyone_rights [IN] access rights for group Everybody + + DESCRIPTION + Set up the security attributes to provide clients with sufficient + access rights to a kernel object. We need this function + because if we simply grant all access to everybody (by installing + a NULL DACL) a mailicious user can attempt a denial of service + attack by taking ownership over the kernel object. Upon successful + return `psa' contains a pointer to SECUIRITY_ATTRIBUTES that can be used + to create kernel objects with proper access rights. + + RETURN + 0 success, psa is 0 or points to a valid SA structure, + perror is left intact + !0 error, SA is set to 0, error message is stored in perror +*/ + +int my_security_attr_create(SECURITY_ATTRIBUTES **psa, const char **perror, + DWORD owner_rights, DWORD everyone_rights) +{ + /* Top-level SID authority */ + SID_IDENTIFIER_AUTHORITY world_auth= SECURITY_WORLD_SID_AUTHORITY; + PSID everyone_sid= 0; + HANDLE htoken= 0; + SECURITY_ATTRIBUTES *sa= 0; + PACL dacl= 0; + DWORD owner_token_length, dacl_length; + SECURITY_DESCRIPTOR *sd; + PTOKEN_USER owner_token; + PSID owner_sid; + My_security_attr *attr; + + if (! is_nt()) + { + *psa= 0; + return 0; + } + + /* + Get SID of Everyone group. Easier to retrieve all SIDs each time + this function is called than worry about thread safety. + */ + if (! AllocateAndInitializeSid(&world_auth, 1, SECURITY_WORLD_RID, + 0, 0, 0, 0, 0, 0, 0, &everyone_sid)) + { + *perror= "Failed to retrieve the SID of Everyone group"; + goto error; + } + + /* + Get SID of the owner. Using GetSecurityInfo this task can be done + in just one call instead of five, but GetSecurityInfo declared in + aclapi.h, so I hesitate to use it. + SIC: OpenThreadToken works only if there is an active impersonation + token, hence OpenProcessToken is used. + */ + if (! OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &htoken)) + { + *perror= "Failed to retrieve thread access token"; + goto error; + } + GetTokenInformation(htoken, TokenUser, 0, 0, &owner_token_length); + + if (! my_multi_malloc(MYF(MY_WME), + &sa, ALIGN_SIZE(sizeof(SECURITY_ATTRIBUTES)) + + sizeof(My_security_attr), + &sd, sizeof(SECURITY_DESCRIPTOR), + &owner_token, owner_token_length, + 0)) + { + *perror= "Failed to allocate memory for SECURITY_ATTRIBUTES"; + goto error; + } + bzero(owner_token, owner_token_length); + if (! GetTokenInformation(htoken, TokenUser, owner_token, + owner_token_length, &owner_token_length)) + { + *perror= "GetTokenInformation failed"; + goto error; + } + owner_sid= owner_token->User.Sid; + + if (! IsValidSid(owner_sid)) + { + *perror= "IsValidSid failed"; + goto error; + } + + /* Calculate the amount of memory that must be allocated for the DACL */ + dacl_length= sizeof(ACL) + (sizeof(ACCESS_ALLOWED_ACE)-sizeof(DWORD)) * 2 + + GetLengthSid(everyone_sid) + GetLengthSid(owner_sid); + + /* Create an ACL */ + if (! (dacl= (PACL) my_malloc(dacl_length, MYF(MY_ZEROFILL|MY_WME)))) + { + *perror= "Failed to allocate memory for DACL"; + goto error; + } + if (! InitializeAcl(dacl, dacl_length, ACL_REVISION)) + { + *perror= "Failed to initialize DACL"; + goto error; + } + if (! AddAccessAllowedAce(dacl, ACL_REVISION, everyone_rights, everyone_sid)) + { + *perror= "Failed to set up DACL"; + goto error; + } + if (! AddAccessAllowedAce(dacl, ACL_REVISION, owner_rights, owner_sid)) + { + *perror= "Failed to set up DACL"; + goto error; + } + if (! InitializeSecurityDescriptor(sd, SECURITY_DESCRIPTOR_REVISION)) + { + *perror= "Could not initialize security descriptor"; + goto error; + } + if (! SetSecurityDescriptorDacl(sd, TRUE, dacl, FALSE)) + { + *perror= "Failed to install DACL"; + goto error; + } + + sa->nLength= sizeof(*sa); + sa->bInheritHandle= TRUE; + sa->lpSecurityDescriptor= sd; + /* Save pointers to everyone_sid and dacl to be able to clean them up */ + attr= (My_security_attr*) (((char*) sa) + ALIGN_SIZE(sizeof(*sa))); + attr->everyone_sid= everyone_sid; + attr->dacl= dacl; + *psa= sa; + + CloseHandle(htoken); + return 0; +error: + if (everyone_sid) + FreeSid(everyone_sid); + if (htoken) + CloseHandle(htoken); + my_free((gptr) sa, MYF(MY_ALLOW_ZERO_PTR)); + my_free((gptr) dacl, MYF(MY_ALLOW_ZERO_PTR)); + *psa= 0; + return 1; +} + +/* + Cleanup security attributes freeing used memory. + + SYNOPSIS + my_security_attr_free() + sa security attributes +*/ + +void my_security_attr_free(SECURITY_ATTRIBUTES *sa) +{ + if (sa) + { + My_security_attr *attr= (My_security_attr*) + (((char*)sa) + ALIGN_SIZE(sizeof(*sa))); + FreeSid(attr->everyone_sid); + my_free((gptr) attr->dacl, MYF(0)); + my_free((gptr) sa, MYF(0)); + } +} + +#endif /* __WIN__ */ diff --git a/mysys/my_write.c b/mysys/my_write.c index de762c16a07..ae8cb4ab02b 100644 --- a/mysys/my_write.c +++ b/mysys/my_write.c @@ -26,7 +26,7 @@ uint my_write(int Filedes, const byte *Buffer, uint Count, myf MyFlags) uint writenbytes,errors; ulong written; DBUG_ENTER("my_write"); - DBUG_PRINT("my",("Fd: %d Buffer: %lx Count: %d MyFlags: %d", + DBUG_PRINT("my",("Fd: %d Buffer: 0x%lx Count: %d MyFlags: %d", Filedes, Buffer, Count, MyFlags)); errors=0; written=0L; diff --git a/mysys/mysys_priv.h b/mysys/mysys_priv.h index d7aee04ae20..89a6d8aa2a7 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; diff --git a/mysys/queues.c b/mysys/queues.c index 93d4c303f22..6a285ce7417 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"); @@ -112,28 +164,22 @@ void delete_queue(QUEUE *queue) void queue_insert(register QUEUE *queue, byte *element) { - reg2 uint idx,next; + reg2 uint idx, next; int cmp; - -#ifndef DBUG_OFF - if (queue->elements < queue->max_elements) -#endif + DBUG_ASSERT(queue->elements < queue->max_elements); + queue->root[0]= element; + idx= ++queue->elements; + /* max_at_top swaps the comparison if we want to order by desc */ + while ((cmp= queue->compare(queue->first_cmp_arg, + element + queue->offset_to_key, + queue->root[(next= idx >> 1)] + + queue->offset_to_key)) && + (cmp ^ queue->max_at_top) < 0) { - queue->root[0]=element; - idx= ++queue->elements; - - /* max_at_top swaps the comparison if we want to order by desc */ - while ((cmp=queue->compare(queue->first_cmp_arg, - element+queue->offset_to_key, - queue->root[(next=idx >> 1)] + - queue->offset_to_key)) && - (cmp ^ queue->max_at_top) < 0) - { - queue->root[idx]=queue->root[next]; - idx=next; - } - queue->root[idx]=element; + queue->root[idx]= queue->root[next]; + idx= next; } + queue->root[idx]= element; } /* Remove item from queue */ @@ -141,23 +187,18 @@ void queue_insert(register QUEUE *queue, byte *element) byte *queue_remove(register QUEUE *queue, uint idx) { -#ifndef DBUG_OFF - if (idx >= queue->max_elements) - return 0; -#endif - { - byte *element=queue->root[++idx]; /* Intern index starts from 1 */ - queue->root[idx]=queue->root[queue->elements--]; - _downheap(queue,idx); - return element; - } + byte *element; + DBUG_ASSERT(idx < queue->max_elements); + element= queue->root[++idx]; /* Intern index starts from 1 */ + queue->root[idx]= queue->root[queue->elements--]; + _downheap(queue, idx); + return element; } /* 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 +240,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/raid.cc b/mysys/raid.cc index 0b688464fb3..62587c438ca 100644 --- a/mysys/raid.cc +++ b/mysys/raid.cc @@ -70,7 +70,7 @@ tonu@mysql.com & monty@mysql.com */ -#ifdef __GNUC__ +#ifdef USE_PRAGMA_IMPLEMENTATION #pragma implementation // gcc: Class implementation #endif 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/safemalloc.c b/mysys/safemalloc.c index 07c40fd91b6..6cdf98c5f5f 100644 --- a/mysys/safemalloc.c +++ b/mysys/safemalloc.c @@ -194,7 +194,7 @@ gptr _mymalloc(uint size, const char *filename, uint lineno, myf MyFlags) if ((MyFlags & MY_ZEROFILL) || !sf_malloc_quick) bfill(data, size, (char) (MyFlags & MY_ZEROFILL ? 0 : ALLOC_VAL)); /* Return a pointer to the real data */ - DBUG_PRINT("exit",("ptr: %lx", data)); + DBUG_PRINT("exit",("ptr: 0x%lx", data)); if (sf_min_adress > data) sf_min_adress= data; if (sf_max_adress < data) @@ -259,7 +259,7 @@ void _myfree(gptr ptr, const char *filename, uint lineno, myf myflags) { struct st_irem *irem; DBUG_ENTER("_myfree"); - DBUG_PRINT("enter",("ptr: %lx", ptr)); + DBUG_PRINT("enter",("ptr: 0x%lx", ptr)); if (!sf_malloc_quick) (void) _sanity (filename, lineno); @@ -446,7 +446,7 @@ static int _checkchunk(register struct st_irem *irem, const char *filename, irem->filename, irem->linenum); fprintf(stderr, " discovered at %s:%d\n", filename, lineno); (void) fflush(stderr); - DBUG_PRINT("safe",("Underrun at %lx, allocated at %s:%d", + DBUG_PRINT("safe",("Underrun at 0x%lx, allocated at %s:%d", data, irem->filename, irem->linenum)); flag=1; } @@ -462,7 +462,7 @@ static int _checkchunk(register struct st_irem *irem, const char *filename, irem->filename, irem->linenum); fprintf(stderr, " discovered at '%s:%d'\n", filename, lineno); (void) fflush(stderr); - DBUG_PRINT("safe",("Overrun at %lx, allocated at %s:%d", + DBUG_PRINT("safe",("Overrun at 0x%lx, allocated at %s:%d", data, irem->filename, irem->linenum)); diff --git a/mysys/sha1.c b/mysys/sha1.c index d93b4571baf..110d24f8bfc 100644 --- a/mysys/sha1.c +++ b/mysys/sha1.c @@ -69,7 +69,7 @@ static void SHA1ProcessMessageBlock(SHA1_CONTEXT*); Initialize SHA1Context SYNOPSIS - sha1_reset() + mysql_sha1_reset() context [in/out] The context to reset. DESCRIPTION @@ -92,7 +92,7 @@ const uint32 sha_const_key[5]= }; -int sha1_reset(SHA1_CONTEXT *context) +int mysql_sha1_reset(SHA1_CONTEXT *context) { #ifndef DBUG_OFF if (!context) @@ -119,7 +119,7 @@ int sha1_reset(SHA1_CONTEXT *context) Return the 160-bit message digest into the array provided by the caller SYNOPSIS - sha1_result() + mysql_sha1_result() context [in/out] The context to use to calculate the SHA-1 hash. Message_Digest: [out] Where the digest is returned. @@ -132,8 +132,8 @@ int sha1_reset(SHA1_CONTEXT *context) != SHA_SUCCESS sha Error Code. */ -int sha1_result(SHA1_CONTEXT *context, - uint8 Message_Digest[SHA1_HASH_SIZE]) +int mysql_sha1_result(SHA1_CONTEXT *context, + uint8 Message_Digest[SHA1_HASH_SIZE]) { int i; @@ -165,7 +165,7 @@ int sha1_result(SHA1_CONTEXT *context, Accepts an array of octets as the next portion of the message. SYNOPSIS - sha1_input() + mysql_sha1_input() context [in/out] The SHA context to update message_array An array of characters representing the next portion of the message. @@ -176,8 +176,8 @@ int sha1_result(SHA1_CONTEXT *context, != SHA_SUCCESS sha Error Code. */ -int sha1_input(SHA1_CONTEXT *context, const uint8 *message_array, - unsigned length) +int mysql_sha1_input(SHA1_CONTEXT *context, const uint8 *message_array, + unsigned length) { if (!length) return SHA_SUCCESS; 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..d15016113cd 100644 --- a/mysys/testhash.c +++ b/mysys/testhash.c @@ -74,7 +74,7 @@ static int do_test() bzero((char*) key1,sizeof(key1[0])*1000); printf("- Creating hash\n"); - if (hash_init(&hash,recant/2,0,6,0,free_record,0)) + if (hash_init(&hash, default_charset_info, recant/2, 0, 6, 0, free_record, 0)) goto err; printf("- Writing records:\n"); @@ -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; @@ -172,15 +172,16 @@ static int do_test() break; if (key1[j] > 1) { + HASH_SEARCH_STATE state; printf("- Testing identical read\n"); sprintf(key,"%6d",j); pos=1; - if (!(recpos=hash_search(&hash,key,0))) + if (!(recpos= hash_first(&hash, key, 0, &state))) { printf("can't find key1: \"%s\"\n",key); goto err; } - while (hash_next(&hash,key,0) && pos < (ulong) (key1[j]+10)) + while (hash_next(&hash, key, 0, &state) && pos < (ulong) (key1[j]+10)) pos++; if (pos != (ulong) key1[j]) { @@ -189,7 +190,7 @@ static int do_test() } } printf("- Creating output heap-file 2\n"); - if (hash_init(&hash2,hash.records,0,0,hash2_key,free_record,0)) + if (hash_init(&hash2, default_charset_info, hash.records, 0, 0, hash2_key, free_record,0)) goto err; printf("- Copying and removing records\n"); @@ -199,7 +200,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 c8e1e55aac2..759544af17b 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 */ @@ -37,6 +36,7 @@ #define ETIME ETIMEDOUT #endif +uint thr_client_alarm; static int alarm_aborted=1; /* No alarm thread */ my_bool thr_alarm_inited= 0; volatile my_bool alarm_thread_running= 0; @@ -75,7 +75,7 @@ void init_thr_alarm(uint max_alarms) alarm_aborted=0; init_queue(&alarm_queue,max_alarms+1,offsetof(ALARM,expire_time),0, compare_ulong,NullS); - sigfillset(&full_signal_set); /* Needed to block signals */ + sigfillset(&full_signal_set); /* Neaded to block signals */ pthread_mutex_init(&LOCK_alarm,MY_MUTEX_INIT_FAST); pthread_cond_init(&COND_alarm,NULL); if (thd_lib_detected == THD_LIB_LT) @@ -255,11 +255,10 @@ void thr_end_alarm(thr_alarm_t *alarmed) if (!found) { if (*alarmed) - fprintf(stderr, - "Warning: Didn't find alarm 0x%lx in queue of %d alarms\n", - (long) *alarmed, alarm_queue.elements); + fprintf(stderr,"Warning: Didn't find alarm 0x%lx in queue of %d alarms\n", + (long) *alarmed, alarm_queue.elements); DBUG_PRINT("warning",("Didn't find alarm 0x%lx in queue\n", - (long) *alarmed)); + (long) *alarmed)); } pthread_mutex_unlock(&LOCK_alarm); pthread_sigmask(SIG_SETMASK,&old_mask,NULL); @@ -307,7 +306,7 @@ sig_handler process_alarm(int sig __attribute__((unused))) process_alarm_part2(sig); #ifndef USE_ALARM_THREAD #if defined(DONT_REMEMBER_SIGNAL) && !defined(USE_ONE_SIGNAL_HAND) - my_sigset(THR_SERVER_ALARM, process_alarm); + my_sigset(THR_SERVER_ALARM,process_alarm); #endif pthread_mutex_unlock(&LOCK_alarm); pthread_sigmask(SIG_SETMASK,&old_mask,NULL); @@ -499,13 +498,14 @@ void thr_alarm_info(ALARM_INFO *info) ARGSUSED */ + static sig_handler thread_alarm(int sig) { #ifdef MAIN printf("thread_alarm\n"); fflush(stdout); #endif #ifdef DONT_REMEMBER_SIGNAL - my_sigset(sig, thread_alarm); /* int. thread system calls */ + my_sigset(sig,thread_alarm); /* int. thread system calls */ #endif } @@ -908,7 +908,7 @@ static sig_handler print_signal_warning(int sig) printf("Warning: Got signal %d from thread %s\n",sig,my_thread_name()); fflush(stdout); #ifdef DONT_REMEMBER_SIGNAL - my_sigset(sig, print_signal_warning); /* int. thread system calls */ + my_sigset(sig,print_signal_warning); /* int. thread system calls */ #endif #ifndef OS2 if (sig == SIGALRM) diff --git a/mysys/thr_lock.c b/mysys/thr_lock.c index a789162a56a..d6443539216 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; } @@ -435,7 +435,7 @@ int thr_lock(THR_LOCK_DATA *data,enum thr_lock_type lock_type) data->thread=pthread_self(); /* Must be reset ! */ data->thread_id=my_thread_id(); /* Must be reset ! */ VOID(pthread_mutex_lock(&lock->mutex)); - DBUG_PRINT("lock",("data: %lx thread: %ld lock: %lx type: %d", + DBUG_PRINT("lock",("data: 0x%lx thread: %ld lock: 0x%lx type: %d", data,data->thread_id,lock,(int) lock_type)); check_locks(lock,(uint) lock_type <= (uint) TL_READ_NO_INSERT ? "enter read_lock" : "enter write_lock",0); @@ -664,7 +664,7 @@ void thr_unlock(THR_LOCK_DATA *data) THR_LOCK *lock=data->lock; enum thr_lock_type lock_type=data->type; DBUG_ENTER("thr_unlock"); - DBUG_PRINT("lock",("data: %lx thread: %ld lock: %lx", + DBUG_PRINT("lock",("data: 0x%lx thread: %ld lock: 0x%lx", data,data->thread_id,lock)); pthread_mutex_lock(&lock->mutex); check_locks(lock,"start of release lock",0); @@ -835,7 +835,7 @@ int thr_multi_lock(THR_LOCK_DATA **data,uint count) { THR_LOCK_DATA **pos,**end; DBUG_ENTER("thr_multi_lock"); - DBUG_PRINT("lock",("data: %lx count: %d",data,count)); + DBUG_PRINT("lock",("data: 0x%lx count: %d",data,count)); if (count > 1) sort_locks(data,count); /* lock everything */ @@ -847,7 +847,7 @@ int thr_multi_lock(THR_LOCK_DATA **data,uint count) DBUG_RETURN(1); } #ifdef MAIN - printf("Thread: %s Got lock: %lx type: %d\n",my_thread_name(), + printf("Thread: %s Got lock: 0x%lx type: %d\n",my_thread_name(), (long) pos[0]->lock, pos[0]->type); fflush(stdout); #endif } @@ -907,12 +907,12 @@ void thr_multi_unlock(THR_LOCK_DATA **data,uint count) { THR_LOCK_DATA **pos,**end; DBUG_ENTER("thr_multi_unlock"); - DBUG_PRINT("lock",("data: %lx count: %d",data,count)); + DBUG_PRINT("lock",("data: 0x%lx count: %d",data,count)); for (pos=data,end=data+count; pos < end ; pos++) { #ifdef MAIN - printf("Thread: %s Rel lock: %lx type: %d\n", + printf("Thread: %s Rel lock: 0x%lx type: %d\n", my_thread_name(), (long) pos[0]->lock, pos[0]->type); fflush(stdout); #endif @@ -920,7 +920,7 @@ void thr_multi_unlock(THR_LOCK_DATA **data,uint count) thr_unlock(*pos); else { - DBUG_PRINT("lock",("Free lock: data: %lx thread: %ld lock: %lx", + DBUG_PRINT("lock",("Free lock: data: 0x%lx thread: %ld lock: 0x%lx", *pos,(*pos)->thread_id,(*pos)->lock)); } } @@ -1109,7 +1109,7 @@ static void thr_print_lock(const char* name,struct st_lock_list *list) prev= &list->data; for (data=list->data; data && count++ < MAX_LOCKS ; data=data->next) { - printf("%lx (%lu:%d); ",(ulong) data,data->thread_id,(int) data->type); + printf("0x%lx (%lu:%d); ",(ulong) data,data->thread_id,(int) data->type); if (data->prev != prev) printf("\nWarning: prev didn't point at previous lock\n"); prev= &data->next; @@ -1127,11 +1127,12 @@ 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= list_rest(list)) { THR_LOCK *lock=(THR_LOCK*) list->data; VOID(pthread_mutex_lock(&lock->mutex)); - printf("lock: %lx:",(ulong) lock); + printf("lock: 0x%lx:",(ulong) lock); if ((lock->write_wait.data || lock->read_wait.data) && (! lock->read.data && ! lock->write.data)) printf(" WARNING: "); diff --git a/mysys/thr_mutex.c b/mysys/thr_mutex.c index 3abac2dc737..2facb4e18cf 100644 --- a/mysys/thr_mutex.c +++ b/mysys/thr_mutex.c @@ -17,7 +17,7 @@ /* This makes a wrapper for mutex handling to make it easier to debug mutex */ #include <my_global.h> -#if defined(HAVE_LINUXTHREADS) && !defined (__USE_UNIX98) +#if defined(TARGET_OS_LINUX) && !defined (__USE_UNIX98) #define __USE_UNIX98 /* To get rw locks under Linux */ #endif #if defined(THREAD) && defined(SAFE_MUTEX) @@ -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; @@ -198,13 +210,13 @@ int safe_cond_wait(pthread_cond_t *cond, safe_mutex_t *mp, const char *file, if (mp->count++) { fprintf(stderr, - "safe_mutex: Count was %d in thread %lx when locking mutex at %s, line %d\n", + "safe_mutex: Count was %d in thread 0x%lx when locking mutex at %s, line %d\n", mp->count-1, my_thread_id(), file, line); fflush(stderr); abort(); } mp->thread=pthread_self(); - mp->file= (char*) file; + mp->file= file; mp->line=line; pthread_mutex_unlock(&mp->global); return error; @@ -236,13 +248,13 @@ int safe_cond_timedwait(pthread_cond_t *cond, safe_mutex_t *mp, if (mp->count++) { fprintf(stderr, - "safe_mutex: Count was %d in thread %lx when locking mutex at %s, line %d (error: %d (%d))\n", + "safe_mutex: Count was %d in thread 0x%lx when locking mutex at %s, line %d (error: %d (%d))\n", mp->count-1, my_thread_id(), file, line, error, error); fflush(stderr); 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..bec1ec680f1 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 @@ -71,7 +89,7 @@ void init_tree(TREE *tree, uint default_alloc_size, uint memory_limit, tree_element_free free_element, void *custom_arg) { DBUG_ENTER("init_tree"); - DBUG_PRINT("enter",("tree: %lx size: %d",tree,size)); + DBUG_PRINT("enter",("tree: 0x%lx size: %d",tree,size)); if (default_alloc_size < DEFAULT_ALLOC_SIZE) default_alloc_size= DEFAULT_ALLOC_SIZE; @@ -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)))) { @@ -118,7 +137,7 @@ void init_tree(TREE *tree, uint default_alloc_size, uint memory_limit, static void free_tree(TREE *tree, myf free_flags) { DBUG_ENTER("free_tree"); - DBUG_PRINT("enter",("tree: %lx",tree)); + DBUG_PRINT("enter",("tree: 0x%lx",tree)); if (tree->root) /* If initialized */ { @@ -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..90a093b0b32 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) { @@ -36,7 +49,7 @@ int find_type(my_string x, TYPELIB *typelib, uint full_name) reg1 my_string i; reg2 const char *j; DBUG_ENTER("find_type"); - DBUG_PRINT("enter",("x: '%s' lib: %lx",x,typelib)); + DBUG_PRINT("enter",("x: '%s' lib: 0x%lx",x,typelib)); if (!typelib->count) { @@ -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); } |