diff options
Diffstat (limited to 'mysys')
48 files changed, 2665 insertions, 1051 deletions
diff --git a/mysys/Makefile.am b/mysys/Makefile.am index a80df810c1c..2de8d7820f5 100644 --- a/mysys/Makefile.am +++ b/mysys/Makefile.am @@ -17,8 +17,7 @@ MYSQLDATAdir = $(localstatedir) MYSQLSHAREdir = $(pkgdatadir) MYSQLBASEdir= $(prefix) -INCLUDES = @MT_INCLUDES@ \ - @ZLIB_INCLUDES@ -I$(top_builddir)/include \ +INCLUDES = @ZLIB_INCLUDES@ -I$(top_builddir)/include \ -I$(top_srcdir)/include -I$(srcdir) pkglib_LIBRARIES = libmysys.a LDADD = libmysys.a ../dbug/libdbug.a \ @@ -27,7 +26,7 @@ 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 \ +libmysys_a_SOURCES = my_init.c my_getwd.c mf_getdate.c my_mmap.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 \ @@ -46,19 +45,20 @@ libmysys_a_SOURCES = my_init.c my_getwd.c mf_getdate.c \ ptr_cmp.c mf_radix.c queues.c \ tree.c list.c hash.c array.c string.c typelib.c \ my_copy.c my_append.c my_lib.c \ - my_delete.c my_rename.c my_redel.c my_tempnam.c \ + my_delete.c my_rename.c my_redel.c \ my_chsize.c my_lread.c my_lwrite.c my_clock.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 \ + default_modify.c default.c \ + my_compress.c checksum.c raid.cc \ my_net.c my_semaphore.c my_port.c my_sleep.c \ charset.c charset-def.c my_bitmap.c my_bit.c md5.c \ my_gethostbyname.c rijndael.c my_aes.c sha1.c \ - my_handler.c my_netware.c my_windac.c my_access.c + my_handler.c my_netware.c my_largepage.c \ + my_windac.c my_access.c base64.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@ -noinst_PROGRAMS = charset2html @THREAD_LPROGRAMS@ # test_dir_DEPENDENCIES= $(LIBRARIES) # testhash_DEPENDENCIES= $(LIBRARIES) # test_charset_DEPENDENCIES= $(LIBRARIES) @@ -68,6 +68,9 @@ DEFS = -DDEFAULT_BASEDIR=\"$(prefix)\" \ -DDATADIR="\"$(MYSQLDATAdir)\"" \ -DDEFAULT_CHARSET_HOME="\"$(MYSQLBASEdir)\"" \ -DSHAREDIR="\"$(MYSQLSHAREdir)\"" \ + -DDEFAULT_HOME_ENV=MYSQL_HOME \ + -DDEFAULT_GROUP_SUFFIX_ENV=MYSQL_GROUP_SUFFIX \ + -DDEFAULT_SYSCONFDIR="\"$(sysconfdir)\"" \ @DEFS@ libmysys_a_DEPENDENCIES= @THREAD_LOBJECTS@ @@ -106,9 +109,6 @@ 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) @@ -117,5 +117,10 @@ test_gethwaddr$(EXEEXT): my_gethwaddr.c $(LIBRARIES) $(LINK) $(FLAGS) -DMAIN ./test_gethwaddr.c $(LDADD) $(LIBS) $(RM) -f ./test_gethwaddr.c +test_base64$(EXEEXT): base64.c $(LIBRARIES) + $(CP) $(srcdir)/base64.c ./test_base64.c + $(LINK) $(FLAGS) -DMAIN ./test_base64.c $(LDADD) $(LIBS) + $(RM) -f ./test_base64.c + # Don't update the files from bitkeeper %::SCCS/s.% diff --git a/mysys/base64.c b/mysys/base64.c new file mode 100644 index 00000000000..b29c8ff8360 --- /dev/null +++ b/mysys/base64.c @@ -0,0 +1,279 @@ +/* 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 <base64.h> +#include <m_string.h> /* strchr() */ + +#ifndef MAIN + +static char base64_table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789+/"; + + +int +base64_needed_encoded_length(int length_of_data) +{ + int nb_base64_chars; + nb_base64_chars= (length_of_data + 2) / 3 * 4; + + return + nb_base64_chars + /* base64 char incl padding */ + (nb_base64_chars - 1)/ 76 + /* newlines */ + 1; /* NUL termination of string */ +} + + +int +base64_needed_decoded_length(int length_of_encoded_data) +{ + return ceil(length_of_encoded_data * 3 / 4); +} + + +/* + Encode a data as base64. + + Note: We require that dst is pre-allocated to correct size. + See base64_needed_encoded_length(). +*/ + +int +base64_encode(const void *src, size_t src_len, char *dst) +{ + const unsigned char *s= (const unsigned char*)src; + size_t i= 0; + size_t len= 0; + + for (; i < src_len; len += 4) + { + unsigned c; + + if (len == 76) + { + len= 0; + *dst++= '\n'; + } + + c= s[i++]; + c <<= 8; + + if (i < src_len) + c += s[i]; + c <<= 8; + i++; + + if (i < src_len) + c += s[i]; + i++; + + *dst++= base64_table[(c >> 18) & 0x3f]; + *dst++= base64_table[(c >> 12) & 0x3f]; + + if (i > (src_len + 1)) + *dst++= '='; + else + *dst++= base64_table[(c >> 6) & 0x3f]; + + if (i > src_len) + *dst++= '='; + else + *dst++= base64_table[(c >> 0) & 0x3f]; + } + *dst= '\0'; + + return 0; +} + + +static inline unsigned +pos(unsigned char c) +{ + return strchr(base64_table, c) - base64_table; +} + + +#define SKIP_SPACE(src, i, size) \ +{ \ + while (i < size && my_isspace(&my_charset_latin1, * src)) \ + { \ + i++; \ + src++; \ + } \ + if (i == size) \ + { \ + i= size + 1; \ + break; \ + } \ +} + + +/* + Decode a base64 string + + Note: We require that dst is pre-allocated to correct size. + See base64_needed_decoded_length(). + + RETURN Number of bytes produced in dst or -1 in case of failure +*/ +int +base64_decode(const char *src, size_t size, void *dst) +{ + char b[3]; + size_t i= 0; + char *dst_base= (char *)dst; + char *d= dst_base; + size_t j; + + while (i < size) + { + unsigned c= 0; + size_t mark= 0; + + SKIP_SPACE(src, i, size); + + c += pos(*src++); + c <<= 6; + i++; + + SKIP_SPACE(src, i, size); + + c += pos(*src++); + c <<= 6; + i++; + + SKIP_SPACE(src, i, size); + + if (* src != '=') + c += pos(*src++); + else + { + i= size; + mark= 2; + c <<= 6; + goto end; + } + c <<= 6; + i++; + + SKIP_SPACE(src, i, size); + + if (*src != '=') + c += pos(*src++); + else + { + i= size; + mark= 1; + goto end; + } + i++; + + end: + b[0]= (c >> 16) & 0xff; + b[1]= (c >> 8) & 0xff; + b[2]= (c >> 0) & 0xff; + + for (j=0; j<3-mark; j++) + *d++= b[j]; + } + + if (i != size) + { + return -1; + } + return d - dst_base; +} + + +#else /* MAIN */ + +#define require(b) { \ + if (!(b)) { \ + printf("Require failed at %s:%d\n", __FILE__, __LINE__); \ + abort(); \ + } \ +} + + +int +main(void) +{ + int i; + size_t j; + size_t k, l; + size_t dst_len; + size_t needed_length; + + for (i= 0; i < 500; i++) + { + /* Create source data */ + const size_t src_len= rand() % 1000 + 1; + + char * src= (char *) malloc(src_len); + char * s= src; + char * str; + char * dst; + + for (j= 0; j<src_len; j++) + { + char c= rand(); + *s++= c; + } + + /* Encode */ + needed_length= base64_needed_encoded_length(src_len); + str= (char *) malloc(needed_length); + for (k= 0; k < needed_length; k++) + str[k]= 0xff; /* Fill memory to check correct NUL termination */ + require(base64_encode(src, src_len, str) == 0); + require(needed_length == strlen(str) + 1); + + /* Decode */ + dst= (char *) malloc(base64_needed_decoded_length(strlen(str))); + dst_len= base64_decode(str, strlen(str), dst); + require(dst_len == src_len); + + if (memcmp(src, dst, src_len) != 0) + { + printf(" --------- src --------- --------- dst ---------\n"); + for (k= 0; k<src_len; k+=8) + { + printf("%.4x ", (uint) k); + for (l=0; l<8 && k+l<src_len; l++) + { + unsigned char c= src[k+l]; + printf("%.2x ", (unsigned)c); + } + + printf(" "); + + for (l=0; l<8 && k+l<dst_len; l++) + { + unsigned char c= dst[k+l]; + printf("%.2x ", (unsigned)c); + } + printf("\n"); + } + printf("src length: %.8x, dst length: %.8x\n", + (uint) src_len, (uint) dst_len); + require(0); + } + } + printf("Test succeeded.\n"); + return 0; +} + +#endif diff --git a/mysys/charset-def.c b/mysys/charset-def.c index 1fa87b715a8..0559fe59d06 100644 --- a/mysys/charset-def.c +++ b/mysys/charset-def.c @@ -42,6 +42,8 @@ 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; +extern CHARSET_INFO my_charset_ucs2_esperanto_uca_ci; +extern CHARSET_INFO my_charset_ucs2_hungarian_uca_ci; #endif #ifdef HAVE_CHARSET_utf8 @@ -62,6 +64,8 @@ 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; +extern CHARSET_INFO my_charset_utf8_esperanto_uca_ci; +extern CHARSET_INFO my_charset_utf8_hungarian_uca_ci; #ifdef HAVE_UTF8_GENERAL_CS extern CHARSET_INFO my_charset_utf8_general_cs; #endif @@ -97,6 +101,11 @@ my_bool init_compiled_charsets(myf flags __attribute__((unused))) add_compiled_collation(&my_charset_latin2_czech_ci); #endif +#ifdef HAVE_CHARSET_eucjpms + add_compiled_collation(&my_charset_eucjpms_japanese_ci); + add_compiled_collation(&my_charset_eucjpms_bin); +#endif + #ifdef HAVE_CHARSET_euckr add_compiled_collation(&my_charset_euckr_korean_ci); add_compiled_collation(&my_charset_euckr_bin); @@ -143,6 +152,8 @@ my_bool init_compiled_charsets(myf flags __attribute__((unused))) 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); + add_compiled_collation(&my_charset_ucs2_esperanto_uca_ci); + add_compiled_collation(&my_charset_ucs2_hungarian_uca_ci); #endif #endif @@ -175,6 +186,8 @@ my_bool init_compiled_charsets(myf flags __attribute__((unused))) 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); + add_compiled_collation(&my_charset_utf8_esperanto_uca_ci); + add_compiled_collation(&my_charset_utf8_hungarian_uca_ci); #endif #endif diff --git a/mysys/charset.c b/mysys/charset.c index 665f2efecd8..64b15fab0c2 100644 --- a/mysys/charset.c +++ b/mysys/charset.c @@ -113,7 +113,7 @@ static my_bool init_state_maps(CHARSET_INFO *cs) /* 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)'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; } @@ -556,10 +556,10 @@ CHARSET_INFO *get_charset_by_csname(const char *cs_name, DBUG_PRINT("enter",("name: '%s'", cs_name)); (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)) { char index_file[FN_REFLEN]; @@ -571,21 +571,54 @@ CHARSET_INFO *get_charset_by_csname(const char *cs_name, } -ulong escape_string_for_mysql(CHARSET_INFO *charset_info, char *to, +/* + Escape string with backslashes (\) + + SYNOPSIS + escape_string_for_mysql() + charset_info Charset of the strings + to Buffer for escaped string + to_length Length of destination buffer, or 0 + from The string to escape + length The length of the string to escape + + DESCRIPTION + This escapes the contents of a string by adding backslashes before special + characters, and turning others into specific escape sequences, such as + turning newlines into \n and null bytes into \0. + + NOTE + To maintain compatibility with the old C API, to_length may be 0 to mean + "big enough" + + RETURN VALUES + ~0 The escaped string did not fit in the to buffer + >=0 The length of the escaped string +*/ + +ulong escape_string_for_mysql(CHARSET_INFO *charset_info, + char *to, ulong to_length, const char *from, ulong length) { const char *to_start= to; - const char *end; + const char *end, *to_end=to_start + (to_length ? to_length-1 : 2*length); + my_bool overflow= FALSE; #ifdef USE_MB my_bool use_mb_flag= use_mb(charset_info); #endif - for (end= from + length; from != end; from++) + for (end= from + length; from < end; from++) { + char escape= 0; #ifdef USE_MB - int l; - if (use_mb_flag && (l= my_ismbchar(charset_info, from, end))) + int tmp_length; + if (use_mb_flag && (tmp_length= my_ismbchar(charset_info, from, end))) { - while (l--) + if (to + tmp_length > to_end) + { + overflow= TRUE; + break; + } + while (tmp_length--) *to++= *from++; from--; continue; @@ -601,48 +634,55 @@ ulong escape_string_for_mysql(CHARSET_INFO *charset_info, char *to, 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) - { - *to++= '\\'; - *to++= *from; - continue; - } + if (use_mb_flag && (tmp_length= my_mbcharlen(charset_info, *from)) > 1) + escape= *from; + else #endif switch (*from) { case 0: /* Must be escaped for 'mysql' */ - *to++= '\\'; - *to++= '0'; + escape= '0'; break; case '\n': /* Must be escaped for logs */ - *to++= '\\'; - *to++= 'n'; + escape= 'n'; break; case '\r': - *to++= '\\'; - *to++= 'r'; + escape= 'r'; break; case '\\': - *to++= '\\'; - *to++= '\\'; + escape= '\\'; break; case '\'': - *to++= '\\'; - *to++= '\''; + escape= '\''; break; case '"': /* Better safe than sorry */ - *to++= '\\'; - *to++= '"'; + escape= '"'; break; case '\032': /* This gives problems on Win32 */ - *to++= '\\'; - *to++= 'Z'; + escape= 'Z'; break; - default: + } + if (escape) + { + if (to + 2 > to_end) + { + overflow= TRUE; + break; + } + *to++= '\\'; + *to++= escape; + } + else + { + if (to + 1 > to_end) + { + overflow= TRUE; + break; + } *to++= *from; } } *to= 0; - return (ulong) (to - to_start); + return overflow ? (ulong)~0 : (ulong) (to - to_start); } @@ -670,3 +710,84 @@ CHARSET_INFO *fs_character_set() return fs_cset_cache; } #endif + +/* + Escape apostrophes by doubling them up + + SYNOPSIS + escape_quotes_for_mysql() + charset_info Charset of the strings + to Buffer for escaped string + to_length Length of destination buffer, or 0 + from The string to escape + length The length of the string to escape + + DESCRIPTION + This escapes the contents of a string by doubling up any apostrophes that + it contains. This is used when the NO_BACKSLASH_ESCAPES SQL_MODE is in + effect on the server. + + NOTE + To be consistent with escape_string_for_mysql(), to_length may be 0 to + mean "big enough" + + RETURN VALUES + ~0 The escaped string did not fit in the to buffer + >=0 The length of the escaped string +*/ + +ulong escape_quotes_for_mysql(CHARSET_INFO *charset_info, + char *to, ulong to_length, + const char *from, ulong length) +{ + const char *to_start= to; + const char *end, *to_end=to_start + (to_length ? to_length-1 : 2*length); + my_bool overflow= FALSE; +#ifdef USE_MB + my_bool use_mb_flag= use_mb(charset_info); +#endif + for (end= from + length; from < end; from++) + { +#ifdef USE_MB + int tmp_length; + if (use_mb_flag && (tmp_length= my_ismbchar(charset_info, from, end))) + { + if (to + tmp_length > to_end) + { + overflow= TRUE; + break; + } + while (tmp_length--) + *to++= *from++; + from--; + continue; + } + /* + We don't have the same issue here with a non-multi-byte character being + turned into a multi-byte character by the addition of an escaping + character, because we are only escaping the ' character with itself. + */ +#endif + if (*from == '\'') + { + if (to + 2 > to_end) + { + overflow= TRUE; + break; + } + *to++= '\''; + *to++= '\''; + } + else + { + if (to + 1 > to_end) + { + overflow= TRUE; + break; + } + *to++= *from; + } + } + *to= 0; + return overflow ? (ulong)~0 : (ulong) (to - to_start); +} diff --git a/mysys/charset2html.c b/mysys/charset2html.c deleted file mode 100644 index 96862ff16a1..00000000000 --- a/mysys/charset2html.c +++ /dev/null @@ -1,177 +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 - */ - -/* - 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/default.c b/mysys/default.c index 9510cfb3464..90f8dc5f689 100644 --- a/mysys/default.c +++ b/mysys/default.c @@ -30,81 +30,301 @@ --no-defaults ; no options are read. --defaults-file=full-path-to-default-file ; Only this file will be read. --defaults-extra-file=full-path-to-default-file ; Read this file before ~/ - --print-defaults ; Print the modified command line and exit + --defaults-group-suffix ; Also read groups with concat(group, suffix) + --print-defaults ; Print the modified command line and exit ****************************************************************************/ #include "mysys_priv.h" #include "m_string.h" #include "m_ctype.h" #include <my_dir.h> +#ifdef __WIN__ +#include <winbase.h> +#endif +const char *defaults_file=0; +const char *defaults_group_suffix=0; char *defaults_extra_file=0; /* Which directories are searched for options (and in which order) */ -const char *default_directories[]= { -#ifdef __WIN__ -"C:/", -#elif defined(__NETWARE__) -"sys:/etc/", -#else -"/etc/", -#endif -#ifdef DATADIR -DATADIR, -#endif -"", /* Place for defaults_extra_dir */ -#if !defined(__WIN__) && !defined(__NETWARE__) -"~/", -#endif -NullS, -}; +#define MAX_DEFAULT_DIRS 7 +const char *default_directories[MAX_DEFAULT_DIRS + 1]; #ifdef __WIN__ static const char *f_extensions[]= { ".ini", ".cnf", 0 }; +#define NEWLINE "\r\n" +static char system_dir[FN_REFLEN], shared_system_dir[FN_REFLEN], + config_dir[FN_REFLEN]; #else static const char *f_extensions[]= { ".cnf", 0 }; +#define NEWLINE "\n" #endif -static int search_default_file(DYNAMIC_ARRAY *args,MEM_ROOT *alloc, - const char *dir, const char *config_file, - TYPELIB *group); +static int handle_default_option(void *in_ctx, const char *group_name, + const char *option); + +/* + This structure defines the context that we pass to callback + function 'handle_default_option' used in search_default_file + to process each option. This context is used if search_default_file + was called from load_defaults. +*/ + +struct handle_option_ctx +{ + MEM_ROOT *alloc; + DYNAMIC_ARRAY *args; + TYPELIB *group; +}; -static int search_default_file_with_ext(DYNAMIC_ARRAY *args, MEM_ROOT *alloc, +static int search_default_file(Process_option_func func, void *func_ctx, + const char *dir, const char *config_file); +static int search_default_file_with_ext(Process_option_func func, + void *func_ctx, const char *dir, const char *ext, - const char *config_file, - TYPELIB *group, int recursion_level); + const char *config_file, int recursion_level); +static void init_default_directories(); static char *remove_end_comment(char *ptr); /* - Gets --defaults-file and --defaults-extra-file options from command line. + Process config files in default directories. SYNOPSIS - get_defaults_files() + my_search_option_files() + conf_file Basename for configuration file to search for. + If this is a path, then only this file is read. + argc Pointer to argc of original program + argv Pointer to argv of original program + args_used Pointer to variable for storing the number of + arguments used. + func Pointer to the function to process options + func_ctx It's context. Usually it is the structure to + store additional options. + DESCRIPTION + Process the default options from argc & argv + Read through each found config file looks and calls 'func' to process + each option. + + NOTES + --defaults-group-suffix is only processed if we are called from + load_defaults(). + + + RETURN + 0 ok + 1 given cinf_file doesn't exist + + The global variable 'defaults_group_suffix' is updated with value for + --defaults_group_suffix +*/ + +int my_search_option_files(const char *conf_file, int *argc, char ***argv, + uint *args_used, Process_option_func func, + void *func_ctx) +{ + const char **dirs, *forced_default_file, *forced_extra_defaults; + int error= 0; + DBUG_ENTER("my_search_option_files"); + + /* Check if we want to force the use a specific default file */ + *args_used+= get_defaults_options(*argc - *args_used, *argv + *args_used, + (char **) &forced_default_file, + (char **) &forced_extra_defaults, + (char **) &defaults_group_suffix); + + if (! defaults_group_suffix) + defaults_group_suffix= getenv(STRINGIFY_ARG(DEFAULT_GROUP_SUFFIX_ENV)); + + if (forced_extra_defaults) + defaults_extra_file= (char *) forced_extra_defaults; + + if (forced_default_file) + defaults_file= forced_default_file; + + /* + We can only handle 'defaults-group-suffix' if we are called from + load_defaults() as otherwise we can't know the type of 'func_ctx' + */ + + if (defaults_group_suffix && func == handle_default_option) + { + /* Handle --defaults-group-suffix= */ + uint i; + const char **extra_groups; + const uint instance_len= strlen(defaults_group_suffix); + struct handle_option_ctx *ctx= (struct handle_option_ctx*) func_ctx; + char *ptr; + TYPELIB *group= ctx->group; + + if (!(extra_groups= + (const char**)alloc_root(ctx->alloc, + (2*group->count+1)*sizeof(char*)))) + goto err; + + for (i= 0; i < group->count; i++) + { + uint len; + extra_groups[i]= group->type_names[i]; /** copy group */ + + len= strlen(extra_groups[i]); + if (!(ptr= alloc_root(ctx->alloc, len+instance_len+1))) + goto err; + + extra_groups[i+group->count]= ptr; + + /** Construct new group */ + memcpy(ptr, extra_groups[i], len); + memcpy(ptr+len, defaults_group_suffix, instance_len+1); + } + + group->count*= 2; + group->type_names= extra_groups; + group->type_names[group->count]= 0; + } + + if (forced_default_file) + { + if ((error= search_default_file_with_ext(func, func_ctx, "", "", + forced_default_file, 0)) < 0) + goto err; + if (error > 0) + { + fprintf(stderr, "Could not open required defaults file: %s\n", + forced_default_file); + goto err; + } + } + else if (dirname_length(conf_file)) + { + if ((error= search_default_file(func, func_ctx, NullS, conf_file)) < 0) + goto err; + } + else + { + for (dirs= default_directories ; *dirs; dirs++) + { + if (**dirs) + { + if (search_default_file(func, func_ctx, *dirs, conf_file) < 0) + goto err; + } + else if (defaults_extra_file) + { + if ((error= search_default_file_with_ext(func, func_ctx, "", "", + defaults_extra_file, 0)) < 0) + goto err; /* Fatal error */ + if (error > 0) + { + fprintf(stderr, "Could not open required defaults file: %s\n", + defaults_extra_file); + goto err; + } + } + } + } + + DBUG_RETURN(error); + +err: + fprintf(stderr,"Fatal error in defaults handling. Program aborted\n"); + exit(1); + return 0; /* Keep compiler happy */ +} + + +/* + The option handler for load_defaults. + + SYNOPSIS + handle_deault_option() + in_ctx Handler context. In this case it is a + handle_option_ctx structure. + group_name The name of the group the option belongs to. + option The very option to be processed. It is already + prepared to be used in argv (has -- prefix) + + DESCRIPTION + This handler checks whether a group is one of the listed and adds an option + to the array if yes. Some other handler can record, for instance, all + groups and their options, not knowing in advance the names and amount of + groups. + + RETURN + 0 - ok + 1 - error occured +*/ + +static int handle_default_option(void *in_ctx, const char *group_name, + const char *option) +{ + char *tmp; + struct handle_option_ctx *ctx= (struct handle_option_ctx *) in_ctx; + + if (find_type((char *)group_name, ctx->group, 3)) + { + if (!(tmp= alloc_root(ctx->alloc, (uint) strlen(option) + 1))) + return 1; + if (insert_dynamic(ctx->args, (gptr) &tmp)) + return 1; + strmov(tmp, option); + } + + return 0; +} + + +/* + Gets options from the command line + + SYNOPSIS + get_defaults_options() 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 + # Number of arguments used from *argv + defaults and extra_defaults will be set to option of the 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) +int get_defaults_options(int argc, char **argv, + char **defaults, + char **extra_defaults, + char **group_suffix) { - *defaults=0; - *extra_defaults=0; - if (argc >= 2) + int org_argc= argc, prev_argc= 0; + *defaults= *extra_defaults= *group_suffix= 0; + + while (argc >= 2 && argc != prev_argc) { - if (is_prefix(argv[1],"--defaults-file=")) - *defaults= argv[1]; - else if (is_prefix(argv[1],"--defaults-extra-file=")) - *extra_defaults= argv[1]; + /* Skip program name or previously handled argument */ + argv++; + prev_argc= argc; /* To check if we found */ + if (!*defaults && is_prefix(*argv,"--defaults-file=")) + { + *defaults= *argv + sizeof("--defaults-file=")-1; + argc--; + continue; + } + if (!*extra_defaults && is_prefix(*argv,"--defaults-extra-file=")) + { + *extra_defaults= *argv + sizeof("--defaults-extra-file=")-1; + argc--; + continue; + } + if (!*group_suffix && is_prefix(*argv, "--defaults-group-suffix=")) + { + *group_suffix= *argv + sizeof("--defaults-group-suffix=")-1; + argc--; + continue; + } } + return org_argc - argc; } @@ -136,7 +356,6 @@ void get_defaults_files(int argc, char **argv, RETURN 0 ok 1 The given conf_file didn't exists - 2 The given conf_file was not a normal readable file */ @@ -144,17 +363,21 @@ int load_defaults(const char *conf_file, const char **groups, int *argc, char ***argv) { DYNAMIC_ARRAY args; - const char **dirs, *forced_default_file, *forced_extra_defaults; TYPELIB group; - my_bool found_print_defaults=0; - uint args_used=0; + my_bool found_print_defaults= 0; + uint args_used= 0; int error= 0; MEM_ROOT alloc; - char *ptr, **res; - + char *ptr,**res; + struct handle_option_ctx ctx; DBUG_ENTER("load_defaults"); + init_default_directories(); init_alloc_root(&alloc,512,0); + /* + Check if the user doesn't want any default option processing + --no-defaults is always the first option + */ if (*argc >= 2 && !strcmp(argv[0][1],"--no-defaults")) { /* remove the --no-defaults argument and return only the other arguments */ @@ -173,76 +396,22 @@ int load_defaults(const char *conf_file, const char **groups, DBUG_RETURN(0); } - 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"; group.type_names= groups; + for (; *groups ; groups++) group.count++; if (my_init_dynamic_array(&args, sizeof(char*),*argc, 32)) goto err; - if (forced_default_file) - { - if ((error= search_default_file_with_ext(&args, &alloc, "", "", - forced_default_file, - &group, 0)) < 0) - goto err; - if (error > 0) - { - fprintf(stderr, "Could not open required defaults file: %s\n", - forced_default_file); - goto err; - } - } - else if (dirname_length(conf_file)) - { - if ((error= search_default_file(&args, &alloc, NullS, conf_file, - &group)) < 0) - goto err; - } - else - { -#ifdef __WIN__ - char system_dir[FN_REFLEN]; - GetWindowsDirectory(system_dir,sizeof(system_dir)); - if ((search_default_file(&args, &alloc, system_dir, conf_file, &group))) - goto err; -#endif -#if defined(__EMX__) || defined(OS2) - { - const char *etc; - if ((etc= getenv("ETC")) && - (search_default_file(&args, &alloc, etc, conf_file, - &group)) < 0) - goto err; - } -#endif - for (dirs=default_directories ; *dirs; dirs++) - { - if (**dirs) - { - if (search_default_file(&args, &alloc, *dirs, conf_file, - &group) < 0) - goto err; - } - else if (defaults_extra_file) - { - if (search_default_file(&args, &alloc, NullS, defaults_extra_file, - &group) < 0) - goto err; /* Fatal error */ - } - } - } + + ctx.alloc= &alloc; + ctx.args= &args; + ctx.group= &group; + + error= my_search_option_files(conf_file, argc, argv, &args_used, + handle_default_option, (void *) &ctx); /* Here error contains <> 0 only if we have a fully specified conf_file or a forced default file @@ -255,11 +424,14 @@ int load_defaults(const char *conf_file, const char **groups, /* copy name + found arguments + command line arguments to new array */ res[0]= argv[0][0]; /* Name MUST be set, even by embedded library */ memcpy((gptr) (res+1), args.buffer, args.elements*sizeof(char*)); - /* Skip --defaults-file and --defaults-extra-file */ + /* Skip --defaults-xxx options */ (*argc)-= args_used; (*argv)+= args_used; - /* Check if we wan't to see the new argument list */ + /* + Check if we wan't to see the new argument list + This options must always be the last of the default options + */ if (*argc >= 2 && !strcmp(argv[0][1],"--print-defaults")) { found_print_defaults=1; @@ -302,17 +474,22 @@ void free_defaults(char **argv) } -static int search_default_file(DYNAMIC_ARRAY *args, MEM_ROOT *alloc, +static int search_default_file(Process_option_func opt_handler, + void *handler_ctx, const char *dir, - const char *config_file, TYPELIB *group) + const char *config_file) { char **ext; + const char *empty_list[]= { "", 0 }; + my_bool have_ext= fn_ext(config_file)[0] != 0; + const char **exts_to_use= have_ext ? empty_list : f_extensions; - for (ext= (char**) f_extensions; *ext; *ext++) + for (ext= (char**) exts_to_use; *ext; *ext++) { int error; - if ((error= search_default_file_with_ext(args, alloc, dir, *ext, - config_file, group, 0)) < 0) + if ((error= search_default_file_with_ext(opt_handler, handler_ctx, + dir, *ext, + config_file, 0)) < 0) return error; } return 0; @@ -374,8 +551,10 @@ static char *get_argument(const char *keyword, uint kwlen, SYNOPSIS search_default_file_with_ext() - args Store pointer to found options here - alloc Allocate strings in this object + opt_handler Option handler function. It is used to process + every separate option. + handler_ctx Pointer to the structure to store actual + parameters of the function. dir directory to read ext Extension for configuration file config_file Name of configuration file @@ -387,21 +566,23 @@ static char *get_argument(const char *keyword, uint kwlen, 0 Success -1 Fatal error, abort 1 File not found (Warning) - 2 File is not a regular file (Warning) */ -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, int recursion_level) +static int search_default_file_with_ext(Process_option_func opt_handler, + void *handler_ctx, + const char *dir, + const char *ext, + const char *config_file, + int recursion_level) { - char name[FN_REFLEN + 10], buff[4096], *ptr, *end, *value, *tmp, **tmp_ext; + char name[FN_REFLEN + 10], buff[4096], curr_gr[4096], *ptr, *end, **tmp_ext; + char *value, option[4096], tmp[FN_REFLEN]; 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 found_group=0; uint i; MY_DIR *search_dir; FILEINFO *search_file; @@ -433,14 +614,14 @@ 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-writable 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)))) - return 0; /* Ignore wrong files */ + if (!(fp= my_fopen(name, O_RDONLY, MYF(0)))) + return 1; /* Ignore wrong files */ while (fgets(buff, sizeof(buff) - 1, fp)) { @@ -499,14 +680,10 @@ static int search_default_file_with_ext(DYNAMIC_ARRAY *args, MEM_ROOT *alloc, 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, + search_default_file_with_ext(opt_handler, handler_ctx, "", "", tmp, recursion_level + 1); } } @@ -521,7 +698,7 @@ static int search_default_file_with_ext(DYNAMIC_ARRAY *args, MEM_ROOT *alloc, name, line))) goto err; - search_default_file_with_ext(args, alloc, "", "", ptr, group, + search_default_file_with_ext(opt_handler, handler_ctx, "", "", ptr, recursion_level + 1); } @@ -540,7 +717,8 @@ static int search_default_file_with_ext(DYNAMIC_ARRAY *args, MEM_ROOT *alloc, } for ( ; my_isspace(&my_charset_latin1,end[-1]) ; end--) ;/* Remove end space */ end[0]=0; - read_values=find_type(ptr,group,3) > 0; + + strnmov(curr_gr, ptr, min((uint) (end-ptr)+1, 4096)); continue; } if (!found_group) @@ -550,19 +728,17 @@ static int search_default_file_with_ext(DYNAMIC_ARRAY *args, MEM_ROOT *alloc, name,line); goto err; } - if (!read_values) - continue; + + end= remove_end_comment(ptr); if ((value= strchr(ptr, '='))) end= value; /* Option without argument */ for ( ; my_isspace(&my_charset_latin1,end[-1]) ; end--) ; if (!value) { - if (!(tmp=alloc_root(alloc,(uint) (end-ptr)+3))) - goto err; - strmake(strmov(tmp,"--"),ptr,(uint) (end-ptr)); - if (insert_dynamic(args,(gptr) &tmp)) - goto err; + strmake(strmov(option,"--"),ptr,(uint) (end-ptr)); + if (opt_handler(handler_ctx, curr_gr, option)) + goto err; } else { @@ -584,12 +760,7 @@ static int search_default_file_with_ext(DYNAMIC_ARRAY *args, MEM_ROOT *alloc, value++; value_end--; } - if (!(tmp=alloc_root(alloc,(uint) (end-ptr)+3 + - (uint) (value_end-value)+1))) - goto err; - if (insert_dynamic(args,(gptr) &tmp)) - goto err; - ptr=strnmov(strmov(tmp,"--"),ptr,(uint) (end-ptr)); + ptr=strnmov(strmov(option,"--"),ptr,(uint) (end-ptr)); *ptr++= '='; for ( ; value != value_end; value++) @@ -631,6 +802,8 @@ static int search_default_file_with_ext(DYNAMIC_ARRAY *args, MEM_ROOT *alloc, *ptr++= *value; } *ptr=0; + if (opt_handler(handler_ctx, curr_gr, option)) + goto err; } } my_fclose(fp,MYF(0)); @@ -669,44 +842,24 @@ static char *remove_end_comment(char *ptr) #include <help_start.h> -void print_defaults(const char *conf_file, const char **groups) +void my_print_default_files(const char *conf_file) { -#ifdef __WIN__ + const char *empty_list[]= { "", 0 }; my_bool have_ext= fn_ext(conf_file)[0] != 0; -#endif + const char **exts_to_use= have_ext ? empty_list : f_extensions; char name[FN_REFLEN], **ext; const char **dirs; + init_default_directories(); puts("\nDefault options are read from the following files in the given order:"); if (dirname_length(conf_file)) fputs(conf_file,stdout); else { -#ifdef __WIN__ - GetWindowsDirectory(name,sizeof(name)); - if (!have_ext) - { - for (ext= (char**) f_extensions; *ext; *ext++) - printf("%s\\%s%s ", name, conf_file, *ext); - } - else - printf("%s\\%s ", name, conf_file); -#endif -#if defined(__EMX__) || defined(OS2) - { - const char *etc; - - if ((etc= getenv("ETC"))) - { - for (ext= (char**) f_extensions; *ext; *ext++) - printf("%s\\%s%s ", etc, conf_file, *ext); - } - } -#endif for (dirs=default_directories ; *dirs; dirs++) { - for (ext= (char**) f_extensions; *ext; *ext++) + for (ext= (char**) exts_to_use; *ext; *ext++) { const char *pos; char *end; @@ -725,12 +878,30 @@ void print_defaults(const char *conf_file, const char **groups) } puts(""); } +} + +void print_defaults(const char *conf_file, const char **groups) +{ + const char **groups_save= groups; + my_print_default_files(conf_file); + fputs("The following groups are read:",stdout); for ( ; *groups ; groups++) { fputc(' ',stdout); fputs(*groups,stdout); } + + if (defaults_group_suffix) + { + groups= groups_save; + for ( ; *groups ; groups++) + { + fputc(' ',stdout); + fputs(*groups,stdout); + fputs(defaults_group_suffix,stdout); + } + } puts("\nThe following options may be given as the first argument:\n\ --print-defaults Print the program argument list and exit\n\ --no-defaults Don't read default options from any options file\n\ @@ -739,3 +910,144 @@ void print_defaults(const char *conf_file, const char **groups) } #include <help_end.h> + + +#ifdef __WIN__ +/* + This wrapper for GetSystemWindowsDirectory() will dynamically bind to the + function if it is available, emulate it on NT4 Terminal Server by stripping + the \SYSTEM32 from the end of the results of GetSystemDirectory(), or just + return GetSystemDirectory(). + */ + +typedef UINT (WINAPI *GET_SYSTEM_WINDOWS_DIRECTORY)(LPSTR, UINT); + +static uint my_get_system_windows_directory(char *buffer, uint size) +{ + uint count; + GET_SYSTEM_WINDOWS_DIRECTORY + func_ptr= (GET_SYSTEM_WINDOWS_DIRECTORY) + GetProcAddress(GetModuleHandle("kernel32.dll"), + "GetSystemWindowsDirectoryA"); + + if (func_ptr) + return func_ptr(buffer, size); + + /* + Windows NT 4.0 Terminal Server Edition: + To retrieve the shared Windows directory, call GetSystemDirectory and + trim the "System32" element from the end of the returned path. + */ + count= GetSystemDirectory(buffer, size); + if (count > 8 && stricmp(buffer+(count-8), "\\System32") == 0) + { + count-= 8; + buffer[count] = '\0'; + } + return count; +} +#endif + + +/* + Create the list of default directories. + + On Microsoft Windows, this is: + 1. C:/ + 2. GetWindowsDirectory() + 3. GetSystemWindowsDirectory() + 4. getenv(DEFAULT_HOME_ENV) + 5. Directory above where the executable is located + 6. "" + 7. --sysconfdir=<path> + + On Novell NetWare, this is: + 1. sys:/etc/ + 2. getenv(DEFAULT_HOME_ENV) + 3. "" + 4. --sysconfdir=<path> + + On OS/2, this is: + 1. getenv(ETC) + 2. /etc/ + 3. getenv(DEFAULT_HOME_ENV) + 4. "" + 5. "~/" + 6. --sysconfdir=<path> + + Everywhere else, this is: + 1. /etc/ + 2. getenv(DEFAULT_HOME_ENV) + 3. "" + 4. "~/" + 5. --sysconfdir=<path> + + */ + +static void init_default_directories() +{ + const char *env, **ptr= default_directories; + +#ifdef __WIN__ + *ptr++= "C:/"; + + if (GetWindowsDirectory(system_dir,sizeof(system_dir))) + *ptr++= (char*)&system_dir; + if (my_get_system_windows_directory(shared_system_dir, + sizeof(shared_system_dir)) && + strcmp(system_dir, shared_system_dir)) + *ptr++= (char *)&shared_system_dir; + +#elif defined(__NETWARE__) + *ptr++= "sys:/etc/"; +#else +#if defined(__EMX__) || defined(OS2) + if ((env= getenv("ETC"))) + *ptr++= env; +#endif + *ptr++= "/etc/"; +#endif + if ((env= getenv(STRINGIFY_ARG(DEFAULT_HOME_ENV)))) + *ptr++= env; + *ptr++= ""; /* Place for defaults_extra_file */ +#if !defined(__WIN__) && !defined(__NETWARE__) + *ptr++= "~/";; +#elif defined(__WIN__) + if (GetModuleFileName(NULL, config_dir, sizeof(config_dir))) + { + char *last= NULL, *end= strend(config_dir); + /* + Look for the second-to-last \ in the filename, but hang on + to a pointer after the last \ in case we're in the root of + a drive. + */ + for ( ; end > config_dir; end--) + { + if (*end == FN_LIBCHAR) + { + if (last) + { + if (end != config_dir) + { + /* Keep the last '\' as this works both with D:\ and a directory */ + end[1]= 0; + } + else + { + /* No parent directory (strange). Use current dir + '\' */ + last[1]= 0; + } + break; + } + last= end; + } + } + *ptr++= (char *)&config_dir; + } +#endif +#ifdef DEFAULT_SYSCONFDIR + if (DEFAULT_SYSCONFDIR != "") + *ptr++= DEFAULT_SYSCONFDIR; +#endif + *ptr= 0; /* end marker */ +} diff --git a/mysys/default_modify.c b/mysys/default_modify.c new file mode 100644 index 00000000000..0f58b8a930c --- /dev/null +++ b/mysys/default_modify.c @@ -0,0 +1,228 @@ +/* Copyright (C) 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 "my_global.h" +#include "mysys_priv.h" +#include "m_string.h" +#include <my_dir.h> + +#define BUFF_SIZE 1024 +#define RESERVE 1024 /* Extend buffer with this extent */ + +#ifdef __WIN__ +#define NEWLINE "\r\n" +#define NEWLINE_LEN 2 +#else +#define NEWLINE "\n" +#define NEWLINE_LEN 1 +#endif + +static char *add_option(char *dst, const char *option_value, + const char *option, int remove_option); + + +/* + Add/remove option to the option file section. + + SYNOPSYS + modify_defaults_file() + file_location The location of configuration file to edit + option option to look for + option value The value of the option we would like to set + section_name the name of the section + remove_option This is true if we want to remove the option. + False otherwise. + IMPLEMENTATION + We open the option file first, then read the file line-by-line, + looking for the section we need. At the same time we put these lines + into a buffer. Then we look for the option within this section and + change/remove it. In the end we get a buffer with modified version of the + file. Then we write it to the file, truncate it if needed and close it. + Note that there is a small time gap, when the file is incomplete, + and this theoretically might introduce a problem. + + RETURN + 0 - ok + 1 - some error has occured. Probably due to the lack of resourses + 2 - cannot open the file +*/ + +int modify_defaults_file(const char *file_location, const char *option, + const char *option_value, + const char *section_name, int remove_option) +{ + FILE *cnf_file; + MY_STAT file_stat; + char linebuff[BUFF_SIZE], *src_ptr, *dst_ptr, *file_buffer; + uint opt_len, optval_len, sect_len, nr_newlines= 0, buffer_size; + my_bool in_section= FALSE, opt_applied= 0; + uint reserve_extended; + uint new_opt_len; + int reserve_occupied= 0; + DBUG_ENTER("modify_defaults_file"); + + if (!(cnf_file= my_fopen(file_location, O_RDWR | O_BINARY, MYF(0)))) + DBUG_RETURN(2); + + /* my_fstat doesn't use the flag parameter */ + if (my_fstat(fileno(cnf_file), &file_stat, MYF(0))) + goto malloc_err; + + opt_len= (uint) strlen(option); + optval_len= (uint) strlen(option_value); + + new_opt_len= opt_len + 1 + optval_len + NEWLINE_LEN; + + /* calculate the size of the buffer we need */ + reserve_extended= (opt_len + + 1 + /* For '=' char */ + optval_len + /* Option value len */ + NEWLINE_LEN + /* Space for newline */ + RESERVE); /* Some additional space */ + + buffer_size= (file_stat.st_size + + 1); /* The ending zero */ + + /* + Reserve space to read the contents of the file and some more + for the option we want to add. + */ + if (!(file_buffer= (char*) my_malloc(buffer_size + reserve_extended, + MYF(MY_WME)))) + goto malloc_err; + + sect_len= (uint) strlen(section_name); + + for (dst_ptr= file_buffer; fgets(linebuff, BUFF_SIZE, cnf_file); ) + { + /* Skip over whitespaces */ + for (src_ptr= linebuff; my_isspace(&my_charset_latin1, *src_ptr); + src_ptr++) + {} + + if (!*src_ptr) /* Empty line */ + { + nr_newlines++; + continue; + } + + /* correct the option */ + if (in_section && !strncmp(src_ptr, option, opt_len) && + (*(src_ptr + opt_len) == '=' || + my_isspace(&my_charset_latin1, *(src_ptr + opt_len)) || + *(src_ptr + opt_len) == '\0')) + { + char *old_src_ptr= src_ptr; + src_ptr= strend(src_ptr+ opt_len); /* Find the end of the line */ + + /* could be negative */ + reserve_occupied+= (int) new_opt_len - (int) (src_ptr - old_src_ptr); + if (reserve_occupied >= (int) reserve_extended) + { + reserve_extended= (uint) reserve_occupied + RESERVE; + if (!(file_buffer= (char*) my_realloc(file_buffer, buffer_size + + reserve_extended, + MYF(MY_WME|MY_FREE_ON_ERROR)))) + goto malloc_err; + } + opt_applied= 1; + dst_ptr= add_option(dst_ptr, option_value, option, remove_option); + } + else + { + /* If going to new group and we have option to apply, do it now */ + if (in_section && !opt_applied && *src_ptr == '[') + { + dst_ptr= add_option(dst_ptr, option_value, option, remove_option); + opt_applied= 1; /* set the flag to do write() later */ + reserve_occupied= new_opt_len+ opt_len + 1 + NEWLINE_LEN; + } + + for (; nr_newlines; nr_newlines--) + dst_ptr= strmov(dst_ptr, NEWLINE); + dst_ptr= strmov(dst_ptr, linebuff); + } + /* Look for a section */ + if (*src_ptr == '[') + { + /* Copy the line to the buffer */ + if (!strncmp(++src_ptr, section_name, sect_len)) + { + src_ptr+= sect_len; + /* Skip over whitespaces. They are allowed after section name */ + for (; my_isspace(&my_charset_latin1, *src_ptr); src_ptr++) + {} + + if (*src_ptr != ']') + continue; /* Missing closing parenthesis. Assume this was no group */ + in_section= TRUE; + } + else + in_section= FALSE; /* mark that this section is of no interest to us */ + } + } + /* File ended. */ + if (!opt_applied && !remove_option && in_section) + { + /* New option still remains to apply at the end */ + if (*(dst_ptr - 1) != '\n') + dst_ptr= strmov(dst_ptr, NEWLINE); + dst_ptr= add_option(dst_ptr, option_value, option, remove_option); + opt_applied= 1; + } + for (; nr_newlines; nr_newlines--) + dst_ptr= strmov(dst_ptr, NEWLINE); + + if (opt_applied) + { + /* Don't write the file if there are no changes to be made */ + if (my_chsize(fileno(cnf_file), (my_off_t) (dst_ptr - file_buffer), 0, + MYF(MY_WME)) || + my_fseek(cnf_file, 0, MY_SEEK_SET, MYF(0)) || + my_fwrite(cnf_file, file_buffer, (uint) (dst_ptr - file_buffer), + MYF(MY_NABP))) + goto err; + } + if (my_fclose(cnf_file, MYF(MY_WME))) + DBUG_RETURN(1); + + my_free(file_buffer, MYF(0)); + DBUG_RETURN(0); + +err: + my_free(file_buffer, MYF(0)); +malloc_err: + my_fclose(cnf_file, MYF(0)); + DBUG_RETURN(1); /* out of resources */ +} + + +static char *add_option(char *dst, const char *option_value, + const char *option, int remove_option) +{ + if (!remove_option) + { + dst= strmov(dst, option); + if (*option_value) + { + *dst++= '='; + dst= strmov(dst, option_value); + } + /* add a newline */ + dst= strmov(dst, NEWLINE); + } + return dst; +} diff --git a/mysys/errors.c b/mysys/errors.c index 05436c9a212..4472b7173fa 100644 --- a/mysys/errors.c +++ b/mysys/errors.c @@ -21,7 +21,6 @@ const char * NEAR globerrs[GLOBERRS]= { - "File '%s' not found (Errcode: %d)", "Can't create/write to file '%s' (Errcode: %d)", "Error reading file '%s' (Errcode: %d)", "Error writing file '%s' (Errcode: %d)", @@ -50,20 +49,18 @@ const char * NEAR globerrs[GLOBERRS]= "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", + "File '%s' not found (Errcode: %d)", }; void init_glob_errs(void) { - my_errmsg[GLOB] = & globerrs[0]; -} /* init_glob_errs */ + /* This is now done statically. */ +} #else void init_glob_errs() { - my_errmsg[GLOB] = & globerrs[0]; - - EE(EE_FILENOTFOUND) = "File '%s' not found (Errcode: %d)"; EE(EE_CANTCREATEFILE) = "Can't create/write to file '%s' (Errcode: %d)"; EE(EE_READ) = "Error reading file '%s' (Errcode: %d)"; EE(EE_WRITE) = "Error writing file '%s' (Errcode: %d)"; @@ -91,5 +88,6 @@ void init_glob_errs() 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"; + EE(EE_FILENOTFOUND) = "File '%s' not found (Errcode: %d)"; } #endif diff --git a/mysys/hash.c b/mysys/hash.c index 75135a470c9..99479ef6769 100644 --- a/mysys/hash.c +++ b/mysys/hash.c @@ -103,7 +103,7 @@ static inline void hash_free_elements(HASH *hash) hash_free() hash the hash to delete elements of - NOTES: Hash can't be reused wuthing calling hash_init again. + NOTES: Hash can't be reused without calling hash_init again. */ void hash_free(HASH *hash) @@ -289,9 +289,8 @@ static void movelink(HASH_LINK *array,uint find,uint next_link,uint newlink) record being compared against. RETURN - < 0 key of record < key = 0 key of record == key - > 0 key of record > key + != 0 key of record != key */ static int hashcmp(const HASH *hash, HASH_LINK *pos, const byte *key, diff --git a/mysys/list.c b/mysys/list.c index 480c1080a45..0e55c9399f5 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: 0x%lx element: %lx", root, element)); + DBUG_PRINT("enter",("root: 0x%lx element: 0x%lx", root, element)); if (root) { if (root->prev) /* If add in mid of list */ diff --git a/mysys/mf_dirname.c b/mysys/mf_dirname.c index 45bf4d56c31..9206aa28078 100644 --- a/mysys/mf_dirname.c +++ b/mysys/mf_dirname.c @@ -108,7 +108,7 @@ char *convert_dirname(char *to, const char *from, const char *from_end) #if FN_LIBCHAR != '/' || defined(FN_C_BEFORE_DIR_2) { - for (; *from && from != from_end; from++) + for (; from != from_end && *from ; from++) { if (*from == '/') *to++= FN_LIBCHAR; diff --git a/mysys/mf_format.c b/mysys/mf_format.c index d1ca1108d02..50f354df1cd 100644 --- a/mysys/mf_format.c +++ b/mysys/mf_format.c @@ -17,13 +17,12 @@ #include "mysys_priv.h" #include <m_string.h> - /* - Formats a filename with possible replace of directory of extension - Function can handle the case where 'to' == 'name' - For a description of the flag values, consult my_sys.h - The arguments should be in unix format. - */ - +/* + Formats a filename with possible replace of directory of extension + Function can handle the case where 'to' == 'name' + For a description of the flag values, consult my_sys.h + The arguments should be in unix format. +*/ my_string fn_format(my_string to, const char *name, const char *dir, const char *extension, uint flag) @@ -54,6 +53,7 @@ my_string fn_format(my_string to, const char *name, const char *dir, pack_dirname(dev,dev); /* Put in ./.. and ~/.. */ if (flag & MY_UNPACK_FILENAME) (void) unpack_dirname(dev,dev); /* Replace ~/.. with dir */ + if ((pos= (char*) strchr(name,FN_EXTCHAR)) != NullS) { if ((flag & MY_REPLACE_EXT) == 0) /* If we should keep old ext */ diff --git a/mysys/mf_getdate.c b/mysys/mf_getdate.c index 189d43e782a..8998da8aefa 100644 --- a/mysys/mf_getdate.c +++ b/mysys/mf_getdate.c @@ -19,11 +19,20 @@ #include "mysys_priv.h" #include <m_string.h> - /* - If flag & 1 Return date and time - If flag & 2 Return short date format YYMMDD - if flag & 4 Return time in HHMMDD format. - */ +/* + get date as string + + SYNOPSIS + get_date() + to - string where date will be written + flag - format of date: + If flag & GETDATE_TIME Return date and time + If flag & GETDATE_SHORT_DATE Return short date format YYMMDD + If flag & GETDATE_HHMMSSTIME Return time in HHMMDD format. + If flag & GETDATE_GMT Date/time in GMT + If flag & GETDATE_FIXEDLENGTH Return fixed length date/time + date - for conversion +*/ void get_date(register my_string to, int flag, time_t date) @@ -36,27 +45,36 @@ void get_date(register my_string to, int flag, time_t date) skr=date ? (time_t) date : time((time_t*) 0); #if defined(HAVE_LOCALTIME_R) && defined(_REENTRANT) - localtime_r(&skr,&tm_tmp); + if (flag & GETDATE_GMT) + localtime_r(&skr,&tm_tmp); + else + gmtime_r(&skr,&tm_tmp); start_time= &tm_tmp; #else - start_time=localtime(&skr); + if (flag & GETDATE_GMT) + start_time= localtime(&skr); + else + start_time= gmtime(&skr); #endif - if (flag & 2) + if (flag & GETDATE_SHORT_DATE) sprintf(to,"%02d%02d%02d", start_time->tm_year % 100, start_time->tm_mon+1, start_time->tm_mday); else - sprintf(to,"%d-%02d-%02d", + sprintf(to, ((flag & GETDATE_FIXEDLENGTH) ? + "%4d-%02d-%02d" : "%d-%02d-%02d"), start_time->tm_year+1900, start_time->tm_mon+1, start_time->tm_mday); - if (flag & 1) - sprintf(strend(to)," %2d:%02d:%02d", + if (flag & GETDATE_DATE_TIME) + sprintf(strend(to), + ((flag & GETDATE_FIXEDLENGTH) ? + " %02d:%02d:%02d" : " %2d:%02d:%02d"), start_time->tm_hour, start_time->tm_min, start_time->tm_sec); - else if (flag & 4) + else if (flag & GETDATE_HHMMSSTIME) sprintf(strend(to),"%02d%02d%02d", start_time->tm_hour, start_time->tm_min, diff --git a/mysys/mf_iocache.c b/mysys/mf_iocache.c index 0007784c2b2..cd2a140182e 100644 --- a/mysys/mf_iocache.c +++ b/mysys/mf_iocache.c @@ -27,7 +27,7 @@ also info->read_pos is set to info->read_end. If called through open_cached_file(), then the temporary file will only be created if a write exeeds the file buffer or if one calls - flush_io_cache(). + my_b_flush_io_cache(). If one uses SEQ_READ_APPEND, then two buffers are allocated, one for reading and another for writing. Reads are first done from disk and @@ -43,7 +43,7 @@ TODO: each time the write buffer gets full and it's written to disk, we will always do a disk read to read a part of the buffer from disk to the read buffer. - This should be fixed so that when we do a flush_io_cache() and + This should be fixed so that when we do a my_b_flush_io_cache() and we have been reading the write buffer, we should transfer the rest of the write buffer to the read buffer before we start to reuse it. */ @@ -165,13 +165,13 @@ int init_io_cache(IO_CACHE *info, File file, uint cachesize, (ulong) info, (int) type, (ulong) seek_offset)); info->file= file; - info->type= 0; /* Don't set it until mutex are created */ + info->type= TYPE_NOT_SET; /* 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->seek_not_done= test(file >= 0 && seek_offset != my_tell(file, MYF(0))); info->disk_writes= 0; #ifdef THREAD info->share=0; @@ -184,8 +184,10 @@ int init_io_cache(IO_CACHE *info, File file, uint cachesize, { /* Assume file isn't growing */ if (!(cache_myflags & MY_DONT_CHECK_FILESIZE)) { - /* Calculate end of file to not allocate to big buffers */ + /* Calculate end of file to avoid allocating oversized buffers */ end_of_file=my_seek(file,0L,MY_SEEK_END,MYF(0)); + /* Need to reset seek_not_done now that we just did a seek. */ + info->seek_not_done= end_of_file == seek_offset ? 0 : 1; if (end_of_file < seek_offset) end_of_file=seek_offset; /* Trim cache size if the file is very small */ @@ -360,7 +362,7 @@ my_bool reinit_io_cache(IO_CACHE *info, enum cache_type type, if (info->type == WRITE_CACHE && type == READ_CACHE) info->end_of_file=my_b_tell(info); /* flush cache if we want to reuse it */ - if (!clear_cache && flush_io_cache(info)) + if (!clear_cache && my_b_flush_io_cache(info,1)) DBUG_RETURN(1); info->pos_in_file=seek_offset; /* Better to do always do a seek */ @@ -646,7 +648,7 @@ int _my_b_read_r(register IO_CACHE *info, byte *Buffer, uint Count) 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); + len= (int) (info->error == -1 ? -1 : info->read_end-info->buffer); } info->read_pos=info->buffer; info->seek_not_done=0; @@ -1016,7 +1018,7 @@ 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)) + if (my_b_flush_io_cache(info,1)) return 1; if (Count >= IO_SIZE) { /* Fill first intern buffer */ @@ -1259,6 +1261,7 @@ int end_io_cache(IO_CACHE *info) int error=0; IO_CACHE_CALLBACK pre_close; DBUG_ENTER("end_io_cache"); + DBUG_PRINT("enter",("cache: 0x%lx", (ulong) info)); #ifdef THREAD /* @@ -1268,7 +1271,7 @@ int end_io_cache(IO_CACHE *info) */ if (info->share) { - pthread_cond_destroy (&info->share->cond); + pthread_cond_destroy(&info->share->cond); pthread_mutex_destroy(&info->share->mutex); info->share=0; } @@ -1283,14 +1286,14 @@ int end_io_cache(IO_CACHE *info) { info->alloced_buffer=0; if (info->file != -1) /* File doesn't exist */ - error=flush_io_cache(info); + error= my_b_flush_io_cache(info,1); my_free((gptr) info->buffer,MYF(MY_WME)); info->buffer=info->read_pos=(byte*) 0; } if (info->type == SEQ_READ_APPEND) { /* Destroy allocated mutex */ - info->type=0; + info->type= TYPE_NOT_SET; #ifdef THREAD pthread_mutex_destroy(&info->append_buffer_lock); #endif diff --git a/mysys/mf_iocache2.c b/mysys/mf_iocache2.c index 3755bcdb53d..e181ccfb88d 100644 --- a/mysys/mf_iocache2.c +++ b/mysys/mf_iocache2.c @@ -65,6 +65,13 @@ my_off_t my_b_append_tell(IO_CACHE* info) return res; } +my_off_t my_b_safe_tell(IO_CACHE *info) +{ + if (unlikely(info->type == SEQ_READ_APPEND)) + return my_b_append_tell(info); + return my_b_tell(info); +} + /* Make next read happen at the given position For write cache, make next write happen at the given position @@ -72,7 +79,7 @@ my_off_t my_b_append_tell(IO_CACHE* info) void my_b_seek(IO_CACHE *info,my_off_t pos) { - my_off_t offset; + my_off_t offset; DBUG_ENTER("my_b_seek"); DBUG_PRINT("enter",("pos: %lu", (ulong) pos)); @@ -84,10 +91,10 @@ void my_b_seek(IO_CACHE *info,my_off_t pos) b) see if there is a better way to make it work */ if (info->type == SEQ_READ_APPEND) - flush_io_cache(info); - + VOID(flush_io_cache(info)); + offset=(pos - info->pos_in_file); - + if (info->type == READ_CACHE || info->type == SEQ_READ_APPEND) { /* TODO: explain why this works if pos < info->pos_in_file */ @@ -112,7 +119,7 @@ void my_b_seek(IO_CACHE *info,my_off_t pos) info->write_pos = info->write_buffer + offset; DBUG_VOID_RETURN; } - flush_io_cache(info); + VOID(flush_io_cache(info)); /* Correct buffer end so that we write in increments of IO_SIZE */ info->write_end=(info->write_buffer+info->buffer_length- (pos & (IO_SIZE-1))); diff --git a/mysys/mf_keycache.c b/mysys/mf_keycache.c index 88b5051c52b..11e27bb19ad 100644 --- a/mysys/mf_keycache.c +++ b/mysys/mf_keycache.c @@ -161,10 +161,12 @@ 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); +#ifdef THREAD 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); +#endif static void free_block(KEY_CACHE *keycache, BLOCK_LINK *block); static void test_key_cache(KEY_CACHE *keycache, const char *where, my_bool lock); @@ -215,6 +217,7 @@ static void keycache_debug_print _VARARGS((const char *fmt,...)); #endif /* defined(KEYCACHE_DEBUG_LOG) && defined(KEYCACHE_DEBUG) */ #if defined(KEYCACHE_DEBUG) || !defined(DBUG_OFF) +#ifdef THREAD static long keycache_thread_id; #define KEYCACHE_THREAD_TRACE(l) \ KEYCACHE_DBUG_PRINT(l,("|thread %ld",keycache_thread_id)) @@ -226,6 +229,11 @@ static long keycache_thread_id; #define KEYCACHE_THREAD_TRACE_END(l) \ KEYCACHE_DBUG_PRINT(l,("]thread %ld",keycache_thread_id)) +#else /* THREAD */ +#define KEYCACHE_THREAD_TRACE(l) KEYCACHE_DBUG_PRINT(l,("")) +#define KEYCACHE_THREAD_TRACE_BEGIN(l) KEYCACHE_DBUG_PRINT(l,("")) +#define KEYCACHE_THREAD_TRACE_END(l) KEYCACHE_DBUG_PRINT(l,("")) +#endif /* THREAD */ #else #define KEYCACHE_THREAD_TRACE_BEGIN(l) #define KEYCACHE_THREAD_TRACE_END(l) @@ -347,8 +355,8 @@ int init_key_cache(KEY_CACHE *keycache, uint key_cache_block_size, blocks--; /* Allocate memory for cache page buffers */ if ((keycache->block_mem= - my_malloc_lock((ulong) blocks * keycache->key_cache_block_size, - MYF(0)))) + my_large_malloc((ulong) blocks * keycache->key_cache_block_size, + MYF(MY_WME)))) { /* Allocate memory for blocks, hash_links and hash entries; @@ -357,7 +365,7 @@ int init_key_cache(KEY_CACHE *keycache, uint key_cache_block_size, if ((keycache->block_root= (BLOCK_LINK*) my_malloc((uint) length, MYF(0)))) break; - my_free_lock(keycache->block_mem, MYF(0)); + my_large_free(keycache->block_mem, MYF(0)); keycache->block_mem= 0; } if (blocks < 8) @@ -428,7 +436,7 @@ err: keycache->blocks= 0; if (keycache->block_mem) { - my_free_lock((gptr) keycache->block_mem, MYF(0)); + my_large_free((gptr) keycache->block_mem, MYF(0)); keycache->block_mem= NULL; } if (keycache->block_root) @@ -492,6 +500,7 @@ int resize_key_cache(KEY_CACHE *keycache, uint key_cache_block_size, keycache_pthread_mutex_lock(&keycache->cache_lock); +#ifdef THREAD wqueue= &keycache->resize_queue; thread= my_thread_var; link_into_queue(wqueue, thread); @@ -500,6 +509,7 @@ int resize_key_cache(KEY_CACHE *keycache, uint key_cache_block_size, { keycache_pthread_cond_wait(&thread->suspend, &keycache->cache_lock); } +#endif keycache->resize_in_flush= 1; if (flush_all_key_blocks(keycache)) @@ -512,12 +522,16 @@ int resize_key_cache(KEY_CACHE *keycache, uint key_cache_block_size, } keycache->resize_in_flush= 0; keycache->can_be_used= 0; +#ifdef THREAD 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); } +#else + KEYCACHE_DBUG_ASSERT(keycache->cnt_for_resize_op == 0); +#endif end_key_cache(keycache, 0); /* Don't free mutex */ /* The following will work even if use_mem is 0 */ @@ -525,6 +539,7 @@ int resize_key_cache(KEY_CACHE *keycache, uint key_cache_block_size, division_limit, age_threshold); finish: +#ifdef THREAD unlink_from_queue(wqueue, thread); /* Signal for the next resize request to proceeed if any */ if (wqueue->last_thread) @@ -533,6 +548,7 @@ finish: ("thread %ld", wqueue->last_thread->next->id)); keycache_pthread_cond_signal(&wqueue->last_thread->next->suspend); } +#endif keycache_pthread_mutex_unlock(&keycache->cache_lock); return blocks; } @@ -553,6 +569,7 @@ static inline void inc_counter_for_resize_op(KEY_CACHE *keycache) */ static inline void dec_counter_for_resize_op(KEY_CACHE *keycache) { +#ifdef THREAD struct st_my_thread_var *last_thread; if (!--keycache->cnt_for_resize_op && (last_thread= keycache->resize_queue.last_thread)) @@ -561,6 +578,9 @@ static inline void dec_counter_for_resize_op(KEY_CACHE *keycache) ("thread %ld", last_thread->next->id)); keycache_pthread_cond_signal(&last_thread->next->suspend); } +#else + keycache->cnt_for_resize_op--; +#endif } /* @@ -622,7 +642,7 @@ void end_key_cache(KEY_CACHE *keycache, my_bool cleanup) { if (keycache->block_mem) { - my_free_lock((gptr) keycache->block_mem, MYF(0)); + my_large_free((gptr) keycache->block_mem, MYF(0)); keycache->block_mem= NULL; my_free((gptr) keycache->block_root, MYF(0)); keycache->block_root= NULL; @@ -650,6 +670,7 @@ void end_key_cache(KEY_CACHE *keycache, my_bool cleanup) } /* end_key_cache */ +#ifdef THREAD /* Link a thread into double-linked queue of waiting threads. @@ -786,6 +807,7 @@ static void release_queue(KEYCACHE_WQUEUE *wqueue) while (thread != last); wqueue->last_thread= NULL; } +#endif /* @@ -893,6 +915,7 @@ static void link_block(KEY_CACHE *keycache, BLOCK_LINK *block, my_bool hot, BLOCK_LINK **pins; KEYCACHE_DBUG_ASSERT(! (block->hash_link && block->hash_link->requests)); +#ifdef THREAD if (!hot && keycache->waiting_for_block.last_thread) { /* Signal that in the LRU warm sub-chain an available block has appeared */ @@ -929,6 +952,10 @@ static void link_block(KEY_CACHE *keycache, BLOCK_LINK *block, my_bool hot, #endif return; } +#else /* THREAD */ + KEYCACHE_DBUG_ASSERT(! (!hot && keycache->waiting_for_block.last_thread)); + /* Condition not transformed using DeMorgan, to keep the text identical */ +#endif /* THREAD */ pins= hot ? &keycache->used_ins : &keycache->used_last; ins= *pins; if (ins) @@ -1101,6 +1128,7 @@ static inline void remove_reader(BLOCK_LINK *block) static inline void wait_for_readers(KEY_CACHE *keycache, BLOCK_LINK *block) { +#ifdef THREAD struct st_my_thread_var *thread= my_thread_var; while (block->hash_link->requests) { @@ -1111,6 +1139,9 @@ static inline void wait_for_readers(KEY_CACHE *keycache, BLOCK_LINK *block) keycache_pthread_cond_wait(&thread->suspend, &keycache->cache_lock); block->condvar= NULL; } +#else + KEYCACHE_DBUG_ASSERT(block->hash_link->requests == 0); +#endif } @@ -1140,6 +1171,7 @@ static void unlink_hash(KEY_CACHE *keycache, HASH_LINK *hash_link) if ((*hash_link->prev= hash_link->next)) hash_link->next->prev= hash_link->prev; hash_link->block= NULL; +#ifdef THREAD if (keycache->waiting_for_hash_link.last_thread) { /* Signal that a free hash link has appeared */ @@ -1175,6 +1207,9 @@ static void unlink_hash(KEY_CACHE *keycache, HASH_LINK *hash_link) hash_link); return; } +#else /* THREAD */ + KEYCACHE_DBUG_ASSERT(! (keycache->waiting_for_hash_link.last_thread)); +#endif /* THREAD */ hash_link->next= keycache->free_hash_list; keycache->free_hash_list= hash_link; } @@ -1240,6 +1275,7 @@ restart: } else { +#ifdef THREAD /* Wait for a free hash link */ struct st_my_thread_var *thread= my_thread_var; KEYCACHE_DBUG_PRINT("get_hash_link", ("waiting")); @@ -1252,6 +1288,9 @@ restart: keycache_pthread_cond_wait(&thread->suspend, &keycache->cache_lock); thread->opt_info= NULL; +#else + KEYCACHE_DBUG_ASSERT(0); +#endif goto restart; } hash_link->file= file; @@ -1363,6 +1402,7 @@ restart: /* Wait intil the page is flushed on disk */ hash_link->requests--; { +#ifdef THREAD struct st_my_thread_var *thread= my_thread_var; add_to_queue(&block->wqueue[COND_FOR_SAVED], thread); do @@ -1373,6 +1413,16 @@ restart: &keycache->cache_lock); } while(thread->next); +#else + KEYCACHE_DBUG_ASSERT(0); + /* + Given the use of "resize_in_flush", it seems impossible + that this whole branch is ever entered in single-threaded case + because "(wrmode && keycache->resize_in_flush)" cannot be true. + TODO: Check this, and then put the whole branch into the + "#ifdef THREAD" guard. + */ +#endif } /* Invalidate page in the block if it has not been done yet */ if (block->status) @@ -1401,6 +1451,7 @@ restart: KEYCACHE_DBUG_PRINT("find_key_block", ("request waiting for old page to be saved")); { +#ifdef THREAD 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); @@ -1413,6 +1464,10 @@ restart: &keycache->cache_lock); } while(thread->next); +#else + KEYCACHE_DBUG_ASSERT(0); + /* No parallel requests in single-threaded case */ +#endif } KEYCACHE_DBUG_PRINT("find_key_block", ("request for old page resubmitted")); @@ -1471,6 +1526,7 @@ restart: all of them must get the same block */ +#ifdef THREAD if (! keycache->used_last) { struct st_my_thread_var *thread= my_thread_var; @@ -1486,6 +1542,9 @@ restart: while (thread->next); thread->opt_info= NULL; } +#else + KEYCACHE_DBUG_ASSERT(keycache->used_last); +#endif block= hash_link->block; if (! block) { @@ -1674,6 +1733,7 @@ static void read_block(KEY_CACHE *keycache, KEYCACHE_DBUG_PRINT("read_block", ("secondary request waiting for new page to be read")); { +#ifdef THREAD 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); @@ -1685,6 +1745,10 @@ static void read_block(KEY_CACHE *keycache, &keycache->cache_lock); } while (thread->next); +#else + KEYCACHE_DBUG_ASSERT(0); + /* No parallel requests in single-threaded case */ +#endif } KEYCACHE_DBUG_PRINT("read_block", ("secondary request: new page in cache")); @@ -1822,7 +1886,7 @@ byte *key_cache_read(KEY_CACHE *keycache, #ifndef THREAD /* This is only true if we where able to read everything in one block */ if (return_buffer) - return (block->buffer); + DBUG_RETURN(block->buffer); #endif buff+= read_length; filepos+= read_length+offset; @@ -2194,7 +2258,7 @@ static int flush_cached_blocks(KEY_CACHE *keycache, { int error; int last_errno= 0; - uint count= end-cache; + uint count= (uint) (end-cache); /* Don't lock the cache during the flush */ keycache_pthread_mutex_unlock(&keycache->cache_lock); @@ -2400,6 +2464,7 @@ restart: #endif block= first_in_switch; { +#ifdef THREAD struct st_my_thread_var *thread= my_thread_var; add_to_queue(&block->wqueue[COND_FOR_SAVED], thread); do @@ -2410,6 +2475,10 @@ restart: &keycache->cache_lock); } while (thread->next); +#else + KEYCACHE_DBUG_ASSERT(0); + /* No parallel requests in single-threaded case */ +#endif } #if defined(KEYCACHE_DEBUG) cnt++; @@ -2576,7 +2645,6 @@ static void test_key_cache(KEY_CACHE *keycache __attribute__((unused)), 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; @@ -2684,9 +2752,12 @@ static int keycache_pthread_cond_wait(pthread_cond_t *cond, 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. */ + /* + timeval uses microseconds. + timespec uses nanoseconds. + 1 nanosecond = 1000 micro seconds + */ + timeout.tv_nsec= now.tv_usec * 1000; KEYCACHE_THREAD_TRACE_END("started waiting"); #if defined(KEYCACHE_DEBUG) cnt++; @@ -2696,17 +2767,15 @@ static int keycache_pthread_cond_wait(pthread_cond_t *cond, #endif rc= pthread_cond_timedwait(cond, mutex, &timeout); KEYCACHE_THREAD_TRACE_BEGIN("finished waiting"); -#if defined(KEYCACHE_DEBUG) - if (rc == ETIMEDOUT) + if (rc == ETIMEDOUT || rc == ETIME) { +#if defined(KEYCACHE_DEBUG) fprintf(keycache_debug_log,"aborted by keycache timeout\n"); fclose(keycache_debug_log); abort(); - } #endif - - if (rc == ETIMEDOUT) keycache_dump(); + } #if defined(KEYCACHE_DEBUG) KEYCACHE_DBUG_ASSERT(rc != ETIMEDOUT); diff --git a/mysys/my_access.c b/mysys/my_access.c index 237312b5c9b..99e7a28914d 100644 --- a/mysys/my_access.c +++ b/mysys/my_access.c @@ -44,7 +44,7 @@ int my_access(const char *path, int amode) result= GetFileAttributesEx(path, GetFileExInfoStandard, &fileinfo); if (! result || - (fileinfo.dwFileAttributes & FILE_ATTRIBUTE_READONLY) && (amode & F_OK)) + (fileinfo.dwFileAttributes & FILE_ATTRIBUTE_READONLY) && (amode & W_OK)) { my_errno= errno= EACCES; return -1; diff --git a/mysys/my_alloc.c b/mysys/my_alloc.c index d52a8efeed2..d5346d530c3 100644 --- a/mysys/my_alloc.c +++ b/mysys/my_alloc.c @@ -39,10 +39,11 @@ 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. + 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))) { @@ -71,6 +72,7 @@ void init_alloc_root(MEM_ROOT *mem_root, uint block_size, DBUG_VOID_RETURN; } + /* SYNOPSIS reset_root_defaults() @@ -86,7 +88,7 @@ void init_alloc_root(MEM_ROOT *mem_root, uint block_size, reuse one of existing blocks as prealloc block, or malloc new one of requested size. If no blocks can be reused, all unused blocks are freed before allocation. - */ +*/ void reset_root_defaults(MEM_ROOT *mem_root, uint block_size, uint pre_alloc_size __attribute__((unused))) @@ -166,7 +168,8 @@ gptr alloc_root(MEM_ROOT *mem_root,unsigned int Size) gptr point; reg1 USED_MEM *next= 0; reg2 USED_MEM **prev; - + DBUG_ENTER("alloc_root"); + DBUG_PRINT("enter",("root: 0x%lx", mem_root)); DBUG_ASSERT(alloc_root_inited(mem_root)); Size= ALIGN_SIZE(Size); @@ -213,15 +216,63 @@ gptr alloc_root(MEM_ROOT *mem_root,unsigned int Size) mem_root->used= next; mem_root->first_block_usage= 0; } - return(point); + DBUG_PRINT("exit",("ptr: 0x%lx", (ulong) point)); + DBUG_RETURN(point); #endif } -#ifdef SAFEMALLOC -#define TRASH(X) bfill(((char*)(X) + ((X)->size-(X)->left)), (X)->left, 0xa5) -#else -#define TRASH /* no-op */ -#endif + +/* + Allocate many pointers at the same time. + + DESCRIPTION + ptr1, ptr2, etc all point into big allocated memory area. + + SYNOPSIS + multi_alloc_root() + root Memory root + ptr1, length1 Multiple arguments terminated by a NULL pointer + ptr2, length2 ... + ... + NULL + + RETURN VALUE + A pointer to the beginning of the allocated memory block + in case of success or NULL if out of memory. +*/ + +gptr multi_alloc_root(MEM_ROOT *root, ...) +{ + va_list args; + char **ptr, *start, *res; + uint tot_length, length; + DBUG_ENTER("multi_alloc_root"); + + va_start(args, root); + tot_length= 0; + while ((ptr= va_arg(args, char **))) + { + length= va_arg(args, uint); + tot_length+= ALIGN_SIZE(length); + } + va_end(args); + + if (!(start= (char*) alloc_root(root, tot_length))) + DBUG_RETURN(0); /* purecov: inspected */ + + va_start(args, root); + res= start; + while ((ptr= va_arg(args, char **))) + { + *ptr= res; + length= va_arg(args, uint); + res+= ALIGN_SIZE(length); + } + va_end(args); + DBUG_RETURN((gptr) start); +} + +#define TRASH_MEM(X) TRASH(((char*)(X) + ((X)->size-(X)->left)), (X)->left) /* Mark all data in blocks free for reusage */ @@ -235,7 +286,7 @@ static inline void mark_blocks_free(MEM_ROOT* root) for (next= root->free; next; next= *(last= &next->next)) { next->left= next->size - ALIGN_SIZE(sizeof(USED_MEM)); - TRASH(next); + TRASH_MEM(next); } /* Combine the free and the used list */ @@ -245,7 +296,7 @@ static inline void mark_blocks_free(MEM_ROOT* root) for (; next; next= next->next) { next->left= next->size - ALIGN_SIZE(sizeof(USED_MEM)); - TRASH(next); + TRASH_MEM(next); } /* Now everything is set; Indicate that nothing is used anymore */ @@ -257,6 +308,20 @@ static inline void mark_blocks_free(MEM_ROOT* root) /* Deallocate everything used by alloc_root or just move used blocks to free list if called with MY_USED_TO_FREE + + SYNOPSIS + free_root() + root Memory root + MyFlags Flags for what should be freed: + + MY_MARK_BLOCKS_FREED Don't free blocks, just mark them free + MY_KEEP_PREALLOC If this is not set, then free also the + preallocated block + + NOTES + One can call this function either with root block initialised with + init_alloc_root() or with a bzero()-ed block. + It's also safe to call this multiple times with the same mem_root. */ void free_root(MEM_ROOT *root, myf MyFlags) @@ -292,7 +357,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); + TRASH_MEM(root->pre_alloc); root->free->next=0; } root->block_num= 4; @@ -328,7 +393,7 @@ void set_prealloc_root(MEM_ROOT *root, char *ptr) char *strdup_root(MEM_ROOT *root,const char *str) { - return strmake_root(root, str, strlen(str)); + return strmake_root(root, str, (uint) strlen(str)); } char *strmake_root(MEM_ROOT *root,const char *str, uint len) diff --git a/mysys/my_bit.c b/mysys/my_bit.c index 55dd72f5f76..01c9b5ea68d 100644 --- a/mysys/my_bit.c +++ b/mysys/my_bit.c @@ -71,3 +71,8 @@ uint my_count_bits(ulonglong v) #endif } +uint my_count_bits_ushort(ushort v) +{ + return nbits[v]; +} + diff --git a/mysys/my_bitmap.c b/mysys/my_bitmap.c index f0d3339535d..49c23aaeae5 100644 --- a/mysys/my_bitmap.c +++ b/mysys/my_bitmap.c @@ -29,6 +29,9 @@ * bitmap_intersect() is an exception :) (for for Bitmap::intersect(ulonglong map2buff)) + If THREAD is defined all bitmap operations except bitmap_init/bitmap_free + are thread-safe. + TODO: Make assembler THREAD safe versions of these using test-and-set instructions */ @@ -37,7 +40,7 @@ #include <my_bitmap.h> #include <m_string.h> -static inline void bitmap_lock(MY_BITMAP* map) +static inline void bitmap_lock(MY_BITMAP *map __attribute__((unused))) { #ifdef THREAD if (map->mutex) @@ -45,7 +48,7 @@ static inline void bitmap_lock(MY_BITMAP* map) #endif } -static inline void bitmap_unlock(MY_BITMAP* map) +static inline void bitmap_unlock(MY_BITMAP *map __attribute__((unused))) { #ifdef THREAD if (map->mutex) @@ -66,7 +69,7 @@ my_bool bitmap_init(MY_BITMAP *map, uchar *buf, uint bitmap_size, (thread_safe ? sizeof(pthread_mutex_t) : 0), MYF(MY_WME | MY_ZEROFILL)))) - return 1; + DBUG_RETURN(1); map->bitmap_size=bitmap_size; #ifdef THREAD if (thread_safe) @@ -106,6 +109,52 @@ void bitmap_set_bit(MY_BITMAP *map, uint bitmap_bit) } +/* + test if bit already set and set it if it was not (thread unsafe method) + + SYNOPSIS + bitmap_fast_test_and_set() + MAP bit map struct + BIT bit number + + RETURN + 0 bit was not set + !=0 bit was set +*/ + +my_bool bitmap_fast_test_and_set(MY_BITMAP *map, uint bitmap_bit) +{ + uchar *byte= map->bitmap + (bitmap_bit / 8); + uchar bit= 1 << ((bitmap_bit) & 7); + uchar res= (*byte) & bit; + *byte|= bit; + return res; +} + + +/* + test if bit already set and set it if it was not (thread safe method) + + SYNOPSIS + bitmap_fast_test_and_set() + map bit map struct + bitmap_bit bit number + + RETURN + 0 bit was not set + !=0 bit was set +*/ + +my_bool bitmap_test_and_set(MY_BITMAP *map, uint bitmap_bit) +{ + my_bool res; + DBUG_ASSERT(map->bitmap && bitmap_bit < map->bitmap_size*8); + bitmap_lock(map); + res= bitmap_fast_test_and_set(map, bitmap_bit); + bitmap_unlock(map); + return res; +} + uint bitmap_set_next(MY_BITMAP *map) { uchar *bitmap=map->bitmap; @@ -290,6 +339,37 @@ void bitmap_intersect(MY_BITMAP *map, const MY_BITMAP *map2) } +/* + Set/clear all bits above a bit. + + SYNOPSIS + bitmap_set_above() + map RETURN The bitmap to change. + from_byte The bitmap buffer byte offset to start with. + use_bit The bit value (1/0) to use for all upper bits. + + NOTE + You can only set/clear full bytes. + The function is meant for the situation that you copy a smaller bitmap + to a bigger bitmap. Bitmap lengths are always multiple of eigth (the + size of a byte). Using 'from_byte' saves multiplication and division + by eight during parameter passing. + + RETURN + void +*/ + +void bitmap_set_above(MY_BITMAP *map, uint from_byte, uint use_bit) +{ + uchar use_byte= use_bit ? 0xff : 0; + uchar *to= map->bitmap + from_byte; + uchar *end= map->bitmap + map->bitmap_size; + + while (to < end) + *to++= use_byte; +} + + void bitmap_subtract(MY_BITMAP *map, const MY_BITMAP *map2) { uchar *to=map->bitmap, *from=map2->bitmap, *end; @@ -327,3 +407,66 @@ void bitmap_union(MY_BITMAP *map, const MY_BITMAP *map2) bitmap_unlock(map); } + +/* + SYNOPSIS + bitmap_bits_set() + map + RETURN + Number of set bits in the bitmap. +*/ + +uint bitmap_bits_set(const MY_BITMAP *map) +{ + uchar *m= map->bitmap; + uchar *end= m + map->bitmap_size; + uint res= 0; + + DBUG_ASSERT(map->bitmap); + bitmap_lock((MY_BITMAP *)map); + while (m < end) + { + res+= my_count_bits_ushort(*m++); + } + bitmap_unlock((MY_BITMAP *)map); + return res; +} + + +/* + SYNOPSIS + bitmap_get_first() + map + RETURN + Number of first unset bit in the bitmap or MY_BIT_NONE if all bits are set. +*/ + +uint bitmap_get_first(const MY_BITMAP *map) +{ + uchar *bitmap=map->bitmap; + uint bit_found = MY_BIT_NONE; + uint bitmap_size=map->bitmap_size*8; + uint i; + + DBUG_ASSERT(map->bitmap); + bitmap_lock((MY_BITMAP *)map); + for (i=0; i < bitmap_size ; i++, bitmap++) + { + if (*bitmap != 0xff) + { /* Found slot with free bit */ + uint b; + for (b=0; ; b++) + { + if (!(*bitmap & (1 << b))) + { + bit_found = (i*8)+b; + break; + } + } + break; /* Found bit */ + } + } + bitmap_unlock((MY_BITMAP *)map); + return bit_found; +} + diff --git a/mysys/my_chsize.c b/mysys/my_chsize.c index cf26428d65f..9760de29a08 100644 --- a/mysys/my_chsize.c +++ b/mysys/my_chsize.c @@ -30,7 +30,9 @@ MyFlags Flags DESCRIPTION - my_chsize() truncates file if shorter else fill with the filler character + my_chsize() truncates file if shorter else fill with the filler character. + The function also changes the file pointer. Usually it points to the end + of the file after execution. RETURN VALUE 0 Ok @@ -48,9 +50,9 @@ int my_chsize(File fd, my_off_t newlength, int filler, myf MyFlags) DBUG_PRINT("info",("old_size: %ld", (ulong) oldsize)); if (oldsize > newlength) + { #if defined(HAVE_SETFILEPOINTER) /* This is for the moment only true on windows */ - { long is_success; HANDLE win_file= (HANDLE) _get_osfhandle(fd); long length_low, length_high; @@ -63,35 +65,29 @@ int my_chsize(File fd, my_off_t newlength, int filler, myf MyFlags) DBUG_RETURN(0); my_errno= GetLastError(); goto err; - } #elif defined(HAVE_FTRUNCATE) - { if (ftruncate(fd, (off_t) newlength)) { my_errno= errno; goto err; } DBUG_RETURN(0); - } #elif defined(HAVE_CHSIZE) - { if (chsize(fd, (off_t) newlength)) { my_errno=errno; goto err; } DBUG_RETURN(0); - } #else - { /* 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_variables(my_off_t, newlength, oldsize); - } #endif + } /* Full file with 'filler' until it's as big as requested */ bfill(buff, IO_SIZE, filler); diff --git a/mysys/my_copy.c b/mysys/my_copy.c index 03f3feb54d3..2fb022a25f2 100644 --- a/mysys/my_copy.c +++ b/mysys/my_copy.c @@ -16,7 +16,7 @@ #define USES_TYPES /* sys/types is included */ #include "mysys_priv.h" -#include <sys/stat.h> +#include <my_dir.h> /* for stat */ #include <m_string.h> #if defined(HAVE_UTIME_H) #include <utime.h> @@ -53,26 +53,27 @@ struct utimbuf { int my_copy(const char *from, const char *to, myf MyFlags) { uint Count; - int new_file_stat, create_flag; + my_bool new_file_stat= 0; /* 1 if we could stat "to" */ + int create_flag; File from_file,to_file; char buff[IO_SIZE]; - struct stat stat_buff,new_stat_buff; + MY_STAT stat_buff,new_stat_buff; DBUG_ENTER("my_copy"); DBUG_PRINT("my",("from %s to %s MyFlags %d", from, to, MyFlags)); from_file=to_file= -1; - new_file_stat=0; + DBUG_ASSERT(!(MyFlags & (MY_FNABP | MY_NABP))); /* for my_read/my_write */ if (MyFlags & MY_HOLD_ORIGINAL_MODES) /* Copy stat if possible */ - new_file_stat=stat((char*) to, &new_stat_buff); + new_file_stat= test(my_stat((char*) to, &new_stat_buff, MYF(0))); if ((from_file=my_open(from,O_RDONLY | O_SHARE,MyFlags)) >= 0) { - if (stat(from,&stat_buff)) + if (!my_stat(from, &stat_buff, MyFlags)) { my_errno=errno; goto err; } - if (MyFlags & MY_HOLD_ORIGINAL_MODES && !new_file_stat) + if (MyFlags & MY_HOLD_ORIGINAL_MODES && new_file_stat) stat_buff=new_stat_buff; create_flag= (MyFlags & MY_DONT_OVERWRITE_FILE) ? O_EXCL : O_TRUNC; @@ -91,7 +92,7 @@ int my_copy(const char *from, const char *to, myf MyFlags) /* Copy modes if possible */ - if (MyFlags & MY_HOLD_ORIGINAL_MODES && new_file_stat) + if (MyFlags & MY_HOLD_ORIGINAL_MODES && !new_file_stat) DBUG_RETURN(0); /* File copyed but not stat */ VOID(chmod(to, stat_buff.st_mode & 07777)); /* Copy modes */ #if !defined(MSDOS) && !defined(__WIN__) && !defined(__EMX__) && !defined(OS2) && !defined(__NETWARE__) diff --git a/mysys/my_create.c b/mysys/my_create.c index 5fa97a9ca78..a85417c7701 100644 --- a/mysys/my_create.c +++ b/mysys/my_create.c @@ -47,13 +47,16 @@ File my_create(const char *FileName, int CreateFlags, int access_flags, #elif defined(VMS) fd = open((my_string) FileName, access_flags | O_CREAT, 0, "ctx=stm","ctx=bin"); -#elif defined(MSDOS) || defined(__WIN__) || defined(__EMX__) || defined(OS2) +#elif defined(MSDOS) || defined(__EMX__) || defined(OS2) if (access_flags & O_SHARE) fd = sopen((my_string) FileName, access_flags | O_CREAT | O_BINARY, SH_DENYNO, MY_S_IREAD | MY_S_IWRITE); else fd = open((my_string) FileName, access_flags | O_CREAT | O_BINARY, MY_S_IREAD | MY_S_IWRITE); +#elif defined(__WIN__) + fd= my_sopen((my_string) FileName, access_flags | O_CREAT | O_BINARY, + SH_DENYNO, MY_S_IREAD | MY_S_IWRITE); #else fd = open(FileName, access_flags); #endif diff --git a/mysys/my_error.c b/mysys/my_error.c index 8a377f63c7e..d7177e7a047 100644 --- a/mysys/my_error.c +++ b/mysys/my_error.c @@ -22,10 +22,40 @@ /* Define some external variables for error handling */ -const char ** NEAR my_errmsg[MAXMAPS]={0,0,0,0}; +/* + WARNING! + my_error family functions have to be used according following rules: + - if message have not parameters use my_message(ER_CODE, ER(ER_CODE), MYF(N)) + - if message registered use my_error(ER_CODE, MYF(N), ...). + - With some special text of errror message use: + my_printf_error(ER_CODE, format, MYF(N), ...) +*/ + char NEAR errbuff[NRERRBUFFS][ERRMSGSIZE]; /* + Message texts are registered into a linked list of 'my_err_head' structs. + Each struct contains (1.) an array of pointers to C character strings with + '\0' termination, (2.) the error number for the first message in the array + (array index 0) and (3.) the error number for the last message in the array + (array index (last - first)). + The array may contain gaps with NULL pointers and pointers to empty strings. + Both kinds of gaps will be translated to "Unknown error %d.", if my_error() + is called with a respective error number. + The list of header structs is sorted in increasing order of error numbers. + Negative error numbers are allowed. Overlap of error numbers is not allowed. + Not registered error numbers will be translated to "Unknown error %d.". +*/ +static struct my_err_head +{ + struct my_err_head *meh_next; /* chain link */ + const char **meh_errmsgs; /* error messages array */ + int meh_first; /* error number matching array slot 0 */ + int meh_last; /* error number matching last slot */ +} my_errmsgs_globerrs = {NULL, globerrs, EE_ERROR_FIRST, EE_ERROR_LAST}; +static struct my_err_head *my_errmsgs_list= &my_errmsgs_globerrs; + +/* Error message to user SYNOPSIS @@ -33,115 +63,42 @@ 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. + RETURN + What (*error_handler_hook)() returns: + 0 OK */ -int my_error(int nr,myf MyFlags, ...) +int my_error(int nr, myf MyFlags, ...) { - va_list ap; - uint olen, plen; - reg1 const char *tpos; - reg2 char *endpos; - char * par; - char ebuff[ERRMSGSIZE+20]; - int prec_chars; /* output precision */ - my_bool prec_supplied; + const char *format; + struct my_err_head *meh_p; + va_list args; + char ebuff[ERRMSGSIZE + 20]; 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)); - if (nr / ERRMOD == GLOB && my_errmsg[GLOB] == 0) - init_glob_errs(); + /* Search for the error messages array, which could contain the message. */ + for (meh_p= my_errmsgs_list; meh_p; meh_p= meh_p->meh_next) + if (nr <= meh_p->meh_last) + break; - olen=(uint) strlen(tpos=my_errmsg[nr / ERRMOD][nr % ERRMOD]); - endpos=ebuff; +#ifdef SHARED_LIBRARY + if ((meh_p == &my_errmsgs_globerrs) && ! globerrs[0]) + init_glob_errs(); +#endif - while (*tpos) + /* get the error message string. Default, if NULL or empty string (""). */ + if (! (format= (meh_p && (nr >= meh_p->meh_first)) ? + meh_p->meh_errmsgs[nr - meh_p->meh_first] : NULL) || ! *format) + (void) my_snprintf (ebuff, sizeof(ebuff), "Unknown error %d", nr); + else { - if (tpos[0] != '%') - { - *endpos++= *tpos++; /* Copy ordinary char */ - continue; - } - if (*++tpos == '%') /* test if %% */ - { - olen--; - } - else - { - /* - Skip size/precision flags to be compatible with printf. - The only size/precision flag supported is "%.*s". - If "%.*u" or "%.*d" are encountered, the precision number is read - from the variable argument list but its value is ignored. - */ - prec_supplied= 0; - if (*tpos== '.') - { - tpos++; - olen--; - if (*tpos == '*') - { - tpos++; - olen--; - prec_chars= va_arg(ap, int); /* get length parameter */ - prec_supplied= 1; - } - } - - if (!prec_supplied) - { - while (my_isdigit(&my_charset_latin1, *tpos) || *tpos == '.' || - *tpos == '-') - tpos++; - - if (*tpos == 'l') /* Skip 'l' argument */ - tpos++; - } - - if (*tpos == 's') /* String parameter */ - { - par= va_arg(ap, char *); - plen= (uint) strlen(par); - if (prec_supplied && prec_chars > 0) - plen= min((uint)prec_chars, plen); - if (olen + plen < ERRMSGSIZE+2) /* Replace if possible */ - { - 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); - if (*tpos == 'd') - plen= (uint) (int10_to_str((long) iarg, endpos, -10) - endpos); - else - plen= (uint) (int10_to_str((long) (uint) iarg, endpos, 10) - endpos); - if (olen + plen < ERRMSGSIZE+2) /* Replace parameter if possible */ - { - endpos+= plen; - tpos++; - olen+= plen-2; - continue; - } - } - } - *endpos++= '%'; /* % used as % or unknown code */ + va_start(args,MyFlags); + (void) my_vsnprintf (ebuff, sizeof(ebuff), format, args); + va_end(args); } - *endpos= '\0'; /* End of errmessage */ - va_end(ap); DBUG_RETURN((*error_handler_hook)(nr, ebuff, MyFlags)); } @@ -160,11 +117,14 @@ int my_printf_error(uint error, const char *format, myf MyFlags, ...) { va_list args; char ebuff[ERRMSGSIZE+20]; + DBUG_ENTER("my_printf_error"); + DBUG_PRINT("my", ("nr: %d MyFlags: %d errno: %d Format: %s", + error, MyFlags, errno, format)); va_start(args,MyFlags); - (void) vsprintf (ebuff,format,args); + (void) my_vsnprintf (ebuff, sizeof(ebuff), format, args); va_end(args); - return (*error_handler_hook)(error, ebuff, MyFlags); + DBUG_RETURN((*error_handler_hook)(error, ebuff, MyFlags)); } /* @@ -181,3 +141,112 @@ int my_message(uint error, const char *str, register myf MyFlags) { return (*error_handler_hook)(error, str, MyFlags); } + + +/* + Register error messages for use with my_error(). + + SYNOPSIS + my_error_register() + errmsgs array of pointers to error messages + first error number of first message in the array + last error number of last message in the array + + DESCRIPTION + The pointer array is expected to contain addresses to NUL-terminated + C character strings. The array contains (last - first + 1) pointers. + NULL pointers and empty strings ("") are allowed. These will be mapped to + "Unknown error" when my_error() is called with a matching error number. + This function registers the error numbers 'first' to 'last'. + No overlapping with previously registered error numbers is allowed. + + RETURN + 0 OK + != 0 Error +*/ + +int my_error_register(const char **errmsgs, int first, int last) +{ + struct my_err_head *meh_p; + struct my_err_head **search_meh_pp; + + /* Allocate a new header structure. */ + if (! (meh_p= (struct my_err_head*) my_malloc(sizeof(struct my_err_head), + MYF(MY_WME)))) + return 1; + meh_p->meh_errmsgs= errmsgs; + meh_p->meh_first= first; + meh_p->meh_last= last; + + /* Search for the right position in the list. */ + for (search_meh_pp= &my_errmsgs_list; + *search_meh_pp; + search_meh_pp= &(*search_meh_pp)->meh_next) + { + if ((*search_meh_pp)->meh_last > first) + break; + } + + /* Error numbers must be unique. No overlapping is allowed. */ + if (*search_meh_pp && ((*search_meh_pp)->meh_first <= last)) + { + my_free((gptr)meh_p, MYF(0)); + return 1; + } + + /* Insert header into the chain. */ + meh_p->meh_next= *search_meh_pp; + *search_meh_pp= meh_p; + return 0; +} + + +/* + Unregister formerly registered error messages. + + SYNOPSIS + my_error_unregister() + first error number of first message + last error number of last message + + DESCRIPTION + This function unregisters the error numbers 'first' to 'last'. + These must have been previously registered by my_error_register(). + 'first' and 'last' must exactly match the registration. + If a matching registration is present, the header is removed from the + list and the pointer to the error messages pointers array is returned. + Otherwise, NULL is returned. + + RETURN + non-NULL OK, returns address of error messages pointers array. + NULL Error, no such number range registered. +*/ + +const char **my_error_unregister(int first, int last) +{ + struct my_err_head *meh_p; + struct my_err_head **search_meh_pp; + const char **errmsgs; + + /* Search for the registration in the list. */ + for (search_meh_pp= &my_errmsgs_list; + *search_meh_pp; + search_meh_pp= &(*search_meh_pp)->meh_next) + { + if (((*search_meh_pp)->meh_first == first) && + ((*search_meh_pp)->meh_last == last)) + break; + } + if (! *search_meh_pp) + return NULL; + + /* Remove header from the chain. */ + meh_p= *search_meh_pp; + *search_meh_pp= meh_p->meh_next; + + /* Save the return value and free the header. */ + errmsgs= meh_p->meh_errmsgs; + my_free((gptr) meh_p, MYF(0)); + + return errmsgs; +} diff --git a/mysys/my_getopt.c b/mysys/my_getopt.c index dfc3fb3d39c..95dc5afeae9 100644 --- a/mysys/my_getopt.c +++ b/mysys/my_getopt.c @@ -180,7 +180,7 @@ int handle_options(int *argc, char ***argv, } opt_str= check_struct_option(cur_arg, key_name); optend= strcend(opt_str, '='); - length= optend - opt_str; + length= (uint) (optend - opt_str); if (*optend == '=') optend++; else @@ -205,7 +205,8 @@ int handle_options(int *argc, char ***argv, { if (!getopt_compare_strings(special_opt_prefix[i], opt_str, special_opt_prefix_lengths[i]) && - opt_str[special_opt_prefix_lengths[i]] == '-') + (opt_str[special_opt_prefix_lengths[i]] == '-' || + opt_str[special_opt_prefix_lengths[i]] == '_')) { /* We were called with a special prefix, we can reuse opt_found @@ -322,7 +323,7 @@ int handle_options(int *argc, char ***argv, return EXIT_NO_ARGUMENT_ALLOWED; } value= optp->var_type & GET_ASK_ADDR ? - (*getopt_get_addr)(key_name, strlen(key_name), optp) : optp->value; + (*getopt_get_addr)(key_name, (uint) strlen(key_name), optp) : optp->value; if (optp->arg_type == NO_ARG) { @@ -341,11 +342,24 @@ int handle_options(int *argc, char ***argv, --enable-'option-name'. *optend was set to '0' if one used --disable-option */ - my_bool tmp= (my_bool) (!optend || *optend == '1'); - *((my_bool*) value)= tmp; (*argc)--; + if (!optend || *optend == '1' || + !my_strcasecmp(&my_charset_latin1, optend, "true")) + *((my_bool*) value)= (my_bool) 1; + else if (*optend == '0' || + !my_strcasecmp(&my_charset_latin1, optend, "false")) + *((my_bool*) value)= (my_bool) 0; + else + { + my_getopt_error_reporter(WARNING_LEVEL, + "%s: ignoring option '--%s' due to \ +invalid value '%s'\n", + my_progname, optp->name, optend); + continue; + } get_one_option(optp->id, optp, - tmp ? (char*) "1" : disabled_my_option); + *((my_bool*) value) ? + (char*) "1" : disabled_my_option); continue; } argument= optend; @@ -518,7 +532,7 @@ static char *check_struct_option(char *cur_arg, char *key_name) */ if (end - ptr > 1) { - uint len= ptr - cur_arg; + uint len= (uint) (ptr - cur_arg); set_if_smaller(len, FN_REFLEN-1); strmake(key_name, cur_arg, len); return ++ptr; @@ -586,16 +600,27 @@ static int setval(const struct my_option *opts, gptr *value, char *argument, return 0; } -/* - function: findopt - Arguments: opt_pattern, length of opt_pattern, opt_struct, first found - name (ffname) +/* + Find option - Go through all options in the my_option struct. Return number - of options found that match the pattern and in the argument - list the option found, if any. In case of ambiguous option, store - the name in ffname argument + SYNOPSIS + findopt() + optpat Prefix of option to find (with - or _) + length Length of optpat + opt_res Options + ffname Place for pointer to first found name + + IMPLEMENTATION + Go through all options in the my_option struct. Return number + of options found that match the pattern and in the argument + list the option found, if any. In case of ambiguous option, store + the name in ffname argument + + RETURN + 0 No matching options + # Number of matching options + ffname points to first matching option */ static int findopt(char *optpat, uint length, @@ -610,12 +635,21 @@ static int findopt(char *optpat, uint length, if (!getopt_compare_strings(opt->name, optpat, length)) /* match found */ { (*opt_res)= opt; - if (!count) - *ffname= (char *) opt->name; /* We only need to know one prev */ if (!opt->name[length]) /* Exact match */ return 1; - if (!count || strcmp(*ffname, opt->name)) /* Don't count synonyms */ + if (!count) + { + count= 1; + *ffname= (char *) opt->name; /* We only need to know one prev */ + } + else if (strcmp(*ffname, opt->name)) + { + /* + The above test is to not count same option twice + (see mysql.cc, option "help") + */ count++; + } } } return count; @@ -832,7 +866,7 @@ void my_print_help(const struct my_option *options) if (strlen(optp->name)) { printf("--%s", optp->name); - col+= 2 + strlen(optp->name); + col+= 2 + (uint) strlen(optp->name); if ((optp->var_type & GET_TYPE_MASK) == GET_STR || (optp->var_type & GET_TYPE_MASK) == GET_STR_ALLOC) { @@ -903,7 +937,7 @@ void my_print_variables(const struct my_option *options) if (value) { printf("%s", optp->name); - length= strlen(optp->name); + length= (uint) strlen(optp->name); for (; length < name_space; length++) putchar(' '); switch ((optp->var_type & GET_TYPE_MASK)) { diff --git a/mysys/my_handler.c b/mysys/my_handler.c index 3eed0ee6c08..56f2298a9f0 100644 --- a/mysys/my_handler.c +++ b/mysys/my_handler.c @@ -21,11 +21,11 @@ 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) + if (!part_key) return charset_info->coll->strnncollsp(charset_info, a, a_length, - b, b_length); + b, b_length, (my_bool)!skip_end_space); return charset_info->coll->strnncoll(charset_info, a, a_length, - b, b_length, part_key); + b, b_length, part_key); } @@ -196,6 +196,7 @@ int ha_key_cmp(register HA_KEYSEG *keyseg, register uchar *a, } break; case HA_KEYTYPE_BINARY: + case HA_KEYTYPE_BIT: if (keyseg->flag & HA_SPACE_PACK) { int a_length,b_length,pack_length; @@ -224,13 +225,12 @@ int ha_key_cmp(register HA_KEYSEG *keyseg, register uchar *a, b+=length; } break; - case HA_KEYTYPE_VARTEXT: + case HA_KEYTYPE_VARTEXT1: + case HA_KEYTYPE_VARTEXT2: { - int a_length,full_a_length,b_length,full_b_length,pack_length; + int a_length,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 && @@ -239,14 +239,17 @@ int ha_key_cmp(register HA_KEYSEG *keyseg, register uchar *a, next_key_length <= 0), (my_bool) ((nextflag & (SEARCH_FIND | SEARCH_UPDATE)) == - SEARCH_FIND)))) + SEARCH_FIND && + ! (keyseg->flag & + HA_END_SPACE_ARE_EQUAL))))) return ((keyseg->flag & HA_REVERSE_SORT) ? -flag : flag); - a+= full_a_length; - b+= full_b_length; + a+= a_length; + b+= b_length; break; } break; - case HA_KEYTYPE_VARBINARY: + case HA_KEYTYPE_VARBINARY1: + case HA_KEYTYPE_VARBINARY2: { int a_length,b_length,pack_length; get_key_length(a_length,a); @@ -511,8 +514,8 @@ HA_KEYSEG *ha_find_null(HA_KEYSEG *keyseg, uchar *a) else a= end; break; - case HA_KEYTYPE_VARTEXT: - case HA_KEYTYPE_VARBINARY: + case HA_KEYTYPE_VARTEXT1: + case HA_KEYTYPE_VARBINARY1: { int a_length; get_key_length(a_length, a); diff --git a/mysys/my_init.c b/mysys/my_init.c index d68c76bef1b..c2bfdde0ddd 100644 --- a/mysys/my_init.c +++ b/mysys/my_init.c @@ -96,7 +96,7 @@ my_bool my_init(void) #endif { DBUG_ENTER("my_init"); - DBUG_PROCESS(my_progname ? my_progname : (char*) "unknown"); + DBUG_PROCESS((char*) (my_progname ? my_progname : "unknown")); if (!home_dir) { /* Don't initialize twice */ my_win_init(); @@ -409,7 +409,7 @@ static void netware_init() } /* Parse program name and change to base format */ - name= my_progname; + name= (char*) my_progname; for (; *name; name++) { if (*name == '\\') diff --git a/mysys/my_largepage.c b/mysys/my_largepage.c new file mode 100644 index 00000000000..0639c360b46 --- /dev/null +++ b/mysys/my_largepage.c @@ -0,0 +1,167 @@ +/* Copyright (C) 2004 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 HAVE_LARGE_PAGES + +#ifdef HAVE_SYS_IPC_H +#include <sys/ipc.h> +#endif + +#ifdef HAVE_SYS_SHM_H +#include <sys/shm.h> +#endif + +static uint my_get_large_page_size_int(void); +static gptr my_large_malloc_int(uint size, myf my_flags); +static my_bool my_large_free_int(gptr ptr, myf my_flags); + +/* Gets the size of large pages from the OS */ + +uint my_get_large_page_size(void) +{ + uint size; + DBUG_ENTER("my_get_large_page_size"); + + if (!(size = my_get_large_page_size_int())) + fprintf(stderr, "Warning: Failed to determine large page size\n"); + + DBUG_RETURN(size); +} + +/* + General large pages allocator. + Tries to allocate memory from large pages pool and falls back to + my_malloc_lock() in case of failure +*/ + +gptr my_large_malloc(uint size, myf my_flags) +{ + gptr ptr; + DBUG_ENTER("my_large_malloc"); + + if (my_use_large_pages && my_large_page_size) + { + if ((ptr = my_large_malloc_int(size, my_flags)) != NULL) + DBUG_RETURN(ptr); + if (my_flags & MY_WME) + fprintf(stderr, "Warning: Using conventional memory pool\n"); + } + + DBUG_RETURN(my_malloc_lock(size, my_flags)); +} + +/* + General large pages deallocator. + Tries to deallocate memory as if it was from large pages pool and falls back + to my_free_lock() in case of failure + */ + +void my_large_free(gptr ptr, myf my_flags __attribute__((unused))) +{ + DBUG_ENTER("my_large_free"); + + /* + my_large_free_int() can only fail if ptr was not allocated with + my_large_malloc_int(), i.e. my_malloc_lock() was used so we should free it + with my_free_lock() + */ + if (!my_use_large_pages || !my_large_page_size || + !my_large_free_int(ptr, my_flags)) + my_free_lock(ptr, my_flags); + + DBUG_VOID_RETURN; +} + +#ifdef HUGETLB_USE_PROC_MEMINFO +/* Linux-specific function to determine the size of large pages */ + +uint my_get_large_page_size_int(void) +{ + FILE *f; + uint size = 0; + char buf[256]; + DBUG_ENTER("my_get_large_page_size_int"); + + if (!(f = my_fopen("/proc/meminfo", O_RDONLY, MYF(MY_WME)))) + goto finish; + + while (fgets(buf, sizeof(buf), f)) + if (sscanf(buf, "Hugepagesize: %u kB", &size)) + break; + + my_fclose(f, MYF(MY_WME)); + +finish: + DBUG_RETURN(size * 1024); +} +#endif /* HUGETLB_USE_PROC_MEMINFO */ + +#if HAVE_DECL_SHM_HUGETLB +/* Linux-specific large pages allocator */ + +gptr my_large_malloc_int(uint size, myf my_flags) +{ + int shmid; + gptr ptr; + struct shmid_ds buf; + DBUG_ENTER("my_large_malloc_int"); + + /* Align block size to my_large_page_size */ + size = ((size - 1) & ~(my_large_page_size - 1)) + my_large_page_size; + + shmid = shmget(IPC_PRIVATE, (size_t)size, SHM_HUGETLB | SHM_R | SHM_W); + if (shmid < 0) + { + if (my_flags & MY_WME) + fprintf(stderr, + "Warning: Failed to allocate %d bytes from HugeTLB memory." + " errno %d\n", size, errno); + + DBUG_RETURN(NULL); + } + + ptr = shmat(shmid, NULL, 0); + if (ptr == (void *)-1) + { + if (my_flags& MY_WME) + fprintf(stderr, "Warning: Failed to attach shared memory segment," + " errno %d\n", errno); + shmctl(shmid, IPC_RMID, &buf); + + DBUG_RETURN(NULL); + } + + /* + Remove the shared memory segment so that it will be automatically freed + after memory is detached or process exits + */ + shmctl(shmid, IPC_RMID, &buf); + + DBUG_RETURN(ptr); +} + +/* Linux-specific large pages deallocator */ + +my_bool my_large_free_int(byte *ptr, myf my_flags __attribute__((unused))) +{ + DBUG_ENTER("my_large_free_int"); + DBUG_RETURN(shmdt(ptr) == 0); +} +#endif /* HAVE_DECL_SHM_HUGETLB */ + +#endif /* HAVE_LARGE_PAGES */ diff --git a/mysys/my_lib.c b/mysys/my_lib.c index 1908c70f407..03f2d91916d 100644 --- a/mysys/my_lib.c +++ b/mysys/my_lib.c @@ -170,6 +170,8 @@ MY_DIR *my_dir(const char *path, myf MyFlags) bzero(finfo.mystat, sizeof(MY_STAT)); VOID(strmov(tmp_file,dp->d_name)); VOID(my_stat(tmp_path, finfo.mystat, MyFlags)); + if (!(finfo.mystat->st_mode & MY_S_IREAD)) + continue; } else finfo.mystat= NULL; @@ -382,11 +384,10 @@ MY_DIR *my_dir(const char *path, myf MyFlags) DBUG_PRINT("my",("path: '%s' stat: %d MyFlags: %d",path,MyFlags)); /* Put LIB-CHAR as last path-character if not there */ - tmp_file=tmp_path; if (!*path) *tmp_file++ ='.'; /* From current dir */ - tmp_file= strmov(tmp_file,path); + tmp_file= strnmov(tmp_file, path, FN_REFLEN-5); if (tmp_file[-1] == FN_DEVCHAR) *tmp_file++= '.'; /* From current dev-dir */ if (tmp_file[-1] != FN_LIBCHAR) @@ -396,14 +397,6 @@ MY_DIR *my_dir(const char *path, myf MyFlags) tmp_file[2]='*'; tmp_file[3]='\0'; -#ifdef __BORLANDC__ - if ((handle= findfirst(tmp_path,&find,0)) == -1L) - goto error; -#else - if ((handle=_findfirst(tmp_path,&find)) == -1L) - goto error; -#endif - if (!(buffer= my_malloc(ALIGN_SIZE(sizeof(MY_DIR)) + ALIGN_SIZE(sizeof(DYNAMIC_ARRAY)) + sizeof(MEM_ROOT), MyFlags))) @@ -423,72 +416,92 @@ MY_DIR *my_dir(const char *path, myf MyFlags) /* MY_DIR structure is allocated and completly initialized at this point */ result= (MY_DIR*)buffer; - - do - { + #ifdef __BORLANDC__ - attrib= find.ff_attrib; + if ((handle= findfirst(tmp_path,&find,0)) == -1L) #else - attrib= find.attrib; + if ((handle=_findfirst(tmp_path,&find)) == -1L) +#endif + { + DBUG_PRINT("info", ("findfirst returned error, errno: %d", errno)); + if (errno != EINVAL) + goto error; /* - 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. + Could not read the directory, no read access. + Probably because by "chmod -r". + continue and return zero files in dir */ - if (attrib & (_A_HIDDEN | _A_SYSTEM)) - continue; -#endif + } + else + { + + do + { #ifdef __BORLANDC__ - if (!(finfo.name= strdup_root(names_storage, find.ff_name))) - goto error; + attrib= find.ff_attrib; #else - if (!(finfo.name= strdup_root(names_storage, find.name))) - goto error; -#endif - if (MyFlags & MY_WANT_STAT) - { - if (!(finfo.mystat= (MY_STAT*)alloc_root(names_storage, - sizeof(MY_STAT)))) + 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; - - bzero(finfo.mystat, sizeof(MY_STAT)); +#else + if (!(finfo.name= strdup_root(names_storage, find.name))) + goto error; +#endif + if (MyFlags & MY_WANT_STAT) + { + if (!(finfo.mystat= (MY_STAT*)alloc_root(names_storage, + sizeof(MY_STAT)))) + goto error; + + bzero(finfo.mystat, sizeof(MY_STAT)); #ifdef __BORLANDC__ - finfo.mystat->st_size=find.ff_fsize; + finfo.mystat->st_size=find.ff_fsize; #else - finfo.mystat->st_size=find.size; + finfo.mystat->st_size=find.size; #endif - mode=MY_S_IREAD; - if (!(attrib & _A_RDONLY)) - mode|=MY_S_IWRITE; - if (attrib & _A_SUBDIR) - mode|=MY_S_IFDIR; - finfo.mystat->st_mode=mode; + mode= MY_S_IREAD; + if (!(attrib & _A_RDONLY)) + mode|= MY_S_IWRITE; + if (attrib & _A_SUBDIR) + mode|= MY_S_IFDIR; + finfo.mystat->st_mode= mode; #ifdef __BORLANDC__ - finfo.mystat->st_mtime=((uint32) find.ff_ftime); + finfo.mystat->st_mtime= ((uint32) find.ff_ftime); #else - finfo.mystat->st_mtime=((uint32) find.time_write); + finfo.mystat->st_mtime= ((uint32) find.time_write); #endif - } - else - finfo.mystat= NULL; + } + else + finfo.mystat= NULL; - if (push_dynamic(dir_entries_storage, (gptr)&finfo)) - goto error; - + if (push_dynamic(dir_entries_storage, (gptr)&finfo)) + goto error; + } #ifdef __BORLANDC__ - } while (findnext(&find) == 0); + while (findnext(&find) == 0); #else - } while (_findnext(handle,&find) == 0); - - _findclose(handle); + while (_findnext(handle,&find) == 0); + + _findclose(handle); #endif + } result->dir_entry= (FILEINFO *)dir_entries_storage->buffer; result->number_off_files= dir_entries_storage->elements; - + if (!(MyFlags & MY_DONT_SORT)) qsort((void *) result->dir_entry, result->number_off_files, sizeof(FILEINFO), (qsort_cmp) comp_names); + DBUG_PRINT(exit, ("found %d files", result->number_off_files)); DBUG_RETURN(result); error: my_errno=errno; diff --git a/mysys/my_mmap.c b/mysys/my_mmap.c new file mode 100644 index 00000000000..21bfddae46c --- /dev/null +++ b/mysys/my_mmap.c @@ -0,0 +1,91 @@ +/* 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 + 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 HAVE_SYS_MMAN_H + +/* + system msync() only syncs mmap'ed area to fs cache. + fsync() is required to really sync to disc +*/ +int my_msync(int fd, void *addr, size_t len, int flags) +{ + msync(addr, len, flags); + return my_sync(fd, MYF(0)); +} + +#elif defined(__WIN__) + +static SECURITY_ATTRIBUTES mmap_security_attributes= + {sizeof(SECURITY_ATTRIBUTES), 0, TRUE}; + +int my_getpagesize(void) +{ + SYSTEM_INFO si; + GetSystemInfo(&si); + return si.dwPageSize; +} + +void *my_mmap(void *addr, size_t len, int prot, + int flags, int fd, my_off_t offset) +{ + DWORD flProtect=0; + HANDLE hFileMap; + LPVOID ptr; + HANDLE hFile= (HANDLE)_get_osfhandle(fd); + if (hFile == INVALID_HANDLE_VALUE) + return MAP_FAILED; + + flProtect|=SEC_COMMIT; + + hFileMap=CreateFileMapping(hFile, &mmap_security_attributes, + PAGE_READWRITE, 0, (DWORD) len, NULL); + if (hFileMap == 0) + return MAP_FAILED; + + ptr=MapViewOfFile(hFileMap, + flags & PROT_WRITE ? FILE_MAP_WRITE : FILE_MAP_READ, + (DWORD)(offset >> 32), (DWORD)offset, len); + + /* + MSDN explicitly states that it's possible to close File Mapping Object + even when a view is not unmapped - then the object will be held open + implicitly until unmap, as every view stores internally a handler of + a corresponding File Mapping Object + */ + CloseHandle(hFileMap); + + if (ptr) + return ptr; + + return MAP_FAILED; +} + +int my_munmap(void *addr, size_t len) +{ + return UnmapViewOfFile(addr) ? 0 : -1; +} + +int my_msync(int fd, void *addr, size_t len, int flags) +{ + return FlushViewOfFile(addr, len) ? 0 : -1; +} + +#else +#warning "no mmap!" +#endif + diff --git a/mysys/my_new.cc b/mysys/my_new.cc index 14423c3afd5..66f3a14eeb4 100644 --- a/mysys/my_new.cc +++ b/mysys/my_new.cc @@ -45,5 +45,14 @@ void operator delete[] (void *ptr) throw () free(ptr); } +C_MODE_START + +int __cxa_pure_virtual() { + assert("Pure virtual method called." == "Aborted"); + return 0; +} + +C_MODE_END + #endif /* USE_MYSYS_NEW */ diff --git a/mysys/my_once.c b/mysys/my_once.c index a4201810b03..ab5fcc51c0e 100644 --- a/mysys/my_once.c +++ b/mysys/my_once.c @@ -81,7 +81,7 @@ gptr my_once_alloc(unsigned int Size, myf MyFlags) char *my_once_strdup(const char *src,myf myflags) { - uint len=strlen(src)+1; + uint len= (uint) strlen(src)+1; char *dst=my_once_alloc(len, myflags); if (dst) memcpy(dst, src, len); diff --git a/mysys/my_open.c b/mysys/my_open.c index 7cf40b57403..6ed3cb5becf 100644 --- a/mysys/my_open.c +++ b/mysys/my_open.c @@ -56,12 +56,18 @@ File my_open(const char *FileName, int Flags, myf MyFlags) DBUG_RETURN(my_register_filename(-1, FileName, FILE_BY_OPEN, EE_FILENOTFOUND, MyFlags)); } +#ifndef __WIN__ if (Flags & O_SHARE) fd = sopen((my_string) FileName, (Flags & ~O_SHARE) | O_BINARY, SH_DENYNO, MY_S_IREAD | MY_S_IWRITE); else fd = open((my_string) FileName, Flags | O_BINARY, MY_S_IREAD | MY_S_IWRITE); +#else + fd= my_sopen((my_string) FileName, (Flags & ~O_SHARE) | O_BINARY, SH_DENYNO, + MY_S_IREAD | MY_S_IWRITE); +#endif + #elif !defined(NO_OPEN_3) fd = open(FileName, Flags, my_umask); /* Normal unix */ #else @@ -79,7 +85,7 @@ File my_open(const char *FileName, int Flags, myf MyFlags) my_close() fd File sescriptor myf Special Flags - + */ int my_close(File fd, myf MyFlags) @@ -89,7 +95,12 @@ int my_close(File fd, myf MyFlags) DBUG_PRINT("my",("fd: %d MyFlags: %d",fd, MyFlags)); pthread_mutex_lock(&THR_LOCK_open); - if ((err = close(fd))) + do + { + err= close(fd); + } while (err == -1 && errno == EINTR); + + if (err) { DBUG_PRINT("error",("Got error %d on close",err)); my_errno=errno; @@ -162,3 +173,181 @@ File my_register_filename(File fd, const char *FileName, enum file_type FileName, my_errno); return(fd); } + +#ifdef __WIN__ + +extern void __cdecl _dosmaperr(unsigned long); + +/* + Open a file with sharing. Similar to _sopen() from libc, but allows managing + share delete on win32 + + SYNOPSIS + my_sopen() + path fully qualified file name + oflag operation flags + shflag share flag + pmode permission flags + + RETURN VALUE + File descriptor of opened file if success + -1 and sets errno if fails. +*/ + +File my_sopen(const char *path, int oflag, int shflag, int pmode) +{ + int fh; /* handle of opened file */ + int mask; + HANDLE osfh; /* OS handle of opened file */ + DWORD fileaccess; /* OS file access (requested) */ + DWORD fileshare; /* OS file sharing mode */ + DWORD filecreate; /* OS method of opening/creating */ + DWORD fileattrib; /* OS file attribute flags */ + SECURITY_ATTRIBUTES SecurityAttributes; + + SecurityAttributes.nLength= sizeof(SecurityAttributes); + SecurityAttributes.lpSecurityDescriptor= NULL; + SecurityAttributes.bInheritHandle= !(oflag & _O_NOINHERIT); + + /* + * decode the access flags + */ + switch (oflag & (_O_RDONLY | _O_WRONLY | _O_RDWR)) { + case _O_RDONLY: /* read access */ + fileaccess= GENERIC_READ; + break; + case _O_WRONLY: /* write access */ + fileaccess= GENERIC_WRITE; + break; + case _O_RDWR: /* read and write access */ + fileaccess= GENERIC_READ | GENERIC_WRITE; + break; + default: /* error, bad oflag */ + errno= EINVAL; + _doserrno= 0L; /* not an OS error */ + return -1; + } + + /* + * decode sharing flags + */ + switch (shflag) { + case _SH_DENYRW: /* exclusive access except delete */ + fileshare= FILE_SHARE_DELETE; + break; + case _SH_DENYWR: /* share read and delete access */ + fileshare= FILE_SHARE_READ | FILE_SHARE_DELETE; + break; + case _SH_DENYRD: /* share write and delete access */ + fileshare= FILE_SHARE_WRITE | FILE_SHARE_DELETE; + break; + case _SH_DENYNO: /* share read, write and delete access */ + fileshare= FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE; + break; + case _SH_DENYRWD: /* exclusive access */ + fileshare= 0L; + break; + case _SH_DENYWRD: /* share read access */ + fileshare= FILE_SHARE_READ; + break; + case _SH_DENYRDD: /* share write access */ + fileshare= FILE_SHARE_WRITE; + break; + case _SH_DENYDEL: /* share read and write access */ + fileshare= FILE_SHARE_READ | FILE_SHARE_WRITE; + break; + default: /* error, bad shflag */ + errno= EINVAL; + _doserrno= 0L; /* not an OS error */ + return -1; + } + + /* + * decode open/create method flags + */ + switch (oflag & (_O_CREAT | _O_EXCL | _O_TRUNC)) { + case 0: + case _O_EXCL: /* ignore EXCL w/o CREAT */ + filecreate= OPEN_EXISTING; + break; + + case _O_CREAT: + filecreate= OPEN_ALWAYS; + break; + + case _O_CREAT | _O_EXCL: + case _O_CREAT | _O_TRUNC | _O_EXCL: + filecreate= CREATE_NEW; + break; + + case _O_TRUNC: + case _O_TRUNC | _O_EXCL: /* ignore EXCL w/o CREAT */ + filecreate= TRUNCATE_EXISTING; + break; + + case _O_CREAT | _O_TRUNC: + filecreate= CREATE_ALWAYS; + break; + + default: + /* this can't happen ... all cases are covered */ + errno= EINVAL; + _doserrno= 0L; + return -1; + } + + /* + * decode file attribute flags if _O_CREAT was specified + */ + fileattrib= FILE_ATTRIBUTE_NORMAL; /* default */ + if (oflag & _O_CREAT) + { + _umask((mask= _umask(0))); + + if (!((pmode & ~mask) & _S_IWRITE)) + fileattrib= FILE_ATTRIBUTE_READONLY; + } + + /* + * Set temporary file (delete-on-close) attribute if requested. + */ + if (oflag & _O_TEMPORARY) + { + fileattrib|= FILE_FLAG_DELETE_ON_CLOSE; + fileaccess|= DELETE; + } + + /* + * Set temporary file (delay-flush-to-disk) attribute if requested. + */ + if (oflag & _O_SHORT_LIVED) + fileattrib|= FILE_ATTRIBUTE_TEMPORARY; + + /* + * Set sequential or random access attribute if requested. + */ + if (oflag & _O_SEQUENTIAL) + fileattrib|= FILE_FLAG_SEQUENTIAL_SCAN; + else if (oflag & _O_RANDOM) + fileattrib|= FILE_FLAG_RANDOM_ACCESS; + + /* + * try to open/create the file + */ + if ((osfh= CreateFile(path, fileaccess, fileshare, &SecurityAttributes, + filecreate, fileattrib, NULL)) == (HANDLE)0xffffffff) + { + /* + * OS call to open/create file failed! map the error, release + * the lock, and return -1. note that it's not necessary to + * call _free_osfhnd (it hasn't been used yet). + */ + _dosmaperr(GetLastError()); /* map error */ + return -1; /* return error to caller */ + } + + fh= _open_osfhandle((long)osfh, oflag & (_O_APPEND | _O_RDONLY | _O_TEXT)); + + return fh; /* return handle */ +} +#endif /* __WIN__ */ diff --git a/mysys/my_os2cond.c b/mysys/my_os2cond.c index e23afb89e66..bf3e85c26a9 100644 --- a/mysys/my_os2cond.c +++ b/mysys/my_os2cond.c @@ -22,7 +22,7 @@ ** The following is a simple implementation of posix conditions *****************************************************************************/ -#undef SAFE_MUTEX /* Avoid safe_mutex redefinitions */ +#undef SAFE_MUTEX /* Avoid safe_mutex redefinitions */ #include "mysys_priv.h" #if defined(THREAD) && defined(OS2) #include <m_string.h> @@ -31,134 +31,109 @@ int pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *attr) { - APIRET rc = 0; - HEV event; - cond->waiting=0; - /* Warp3 FP29 or Warp4 FP4 or better required */ - rc = DosCreateEventSem( NULL, &cond->semaphore, 0x0800, 0); - if (rc) - return ENOMEM; - + cond->waiting= 0; + /* Warp3 FP29 or Warp4 FP4 or better required */ + if (DosCreateEventSem(NULL, &cond->semaphore, 0x0800, 0)) + return ENOMEM; return 0; } int pthread_cond_destroy(pthread_cond_t *cond) { - APIRET rc; - - do { - rc = DosCloseEventSem(cond->semaphore); - if (rc == 301) DosPostEventSem(cond->semaphore); - } while (rc == 301); - if (rc) - return EINVAL; - - return 0; + for (;;) + { + APIRET rc; + if ((rc= DosCloseEventSem(cond->semaphore)) != 301) + return rc ? EINVAL : 0; + DosPostEventSem(cond->semaphore); + } } int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex) { - APIRET rc; - int rval; - - rval = 0; - cond->waiting++; - - if (mutex) pthread_mutex_unlock(mutex); - - rc = DosWaitEventSem(cond->semaphore,SEM_INDEFINITE_WAIT); - if (rc != 0) - rval = EINVAL; - - if (mutex) pthread_mutex_lock(mutex); - - cond->waiting--; - - return rval; + int rval= 0; + cond->waiting++; + if (mutex) + pthread_mutex_unlock(mutex); + if (DosWaitEventSem(cond->semaphore, SEM_INDEFINITE_WAIT)) + rval= EINVAL; + if (mutex) + pthread_mutex_lock(mutex); + cond->waiting--; + return rval; } int pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, - struct timespec *abstime) + struct timespec *abstime) { struct timeb curtime; int result; long timeout; - APIRET rc; - int rval; - - _ftime(&curtime); - timeout= ((long) (abstime->ts_sec - curtime.time)*1000L + - (long)((abstime->ts_nsec/1000) - curtime.millitm)/1000L); - if (timeout < 0) /* Some safety */ - timeout = 0L; + int rval= 0; - rval = 0; - cond->waiting++; + _ftime(&curtime); + timeout= ((long) (abstime->ts_sec - curtime.time) * 1000L + + (long) ((abstime->ts_nsec / 1000) - curtime.millitm) / 1000L); + if (timeout < 0) /* Some safety */ + timeout= 0L; - if (mutex) pthread_mutex_unlock(mutex); + cond->waiting++; - rc = DosWaitEventSem(cond->semaphore, timeout); - if (rc != 0) - rval = ETIME; + if (mutex) + pthread_mutex_unlock(mutex); + if (DosWaitEventSem(cond->semaphore, timeout) != 0) + rval= ETIMEDOUT; + if (mutex) + pthread_mutex_lock(mutex); - if (mutex) pthread_mutex_lock(mutex); + cond->waiting--; - cond->waiting--; - - return rval; + return rval; } int pthread_cond_signal(pthread_cond_t *cond) { - APIRET rc; - - /* Bring the next thread off the condition queue: */ - rc = DosPostEventSem(cond->semaphore); - return 0; + /* Bring the next thread off the condition queue: */ + DosPostEventSem(cond->semaphore); + return 0; } int pthread_cond_broadcast(pthread_cond_t *cond) { - int i; - APIRET rc; - - /* - * Enter a loop to bring all threads off the - * condition queue: - */ - i = cond->waiting; - while (i--) rc = DosPostEventSem(cond->semaphore); - - return 0 ; + int i; + /* Enter a loop to bring all threads off the condition queue */ + for (i= cond->waiting; i--;) + DosPostEventSem(cond->semaphore); + return 0; } int pthread_attr_init(pthread_attr_t *connect_att) { - connect_att->dwStackSize = 0; - connect_att->dwCreatingFlag = 0; - connect_att->priority = 0; + connect_att->dwStackSize= 0; + connect_att->dwCreatingFlag= 0; + connect_att->priority= 0; return 0; } -int pthread_attr_setstacksize(pthread_attr_t *connect_att,DWORD stack) +int pthread_attr_setstacksize(pthread_attr_t *connect_att, DWORD stack) { - connect_att->dwStackSize=stack; + connect_att->dwStackSize= stack; return 0; } -int pthread_attr_setprio(pthread_attr_t *connect_att,int priority) +int pthread_attr_setprio(pthread_attr_t *connect_att, int priority) { - connect_att->priority=priority; + connect_att->priority= priority; return 0; } int pthread_attr_destroy(pthread_attr_t *connect_att) { - bzero((gptr) connect_att,sizeof(*connect_att)); + bzero((gptr) connect_att, sizeof(*connect_att)); return 0; } @@ -166,22 +141,22 @@ int pthread_attr_destroy(pthread_attr_t *connect_att) ** Fix localtime_r() to be a bit safer ****************************************************************************/ -struct tm *localtime_r(const time_t *timep,struct tm *tmp) +struct tm *localtime_r(const time_t *timep, struct tm *tmp) { - if (*timep == (time_t) -1) /* This will crash win32 */ + if (*timep == (time_t) - 1) /* This will crash win32 */ { - bzero(tmp,sizeof(*tmp)); + bzero(tmp, sizeof(*tmp)); } else { - struct tm *res=localtime(timep); - if (!res) /* Wrong date */ + struct tm *res= localtime(timep); + if (!res) /* Wrong date */ { - bzero(tmp,sizeof(*tmp)); /* Keep things safe */ + bzero(tmp, sizeof(*tmp)); /* Keep things safe */ return 0; } *tmp= *res; } return tmp; } -#endif /* __WIN__ */ +#endif /* __WIN__ */ diff --git a/mysys/my_os2thread.c b/mysys/my_os2thread.c index 0696f480a91..785ff07954d 100644 --- a/mysys/my_os2thread.c +++ b/mysys/my_os2thread.c @@ -51,7 +51,7 @@ void win_pthread_init(void) ** in the new thread. */ -static pthread_handler_decl(pthread_start,param) +pthread_handler_t pthread_start(void *param) { DBUG_ENTER("pthread_start"); pthread_handler func=((struct pthread_map *) param)->func; diff --git a/mysys/my_realloc.c b/mysys/my_realloc.c index c8edb172890..a385bf1e530 100644 --- a/mysys/my_realloc.c +++ b/mysys/my_realloc.c @@ -52,7 +52,7 @@ gptr my_realloc(gptr oldpoint, uint size, myf my_flags) if ((point = (char*)realloc(oldpoint,size)) == NULL) { if (my_flags & MY_FREE_ON_ERROR) - my_free(oldpoint,MyFLAGS); + my_free(oldpoint, my_flags); if (my_flags & MY_HOLD_ON_ERROR) DBUG_RETURN(oldpoint); my_errno=errno; diff --git a/mysys/my_static.c b/mysys/my_static.c index 5f034555156..8448afdc158 100644 --- a/mysys/my_static.c +++ b/mysys/my_static.c @@ -25,8 +25,11 @@ #include "my_alarm.h" #endif +my_bool timed_mutexes= 0; + /* from my_init */ -my_string home_dir=0,my_progname=0; +my_string home_dir=0; +const char *my_progname=0; char NEAR curr_dir[FN_REFLEN]= {0}, NEAR home_dir_buff[FN_REFLEN]= {0}; ulong my_stream_opened=0,my_file_opened=0, my_tmp_file_created=0; @@ -61,6 +64,12 @@ const char *soundex_map= "01230120022455012623010202"; USED_MEM* my_once_root_block=0; /* pointer to first block */ uint my_once_extra=ONCE_ALLOC_INIT; /* Memory to alloc / block */ + /* from my_largepage.c */ +#ifdef HAVE_LARGE_PAGES +my_bool my_use_large_pages= 0; +uint my_large_page_size= 0; +#endif + /* from my_tempnam */ #if !defined(HAVE_TEMPNAM) || defined(HPUX11) int _my_tempnam_used=0; diff --git a/mysys/my_sync.c b/mysys/my_sync.c index 317ca039346..c557324b52c 100644 --- a/mysys/my_sync.c +++ b/mysys/my_sync.c @@ -27,7 +27,14 @@ my_flags Flags (now only MY_WME is supported) NOTE - If file system supports its, only file data is synced, not inode date + If file system supports its, only file data is synced, not inode data. + + MY_IGNORE_BADFD is useful when fd is "volatile" - not protected by a + mutex. In this case by the time of fsync(), fd may be already closed by + another thread, or even reassigned to a different file. With this flag - + MY_IGNORE_BADFD - such a situation will not be considered an error. + (which is correct behaviour, if we know that the other thread synced the + file before closing) RETURN 0 ok @@ -40,21 +47,30 @@ int my_sync(File fd, myf my_flags) DBUG_ENTER("my_sync"); DBUG_PRINT("my",("Fd: %d my_flags: %d", fd, my_flags)); + do + { #if defined(HAVE_FDATASYNC) - res= fdatasync(fd); + res= fdatasync(fd); #elif defined(HAVE_FSYNC) - res=fsync(fd); + res= fsync(fd); #elif defined(__WIN__) res= _commit(fd); #else - res= 0; /* No sync (strange OS) */ + res= 0; /* No sync (strange OS) */ #endif + } while (res == -1 && errno == EINTR); + if (res) { - if (!(my_errno= errno)) - my_errno= -1; /* Unknown error */ - if (my_flags & MY_WME) + int er= errno; + if (!(my_errno= er)) + my_errno= -1; /* Unknown error */ + if ((my_flags & MY_IGNORE_BADFD) && + (er == EBADF || er == EINVAL || er == EROFS)) + res= 0; + else if (my_flags & MY_WME) my_error(EE_SYNC, MYF(ME_BELL+ME_WAITTANG), my_filename(fd), my_errno); } DBUG_RETURN(res); -} /* my_read */ +} /* my_sync */ + diff --git a/mysys/my_tempnam.c b/mysys/my_tempnam.c deleted file mode 100644 index 9f765298fb6..00000000000 --- a/mysys/my_tempnam.c +++ /dev/null @@ -1,173 +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 */ - -/* - This function is only used by some old ISAM code. - When we remove ISAM support from MySQL, we should also delete this file - - One should instead use the functions in mf_tempfile.c -*/ - -#include "mysys_priv.h" -#include <m_string.h> - -/* HPUX 11.0 doesn't allow us to change the environ pointer */ -#ifdef HPUX11 -#undef HAVE_TEMPNAM -#endif - -#include "my_static.h" -#include "mysys_err.h" - -#define TMP_EXT ".tmp" /* Extension of tempfile */ -#if ! defined(P_tmpdir) -#define P_tmpdir "" -#endif - -#ifdef HAVE_TEMPNAM -#if !defined( MSDOS) && !defined(OS2) && !defined(__NETWARE__) -extern char **environ; -#endif -#endif - -/* Make a uniq temp file name by using dir and adding something after - pfx to make name uniq. Name is made by adding a uniq 8 length-string and - TMP_EXT after pfx. - Returns pointer to malloced area for filename. Should be freed by - free(). - The name should be uniq, but it isn't checked if it file allready exists. - Uses tempnam() if function exist on system. - This function fixes that if dir is given it's used. For example - MSDOS tempnam() uses always TMP environment-variable if it exists. -*/ - /* ARGSUSED */ - -my_string my_tempnam(const char *dir, const char *pfx, - myf MyFlags __attribute__((unused))) -{ -#ifdef _MSC_VER - char temp[FN_REFLEN],*end,*res,**old_env,*temp_env[1]; - old_env=environ; - if (dir) - { - end=strend(dir)-1; - if (!dir[0]) - { /* Change empty string to current dir */ - temp[0]= FN_CURLIB; - temp[1]= 0; - dir=temp; - } - else if (*end == FN_DEVCHAR) - { /* Get current dir for drive */ - _fullpath(temp,dir,FN_REFLEN); - dir=temp; - } - else if (*end == FN_LIBCHAR && dir < end && end[-1] != FN_DEVCHAR) - { - strmake(temp,dir,(uint) (end-dir)); /* Copy and remove last '\' */ - dir=temp; - } - environ=temp_env; /* Force use of dir (dir not checked) */ - temp_env[0]=0; - } - res=tempnam((char*) dir,(my_string) pfx); - environ=old_env; - return res; -#else -#ifdef __ZTC__ - if (!dir) - { /* If empty test first if TMP can be used */ - dir=getenv("TMP"); - } - return tempnam((char*) dir,(my_string) pfx); /* Use stand. dir with prefix */ -#else -#ifdef HAVE_TEMPNAM - char temp[2],*res,**old_env,*temp_env[1]; - - if (dir && !dir[0]) - { /* Change empty string to current dir */ - temp[0]= FN_CURLIB; - temp[1]= 0; - dir=temp; - } -#ifdef OS2 - /* changing environ variable doesn't work with VACPP */ - char buffer[256], *end; - buffer[sizeof[buffer)-1]= 0; - end= strxnmov(buffer, sizeof(buffer)-1, (char*) "TMP=", dir, NullS); - /* remove ending backslash */ - if (end[-1] == '\\') - end[-1]= 0; - putenv(buffer); -#elif !defined(__NETWARE__) - old_env=(char**)environ; - if (dir) - { /* Don't use TMPDIR if dir is given */ - /* - The following strange cast is required because the IBM compiler on AIX - doesn't allow us to cast the value of environ. - The cast of environ is needed as some systems doesn't allow us to - update environ with a char ** pointer. (const mismatch) - */ - (*(char***) &environ)=(char**) temp_env; - temp_env[0]=0; - } -#endif - res=tempnam((char*) dir,(my_string) pfx); /* Use stand. dir with prefix */ -#if !defined(OS2) && !defined(__NETWARE__) - (*(char***) &environ)=(char**) old_env; -#endif - if (!res) - DBUG_PRINT("error",("Got error: %d from tempnam",errno)); - return res; -#else - register long uniq; - register int length; - my_string pos,end_pos; - DBUG_ENTER("my_tempnam"); - /* Make a uniq nummber */ - pthread_mutex_lock(&THR_LOCK_open); - uniq= ((long) getpid() << 20) + (long) _my_tempnam_used++ ; - pthread_mutex_unlock(&THR_LOCK_open); - if (!dir && !(dir=getenv("TMPDIR"))) /* Use this if possibly */ - dir=P_tmpdir; /* Use system default */ - length=strlen(dir)+strlen(pfx)+1; - - DBUG_PRINT("test",("mallocing %d byte",length+8+sizeof(TMP_EXT)+1)); - if (!(pos=(char*) malloc(length+8+sizeof(TMP_EXT)+1))) - { - if (MyFlags & MY_FAE+MY_WME) - my_error(EE_OUTOFMEMORY, MYF(ME_BELL+ME_WAITTANG), - length+8+sizeof(TMP_EXT)+1); - DBUG_RETURN(NullS); - } - end_pos=strmov(pos,dir); - if (end_pos != pos && end_pos[-1] != FN_LIBCHAR) - *end_pos++=FN_LIBCHAR; - end_pos=strmov(end_pos,pfx); - - for (length=0 ; length < 8 && uniq ; length++) - { - *end_pos++= _dig_vec_upper[(int) (uniq & 31)]; - uniq >>= 5; - } - VOID(strmov(end_pos,TMP_EXT)); - DBUG_PRINT("exit",("tempnam: '%s'",pos)); - DBUG_RETURN(pos); -#endif /* HAVE_TEMPNAM */ -#endif /* __ZTC__ */ -#endif /* _MSC_VER */ -} /* my_tempnam */ diff --git a/mysys/my_thr_init.c b/mysys/my_thr_init.c index 878e1f6bfc6..4d23d01cd82 100644 --- a/mysys/my_thr_init.c +++ b/mysys/my_thr_init.c @@ -67,12 +67,11 @@ my_bool my_thread_global_init(void) /* 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. + In this case the thread may steal the mutex from some other thread + that is waiting for the same mutex. This will save us some + context switches but may cause a thread to 'starve forever' while + waiting for the mutex (not likely if the code within the mutex is + short). */ pthread_mutexattr_init(&my_fast_mutexattr); pthread_mutexattr_settype(&my_fast_mutexattr, diff --git a/mysys/my_winthread.c b/mysys/my_winthread.c index eebc07df180..8aaf3b1e31c 100644 --- a/mysys/my_winthread.c +++ b/mysys/my_winthread.c @@ -51,7 +51,7 @@ void win_pthread_init(void) ** in the new thread. */ -static pthread_handler_decl(pthread_start,param) +pthread_handler_t pthread_start(void *param) { pthread_handler func=((struct pthread_map *) param)->func; void *func_param=((struct pthread_map *) param)->param; diff --git a/mysys/ptr_cmp.c b/mysys/ptr_cmp.c index 5fc7ccab4fa..57778574bb6 100644 --- a/mysys/ptr_cmp.c +++ b/mysys/ptr_cmp.c @@ -21,6 +21,7 @@ */ #include "mysys_priv.h" +#include <myisampack.h> static int ptr_compare(uint *compare_length, uchar **a, uchar **b); static int ptr_compare_0(uint *compare_length, uchar **a, uchar **b); @@ -152,3 +153,41 @@ static int ptr_compare_3(uint *compare_length,uchar **a, uchar **b) } return (0); } + +void my_store_ptr(byte *buff, uint pack_length, my_off_t pos) +{ + switch (pack_length) { +#if SIZEOF_OFF_T > 4 + case 8: mi_int8store(buff,pos); break; + case 7: mi_int7store(buff,pos); break; + case 6: mi_int6store(buff,pos); break; + case 5: mi_int5store(buff,pos); break; +#endif + case 4: mi_int4store(buff,pos); break; + case 3: mi_int3store(buff,pos); break; + case 2: mi_int2store(buff,pos); break; + case 1: buff[0]= (uchar) pos; break; + default: DBUG_ASSERT(0); + } + return; +} + +my_off_t my_get_ptr(byte *ptr, uint pack_length) +{ + my_off_t pos; + switch (pack_length) { +#if SIZEOF_OFF_T > 4 + case 8: pos= (my_off_t) mi_uint8korr(ptr); break; + case 7: pos= (my_off_t) mi_uint7korr(ptr); break; + case 6: pos= (my_off_t) mi_uint6korr(ptr); break; + case 5: pos= (my_off_t) mi_uint5korr(ptr); break; +#endif + case 4: pos= (my_off_t) mi_uint4korr(ptr); break; + case 3: pos= (my_off_t) mi_uint3korr(ptr); break; + case 2: pos= (my_off_t) mi_uint2korr(ptr); break; + case 1: pos= (my_off_t) mi_uint2korr(ptr); break; + default: DBUG_ASSERT(0); + } + return pos; +} + diff --git a/mysys/raid.cc b/mysys/raid.cc index 62587c438ca..29819a878c4 100644 --- a/mysys/raid.cc +++ b/mysys/raid.cc @@ -185,7 +185,7 @@ extern "C" { uint my_raid_write(File fd,const byte *Buffer, uint Count, myf MyFlags) { DBUG_ENTER("my_raid_write"); - DBUG_PRINT("enter",("Fd: %d Buffer: %lx Count: %u MyFlags: %d", + DBUG_PRINT("enter",("Fd: %d Buffer: 0x%lx Count: %u MyFlags: %d", fd, Buffer, Count, MyFlags)); if (is_raid(fd)) { @@ -198,7 +198,7 @@ extern "C" { uint my_raid_read(File fd, byte *Buffer, uint Count, myf MyFlags) { DBUG_ENTER("my_raid_read"); - DBUG_PRINT("enter",("Fd: %d Buffer: %lx Count: %u MyFlags: %d", + DBUG_PRINT("enter",("Fd: %d Buffer: 0x%lx Count: %u MyFlags: %d", fd, Buffer, Count, MyFlags)); if (is_raid(fd)) { @@ -212,8 +212,9 @@ extern "C" { myf MyFlags) { DBUG_ENTER("my_raid_pread"); - DBUG_PRINT("enter",("Fd: %d Buffer: %lx Count: %u offset: %u MyFlags: %d", - Filedes, Buffer, Count, offset, MyFlags)); + DBUG_PRINT("enter", + ("Fd: %d Buffer: 0x%lx Count: %u offset: %u MyFlags: %d", + Filedes, Buffer, Count, offset, MyFlags)); if (is_raid(Filedes)) { assert(offset != MY_FILEPOS_ERROR); @@ -231,8 +232,9 @@ extern "C" { my_off_t offset, myf MyFlags) { DBUG_ENTER("my_raid_pwrite"); - DBUG_PRINT("enter",("Fd: %d Buffer: %lx Count: %u offset: %u MyFlags: %d", - Filedes, Buffer, Count, offset, MyFlags)); + DBUG_PRINT("enter", + ("Fd: %d Buffer: 0x %lx Count: %u offset: %u MyFlags: %d", + Filedes, Buffer, Count, offset, MyFlags)); if (is_raid(Filedes)) { assert(offset != MY_FILEPOS_ERROR); diff --git a/mysys/string.c b/mysys/string.c index ea747bc9847..dfd42d137dd 100644 --- a/mysys/string.c +++ b/mysys/string.c @@ -91,7 +91,7 @@ my_bool dynstr_realloc(DYNAMIC_STRING *str, ulong additional_size) my_bool dynstr_append(DYNAMIC_STRING *str, const char *append) { - return dynstr_append_mem(str,append,strlen(append)); + return dynstr_append_mem(str,append,(uint) strlen(append)); } diff --git a/mysys/thr_alarm.c b/mysys/thr_alarm.c index e5b77de5e38..41914080a9d 100644 --- a/mysys/thr_alarm.c +++ b/mysys/thr_alarm.c @@ -401,7 +401,7 @@ void end_thr_alarm(my_bool free_structures) { DBUG_ENTER("end_thr_alarm"); if (alarm_aborted != 1) /* If memory not freed */ - { + { pthread_mutex_lock(&LOCK_alarm); DBUG_PRINT("info",("Resheduling %d waiting alarms",alarm_queue.elements)); alarm_aborted= -1; /* mark aborted */ @@ -415,13 +415,10 @@ void end_thr_alarm(my_bool free_structures) if (free_structures) { struct timespec abstime; - /* - The following test is just for safety, the caller should not - depend on this - */ + DBUG_ASSERT(!alarm_queue.elements); - /* Wait until alarm thread dies */ + /* Wait until alarm thread dies */ set_timespec(abstime, 10); /* Wait up to 10 seconds */ while (alarm_thread_running) { @@ -429,16 +426,13 @@ void end_thr_alarm(my_bool free_structures) if (error == ETIME || error == ETIMEDOUT) break; /* Don't wait forever */ } - if (!alarm_queue.elements) + delete_queue(&alarm_queue); + alarm_aborted= 1; + pthread_mutex_unlock(&LOCK_alarm); + if (!alarm_thread_running) /* Safety */ { - delete_queue(&alarm_queue); - alarm_aborted= 1; - pthread_mutex_unlock(&LOCK_alarm); - if (!alarm_thread_running) /* Safety */ - { - pthread_mutex_destroy(&LOCK_alarm); - pthread_cond_destroy(&COND_alarm); - } + pthread_mutex_destroy(&LOCK_alarm); + pthread_cond_destroy(&COND_alarm); } } else diff --git a/mysys/thr_lock.c b/mysys/thr_lock.c index d6443539216..f5a8b618949 100644 --- a/mysys/thr_lock.c +++ b/mysys/thr_lock.c @@ -84,6 +84,7 @@ multiple read locks. my_bool thr_lock_inited=0; ulong locks_immediate = 0L, locks_waited = 0L; +ulong table_lock_wait_timeout; enum thr_lock_type thr_upgraded_concurrent_insert_lock = TL_WRITE; /* The following constants are only for debug output */ @@ -109,25 +110,32 @@ my_bool init_thr_lock() return 0; } +static inline my_bool +thr_lock_owner_equal(THR_LOCK_OWNER *rhs, THR_LOCK_OWNER *lhs) +{ + return rhs == lhs; +} + + #ifdef EXTRA_DEBUG #define MAX_FOUND_ERRORS 10 /* Report 10 first errors */ static uint found_errors=0; static int check_lock(struct st_lock_list *list, const char* lock_type, - const char *where, my_bool same_thread, bool no_cond) + const char *where, my_bool same_owner, bool no_cond) { THR_LOCK_DATA *data,**prev; uint count=0; - pthread_t first_thread; - LINT_INIT(first_thread); + THR_LOCK_OWNER *first_owner; + LINT_INIT(first_owner); prev= &list->data; if (list->data) { enum thr_lock_type last_lock_type=list->data->type; - if (same_thread && list->data) - first_thread=list->data->thread; + if (same_owner && list->data) + first_owner= list->data->owner; for (data=list->data; data && count++ < MAX_LOCKS ; data=data->next) { if (data->type != last_lock_type) @@ -139,7 +147,8 @@ static int check_lock(struct st_lock_list *list, const char* lock_type, count, lock_type, where); return 1; } - if (same_thread && ! pthread_equal(data->thread,first_thread) && + if (same_owner && + !thr_lock_owner_equal(data->owner, first_owner) && last_lock_type != TL_WRITE_ALLOW_WRITE) { fprintf(stderr, @@ -255,8 +264,8 @@ static void check_locks(THR_LOCK *lock, const char *where, } if (lock->read.data) { - if (!pthread_equal(lock->write.data->thread, - lock->read.data->thread) && + if (!thr_lock_owner_equal(lock->write.data->owner, + lock->read.data->owner) && ((lock->write.data->type > TL_WRITE_DELAYED && lock->write.data->type != TL_WRITE_ONLY) || ((lock->write.data->type == TL_WRITE_CONCURRENT_INSERT || @@ -330,24 +339,32 @@ void thr_lock_delete(THR_LOCK *lock) DBUG_VOID_RETURN; } + +void thr_lock_info_init(THR_LOCK_INFO *info) +{ + info->thread= pthread_self(); + info->thread_id= my_thread_id(); /* for debugging */ + info->n_cursors= 0; +} + /* Initialize a lock instance */ void thr_lock_data_init(THR_LOCK *lock,THR_LOCK_DATA *data, void *param) { data->lock=lock; data->type=TL_UNLOCK; - data->thread=pthread_self(); - data->thread_id=my_thread_id(); /* for debugging */ + data->owner= 0; /* no owner yet */ data->status_param=param; data->cond=0; } -static inline my_bool have_old_read_lock(THR_LOCK_DATA *data,pthread_t thread) +static inline my_bool +have_old_read_lock(THR_LOCK_DATA *data, THR_LOCK_OWNER *owner) { for ( ; data ; data=data->next) { - if ((pthread_equal(data->thread,thread))) + if (thr_lock_owner_equal(data->owner, owner)) return 1; /* Already locked by thread */ } return 0; @@ -365,12 +382,15 @@ static inline my_bool have_specific_lock(THR_LOCK_DATA *data, } -static my_bool wait_for_lock(struct st_lock_list *wait, THR_LOCK_DATA *data, - my_bool in_wait_list) +static enum enum_thr_lock_result +wait_for_lock(struct st_lock_list *wait, THR_LOCK_DATA *data, + my_bool in_wait_list) { - pthread_cond_t *cond=get_cond(); - struct st_my_thread_var *thread_var=my_thread_var; - int result; + struct st_my_thread_var *thread_var= my_thread_var; + pthread_cond_t *cond= &thread_var->suspend; + struct timespec wait_timeout; + enum enum_thr_lock_result result= THR_LOCK_ABORTED; + my_bool can_deadlock= test(data->owner->info->n_cursors); if (!in_wait_list) { @@ -382,34 +402,56 @@ static my_bool wait_for_lock(struct st_lock_list *wait, THR_LOCK_DATA *data, /* Set up control struct to allow others to abort locks */ thread_var->current_mutex= &data->lock->mutex; thread_var->current_cond= cond; + data->cond= cond; - data->cond=cond; + if (can_deadlock) + set_timespec(wait_timeout, table_lock_wait_timeout); while (!thread_var->abort || in_wait_list) { - pthread_cond_wait(cond,&data->lock->mutex); - if (data->cond != cond) + int rc= (can_deadlock ? + pthread_cond_timedwait(cond, &data->lock->mutex, + &wait_timeout) : + pthread_cond_wait(cond, &data->lock->mutex)); + /* + We must break the wait if one of the following occurs: + - the connection has been aborted (!thread_var->abort), but + this is not a delayed insert thread (in_wait_list). For a delayed + insert thread the proper action at shutdown is, apparently, to + acquire the lock and complete the insert. + - the lock has been granted (data->cond is set to NULL by the granter), + or the waiting has been aborted (additionally data->type is set to + TL_UNLOCK). + - the wait has timed out (rc == ETIMEDOUT) + Order of checks below is important to not report about timeout + if the predicate is true. + */ + if (data->cond == 0) + break; + if (rc == ETIMEDOUT || rc == ETIME) + { + result= THR_LOCK_WAIT_TIMEOUT; break; + } } if (data->cond || data->type == TL_UNLOCK) { - if (data->cond) /* aborted */ + if (data->cond) /* aborted or timed out */ { if (((*data->prev)=data->next)) /* remove from wait-list */ data->next->prev= data->prev; else wait->last=data->prev; + data->type= TL_UNLOCK; /* No lock */ } - data->type=TL_UNLOCK; /* No lock */ - result=1; /* Didn't get lock */ check_locks(data->lock,"failed wait_for_lock",0); } else { - result=0; + result= THR_LOCK_SUCCESS; statistic_increment(locks_waited, &THR_LOCK_lock); if (data->lock->get_status) - (*data->lock->get_status)(data->status_param); + (*data->lock->get_status)(data->status_param, 0); check_locks(data->lock,"got wait_for_lock",0); } pthread_mutex_unlock(&data->lock->mutex); @@ -423,20 +465,24 @@ static my_bool wait_for_lock(struct st_lock_list *wait, THR_LOCK_DATA *data, } -int thr_lock(THR_LOCK_DATA *data,enum thr_lock_type lock_type) +enum enum_thr_lock_result +thr_lock(THR_LOCK_DATA *data, THR_LOCK_OWNER *owner, + enum thr_lock_type lock_type) { THR_LOCK *lock=data->lock; - int result=0; + enum enum_thr_lock_result result= THR_LOCK_SUCCESS; + struct st_lock_list *wait_queue; + THR_LOCK_DATA *lock_owner; DBUG_ENTER("thr_lock"); data->next=0; data->cond=0; /* safety */ data->type=lock_type; - data->thread=pthread_self(); /* Must be reset ! */ - data->thread_id=my_thread_id(); /* Must be reset ! */ + data->owner= owner; /* Must be reset ! */ VOID(pthread_mutex_lock(&lock->mutex)); DBUG_PRINT("lock",("data: 0x%lx thread: %ld lock: 0x%lx type: %d", - data,data->thread_id,lock,(int) lock_type)); + data, data->owner->info->thread_id, + lock, (int) lock_type)); check_locks(lock,(uint) lock_type <= (uint) TL_READ_NO_INSERT ? "enter read_lock" : "enter write_lock",0); if ((int) lock_type <= (int) TL_READ_NO_INSERT) @@ -454,8 +500,8 @@ int thr_lock(THR_LOCK_DATA *data,enum thr_lock_type lock_type) */ DBUG_PRINT("lock",("write locked by thread: %ld", - lock->write.data->thread_id)); - if (pthread_equal(data->thread,lock->write.data->thread) || + lock->write.data->owner->info->thread_id)); + if (thr_lock_owner_equal(data->owner, lock->write.data->owner) || (lock->write.data->type <= TL_WRITE_DELAYED && (((int) lock_type <= (int) TL_READ_HIGH_PRIORITY) || (lock->write.data->type != TL_WRITE_CONCURRENT_INSERT && @@ -464,11 +510,11 @@ int thr_lock(THR_LOCK_DATA *data,enum thr_lock_type lock_type) (*lock->read.last)=data; /* Add to running FIFO */ data->prev=lock->read.last; lock->read.last= &data->next; - if ((int) lock_type == (int) TL_READ_NO_INSERT) + if (lock_type == TL_READ_NO_INSERT) lock->read_no_write_count++; check_locks(lock,"read lock with old write lock",0); if (lock->get_status) - (*lock->get_status)(data->status_param); + (*lock->get_status)(data->status_param, 0); statistic_increment(locks_immediate,&THR_LOCK_lock); goto end; } @@ -476,28 +522,32 @@ int thr_lock(THR_LOCK_DATA *data,enum thr_lock_type lock_type) { /* We are not allowed to get a READ lock in this case */ data->type=TL_UNLOCK; - result=1; /* Can't wait for this one */ + result= THR_LOCK_ABORTED; /* Can't wait for this one */ goto end; } } else if (!lock->write_wait.data || lock->write_wait.data->type <= TL_WRITE_LOW_PRIORITY || lock_type == TL_READ_HIGH_PRIORITY || - have_old_read_lock(lock->read.data,data->thread)) + have_old_read_lock(lock->read.data, data->owner)) { /* No important write-locks */ (*lock->read.last)=data; /* Add to running FIFO */ data->prev=lock->read.last; lock->read.last= &data->next; if (lock->get_status) - (*lock->get_status)(data->status_param); - if ((int) lock_type == (int) TL_READ_NO_INSERT) + (*lock->get_status)(data->status_param, 0); + if (lock_type == TL_READ_NO_INSERT) lock->read_no_write_count++; check_locks(lock,"read lock with no write locks",0); statistic_increment(locks_immediate,&THR_LOCK_lock); goto end; } - /* Can't get lock yet; Wait for it */ - DBUG_RETURN(wait_for_lock(&lock->read_wait,data,0)); + /* + We're here if there is an active write lock or no write + lock but a high priority write waiting in the write_wait queue. + In the latter case we should yield the lock to the writer. + */ + wait_queue= &lock->read_wait; } else /* Request for WRITE lock */ { @@ -506,7 +556,7 @@ int thr_lock(THR_LOCK_DATA *data,enum thr_lock_type lock_type) if (lock->write.data && lock->write.data->type == TL_WRITE_ONLY) { data->type=TL_UNLOCK; - result=1; /* Can't wait for this one */ + result= THR_LOCK_ABORTED; /* Can't wait for this one */ goto end; } /* @@ -523,8 +573,10 @@ int thr_lock(THR_LOCK_DATA *data,enum thr_lock_type lock_type) data->prev=lock->write_wait.last; lock->write_wait.last= &data->next; data->cond=get_cond(); - if (lock->get_status) - (*lock->get_status)(data->status_param); + /* + We don't have to do get_status here as we will do it when we change + the delayed lock to a real write lock + */ statistic_increment(locks_immediate,&THR_LOCK_lock); goto end; } @@ -538,7 +590,7 @@ int thr_lock(THR_LOCK_DATA *data,enum thr_lock_type lock_type) { /* We are not allowed to get a lock in this case */ data->type=TL_UNLOCK; - result=1; /* Can't wait for this one */ + result= THR_LOCK_ABORTED; /* Can't wait for this one */ goto end; } @@ -547,7 +599,7 @@ int thr_lock(THR_LOCK_DATA *data,enum thr_lock_type lock_type) TL_WRITE_ALLOW_WRITE, TL_WRITE_ALLOW_READ or TL_WRITE_DELAYED in the same thread, but this will never happen within MySQL. */ - if (pthread_equal(data->thread,lock->write.data->thread) || + if (thr_lock_owner_equal(data->owner, lock->write.data->owner) || (lock_type == TL_WRITE_ALLOW_WRITE && !lock->write_wait.data && lock->write.data->type == TL_WRITE_ALLOW_WRITE)) @@ -565,12 +617,12 @@ int thr_lock(THR_LOCK_DATA *data,enum thr_lock_type lock_type) lock->write.last= &data->next; check_locks(lock,"second write lock",0); if (data->lock->get_status) - (*data->lock->get_status)(data->status_param); + (*data->lock->get_status)(data->status_param, 0); statistic_increment(locks_immediate,&THR_LOCK_lock); goto end; } DBUG_PRINT("lock",("write locked by thread: %ld", - lock->write.data->thread_id)); + lock->write.data->owner->info->thread_id)); } else { @@ -578,9 +630,16 @@ int thr_lock(THR_LOCK_DATA *data,enum thr_lock_type lock_type) (ulong) lock->write_wait.data)); if (!lock->write_wait.data) { /* no scheduled write locks */ - if (lock_type == TL_WRITE_CONCURRENT_INSERT && - (*lock->check_status)(data->status_param)) - data->type=lock_type= thr_upgraded_concurrent_insert_lock; + my_bool concurrent_insert= 0; + if (lock_type == TL_WRITE_CONCURRENT_INSERT) + { + concurrent_insert= 1; + if ((*lock->check_status)(data->status_param)) + { + concurrent_insert= 0; + data->type=lock_type= thr_upgraded_concurrent_insert_lock; + } + } if (!lock->read.data || (lock_type <= TL_WRITE_DELAYED && @@ -592,17 +651,31 @@ int thr_lock(THR_LOCK_DATA *data,enum thr_lock_type lock_type) data->prev=lock->write.last; lock->write.last= &data->next; if (data->lock->get_status) - (*data->lock->get_status)(data->status_param); + (*data->lock->get_status)(data->status_param, concurrent_insert); check_locks(lock,"only write lock",0); statistic_increment(locks_immediate,&THR_LOCK_lock); goto end; } } DBUG_PRINT("lock",("write locked by thread: %ld, type: %ld", - lock->read.data->thread_id,data->type)); + lock->read.data->owner->info->thread_id, data->type)); } - DBUG_RETURN(wait_for_lock(&lock->write_wait,data,0)); + wait_queue= &lock->write_wait; } + /* + Try to detect a trivial deadlock when using cursors: attempt to + lock a table that is already locked by an open cursor within the + same connection. lock_owner can be zero if we succumbed to a high + priority writer in the write_wait queue. + */ + lock_owner= lock->read.data ? lock->read.data : lock->write.data; + if (lock_owner && lock_owner->owner->info == owner->info) + { + result= THR_LOCK_DEADLOCK; + goto end; + } + /* Can't get lock yet; Wait for it */ + DBUG_RETURN(wait_for_lock(wait_queue, data, 0)); end: pthread_mutex_unlock(&lock->mutex); DBUG_RETURN(result); @@ -647,7 +720,7 @@ static inline void free_all_read_locks(THR_LOCK *lock, lock->read_no_write_count++; } DBUG_PRINT("lock",("giving read lock to thread: %ld", - data->thread_id)); + data->owner->info->thread_id)); data->cond=0; /* Mark thread free */ VOID(pthread_cond_signal(cond)); } while ((data=data->next)); @@ -665,7 +738,7 @@ void thr_unlock(THR_LOCK_DATA *data) enum thr_lock_type lock_type=data->type; DBUG_ENTER("thr_unlock"); DBUG_PRINT("lock",("data: 0x%lx thread: %ld lock: 0x%lx", - data,data->thread_id,lock)); + data, data->owner->info->thread_id, lock)); pthread_mutex_lock(&lock->mutex); check_locks(lock,"start of release lock",0); @@ -675,8 +748,10 @@ void thr_unlock(THR_LOCK_DATA *data) lock->read.last=data->prev; else if (lock_type == TL_WRITE_DELAYED && data->cond) { - /* This only happens in extreme circumstances when a - write delayed lock that is waiting for a lock */ + /* + This only happens in extreme circumstances when a + write delayed lock that is waiting for a lock + */ lock->write_wait.last=data->prev; /* Put it on wait queue */ } else @@ -723,7 +798,7 @@ void thr_unlock(THR_LOCK_DATA *data) (*lock->check_status)(data->status_param)) data->type=TL_WRITE; /* Upgrade lock */ DBUG_PRINT("lock",("giving write lock of type %d to thread: %ld", - data->type,data->thread_id)); + data->type, data->owner->info->thread_id)); { pthread_cond_t *cond=data->cond; data->cond=0; /* Mark thread free */ @@ -831,7 +906,8 @@ static void sort_locks(THR_LOCK_DATA **data,uint count) } -int thr_multi_lock(THR_LOCK_DATA **data,uint count) +enum enum_thr_lock_result +thr_multi_lock(THR_LOCK_DATA **data, uint count, THR_LOCK_OWNER *owner) { THR_LOCK_DATA **pos,**end; DBUG_ENTER("thr_multi_lock"); @@ -841,10 +917,11 @@ int thr_multi_lock(THR_LOCK_DATA **data,uint count) /* lock everything */ for (pos=data,end=data+count; pos < end ; pos++) { - if (thr_lock(*pos,(*pos)->type)) + enum enum_thr_lock_result result= thr_lock(*pos, owner, (*pos)->type); + if (result != THR_LOCK_SUCCESS) { /* Aborted */ thr_multi_unlock(data,(uint) (pos-data)); - DBUG_RETURN(1); + DBUG_RETURN(result); } #ifdef MAIN printf("Thread: %s Got lock: 0x%lx type: %d\n",my_thread_name(), @@ -898,7 +975,7 @@ int thr_multi_lock(THR_LOCK_DATA **data,uint count) } while (pos != data); } #endif - DBUG_RETURN(0); + DBUG_RETURN(THR_LOCK_SUCCESS); } /* free all locks */ @@ -921,7 +998,7 @@ void thr_multi_unlock(THR_LOCK_DATA **data,uint count) else { DBUG_PRINT("lock",("Free lock: data: 0x%lx thread: %ld lock: 0x%lx", - *pos,(*pos)->thread_id,(*pos)->lock)); + *pos, (*pos)->owner->info->thread_id, (*pos)->lock)); } } DBUG_VOID_RETURN; @@ -941,6 +1018,7 @@ void thr_abort_locks(THR_LOCK *lock) for (data=lock->read_wait.data; data ; data=data->next) { data->type=TL_UNLOCK; /* Mark killed */ + /* It's safe to signal the cond first: we're still holding the mutex. */ pthread_cond_signal(data->cond); data->cond=0; /* Removed from list */ } @@ -975,10 +1053,11 @@ my_bool thr_abort_locks_for_thread(THR_LOCK *lock, pthread_t thread) pthread_mutex_lock(&lock->mutex); for (data= lock->read_wait.data; data ; data= data->next) { - if (pthread_equal(thread, data->thread)) + if (pthread_equal(thread, data->owner->info->thread)) { DBUG_PRINT("info",("Aborting read-wait lock")); data->type= TL_UNLOCK; /* Mark killed */ + /* It's safe to signal the cond first: we're still holding the mutex. */ found= TRUE; pthread_cond_signal(data->cond); data->cond= 0; /* Removed from list */ @@ -991,7 +1070,7 @@ my_bool thr_abort_locks_for_thread(THR_LOCK *lock, pthread_t thread) } for (data= lock->write_wait.data; data ; data= data->next) { - if (pthread_equal(thread, data->thread)) + if (pthread_equal(thread, data->owner->info->thread)) { DBUG_PRINT("info",("Aborting write-wait lock")); data->type= TL_UNLOCK; @@ -1034,7 +1113,7 @@ my_bool thr_upgrade_write_delay_lock(THR_LOCK_DATA *data) if (!lock->read.data) /* No read locks */ { /* We have the lock */ if (data->lock->get_status) - (*data->lock->get_status)(data->status_param); + (*data->lock->get_status)(data->status_param, 0); pthread_mutex_unlock(&lock->mutex); DBUG_RETURN(0); } @@ -1109,7 +1188,8 @@ 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("0x%lx (%lu:%d); ",(ulong) data,data->thread_id,(int) data->type); + printf("0x%lx (%lu:%d); ", (ulong) data, data->owner->info->thread_id, + (int) data->type); if (data->prev != prev) printf("\nWarning: prev didn't point at previous lock\n"); prev= &data->next; @@ -1223,7 +1303,8 @@ static ulong sum=0; /* The following functions is for WRITE_CONCURRENT_INSERT */ -static void test_get_status(void* param __attribute__((unused))) +static void test_get_status(void* param __attribute__((unused)), + int concurrent_insert __attribute__((unused))) { } @@ -1242,11 +1323,16 @@ static void *test_thread(void *arg) { int i,j,param=*((int*) arg); THR_LOCK_DATA data[MAX_LOCK_COUNT]; + THR_LOCK_OWNER owner; + THR_LOCK_INFO lock_info; THR_LOCK_DATA *multi_locks[MAX_LOCK_COUNT]; my_thread_init(); printf("Thread %s (%d) started\n",my_thread_name(),param); fflush(stdout); + + thr_lock_info_init(&lock_info); + thr_lock_owner_init(&owner, &lock_info); for (i=0; i < lock_counts[param] ; i++) thr_lock_data_init(locks+tests[param][i].lock_nr,data+i,NULL); for (j=1 ; j < 10 ; j++) /* try locking 10 times */ @@ -1256,7 +1342,7 @@ static void *test_thread(void *arg) multi_locks[i]= &data[i]; data[i].type= tests[param][i].lock_type; } - thr_multi_lock(multi_locks,lock_counts[param]); + thr_multi_lock(multi_locks, lock_counts[param], &owner); pthread_mutex_lock(&LOCK_thread_count); { int tmp=rand() & 7; /* Do something from 0-2 sec */ diff --git a/mysys/thr_mutex.c b/mysys/thr_mutex.c index 2facb4e18cf..1791bb6e4c4 100644 --- a/mysys/thr_mutex.c +++ b/mysys/thr_mutex.c @@ -103,7 +103,7 @@ int safe_mutex_lock(safe_mutex_t *mp,const char *file, uint line) fflush(stderr); abort(); } - + pthread_mutex_lock(&mp->global); if (mp->count > 0 && pthread_equal(pthread_self(),mp->thread)) { @@ -121,6 +121,7 @@ int safe_mutex_lock(safe_mutex_t *mp,const char *file, uint line) fflush(stderr); abort(); } + mp->thread= pthread_self(); if (mp->count++) { fprintf(stderr,"safe_mutex: Error in thread libray: Got mutex at %s, \ @@ -128,7 +129,6 @@ line %d more than 1 time\n", file,line); fflush(stderr); abort(); } - mp->thread=pthread_self(); mp->file= file; mp->line=line; pthread_mutex_unlock(&mp->global); @@ -154,6 +154,7 @@ int safe_mutex_unlock(safe_mutex_t *mp,const char *file, uint line) fflush(stderr); abort(); } + mp->thread= 0; mp->count--; #ifdef __WIN__ pthread_mutex_unlock(&mp->mutex); @@ -207,6 +208,7 @@ int safe_cond_wait(pthread_cond_t *cond, safe_mutex_t *mp, const char *file, fflush(stderr); abort(); } + mp->thread=pthread_self(); if (mp->count++) { fprintf(stderr, @@ -215,7 +217,6 @@ int safe_cond_wait(pthread_cond_t *cond, safe_mutex_t *mp, const char *file, fflush(stderr); abort(); } - mp->thread=pthread_self(); mp->file= file; mp->line=line; pthread_mutex_unlock(&mp->global); @@ -239,12 +240,13 @@ int safe_cond_timedwait(pthread_cond_t *cond, safe_mutex_t *mp, pthread_mutex_unlock(&mp->global); error=pthread_cond_timedwait(cond,&mp->mutex,abstime); #ifdef EXTRA_DEBUG - if (error && (error != EINTR && error != ETIMEDOUT)) + if (error && (error != EINTR && error != ETIMEDOUT && error != ETIME)) { fprintf(stderr,"safe_mutex: Got error: %d (%d) when doing a safe_mutex_timedwait at %s, line %d\n", error, errno, file, line); } #endif pthread_mutex_lock(&mp->global); + mp->thread=pthread_self(); if (mp->count++) { fprintf(stderr, @@ -253,7 +255,6 @@ int safe_cond_timedwait(pthread_cond_t *cond, safe_mutex_t *mp, fflush(stderr); abort(); } - mp->thread=pthread_self(); mp->file= file; mp->line=line; pthread_mutex_unlock(&mp->global); diff --git a/mysys/tree.c b/mysys/tree.c index bec1ec680f1..1780913961e 100644 --- a/mysys/tree.c +++ b/mysys/tree.c @@ -263,6 +263,9 @@ TREE_ELEMENT *tree_insert(TREE *tree, void *key, uint key_size, if (tree->flag & TREE_NO_DUPS) return(NULL); element->count++; + /* Avoid a wrap over of the count. */ + if (! element->count) + element->count--; } DBUG_EXECUTE("check_tree", test_rb_tree(tree->root);); return element; |