summaryrefslogtreecommitdiff
path: root/mysys
diff options
context:
space:
mode:
Diffstat (limited to 'mysys')
-rw-r--r--mysys/ChangeLog4
-rw-r--r--mysys/Makefile.am35
-rw-r--r--mysys/charset-def.c186
-rw-r--r--mysys/charset.c973
-rw-r--r--mysys/charset2html.c177
-rw-r--r--mysys/checksum.c25
-rw-r--r--mysys/default.c263
-rw-r--r--mysys/errors.c4
-rw-r--r--mysys/hash.c281
-rw-r--r--mysys/list.c6
-rw-r--r--mysys/mf_casecnv.c297
-rw-r--r--mysys/mf_dirname.c39
-rw-r--r--mysys/mf_format.c8
-rw-r--r--mysys/mf_iocache.c845
-rw-r--r--mysys/mf_iocache2.c5
-rw-r--r--mysys/mf_keycache.c3149
-rw-r--r--mysys/mf_keycaches.c364
-rw-r--r--mysys/mf_loadpath.c11
-rw-r--r--mysys/mf_pack.c28
-rw-r--r--mysys/mf_soundex.c23
-rw-r--r--mysys/mf_tempdir.c83
-rw-r--r--mysys/mf_tempfile.c2
-rw-r--r--mysys/mf_wfile.c9
-rw-r--r--mysys/mulalloc.c14
-rw-r--r--mysys/my_access.c122
-rw-r--r--mysys/my_alloc.c70
-rw-r--r--mysys/my_append.c6
-rw-r--r--mysys/my_bitmap.c257
-rw-r--r--mysys/my_chsize.c12
-rw-r--r--mysys/my_compress.c2
-rw-r--r--mysys/my_conio.c217
-rw-r--r--mysys/my_copy.c6
-rw-r--r--mysys/my_crc32.c23
-rw-r--r--mysys/my_div.c2
-rw-r--r--mysys/my_dup.c2
-rw-r--r--mysys/my_error.c89
-rw-r--r--mysys/my_file.c147
-rw-r--r--mysys/my_fopen.c134
-rw-r--r--mysys/my_fstream.c8
-rw-r--r--mysys/my_gethostbyname.c1
-rw-r--r--mysys/my_gethwaddr.c130
-rw-r--r--mysys/my_getopt.c319
-rw-r--r--mysys/my_getsystime.c65
-rw-r--r--mysys/my_getwd.c32
-rw-r--r--mysys/my_handler.c550
-rw-r--r--mysys/my_init.c53
-rw-r--r--mysys/my_lib.c17
-rw-r--r--mysys/my_lock.c41
-rw-r--r--mysys/my_lwrite.c2
-rw-r--r--mysys/my_malloc.c38
-rw-r--r--mysys/my_new.cc2
-rw-r--r--mysys/my_once.c20
-rw-r--r--mysys/my_open.c14
-rw-r--r--mysys/my_pread.c4
-rw-r--r--mysys/my_pthread.c78
-rw-r--r--mysys/my_read.c2
-rw-r--r--mysys/my_realloc.c35
-rw-r--r--mysys/my_redel.c6
-rw-r--r--mysys/my_seek.c40
-rw-r--r--mysys/my_sleep.c2
-rw-r--r--mysys/my_static.c4
-rw-r--r--mysys/my_static.h5
-rw-r--r--mysys/my_symlink.c21
-rw-r--r--mysys/my_symlink2.c7
-rw-r--r--mysys/my_tempnam.c2
-rw-r--r--mysys/my_thr_init.c47
-rw-r--r--mysys/my_vsnprintf.c180
-rw-r--r--mysys/my_windac.c224
-rw-r--r--mysys/my_write.c2
-rw-r--r--mysys/mysys_priv.h4
-rw-r--r--mysys/queues.c115
-rw-r--r--mysys/raid.cc2
-rw-r--r--mysys/rijndael.c1
-rw-r--r--mysys/safemalloc.c8
-rw-r--r--mysys/sha1.c16
-rw-r--r--mysys/test_charset.c48
-rw-r--r--mysys/test_xml.c105
-rw-r--r--mysys/testhash.c13
-rw-r--r--mysys/thr_alarm.c18
-rw-r--r--mysys/thr_lock.c27
-rw-r--r--mysys/thr_mutex.c39
-rw-r--r--mysys/tree.c258
-rw-r--r--mysys/typelib.c39
83 files changed, 8133 insertions, 2431 deletions
diff --git a/mysys/ChangeLog b/mysys/ChangeLog
index e24fc00b493..7a426106667 100644
--- a/mysys/ChangeLog
+++ b/mysys/ChangeLog
@@ -91,7 +91,7 @@ Tue Mar 26 15:09:45 1991 Mikael WIDENIUS (monty at panther)
Sat Mar 23 10:49:49 1991 Michael Widenius (monty at LYNX)
- * Added init of alarm variables to skipp some warnings from gcc.
+ * Added init of alarm variables to skip some warnings from gcc.
Tue Mar 5 16:50:34 1991 Michael Widenius (monty at LYNX)
@@ -124,7 +124,7 @@ Mon Aug 27 22:20:38 1990 Michael Widenius (monty at lynx)
Sun Apr 1 23:29:47 1990 Monty (monty at monty)
* Changed mf_keydisk.c to have separate functions for read and write.
- Read can now return pointer to intern key-buffer to skipp
+ Read can now return pointer to intern key-buffer to skip
unessessary memcpy-s.
Fri Mar 23 23:03:39 1990 Monty (monty at monty)
diff --git a/mysys/Makefile.am b/mysys/Makefile.am
index 68d5da5afe8..90a28a9ce4b 100644
--- a/mysys/Makefile.am
+++ b/mysys/Makefile.am
@@ -17,7 +17,9 @@
MYSQLDATAdir = $(localstatedir)
MYSQLSHAREdir = $(pkgdatadir)
MYSQLBASEdir= $(prefix)
-INCLUDES = @MT_INCLUDES@ -I$(srcdir)/../include -I../include -I.. -I$(srcdir)
+INCLUDES = @MT_INCLUDES@ \
+ @ZLIB_INCLUDES@ -I$(top_builddir)/include \
+ -I$(top_srcdir)/include -I$(srcdir)
pkglib_LIBRARIES = libmysys.a
LDADD = libmysys.a ../dbug/libdbug.a \
../strings/libmystrings.a
@@ -25,21 +27,21 @@ noinst_HEADERS = mysys_priv.h my_static.h \
my_os2cond.c my_os2dirsrch.c my_os2dirsrch.h \
my_os2dlfcn.c my_os2file64.c my_os2mutex.c \
my_os2thread.c my_os2tls.c
-libmysys_a_SOURCES = my_init.c my_getwd.c mf_getdate.c\
- mf_path.c mf_loadpath.c\
+libmysys_a_SOURCES = my_init.c my_getwd.c mf_getdate.c \
+ mf_path.c mf_loadpath.c my_file.c \
my_open.c my_create.c my_dup.c my_seek.c my_read.c \
my_pread.c my_write.c \
- mf_keycache.c \
+ mf_keycache.c mf_keycaches.c my_crc32.c \
mf_iocache.c mf_iocache2.c mf_cache.c mf_tempfile.c \
- my_lock.c mf_brkhant.c my_alarm.c \
+ mf_tempdir.c my_lock.c mf_brkhant.c my_alarm.c \
my_malloc.c my_realloc.c my_once.c mulalloc.c \
my_alloc.c safemalloc.c my_new.cc \
- my_fopen.c my_fstream.c \
+ my_fopen.c my_fstream.c my_getsystime.c \
my_error.c errors.c my_div.c my_messnc.c \
mf_format.c mf_same.c mf_dirname.c mf_fn_ext.c \
my_symlink.c my_symlink2.c \
mf_pack.c mf_unixpath.c mf_strip.c \
- mf_casecnv.c mf_soundex.c mf_wcomp.c mf_wfile.c \
+ mf_wcomp.c mf_wfile.c my_gethwaddr.c \
mf_qsort.c mf_qsort2.c mf_sort.c \
ptr_cmp.c mf_radix.c queues.c \
tree.c list.c hash.c array.c string.c typelib.c \
@@ -49,18 +51,18 @@ libmysys_a_SOURCES = my_init.c my_getwd.c mf_getdate.c\
my_quick.c my_lockmem.c my_static.c \
my_sync.c my_getopt.c my_mkdir.c \
default.c my_compress.c checksum.c raid.cc \
- my_net.c my_port.c my_sleep.c \
- my_vsnprintf.c charset.c my_bitmap.c my_bit.c md5.c \
+ my_net.c my_port.c my_sleep.c \
+ charset.c charset-def.c my_bitmap.c my_bit.c md5.c \
my_gethostbyname.c rijndael.c my_aes.c sha1.c \
- my_netware.c
+ my_handler.c my_netware.c my_windac.c my_access.c
EXTRA_DIST = thr_alarm.c thr_lock.c my_pthread.c my_thr_init.c \
thr_mutex.c thr_rwlock.c
libmysys_a_LIBADD = @THREAD_LOBJECTS@
-# test_fn removed 980815 since it not up to date test_dir
-noinst_PROGRAMS = @THREAD_LPROGRAMS@
+noinst_PROGRAMS = charset2html @THREAD_LPROGRAMS@
# test_dir_DEPENDENCIES= $(LIBRARIES)
# testhash_DEPENDENCIES= $(LIBRARIES)
# test_charset_DEPENDENCIES= $(LIBRARIES)
+# charset2html_DEPENDENCIES= $(LIBRARIES)
EXTRA_PROGRAMS =
DEFS = -DDEFAULT_BASEDIR=\"$(prefix)\" \
-DDATADIR="\"$(MYSQLDATAdir)\"" \
@@ -92,6 +94,7 @@ test_vsnprintf$(EXEEXT): my_vsnprintf.c $(LIBRARIES)
$(CP) $(srcdir)/my_vsnprintf.c test_vsnprintf.c
$(LINK) $(FLAGS) -DMAIN ./test_vsnprintf.c $(LDADD) $(LIBS)
$(RM) -f test_vsnprintf.c
+
test_io_cache$(EXEEXT): mf_iocache.c $(LIBRARIES)
$(CP) $(srcdir)/mf_iocache.c test_io_cache.c
$(LINK) $(FLAGS) -DMAIN ./test_io_cache.c $(LDADD) $(LIBS)
@@ -103,8 +106,16 @@ test_dir$(EXEEXT): test_dir.c $(LIBRARIES)
test_charset$(EXEEXT): test_charset.c $(LIBRARIES)
$(LINK) $(FLAGS) -DMAIN $(srcdir)/test_charset.c $(LDADD) $(LIBS)
+charset2html$(EXEEXT): charset2html.c $(LIBRARIES)
+ $(LINK) $(FLAGS) -DMAIN $(srcdir)/charset2html.c $(LDADD) $(LIBS)
+
testhash$(EXEEXT): testhash.c $(LIBRARIES)
$(LINK) $(FLAGS) -DMAIN $(srcdir)/testhash.c $(LDADD) $(LIBS)
+test_gethwaddr$(EXEEXT): my_gethwaddr.c $(LIBRARIES)
+ $(CP) $(srcdir)/my_gethwaddr.c ./test_gethwaddr.c
+ $(LINK) $(FLAGS) -DMAIN ./test_gethwaddr.c $(LDADD) $(LIBS)
+ $(RM) -f ./test_gethwaddr.c
+
# Don't update the files from bitkeeper
%::SCCS/s.%
diff --git a/mysys/charset-def.c b/mysys/charset-def.c
new file mode 100644
index 00000000000..1fa87b715a8
--- /dev/null
+++ b/mysys/charset-def.c
@@ -0,0 +1,186 @@
+/* Copyright (C) 2000 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#include "mysys_priv.h"
+
+/*
+ Include all compiled character sets into the client
+ If a client don't want to use all of them, he can define his own
+ init_compiled_charsets() that only adds those that he wants
+*/
+
+#ifdef HAVE_UCA_COLLATIONS
+
+#ifdef HAVE_CHARSET_ucs2
+extern CHARSET_INFO my_charset_ucs2_general_uca;
+extern CHARSET_INFO my_charset_ucs2_icelandic_uca_ci;
+extern CHARSET_INFO my_charset_ucs2_latvian_uca_ci;
+extern CHARSET_INFO my_charset_ucs2_romanian_uca_ci;
+extern CHARSET_INFO my_charset_ucs2_slovenian_uca_ci;
+extern CHARSET_INFO my_charset_ucs2_polish_uca_ci;
+extern CHARSET_INFO my_charset_ucs2_estonian_uca_ci;
+extern CHARSET_INFO my_charset_ucs2_spanish_uca_ci;
+extern CHARSET_INFO my_charset_ucs2_swedish_uca_ci;
+extern CHARSET_INFO my_charset_ucs2_turkish_uca_ci;
+extern CHARSET_INFO my_charset_ucs2_czech_uca_ci;
+extern CHARSET_INFO my_charset_ucs2_danish_uca_ci;
+extern CHARSET_INFO my_charset_ucs2_lithuanian_uca_ci;
+extern CHARSET_INFO my_charset_ucs2_slovak_uca_ci;
+extern CHARSET_INFO my_charset_ucs2_spanish2_uca_ci;
+extern CHARSET_INFO my_charset_ucs2_roman_uca_ci;
+extern CHARSET_INFO my_charset_ucs2_persian_uca_ci;
+#endif
+
+#ifdef HAVE_CHARSET_utf8
+extern CHARSET_INFO my_charset_utf8_general_uca_ci;
+extern CHARSET_INFO my_charset_utf8_icelandic_uca_ci;
+extern CHARSET_INFO my_charset_utf8_latvian_uca_ci;
+extern CHARSET_INFO my_charset_utf8_romanian_uca_ci;
+extern CHARSET_INFO my_charset_utf8_slovenian_uca_ci;
+extern CHARSET_INFO my_charset_utf8_polish_uca_ci;
+extern CHARSET_INFO my_charset_utf8_estonian_uca_ci;
+extern CHARSET_INFO my_charset_utf8_spanish_uca_ci;
+extern CHARSET_INFO my_charset_utf8_swedish_uca_ci;
+extern CHARSET_INFO my_charset_utf8_turkish_uca_ci;
+extern CHARSET_INFO my_charset_utf8_czech_uca_ci;
+extern CHARSET_INFO my_charset_utf8_danish_uca_ci;
+extern CHARSET_INFO my_charset_utf8_lithuanian_uca_ci;
+extern CHARSET_INFO my_charset_utf8_slovak_uca_ci;
+extern CHARSET_INFO my_charset_utf8_spanish2_uca_ci;
+extern CHARSET_INFO my_charset_utf8_roman_uca_ci;
+extern CHARSET_INFO my_charset_utf8_persian_uca_ci;
+#ifdef HAVE_UTF8_GENERAL_CS
+extern CHARSET_INFO my_charset_utf8_general_cs;
+#endif
+#endif
+
+#endif /* HAVE_UCA_COLLATIONS */
+
+my_bool init_compiled_charsets(myf flags __attribute__((unused)))
+{
+ CHARSET_INFO *cs;
+
+ add_compiled_collation(&my_charset_bin);
+
+ add_compiled_collation(&my_charset_latin1);
+ add_compiled_collation(&my_charset_latin1_bin);
+ add_compiled_collation(&my_charset_latin1_german2_ci);
+
+#ifdef HAVE_CHARSET_big5
+ add_compiled_collation(&my_charset_big5_chinese_ci);
+ add_compiled_collation(&my_charset_big5_bin);
+#endif
+
+#ifdef HAVE_CHARSET_cp1250
+ add_compiled_collation(&my_charset_cp1250_czech_ci);
+#endif
+
+#ifdef HAVE_CHARSET_cp932
+ add_compiled_collation(&my_charset_cp932_japanese_ci);
+ add_compiled_collation(&my_charset_cp932_bin);
+#endif
+
+#ifdef HAVE_CHARSET_latin2
+ add_compiled_collation(&my_charset_latin2_czech_ci);
+#endif
+
+#ifdef HAVE_CHARSET_euckr
+ add_compiled_collation(&my_charset_euckr_korean_ci);
+ add_compiled_collation(&my_charset_euckr_bin);
+#endif
+
+#ifdef HAVE_CHARSET_gb2312
+ add_compiled_collation(&my_charset_gb2312_chinese_ci);
+ add_compiled_collation(&my_charset_gb2312_bin);
+#endif
+
+#ifdef HAVE_CHARSET_gbk
+ add_compiled_collation(&my_charset_gbk_chinese_ci);
+ add_compiled_collation(&my_charset_gbk_bin);
+#endif
+
+#ifdef HAVE_CHARSET_sjis
+ add_compiled_collation(&my_charset_sjis_japanese_ci);
+ add_compiled_collation(&my_charset_sjis_bin);
+#endif
+
+#ifdef HAVE_CHARSET_tis620
+ add_compiled_collation(&my_charset_tis620_thai_ci);
+ add_compiled_collation(&my_charset_tis620_bin);
+#endif
+
+#ifdef HAVE_CHARSET_ucs2
+ add_compiled_collation(&my_charset_ucs2_general_ci);
+ add_compiled_collation(&my_charset_ucs2_bin);
+#ifdef HAVE_UCA_COLLATIONS
+ add_compiled_collation(&my_charset_ucs2_general_uca);
+ add_compiled_collation(&my_charset_ucs2_icelandic_uca_ci);
+ add_compiled_collation(&my_charset_ucs2_latvian_uca_ci);
+ add_compiled_collation(&my_charset_ucs2_romanian_uca_ci);
+ add_compiled_collation(&my_charset_ucs2_slovenian_uca_ci);
+ add_compiled_collation(&my_charset_ucs2_polish_uca_ci);
+ add_compiled_collation(&my_charset_ucs2_estonian_uca_ci);
+ add_compiled_collation(&my_charset_ucs2_spanish_uca_ci);
+ add_compiled_collation(&my_charset_ucs2_swedish_uca_ci);
+ add_compiled_collation(&my_charset_ucs2_turkish_uca_ci);
+ add_compiled_collation(&my_charset_ucs2_czech_uca_ci);
+ add_compiled_collation(&my_charset_ucs2_danish_uca_ci);
+ add_compiled_collation(&my_charset_ucs2_lithuanian_uca_ci);
+ add_compiled_collation(&my_charset_ucs2_slovak_uca_ci);
+ add_compiled_collation(&my_charset_ucs2_spanish2_uca_ci);
+ add_compiled_collation(&my_charset_ucs2_roman_uca_ci);
+ add_compiled_collation(&my_charset_ucs2_persian_uca_ci);
+#endif
+#endif
+
+#ifdef HAVE_CHARSET_ujis
+ add_compiled_collation(&my_charset_ujis_japanese_ci);
+ add_compiled_collation(&my_charset_ujis_bin);
+#endif
+
+#ifdef HAVE_CHARSET_utf8
+ add_compiled_collation(&my_charset_utf8_general_ci);
+ add_compiled_collation(&my_charset_utf8_bin);
+#ifdef HAVE_UTF8_GENERAL_CS
+ add_compiled_collation(&my_charset_utf8_general_cs);
+#endif
+#ifdef HAVE_UCA_COLLATIONS
+ add_compiled_collation(&my_charset_utf8_general_uca_ci);
+ add_compiled_collation(&my_charset_utf8_icelandic_uca_ci);
+ add_compiled_collation(&my_charset_utf8_latvian_uca_ci);
+ add_compiled_collation(&my_charset_utf8_romanian_uca_ci);
+ add_compiled_collation(&my_charset_utf8_slovenian_uca_ci);
+ add_compiled_collation(&my_charset_utf8_polish_uca_ci);
+ add_compiled_collation(&my_charset_utf8_estonian_uca_ci);
+ add_compiled_collation(&my_charset_utf8_spanish_uca_ci);
+ add_compiled_collation(&my_charset_utf8_swedish_uca_ci);
+ add_compiled_collation(&my_charset_utf8_turkish_uca_ci);
+ add_compiled_collation(&my_charset_utf8_czech_uca_ci);
+ add_compiled_collation(&my_charset_utf8_danish_uca_ci);
+ add_compiled_collation(&my_charset_utf8_lithuanian_uca_ci);
+ add_compiled_collation(&my_charset_utf8_slovak_uca_ci);
+ add_compiled_collation(&my_charset_utf8_spanish2_uca_ci);
+ add_compiled_collation(&my_charset_utf8_roman_uca_ci);
+ add_compiled_collation(&my_charset_utf8_persian_uca_ci);
+#endif
+#endif
+
+ /* Copy compiled charsets */
+ for (cs=compiled_charsets; cs->name; cs++)
+ add_compiled_collation(cs);
+
+ return FALSE;
+}
diff --git a/mysys/charset.c b/mysys/charset.c
index 5e5be9e1b42..f0ac61ceed5 100644
--- a/mysys/charset.c
+++ b/mysys/charset.c
@@ -19,76 +19,337 @@
#include <m_ctype.h>
#include <m_string.h>
#include <my_dir.h>
+#include <my_xml.h>
-typedef struct cs_id_st {
- char *name;
- uint number;
-} CS_ID;
-const char *charsets_dir = NULL;
-static DYNAMIC_ARRAY cs_info_table;
-static CS_ID **available_charsets;
-static int charset_initialized=0;
+/*
+ The code below implements this functionality:
+
+ - Initializing charset related structures
+ - Loading dynamic charsets
+ - Searching for a proper CHARSET_INFO
+ using charset name, collation name or collation ID
+ - Setting server default character set
+*/
-#define MAX_LINE 1024
+my_bool my_charset_same(CHARSET_INFO *cs1, CHARSET_INFO *cs2)
+{
+ return ((cs1 == cs2) || !strcmp(cs1->csname,cs2->csname));
+}
-#define CTYPE_TABLE_SIZE 257
-#define TO_LOWER_TABLE_SIZE 256
-#define TO_UPPER_TABLE_SIZE 256
-#define SORT_ORDER_TABLE_SIZE 256
-struct simpleconfig_buf_st {
- FILE *f;
- char buf[MAX_LINE];
- char *p;
-};
+static uint
+get_collation_number_internal(const char *name)
+{
+ CHARSET_INFO **cs;
+ for (cs= all_charsets;
+ cs < all_charsets+array_elements(all_charsets)-1 ;
+ cs++)
+ {
+ if ( cs[0] && cs[0]->name &&
+ !my_strcasecmp(&my_charset_latin1, cs[0]->name, name))
+ return cs[0]->number;
+ }
+ return 0;
+}
-static uint num_from_csname(CS_ID **cs, const char *name)
+
+static my_bool init_state_maps(CHARSET_INFO *cs)
{
- CS_ID **c;
- for (c = cs; *c; ++c)
- if (!strcmp((*c)->name, name))
- return (*c)->number;
- return 0; /* this mimics find_type() */
+ uint i;
+ uchar *state_map;
+ uchar *ident_map;
+
+ if (!(cs->state_map= (uchar*) my_once_alloc(256, MYF(MY_WME))))
+ return 1;
+
+ if (!(cs->ident_map= (uchar*) my_once_alloc(256, MYF(MY_WME))))
+ return 1;
+
+ state_map= cs->state_map;
+ ident_map= cs->ident_map;
+
+ /* Fill state_map with states to get a faster parser */
+ for (i=0; i < 256 ; i++)
+ {
+ if (my_isalpha(cs,i))
+ state_map[i]=(uchar) MY_LEX_IDENT;
+ else if (my_isdigit(cs,i))
+ state_map[i]=(uchar) MY_LEX_NUMBER_IDENT;
+#if defined(USE_MB) && defined(USE_MB_IDENT)
+ else if (my_mbcharlen(cs, i)>1)
+ state_map[i]=(uchar) MY_LEX_IDENT;
+#endif
+ else if (my_isspace(cs,i))
+ state_map[i]=(uchar) MY_LEX_SKIP;
+ else
+ state_map[i]=(uchar) MY_LEX_CHAR;
+ }
+ state_map[(uchar)'_']=state_map[(uchar)'$']=(uchar) MY_LEX_IDENT;
+ state_map[(uchar)'\'']=(uchar) MY_LEX_STRING;
+ state_map[(uchar)'.']=(uchar) MY_LEX_REAL_OR_POINT;
+ state_map[(uchar)'>']=state_map[(uchar)'=']=state_map[(uchar)'!']= (uchar) MY_LEX_CMP_OP;
+ state_map[(uchar)'<']= (uchar) MY_LEX_LONG_CMP_OP;
+ state_map[(uchar)'&']=state_map[(uchar)'|']=(uchar) MY_LEX_BOOL;
+ state_map[(uchar)'#']=(uchar) MY_LEX_COMMENT;
+ state_map[(uchar)';']=(uchar) MY_LEX_SEMICOLON;
+ state_map[(uchar)':']=(uchar) MY_LEX_SET_VAR;
+ state_map[0]=(uchar) MY_LEX_EOL;
+ state_map[(uchar)'\\']= (uchar) MY_LEX_ESCAPE;
+ state_map[(uchar)'/']= (uchar) MY_LEX_LONG_COMMENT;
+ state_map[(uchar)'*']= (uchar) MY_LEX_END_LONG_COMMENT;
+ state_map[(uchar)'@']= (uchar) MY_LEX_USER_END;
+ state_map[(uchar) '`']= (uchar) MY_LEX_USER_VARIABLE_DELIMITER;
+ state_map[(uchar)'"']= (uchar) MY_LEX_STRING_OR_DELIMITER;
+
+ /*
+ Create a second map to make it faster to find identifiers
+ */
+ for (i=0; i < 256 ; i++)
+ {
+ ident_map[i]= (uchar) (state_map[i] == MY_LEX_IDENT ||
+ state_map[i] == MY_LEX_NUMBER_IDENT);
+ }
+
+ /* Special handling of hex and binary strings */
+ state_map[(uchar)'x']= state_map[(uchar)'X']= (uchar) MY_LEX_IDENT_OR_HEX;
+ state_map[(uchar)'b']= state_map[(uchar)'b']= (uchar) MY_LEX_IDENT_OR_BIN;
+ state_map[(uchar)'n']= state_map[(uchar)'N']= (uchar) MY_LEX_IDENT_OR_NCHAR;
+ return 0;
}
-static char *name_from_csnum(CS_ID **cs, uint number)
+
+static void simple_cs_init_functions(CHARSET_INFO *cs)
{
- CS_ID **c;
- if(cs)
- for (c = cs; *c; ++c)
- if ((*c)->number == number)
- return (*c)->name;
- return (char*) "?"; /* this mimics find_type() */
+ if (cs->state & MY_CS_BINSORT)
+ cs->coll= &my_collation_8bit_bin_handler;
+ else
+ cs->coll= &my_collation_8bit_simple_ci_handler;
+
+ cs->cset= &my_charset_8bit_handler;
}
-static my_bool get_word(struct simpleconfig_buf_st *fb, char *buf)
+
+
+static int cs_copy_data(CHARSET_INFO *to, CHARSET_INFO *from)
{
- char *endptr=fb->p;
+ to->number= from->number ? from->number : to->number;
- for (;;)
+ if (from->csname)
+ if (!(to->csname= my_once_strdup(from->csname,MYF(MY_WME))))
+ goto err;
+
+ if (from->name)
+ if (!(to->name= my_once_strdup(from->name,MYF(MY_WME))))
+ goto err;
+
+ if (from->comment)
+ if (!(to->comment= my_once_strdup(from->comment,MYF(MY_WME))))
+ goto err;
+
+ if (from->ctype)
{
- while (isspace(*endptr))
- ++endptr;
- if (*endptr && *endptr != '#') /* Not comment */
- break; /* Found something */
- if ((fgets(fb->buf, sizeof(fb->buf), fb->f)) == NULL)
- return TRUE; /* end of file */
- endptr = fb->buf;
+ if (!(to->ctype= (uchar*) my_once_memdup((char*) from->ctype,
+ MY_CS_CTYPE_TABLE_SIZE,
+ MYF(MY_WME))))
+ goto err;
+ if (init_state_maps(to))
+ goto err;
}
+ if (from->to_lower)
+ if (!(to->to_lower= (uchar*) my_once_memdup((char*) from->to_lower,
+ MY_CS_TO_LOWER_TABLE_SIZE,
+ MYF(MY_WME))))
+ goto err;
+
+ if (from->to_upper)
+ if (!(to->to_upper= (uchar*) my_once_memdup((char*) from->to_upper,
+ MY_CS_TO_UPPER_TABLE_SIZE,
+ MYF(MY_WME))))
+ goto err;
+ if (from->sort_order)
+ {
+ if (!(to->sort_order= (uchar*) my_once_memdup((char*) from->sort_order,
+ MY_CS_SORT_ORDER_TABLE_SIZE,
+ MYF(MY_WME))))
+ goto err;
+
+ }
+ if (from->tab_to_uni)
+ {
+ uint sz= MY_CS_TO_UNI_TABLE_SIZE*sizeof(uint16);
+ if (!(to->tab_to_uni= (uint16*) my_once_memdup((char*)from->tab_to_uni,
+ sz, MYF(MY_WME))))
+ goto err;
+ }
+ if (from->tailoring)
+ if (!(to->tailoring= my_once_strdup(from->tailoring,MYF(MY_WME))))
+ goto err;
+
+ return 0;
+
+err:
+ return 1;
+}
- while (*endptr && !isspace(*endptr))
- *buf++= *endptr++;
- *buf=0;
- fb->p = endptr;
+
+static my_bool simple_cs_is_full(CHARSET_INFO *cs)
+{
+ return ((cs->csname && cs->tab_to_uni && cs->ctype && cs->to_upper &&
+ cs->to_lower) &&
+ (cs->number && cs->name &&
+ (cs->sort_order || (cs->state & MY_CS_BINSORT) )));
+}
+
+
+static int add_collation(CHARSET_INFO *cs)
+{
+ if (cs->name && (cs->number ||
+ (cs->number=get_collation_number_internal(cs->name))))
+ {
+ if (!all_charsets[cs->number])
+ {
+ if (!(all_charsets[cs->number]=
+ (CHARSET_INFO*) my_once_alloc(sizeof(CHARSET_INFO),MYF(0))))
+ return MY_XML_ERROR;
+ bzero((void*)all_charsets[cs->number],sizeof(CHARSET_INFO));
+ }
+
+ if (cs->primary_number == cs->number)
+ cs->state |= MY_CS_PRIMARY;
+
+ if (cs->binary_number == cs->number)
+ cs->state |= MY_CS_BINSORT;
+
+ all_charsets[cs->number]->state|= cs->state;
+
+ if (!(all_charsets[cs->number]->state & MY_CS_COMPILED))
+ {
+ CHARSET_INFO *new= all_charsets[cs->number];
+ if (cs_copy_data(all_charsets[cs->number],cs))
+ return MY_XML_ERROR;
+
+ if (!strcmp(cs->csname,"ucs2") )
+ {
+#if defined(HAVE_CHARSET_ucs2) && defined(HAVE_UCA_COLLATIONS)
+ new->cset= my_charset_ucs2_general_uca.cset;
+ new->coll= my_charset_ucs2_general_uca.coll;
+ new->strxfrm_multiply= my_charset_ucs2_general_uca.strxfrm_multiply;
+ new->min_sort_char= my_charset_ucs2_general_uca.min_sort_char;
+ new->max_sort_char= my_charset_ucs2_general_uca.max_sort_char;
+ new->mbminlen= 2;
+ new->mbmaxlen= 2;
+ new->state |= MY_CS_AVAILABLE | MY_CS_LOADED;
+#endif
+ }
+ else
+ {
+ uchar *sort_order= all_charsets[cs->number]->sort_order;
+ simple_cs_init_functions(all_charsets[cs->number]);
+ new->mbminlen= 1;
+ new->mbmaxlen= 1;
+ if (simple_cs_is_full(all_charsets[cs->number]))
+ {
+ all_charsets[cs->number]->state |= MY_CS_LOADED;
+ }
+ all_charsets[cs->number]->state|= MY_CS_AVAILABLE;
+
+ /*
+ Check if case sensitive sort order: A < a < B.
+ We need MY_CS_FLAG for regex library, and for
+ case sensitivity flag for 5.0 client protocol,
+ to support isCaseSensitive() method in JDBC driver
+ */
+ if (sort_order && sort_order['A'] < sort_order['a'] &&
+ sort_order['a'] < sort_order['B'])
+ all_charsets[cs->number]->state|= MY_CS_CSSORT;
+ }
+ }
+ else
+ {
+ /*
+ We need the below to make get_charset_name()
+ and get_charset_number() working even if a
+ character set has not been really incompiled.
+ The above functions are used for example
+ in error message compiler extra/comp_err.c.
+ If a character set was compiled, this information
+ will get lost and overwritten in add_compiled_collation().
+ */
+ CHARSET_INFO *dst= all_charsets[cs->number];
+ dst->number= cs->number;
+ if (cs->comment)
+ if (!(dst->comment= my_once_strdup(cs->comment,MYF(MY_WME))))
+ return MY_XML_ERROR;
+ if (cs->csname)
+ if (!(dst->csname= my_once_strdup(cs->csname,MYF(MY_WME))))
+ return MY_XML_ERROR;
+ if (cs->name)
+ if (!(dst->name= my_once_strdup(cs->name,MYF(MY_WME))))
+ return MY_XML_ERROR;
+ }
+ cs->number= 0;
+ cs->primary_number= 0;
+ cs->binary_number= 0;
+ cs->name= NULL;
+ cs->state= 0;
+ cs->sort_order= NULL;
+ cs->state= 0;
+ }
+ return MY_XML_OK;
+}
+
+
+#define MY_MAX_ALLOWED_BUF 1024*1024
+#define MY_CHARSET_INDEX "Index.xml"
+
+const char *charsets_dir= NULL;
+static int charset_initialized=0;
+
+
+static my_bool my_read_charset_file(const char *filename, myf myflags)
+{
+ char *buf;
+ int fd;
+ uint len, tmp_len;
+ MY_STAT stat_info;
+
+ if (!my_stat(filename, &stat_info, MYF(myflags)) ||
+ ((len= (uint)stat_info.st_size) > MY_MAX_ALLOWED_BUF) ||
+ !(buf= (char *)my_malloc(len,myflags)))
+ return TRUE;
+
+ if ((fd=my_open(filename,O_RDONLY,myflags)) < 0)
+ goto error;
+ tmp_len=my_read(fd, buf, len, myflags);
+ my_close(fd,myflags);
+ if (tmp_len != len)
+ goto error;
+
+ if (my_parse_charset_xml(buf,len,add_collation))
+ {
+#ifdef NOT_YET
+ printf("ERROR at line %d pos %d '%s'\n",
+ my_xml_error_lineno(&p)+1,
+ my_xml_error_pos(&p),
+ my_xml_error_string(&p));
+#endif
+ }
+
+ my_free(buf, myflags);
return FALSE;
+
+error:
+ my_free(buf, myflags);
+ return TRUE;
}
char *get_charsets_dir(char *buf)
{
- const char *sharedir = SHAREDIR;
+ const char *sharedir= SHAREDIR;
+ char *res;
DBUG_ENTER("get_charsets_dir");
if (charsets_dir != NULL)
@@ -102,65 +363,23 @@ char *get_charsets_dir(char *buf)
strxmov(buf, DEFAULT_CHARSET_HOME, "/", sharedir, "/", CHARSET_DIR,
NullS);
}
- convert_dirname(buf,buf,NullS);
- DBUG_PRINT("info",("charsets dir='%s'", buf));
- DBUG_RETURN(strend(buf));
+ res= convert_dirname(buf,buf,NullS);
+ DBUG_PRINT("info",("charsets dir: '%s'", buf));
+ DBUG_RETURN(res);
}
+CHARSET_INFO *all_charsets[256];
+CHARSET_INFO *default_charset_info = &my_charset_latin1;
-static my_bool read_charset_index(CS_ID ***charsets, myf myflags)
+void add_compiled_collation(CHARSET_INFO *cs)
{
- struct simpleconfig_buf_st fb;
- char buf[MAX_LINE], num_buf[MAX_LINE];
- DYNAMIC_ARRAY cs;
- CS_ID *csid;
-
- strmov(get_charsets_dir(buf), "Index");
-
- if ((fb.f = my_fopen(buf, O_RDONLY, myflags)) == NULL)
- return TRUE;
- fb.buf[0] = '\0';
- fb.p = fb.buf;
-
- if (my_init_dynamic_array(&cs, sizeof(CS_ID *), 32, 32))
- return TRUE;
-
- while (!get_word(&fb, buf) && !get_word(&fb, num_buf))
- {
- uint csnum;
- uint length;
-
- if (!(csnum = atoi(num_buf)))
- {
- /* corrupt Index file */
- my_fclose(fb.f,myflags);
- return TRUE;
- }
-
- if (!(csid = (CS_ID*) my_once_alloc(sizeof(CS_ID), myflags)) ||
- !(csid->name=
- (char*) my_once_alloc(length= (uint) strlen(buf)+1, myflags)))
- {
- my_fclose(fb.f,myflags);
- return TRUE;
- }
- memcpy(csid->name,buf,length);
- csid->number = csnum;
-
- insert_dynamic(&cs, (gptr) &csid);
- }
- my_fclose(fb.f,myflags);
-
-
- if (!(*charsets =
- (CS_ID **) my_once_alloc((cs.elements + 1) * sizeof(CS_ID *), myflags)))
- return TRUE;
- /* unwarranted chumminess with dynamic_array implementation? */
- memcpy((byte *) *charsets, cs.buffer, cs.elements * sizeof(CS_ID *));
- (*charsets)[cs.elements] = NULL;
- delete_dynamic(&cs);
+ all_charsets[cs->number]= cs;
+ cs->state|= MY_CS_AVAILABLE;
+}
- return FALSE;
+static void *cs_alloc(uint size)
+{
+ return my_once_alloc(size, MYF(MY_WME));
}
@@ -170,227 +389,117 @@ my_bool STDCALL init_available_charsets(myf myflags)
static my_bool init_available_charsets(myf myflags)
#endif
{
- my_bool error=0;
+ char fname[FN_REFLEN];
+ my_bool error=FALSE;
/*
We have to use charset_initialized to not lock on THR_LOCK_charset
inside get_internal_charset...
- */
+ */
if (!charset_initialized)
{
- /*
- To make things thread safe we are not allowing other threads to interfere
- while we may changing the cs_info_table
- */
+ CHARSET_INFO **cs;
+ /*
+ To make things thread safe we are not allowing other threads to interfere
+ while we may changing the cs_info_table
+ */
pthread_mutex_lock(&THR_LOCK_charset);
- if (!cs_info_table.buffer) /* If not initialized */
+ if (!charset_initialized)
{
- my_init_dynamic_array(&cs_info_table, sizeof(CHARSET_INFO*), 16, 8);
- error = read_charset_index(&available_charsets, myflags);
+ bzero(&all_charsets,sizeof(all_charsets));
+ init_compiled_charsets(myflags);
+
+ /* Copy compiled charsets */
+ for (cs=all_charsets;
+ cs < all_charsets+array_elements(all_charsets)-1 ;
+ cs++)
+ {
+ if (*cs)
+ {
+ if (cs[0]->ctype)
+ if (init_state_maps(*cs))
+ *cs= NULL;
+ }
+ }
+
+ strmov(get_charsets_dir(fname), MY_CHARSET_INDEX);
+ error= my_read_charset_file(fname,myflags);
+ charset_initialized=1;
}
- charset_initialized=1;
pthread_mutex_unlock(&THR_LOCK_charset);
}
- if(!available_charsets || !available_charsets[0])
- error = TRUE;
return error;
}
void free_charsets(void)
{
- delete_dynamic(&cs_info_table);
charset_initialized=0;
}
-static my_bool fill_array(uchar *array, int sz, struct simpleconfig_buf_st *fb)
+uint get_collation_number(const char *name)
{
- char buf[MAX_LINE];
- while (sz--)
- {
- if (get_word(fb, buf))
- {
- DBUG_PRINT("error",("get_word failed, expecting %d more words", sz + 1));
- return 1;
- }
- *array++ = (uchar) strtol(buf, NULL, 16);
- }
- return 0;
+ init_available_charsets(MYF(0));
+ return get_collation_number_internal(name);
}
-static void get_charset_conf_name(uint cs_number, char *buf)
+uint get_charset_number(const char *charset_name, uint cs_flags)
{
- strxmov(get_charsets_dir(buf),
- name_from_csnum(available_charsets, cs_number), ".conf", NullS);
-}
-
-
-static my_bool read_charset_file(uint cs_number, CHARSET_INFO *set,
- myf myflags)
-{
- struct simpleconfig_buf_st fb;
- char buf[FN_REFLEN];
- my_bool result;
- DBUG_ENTER("read_charset_file");
- DBUG_PRINT("enter",("cs_number: %d", cs_number));
-
- if (cs_number <= 0)
- DBUG_RETURN(TRUE);
-
- get_charset_conf_name(cs_number, buf);
- DBUG_PRINT("info",("file name: %s", buf));
-
- if ((fb.f = my_fopen(buf, O_RDONLY, myflags)) == NULL)
- DBUG_RETURN(TRUE);
-
- fb.buf[0] = '\0'; /* Init for get_word */
- fb.p = fb.buf;
-
- result=FALSE;
- if (fill_array(set->ctype, CTYPE_TABLE_SIZE, &fb) ||
- fill_array(set->to_lower, TO_LOWER_TABLE_SIZE, &fb) ||
- fill_array(set->to_upper, TO_UPPER_TABLE_SIZE, &fb) ||
- fill_array(set->sort_order, SORT_ORDER_TABLE_SIZE, &fb))
- result=TRUE;
-
- my_fclose(fb.f, MYF(0));
- DBUG_RETURN(result);
+ CHARSET_INFO **cs;
+ init_available_charsets(MYF(0));
+
+ for (cs= all_charsets;
+ cs < all_charsets+array_elements(all_charsets)-1 ;
+ cs++)
+ {
+ if ( cs[0] && cs[0]->csname && (cs[0]->state & cs_flags) &&
+ !my_strcasecmp(&my_charset_latin1, cs[0]->csname, charset_name))
+ return cs[0]->number;
+ }
+ return 0;
}
-uint get_charset_number(const char *charset_name)
-{
- uint number=compiled_charset_number(charset_name);
- if (number)
- return number;
- if (init_available_charsets(MYF(0))) /* If it isn't initialized */
- return 0;
- return num_from_csname(available_charsets, charset_name);
-}
-
const char *get_charset_name(uint charset_number)
{
- const char *name=compiled_charset_name(charset_number);
- if (*name != '?')
- return name;
- if (init_available_charsets(MYF(0))) /* If it isn't initialized */
- return "?";
- return name_from_csnum(available_charsets, charset_number);
-}
-
-
-static CHARSET_INFO *find_charset(CHARSET_INFO **table, uint cs_number,
- size_t tablesz)
-{
- uint i;
- for (i = 0; i < tablesz; ++i)
- if (table[i]->number == cs_number)
- return table[i];
- return NULL;
-}
-
-static CHARSET_INFO *find_charset_by_name(CHARSET_INFO **table,
- const char *name, size_t tablesz)
-{
- uint i;
- for (i = 0; i < tablesz; ++i)
- if (!strcmp(table[i]->name,name))
- return table[i];
- return NULL;
-}
-
-/*
- Read charset from file.
+ CHARSET_INFO *cs;
+ init_available_charsets(MYF(0));
- NOTES
- One never has to deallocate character sets. They will all be deallocated
- by my_once_free() when program ends.
+ cs=all_charsets[charset_number];
+ if (cs && (cs->number == charset_number) && cs->name )
+ return (char*) cs->name;
- If my_once_alloc() fails then this function may 'leak' some memory
- which my_once_free() will deallocate, but this is so unlikely to happen
- that this can be ignored.
-
- RETURN
- 0 Error
- # Pointer to allocated charset structure
-*/
-
-
-static CHARSET_INFO *add_charset(uint cs_number, const char *cs_name,
- myf flags)
-{
- CHARSET_INFO tmp_cs,*cs;
- uchar tmp_ctype[CTYPE_TABLE_SIZE];
- uchar tmp_to_lower[TO_LOWER_TABLE_SIZE];
- uchar tmp_to_upper[TO_UPPER_TABLE_SIZE];
- uchar tmp_sort_order[SORT_ORDER_TABLE_SIZE];
-
- /* Don't allocate memory if we are not sure we can find the char set */
- cs= &tmp_cs;
- bzero((char*) cs, sizeof(*cs));
- cs->ctype=tmp_ctype;
- cs->to_lower=tmp_to_lower;
- cs->to_upper=tmp_to_upper;
- cs->sort_order=tmp_sort_order;
- cs->strxfrm_multiply=cs->mbmaxlen=1;
- if (read_charset_file(cs_number, cs, flags))
- return 0;
-
- if (!(cs= (CHARSET_INFO*) my_once_alloc(sizeof(CHARSET_INFO),
- MYF(MY_WME))))
- return 0;
-
- *cs= tmp_cs;
- cs->name= (char *) my_once_alloc((uint) strlen(cs_name)+1, MYF(MY_WME));
- cs->ctype= (uchar*) my_once_alloc(CTYPE_TABLE_SIZE, MYF(MY_WME));
- cs->to_lower= (uchar*) my_once_alloc(TO_LOWER_TABLE_SIZE, MYF(MY_WME));
- cs->to_upper= (uchar*) my_once_alloc(TO_UPPER_TABLE_SIZE, MYF(MY_WME));
- cs->sort_order=(uchar*) my_once_alloc(SORT_ORDER_TABLE_SIZE, MYF(MY_WME));
- if (!cs->name || !cs->ctype || !cs->to_lower || !cs->to_upper ||
- !cs->sort_order)
- return 0;
-
- cs->number= cs_number;
- memcpy((char*) cs->name, (char*) cs_name, strlen(cs_name) + 1);
- memcpy((char*) cs->ctype, (char*) tmp_ctype, sizeof(tmp_ctype));
- memcpy((char*) cs->to_lower, (char*) tmp_to_lower, sizeof(tmp_to_lower));
- memcpy((char*) cs->to_upper, (char*) tmp_to_upper, sizeof(tmp_to_upper));
- memcpy((char*) cs->sort_order, (char*) tmp_sort_order,
- sizeof(tmp_sort_order));
- insert_dynamic(&cs_info_table, (gptr) &cs);
- return cs;
-}
-
-static CHARSET_INFO *get_internal_charset(uint cs_number, myf flags)
-{
- CHARSET_INFO *cs;
- /*
- To make things thread safe we are not allowing other threads to interfere
- while we may changing the cs_info_table
- */
- pthread_mutex_lock(&THR_LOCK_charset);
- if (!(cs = find_charset((CHARSET_INFO**) cs_info_table.buffer, cs_number,
- cs_info_table.elements)))
- if (!(cs = find_compiled_charset(cs_number)))
- cs=add_charset(cs_number, get_charset_name(cs_number), flags);
- pthread_mutex_unlock(&THR_LOCK_charset);
- return cs;
+ return (char*) "?"; /* this mimics find_type() */
}
-static CHARSET_INFO *get_internal_charset_by_name(const char *name, myf flags)
+static CHARSET_INFO *get_internal_charset(uint cs_number, myf flags)
{
+ char buf[FN_REFLEN];
CHARSET_INFO *cs;
/*
To make things thread safe we are not allowing other threads to interfere
while we may changing the cs_info_table
*/
pthread_mutex_lock(&THR_LOCK_charset);
- if (!(cs = find_charset_by_name((CHARSET_INFO**) cs_info_table.buffer, name,
- cs_info_table.elements)))
- if (!(cs = find_compiled_charset_by_name(name)))
- cs=add_charset(get_charset_number(name), name, flags);
+ if ((cs= all_charsets[cs_number]))
+ {
+ if (!(cs->state & MY_CS_COMPILED) && !(cs->state & MY_CS_LOADED))
+ {
+ strxmov(get_charsets_dir(buf), cs->csname, ".xml", NullS);
+ my_read_charset_file(buf,flags);
+ }
+ cs= (cs->state & MY_CS_AVAILABLE) ? cs : NULL;
+ }
+ if (cs && !(cs->state & MY_CS_READY))
+ {
+ if ((cs->cset->init && cs->cset->init(cs, cs_alloc)) ||
+ (cs->coll->init && cs->coll->init(cs, cs_alloc)))
+ cs= NULL;
+ else
+ cs->state|= MY_CS_READY;
+ }
pthread_mutex_unlock(&THR_LOCK_charset);
return cs;
}
@@ -399,13 +508,20 @@ static CHARSET_INFO *get_internal_charset_by_name(const char *name, myf flags)
CHARSET_INFO *get_charset(uint cs_number, myf flags)
{
CHARSET_INFO *cs;
+ if (cs_number == default_charset_info->number)
+ return default_charset_info;
+
(void) init_available_charsets(MYF(0)); /* If it isn't initialized */
+
+ if (!cs_number || cs_number >= array_elements(all_charsets)-1)
+ return NULL;
+
cs=get_internal_charset(cs_number, flags);
if (!cs && (flags & MY_WME))
{
char index_file[FN_REFLEN], cs_string[23];
- strmov(get_charsets_dir(index_file), "Index");
+ strmov(get_charsets_dir(index_file),MY_CHARSET_INDEX);
cs_string[0]='#';
int10_to_str(cs_number, cs_string+1, 10);
my_error(EE_UNKNOWN_CHARSET, MYF(ME_BELL), cs_string, index_file);
@@ -413,194 +529,187 @@ CHARSET_INFO *get_charset(uint cs_number, myf flags)
return cs;
}
-my_bool set_default_charset(uint cs, myf flags)
-{
- CHARSET_INFO *new_charset;
- DBUG_ENTER("set_default_charset");
- DBUG_PRINT("enter",("character set: %d",(int) cs));
- new_charset = get_charset(cs, flags);
- if (!new_charset)
- {
- DBUG_PRINT("error",("Couldn't set default character set"));
- DBUG_RETURN(TRUE); /* error */
- }
- default_charset_info = new_charset;
- DBUG_RETURN(FALSE);
-}
-
CHARSET_INFO *get_charset_by_name(const char *cs_name, myf flags)
{
+ uint cs_number;
CHARSET_INFO *cs;
(void) init_available_charsets(MYF(0)); /* If it isn't initialized */
- cs=get_internal_charset_by_name(cs_name, flags);
+
+ cs_number=get_collation_number(cs_name);
+ cs= cs_number ? get_internal_charset(cs_number,flags) : NULL;
if (!cs && (flags & MY_WME))
{
char index_file[FN_REFLEN];
- strmov(get_charsets_dir(index_file), "Index");
- my_error(EE_UNKNOWN_CHARSET, MYF(ME_BELL), cs_name, index_file);
+ strmov(get_charsets_dir(index_file),MY_CHARSET_INDEX);
+ my_error(EE_UNKNOWN_COLLATION, MYF(ME_BELL), cs_name, index_file);
}
return cs;
}
-my_bool set_default_charset_by_name(const char *cs_name, myf flags)
-{
- CHARSET_INFO *new_charset;
- DBUG_ENTER("set_default_charset_by_name");
- DBUG_PRINT("enter",("character set: %s", cs_name));
- new_charset = get_charset_by_name(cs_name, flags);
- if (!new_charset)
- {
- DBUG_PRINT("error",("Couldn't set default character set"));
- DBUG_RETURN(TRUE); /* error */
- }
-
- default_charset_info = new_charset;
- DBUG_RETURN(FALSE);
-}
-/* Only append name if it doesn't exist from before */
-
-static my_bool charset_in_string(const char *name, DYNAMIC_STRING *s)
+CHARSET_INFO *get_charset_by_csname(const char *cs_name,
+ uint cs_flags,
+ myf flags)
{
- uint length= (uint) strlen(name);
- const char *pos;
- for (pos=s->str ; (pos=strstr(pos,name)) ; pos++)
- {
- if (! pos[length] || pos[length] == ' ')
- return TRUE; /* Already existed */
- }
-
- return FALSE;
-}
+ uint cs_number;
+ CHARSET_INFO *cs;
+ DBUG_ENTER("get_charset_by_csname");
+ DBUG_PRINT("enter",("name: '%s'", cs_name));
-static void charset_append(DYNAMIC_STRING *s, const char *name)
-{
- if (!charset_in_string(name, s))
+ (void) init_available_charsets(MYF(0)); /* If it isn't initialized */
+
+ cs_number= get_charset_number(cs_name, cs_flags);
+ cs= cs_number ? get_internal_charset(cs_number, flags) : NULL;
+
+ if (!cs && (flags & MY_WME))
{
- dynstr_append(s, name);
- dynstr_append(s, " ");
+ char index_file[FN_REFLEN];
+ strmov(get_charsets_dir(index_file),MY_CHARSET_INDEX);
+ my_error(EE_UNKNOWN_CHARSET, MYF(ME_BELL), cs_name, index_file);
}
-}
-
-/*
- Returns a dynamically-allocated string listing the character sets
- requested.
-
- SYNOPSIS
- list_charsets()
- want_flags Flags for which character sets to return:
- MY_COMPILED_SETS: Return incompiled charsets
- MY_INDEX_SETS:
- MY_LOADED_SETS:
-
- NOTES
- The caller is responsible for freeing the memory.
+ DBUG_RETURN(cs);
+}
- RETURN
- A string with available character sets separated by space
-*/
-
-char * list_charsets(myf want_flags)
+ulong escape_string_for_mysql(CHARSET_INFO *charset_info, char *to,
+ const char *from, ulong length)
{
- DYNAMIC_STRING s;
- char *result;
-
- (void)init_available_charsets(MYF(0));
- init_dynamic_string(&s, NullS, 256, 1024);
-
- if (want_flags & MY_COMPILED_SETS)
+ const char *to_start= to;
+ const char *end;
+#ifdef USE_MB
+ my_bool use_mb_flag= use_mb(charset_info);
+#endif
+ for (end= from + length; from != end; from++)
{
- CHARSET_INFO *cs;
- for (cs = compiled_charsets; cs->number > 0; cs++)
+#ifdef USE_MB
+ int l;
+ if (use_mb_flag && (l= my_ismbchar(charset_info, from, end)))
{
- dynstr_append(&s, cs->name);
- dynstr_append(&s, " ");
+ while (l--)
+ *to++= *from++;
+ from--;
+ continue;
}
- }
-
- if (want_flags & MY_CONFIG_SETS)
- {
- CS_ID **charset;
- char buf[FN_REFLEN];
- MY_STAT status;
-
- if ((charset=available_charsets))
+ /*
+ If the next character appears to begin a multi-byte character, we
+ escape that first byte of that apparent multi-byte character. (The
+ character just looks like a multi-byte character -- if it were actually
+ a multi-byte character, it would have been passed through in the test
+ above.)
+
+ Without this check, we can create a problem by converting an invalid
+ multi-byte character into a valid one. For example, 0xbf27 is not
+ a valid GBK character, but 0xbf5c is. (0x27 = ', 0x5c = \)
+ */
+ if (use_mb_flag && (l= my_mbcharlen(charset_info, *from)) > 1)
{
- for (; *charset; charset++)
- {
- if (charset_in_string((*charset)->name, &s))
- continue;
- get_charset_conf_name((*charset)->number, buf);
- if (!my_stat(buf, &status, MYF(0)))
- continue; /* conf file doesn't exist */
- dynstr_append(&s, (*charset)->name);
- dynstr_append(&s, " ");
- }
+ *to++= '\\';
+ *to++= *from;
+ continue;
+ }
+#endif
+ switch (*from) {
+ case 0: /* Must be escaped for 'mysql' */
+ *to++= '\\';
+ *to++= '0';
+ break;
+ case '\n': /* Must be escaped for logs */
+ *to++= '\\';
+ *to++= 'n';
+ break;
+ case '\r':
+ *to++= '\\';
+ *to++= 'r';
+ break;
+ case '\\':
+ *to++= '\\';
+ *to++= '\\';
+ break;
+ case '\'':
+ *to++= '\\';
+ *to++= '\'';
+ break;
+ case '"': /* Better safe than sorry */
+ *to++= '\\';
+ *to++= '"';
+ break;
+ case '\032': /* This gives problems on Win32 */
+ *to++= '\\';
+ *to++= 'Z';
+ break;
+ default:
+ *to++= *from;
}
}
-
- if (want_flags & MY_INDEX_SETS)
- {
- CS_ID **charset;
- for (charset = available_charsets; *charset; charset++)
- charset_append(&s, (*charset)->name);
- }
-
- if (want_flags & MY_LOADED_SETS)
- {
- uint i;
- for (i = 0; i < cs_info_table.elements; i++)
- charset_append(&s,
- dynamic_element(&cs_info_table, i, CHARSET_INFO *)->name);
- }
- if (s.length)
- s.length--; /* Remove end space */
- result= my_strdup_with_length(s.str, s.length, MYF(MY_WME));
- dynstr_free(&s);
-
- return result;
+ *to= 0;
+ return (ulong) (to - to_start);
}
-/****************************************************************************
-* Code for debugging.
-****************************************************************************/
+#ifdef BACKSLASH_MBTAIL
+static CHARSET_INFO *fs_cset_cache= NULL;
-static void _print_array(uint8 *data, uint size)
+CHARSET_INFO *fs_character_set()
{
- uint i;
- for (i = 0; i < size; ++i)
+ if (!fs_cset_cache)
{
- if (i == 0 || i % 16 == size % 16) printf(" ");
- printf(" %02x", data[i]);
- if ((i+1) % 16 == size % 16) printf("\n");
+ char buf[10]= "cp";
+ GetLocaleInfo(LOCALE_SYSTEM_DEFAULT, LOCALE_IDEFAULTANSICODEPAGE,
+ buf+2, sizeof(buf)-3);
+ /*
+ We cannot call get_charset_by_name here
+ because fs_character_set() is executed before
+ LOCK_THD_charset mutex initialization, which
+ is used inside get_charset_by_name.
+ As we're now interested in cp932 only,
+ let's just detect it using strcmp().
+ */
+ fs_cset_cache= !strcmp(buf, "cp932") ?
+ &my_charset_cp932_japanese_ci : &my_charset_bin;
}
+ return fs_cset_cache;
}
+#endif
+
+
+/**
+ @brief Find compatible character set with ctype.
+
+ @param[in] original_cs Original character set
+
+ @note
+ 128 my_charset_ucs2_general_uca ->192 my_charset_utf8_general_uca_ci
+ 129 my_charset_ucs2_icelandic_uca_ci ->193 my_charset_utf8_icelandic_uca_ci
+ 130 my_charset_ucs2_latvian_uca_ci ->194 my_charset_utf8_latvian_uca_ci
+ 131 my_charset_ucs2_romanian_uca_ci ->195 my_charset_utf8_romanian_uca_ci
+ 132 my_charset_ucs2_slovenian_uca_ci ->196 my_charset_utf8_slovenian_uca_ci
+ 133 my_charset_ucs2_polish_uca_ci ->197 my_charset_utf8_polish_uca_ci
+ 134 my_charset_ucs2_estonian_uca_ci ->198 my_charset_utf8_estonian_uca_ci
+ 135 my_charset_ucs2_spanish_uca_ci ->199 my_charset_utf8_spanish_uca_ci
+ 136 my_charset_ucs2_swedish_uca_ci ->200 my_charset_utf8_swedish_uca_ci
+ 137 my_charset_ucs2_turkish_uca_ci ->201 my_charset_utf8_turkish_uca_ci
+ 138 my_charset_ucs2_czech_uca_ci ->202 my_charset_utf8_czech_uca_ci
+ 139 my_charset_ucs2_danish_uca_ci ->203 my_charset_utf8_danish_uca_ci
+ 140 my_charset_ucs2_lithuanian_uca_ci->204 my_charset_utf8_lithuanian_uca_ci
+ 141 my_charset_ucs2_slovak_uca_ci ->205 my_charset_utf8_slovak_uca_ci
+ 142 my_charset_ucs2_spanish2_uca_ci ->206 my_charset_utf8_spanish2_uca_ci
+ 143 my_charset_ucs2_roman_uca_ci ->207 my_charset_utf8_roman_uca_ci
+ 144 my_charset_ucs2_persian_uca_ci ->208 my_charset_utf8_persian_uca_ci
+
+ @return Compatible character set or NULL.
+*/
-/* _print_csinfo is called from test_charset.c */
-void _print_csinfo(CHARSET_INFO *cs)
+CHARSET_INFO *get_compatible_charset_with_ctype(CHARSET_INFO *original_cs)
{
- printf("%s #%d\n", cs->name, cs->number);
- printf("ctype:\n"); _print_array(cs->ctype, 257);
- printf("to_lower:\n"); _print_array(cs->to_lower, 256);
- printf("to_upper:\n"); _print_array(cs->to_upper, 256);
- printf("sort_order:\n"); _print_array(cs->sort_order, 256);
- printf("collate: %3s (%d, %p, %p, %p, %p, %p)\n",
- cs->strxfrm_multiply ? "yes" : "no",
- cs->strxfrm_multiply,
- cs->strcoll,
- cs->strxfrm,
- cs->strnncoll,
- cs->strnxfrm,
- cs->like_range);
- printf("multi-byte: %3s (%d, %p, %p, %p)\n",
- cs->mbmaxlen ? "yes" : "no",
- cs->mbmaxlen,
- cs->ismbchar,
- cs->ismbhead,
- cs->mbcharlen);
+ CHARSET_INFO *compatible_cs= 0;
+ DBUG_ENTER("get_compatible_charset_with_ctype");
+ if (!strcmp(original_cs->csname, "ucs2") &&
+ (compatible_cs= get_charset(original_cs->number + 64, MYF(0))) &&
+ (!compatible_cs->ctype ||
+ strcmp(original_cs->name + 4, compatible_cs->name + 4)))
+ compatible_cs= 0;
+ DBUG_RETURN(compatible_cs);
}
diff --git a/mysys/charset2html.c b/mysys/charset2html.c
new file mode 100644
index 00000000000..96862ff16a1
--- /dev/null
+++ b/mysys/charset2html.c
@@ -0,0 +1,177 @@
+/* Copyright (C) 2000 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/*
+ Written by Alexander Barkov to check what
+ a charset is in your favorite web browser
+*/
+
+#include <my_global.h>
+#include <m_ctype.h>
+#include <my_sys.h>
+#include <mysql_version.h>
+
+#include <stdio.h>
+
+typedef struct char_info_st
+{
+ int cod;
+ int srt;
+ int uni;
+ int low;
+ int upp;
+ int ctp;
+} MY_CH;
+
+static int chcmp(const void *vf, const void *vs)
+{
+ const MY_CH *f=vf;
+ const MY_CH *s=vs;
+
+ return f->srt-s->srt ? f->srt-s->srt : f->uni-s->uni;
+}
+
+static void print_cs(CHARSET_INFO *cs)
+{
+ uint i;
+ int srt;
+ int clr=0;
+ MY_CH ch[256];
+
+ printf("<HTML>\n");
+ printf("<HEAD>\n");
+ printf("</HEAD>\n");
+ printf("<BODY><PRE>\n");
+ printf("Charset %s\n",cs->name);
+
+ printf("<TABLE>\n");
+ printf("<TR><TH>Code<TH>Uni<TH>Sort<TH>Ctype<TH>Ch<TH>Lo<TH>Up</TR>");
+
+ for (i=0; i<256; i++)
+ {
+ ch[i].cod=i;
+ ch[i].srt=cs->sort_order ? cs->sort_order[i] : i;
+ ch[i].uni=cs->tab_to_uni[i];
+ ch[i].low=cs->tab_to_uni[cs->to_lower[i]];
+ ch[i].upp=cs->tab_to_uni[cs->to_upper[i]];
+ ch[i].ctp=cs->ctype[i+1];
+ }
+
+ qsort(ch,256,sizeof(MY_CH),&chcmp);
+ srt=ch[0].srt;
+
+ for (i=0; i<256; i++)
+ {
+ clr = (srt!=ch[i].srt) ? !clr : clr;
+
+ printf("<TR bgcolor=#%s>",clr ? "DDDDDD" : "EEEE99");
+ printf("<TD>%02X",ch[i].cod);
+ printf("<TD>%04X",ch[i].uni);
+ printf("<TD>%02X",ch[i].srt);
+
+ printf("<TD>%s%s%s%s%s%s%s%s",
+ ch[i].ctp & _MY_U ? "U" : "",
+ ch[i].ctp & _MY_L ? "L" : "",
+ ch[i].ctp & _MY_NMR ? "N" : "",
+ ch[i].ctp & _MY_SPC ? "S" : "",
+ ch[i].ctp & _MY_PNT ? "P" : "",
+ ch[i].ctp & _MY_CTR ? "C" : "",
+ ch[i].ctp & _MY_B ? "B" : "",
+ ch[i].ctp & _MY_X ? "X" : "");
+
+ if ((ch[i].uni >= 0x80) && (ch[i].uni <= 0x9F))
+ {
+ /*
+ Control characters 0x0080..0x009F are dysplayed by some
+ browers as if they were letters. Don't print them to
+ avoid confusion.
+ */
+ printf("<TD>ctrl<TD>ctrl<TD>ctrl");
+ }
+ else
+ {
+ printf("<TD>&#%d;",ch[i].uni);
+ printf("<TD>&#%d;",ch[i].low);
+ printf("<TD>&#%d;",ch[i].upp);
+ }
+ printf("</TR>\n");
+ srt=ch[i].srt;
+ }
+ printf("</TABLE>\n");
+ printf("</PRE></BODY>\n");
+ printf("</HTML>\n");
+}
+
+static void print_index()
+{
+ CHARSET_INFO **cs;
+ int clr=0;
+
+ get_charset_by_name("",MYF(0)); /* To execute init_available_charsets */
+
+ printf("All charsets\n");
+ printf("<table border=1>\n");
+ printf("<tr bgcolor=EEEE99><th>ID<th>Charset<th>Collation<th>Def<th>Bin<th>Com<th>Comment\n");
+ for (cs=all_charsets ; cs < all_charsets+256; cs++)
+ {
+ if (!cs[0])
+ continue;
+ printf("<tr bgcolor=#%s><td><a href=\"?%s\">%d</a><td>%s<td>%s<td>%s<td>%s<td>%s<td>%s\n",
+ (clr= !clr) ? "DDDDDD" : "EEEE99",
+ cs[0]->name,cs[0]->number,cs[0]->csname,
+ cs[0]->name,
+ (cs[0]->state & MY_CS_PRIMARY) ? "def " : "&nbsp;",
+ (cs[0]->state & MY_CS_BINSORT) ? "bin " : "&nbsp;",
+ (cs[0]->state & MY_CS_COMPILED) ? "com " : "&nbsp;",
+ cs[0]->comment);
+ }
+ printf("</table>\n");
+}
+
+int main(int argc, char **argv) {
+ const char *the_set = NULL;
+ int argcnt = 1;
+ CHARSET_INFO *cs;
+
+ if (getenv("SCRIPT_NAME"))
+ {
+ printf("Content-Type: text/html\r\n\r\n");
+ }
+ my_init();
+
+ if (argc > argcnt && argv[argcnt][0] == '-' && argv[argcnt][1] == '#')
+ DBUG_PUSH(argv[argcnt++]+2);
+
+ if (argc > argcnt)
+ the_set = argv[argcnt++];
+
+ if (argc > argcnt)
+ charsets_dir = argv[argcnt++];
+
+ if (!the_set)
+ {
+ print_index();
+ return 0;
+ }
+
+ if (!(cs= get_charset_by_name(the_set, MYF(MY_WME))))
+ return 1;
+
+ print_cs(cs);
+
+ return 0;
+}
diff --git a/mysys/checksum.c b/mysys/checksum.c
index 1dd135c7ad9..92ec3d550f1 100644
--- a/mysys/checksum.c
+++ b/mysys/checksum.c
@@ -19,19 +19,24 @@
#include "my_sys.h"
/*
- Calculate a long checksum for a memoryblock. Used to verify pack_isam
-
+ Calculate a long checksum for a memoryblock.
+
SYNOPSIS
- checksum()
- mem Pointer to memory block
- count Count of bytes
+ my_checksum()
+ crc start value for crc
+ pos pointer to memory block
+ length length of the block
*/
-ulong checksum(const byte *mem, uint count)
+ha_checksum my_checksum(ha_checksum crc, const byte *pos, uint length)
{
- ulong crc;
- for (crc= 0; count-- ; mem++)
- crc= ((crc << 1) + *((uchar*) mem)) +
- test(crc & ((ulong) 1L << (8*sizeof(ulong)-1)));
+#ifdef NOT_USED
+ const byte *end=pos+length;
+ for ( ; pos != end ; pos++)
+ crc=((crc << 8) + *((uchar*) pos)) + (crc >> (8*sizeof(ha_checksum)-8));
return crc;
+#else
+ return (ha_checksum)crc32((uint)crc, (const uchar *)pos, length);
+#endif
}
+
diff --git a/mysys/default.c b/mysys/default.c
index 911f516dad8..fadf6efbc5b 100644
--- a/mysys/default.c
+++ b/mysys/default.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 2000 MySQL AB
+/* Copyright (C) 2000-2003 MySQL AB
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -73,12 +73,42 @@ static int search_default_file(DYNAMIC_ARRAY *args,MEM_ROOT *alloc,
static int search_default_file_with_ext(DYNAMIC_ARRAY *args, MEM_ROOT *alloc,
const char *dir, const char *ext,
const char *config_file,
- TYPELIB *group);
+ TYPELIB *group, int recursion_level);
static char *remove_end_comment(char *ptr);
/*
+ Gets --defaults-file and --defaults-extra-file options from command line.
+
+ SYNOPSIS
+ get_defaults_files()
+ argc Pointer to argc of original program
+ argv Pointer to argv of original program
+ defaults --defaults-file option
+ extra_defaults --defaults-extra-file option
+
+ RETURN
+ defaults and extra_defaults will be set to appropriate items
+ of argv array, or to NULL if there are no such options
+*/
+
+void get_defaults_files(int argc, char **argv,
+ char **defaults, char **extra_defaults)
+{
+ *defaults=0;
+ *extra_defaults=0;
+ if (argc >= 2)
+ {
+ if (is_prefix(argv[1],"--defaults-file="))
+ *defaults= argv[1];
+ else if (is_prefix(argv[1],"--defaults-extra-file="))
+ *extra_defaults= argv[1];
+ }
+}
+
+
+/*
Read options from configurations files
SYNOPSIS
@@ -111,10 +141,10 @@ static char *remove_end_comment(char *ptr);
int load_defaults(const char *conf_file, const char **groups,
- int *argc, char ***argv)
+ int *argc, char ***argv)
{
DYNAMIC_ARRAY args;
- const char **dirs, *forced_default_file;
+ const char **dirs, *forced_default_file, *forced_extra_defaults;
TYPELIB group;
my_bool found_print_defaults=0;
uint args_used=0;
@@ -143,21 +173,15 @@ int load_defaults(const char *conf_file, const char **groups,
DBUG_RETURN(0);
}
- /* Check if we want to force the use a specific default file */
- forced_default_file=0;
- if (*argc >= 2)
- {
- if (is_prefix(argv[0][1],"--defaults-file="))
- {
- forced_default_file=strchr(argv[0][1],'=')+1;
- args_used++;
- }
- else if (is_prefix(argv[0][1],"--defaults-extra-file="))
- {
- defaults_extra_file=strchr(argv[0][1],'=')+1;
- args_used++;
- }
- }
+ get_defaults_files(*argc, *argv,
+ (char **)&forced_default_file,
+ (char **)&forced_extra_defaults);
+ if (forced_default_file)
+ forced_default_file= strchr(forced_default_file,'=')+1;
+ if (forced_extra_defaults)
+ defaults_extra_file= strchr(forced_extra_defaults,'=')+1;
+
+ args_used+= (forced_default_file ? 1 : 0) + (forced_extra_defaults ? 1 : 0);
group.count=0;
group.name= "defaults";
@@ -170,10 +194,13 @@ int load_defaults(const char *conf_file, const char **groups,
if (forced_default_file)
{
if ((error= search_default_file_with_ext(&args, &alloc, "", "",
- forced_default_file,
- &group)))
+ forced_default_file,
+ &group, 0)) < 0)
+ goto err;
+ if (error > 0)
{
- fprintf(stderr, "Error reading '%s' file.\n", forced_default_file);
+ fprintf(stderr, "Could not open required defaults file: %s\n",
+ forced_default_file);
goto err;
}
}
@@ -211,11 +238,8 @@ int load_defaults(const char *conf_file, const char **groups,
else if (defaults_extra_file)
{
if (search_default_file(&args, &alloc, NullS, defaults_extra_file,
- &group))
- {
- fprintf(stderr, "Error reading '%s' file.\n", defaults_extra_file);
+ &group) < 0)
goto err; /* Fatal error */
- }
}
}
}
@@ -229,9 +253,9 @@ int load_defaults(const char *conf_file, const char **groups,
res= (char**) (ptr+sizeof(alloc));
/* copy name + found arguments + command line arguments to new array */
- res[0]=argv[0][0];
+ res[0]= argv[0][0]; /* Name MUST be set, even by embedded library */
memcpy((gptr) (res+1), args.buffer, args.elements*sizeof(char*));
- /* Skipp --defaults-file and --defaults-extra-file */
+ /* Skip --defaults-file and --defaults-extra-file */
(*argc)-= args_used;
(*argv)+= args_used;
@@ -239,11 +263,12 @@ int load_defaults(const char *conf_file, const char **groups,
if (*argc >= 2 && !strcmp(argv[0][1],"--print-defaults"))
{
found_print_defaults=1;
- --*argc; ++*argv; /* skipp argument */
+ --*argc; ++*argv; /* skip argument */
}
- memcpy((gptr) (res+1+args.elements), (char*) ((*argv)+1),
- (*argc-1)*sizeof(char*));
+ if (*argc)
+ memcpy((gptr) (res+1+args.elements), (char*) ((*argv)+1),
+ (*argc-1)*sizeof(char*));
res[args.elements+ *argc]=0; /* last null */
(*argc)+=args.elements;
@@ -265,6 +290,7 @@ int load_defaults(const char *conf_file, const char **groups,
err:
fprintf(stderr,"Fatal error in defaults handling. Program aborted\n");
exit(1);
+ return 0; /* Keep compiler happy */
}
@@ -286,7 +312,7 @@ static int search_default_file(DYNAMIC_ARRAY *args, MEM_ROOT *alloc,
{
int error;
if ((error= search_default_file_with_ext(args, alloc, dir, *ext,
- config_file, group)) < 0)
+ config_file, group, 0)) < 0)
return error;
}
return 0;
@@ -294,16 +320,68 @@ static int search_default_file(DYNAMIC_ARRAY *args, MEM_ROOT *alloc,
/*
+ Skip over keyword and get argument after keyword
+
+ SYNOPSIS
+ get_argument()
+ keyword Include directive keyword
+ kwlen Length of keyword
+ ptr Pointer to the keword in the line under process
+ line line number
+
+ RETURN
+ 0 error
+ # Returns pointer to the argument after the keyword.
+*/
+
+static char *get_argument(const char *keyword, uint kwlen,
+ char *ptr, char *name, uint line)
+{
+ char *end;
+
+ /* Skip over "include / includedir keyword" and following whitespace */
+
+ for (ptr+= kwlen - 1;
+ my_isspace(&my_charset_latin1, ptr[0]);
+ ptr++)
+ {}
+
+ /*
+ Trim trailing whitespace from directory name
+ The -1 below is for the newline added by fgets()
+ Note that my_isspace() is true for \r and \n
+ */
+ for (end= ptr + strlen(ptr) - 1;
+ my_isspace(&my_charset_latin1, *(end - 1));
+ end--)
+ {}
+ end[0]= 0; /* Cut off end space */
+
+ /* Print error msg if there is nothing after !include* directive */
+ if (end <= ptr)
+ {
+ fprintf(stderr,
+ "error: Wrong '!%s' directive in config file: %s at line %d\n",
+ keyword, name, line);
+ return 0;
+ }
+ return ptr;
+}
+
+
+/*
Open a configuration file (if exists) and read given options from it
-
+
SYNOPSIS
search_default_file_with_ext()
args Store pointer to found options here
alloc Allocate strings in this object
dir directory to read
- config_file Name of configuration file
ext Extension for configuration file
+ config_file Name of configuration file
group groups to read
+ recursion_level the level of recursion, got while processing
+ "!include" or "!includedir"
RETURN
0 Success
@@ -315,12 +393,18 @@ static int search_default_file(DYNAMIC_ARRAY *args, MEM_ROOT *alloc,
static int search_default_file_with_ext(DYNAMIC_ARRAY *args, MEM_ROOT *alloc,
const char *dir, const char *ext,
const char *config_file,
- TYPELIB *group)
+ TYPELIB *group, int recursion_level)
{
- char name[FN_REFLEN+10],buff[4096],*ptr,*end,*value,*tmp;
+ char name[FN_REFLEN + 10], buff[4096], *ptr, *end, *value, *tmp, **tmp_ext;
+ static const char includedir_keyword[]= "includedir";
+ static const char include_keyword[]= "include";
+ const int max_recursion_level= 10;
FILE *fp;
- uint line=0;
- my_bool read_values=0,found_group=0;
+ uint line= 0;
+ my_bool read_values= 0, found_group= 0;
+ uint i;
+ MY_DIR *search_dir;
+ FILEINFO *search_file;
if ((dir ? strlen(dir) : 0 )+strlen(config_file) >= FN_REFLEN-3)
return 0; /* Ignore wrong paths */
@@ -349,22 +433,101 @@ static int search_default_file_with_ext(DYNAMIC_ARRAY *args, MEM_ROOT *alloc,
if ((stat_info.st_mode & S_IWOTH) &&
(stat_info.st_mode & S_IFMT) == S_IFREG)
{
- fprintf(stderr, "warning: World-writeable config file %s is ignored\n",
+ fprintf(stderr, "warning: World-writable config file %s is ignored\n",
name);
return 0;
}
}
#endif
- if (!(fp = my_fopen(fn_format(name,name,"","",4),O_RDONLY,MYF(0))))
+ if (!(fp= my_fopen(fn_format(name, name, "", "", 4), O_RDONLY, MYF(0))))
return 0; /* Ignore wrong files */
- while (fgets(buff,sizeof(buff)-1,fp))
+ while (fgets(buff, sizeof(buff) - 1, fp))
{
line++;
/* Ignore comment and empty lines */
- for (ptr=buff ; isspace(*ptr) ; ptr++ ) ;
+ for (ptr= buff; my_isspace(&my_charset_latin1, *ptr); ptr++)
+ {}
+
if (*ptr == '#' || *ptr == ';' || !*ptr)
continue;
+
+ /* Configuration File Directives */
+ if ((*ptr == '!'))
+ {
+ if (recursion_level >= max_recursion_level)
+ {
+ for (end= ptr + strlen(ptr) - 1;
+ my_isspace(&my_charset_latin1, *(end - 1));
+ end--)
+ {}
+ end[0]= 0;
+ fprintf(stderr,
+ "Warning: skipping '%s' directive as maximum include"
+ "recursion level was reached in file %s at line %d\n",
+ ptr, name, line);
+ continue;
+ }
+
+ /* skip over `!' and following whitespace */
+ for (++ptr; my_isspace(&my_charset_latin1, ptr[0]); ptr++)
+ {}
+
+ if ((!strncmp(ptr, includedir_keyword,
+ sizeof(includedir_keyword) - 1)) &&
+ my_isspace(&my_charset_latin1, ptr[sizeof(includedir_keyword) - 1]))
+ {
+ if (!(ptr= get_argument(includedir_keyword,
+ sizeof(includedir_keyword),
+ ptr, name, line)))
+ goto err;
+
+ if (!(search_dir= my_dir(ptr, MYF(MY_WME))))
+ goto err;
+
+ for (i= 0; i < (uint) search_dir->number_off_files; i++)
+ {
+ search_file= search_dir->dir_entry + i;
+ ext= fn_ext(search_file->name);
+
+ /* check extension */
+ for (tmp_ext= (char**) f_extensions; *tmp_ext; *tmp_ext++)
+ {
+ if (!strcmp(ext, *tmp_ext))
+ break;
+ }
+
+ if (*tmp_ext)
+ {
+ if (!(tmp= alloc_root(alloc, 2 + strlen(search_file->name)
+ + strlen(ptr))))
+ goto err;
+
+ fn_format(tmp, search_file->name, ptr, "",
+ MY_UNPACK_FILENAME | MY_SAFE_PATH);
+
+ search_default_file_with_ext(args, alloc, "", "", tmp, group,
+ recursion_level + 1);
+ }
+ }
+
+ my_dirend(search_dir);
+ }
+ else if ((!strncmp(ptr, include_keyword, sizeof(include_keyword) - 1)) &&
+ my_isspace(&my_charset_latin1, ptr[sizeof(include_keyword)-1]))
+ {
+ if (!(ptr= get_argument(include_keyword,
+ sizeof(include_keyword), ptr,
+ name, line)))
+ goto err;
+
+ search_default_file_with_ext(args, alloc, "", "", ptr, group,
+ recursion_level + 1);
+ }
+
+ continue;
+ }
+
if (*ptr == '[') /* Group name */
{
found_group=1;
@@ -375,7 +538,7 @@ static int search_default_file_with_ext(DYNAMIC_ARRAY *args, MEM_ROOT *alloc,
name,line);
goto err;
}
- for ( ; isspace(end[-1]) ; end--) ; /* Remove end space */
+ for ( ; my_isspace(&my_charset_latin1,end[-1]) ; end--) ;/* Remove end space */
end[0]=0;
read_values=find_type(ptr,group,3) > 0;
continue;
@@ -392,8 +555,7 @@ static int search_default_file_with_ext(DYNAMIC_ARRAY *args, MEM_ROOT *alloc,
end= remove_end_comment(ptr);
if ((value= strchr(ptr, '=')))
end= value; /* Option without argument */
- for ( ; isspace(end[-1]) ; end--) ;
-
+ for ( ; my_isspace(&my_charset_latin1,end[-1]) ; end--) ;
if (!value)
{
if (!(tmp=alloc_root(alloc,(uint) (end-ptr)+3)))
@@ -406,18 +568,20 @@ static int search_default_file_with_ext(DYNAMIC_ARRAY *args, MEM_ROOT *alloc,
{
/* Remove pre- and end space */
char *value_end;
- for (value++ ; isspace(*value); value++) ;
+ for (value++ ; my_isspace(&my_charset_latin1,*value); value++) ;
value_end=strend(value);
/*
We don't have to test for value_end >= value as we know there is
an '=' before
*/
- for ( ; isspace(value_end[-1]) ; value_end--) ;
+ for ( ; my_isspace(&my_charset_latin1,value_end[-1]) ; value_end--) ;
if (value_end < value) /* Empty string */
value_end=value;
/* remove quotes around argument */
- if ((*value == '\"' || *value == '\'') && *value == value_end[-1])
+ if ((*value == '\"' || *value == '\'') && /* First char is quote */
+ (value + 1 < value_end ) && /* String is longer than 1 */
+ *value == value_end[-1] ) /* First char is equal to last char */
{
value++;
value_end--;
@@ -494,7 +658,8 @@ static char *remove_end_comment(char *ptr)
else if (quote == *ptr)
quote= 0;
}
- if (!quote && *ptr == '#') /* We are not inside a string */
+ /* We are not inside a string */
+ if (!quote && *ptr == '#')
{
*ptr= 0;
return ptr;
diff --git a/mysys/errors.c b/mysys/errors.c
index e21609f6e94..05436c9a212 100644
--- a/mysys/errors.c
+++ b/mysys/errors.c
@@ -49,6 +49,7 @@ const char * NEAR globerrs[GLOBERRS]=
"Can't create symlink '%s' pointing at '%s' (Error %d)",
"Error on realpath() on '%s' (Error %d)",
"Can't sync file '%s' to disk (Errcode: %d)",
+ "Collation '%s' is not a compiled collation and is not specified in the '%s' file",
};
void init_glob_errs(void)
@@ -83,11 +84,12 @@ void init_glob_errs()
EE(EE_OPEN_WARNING) = "%d files and %d streams is left open\n";
EE(EE_DISK_FULL) = "Disk is full writing '%s'. Waiting for someone to free space...";
EE(EE_CANT_MKDIR) ="Can't create directory '%s' (Errcode: %d)";
- EE(EE_UNKNOWN_CHARSET)= "Character set is not a compiled character set and is not specified in the %s file";
+ EE(EE_UNKNOWN_CHARSET)= "Character set '%s' is not a compiled character set and is not specified in the %s file";
EE(EE_OUT_OF_FILERESOURCES)="Out of resources when opening file '%s' (Errcode: %d)";
EE(EE_CANT_READLINK)= "Can't read value for symlink '%s' (Error %d)";
EE(EE_CANT_SYMLINK)= "Can't create symlink '%s' pointing at '%s' (Error %d)";
EE(EE_REALPATH)= "Error on realpath() on '%s' (Error %d)";
EE(EE_SYNC)= "Can't sync file '%s' to disk (Errcode: %d)";
+ EE(EE_UNKNOWN_COLLATION)= "Collation '%s' is not a compiled collation and is not specified in the %s file";
}
#endif
diff --git a/mysys/hash.c b/mysys/hash.c
index 3ee575a9c98..1ba80dc6fd3 100644
--- a/mysys/hash.c
+++ b/mysys/hash.c
@@ -29,54 +29,112 @@
#define HIGHFIND 4
#define HIGHUSED 8
+typedef struct st_hash_info {
+ uint next; /* index to next key */
+ byte *data; /* data for current entry */
+} HASH_LINK;
+
static uint hash_mask(uint hashnr,uint buffmax,uint maxlength);
static void movelink(HASH_LINK *array,uint pos,uint next_link,uint newlink);
-static uint calc_hashnr(const byte *key,uint length);
-static uint calc_hashnr_caseup(const byte *key,uint length);
-static int hashcmp(HASH *hash,HASH_LINK *pos,const byte *key,uint length);
+static int hashcmp(const HASH *hash, HASH_LINK *pos, const byte *key,
+ uint length);
+static uint calc_hash(const HASH *hash, const byte *key, uint length)
+{
+ ulong nr1=1, nr2=4;
+ hash->charset->coll->hash_sort(hash->charset,(uchar*) key,length,&nr1,&nr2);
+ return nr1;
+}
-my_bool _hash_init(HASH *hash,uint size,uint key_offset,uint key_length,
- hash_get_key get_key,
- void (*free_element)(void*),uint flags CALLER_INFO_PROTO)
+my_bool
+_hash_init(HASH *hash,CHARSET_INFO *charset,
+ uint size,uint key_offset,uint key_length,
+ hash_get_key get_key,
+ void (*free_element)(void*),uint flags CALLER_INFO_PROTO)
{
DBUG_ENTER("hash_init");
- DBUG_PRINT("enter",("hash: %lx size: %d",hash,size));
+ DBUG_PRINT("enter",("hash: 0x%lx size: %d",hash,size));
hash->records=0;
if (my_init_dynamic_array_ci(&hash->array,sizeof(HASH_LINK),size,0))
{
hash->free=0; /* Allow call to hash_free */
- DBUG_RETURN(TRUE);
+ DBUG_RETURN(1);
}
hash->key_offset=key_offset;
hash->key_length=key_length;
hash->blength=1;
- hash->current_record= NO_RECORD; /* For the future */
hash->get_key=get_key;
hash->free=free_element;
hash->flags=flags;
- if (flags & HASH_CASE_INSENSITIVE)
- hash->calc_hashnr=calc_hashnr_caseup;
- else
- hash->calc_hashnr=calc_hashnr;
+ hash->charset=charset;
DBUG_RETURN(0);
}
-void hash_free(HASH *hash)
+/*
+ Call hash->free on all elements in hash.
+
+ SYNOPSIS
+ hash_free_elements()
+ hash hash table
+
+ NOTES:
+ Sets records to 0
+*/
+
+static inline void hash_free_elements(HASH *hash)
{
- DBUG_ENTER("hash_free");
if (hash->free)
{
- uint i,records;
HASH_LINK *data=dynamic_element(&hash->array,0,HASH_LINK*);
- for (i=0,records=hash->records ; i < records ; i++)
- (*hash->free)(data[i].data);
- hash->free=0;
+ HASH_LINK *end= data + hash->records;
+ while (data < end)
+ (*hash->free)((data++)->data);
}
- delete_dynamic(&hash->array);
hash->records=0;
+}
+
+
+/*
+ Free memory used by hash.
+
+ SYNOPSIS
+ hash_free()
+ hash the hash to delete elements of
+
+ NOTES: Hash can't be reused wuthing calling hash_init again.
+*/
+
+void hash_free(HASH *hash)
+{
+ DBUG_ENTER("hash_free");
+ DBUG_PRINT("enter",("hash: 0x%lxd",hash));
+
+ hash_free_elements(hash);
+ hash->free= 0;
+ delete_dynamic(&hash->array);
+ DBUG_VOID_RETURN;
+}
+
+
+/*
+ Delete all elements from the hash (the hash itself is to be reused).
+
+ SYNOPSIS
+ my_hash_reset()
+ hash the hash to delete elements of
+*/
+
+void my_hash_reset(HASH *hash)
+{
+ DBUG_ENTER("my_hash_reset");
+ DBUG_PRINT("enter",("hash: 0x%lxd",hash));
+
+ hash_free_elements(hash);
+ reset_dynamic(&hash->array);
+ /* Set row pointers so that the hash can be reused at once */
+ hash->blength= 1;
DBUG_VOID_RETURN;
}
@@ -88,7 +146,8 @@ void hash_free(HASH *hash)
*/
static inline char*
-hash_key(HASH *hash,const byte *record,uint *length,my_bool first)
+hash_key(const HASH *hash, const byte *record, uint *length,
+ my_bool first)
{
if (hash->get_key)
return (*hash->get_key)(record,length,first);
@@ -104,110 +163,53 @@ static uint hash_mask(uint hashnr,uint buffmax,uint maxlength)
return (hashnr & ((buffmax >> 1) -1));
}
-static uint hash_rec_mask(HASH *hash,HASH_LINK *pos,uint buffmax,
- uint maxlength)
+static uint hash_rec_mask(const HASH *hash, HASH_LINK *pos,
+ uint buffmax, uint maxlength)
{
uint length;
byte *key= (byte*) hash_key(hash,pos->data,&length,0);
- return hash_mask((*hash->calc_hashnr)(key,length),buffmax,maxlength);
-}
-
-#ifndef NEW_HASH_FUNCTION
-
- /* Calc hashvalue for a key */
-
-static uint calc_hashnr(const byte *key,uint length)
-{
- register uint nr=1, nr2=4;
- while (length--)
- {
- nr^= (((nr & 63)+nr2)*((uint) (uchar) *key++))+ (nr << 8);
- nr2+=3;
- }
- return((uint) nr);
-}
-
- /* Calc hashvalue for a key, case indepenently */
-
-static uint calc_hashnr_caseup(const byte *key,uint length)
-{
- register uint nr=1, nr2=4;
- while (length--)
- {
- nr^= (((nr & 63)+nr2)*((uint) (uchar) toupper(*key++)))+ (nr << 8);
- nr2+=3;
- }
- return((uint) nr);
-}
-
-#else
-
-/*
- * Fowler/Noll/Vo hash
- *
- * The basis of the hash algorithm was taken from an idea sent by email to the
- * IEEE Posix P1003.2 mailing list from Phong Vo (kpv@research.att.com) and
- * Glenn Fowler (gsf@research.att.com). Landon Curt Noll (chongo@toad.com)
- * later improved on their algorithm.
- *
- * The magic is in the interesting relationship between the special prime
- * 16777619 (2^24 + 403) and 2^32 and 2^8.
- * This works well on both numbers and strings.
- */
-
-uint calc_hashnr(const byte *key, uint len)
-{
- const byte *end=key+len;
- uint hash;
- for (hash = 0; key < end; key++)
- {
- hash *= 16777619;
- hash ^= (uint) *(uchar*) key;
- }
- return (hash);
-}
-
-uint calc_hashnr_caseup(const byte *key, uint len)
-{
- const byte *end=key+len;
- uint hash;
- for (hash = 0; key < end; key++)
- {
- hash *= 16777619;
- hash ^= (uint) (uchar) toupper(*key);
- }
- return (hash);
+ return hash_mask(calc_hash(hash,key,length),buffmax,maxlength);
}
-#endif
/* for compilers which can not handle inline */
+static
#if !defined(__USLC__) && !defined(__sgi)
-static inline
+inline
#endif
unsigned int rec_hashnr(HASH *hash,const byte *record)
{
uint length;
byte *key= (byte*) hash_key(hash,record,&length,0);
- return (*hash->calc_hashnr)(key,length);
+ return calc_hash(hash,key,length);
}
- /* Search after a record based on a key */
- /* Sets info->current_ptr to found record */
+gptr hash_search(const HASH *hash, const byte *key, uint length)
+{
+ HASH_SEARCH_STATE state;
+ return hash_first(hash, key, length, &state);
+}
-gptr hash_search(HASH *hash,const byte *key,uint length)
+/*
+ Search after a record based on a key
+
+ NOTE
+ Assigns the number of the found record to HASH_SEARCH_STATE state
+*/
+
+gptr hash_first(const HASH *hash, const byte *key, uint length,
+ HASH_SEARCH_STATE *current_record)
{
HASH_LINK *pos;
uint flag,idx;
- DBUG_ENTER("hash_search");
+ DBUG_ENTER("hash_first");
flag=1;
if (hash->records)
{
- idx=hash_mask((*hash->calc_hashnr)(key,length ? length :
- hash->key_length),
+ idx=hash_mask(calc_hash(hash,key,length ? length : hash->key_length),
hash->blength,hash->records);
do
{
@@ -215,7 +217,7 @@ gptr hash_search(HASH *hash,const byte *key,uint length)
if (!hashcmp(hash,pos,key,length))
{
DBUG_PRINT("exit",("found key at %d",idx));
- hash->current_record= idx;
+ *current_record= idx;
DBUG_RETURN (pos->data);
}
if (flag)
@@ -227,31 +229,32 @@ gptr hash_search(HASH *hash,const byte *key,uint length)
}
while ((idx=pos->next) != NO_RECORD);
}
- hash->current_record= NO_RECORD;
+ *current_record= NO_RECORD;
DBUG_RETURN(0);
}
/* Get next record with identical key */
/* Can only be called if previous calls was hash_search */
-gptr hash_next(HASH *hash,const byte *key,uint length)
+gptr hash_next(const HASH *hash, const byte *key, uint length,
+ HASH_SEARCH_STATE *current_record)
{
HASH_LINK *pos;
uint idx;
- if (hash->current_record != NO_RECORD)
+ if (*current_record != NO_RECORD)
{
HASH_LINK *data=dynamic_element(&hash->array,0,HASH_LINK*);
- for (idx=data[hash->current_record].next; idx != NO_RECORD ; idx=pos->next)
+ for (idx=data[*current_record].next; idx != NO_RECORD ; idx=pos->next)
{
pos=data+idx;
if (!hashcmp(hash,pos,key,length))
{
- hash->current_record= idx;
+ *current_record= idx;
return pos->data;
}
}
- hash->current_record=NO_RECORD;
+ *current_record= NO_RECORD;
}
return 0;
}
@@ -271,22 +274,40 @@ static void movelink(HASH_LINK *array,uint find,uint next_link,uint newlink)
return;
}
- /* Compare a key in a record to a whole key. Return 0 if identical */
+/*
+ Compare a key in a record to a whole key. Return 0 if identical
+
+ SYNOPSIS
+ hashcmp()
+ hash hash table
+ pos position of hash record to use in comparison
+ key key for comparison
+ length length of key
+
+ NOTES:
+ If length is 0, comparison is done using the length of the
+ record being compared against.
+
+ RETURN
+ < 0 key of record < key
+ = 0 key of record == key
+ > 0 key of record > key
+ */
-static int hashcmp(HASH *hash,HASH_LINK *pos,const byte *key,uint length)
+static int hashcmp(const HASH *hash, HASH_LINK *pos, const byte *key,
+ uint length)
{
uint rec_keylength;
byte *rec_key= (byte*) hash_key(hash,pos->data,&rec_keylength,1);
- return (length && length != rec_keylength) ||
- (hash->flags & HASH_CASE_INSENSITIVE ?
- my_casecmp(rec_key,key,rec_keylength) :
- memcmp(rec_key,key,rec_keylength));
+ return ((length && length != rec_keylength) ||
+ my_strnncoll(hash->charset, (uchar*) rec_key, rec_keylength,
+ (uchar*) key, rec_keylength));
}
/* Write a hash-key to the hash-index */
-my_bool hash_insert(HASH *info,const byte *record)
+my_bool my_hash_insert(HASH *info,const byte *record)
{
int flag;
uint halfbuff,hash_nr,first_index,idx;
@@ -300,7 +321,6 @@ my_bool hash_insert(HASH *info,const byte *record)
if (!(empty=(HASH_LINK*) alloc_dynamic(&info->array)))
return(TRUE); /* No more memory */
- info->current_record= NO_RECORD;
data=dynamic_element(&info->array,0,HASH_LINK*);
halfbuff= info->blength >> 1;
@@ -443,7 +463,6 @@ my_bool hash_delete(HASH *hash,byte *record)
}
if ( --(hash->records) < hash->blength >> 1) hash->blength>>=1;
- hash->current_record= NO_RECORD;
lastpos=data+hash->records;
/* Remove link to record */
@@ -519,7 +538,7 @@ my_bool hash_update(HASH *hash,byte *record,byte *old_key,uint old_key_length)
/* Search after record with key */
- idx=hash_mask((*hash->calc_hashnr)(old_key,(old_key_length ?
+ idx=hash_mask(calc_hash(hash, old_key,(old_key_length ?
old_key_length :
hash->key_length)),
blength,records);
@@ -536,7 +555,6 @@ my_bool hash_update(HASH *hash,byte *record,byte *old_key,uint old_key_length)
if ((idx=pos->next) == NO_RECORD)
DBUG_RETURN(1); /* Not found in links */
}
- hash->current_record= NO_RECORD;
org_link= *pos;
empty=idx;
@@ -554,6 +572,25 @@ my_bool hash_update(HASH *hash,byte *record,byte *old_key,uint old_key_length)
previous->next=pos->next; /* unlink pos */
/* Move data to correct position */
+ if (new_index == empty)
+ {
+ /*
+ At this point record is unlinked from the old chain, thus it holds
+ random position. By the chance this position is equal to position
+ for the first element in the new chain. That means updated record
+ is the only record in the new chain.
+ */
+ if (empty != idx)
+ {
+ /*
+ Record was moved while unlinking it from the old chain.
+ Copy data to a new position.
+ */
+ data[empty]= org_link;
+ }
+ data[empty].next= NO_RECORD;
+ DBUG_RETURN(0);
+ }
pos=data+new_index;
new_pos_index=hash_rec_mask(hash,pos,blength,records);
if (new_index != new_pos_index)
@@ -586,10 +623,10 @@ byte *hash_element(HASH *hash,uint idx)
isn't changed
*/
-void hash_replace(HASH *hash, uint idx, byte *new_row)
+void hash_replace(HASH *hash, HASH_SEARCH_STATE *current_record, byte *new_row)
{
- if (idx != NO_RECORD) /* Safety */
- dynamic_element(&hash->array,idx,HASH_LINK*)->data=new_row;
+ if (*current_record != NO_RECORD) /* Safety */
+ dynamic_element(&hash->array, *current_record, HASH_LINK*)->data= new_row;
}
@@ -627,7 +664,7 @@ my_bool hash_check(HASH *hash)
if ((rec_link=hash_rec_mask(hash,hash_info,blength,records)) != i)
{
DBUG_PRINT("error",
- ("Record in wrong link at %d: Start %d Record: %lx Record-link %d", idx,i,hash_info->data,rec_link));
+ ("Record in wrong link at %d: Start %d Record: 0x%lx Record-link %d", idx,i,hash_info->data,rec_link));
error=1;
}
else
diff --git a/mysys/list.c b/mysys/list.c
index ac9e1b979a0..480c1080a45 100644
--- a/mysys/list.c
+++ b/mysys/list.c
@@ -28,7 +28,7 @@
LIST *list_add(LIST *root, LIST *element)
{
DBUG_ENTER("list_add");
- DBUG_PRINT("enter",("root: %lx element: %lx", root, element));
+ DBUG_PRINT("enter",("root: 0x%lx element: %lx", root, element));
if (root)
{
if (root->prev) /* If add in mid of list */
@@ -55,7 +55,7 @@ LIST *list_delete(LIST *root, LIST *element)
}
-void list_free(LIST *root, pbool free_data)
+void list_free(LIST *root, uint free_data)
{
LIST *next;
while (root)
@@ -109,7 +109,7 @@ int list_walk(LIST *list, list_walk_action action, gptr argument)
{
if ((error = (*action)(list->data,argument)))
return error;
- list=rest(list);
+ list=list_rest(list);
}
return 0;
}
diff --git a/mysys/mf_casecnv.c b/mysys/mf_casecnv.c
deleted file mode 100644
index c037cbd0f16..00000000000
--- a/mysys/mf_casecnv.c
+++ /dev/null
@@ -1,297 +0,0 @@
-/* Copyright (C) 2000 MySQL AB
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
-
-/*
- Functions to convert to lover_case and to upper_case in scandinavia.
-
- case_sort converts a character string to a representaion that can
- be compared by strcmp to find with is alfabetical bigger.
- (lower- and uppercase letters is compared as the same)
-*/
-
-#include "mysys_priv.h"
-#include <m_ctype.h>
-#ifndef SCO
-#include <m_string.h>
-#endif
-
-/*
- Upcase string
-
- SYNOPSIS
- str IN/OUT String to upcase
-
- RETURN VALUE
- none
- DESCRIPTION
- Function changes input parameter so all chars it consist from
- are replaced with matching one in upper case.
- String should be writable with exception read-only empty string
- constant is handled correctly.
-*/
-
-
-void caseup_str(my_string str)
-{
-#ifdef USE_MB
- if (use_mb(default_charset_info))
- {
- register uint32 l;
- register char *end=str+strlen(str);
- while (*str)
- {
- if ((l=my_ismbchar(default_charset_info, str,end))) str+=l;
- else *str=toupper(*str),++str;
- }
- }
- else
-#endif
- while (*str!=0) /* iterate till the end of string */
- {
- *str= toupper(*str);
- str++;
- }
-} /* caseup_str */
-
-
-/*
- Downcase string
-
- SYNOPSIS
- str IN/OUT String to downcase
-
- RETURN VALUE
- none
- DESCRIPTION
- Function changes input parameter so all chars it consist from
- are replaced with matching one in lower case.
- String should be writable with exception read-only empty string
- constant is handled correctly.
-*/
-
-
-void casedn_str(my_string str)
-{
-#ifdef USE_MB
- if (use_mb(default_charset_info))
- {
- register uint32 l;
- register char *end=str+strlen(str);
- while (*str)
- {
- if ((l=my_ismbchar(default_charset_info, str,end))) str+=l;
- else *str=tolower(*str),++str;
- }
- }
- else
-#endif
- while (*str!=0) /* iterate till the end of string */
- {
- *str= tolower(*str);
- str++;
- }
-} /* casedn_str */
-
-
- /* to uppercase */
-
-void caseup(my_string str, uint length)
-{
-#ifdef USE_MB
- if (use_mb(default_charset_info))
- {
- register uint32 l;
- register char *end=str+length;
- while (str<end)
- {
- if ((l=my_ismbchar(default_charset_info, str,end))) str+=l;
- else *str=toupper(*str),++str;
- }
- }
- else
-#endif
- for ( ; length>0 ; length--, str++)
- *str= toupper(*str);
-} /* caseup */
-
- /* to lowercase */
-
-void casedn(my_string str, uint length)
-{
-#ifdef USE_MB
- if (use_mb(default_charset_info))
- {
- register uint32 l;
- register char *end=str+length;
- while (str<end)
- {
- if ((l=my_ismbchar(default_charset_info, str,end))) str+=l;
- else *str=tolower(*str),++str;
- }
- }
- else
-#endif
- for ( ; length>0 ; length--, str++)
- *str= tolower(*str);
-} /* casedn */
-
- /* to sort-string that can be compared to get text in order */
-
-void case_sort(my_string str, uint length)
-{
- for ( ; length>0 ; length--, str++)
- *str= (char) my_sort_order[(uchar) *str];
-} /* case_sort */
-
- /* find string in another with no case_sensivity */
-
-/* ToDo: This function should be modified to support multibyte charset.
- However it is not used untill 3.23.5.
- Wei He (hewei@mail.ied.ac.cn)
-*/
-
-my_string my_strcasestr(const char *str, const char *search)
-{
- uchar *i,*j,*pos;
-
- pos=(uchar*) str;
-skipp:
- while (*pos != '\0')
- {
- if (toupper((uchar) *pos++) == toupper((uchar) *search))
- {
- i=(uchar*) pos; j=(uchar*) search+1;
- while (*j)
- if (toupper(*i++) != toupper(*j++)) goto skipp;
- return ((char*) pos-1);
- }
- }
- return ((my_string) 0);
-} /* strcstr */
-
-
- /* compare strings without regarding to case */
-
-int my_strcasecmp(const char *s, const char *t)
-{
-#ifdef USE_MB
- if (use_mb(default_charset_info))
- {
- register uint32 l;
- register const char *end=s+strlen(s);
- while (s<end)
- {
- if ((l=my_ismbchar(default_charset_info, s,end)))
- {
- while (l--)
- if (*s++ != *t++) return 1;
- }
- else if (my_ismbhead(default_charset_info, *t)) return 1;
- else if (toupper((uchar) *s++) != toupper((uchar) *t++)) return 1;
- }
- return *t;
- }
- else
-#endif
- {
- while (toupper((uchar) *s) == toupper((uchar) *t++))
- if (!*s++) return 0;
- return ((int) toupper((uchar) s[0]) - (int) toupper((uchar) t[-1]));
- }
-}
-
-
-int my_casecmp(const char *s, const char *t, uint len)
-{
-#ifdef USE_MB
- if (use_mb(default_charset_info))
- {
- register uint32 l;
- register const char *end=s+len;
- while (s<end)
- {
- if ((l=my_ismbchar(default_charset_info, s,end)))
- {
- while (l--)
- if (*s++ != *t++) return 1;
- }
- else if (my_ismbhead(default_charset_info, *t)) return 1;
- else if (toupper((uchar) *s++) != toupper((uchar) *t++)) return 1;
- }
- return 0;
- }
- else
-#endif
- {
- while (len-- != 0 && toupper(*s++) == toupper(*t++)) ;
- return (int) len+1;
- }
-}
-
-
-int my_strsortcmp(const char *s, const char *t)
-{
-#ifdef USE_STRCOLL
- if (use_strcoll(default_charset_info))
- return my_strcoll(default_charset_info, (uchar *)s, (uchar *)t);
- else
-#endif
- {
- while (my_sort_order[(uchar) *s] == my_sort_order[(uchar) *t++])
- if (!*s++) return 0;
- return ((int) my_sort_order[(uchar) s[0]] -
- (int) my_sort_order[(uchar) t[-1]]);
- }
-}
-
-int my_sortcmp(const char *s, const char *t, uint len)
-{
-#ifdef USE_STRCOLL
- if (use_strcoll(default_charset_info))
- return my_strnncoll(default_charset_info,
- (uchar *)s, len, (uchar *)t, len);
- else
-#endif
- {
- while (len--)
- {
- if (my_sort_order[(uchar) *s++] != my_sort_order[(uchar) *t++])
- return ((int) my_sort_order[(uchar) s[-1]] -
- (int) my_sort_order[(uchar) t[-1]]);
- }
- return 0;
- }
-}
-
-int my_sortncmp(const char *s, uint s_len, const char *t, uint t_len)
-{
-#ifdef USE_STRCOLL
- if (use_strcoll(default_charset_info))
- return my_strnncoll(default_charset_info,
- (uchar *)s, s_len, (uchar *)t, t_len);
- else
-#endif
- {
- uint len= min(s_len,t_len);
- while (len--)
- {
- if (my_sort_order[(uchar) *s++] != my_sort_order[(uchar) *t++])
- return ((int) my_sort_order[(uchar) s[-1]] -
- (int) my_sort_order[(uchar) t[-1]]);
- }
- return (int) (s_len - t_len);
- }
-}
diff --git a/mysys/mf_dirname.c b/mysys/mf_dirname.c
index d35f2c4f7ae..45bf4d56c31 100644
--- a/mysys/mf_dirname.c
+++ b/mysys/mf_dirname.c
@@ -22,6 +22,9 @@
uint dirname_length(const char *name)
{
register my_string pos,gpos;
+#ifdef BASKSLASH_MBTAIL
+ CHARSET_INFO *fs= fs_character_set();
+#endif
#ifdef FN_DEVCHAR
if ((pos=(char*)strrchr(name,FN_DEVCHAR)) == 0)
#endif
@@ -29,12 +32,22 @@ uint dirname_length(const char *name)
gpos= pos++;
for ( ; *pos ; pos++) /* Find last FN_LIBCHAR */
+ {
+#ifdef BASKSLASH_MBTAIL
+ uint l;
+ if (use_mb(fs) && (l= my_ismbchar(fs, pos, pos + 3)))
+ {
+ pos+= l - 1;
+ continue;
+ }
+#endif
if (*pos == FN_LIBCHAR || *pos == '/'
#ifdef FN_C_AFTER_DIR
|| *pos == FN_C_AFTER_DIR || *pos == FN_C_AFTER_DIR_2
#endif
)
gpos=pos;
+ }
return ((uint) (uint) (gpos+1-(char*) name));
}
@@ -85,6 +98,9 @@ uint dirname_part(my_string to, const char *name)
char *convert_dirname(char *to, const char *from, const char *from_end)
{
char *to_org=to;
+#ifdef BACKSLASH_MBTAIL
+ CHARSET_INFO *fs= fs_character_set();
+#endif
/* We use -2 here, becasue we need place for the last FN_LIBCHAR */
if (!from_end || (from_end - from) > FN_REFLEN-2)
@@ -103,7 +119,22 @@ char *convert_dirname(char *to, const char *from, const char *from_end)
*to++= FN_C_AFTER_DIR;
#endif
else
- *to++= *from;
+ {
+#ifdef BACKSLASH_MBTAIL
+ uint l;
+ if (use_mb(fs) && (l= my_ismbchar(fs, from, from + 3)))
+ {
+ memmove(to, from, l);
+ to+= l;
+ from+= l - 1;
+ to_org= to; /* Don't look inside mbchar */
+ }
+ else
+#endif
+ {
+ *to++= *from;
+ }
+ }
}
*to=0;
}
@@ -118,11 +149,5 @@ char *convert_dirname(char *to, const char *from, const char *from_end)
*to++=FN_LIBCHAR;
*to=0;
}
-#ifdef FN_UPPER_CASE
- caseup_str(to_org);
-#endif
-#ifdef FN_LOWER_CASE
- casedn_str(to_org);
-#endif
return to; /* Pointer to end of dir */
} /* convert_dirname */
diff --git a/mysys/mf_format.c b/mysys/mf_format.c
index 937904847ee..d1ca1108d02 100644
--- a/mysys/mf_format.c
+++ b/mysys/mf_format.c
@@ -42,7 +42,7 @@ my_string fn_format(my_string to, const char *name, const char *dir,
/* Use given directory */
convert_dirname(dev,dir,NullS); /* Fix to this OS */
}
- else if ((flag & MY_RELATIVE_PATH) && !test_if_hard_path(name))
+ else if ((flag & MY_RELATIVE_PATH) && !test_if_hard_path(dev))
{
/* Put 'dir' before the given path */
strmake(buff,dev,sizeof(buff)-1);
@@ -91,12 +91,6 @@ my_string fn_format(my_string to, const char *name, const char *dir,
name=buff;
}
pos=strmake(strmov(to,dev),name,length);
-#ifdef FN_UPPER_CASE
- caseup_str(to);
-#endif
-#ifdef FN_LOWER_CASE
- casedn_str(to);
-#endif
(void) strmov(pos,ext); /* Don't convert extension */
}
/*
diff --git a/mysys/mf_iocache.c b/mysys/mf_iocache.c
index a7937da0cc2..a91002d3b4c 100644
--- a/mysys/mf_iocache.c
+++ b/mysys/mf_iocache.c
@@ -55,7 +55,6 @@ TODO:
#include "mysys_err.h"
static void my_aiowait(my_aio_result *result);
#endif
-#include <assert.h>
#include <errno.h>
#ifdef THREAD
@@ -71,7 +70,6 @@ static void my_aiowait(my_aio_result *result);
#define IO_ROUND_UP(X) (((X)+IO_SIZE-1) & ~(IO_SIZE-1))
#define IO_ROUND_DN(X) ( (X) & ~(IO_SIZE-1))
-
/*
Setup internal pointers inside IO_CACHE
@@ -131,11 +129,29 @@ init_functions(IO_CACHE* info)
setup_io_cache(info);
}
- /*
- ** if cachesize == 0 then use default cachesize (from s-file)
- ** if file == -1 then real_open_cached_file() will be called.
- ** returns 0 if ok
- */
+
+/*
+ Initialize an IO_CACHE object
+
+ SYNOPSOS
+ init_io_cache()
+ info cache handler to initialize
+ file File that should be associated to to the handler
+ If == -1 then real_open_cached_file()
+ will be called when it's time to open file.
+ cachesize Size of buffer to allocate for read/write
+ If == 0 then use my_default_record_cache_size
+ type Type of cache
+ seek_offset Where cache should start reading/writing
+ use_async_io Set to 1 of we should use async_io (if avaiable)
+ cache_myflags Bitmap of differnt flags
+ MY_WME | MY_FAE | MY_NABP | MY_FNABP |
+ MY_DONT_CHECK_FILESIZE
+
+ RETURN
+ 0 ok
+ # error
+*/
int init_io_cache(IO_CACHE *info, File file, uint cachesize,
enum cache_type type, my_off_t seek_offset,
@@ -144,24 +160,24 @@ int init_io_cache(IO_CACHE *info, File file, uint cachesize,
uint min_cache;
my_off_t end_of_file= ~(my_off_t) 0;
DBUG_ENTER("init_io_cache");
- DBUG_PRINT("enter",("cache: %lx type: %d pos: %ld",
+ DBUG_PRINT("enter",("cache: 0x%lx type: %d pos: %ld",
(ulong) info, (int) type, (ulong) seek_offset));
info->file= file;
- info->type=type;
+ info->type= 0; /* Don't set it until mutex are created */
info->pos_in_file= seek_offset;
info->pre_close = info->pre_read = info->post_read = 0;
info->arg = 0;
info->alloced_buffer = 0;
info->buffer=0;
info->seek_not_done= test(file >= 0);
+ info->disk_writes= 0;
#ifdef THREAD
info->share=0;
#endif
- if (!cachesize)
- if (! (cachesize= my_default_record_cache_size))
- DBUG_RETURN(1); /* No cache requested */
+ if (!cachesize && !(cachesize= my_default_record_cache_size))
+ DBUG_RETURN(1); /* No cache requested */
min_cache=use_async_io ? IO_SIZE*4 : IO_SIZE*2;
if (type == READ_CACHE || type == SEQ_READ_APPEND)
{ /* Assume file isn't growing */
@@ -183,11 +199,11 @@ int init_io_cache(IO_CACHE *info, File file, uint cachesize,
if (type != READ_NET && type != WRITE_NET)
{
/* Retry allocating memory in smaller blocks until we get one */
+ cachesize=(uint) ((ulong) (cachesize + min_cache-1) &
+ (ulong) ~(min_cache-1));
for (;;)
{
uint buffer_block;
- cachesize=(uint) ((ulong) (cachesize + min_cache-1) &
- (ulong) ~(min_cache-1));
if (cachesize < min_cache)
cachesize = min_cache;
buffer_block = cachesize;
@@ -206,7 +222,8 @@ int init_io_cache(IO_CACHE *info, File file, uint cachesize,
}
if (cachesize == min_cache)
DBUG_RETURN(2); /* Can't alloc cache */
- cachesize= (uint) ((long) cachesize*3/4); /* Try with less memory */
+ /* Try with less memory */
+ cachesize= (uint) ((ulong) cachesize*3/4 & (ulong)~(min_cache-1));
}
}
@@ -222,6 +239,13 @@ int init_io_cache(IO_CACHE *info, File file, uint cachesize,
pthread_mutex_init(&info->append_buffer_lock,MY_MUTEX_INIT_FAST);
#endif
}
+#if defined(SAFE_MUTEX) && defined(THREAD)
+ else
+ {
+ /* Clear mutex so that safe_mutex will notice that it's not initialized */
+ bzero((char*) &info->append_buffer_lock, sizeof(info));
+ }
+#endif
if (type == WRITE_CACHE)
info->write_end=
@@ -232,6 +256,7 @@ int init_io_cache(IO_CACHE *info, File file, uint cachesize,
/* End_of_file may be changed by user later */
info->end_of_file= end_of_file;
info->error=0;
+ info->type= type;
init_functions(info);
#ifdef HAVE_AIOWAIT
if (use_async_io && ! my_disable_async_io)
@@ -286,7 +311,7 @@ my_bool reinit_io_cache(IO_CACHE *info, enum cache_type type,
pbool clear_cache)
{
DBUG_ENTER("reinit_io_cache");
- DBUG_PRINT("enter",("cache: %lx type: %d seek_offset: %lu clear_cache: %d",
+ DBUG_PRINT("enter",("cache: 0x%lx type: %d seek_offset: %lu clear_cache: %d",
(ulong) info, type, (ulong) seek_offset,
(int) clear_cache));
@@ -372,11 +397,31 @@ my_bool reinit_io_cache(IO_CACHE *info, enum cache_type type,
/*
- Read buffered. Returns 1 if can't read requested characters
- This function is only called from the my_b_read() macro
- when there isn't enough characters in the buffer to
- satisfy the request.
- Returns 0 we succeeded in reading all data
+ Read buffered.
+
+ SYNOPSIS
+ _my_b_read()
+ info IO_CACHE pointer
+ Buffer Buffer to retrieve count bytes from file
+ Count Number of bytes to read into Buffer
+
+ NOTE
+ This function is only called from the my_b_read() macro when there
+ isn't enough characters in the buffer to satisfy the request.
+
+ WARNING
+
+ When changing this function, be careful with handling file offsets
+ (end-of_file, pos_in_file). Do not cast them to possibly smaller
+ types than my_off_t unless you can be sure that their value fits.
+ Same applies to differences of file offsets.
+
+ When changing this function, check _my_b_read_r(). It might need the
+ same change.
+
+ RETURN
+ 0 we succeeded in reading all data
+ 1 Error: can't read requested characters
*/
int _my_b_read(register IO_CACHE *info, byte *Buffer, uint Count)
@@ -395,16 +440,29 @@ int _my_b_read(register IO_CACHE *info, byte *Buffer, uint Count)
/* pos_in_file always point on where info->buffer was read */
pos_in_file=info->pos_in_file+(uint) (info->read_end - info->buffer);
+
+ /*
+ Whenever a function which operates on IO_CACHE flushes/writes
+ some part of the IO_CACHE to disk it will set the property
+ "seek_not_done" to indicate this to other functions operating
+ on the IO_CACHE.
+ */
if (info->seek_not_done)
- { /* File touched, do seek */
- VOID(my_seek(info->file,pos_in_file,MY_SEEK_SET,MYF(0)));
+ {
+ if (my_seek(info->file,pos_in_file,MY_SEEK_SET,MYF(0))
+ == MY_FILEPOS_ERROR)
+ {
+ info->error= -1;
+ DBUG_RETURN(1);
+ }
info->seek_not_done=0;
}
+
diff_length=(uint) (pos_in_file & (IO_SIZE-1));
if (Count >= (uint) (IO_SIZE+(IO_SIZE-diff_length)))
{ /* Fill first intern buffer */
uint read_length;
- if (info->end_of_file == pos_in_file)
+ if (info->end_of_file <= pos_in_file)
{ /* End of file */
info->error=(int) left_length;
DBUG_RETURN(1);
@@ -455,152 +513,578 @@ int _my_b_read(register IO_CACHE *info, byte *Buffer, uint Count)
DBUG_RETURN(0);
}
+
#ifdef THREAD
-/* Prepare IO_CACHE for shared use */
-void init_io_cache_share(IO_CACHE *info, IO_CACHE_SHARE *s, uint num_threads)
+/*
+ Prepare IO_CACHE for shared use.
+
+ SYNOPSIS
+ init_io_cache_share()
+ read_cache A read cache. This will be copied for
+ every thread after setup.
+ cshare The share.
+ write_cache If non-NULL a write cache that is to be
+ synchronized with the read caches.
+ num_threads Number of threads sharing the cache
+ including the write thread if any.
+
+ DESCRIPTION
+
+ The shared cache is used so: One IO_CACHE is initialized with
+ init_io_cache(). This includes the allocation of a buffer. Then a
+ share is allocated and init_io_cache_share() is called with the io
+ cache and the share. Then the io cache is copied for each thread. So
+ every thread has its own copy of IO_CACHE. But the allocated buffer
+ is shared because cache->buffer is the same for all caches.
+
+ One thread reads data from the file into the buffer. All threads
+ read from the buffer, but every thread maintains its own set of
+ pointers into the buffer. When all threads have used up the buffer
+ contents, one of the threads reads the next block of data into the
+ buffer. To accomplish this, each thread enters the cache lock before
+ accessing the buffer. They wait in lock_io_cache() until all threads
+ joined the lock. The last thread entering the lock is in charge of
+ reading from file to buffer. It wakes all threads when done.
+
+ Synchronizing a write cache to the read caches works so: Whenever
+ the write buffer needs a flush, the write thread enters the lock and
+ waits for all other threads to enter the lock too. They do this when
+ they have used up the read buffer. When all threads are in the lock,
+ the write thread copies the write buffer to the read buffer and
+ wakes all threads.
+
+ share->running_threads is the number of threads not being in the
+ cache lock. When entering lock_io_cache() the number is decreased.
+ When the thread that fills the buffer enters unlock_io_cache() the
+ number is reset to the number of threads. The condition
+ running_threads == 0 means that all threads are in the lock. Bumping
+ up the number to the full count is non-intuitive. But increasing the
+ number by one for each thread that leaves the lock could lead to a
+ solo run of one thread. The last thread to join a lock reads from
+ file to buffer, wakes the other threads, processes the data in the
+ cache and enters the lock again. If no other thread left the lock
+ meanwhile, it would think it's the last one again and read the next
+ block...
+
+ The share has copies of 'error', 'buffer', 'read_end', and
+ 'pos_in_file' from the thread that filled the buffer. We may not be
+ able to access this information directly from its cache because the
+ thread may be removed from the share before the variables could be
+ copied by all other threads. Or, if a write buffer is synchronized,
+ it would change its 'pos_in_file' after waking the other threads,
+ possibly before they could copy its value.
+
+ However, the 'buffer' variable in the share is for a synchronized
+ write cache. It needs to know where to put the data. Otherwise it
+ would need access to the read cache of one of the threads that is
+ not yet removed from the share.
+
+ RETURN
+ void
+*/
+
+void init_io_cache_share(IO_CACHE *read_cache, IO_CACHE_SHARE *cshare,
+ IO_CACHE *write_cache, uint num_threads)
{
- DBUG_ASSERT(info->type == READ_CACHE);
- pthread_mutex_init(&s->mutex, MY_MUTEX_INIT_FAST);
- pthread_cond_init (&s->cond, 0);
- s->total=s->count=num_threads-1;
- s->active=0;
- info->share=s;
- info->read_function=_my_b_read_r;
- info->current_pos= info->current_end= 0;
+ DBUG_ENTER("init_io_cache_share");
+ DBUG_PRINT("io_cache_share", ("read_cache: 0x%lx share: 0x%lx "
+ "write_cache: 0x%lx threads: %u",
+ read_cache, cshare, write_cache, num_threads));
+
+ DBUG_ASSERT(num_threads > 1);
+ DBUG_ASSERT(read_cache->type == READ_CACHE);
+ DBUG_ASSERT(!write_cache || (write_cache->type == WRITE_CACHE));
+
+ pthread_mutex_init(&cshare->mutex, MY_MUTEX_INIT_FAST);
+ pthread_cond_init(&cshare->cond, 0);
+ pthread_cond_init(&cshare->cond_writer, 0);
+
+ cshare->running_threads= num_threads;
+ cshare->total_threads= num_threads;
+ cshare->error= 0; /* Initialize. */
+ cshare->buffer= read_cache->buffer;
+ cshare->read_end= NULL; /* See function comment of lock_io_cache(). */
+ cshare->pos_in_file= 0; /* See function comment of lock_io_cache(). */
+ cshare->source_cache= write_cache; /* Can be NULL. */
+
+ read_cache->share= cshare;
+ read_cache->read_function= _my_b_read_r;
+ read_cache->current_pos= NULL;
+ read_cache->current_end= NULL;
+
+ if (write_cache)
+ write_cache->share= cshare;
+
+ DBUG_VOID_RETURN;
}
+
/*
- Remove a thread from shared access to IO_CACHE
- Every thread should do that on exit for not
- to deadlock other threads
+ Remove a thread from shared access to IO_CACHE.
+
+ SYNOPSIS
+ remove_io_thread()
+ cache The IO_CACHE to be removed from the share.
+
+ NOTE
+
+ Every thread must do that on exit for not to deadlock other threads.
+
+ The last thread destroys the pthread resources.
+
+ A writer flushes its cache first.
+
+ RETURN
+ void
*/
-void remove_io_thread(IO_CACHE *info)
+
+void remove_io_thread(IO_CACHE *cache)
{
- IO_CACHE_SHARE *s=info->share;
+ IO_CACHE_SHARE *cshare= cache->share;
+ uint total;
+ DBUG_ENTER("remove_io_thread");
- pthread_mutex_lock(&s->mutex);
- s->total--;
- if (! s->count--)
- pthread_cond_signal(&s->cond);
- pthread_mutex_unlock(&s->mutex);
+ /* If the writer goes, it needs to flush the write cache. */
+ if (cache == cshare->source_cache)
+ flush_io_cache(cache);
+
+ pthread_mutex_lock(&cshare->mutex);
+ DBUG_PRINT("io_cache_share", ("%s: 0x%lx",
+ (cache == cshare->source_cache) ?
+ "writer" : "reader", cache));
+
+ /* Remove from share. */
+ total= --cshare->total_threads;
+ DBUG_PRINT("io_cache_share", ("remaining threads: %u", total));
+
+ /* Detach from share. */
+ cache->share= NULL;
+
+ /* If the writer goes, let the readers know. */
+ if (cache == cshare->source_cache)
+ {
+ DBUG_PRINT("io_cache_share", ("writer leaves"));
+ cshare->source_cache= NULL;
+ }
+
+ /* If all threads are waiting for me to join the lock, wake them. */
+ if (!--cshare->running_threads)
+ {
+ DBUG_PRINT("io_cache_share", ("the last running thread leaves, wake all"));
+ pthread_cond_signal(&cshare->cond_writer);
+ pthread_cond_broadcast(&cshare->cond);
+ }
+
+ pthread_mutex_unlock(&cshare->mutex);
+
+ if (!total)
+ {
+ DBUG_PRINT("io_cache_share", ("last thread removed, destroy share"));
+ pthread_cond_destroy (&cshare->cond_writer);
+ pthread_cond_destroy (&cshare->cond);
+ pthread_mutex_destroy(&cshare->mutex);
+ }
+
+ DBUG_VOID_RETURN;
}
-static int lock_io_cache(IO_CACHE *info, my_off_t pos)
-{
- int total;
- IO_CACHE_SHARE *s=info->share;
- pthread_mutex_lock(&s->mutex);
- if (!s->count)
+/*
+ Lock IO cache and wait for all other threads to join.
+
+ SYNOPSIS
+ lock_io_cache()
+ cache The cache of the thread entering the lock.
+ pos File position of the block to read.
+ Unused for the write thread.
+
+ DESCRIPTION
+
+ Wait for all threads to finish with the current buffer. We want
+ all threads to proceed in concert. The last thread to join
+ lock_io_cache() will read the block from file and all threads start
+ to use it. Then they will join again for reading the next block.
+
+ The waiting threads detect a fresh buffer by comparing
+ cshare->pos_in_file with the position they want to process next.
+ Since the first block may start at position 0, we take
+ cshare->read_end as an additional condition. This variable is
+ initialized to NULL and will be set after a block of data is written
+ to the buffer.
+
+ RETURN
+ 1 OK, lock in place, go ahead and read.
+ 0 OK, unlocked, another thread did the read.
+*/
+
+static int lock_io_cache(IO_CACHE *cache, my_off_t pos)
+{
+ IO_CACHE_SHARE *cshare= cache->share;
+ DBUG_ENTER("lock_io_cache");
+
+ /* Enter the lock. */
+ pthread_mutex_lock(&cshare->mutex);
+ cshare->running_threads--;
+ DBUG_PRINT("io_cache_share", ("%s: 0x%lx pos: %lu running: %u",
+ (cache == cshare->source_cache) ?
+ "writer" : "reader", cache, (ulong) pos,
+ cshare->running_threads));
+
+ if (cshare->source_cache)
{
- s->count=s->total;
- return 1;
+ /* A write cache is synchronized to the read caches. */
+
+ if (cache == cshare->source_cache)
+ {
+ /* The writer waits until all readers are here. */
+ while (cshare->running_threads)
+ {
+ DBUG_PRINT("io_cache_share", ("writer waits in lock"));
+ pthread_cond_wait(&cshare->cond_writer, &cshare->mutex);
+ }
+ DBUG_PRINT("io_cache_share", ("writer awoke, going to copy"));
+
+ /* Stay locked. Leave the lock later by unlock_io_cache(). */
+ DBUG_RETURN(1);
+ }
+
+ /* The last thread wakes the writer. */
+ if (!cshare->running_threads)
+ {
+ DBUG_PRINT("io_cache_share", ("waking writer"));
+ pthread_cond_signal(&cshare->cond_writer);
+ }
+
+ /*
+ Readers wait until the data is copied from the writer. Another
+ reason to stop waiting is the removal of the write thread. If this
+ happens, we leave the lock with old data in the buffer.
+ */
+ while ((!cshare->read_end || (cshare->pos_in_file < pos)) &&
+ cshare->source_cache)
+ {
+ DBUG_PRINT("io_cache_share", ("reader waits in lock"));
+ pthread_cond_wait(&cshare->cond, &cshare->mutex);
+ }
+
+ /*
+ If the writer was removed from the share while this thread was
+ asleep, we need to simulate an EOF condition. The writer cannot
+ reset the share variables as they might still be in use by readers
+ of the last block. When we awake here then because the last
+ joining thread signalled us. If the writer is not the last, it
+ will not signal. So it is safe to clear the buffer here.
+ */
+ if (!cshare->read_end || (cshare->pos_in_file < pos))
+ {
+ DBUG_PRINT("io_cache_share", ("reader found writer removed. EOF"));
+ cshare->read_end= cshare->buffer; /* Empty buffer. */
+ cshare->error= 0; /* EOF is not an error. */
+ }
}
+ else
+ {
+ /*
+ There are read caches only. The last thread arriving in
+ lock_io_cache() continues with a locked cache and reads the block.
+ */
+ if (!cshare->running_threads)
+ {
+ DBUG_PRINT("io_cache_share", ("last thread joined, going to read"));
+ /* Stay locked. Leave the lock later by unlock_io_cache(). */
+ DBUG_RETURN(1);
+ }
- total=s->total;
- s->count--;
- while (!s->active || s->active->pos_in_file < pos)
- pthread_cond_wait(&s->cond, &s->mutex);
+ /*
+ All other threads wait until the requested block is read by the
+ last thread arriving. Another reason to stop waiting is the
+ removal of a thread. If this leads to all threads being in the
+ lock, we have to continue also. The first of the awaken threads
+ will then do the read.
+ */
+ while ((!cshare->read_end || (cshare->pos_in_file < pos)) &&
+ cshare->running_threads)
+ {
+ DBUG_PRINT("io_cache_share", ("reader waits in lock"));
+ pthread_cond_wait(&cshare->cond, &cshare->mutex);
+ }
- if (s->total < total &&
- (!s->active || s->active->pos_in_file < pos))
- return 1;
+ /* If the block is not yet read, continue with a locked cache and read. */
+ if (!cshare->read_end || (cshare->pos_in_file < pos))
+ {
+ DBUG_PRINT("io_cache_share", ("reader awoke, going to read"));
+ /* Stay locked. Leave the lock later by unlock_io_cache(). */
+ DBUG_RETURN(1);
+ }
- pthread_mutex_unlock(&s->mutex);
- return 0;
+ /* Another thread did read the block already. */
+ }
+ DBUG_PRINT("io_cache_share", ("reader awoke, going to process %u bytes",
+ cshare->read_end ? (uint)
+ (cshare->read_end - cshare->buffer) : 0));
+
+ /*
+ Leave the lock. Do not call unlock_io_cache() later. The thread that
+ filled the buffer did this and marked all threads as running.
+ */
+ pthread_mutex_unlock(&cshare->mutex);
+ DBUG_RETURN(0);
}
-static void unlock_io_cache(IO_CACHE *info)
+
+/*
+ Unlock IO cache.
+
+ SYNOPSIS
+ unlock_io_cache()
+ cache The cache of the thread leaving the lock.
+
+ NOTE
+ This is called by the thread that filled the buffer. It marks all
+ threads as running and awakes them. This must not be done by any
+ other thread.
+
+ Do not signal cond_writer. Either there is no writer or the writer
+ is the only one who can call this function.
+
+ The reason for resetting running_threads to total_threads before
+ waking all other threads is that it could be possible that this
+ thread is so fast with processing the buffer that it enters the lock
+ before even one other thread has left it. If every awoken thread
+ would increase running_threads by one, this thread could think that
+ he is again the last to join and would not wait for the other
+ threads to process the data.
+
+ RETURN
+ void
+*/
+
+static void unlock_io_cache(IO_CACHE *cache)
{
- pthread_cond_broadcast(&info->share->cond);
- pthread_mutex_unlock(&info->share->mutex);
+ IO_CACHE_SHARE *cshare= cache->share;
+ DBUG_ENTER("unlock_io_cache");
+ DBUG_PRINT("io_cache_share", ("%s: 0x%lx pos: %lu running: %u",
+ (cache == cshare->source_cache) ?
+ "writer" : "reader",
+ cache, (ulong) cshare->pos_in_file,
+ cshare->total_threads));
+
+ cshare->running_threads= cshare->total_threads;
+ pthread_cond_broadcast(&cshare->cond);
+ pthread_mutex_unlock(&cshare->mutex);
+ DBUG_VOID_RETURN;
}
/*
Read from IO_CACHE when it is shared between several threads.
- It works as follows: when a thread tries to read from a file
- (that is, after using all the data from the (shared) buffer),
- it just hangs on lock_io_cache(), wating for other threads.
- When the very last thread attempts a read, lock_io_cache()
- returns 1, the thread does actual IO and unlock_io_cache(),
- which signals all the waiting threads that data is in the buffer.
+
+ SYNOPSIS
+ _my_b_read_r()
+ cache IO_CACHE pointer
+ Buffer Buffer to retrieve count bytes from file
+ Count Number of bytes to read into Buffer
+
+ NOTE
+ This function is only called from the my_b_read() macro when there
+ isn't enough characters in the buffer to satisfy the request.
+
+ IMPLEMENTATION
+
+ It works as follows: when a thread tries to read from a file (that
+ is, after using all the data from the (shared) buffer), it just
+ hangs on lock_io_cache(), waiting for other threads. When the very
+ last thread attempts a read, lock_io_cache() returns 1, the thread
+ does actual IO and unlock_io_cache(), which signals all the waiting
+ threads that data is in the buffer.
+
+ WARNING
+
+ When changing this function, be careful with handling file offsets
+ (end-of_file, pos_in_file). Do not cast them to possibly smaller
+ types than my_off_t unless you can be sure that their value fits.
+ Same applies to differences of file offsets. (Bug #11527)
+
+ When changing this function, check _my_b_read(). It might need the
+ same change.
+
+ RETURN
+ 0 we succeeded in reading all data
+ 1 Error: can't read requested characters
*/
-int _my_b_read_r(register IO_CACHE *info, byte *Buffer, uint Count)
+int _my_b_read_r(register IO_CACHE *cache, byte *Buffer, uint Count)
{
my_off_t pos_in_file;
- uint length,diff_length,read_len;
+ uint length, diff_length, left_length;
+ IO_CACHE_SHARE *cshare= cache->share;
DBUG_ENTER("_my_b_read_r");
- if ((read_len=(uint) (info->read_end-info->read_pos)))
+ if ((left_length= (uint) (cache->read_end - cache->read_pos)))
{
- DBUG_ASSERT(Count >= read_len); /* User is not using my_b_read() */
- memcpy(Buffer,info->read_pos, (size_t) (read_len));
- Buffer+=read_len;
- Count-=read_len;
+ DBUG_ASSERT(Count >= left_length); /* User is not using my_b_read() */
+ memcpy(Buffer, cache->read_pos, (size_t) (left_length));
+ Buffer+= left_length;
+ Count-= left_length;
}
while (Count)
{
int cnt, len;
- pos_in_file= info->pos_in_file + (uint)(info->read_end - info->buffer);
+ pos_in_file= cache->pos_in_file + (cache->read_end - cache->buffer);
diff_length= (uint) (pos_in_file & (IO_SIZE-1));
length=IO_ROUND_UP(Count+diff_length)-diff_length;
- length=(length <= info->read_length) ?
- length + IO_ROUND_DN(info->read_length - length) :
- length - IO_ROUND_UP(length - info->read_length) ;
- if (info->type != READ_FIFO &&
- (length > (uint) (info->end_of_file - pos_in_file)))
- length= (uint) (info->end_of_file - pos_in_file);
+ length= ((length <= cache->read_length) ?
+ length + IO_ROUND_DN(cache->read_length - length) :
+ length - IO_ROUND_UP(length - cache->read_length));
+ if (cache->type != READ_FIFO &&
+ (length > (cache->end_of_file - pos_in_file)))
+ length= (uint) (cache->end_of_file - pos_in_file);
if (length == 0)
{
- info->error=(int) read_len;
+ cache->error= (int) left_length;
DBUG_RETURN(1);
}
- if (lock_io_cache(info, pos_in_file))
+ if (lock_io_cache(cache, pos_in_file))
{
- info->share->active=info;
- if (info->seek_not_done) /* File touched, do seek */
- VOID(my_seek(info->file,pos_in_file,MY_SEEK_SET,MYF(0)));
- len=(int)my_read(info->file,info->buffer, length, info->myflags);
- info->read_end=info->buffer + (len == -1 ? 0 : len);
- info->error=(len == (int)length ? 0 : len);
- info->pos_in_file=pos_in_file;
- unlock_io_cache(info);
+ /* With a synchronized write/read cache we won't come here... */
+ DBUG_ASSERT(!cshare->source_cache);
+ /*
+ ... unless the writer has gone before this thread entered the
+ lock. Simulate EOF in this case. It can be distinguished by
+ cache->file.
+ */
+ if (cache->file < 0)
+ len= 0;
+ else
+ {
+ /*
+ Whenever a function which operates on IO_CACHE flushes/writes
+ some part of the IO_CACHE to disk it will set the property
+ "seek_not_done" to indicate this to other functions operating
+ on the IO_CACHE.
+ */
+ if (cache->seek_not_done)
+ {
+ if (my_seek(cache->file,pos_in_file,MY_SEEK_SET,MYF(0))
+ == MY_FILEPOS_ERROR)
+ {
+ cache->error= -1;
+ unlock_io_cache(cache);
+ DBUG_RETURN(1);
+ }
+ }
+ len= (int) my_read(cache->file, cache->buffer, length, cache->myflags);
+ }
+ DBUG_PRINT("io_cache_share", ("read %d bytes", len));
+
+ cache->read_end= cache->buffer + (len == -1 ? 0 : len);
+ cache->error= (len == (int)length ? 0 : len);
+ cache->pos_in_file= pos_in_file;
+
+ /* Copy important values to the share. */
+ cshare->error= cache->error;
+ cshare->read_end= cache->read_end;
+ cshare->pos_in_file= pos_in_file;
+
+ /* Mark all threads as running and wake them. */
+ unlock_io_cache(cache);
}
else
{
- info->error= info->share->active->error;
- info->read_end= info->share->active->read_end;
- info->pos_in_file= info->share->active->pos_in_file;
- len= (info->error == -1 ? -1 : info->read_end-info->buffer);
+ /*
+ With a synchronized write/read cache readers always come here.
+ Copy important values from the share.
+ */
+ cache->error= cshare->error;
+ cache->read_end= cshare->read_end;
+ cache->pos_in_file= cshare->pos_in_file;
+
+ len= ((cache->error == -1) ? -1 : cache->read_end - cache->buffer);
}
- info->read_pos=info->buffer;
- info->seek_not_done=0;
+ cache->read_pos= cache->buffer;
+ cache->seek_not_done= 0;
if (len <= 0)
{
- info->error=(int) read_len;
+ DBUG_PRINT("io_cache_share", ("reader error. len %d left %u",
+ len, left_length));
+ cache->error= (int) left_length;
DBUG_RETURN(1);
}
- cnt=((uint) len > Count) ? (int) Count : len;
- memcpy(Buffer,info->read_pos, (size_t)cnt);
- Count -=cnt;
- Buffer+=cnt;
- read_len+=cnt;
- info->read_pos+=cnt;
+ cnt= ((uint) len > Count) ? (int) Count : len;
+ memcpy(Buffer, cache->read_pos, (size_t) cnt);
+ Count -= cnt;
+ Buffer+= cnt;
+ left_length+= cnt;
+ cache->read_pos+= cnt;
}
DBUG_RETURN(0);
}
-#endif
/*
- Do sequential read from the SEQ_READ_APPEND cache
- we do this in three stages:
+ Copy data from write cache to read cache.
+
+ SYNOPSIS
+ copy_to_read_buffer()
+ write_cache The write cache.
+ write_buffer The source of data, mostly the cache buffer.
+ write_length The number of bytes to copy.
+
+ NOTE
+ The write thread will wait for all read threads to join the cache
+ lock. Then it copies the data over and wakes the read threads.
+
+ RETURN
+ void
+*/
+
+static void copy_to_read_buffer(IO_CACHE *write_cache,
+ const byte *write_buffer, uint write_length)
+{
+ IO_CACHE_SHARE *cshare= write_cache->share;
+
+ DBUG_ASSERT(cshare->source_cache == write_cache);
+ /*
+ write_length is usually less or equal to buffer_length.
+ It can be bigger if _my_b_write() is called with a big length.
+ */
+ while (write_length)
+ {
+ uint copy_length= min(write_length, write_cache->buffer_length);
+ int __attribute__((unused)) rc;
+
+ rc= lock_io_cache(write_cache, write_cache->pos_in_file);
+ /* The writing thread does always have the lock when it awakes. */
+ DBUG_ASSERT(rc);
+
+ memcpy(cshare->buffer, write_buffer, copy_length);
+
+ cshare->error= 0;
+ cshare->read_end= cshare->buffer + copy_length;
+ cshare->pos_in_file= write_cache->pos_in_file;
+
+ /* Mark all threads as running and wake them. */
+ unlock_io_cache(write_cache);
+
+ write_buffer+= copy_length;
+ write_length-= copy_length;
+ }
+}
+#endif /*THREAD*/
+
+
+/*
+ Do sequential read from the SEQ_READ_APPEND cache.
+
+ We do this in three stages:
- first read from info->buffer
- then if there are still data to read, try the file descriptor
- afterwards, if there are still data to read, try append buffer
+
+ RETURNS
+ 0 Success
+ 1 Failed to read
*/
int _my_b_seq_read(register IO_CACHE *info, byte *Buffer, uint Count)
@@ -628,7 +1112,13 @@ int _my_b_seq_read(register IO_CACHE *info, byte *Buffer, uint Count)
With read-append cache we must always do a seek before we read,
because the write could have moved the file pointer astray
*/
- VOID(my_seek(info->file,pos_in_file,MY_SEEK_SET,MYF(0)));
+ if (my_seek(info->file,pos_in_file,MY_SEEK_SET,MYF(0))
+ == MY_FILEPOS_ERROR)
+ {
+ info->error= -1;
+ unlock_append_buffer(info);
+ return (1);
+ }
info->seek_not_done=0;
diff_length=(uint) (pos_in_file & (IO_SIZE-1));
@@ -744,6 +1234,21 @@ read_append_buffer:
#ifdef HAVE_AIOWAIT
+/*
+ Read from the IO_CACHE into a buffer and feed asynchronously
+ from disk when needed.
+
+ SYNOPSIS
+ _my_b_async_read()
+ info IO_CACHE pointer
+ Buffer Buffer to retrieve count bytes from file
+ Count Number of bytes to read into Buffer
+
+ RETURN VALUE
+ -1 An error has occurred; my_errno is set.
+ 0 Success
+ 1 An error has occurred; IO_CACHE to error state.
+*/
int _my_b_async_read(register IO_CACHE *info, byte *Buffer, uint Count)
{
uint length,read_length,diff_length,left_length,use_length,org_Count;
@@ -795,7 +1300,7 @@ int _my_b_async_read(register IO_CACHE *info, byte *Buffer, uint Count)
{ /* Fix if skipped bytes */
if (info->aio_read_pos + read_length < info->pos_in_file)
{
- read_length=0; /* Skipp block */
+ read_length=0; /* Skip block */
next_pos_in_file=info->pos_in_file;
}
else
@@ -826,7 +1331,7 @@ int _my_b_async_read(register IO_CACHE *info, byte *Buffer, uint Count)
next_pos_in_file=(info->pos_in_file+ (uint)
(info->read_end - info->request_pos));
- /* If reading large blocks, or first read or read with skipp */
+ /* If reading large blocks, or first read or read with skip */
if (Count)
{
if (next_pos_in_file == info->end_of_file)
@@ -834,13 +1339,20 @@ int _my_b_async_read(register IO_CACHE *info, byte *Buffer, uint Count)
info->error=(int) (read_length+left_length);
return 1;
}
- VOID(my_seek(info->file,next_pos_in_file,MY_SEEK_SET,MYF(0)));
+
+ if (my_seek(info->file,next_pos_in_file,MY_SEEK_SET,MYF(0))
+ == MY_FILEPOS_ERROR)
+ {
+ info->error= -1;
+ return (1);
+ }
+
read_length=IO_SIZE*2- (uint) (next_pos_in_file & (IO_SIZE-1));
if (Count < read_length)
{ /* Small block, read to cache */
if ((read_length=my_read(info->file,info->request_pos,
read_length, info->myflags)) == (uint) -1)
- return info->error= -1;
+ return info->error= -1;
use_length=min(Count,read_length);
memcpy(Buffer,info->request_pos,(size_t) use_length);
info->read_pos=info->request_pos+Count;
@@ -889,7 +1401,7 @@ int _my_b_async_read(register IO_CACHE *info, byte *Buffer, uint Count)
if (aioread(info->file,read_buffer,(int) max_length,
(my_off_t) next_pos_in_file,MY_SEEK_SET,
&info->aio_result.result))
- { /* Skipp async io */
+ { /* Skip async io */
my_errno=errno;
DBUG_PRINT("error",("got error: %d, aio_result: %d from aioread, async skipped",
errno, info->aio_result.result.aio_errno));
@@ -927,7 +1439,15 @@ int _my_b_get(IO_CACHE *info)
return (int) (uchar) buff;
}
- /* Returns != 0 if error on write */
+/*
+ Write a byte buffer to IO_CACHE and flush to disk
+ if IO_CACHE is full.
+
+ RETURN VALUE
+ 1 On error on write
+ 0 On success
+ -1 On error; my_errno contains error code.
+*/
int _my_b_write(register IO_CACHE *info, const byte *Buffer, uint Count)
{
@@ -944,18 +1464,46 @@ int _my_b_write(register IO_CACHE *info, const byte *Buffer, uint Count)
Buffer+=rest_length;
Count-=rest_length;
info->write_pos+=rest_length;
+
if (flush_io_cache(info))
return 1;
if (Count >= IO_SIZE)
{ /* Fill first intern buffer */
length=Count & (uint) ~(IO_SIZE-1);
if (info->seek_not_done)
- { /* File touched, do seek */
- VOID(my_seek(info->file,info->pos_in_file,MY_SEEK_SET,MYF(0)));
+ {
+ /*
+ Whenever a function which operates on IO_CACHE flushes/writes
+ some part of the IO_CACHE to disk it will set the property
+ "seek_not_done" to indicate this to other functions operating
+ on the IO_CACHE.
+ */
+ if (my_seek(info->file,info->pos_in_file,MY_SEEK_SET,MYF(0)))
+ {
+ info->error= -1;
+ return (1);
+ }
info->seek_not_done=0;
}
if (my_write(info->file,Buffer,(uint) length,info->myflags | MY_NABP))
return info->error= -1;
+
+#ifdef THREAD
+ /*
+ In case of a shared I/O cache with a writer we normally do direct
+ write cache to read cache copy. Simulate this here by direct
+ caller buffer to read cache copy. Do it after the write so that
+ the cache readers actions on the flushed part can go in parallel
+ with the write of the extra stuff. copy_to_read_buffer()
+ synchronizes writer and readers so that after this call the
+ readers can act on the extra stuff while the writer can go ahead
+ and prepare the next output. copy_to_read_buffer() relies on
+ info->pos_in_file.
+ */
+ if (info->share)
+ copy_to_read_buffer(info, Buffer, length);
+#endif
+
Count-=length;
Buffer+=length;
info->pos_in_file+=length;
@@ -976,6 +1524,14 @@ int my_b_append(register IO_CACHE *info, const byte *Buffer, uint Count)
{
uint rest_length,length;
+#ifdef THREAD
+ /*
+ Assert that we cannot come here with a shared cache. If we do one
+ day, we might need to add a call to copy_to_read_buffer().
+ */
+ DBUG_ASSERT(!info->share);
+#endif
+
lock_append_buffer(info);
rest_length=(uint) (info->write_end - info->write_pos);
if (Count <= rest_length)
@@ -1036,6 +1592,14 @@ int my_block_write(register IO_CACHE *info, const byte *Buffer, uint Count,
uint length;
int error=0;
+#ifdef THREAD
+ /*
+ Assert that we cannot come here with a shared cache. If we do one
+ day, we might need to add a call to copy_to_read_buffer().
+ */
+ DBUG_ASSERT(!info->share);
+#endif
+
if (pos < info->pos_in_file)
{
/* Of no overlap, write everything without buffering */
@@ -1112,6 +1676,17 @@ int my_b_flush_io_cache(IO_CACHE *info, int need_append_buffer_lock)
if ((length=(uint) (info->write_pos - info->write_buffer)))
{
+#ifdef THREAD
+ /*
+ In case of a shared I/O cache with a writer we do direct write
+ cache to read cache copy. Do it before the write here so that
+ the readers can work in parallel with the write.
+ copy_to_read_buffer() relies on info->pos_in_file.
+ */
+ if (info->share)
+ copy_to_read_buffer(info, info->write_buffer, length);
+#endif
+
pos_in_file=info->pos_in_file;
/*
If we have append cache, we always open the file with
@@ -1149,6 +1724,7 @@ int my_b_flush_io_cache(IO_CACHE *info, int need_append_buffer_lock)
}
info->append_read_pos=info->write_pos=info->write_buffer;
+ ++info->disk_writes;
UNLOCK_APPEND_BUFFER;
DBUG_RETURN(info->error);
}
@@ -1164,6 +1740,22 @@ int my_b_flush_io_cache(IO_CACHE *info, int need_append_buffer_lock)
DBUG_RETURN(0);
}
+/*
+ Free an IO_CACHE object
+
+ SYNOPSOS
+ end_io_cache()
+ info IO_CACHE Handle to free
+
+ NOTES
+ It's currently safe to call this if one has called init_io_cache()
+ on the 'info' object, even if init_io_cache() failed.
+ This function is also safe to call twice with the same handle.
+
+ RETURN
+ 0 ok
+ # Error
+*/
int end_io_cache(IO_CACHE *info)
{
@@ -1173,20 +1765,17 @@ int end_io_cache(IO_CACHE *info)
#ifdef THREAD
/*
- if IO_CACHE is shared between several threads, only one
- thread needs to call end_io_cache() - just as init_io_cache()
- should be called only once and then memcopy'ed
+ Every thread must call remove_io_thread(). The last one destroys
+ the share elements.
*/
- if (info->share)
- {
- pthread_cond_destroy (&info->share->cond);
- pthread_mutex_destroy(&info->share->mutex);
- info->share=0;
- }
+ DBUG_ASSERT(!info->share || !info->share->total_threads);
#endif
if ((pre_close=info->pre_close))
+ {
(*pre_close)(info);
+ info->pre_close= 0;
+ }
if (info->alloced_buffer)
{
info->alloced_buffer=0;
diff --git a/mysys/mf_iocache2.c b/mysys/mf_iocache2.c
index 344b7ac2251..3755bcdb53d 100644
--- a/mysys/mf_iocache2.c
+++ b/mysys/mf_iocache2.c
@@ -23,7 +23,6 @@
#include <m_string.h>
#include <stdarg.h>
#include <m_ctype.h>
-#include <assert.h>
my_off_t my_b_append_tell(IO_CACHE* info)
{
@@ -266,8 +265,8 @@ uint my_b_vprintf(IO_CACHE *info, const char* fmt, va_list args)
fmt++;
/* Found one '%' */
}
- /* Skipp if max size is used (to be compatible with printf) */
- while (isdigit(*fmt) || *fmt == '.' || *fmt == '-')
+ /* Skip if max size is used (to be compatible with printf) */
+ while (my_isdigit(&my_charset_latin1, *fmt) || *fmt == '.' || *fmt == '-')
fmt++;
if (*fmt == 's') /* String parameter */
{
diff --git a/mysys/mf_keycache.c b/mysys/mf_keycache.c
index 0f8eb7e6f65..88b5051c52b 100644
--- a/mysys/mf_keycache.c
+++ b/mysys/mf_keycache.c
@@ -15,865 +15,2772 @@
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
/*
- This functions is handle keyblock cacheing for NISAM, MISAM and PISAM
- databases.
- One cache can handle many files. Every different blocksize has it owns
- set of buffers that are allocated from block_mem.
+ These functions handle keyblock cacheing for ISAM and MyISAM tables.
+
+ One cache can handle many files.
+ It must contain buffers of the same blocksize.
init_key_cache() should be used to init cache handler.
- */
+
+ The free list (free_block_list) is a stack like structure.
+ When a block is freed by free_block(), it is pushed onto the stack.
+ When a new block is required it is first tried to pop one from the stack.
+ If the stack is empty, it is tried to get a never-used block from the pool.
+ If this is empty too, then a block is taken from the LRU ring, flushing it
+ to disk, if neccessary. This is handled in find_key_block().
+ With the new free list, the blocks can have three temperatures:
+ hot, warm and cold (which is free). This is remembered in the block header
+ by the enum BLOCK_TEMPERATURE temperature variable. Remembering the
+ temperature is neccessary to correctly count the number of warm blocks,
+ which is required to decide when blocks are allowed to become hot. Whenever
+ a block is inserted to another (sub-)chain, we take the old and new
+ temperature into account to decide if we got one more or less warm block.
+ blocks_unused is the sum of never used blocks in the pool and of currently
+ free blocks. blocks_used is the number of blocks fetched from the pool and
+ as such gives the maximum number of in-use blocks at any time.
+*/
#include "mysys_priv.h"
+#include <keycache.h>
#include "my_static.h"
#include <m_string.h>
#include <errno.h>
+#include <stdarg.h>
+
+/*
+ Some compilation flags have been added specifically for this module
+ to control the following:
+ - not to let a thread to yield the control when reading directly
+ from key cache, which might improve performance in many cases;
+ to enable this add:
+ #define SERIALIZED_READ_FROM_CACHE
+ - to set an upper bound for number of threads simultaneously
+ using the key cache; this setting helps to determine an optimal
+ size for hash table and improve performance when the number of
+ blocks in the key cache much less than the number of threads
+ accessing it;
+ to set this number equal to <N> add
+ #define MAX_THREADS <N>
+ - to substitute calls of pthread_cond_wait for calls of
+ pthread_cond_timedwait (wait with timeout set up);
+ this setting should be used only when you want to trap a deadlock
+ situation, which theoretically should not happen;
+ to set timeout equal to <T> seconds add
+ #define KEYCACHE_TIMEOUT <T>
+ - to enable the module traps and to send debug information from
+ key cache module to a special debug log add:
+ #define KEYCACHE_DEBUG
+ the name of this debug log file <LOG NAME> can be set through:
+ #define KEYCACHE_DEBUG_LOG <LOG NAME>
+ if the name is not defined, it's set by default;
+ if the KEYCACHE_DEBUG flag is not set up and we are in a debug
+ mode, i.e. when ! defined(DBUG_OFF), the debug information from the
+ module is sent to the regular debug log.
+
+ Example of the settings:
+ #define SERIALIZED_READ_FROM_CACHE
+ #define MAX_THREADS 100
+ #define KEYCACHE_TIMEOUT 1
+ #define KEYCACHE_DEBUG
+ #define KEYCACHE_DEBUG_LOG "my_key_cache_debug.log"
+*/
#if defined(MSDOS) && !defined(M_IC80386)
- /* We nead much memory */
+/* we nead much memory */
#undef my_malloc_lock
#undef my_free_lock
-#define my_malloc_lock(A,B) halloc((long) (A/IO_SIZE),IO_SIZE)
-#define my_free_lock(A,B) hfree(A)
-#endif
-
-/* size of map to be used to find changed files */
-
-#define CHANGED_BLOCKS_HASH 128 /* Must be power of 2 */
-#define CHANGED_BLOCKS_MASK (CHANGED_BLOCKS_HASH-1)
-#define FLUSH_CACHE 2000 /* Sort this many blocks at once */
-
-typedef struct sec_link {
- struct sec_link *next_hash,**prev_hash;/* Blocks linked acc. to hash-value */
- struct sec_link *next_used,*prev_used;
- struct sec_link *next_changed,**prev_changed;
- File file;
- my_off_t diskpos;
- byte *buffer;
- my_bool changed;
-} SEC_LINK;
-
-
-static SEC_LINK *find_key_block(int file,my_off_t filepos,int *error);
-static int flush_all_key_blocks();
-
- /* static variables in this file */
-static SEC_LINK *_my_block_root,**_my_hash_root,
- *_my_used_first,*_my_used_last;
-static int _my_disk_blocks;
-static uint _my_disk_blocks_used, _my_hash_blocks;
-static uint key_cache_shift;
-ulong _my_blocks_used,_my_blocks_changed;
-ulonglong _my_cache_w_requests, _my_cache_write;
-ulonglong _my_cache_r_requests, _my_cache_read;
-uint key_cache_block_size=DEFAULT_KEYCACHE_BLOCK_SIZE;
-static byte HUGE_PTR *_my_block_mem;
-static SEC_LINK *changed_blocks[CHANGED_BLOCKS_HASH];
-static SEC_LINK *file_blocks[CHANGED_BLOCKS_HASH];
-#ifndef DBUG_OFF
-static my_bool _my_printed;
+#define my_malloc_lock(A,B) halloc((long) (A/IO_SIZE),IO_SIZE)
+#define my_free_lock(A,B) hfree(A)
+#endif /* defined(MSDOS) && !defined(M_IC80386) */
+
+#define STRUCT_PTR(TYPE, MEMBER, a) \
+ (TYPE *) ((char *) (a) - offsetof(TYPE, MEMBER))
+
+/* types of condition variables */
+#define COND_FOR_REQUESTED 0
+#define COND_FOR_SAVED 1
+#define COND_FOR_READERS 2
+
+typedef pthread_cond_t KEYCACHE_CONDVAR;
+
+/* descriptor of the page in the key cache block buffer */
+struct st_keycache_page
+{
+ int file; /* file to which the page belongs to */
+ my_off_t filepos; /* position of the page in the file */
+};
+
+/* element in the chain of a hash table bucket */
+struct st_hash_link
+{
+ struct st_hash_link *next, **prev; /* to connect links in the same bucket */
+ struct st_block_link *block; /* reference to the block for the page: */
+ File file; /* from such a file */
+ my_off_t diskpos; /* with such an offset */
+ uint requests; /* number of requests for the page */
+};
+
+/* simple states of a block */
+#define BLOCK_ERROR 1 /* an error occured when performing disk i/o */
+#define BLOCK_READ 2 /* the is page in the block buffer */
+#define BLOCK_IN_SWITCH 4 /* block is preparing to read new page */
+#define BLOCK_REASSIGNED 8 /* block does not accept requests for old page */
+#define BLOCK_IN_FLUSH 16 /* block is in flush operation */
+#define BLOCK_CHANGED 32 /* block buffer contains a dirty page */
+
+/* page status, returned by find_key_block */
+#define PAGE_READ 0
+#define PAGE_TO_BE_READ 1
+#define PAGE_WAIT_TO_BE_READ 2
+
+/* block temperature determines in which (sub-)chain the block currently is */
+enum BLOCK_TEMPERATURE { BLOCK_COLD /*free*/ , BLOCK_WARM , BLOCK_HOT };
+
+/* key cache block */
+struct st_block_link
+{
+ struct st_block_link
+ *next_used, **prev_used; /* to connect links in the LRU chain (ring) */
+ struct st_block_link
+ *next_changed, **prev_changed; /* for lists of file dirty/clean blocks */
+ struct st_hash_link *hash_link; /* backward ptr to referring hash_link */
+ KEYCACHE_WQUEUE wqueue[2]; /* queues on waiting requests for new/old pages */
+ uint requests; /* number of requests for the block */
+ byte *buffer; /* buffer for the block page */
+ uint offset; /* beginning of modified data in the buffer */
+ uint length; /* end of data in the buffer */
+ uint status; /* state of the block */
+ enum BLOCK_TEMPERATURE temperature; /* block temperature: cold, warm, hot */
+ uint hits_left; /* number of hits left until promotion */
+ ulonglong last_hit_time; /* timestamp of the last hit */
+ KEYCACHE_CONDVAR *condvar; /* condition variable for 'no readers' event */
+};
+
+KEY_CACHE dflt_key_cache_var;
+KEY_CACHE *dflt_key_cache= &dflt_key_cache_var;
+
+#define FLUSH_CACHE 2000 /* sort this many blocks at once */
+
+static int flush_all_key_blocks(KEY_CACHE *keycache);
+static void link_into_queue(KEYCACHE_WQUEUE *wqueue,
+ struct st_my_thread_var *thread);
+static void unlink_from_queue(KEYCACHE_WQUEUE *wqueue,
+ struct st_my_thread_var *thread);
+static void free_block(KEY_CACHE *keycache, BLOCK_LINK *block);
+static void test_key_cache(KEY_CACHE *keycache,
+ const char *where, my_bool lock);
+
+#define KEYCACHE_HASH(f, pos) \
+(((ulong) ((pos) >> keycache->key_cache_shift)+ \
+ (ulong) (f)) & (keycache->hash_entries-1))
+#define FILE_HASH(f) ((uint) (f) & (CHANGED_BLOCKS_HASH-1))
+
+#define DEFAULT_KEYCACHE_DEBUG_LOG "keycache_debug.log"
+
+#if defined(KEYCACHE_DEBUG) && ! defined(KEYCACHE_DEBUG_LOG)
+#define KEYCACHE_DEBUG_LOG DEFAULT_KEYCACHE_DEBUG_LOG
#endif
+#if defined(KEYCACHE_DEBUG_LOG)
+static FILE *keycache_debug_log=NULL;
+static void keycache_debug_print _VARARGS((const char *fmt,...));
+#define KEYCACHE_DEBUG_OPEN \
+ if (!keycache_debug_log) \
+ { \
+ keycache_debug_log= fopen(KEYCACHE_DEBUG_LOG, "w"); \
+ (void) setvbuf(keycache_debug_log, NULL, _IOLBF, BUFSIZ); \
+ }
+
+#define KEYCACHE_DEBUG_CLOSE \
+ if (keycache_debug_log) \
+ { \
+ fclose(keycache_debug_log); \
+ keycache_debug_log= 0; \
+ }
+#else
+#define KEYCACHE_DEBUG_OPEN
+#define KEYCACHE_DEBUG_CLOSE
+#endif /* defined(KEYCACHE_DEBUG_LOG) */
+
+#if defined(KEYCACHE_DEBUG_LOG) && defined(KEYCACHE_DEBUG)
+#define KEYCACHE_DBUG_PRINT(l, m) \
+ { if (keycache_debug_log) fprintf(keycache_debug_log, "%s: ", l); \
+ keycache_debug_print m; }
+
+#define KEYCACHE_DBUG_ASSERT(a) \
+ { if (! (a) && keycache_debug_log) fclose(keycache_debug_log); \
+ assert(a); }
+#else
+#define KEYCACHE_DBUG_PRINT(l, m) DBUG_PRINT(l, m)
+#define KEYCACHE_DBUG_ASSERT(a) DBUG_ASSERT(a)
+#endif /* defined(KEYCACHE_DEBUG_LOG) && defined(KEYCACHE_DEBUG) */
+
+#if defined(KEYCACHE_DEBUG) || !defined(DBUG_OFF)
+static long keycache_thread_id;
+#define KEYCACHE_THREAD_TRACE(l) \
+ KEYCACHE_DBUG_PRINT(l,("|thread %ld",keycache_thread_id))
+
+#define KEYCACHE_THREAD_TRACE_BEGIN(l) \
+ { struct st_my_thread_var *thread_var= my_thread_var; \
+ keycache_thread_id= thread_var->id; \
+ KEYCACHE_DBUG_PRINT(l,("[thread %ld",keycache_thread_id)) }
+
+#define KEYCACHE_THREAD_TRACE_END(l) \
+ KEYCACHE_DBUG_PRINT(l,("]thread %ld",keycache_thread_id))
+#else
+#define KEYCACHE_THREAD_TRACE_BEGIN(l)
+#define KEYCACHE_THREAD_TRACE_END(l)
+#define KEYCACHE_THREAD_TRACE(l)
+#endif /* defined(KEYCACHE_DEBUG) || !defined(DBUG_OFF) */
+
+#define BLOCK_NUMBER(b) \
+ ((uint) (((char*)(b)-(char *) keycache->block_root)/sizeof(BLOCK_LINK)))
+#define HASH_LINK_NUMBER(h) \
+ ((uint) (((char*)(h)-(char *) keycache->hash_link_root)/sizeof(HASH_LINK)))
+
+#if (defined(KEYCACHE_TIMEOUT) && !defined(__WIN__)) || defined(KEYCACHE_DEBUG)
+static int keycache_pthread_cond_wait(pthread_cond_t *cond,
+ pthread_mutex_t *mutex);
+#else
+#define keycache_pthread_cond_wait pthread_cond_wait
+#endif
- /* Init of disk_buffert */
- /* Returns blocks in use */
- /* ARGSUSED */
+#if defined(KEYCACHE_DEBUG)
+static int keycache_pthread_mutex_lock(pthread_mutex_t *mutex);
+static void keycache_pthread_mutex_unlock(pthread_mutex_t *mutex);
+static int keycache_pthread_cond_signal(pthread_cond_t *cond);
+#else
+#define keycache_pthread_mutex_lock pthread_mutex_lock
+#define keycache_pthread_mutex_unlock pthread_mutex_unlock
+#define keycache_pthread_cond_signal pthread_cond_signal
+#endif /* defined(KEYCACHE_DEBUG) */
+
+static uint next_power(uint value)
+{
+ uint old_value= 1;
+ while (value)
+ {
+ old_value= value;
+ value&= value-1;
+ }
+ return (old_value << 1);
+}
-int init_key_cache(ulong use_mem)
+
+/*
+ Initialize a key cache
+
+ SYNOPSIS
+ init_key_cache()
+ keycache pointer to a key cache data structure
+ key_cache_block_size size of blocks to keep cached data
+ use_mem total memory to use for the key cache
+ division_limit division limit (may be zero)
+ age_threshold age threshold (may be zero)
+
+ RETURN VALUE
+ number of blocks in the key cache, if successful,
+ 0 - otherwise.
+
+ NOTES.
+ if keycache->key_cache_inited != 0 we assume that the key cache
+ is already initialized. This is for now used by myisamchk, but shouldn't
+ be something that a program should rely on!
+
+ It's assumed that no two threads call this function simultaneously
+ referring to the same key cache handle.
+
+*/
+
+int init_key_cache(KEY_CACHE *keycache, uint key_cache_block_size,
+ ulong use_mem, uint division_limit,
+ uint age_threshold)
{
- uint blocks,length;
+ uint blocks, hash_links, length;
+ int error;
DBUG_ENTER("init_key_cache");
+ DBUG_ASSERT(key_cache_block_size >= 512);
- if (key_cache_inited && _my_disk_blocks > 0)
+ KEYCACHE_DEBUG_OPEN;
+ if (keycache->key_cache_inited && keycache->disk_blocks > 0)
{
- DBUG_PRINT("warning",("key cache already in use")); /* purecov: inspected */
- DBUG_RETURN(0); /* purecov: inspected */
+ DBUG_PRINT("warning",("key cache already in use"));
+ DBUG_RETURN(0);
}
- if (! key_cache_inited)
+
+ keycache->global_cache_w_requests= keycache->global_cache_r_requests= 0;
+ keycache->global_cache_read= keycache->global_cache_write= 0;
+ keycache->disk_blocks= -1;
+ if (! keycache->key_cache_inited)
{
- key_cache_inited=TRUE;
- _my_disk_blocks= -1;
- key_cache_shift=my_bit_log2(key_cache_block_size);
- DBUG_PRINT("info",("key_cache_block_size: %u key_cache_shift: %u",
- key_cache_block_size, key_cache_shift));
-#ifndef DBUG_OFF
- _my_printed=0;
-#endif
+ keycache->key_cache_inited= 1;
+ keycache->in_init= 0;
+ pthread_mutex_init(&keycache->cache_lock, MY_MUTEX_INIT_FAST);
+ keycache->resize_queue.last_thread= NULL;
}
- blocks= (uint) (use_mem/(sizeof(SEC_LINK)+sizeof(SEC_LINK*)*5/4+
- key_cache_block_size));
- /* No use to have very few blocks */
- if (blocks >= 8 && _my_disk_blocks < 0)
+ keycache->key_cache_mem_size= use_mem;
+ keycache->key_cache_block_size= key_cache_block_size;
+ keycache->key_cache_shift= my_bit_log2(key_cache_block_size);
+ DBUG_PRINT("info", ("key_cache_block_size: %u",
+ key_cache_block_size));
+
+ blocks= (uint) (use_mem / (sizeof(BLOCK_LINK) + 2 * sizeof(HASH_LINK) +
+ sizeof(HASH_LINK*) * 5/4 + key_cache_block_size));
+ /* It doesn't make sense to have too few blocks (less than 8) */
+ if (blocks >= 8 && keycache->disk_blocks < 0)
{
- for (;;)
+ for ( ; ; )
{
- /* Set my_hash_blocks to the next bigger 2 power */
- _my_hash_blocks=(uint) 1 << (my_bit_log2(blocks*5/4)+1);
- while ((length=(uint) blocks*sizeof(SEC_LINK)+
- sizeof(SEC_LINK*)*_my_hash_blocks)+
- ((ulong) blocks << key_cache_shift) >
- use_mem)
- blocks--;
- if ((_my_block_mem=my_malloc_lock((ulong) blocks << key_cache_shift,
- MYF(0))))
+ /* Set my_hash_entries to the next bigger 2 power */
+ if ((keycache->hash_entries= next_power(blocks)) < blocks * 5/4)
+ keycache->hash_entries<<= 1;
+ hash_links= 2 * blocks;
+#if defined(MAX_THREADS)
+ if (hash_links < MAX_THREADS + blocks - 1)
+ hash_links= MAX_THREADS + blocks - 1;
+#endif
+ while ((length= (ALIGN_SIZE(blocks * sizeof(BLOCK_LINK)) +
+ ALIGN_SIZE(hash_links * sizeof(HASH_LINK)) +
+ ALIGN_SIZE(sizeof(HASH_LINK*) *
+ keycache->hash_entries))) +
+ ((ulong) blocks << keycache->key_cache_shift) > use_mem)
+ blocks--;
+ /* Allocate memory for cache page buffers */
+ if ((keycache->block_mem=
+ my_malloc_lock((ulong) blocks * keycache->key_cache_block_size,
+ MYF(0))))
{
- if ((_my_block_root=(SEC_LINK*) my_malloc((uint) length,MYF(0))) != 0)
- break;
- my_free_lock(_my_block_mem,MYF(0));
+ /*
+ Allocate memory for blocks, hash_links and hash entries;
+ For each block 2 hash links are allocated
+ */
+ if ((keycache->block_root= (BLOCK_LINK*) my_malloc((uint) length,
+ MYF(0))))
+ break;
+ my_free_lock(keycache->block_mem, MYF(0));
+ keycache->block_mem= 0;
}
if (blocks < 8)
- goto err;
- blocks=blocks/4*3;
+ {
+ my_errno= ENOMEM;
+ goto err;
+ }
+ blocks= blocks / 4*3;
}
- _my_disk_blocks=(int) blocks;
- _my_hash_root= (SEC_LINK**) (_my_block_root+blocks);
- bzero((byte*) _my_hash_root,_my_hash_blocks*sizeof(SEC_LINK*));
- _my_used_first=_my_used_last=0;
- _my_blocks_used=_my_disk_blocks_used=_my_blocks_changed=0;
- _my_cache_w_requests=_my_cache_r_requests=_my_cache_read=_my_cache_write=0;
- DBUG_PRINT("exit",("disk_blocks: %d block_root: %lx _my_hash_blocks: %d hash_root: %lx",
- _my_disk_blocks,_my_block_root,_my_hash_blocks,
- _my_hash_root));
- }
- bzero((gptr) changed_blocks,sizeof(changed_blocks[0])*CHANGED_BLOCKS_HASH);
- bzero((gptr) file_blocks,sizeof(file_blocks[0])*CHANGED_BLOCKS_HASH);
- DBUG_RETURN((int) blocks);
+ keycache->blocks_unused= (ulong) blocks;
+ keycache->disk_blocks= (int) blocks;
+ keycache->hash_links= hash_links;
+ keycache->hash_root= (HASH_LINK**) ((char*) keycache->block_root +
+ ALIGN_SIZE(blocks*sizeof(BLOCK_LINK)));
+ keycache->hash_link_root= (HASH_LINK*) ((char*) keycache->hash_root +
+ ALIGN_SIZE((sizeof(HASH_LINK*) *
+ keycache->hash_entries)));
+ bzero((byte*) keycache->block_root,
+ keycache->disk_blocks * sizeof(BLOCK_LINK));
+ bzero((byte*) keycache->hash_root,
+ keycache->hash_entries * sizeof(HASH_LINK*));
+ bzero((byte*) keycache->hash_link_root,
+ keycache->hash_links * sizeof(HASH_LINK));
+ keycache->hash_links_used= 0;
+ keycache->free_hash_list= NULL;
+ keycache->blocks_used= keycache->blocks_changed= 0;
+
+ keycache->global_blocks_changed= 0;
+ keycache->blocks_available=0; /* For debugging */
+
+ /* The LRU chain is empty after initialization */
+ keycache->used_last= NULL;
+ keycache->used_ins= NULL;
+ keycache->free_block_list= NULL;
+ keycache->keycache_time= 0;
+ keycache->warm_blocks= 0;
+ keycache->min_warm_blocks= (division_limit ?
+ blocks * division_limit / 100 + 1 :
+ blocks);
+ keycache->age_threshold= (age_threshold ?
+ blocks * age_threshold / 100 :
+ blocks);
+
+ keycache->cnt_for_resize_op= 0;
+ keycache->resize_in_flush= 0;
+ keycache->can_be_used= 1;
+
+ keycache->waiting_for_hash_link.last_thread= NULL;
+ keycache->waiting_for_block.last_thread= NULL;
+ DBUG_PRINT("exit",
+ ("disk_blocks: %d block_root: 0x%lx hash_entries: %d\
+ hash_root: 0x%lx hash_links: %d hash_link_root: 0x%lx",
+ keycache->disk_blocks, keycache->block_root,
+ keycache->hash_entries, keycache->hash_root,
+ keycache->hash_links, keycache->hash_link_root));
+ bzero((gptr) keycache->changed_blocks,
+ sizeof(keycache->changed_blocks[0]) * CHANGED_BLOCKS_HASH);
+ bzero((gptr) keycache->file_blocks,
+ sizeof(keycache->file_blocks[0]) * CHANGED_BLOCKS_HASH);
+ }
+
+ keycache->blocks= keycache->disk_blocks > 0 ? keycache->disk_blocks : 0;
+ DBUG_RETURN((int) keycache->disk_blocks);
err:
- my_errno=ENOMEM;
+ error= my_errno;
+ keycache->disk_blocks= 0;
+ keycache->blocks= 0;
+ if (keycache->block_mem)
+ {
+ my_free_lock((gptr) keycache->block_mem, MYF(0));
+ keycache->block_mem= NULL;
+ }
+ if (keycache->block_root)
+ {
+ my_free((gptr) keycache->block_root, MYF(0));
+ keycache->block_root= NULL;
+ }
+ my_errno= error;
+ keycache->can_be_used= 0;
DBUG_RETURN(0);
-} /* init_key_cache */
+}
/*
- Resize the key cache
+ Resize a key cache
SYNOPSIS
resize_key_cache()
- use_mem Bytes to use for new key cache
+ keycache pointer to a key cache data structure
+ key_cache_block_size size of blocks to keep cached data
+ use_mem total memory to use for the new key cache
+ division_limit new division limit (if not zero)
+ age_threshold new age threshold (if not zero)
+
+ RETURN VALUE
+ number of blocks in the key cache, if successful,
+ 0 - otherwise.
+
+ NOTES.
+ The function first compares the memory size and the block size parameters
+ with the key cache values.
+
+ If they differ the function free the the memory allocated for the
+ old key cache blocks by calling the end_key_cache function and
+ then rebuilds the key cache with new blocks by calling
+ init_key_cache.
+
+ The function starts the operation only when all other threads
+ performing operations with the key cache let her to proceed
+ (when cnt_for_resize=0).
+*/
+
+int resize_key_cache(KEY_CACHE *keycache, uint key_cache_block_size,
+ ulong use_mem, uint division_limit,
+ uint age_threshold)
+{
+ int blocks;
+ struct st_my_thread_var *thread;
+ KEYCACHE_WQUEUE *wqueue;
+ DBUG_ENTER("resize_key_cache");
+
+ if (!keycache->key_cache_inited)
+ DBUG_RETURN(keycache->disk_blocks);
+
+ if(key_cache_block_size == keycache->key_cache_block_size &&
+ use_mem == keycache->key_cache_mem_size)
+ {
+ change_key_cache_param(keycache, division_limit, age_threshold);
+ DBUG_RETURN(keycache->disk_blocks);
+ }
+
+ keycache_pthread_mutex_lock(&keycache->cache_lock);
+
+ wqueue= &keycache->resize_queue;
+ thread= my_thread_var;
+ link_into_queue(wqueue, thread);
+
+ while (wqueue->last_thread->next != thread)
+ {
+ keycache_pthread_cond_wait(&thread->suspend, &keycache->cache_lock);
+ }
- RETURN VALUES
- 0 Error
- # number of blocks in key cache
+ keycache->resize_in_flush= 1;
+ if (flush_all_key_blocks(keycache))
+ {
+ /* TODO: if this happens, we should write a warning in the log file ! */
+ keycache->resize_in_flush= 0;
+ blocks= 0;
+ keycache->can_be_used= 0;
+ goto finish;
+ }
+ keycache->resize_in_flush= 0;
+ keycache->can_be_used= 0;
+ while (keycache->cnt_for_resize_op)
+ {
+ KEYCACHE_DBUG_PRINT("resize_key_cache: wait",
+ ("suspend thread %ld", thread->id));
+ keycache_pthread_cond_wait(&thread->suspend, &keycache->cache_lock);
+ }
+
+ end_key_cache(keycache, 0); /* Don't free mutex */
+ /* The following will work even if use_mem is 0 */
+ blocks= init_key_cache(keycache, key_cache_block_size, use_mem,
+ division_limit, age_threshold);
+
+finish:
+ unlink_from_queue(wqueue, thread);
+ /* Signal for the next resize request to proceeed if any */
+ if (wqueue->last_thread)
+ {
+ KEYCACHE_DBUG_PRINT("resize_key_cache: signal",
+ ("thread %ld", wqueue->last_thread->next->id));
+ keycache_pthread_cond_signal(&wqueue->last_thread->next->suspend);
+ }
+ keycache_pthread_mutex_unlock(&keycache->cache_lock);
+ return blocks;
+}
+
+
+/*
+ Increment counter blocking resize key cache operation
*/
+static inline void inc_counter_for_resize_op(KEY_CACHE *keycache)
+{
+ keycache->cnt_for_resize_op++;
+}
-int resize_key_cache(ulong use_mem)
+/*
+ Decrement counter blocking resize key cache operation;
+ Signal the operation to proceed when counter becomes equal zero
+*/
+static inline void dec_counter_for_resize_op(KEY_CACHE *keycache)
{
- int block;
- pthread_mutex_lock(&THR_LOCK_keycache);
- if (flush_all_key_blocks())
+ struct st_my_thread_var *last_thread;
+ if (!--keycache->cnt_for_resize_op &&
+ (last_thread= keycache->resize_queue.last_thread))
{
- /* TODO: If this happens, we should write a warning in the log file ! */
- pthread_mutex_unlock(&THR_LOCK_keycache);
- return 0;
+ KEYCACHE_DBUG_PRINT("dec_counter_for_resize_op: signal",
+ ("thread %ld", last_thread->next->id));
+ keycache_pthread_cond_signal(&last_thread->next->suspend);
}
- end_key_cache();
- /* The following will work even if memory is 0 */
- block=init_key_cache(use_mem);
- pthread_mutex_unlock(&THR_LOCK_keycache);
- return block;
}
+/*
+ Change the key cache parameters
+
+ SYNOPSIS
+ change_key_cache_param()
+ keycache pointer to a key cache data structure
+ division_limit new division limit (if not zero)
+ age_threshold new age threshold (if not zero)
+
+ RETURN VALUE
+ none
+
+ NOTES.
+ Presently the function resets the key cache parameters
+ concerning midpoint insertion strategy - division_limit and
+ age_threshold.
+*/
+
+void change_key_cache_param(KEY_CACHE *keycache, uint division_limit,
+ uint age_threshold)
+{
+ DBUG_ENTER("change_key_cache_param");
+
+ keycache_pthread_mutex_lock(&keycache->cache_lock);
+ if (division_limit)
+ keycache->min_warm_blocks= (keycache->disk_blocks *
+ division_limit / 100 + 1);
+ if (age_threshold)
+ keycache->age_threshold= (keycache->disk_blocks *
+ age_threshold / 100);
+ keycache_pthread_mutex_unlock(&keycache->cache_lock);
+ DBUG_VOID_RETURN;
+}
+
+
+/*
+ Remove key_cache from memory
+
+ SYNOPSIS
+ end_key_cache()
+ keycache key cache handle
+ cleanup Complete free (Free also mutex for key cache)
- /* Remove key_cache from memory */
+ RETURN VALUE
+ none
+*/
-void end_key_cache(void)
+void end_key_cache(KEY_CACHE *keycache, my_bool cleanup)
{
DBUG_ENTER("end_key_cache");
- if (! _my_blocks_changed)
+ DBUG_PRINT("enter", ("key_cache: 0x%lx", keycache));
+
+ if (!keycache->key_cache_inited)
+ DBUG_VOID_RETURN;
+
+ if (keycache->disk_blocks > 0)
{
- if (_my_disk_blocks > 0)
+ if (keycache->block_mem)
{
- my_free_lock((gptr) _my_block_mem,MYF(0));
- my_free((gptr) _my_block_root,MYF(0));
- _my_disk_blocks= -1;
+ my_free_lock((gptr) keycache->block_mem, MYF(0));
+ keycache->block_mem= NULL;
+ my_free((gptr) keycache->block_root, MYF(0));
+ keycache->block_root= NULL;
}
+ keycache->disk_blocks= -1;
+ /* Reset blocks_changed to be safe if flush_all_key_blocks is called */
+ keycache->blocks_changed= 0;
+ }
+
+ DBUG_PRINT("status", ("used: %d changed: %d w_requests: %lu "
+ "writes: %lu r_requests: %lu reads: %lu",
+ keycache->blocks_used, keycache->global_blocks_changed,
+ (ulong) keycache->global_cache_w_requests,
+ (ulong) keycache->global_cache_write,
+ (ulong) keycache->global_cache_r_requests,
+ (ulong) keycache->global_cache_read));
+
+ if (cleanup)
+ {
+ pthread_mutex_destroy(&keycache->cache_lock);
+ keycache->key_cache_inited= keycache->can_be_used= 0;
+ KEYCACHE_DEBUG_CLOSE;
}
- key_cache_inited=0;
- _my_hash_blocks=_my_blocks_used=0;
- DBUG_PRINT("status",
- ("used: %d changed: %d w_requests: %lu writes: %lu "
- "r_requests: %lu reads: %lu",
- _my_blocks_used, _my_blocks_changed,
- (ulong) _my_cache_w_requests, (ulong) _my_cache_write,
- (ulong) _my_cache_r_requests, (ulong) _my_cache_read));
DBUG_VOID_RETURN;
} /* end_key_cache */
-static inline void link_into_file_blocks(SEC_LINK *next, int file)
+/*
+ Link a thread into double-linked queue of waiting threads.
+
+ SYNOPSIS
+ link_into_queue()
+ wqueue pointer to the queue structure
+ thread pointer to the thread to be added to the queue
+
+ RETURN VALUE
+ none
+
+ NOTES.
+ Queue is represented by a circular list of the thread structures
+ The list is double-linked of the type (**prev,*next), accessed by
+ a pointer to the last element.
+*/
+
+static void link_into_queue(KEYCACHE_WQUEUE *wqueue,
+ struct st_my_thread_var *thread)
+{
+ struct st_my_thread_var *last;
+ if (! (last= wqueue->last_thread))
+ {
+ /* Queue is empty */
+ thread->next= thread;
+ thread->prev= &thread->next;
+ }
+ else
+ {
+ thread->prev= last->next->prev;
+ last->next->prev= &thread->next;
+ thread->next= last->next;
+ last->next= thread;
+ }
+ wqueue->last_thread= thread;
+}
+
+/*
+ Unlink a thread from double-linked queue of waiting threads
+
+ SYNOPSIS
+ unlink_from_queue()
+ wqueue pointer to the queue structure
+ thread pointer to the thread to be removed from the queue
+
+ RETURN VALUE
+ none
+
+ NOTES.
+ See NOTES for link_into_queue
+*/
+
+static void unlink_from_queue(KEYCACHE_WQUEUE *wqueue,
+ struct st_my_thread_var *thread)
+{
+ KEYCACHE_DBUG_PRINT("unlink_from_queue", ("thread %ld", thread->id));
+ if (thread->next == thread)
+ /* The queue contains only one member */
+ wqueue->last_thread= NULL;
+ else
+ {
+ thread->next->prev= thread->prev;
+ *thread->prev=thread->next;
+ if (wqueue->last_thread == thread)
+ wqueue->last_thread= STRUCT_PTR(struct st_my_thread_var, next,
+ thread->prev);
+ }
+ thread->next= NULL;
+}
+
+
+/*
+ Add a thread to single-linked queue of waiting threads
+
+ SYNOPSIS
+ add_to_queue()
+ wqueue pointer to the queue structure
+ thread pointer to the thread to be added to the queue
+
+ RETURN VALUE
+ none
+
+ NOTES.
+ Queue is represented by a circular list of the thread structures
+ The list is single-linked of the type (*next), accessed by a pointer
+ to the last element.
+*/
+
+static inline void add_to_queue(KEYCACHE_WQUEUE *wqueue,
+ struct st_my_thread_var *thread)
+{
+ struct st_my_thread_var *last;
+ if (! (last= wqueue->last_thread))
+ thread->next= thread;
+ else
+ {
+ thread->next= last->next;
+ last->next= thread;
+ }
+ wqueue->last_thread= thread;
+}
+
+
+/*
+ Remove all threads from queue signaling them to proceed
+
+ SYNOPSIS
+ realease_queue()
+ wqueue pointer to the queue structure
+ thread pointer to the thread to be added to the queue
+
+ RETURN VALUE
+ none
+
+ NOTES.
+ See notes for add_to_queue
+ When removed from the queue each thread is signaled via condition
+ variable thread->suspend.
+*/
+
+static void release_queue(KEYCACHE_WQUEUE *wqueue)
{
- reg1 SEC_LINK **ptr= &file_blocks[(uint) file & CHANGED_BLOCKS_MASK];
- next->prev_changed= ptr;
- if ((next->next_changed= *ptr))
- (*ptr)->prev_changed= &next->next_changed;
- *ptr=next;
+ struct st_my_thread_var *last= wqueue->last_thread;
+ struct st_my_thread_var *next= last->next;
+ struct st_my_thread_var *thread;
+ do
+ {
+ thread=next;
+ KEYCACHE_DBUG_PRINT("release_queue: signal", ("thread %ld", thread->id));
+ keycache_pthread_cond_signal(&thread->suspend);
+ next=thread->next;
+ thread->next= NULL;
+ }
+ while (thread != last);
+ wqueue->last_thread= NULL;
}
-static inline void relink_into_file_blocks(SEC_LINK *next, int file)
+/*
+ Unlink a block from the chain of dirty/clean blocks
+*/
+
+static inline void unlink_changed(BLOCK_LINK *block)
{
- reg1 SEC_LINK **ptr= &file_blocks[(uint) file & CHANGED_BLOCKS_MASK];
- if (next->next_changed)
- next->next_changed->prev_changed=next->prev_changed;
- *next->prev_changed=next->next_changed;
- next->prev_changed= ptr;
- if ((next->next_changed= *ptr))
- (*ptr)->prev_changed= &next->next_changed;
- *ptr=next;
+ if (block->next_changed)
+ block->next_changed->prev_changed= block->prev_changed;
+ *block->prev_changed= block->next_changed;
}
-static inline void link_changed_to_file(SEC_LINK *next,int file)
+
+/*
+ Link a block into the chain of dirty/clean blocks
+*/
+
+static inline void link_changed(BLOCK_LINK *block, BLOCK_LINK **phead)
{
- reg1 SEC_LINK **ptr= &file_blocks[(uint) file & CHANGED_BLOCKS_MASK];
- if (next->next_changed)
- next->next_changed->prev_changed=next->prev_changed;
- *next->prev_changed=next->next_changed;
- next->prev_changed= ptr;
- if ((next->next_changed= *ptr))
- (*ptr)->prev_changed= &next->next_changed;
- *ptr=next;
- next->changed=0;
- _my_blocks_changed--;
+ block->prev_changed= phead;
+ if ((block->next_changed= *phead))
+ (*phead)->prev_changed= &block->next_changed;
+ *phead= block;
}
-static inline void link_file_to_changed(SEC_LINK *next)
+
+/*
+ Unlink a block from the chain of dirty/clean blocks, if it's asked for,
+ and link it to the chain of clean blocks for the specified file
+*/
+
+static void link_to_file_list(KEY_CACHE *keycache,
+ BLOCK_LINK *block, int file, my_bool unlink)
{
- reg1 SEC_LINK **ptr= &changed_blocks[(uint) next->file & CHANGED_BLOCKS_MASK];
- if (next->next_changed)
- next->next_changed->prev_changed=next->prev_changed;
- *next->prev_changed=next->next_changed;
- next->prev_changed= ptr;
- if ((next->next_changed= *ptr))
- (*ptr)->prev_changed= &next->next_changed;
- *ptr=next;
- next->changed=1;
- _my_blocks_changed++;
+ if (unlink)
+ unlink_changed(block);
+ link_changed(block, &keycache->file_blocks[FILE_HASH(file)]);
+ if (block->status & BLOCK_CHANGED)
+ {
+ block->status&= ~BLOCK_CHANGED;
+ keycache->blocks_changed--;
+ keycache->global_blocks_changed--;
+ }
}
-#if !defined(DBUG_OFF) && !defined(EXTRA_DEBUG)
-#define DBUG_OFF /* This should work */
+/*
+ Unlink a block from the chain of clean blocks for the specified
+ file and link it to the chain of dirty blocks for this file
+*/
+
+static inline void link_to_changed_list(KEY_CACHE *keycache,
+ BLOCK_LINK *block)
+{
+ unlink_changed(block);
+ link_changed(block,
+ &keycache->changed_blocks[FILE_HASH(block->hash_link->file)]);
+ block->status|=BLOCK_CHANGED;
+ keycache->blocks_changed++;
+ keycache->global_blocks_changed++;
+}
+
+
+/*
+ Link a block to the LRU chain at the beginning or at the end of
+ one of two parts.
+
+ SYNOPSIS
+ link_block()
+ keycache pointer to a key cache data structure
+ block pointer to the block to link to the LRU chain
+ hot <-> to link the block into the hot subchain
+ at_end <-> to link the block at the end of the subchain
+
+ RETURN VALUE
+ none
+
+ NOTES.
+ The LRU chain is represented by a curcular list of block structures.
+ The list is double-linked of the type (**prev,*next) type.
+ The LRU chain is divided into two parts - hot and warm.
+ There are two pointers to access the last blocks of these two
+ parts. The beginning of the warm part follows right after the
+ end of the hot part.
+ Only blocks of the warm part can be used for replacement.
+ The first block from the beginning of this subchain is always
+ taken for eviction (keycache->last_used->next)
+
+ LRU chain: +------+ H O T +------+
+ +----| end |----...<----| beg |----+
+ | +------+last +------+ |
+ v<-link in latest hot (new end) |
+ | link in latest warm (new end)->^
+ | +------+ W A R M +------+ |
+ +----| beg |---->...----| end |----+
+ +------+ +------+ins
+ first for eviction
+*/
+
+static void link_block(KEY_CACHE *keycache, BLOCK_LINK *block, my_bool hot,
+ my_bool at_end)
+{
+ BLOCK_LINK *ins;
+ BLOCK_LINK **pins;
+
+ KEYCACHE_DBUG_ASSERT(! (block->hash_link && block->hash_link->requests));
+ if (!hot && keycache->waiting_for_block.last_thread)
+ {
+ /* Signal that in the LRU warm sub-chain an available block has appeared */
+ struct st_my_thread_var *last_thread=
+ keycache->waiting_for_block.last_thread;
+ struct st_my_thread_var *first_thread= last_thread->next;
+ struct st_my_thread_var *next_thread= first_thread;
+ HASH_LINK *hash_link= (HASH_LINK *) first_thread->opt_info;
+ struct st_my_thread_var *thread;
+ do
+ {
+ thread= next_thread;
+ next_thread= thread->next;
+ /*
+ We notify about the event all threads that ask
+ for the same page as the first thread in the queue
+ */
+ if ((HASH_LINK *) thread->opt_info == hash_link)
+ {
+ KEYCACHE_DBUG_PRINT("link_block: signal", ("thread %ld", thread->id));
+ keycache_pthread_cond_signal(&thread->suspend);
+ unlink_from_queue(&keycache->waiting_for_block, thread);
+ block->requests++;
+ }
+ }
+ while (thread != last_thread);
+ hash_link->block= block;
+ KEYCACHE_THREAD_TRACE("link_block: after signaling");
+#if defined(KEYCACHE_DEBUG)
+ KEYCACHE_DBUG_PRINT("link_block",
+ ("linked,unlinked block %u status=%x #requests=%u #available=%u",
+ BLOCK_NUMBER(block), block->status,
+ block->requests, keycache->blocks_available));
+#endif
+ return;
+ }
+ pins= hot ? &keycache->used_ins : &keycache->used_last;
+ ins= *pins;
+ if (ins)
+ {
+ ins->next_used->prev_used= &block->next_used;
+ block->next_used= ins->next_used;
+ block->prev_used= &ins->next_used;
+ ins->next_used= block;
+ if (at_end)
+ *pins= block;
+ }
+ else
+ {
+ /* The LRU chain is empty */
+ keycache->used_last= keycache->used_ins= block->next_used= block;
+ block->prev_used= &block->next_used;
+ }
+ KEYCACHE_THREAD_TRACE("link_block");
+#if defined(KEYCACHE_DEBUG)
+ keycache->blocks_available++;
+ KEYCACHE_DBUG_PRINT("link_block",
+ ("linked block %u:%1u status=%x #requests=%u #available=%u",
+ BLOCK_NUMBER(block), at_end, block->status,
+ block->requests, keycache->blocks_available));
+ KEYCACHE_DBUG_ASSERT((ulong) keycache->blocks_available <=
+ keycache->blocks_used);
#endif
+}
-#ifndef DBUG_OFF
-static void test_key_cache(const char *where, my_bool lock);
+
+/*
+ Unlink a block from the LRU chain
+
+ SYNOPSIS
+ unlink_block()
+ keycache pointer to a key cache data structure
+ block pointer to the block to unlink from the LRU chain
+
+ RETURN VALUE
+ none
+
+ NOTES.
+ See NOTES for link_block
+*/
+
+static void unlink_block(KEY_CACHE *keycache, BLOCK_LINK *block)
+{
+ if (block->next_used == block)
+ /* The list contains only one member */
+ keycache->used_last= keycache->used_ins= NULL;
+ else
+ {
+ block->next_used->prev_used= block->prev_used;
+ *block->prev_used= block->next_used;
+ if (keycache->used_last == block)
+ keycache->used_last= STRUCT_PTR(BLOCK_LINK, next_used, block->prev_used);
+ if (keycache->used_ins == block)
+ keycache->used_ins=STRUCT_PTR(BLOCK_LINK, next_used, block->prev_used);
+ }
+ block->next_used= NULL;
+
+ KEYCACHE_THREAD_TRACE("unlink_block");
+#if defined(KEYCACHE_DEBUG)
+ keycache->blocks_available--;
+ KEYCACHE_DBUG_PRINT("unlink_block",
+ ("unlinked block %u status=%x #requests=%u #available=%u",
+ BLOCK_NUMBER(block), block->status,
+ block->requests, keycache->blocks_available));
+ KEYCACHE_DBUG_ASSERT(keycache->blocks_available >= 0);
+#endif
+}
+
+
+/*
+ Register requests for a block
+*/
+static void reg_requests(KEY_CACHE *keycache, BLOCK_LINK *block, int count)
+{
+ if (! block->requests)
+ /* First request for the block unlinks it */
+ unlink_block(keycache, block);
+ block->requests+=count;
+}
+
+
+/*
+ Unregister request for a block
+ linking it to the LRU chain if it's the last request
+
+ SYNOPSIS
+ unreg_request()
+ keycache pointer to a key cache data structure
+ block pointer to the block to link to the LRU chain
+ at_end <-> to link the block at the end of the LRU chain
+
+ RETURN VALUE
+ none
+
+ NOTES.
+ Every linking to the LRU chain decrements by one a special block
+ counter (if it's positive). If the at_end parameter is TRUE the block is
+ added either at the end of warm sub-chain or at the end of hot sub-chain.
+ It is added to the hot subchain if its counter is zero and number of
+ blocks in warm sub-chain is not less than some low limit (determined by
+ the division_limit parameter). Otherwise the block is added to the warm
+ sub-chain. If the at_end parameter is FALSE the block is always added
+ at beginning of the warm sub-chain.
+ Thus a warm block can be promoted to the hot sub-chain when its counter
+ becomes zero for the first time.
+ At the same time the block at the very beginning of the hot subchain
+ might be moved to the beginning of the warm subchain if it stays untouched
+ for a too long time (this time is determined by parameter age_threshold).
+*/
+
+static void unreg_request(KEY_CACHE *keycache,
+ BLOCK_LINK *block, int at_end)
+{
+ if (! --block->requests)
+ {
+ my_bool hot;
+ if (block->hits_left)
+ block->hits_left--;
+ hot= !block->hits_left && at_end &&
+ keycache->warm_blocks > keycache->min_warm_blocks;
+ if (hot)
+ {
+ if (block->temperature == BLOCK_WARM)
+ keycache->warm_blocks--;
+ block->temperature= BLOCK_HOT;
+ KEYCACHE_DBUG_PRINT("unreg_request", ("#warm_blocks=%u",
+ keycache->warm_blocks));
+ }
+ link_block(keycache, block, hot, (my_bool)at_end);
+ block->last_hit_time= keycache->keycache_time;
+ keycache->keycache_time++;
+
+ block= keycache->used_ins;
+ /* Check if we should link a hot block to the warm block */
+ if (block && keycache->keycache_time - block->last_hit_time >
+ keycache->age_threshold)
+ {
+ unlink_block(keycache, block);
+ link_block(keycache, block, 0, 0);
+ if (block->temperature != BLOCK_WARM)
+ {
+ keycache->warm_blocks++;
+ block->temperature= BLOCK_WARM;
+ }
+ KEYCACHE_DBUG_PRINT("unreg_request", ("#warm_blocks=%u",
+ keycache->warm_blocks));
+ }
+ }
+}
+
+/*
+ Remove a reader of the page in block
+*/
+
+static inline void remove_reader(BLOCK_LINK *block)
+{
+ if (! --block->hash_link->requests && block->condvar)
+ keycache_pthread_cond_signal(block->condvar);
+}
+
+
+/*
+ Wait until the last reader of the page in block
+ signals on its termination
+*/
+
+static inline void wait_for_readers(KEY_CACHE *keycache, BLOCK_LINK *block)
+{
+ struct st_my_thread_var *thread= my_thread_var;
+ while (block->hash_link->requests)
+ {
+ KEYCACHE_DBUG_PRINT("wait_for_readers: wait",
+ ("suspend thread %ld block %u",
+ thread->id, BLOCK_NUMBER(block)));
+ block->condvar= &thread->suspend;
+ keycache_pthread_cond_wait(&thread->suspend, &keycache->cache_lock);
+ block->condvar= NULL;
+ }
+}
+
+
+/*
+ Add a hash link to a bucket in the hash_table
+*/
+
+static inline void link_hash(HASH_LINK **start, HASH_LINK *hash_link)
+{
+ if (*start)
+ (*start)->prev= &hash_link->next;
+ hash_link->next= *start;
+ hash_link->prev= start;
+ *start= hash_link;
+}
+
+
+/*
+ Remove a hash link from the hash table
+*/
+
+static void unlink_hash(KEY_CACHE *keycache, HASH_LINK *hash_link)
+{
+ KEYCACHE_DBUG_PRINT("unlink_hash", ("fd: %u pos_ %lu #requests=%u",
+ (uint) hash_link->file,(ulong) hash_link->diskpos, hash_link->requests));
+ KEYCACHE_DBUG_ASSERT(hash_link->requests == 0);
+ if ((*hash_link->prev= hash_link->next))
+ hash_link->next->prev= hash_link->prev;
+ hash_link->block= NULL;
+ if (keycache->waiting_for_hash_link.last_thread)
+ {
+ /* Signal that a free hash link has appeared */
+ struct st_my_thread_var *last_thread=
+ keycache->waiting_for_hash_link.last_thread;
+ struct st_my_thread_var *first_thread= last_thread->next;
+ struct st_my_thread_var *next_thread= first_thread;
+ KEYCACHE_PAGE *first_page= (KEYCACHE_PAGE *) (first_thread->opt_info);
+ struct st_my_thread_var *thread;
+
+ hash_link->file= first_page->file;
+ hash_link->diskpos= first_page->filepos;
+ do
+ {
+ KEYCACHE_PAGE *page;
+ thread= next_thread;
+ page= (KEYCACHE_PAGE *) thread->opt_info;
+ next_thread= thread->next;
+ /*
+ We notify about the event all threads that ask
+ for the same page as the first thread in the queue
+ */
+ if (page->file == hash_link->file && page->filepos == hash_link->diskpos)
+ {
+ KEYCACHE_DBUG_PRINT("unlink_hash: signal", ("thread %ld", thread->id));
+ keycache_pthread_cond_signal(&thread->suspend);
+ unlink_from_queue(&keycache->waiting_for_hash_link, thread);
+ }
+ }
+ while (thread != last_thread);
+ link_hash(&keycache->hash_root[KEYCACHE_HASH(hash_link->file,
+ hash_link->diskpos)],
+ hash_link);
+ return;
+ }
+ hash_link->next= keycache->free_hash_list;
+ keycache->free_hash_list= hash_link;
+}
+
+
+/*
+ Get the hash link for a page
+*/
+
+static HASH_LINK *get_hash_link(KEY_CACHE *keycache,
+ int file, my_off_t filepos)
+{
+ reg1 HASH_LINK *hash_link, **start;
+ KEYCACHE_PAGE page;
+#if defined(KEYCACHE_DEBUG)
+ int cnt;
+#endif
+
+ KEYCACHE_DBUG_PRINT("get_hash_link", ("fd: %u pos: %lu",
+ (uint) file,(ulong) filepos));
+
+restart:
+ /*
+ Find the bucket in the hash table for the pair (file, filepos);
+ start contains the head of the bucket list,
+ hash_link points to the first member of the list
+ */
+ hash_link= *(start= &keycache->hash_root[KEYCACHE_HASH(file, filepos)]);
+#if defined(KEYCACHE_DEBUG)
+ cnt= 0;
+#endif
+ /* Look for an element for the pair (file, filepos) in the bucket chain */
+ while (hash_link &&
+ (hash_link->diskpos != filepos || hash_link->file != file))
+ {
+ hash_link= hash_link->next;
+#if defined(KEYCACHE_DEBUG)
+ cnt++;
+ if (! (cnt <= keycache->hash_links_used))
+ {
+ int i;
+ for (i=0, hash_link= *start ;
+ i < cnt ; i++, hash_link= hash_link->next)
+ {
+ KEYCACHE_DBUG_PRINT("get_hash_link", ("fd: %u pos: %lu",
+ (uint) hash_link->file,(ulong) hash_link->diskpos));
+ }
+ }
+ KEYCACHE_DBUG_ASSERT(cnt <= keycache->hash_links_used);
+#endif
+ }
+ if (! hash_link)
+ {
+ /* There is no hash link in the hash table for the pair (file, filepos) */
+ if (keycache->free_hash_list)
+ {
+ hash_link= keycache->free_hash_list;
+ keycache->free_hash_list= hash_link->next;
+ }
+ else if (keycache->hash_links_used < keycache->hash_links)
+ {
+ hash_link= &keycache->hash_link_root[keycache->hash_links_used++];
+ }
+ else
+ {
+ /* Wait for a free hash link */
+ struct st_my_thread_var *thread= my_thread_var;
+ KEYCACHE_DBUG_PRINT("get_hash_link", ("waiting"));
+ page.file= file;
+ page.filepos= filepos;
+ thread->opt_info= (void *) &page;
+ link_into_queue(&keycache->waiting_for_hash_link, thread);
+ KEYCACHE_DBUG_PRINT("get_hash_link: wait",
+ ("suspend thread %ld", thread->id));
+ keycache_pthread_cond_wait(&thread->suspend,
+ &keycache->cache_lock);
+ thread->opt_info= NULL;
+ goto restart;
+ }
+ hash_link->file= file;
+ hash_link->diskpos= filepos;
+ link_hash(start, hash_link);
+ }
+ /* Register the request for the page */
+ hash_link->requests++;
+
+ return hash_link;
+}
+
+
+/*
+ Get a block for the file page requested by a keycache read/write operation;
+ If the page is not in the cache return a free block, if there is none
+ return the lru block after saving its buffer if the page is dirty.
+
+ SYNOPSIS
+
+ find_key_block()
+ keycache pointer to a key cache data structure
+ file handler for the file to read page from
+ filepos position of the page in the file
+ init_hits_left how initialize the block counter for the page
+ wrmode <-> get for writing
+ page_st out {PAGE_READ,PAGE_TO_BE_READ,PAGE_WAIT_TO_BE_READ}
+
+ RETURN VALUE
+ Pointer to the found block if successful, 0 - otherwise
+
+ NOTES.
+ For the page from file positioned at filepos the function checks whether
+ the page is in the key cache specified by the first parameter.
+ If this is the case it immediately returns the block.
+ If not, the function first chooses a block for this page. If there is
+ no not used blocks in the key cache yet, the function takes the block
+ at the very beginning of the warm sub-chain. It saves the page in that
+ block if it's dirty before returning the pointer to it.
+ The function returns in the page_st parameter the following values:
+ PAGE_READ - if page already in the block,
+ PAGE_TO_BE_READ - if it is to be read yet by the current thread
+ WAIT_TO_BE_READ - if it is to be read by another thread
+ If an error occurs THE BLOCK_ERROR bit is set in the block status.
+ It might happen that there are no blocks in LRU chain (in warm part) -
+ all blocks are unlinked for some read/write operations. Then the function
+ waits until first of this operations links any block back.
+*/
+
+static BLOCK_LINK *find_key_block(KEY_CACHE *keycache,
+ File file, my_off_t filepos,
+ int init_hits_left,
+ int wrmode, int *page_st)
+{
+ HASH_LINK *hash_link;
+ BLOCK_LINK *block;
+ int error= 0;
+ int page_status;
+
+ DBUG_ENTER("find_key_block");
+ KEYCACHE_THREAD_TRACE("find_key_block:begin");
+ DBUG_PRINT("enter", ("fd: %u pos %lu wrmode: %lu",
+ (uint) file, (ulong) filepos, (uint) wrmode));
+ KEYCACHE_DBUG_PRINT("find_key_block", ("fd: %u pos: %lu wrmode: %lu",
+ (uint) file, (ulong) filepos,
+ (uint) wrmode));
+#if !defined(DBUG_OFF) && defined(EXTRA_DEBUG)
+ DBUG_EXECUTE("check_keycache2",
+ test_key_cache(keycache, "start of find_key_block", 0););
+#endif
+
+restart:
+ /* Find the hash link for the requested page (file, filepos) */
+ hash_link= get_hash_link(keycache, file, filepos);
+
+ page_status= -1;
+ if ((block= hash_link->block) &&
+ block->hash_link == hash_link && (block->status & BLOCK_READ))
+ page_status= PAGE_READ;
+
+ if (wrmode && keycache->resize_in_flush)
+ {
+ /* This is a write request during the flush phase of a resize operation */
+
+ if (page_status != PAGE_READ)
+ {
+ /* We don't need the page in the cache: we are going to write on disk */
+ hash_link->requests--;
+ unlink_hash(keycache, hash_link);
+ return 0;
+ }
+ if (!(block->status & BLOCK_IN_FLUSH))
+ {
+ hash_link->requests--;
+ /*
+ Remove block to invalidate the page in the block buffer
+ as we are going to write directly on disk.
+ Although we have an exlusive lock for the updated key part
+ the control can be yieded by the current thread as we might
+ have unfinished readers of other key parts in the block
+ buffer. Still we are guaranteed not to have any readers
+ of the key part we are writing into until the block is
+ removed from the cache as we set the BLOCL_REASSIGNED
+ flag (see the code below that handles reading requests).
+ */
+ free_block(keycache, block);
+ return 0;
+ }
+ /* Wait intil the page is flushed on disk */
+ hash_link->requests--;
+ {
+ struct st_my_thread_var *thread= my_thread_var;
+ add_to_queue(&block->wqueue[COND_FOR_SAVED], thread);
+ do
+ {
+ KEYCACHE_DBUG_PRINT("find_key_block: wait",
+ ("suspend thread %ld", thread->id));
+ keycache_pthread_cond_wait(&thread->suspend,
+ &keycache->cache_lock);
+ }
+ while(thread->next);
+ }
+ /* Invalidate page in the block if it has not been done yet */
+ if (block->status)
+ free_block(keycache, block);
+ return 0;
+ }
+
+ if (page_status == PAGE_READ &&
+ (block->status & (BLOCK_IN_SWITCH | BLOCK_REASSIGNED)))
+ {
+ /* This is a request for a page to be removed from cache */
+
+ KEYCACHE_DBUG_PRINT("find_key_block",
+ ("request for old page in block %u "
+ "wrmode: %d block->status: %d",
+ BLOCK_NUMBER(block), wrmode, block->status));
+ /*
+ Only reading requests can proceed until the old dirty page is flushed,
+ all others are to be suspended, then resubmitted
+ */
+ if (!wrmode && !(block->status & BLOCK_REASSIGNED))
+ reg_requests(keycache, block, 1);
+ else
+ {
+ hash_link->requests--;
+ KEYCACHE_DBUG_PRINT("find_key_block",
+ ("request waiting for old page to be saved"));
+ {
+ struct st_my_thread_var *thread= my_thread_var;
+ /* Put the request into the queue of those waiting for the old page */
+ add_to_queue(&block->wqueue[COND_FOR_SAVED], thread);
+ /* Wait until the request can be resubmitted */
+ do
+ {
+ KEYCACHE_DBUG_PRINT("find_key_block: wait",
+ ("suspend thread %ld", thread->id));
+ keycache_pthread_cond_wait(&thread->suspend,
+ &keycache->cache_lock);
+ }
+ while(thread->next);
+ }
+ KEYCACHE_DBUG_PRINT("find_key_block",
+ ("request for old page resubmitted"));
+ /* Resubmit the request */
+ goto restart;
+ }
+ }
+ else
+ {
+ /* This is a request for a new page or for a page not to be removed */
+ if (! block)
+ {
+ /* No block is assigned for the page yet */
+ if (keycache->blocks_unused)
+ {
+ if (keycache->free_block_list)
+ {
+ /* There is a block in the free list. */
+ block= keycache->free_block_list;
+ keycache->free_block_list= block->next_used;
+ block->next_used= NULL;
+ }
+ else
+ {
+ /* There are some never used blocks, take first of them */
+ block= &keycache->block_root[keycache->blocks_used];
+ block->buffer= ADD_TO_PTR(keycache->block_mem,
+ ((ulong) keycache->blocks_used*
+ keycache->key_cache_block_size),
+ byte*);
+ keycache->blocks_used++;
+ }
+ keycache->blocks_unused--;
+ block->status= 0;
+ block->length= 0;
+ block->offset= keycache->key_cache_block_size;
+ block->requests= 1;
+ block->temperature= BLOCK_COLD;
+ block->hits_left= init_hits_left;
+ block->last_hit_time= 0;
+ link_to_file_list(keycache, block, file, 0);
+ block->hash_link= hash_link;
+ hash_link->block= block;
+ page_status= PAGE_TO_BE_READ;
+ KEYCACHE_DBUG_PRINT("find_key_block",
+ ("got free or never used block %u",
+ BLOCK_NUMBER(block)));
+ }
+ else
+ {
+ /* There are no never used blocks, use a block from the LRU chain */
+
+ /*
+ Wait until a new block is added to the LRU chain;
+ several threads might wait here for the same page,
+ all of them must get the same block
+ */
+
+ if (! keycache->used_last)
+ {
+ struct st_my_thread_var *thread= my_thread_var;
+ thread->opt_info= (void *) hash_link;
+ link_into_queue(&keycache->waiting_for_block, thread);
+ do
+ {
+ KEYCACHE_DBUG_PRINT("find_key_block: wait",
+ ("suspend thread %ld", thread->id));
+ keycache_pthread_cond_wait(&thread->suspend,
+ &keycache->cache_lock);
+ }
+ while (thread->next);
+ thread->opt_info= NULL;
+ }
+ block= hash_link->block;
+ if (! block)
+ {
+ /*
+ Take the first block from the LRU chain
+ unlinking it from the chain
+ */
+ block= keycache->used_last->next_used;
+ block->hits_left= init_hits_left;
+ block->last_hit_time= 0;
+ reg_requests(keycache, block,1);
+ hash_link->block= block;
+ }
+
+ if (block->hash_link != hash_link &&
+ ! (block->status & BLOCK_IN_SWITCH) )
+ {
+ /* this is a primary request for a new page */
+ block->status|= BLOCK_IN_SWITCH;
+
+ KEYCACHE_DBUG_PRINT("find_key_block",
+ ("got block %u for new page", BLOCK_NUMBER(block)));
+
+ if (block->status & BLOCK_CHANGED)
+ {
+ /* The block contains a dirty page - push it out of the cache */
+
+ KEYCACHE_DBUG_PRINT("find_key_block", ("block is dirty"));
+
+ keycache_pthread_mutex_unlock(&keycache->cache_lock);
+ /*
+ The call is thread safe because only the current
+ thread might change the block->hash_link value
+ */
+ error= my_pwrite(block->hash_link->file,
+ block->buffer+block->offset,
+ block->length - block->offset,
+ block->hash_link->diskpos+ block->offset,
+ MYF(MY_NABP | MY_WAIT_IF_FULL));
+ keycache_pthread_mutex_lock(&keycache->cache_lock);
+ keycache->global_cache_write++;
+ }
+
+ block->status|= BLOCK_REASSIGNED;
+ if (block->hash_link)
+ {
+ /*
+ Wait until all pending read requests
+ for this page are executed
+ (we could have avoided this waiting, if we had read
+ a page in the cache in a sweep, without yielding control)
+ */
+ wait_for_readers(keycache, block);
+
+ /* Remove the hash link for this page from the hash table */
+ unlink_hash(keycache, block->hash_link);
+ /* All pending requests for this page must be resubmitted */
+ if (block->wqueue[COND_FOR_SAVED].last_thread)
+ release_queue(&block->wqueue[COND_FOR_SAVED]);
+ }
+ link_to_file_list(keycache, block, file,
+ (my_bool)(block->hash_link ? 1 : 0));
+ block->status= error? BLOCK_ERROR : 0;
+ block->length= 0;
+ block->offset= keycache->key_cache_block_size;
+ block->hash_link= hash_link;
+ page_status= PAGE_TO_BE_READ;
+
+ KEYCACHE_DBUG_ASSERT(block->hash_link->block == block);
+ KEYCACHE_DBUG_ASSERT(hash_link->block->hash_link == hash_link);
+ }
+ else
+ {
+ /* This is for secondary requests for a new page only */
+ KEYCACHE_DBUG_PRINT("find_key_block",
+ ("block->hash_link: %p hash_link: %p "
+ "block->status: %u", block->hash_link,
+ hash_link, block->status ));
+ page_status= (((block->hash_link == hash_link) &&
+ (block->status & BLOCK_READ)) ?
+ PAGE_READ : PAGE_WAIT_TO_BE_READ);
+ }
+ }
+ keycache->global_cache_read++;
+ }
+ else
+ {
+ reg_requests(keycache, block, 1);
+ KEYCACHE_DBUG_PRINT("find_key_block",
+ ("block->hash_link: %p hash_link: %p "
+ "block->status: %u", block->hash_link,
+ hash_link, block->status ));
+ page_status= (((block->hash_link == hash_link) &&
+ (block->status & BLOCK_READ)) ?
+ PAGE_READ : PAGE_WAIT_TO_BE_READ);
+ }
+ }
+
+ KEYCACHE_DBUG_ASSERT(page_status != -1);
+ *page_st=page_status;
+ KEYCACHE_DBUG_PRINT("find_key_block",
+ ("fd: %u pos %lu block->status %u page_status %lu",
+ (uint) file, (ulong) filepos, block->status,
+ (uint) page_status));
+
+#if !defined(DBUG_OFF) && defined(EXTRA_DEBUG)
+ DBUG_EXECUTE("check_keycache2",
+ test_key_cache(keycache, "end of find_key_block",0););
#endif
+ KEYCACHE_THREAD_TRACE("find_key_block:end");
+ DBUG_RETURN(block);
+}
+
+
+/*
+ Read into a key cache block buffer from disk.
+ SYNOPSIS
+
+ read_block()
+ keycache pointer to a key cache data structure
+ block block to which buffer the data is to be read
+ read_length size of data to be read
+ min_length at least so much data must be read
+ primary <-> the current thread will read the data
+
+ RETURN VALUE
+ None
+
+ NOTES.
+ The function either reads a page data from file to the block buffer,
+ or waits until another thread reads it. What page to read is determined
+ by a block parameter - reference to a hash link for this page.
+ If an error occurs THE BLOCK_ERROR bit is set in the block status.
+ We do not report error when the size of successfully read
+ portion is less than read_length, but not less than min_length.
+*/
+
+static void read_block(KEY_CACHE *keycache,
+ BLOCK_LINK *block, uint read_length,
+ uint min_length, my_bool primary)
+{
+ uint got_length;
- /*
- ** read a key_buffer
- ** filepos must point at a even key_cache_block_size block
- ** if return_buffer is set then the intern buffer is returned if
- ** it can be used
- ** Returns adress to where data is read
- */
+ /* On entry cache_lock is locked */
+
+ KEYCACHE_THREAD_TRACE("read_block");
+ if (primary)
+ {
+ /*
+ This code is executed only by threads
+ that submitted primary requests
+ */
+
+ KEYCACHE_DBUG_PRINT("read_block",
+ ("page to be read by primary request"));
+
+ /* Page is not in buffer yet, is to be read from disk */
+ keycache_pthread_mutex_unlock(&keycache->cache_lock);
+ /*
+ Here other threads may step in and register as secondary readers.
+ They will register in block->wqueue[COND_FOR_REQUESTED].
+ */
+ got_length= my_pread(block->hash_link->file, block->buffer,
+ read_length, block->hash_link->diskpos, MYF(0));
+ keycache_pthread_mutex_lock(&keycache->cache_lock);
+ if (got_length < min_length)
+ block->status|= BLOCK_ERROR;
+ else
+ {
+ block->status= BLOCK_READ;
+ block->length= got_length;
+ }
+ KEYCACHE_DBUG_PRINT("read_block",
+ ("primary request: new page in cache"));
+ /* Signal that all pending requests for this page now can be processed */
+ if (block->wqueue[COND_FOR_REQUESTED].last_thread)
+ release_queue(&block->wqueue[COND_FOR_REQUESTED]);
+ }
+ else
+ {
+ /*
+ This code is executed only by threads
+ that submitted secondary requests
+ */
+ KEYCACHE_DBUG_PRINT("read_block",
+ ("secondary request waiting for new page to be read"));
+ {
+ struct st_my_thread_var *thread= my_thread_var;
+ /* Put the request into a queue and wait until it can be processed */
+ add_to_queue(&block->wqueue[COND_FOR_REQUESTED], thread);
+ do
+ {
+ KEYCACHE_DBUG_PRINT("read_block: wait",
+ ("suspend thread %ld", thread->id));
+ keycache_pthread_cond_wait(&thread->suspend,
+ &keycache->cache_lock);
+ }
+ while (thread->next);
+ }
+ KEYCACHE_DBUG_PRINT("read_block",
+ ("secondary request: new page in cache"));
+ }
+}
+
+
+/*
+ Read a block of data from a cached file into a buffer;
+
+ SYNOPSIS
+
+ key_cache_read()
+ keycache pointer to a key cache data structure
+ file handler for the file for the block of data to be read
+ filepos position of the block of data in the file
+ level determines the weight of the data
+ buff buffer to where the data must be placed
+ length length of the buffer
+ block_length length of the block in the key cache buffer
+ return_buffer return pointer to the key cache buffer with the data
+
+ RETURN VALUE
+ Returns address from where the data is placed if sucessful, 0 - otherwise.
+
+ NOTES.
+ The function ensures that a block of data of size length from file
+ positioned at filepos is in the buffers for some key cache blocks.
+ Then the function either copies the data into the buffer buff, or,
+ if return_buffer is TRUE, it just returns the pointer to the key cache
+ buffer with the data.
+ Filepos must be a multiple of 'block_length', but it doesn't
+ have to be a multiple of key_cache_block_size;
+*/
-byte *key_cache_read(File file, my_off_t filepos, byte *buff, uint length,
+byte *key_cache_read(KEY_CACHE *keycache,
+ File file, my_off_t filepos, int level,
+ byte *buff, uint length,
uint block_length __attribute__((unused)),
int return_buffer __attribute__((unused)))
{
- reg1 SEC_LINK *next;
int error=0;
+ uint offset= 0;
+ byte *start= buff;
DBUG_ENTER("key_cache_read");
- DBUG_PRINT("enter", ("file %u, filepos %lu, length %u",
- (uint) file, (ulong) filepos, length));
+ DBUG_PRINT("enter", ("fd: %u pos: %lu length: %u",
+ (uint) file, (ulong) filepos, length));
-#ifndef THREAD
- if (block_length > key_cache_block_size)
- return_buffer=0;
-#endif
- if (_my_disk_blocks > 0)
- { /* We have key_cacheing */
- byte *start=buff;
+ if (keycache->can_be_used)
+ {
+ /* Key cache is used */
+ reg1 BLOCK_LINK *block;
uint read_length;
- pthread_mutex_lock(&THR_LOCK_keycache);
- if (_my_disk_blocks <= 0) /* Resize failed */
- {
- pthread_mutex_unlock(&THR_LOCK_keycache);
- goto no_key_cache;
- }
+ uint status;
+ int page_st;
+
+ offset= (uint) (filepos & (keycache->key_cache_block_size-1));
+ /* Read data in key_cache_block_size increments */
do
{
- _my_cache_r_requests++;
- read_length= (length > key_cache_block_size ? key_cache_block_size :
- length);
- if (!(next=find_key_block(file,filepos,&error)))
+ keycache_pthread_mutex_lock(&keycache->cache_lock);
+ if (!keycache->can_be_used)
{
- pthread_mutex_unlock(&THR_LOCK_keycache);
- DBUG_RETURN ((byte*) 0); /* Got a fatal error */
+ keycache_pthread_mutex_unlock(&keycache->cache_lock);
+ goto no_key_cache;
}
- if (error)
- { /* Didn't find it in cache */
- if (my_pread(file,next->buffer,read_length,filepos,MYF(MY_NABP)))
- {
- pthread_mutex_unlock(&THR_LOCK_keycache);
- DBUG_RETURN((byte*) 0);
- }
- _my_cache_read++;
+ filepos-= offset;
+ read_length= length;
+ set_if_smaller(read_length, keycache->key_cache_block_size-offset);
+ KEYCACHE_DBUG_ASSERT(read_length > 0);
+
+#ifndef THREAD
+ if (block_length > keycache->key_cache_block_size || offset)
+ return_buffer=0;
+#endif
+
+ inc_counter_for_resize_op(keycache);
+ keycache->global_cache_r_requests++;
+ block=find_key_block(keycache, file, filepos, level, 0, &page_st);
+ if (block->status != BLOCK_ERROR && page_st != PAGE_READ)
+ {
+ /* The requested page is to be read into the block buffer */
+ read_block(keycache, block,
+ keycache->key_cache_block_size, read_length+offset,
+ (my_bool)(page_st == PAGE_TO_BE_READ));
}
-#ifndef THREAD /* buffer may be used a long time */
- if (return_buffer)
+ else if (! (block->status & BLOCK_ERROR) &&
+ block->length < read_length + offset)
{
- pthread_mutex_unlock(&THR_LOCK_keycache);
- DBUG_RETURN (next->buffer);
+ /*
+ Impossible if nothing goes wrong:
+ this could only happen if we are using a file with
+ small key blocks and are trying to read outside the file
+ */
+ my_errno= -1;
+ block->status|= BLOCK_ERROR;
}
+
+ if (! ((status= block->status) & BLOCK_ERROR))
+ {
+#ifndef THREAD
+ if (! return_buffer)
#endif
- if (! (read_length & 511))
- bmove512(buff,next->buffer,read_length);
- else
- memcpy(buff,next->buffer,(size_t) read_length);
- buff+=read_length;
- filepos+=read_length;
+ {
+#if !defined(SERIALIZED_READ_FROM_CACHE)
+ keycache_pthread_mutex_unlock(&keycache->cache_lock);
+#endif
+
+ /* Copy data from the cache buffer */
+ if (!(read_length & 511))
+ bmove512(buff, block->buffer+offset, read_length);
+ else
+ memcpy(buff, block->buffer+offset, (size_t) read_length);
+
+#if !defined(SERIALIZED_READ_FROM_CACHE)
+ keycache_pthread_mutex_lock(&keycache->cache_lock);
+#endif
+ }
+ }
+
+ remove_reader(block);
+ /*
+ Link the block into the LRU chain
+ if it's the last submitted request for the block
+ */
+ unreg_request(keycache, block, 1);
+
+ dec_counter_for_resize_op(keycache);
+
+ keycache_pthread_mutex_unlock(&keycache->cache_lock);
+
+ if (status & BLOCK_ERROR)
+ DBUG_RETURN((byte *) 0);
+
+#ifndef THREAD
+ /* This is only true if we where able to read everything in one block */
+ if (return_buffer)
+ return (block->buffer);
+#endif
+ buff+= read_length;
+ filepos+= read_length+offset;
+ offset= 0;
+
} while ((length-= read_length));
- pthread_mutex_unlock(&THR_LOCK_keycache);
DBUG_RETURN(start);
}
-no_key_cache:
- _my_cache_r_requests++;
- _my_cache_read++;
- if (my_pread(file,(byte*) buff,length,filepos,MYF(MY_NABP)))
- error=1;
- DBUG_RETURN(error ? (byte*) 0 : buff);
-} /* key_cache_read */
+no_key_cache: /* Key cache is not used */
+ /* We can't use mutex here as the key cache may not be initialized */
+ keycache->global_cache_r_requests++;
+ keycache->global_cache_read++;
+ if (my_pread(file, (byte*) buff, length, filepos+offset, MYF(MY_NABP)))
+ error= 1;
+ DBUG_RETURN(error ? (byte*) 0 : start);
+}
- /* write a key_buffer */
- /* We don't have to use pwrite because of write locking */
- /* buff must point at a even key_cache_block_size block */
-int key_cache_write(File file, my_off_t filepos, byte *buff, uint length,
- uint block_length __attribute__((unused)),
- int dont_write)
-{
- reg1 SEC_LINK *next;
- int error=0;
- DBUG_ENTER("key_cache_write");
- DBUG_PRINT("enter", ("file %u, filepos %lu, length %u",
- (uint) file, (ulong) filepos, length));
+/*
+ Insert a block of file data from a buffer into key cache
- if (!dont_write)
- { /* Forced write of buffer */
- _my_cache_write++;
- if (my_pwrite(file,buff,length,filepos,MYF(MY_NABP | MY_WAIT_IF_FULL)))
- DBUG_RETURN(1);
- }
+ SYNOPSIS
+ key_cache_insert()
+ keycache pointer to a key cache data structure
+ file handler for the file to insert data from
+ filepos position of the block of data in the file to insert
+ level determines the weight of the data
+ buff buffer to read data from
+ length length of the data in the buffer
+
+ NOTES
+ This is used by MyISAM to move all blocks from a index file to the key
+ cache
+
+ RETURN VALUE
+ 0 if a success, 1 - otherwise.
+*/
-#if !defined(DBUG_OFF) && defined(EXTRA_DEBUG)
- DBUG_EXECUTE("check_keycache",test_key_cache("start of key_cache_write",1););
-#endif
- if (_my_disk_blocks > 0)
- { /* We have key_cacheing */
+int key_cache_insert(KEY_CACHE *keycache,
+ File file, my_off_t filepos, int level,
+ byte *buff, uint length)
+{
+ DBUG_ENTER("key_cache_insert");
+ DBUG_PRINT("enter", ("fd: %u pos: %lu length: %u",
+ (uint) file,(ulong) filepos, length));
+
+ if (keycache->can_be_used)
+ {
+ /* Key cache is used */
+ reg1 BLOCK_LINK *block;
uint read_length;
- pthread_mutex_lock(&THR_LOCK_keycache);
- if (_my_disk_blocks <= 0) /* If resize failed */
- {
- pthread_mutex_unlock(&THR_LOCK_keycache);
- goto no_key_cache;
- }
+ int page_st;
+ int error;
+ uint offset;
- _my_cache_w_requests++;
+ offset= (uint) (filepos & (keycache->key_cache_block_size-1));
do
{
- read_length= length > key_cache_block_size ? key_cache_block_size : length;
- if (!(next=find_key_block(file,filepos,&error)))
- goto end; /* Fatal error */
- if (!dont_write) /* If we wrote buff at start */
+ keycache_pthread_mutex_lock(&keycache->cache_lock);
+ if (!keycache->can_be_used)
+ {
+ keycache_pthread_mutex_unlock(&keycache->cache_lock);
+ DBUG_RETURN(0);
+ }
+ /* Read data into key cache from buff in key_cache_block_size incr. */
+ filepos-= offset;
+ read_length= length;
+ set_if_smaller(read_length, keycache->key_cache_block_size-offset);
+ KEYCACHE_DBUG_ASSERT(read_length > 0);
+
+ inc_counter_for_resize_op(keycache);
+ keycache->global_cache_r_requests++;
+ block= find_key_block(keycache, file, filepos, level, 0, &page_st);
+ if (block->status != BLOCK_ERROR && page_st != PAGE_READ)
{
- if (next->changed) /* Unlink from changed list */
- link_changed_to_file(next,next->file);
+ /* The requested page is to be read into the block buffer */
+#if !defined(SERIALIZED_READ_FROM_CACHE)
+ keycache_pthread_mutex_unlock(&keycache->cache_lock);
+ /*
+ Here other threads may step in and register as secondary readers.
+ They will register in block->wqueue[COND_FOR_REQUESTED].
+ */
+#endif
+
+ /* Copy data from buff */
+ if (!(read_length & 511))
+ bmove512(block->buffer+offset, buff, read_length);
+ else
+ memcpy(block->buffer+offset, buff, (size_t) read_length);
+
+#if !defined(SERIALIZED_READ_FROM_CACHE)
+ keycache_pthread_mutex_lock(&keycache->cache_lock);
+ /* Here we are alone again. */
+#endif
+ block->status= BLOCK_READ;
+ block->length= read_length+offset;
+ KEYCACHE_DBUG_PRINT("key_cache_insert",
+ ("primary request: new page in cache"));
+ /* Signal that all pending requests for this now can be processed. */
+ if (block->wqueue[COND_FOR_REQUESTED].last_thread)
+ release_queue(&block->wqueue[COND_FOR_REQUESTED]);
}
- else if (!next->changed)
- link_file_to_changed(next); /* Add to changed list */
- if (!(read_length & 511))
- bmove512(next->buffer,buff,read_length);
- else
- memcpy(next->buffer,buff,(size_t) read_length);
- buff+=read_length;
- filepos+=read_length;
+ remove_reader(block);
+ /*
+ Link the block into the LRU chain
+ if it's the last submitted request for the block
+ */
+ unreg_request(keycache, block, 1);
+
+ error= (block->status & BLOCK_ERROR);
+
+ dec_counter_for_resize_op(keycache);
+
+ keycache_pthread_mutex_unlock(&keycache->cache_lock);
+
+ if (error)
+ DBUG_RETURN(1);
+
+ buff+= read_length;
+ filepos+= read_length+offset;
+ offset= 0;
+
} while ((length-= read_length));
- error=0;
- pthread_mutex_unlock(&THR_LOCK_keycache);
- goto end;
}
+ DBUG_RETURN(0);
+}
-no_key_cache:
- if (dont_write)
- { /* We must write, no cache */
- _my_cache_w_requests++;
- _my_cache_write++;
- if (my_pwrite(file,(byte*) buff,length,filepos,
- MYF(MY_NABP | MY_WAIT_IF_FULL)))
- error=1;
- }
-end:
-#if !defined(DBUG_OFF) && defined(EXTRA_DEBUG)
- DBUG_EXECUTE("check_keycache",test_key_cache("end of key_cache_write",1););
-#endif
- DBUG_RETURN(error);
-} /* key_cache_write */
+/*
+ Write a buffer into a cached file.
+ SYNOPSIS
- /* Find block in cache */
- /* IF found sector and error is set then next->changed is cleared */
+ key_cache_write()
+ keycache pointer to a key cache data structure
+ file handler for the file to write data to
+ filepos position in the file to write data to
+ level determines the weight of the data
+ buff buffer with the data
+ length length of the buffer
+ dont_write if is 0 then all dirty pages involved in writing
+ should have been flushed from key cache
+
+ RETURN VALUE
+ 0 if a success, 1 - otherwise.
+
+ NOTES.
+ The function copies the data of size length from buff into buffers
+ for key cache blocks that are assigned to contain the portion of
+ the file starting with position filepos.
+ It ensures that this data is flushed to the file if dont_write is FALSE.
+ Filepos must be a multiple of 'block_length', but it doesn't
+ have to be a multiple of key_cache_block_size;
+*/
-static SEC_LINK *find_key_block(int file, my_off_t filepos, int *error)
+int key_cache_write(KEY_CACHE *keycache,
+ File file, my_off_t filepos, int level,
+ byte *buff, uint length,
+ uint block_length __attribute__((unused)),
+ int dont_write)
{
- reg1 SEC_LINK *next,**start;
- DBUG_ENTER("find_key_block");
- DBUG_PRINT("enter", ("file %u, filepos %lu",
- (uint) file, (ulong) filepos));
+ reg1 BLOCK_LINK *block;
+ int error=0;
+ DBUG_ENTER("key_cache_write");
+ DBUG_PRINT("enter",
+ ("fd: %u pos: %lu length: %u block_length: %u key_block_length: %u",
+ (uint) file, (ulong) filepos, length, block_length,
+ keycache ? keycache->key_cache_block_size : 0));
+
+ if (!dont_write)
+ {
+ /* Force writing from buff into disk */
+ keycache->global_cache_write++;
+ if (my_pwrite(file, buff, length, filepos, MYF(MY_NABP | MY_WAIT_IF_FULL)))
+ DBUG_RETURN(1);
+ }
#if !defined(DBUG_OFF) && defined(EXTRA_DEBUG)
- DBUG_EXECUTE("check_keycache2",test_key_cache("start of find_key_block",0););
+ DBUG_EXECUTE("check_keycache",
+ test_key_cache(keycache, "start of key_cache_write", 1););
#endif
- *error=0;
- next= *(start= &_my_hash_root[((ulong) (filepos >> key_cache_shift)+(ulong) file) &
- (_my_hash_blocks-1)]);
- while (next && (next->diskpos != filepos || next->file != file))
- next= next->next_hash;
+ if (keycache->can_be_used)
+ {
+ /* Key cache is used */
+ uint read_length;
+ int page_st;
+ uint offset;
- if (next)
- { /* Found block */
- if (next != _my_used_last)
- { /* Relink used-chain */
- if (next == _my_used_first)
- _my_used_first=next->next_used;
- else
+ offset= (uint) (filepos & (keycache->key_cache_block_size-1));
+ do
+ {
+ keycache_pthread_mutex_lock(&keycache->cache_lock);
+ if (!keycache->can_be_used)
{
- next->prev_used->next_used = next->next_used;
- next->next_used->prev_used = next->prev_used;
+ keycache_pthread_mutex_unlock(&keycache->cache_lock);
+ goto no_key_cache;
}
- next->prev_used=_my_used_last;
- _my_used_last->next_used=next;
- }
- }
- else
- { /* New block */
- if (_my_disk_blocks_used+1 <= (uint) _my_disk_blocks)
- { /* There are unused blocks */
- next= &_my_block_root[_my_blocks_used++]; /* Link in hash-chain */
- next->buffer=ADD_TO_PTR(_my_block_mem,
- ((ulong) _my_disk_blocks_used << key_cache_shift),
- byte*);
- /* link first in file_blocks */
- next->changed=0;
- link_into_file_blocks(next,file);
- _my_disk_blocks_used++;
- if (!_my_used_first)
- _my_used_first=next;
- if (_my_used_last)
- _my_used_last->next_used=next; /* Last in used-chain */
- }
- else
- { /* Reuse old block */
- next= _my_used_first;
- if (next->changed)
+ /* Write data in key_cache_block_size increments */
+ filepos-= offset;
+ read_length= length;
+ set_if_smaller(read_length, keycache->key_cache_block_size-offset);
+ KEYCACHE_DBUG_ASSERT(read_length > 0);
+
+ inc_counter_for_resize_op(keycache);
+ keycache->global_cache_w_requests++;
+ block= find_key_block(keycache, file, filepos, level, 1, &page_st);
+ if (!block)
{
- if (my_pwrite(next->file,next->buffer,key_cache_block_size,
- next->diskpos,
- MYF(MY_NABP | MY_WAIT_IF_FULL)))
- {
- *error=1;
- return((SEC_LINK*) 0);
+ /* It happens only for requests submitted during resize operation */
+ dec_counter_for_resize_op(keycache);
+ keycache_pthread_mutex_unlock(&keycache->cache_lock);
+ if (dont_write)
+ {
+ keycache->global_cache_w_requests++;
+ keycache->global_cache_write++;
+ if (my_pwrite(file, (byte*) buff, length, filepos,
+ MYF(MY_NABP | MY_WAIT_IF_FULL)))
+ error=1;
}
- _my_cache_write++;
- link_changed_to_file(next,file);
+ goto next_block;
}
- else
+
+ if (block->status != BLOCK_ERROR && page_st != PAGE_READ &&
+ (offset || read_length < keycache->key_cache_block_size))
+ read_block(keycache, block,
+ offset + read_length >= keycache->key_cache_block_size?
+ offset : keycache->key_cache_block_size,
+ offset,(my_bool)(page_st == PAGE_TO_BE_READ));
+
+ if (!dont_write)
{
- if (next->file == -1)
- link_into_file_blocks(next,file);
- else
- relink_into_file_blocks(next,file);
+ /* buff has been written to disk at start */
+ if ((block->status & BLOCK_CHANGED) &&
+ (!offset && read_length >= keycache->key_cache_block_size))
+ link_to_file_list(keycache, block, block->hash_link->file, 1);
}
- if (next->prev_hash) /* If in hash-link */
- if ((*next->prev_hash=next->next_hash) != 0) /* Remove from link */
- next->next_hash->prev_hash= next->prev_hash;
+ else if (! (block->status & BLOCK_CHANGED))
+ link_to_changed_list(keycache, block);
- _my_used_last->next_used=next;
- _my_used_first=next->next_used;
- }
- if (*start) /* Link in first in h.-chain */
- (*start)->prev_hash= &next->next_hash;
- next->next_hash= *start; next->prev_hash=start; *start=next;
- next->prev_used=_my_used_last;
- next->file=file;
- next->diskpos=filepos;
- *error=1; /* Block wasn't in memory */
- }
- _my_used_last=next;
+ set_if_smaller(block->offset, offset);
+ set_if_bigger(block->length, read_length+offset);
+
+ if (! (block->status & BLOCK_ERROR))
+ {
+ if (!(read_length & 511))
+ bmove512(block->buffer+offset, buff, read_length);
+ else
+ memcpy(block->buffer+offset, buff, (size_t) read_length);
+ }
+
+ block->status|=BLOCK_READ;
+
+ /* Unregister the request */
+ block->hash_link->requests--;
+ unreg_request(keycache, block, 1);
+
+ if (block->status & BLOCK_ERROR)
+ {
+ keycache_pthread_mutex_unlock(&keycache->cache_lock);
+ error= 1;
+ break;
+ }
+
+ dec_counter_for_resize_op(keycache);
+
+ keycache_pthread_mutex_unlock(&keycache->cache_lock);
+
+ next_block:
+ buff+= read_length;
+ filepos+= read_length+offset;
+ offset= 0;
+
+ } while ((length-= read_length));
+ goto end;
+ }
+
+no_key_cache:
+ /* Key cache is not used */
+ if (dont_write)
+ {
+ keycache->global_cache_w_requests++;
+ keycache->global_cache_write++;
+ if (my_pwrite(file, (byte*) buff, length, filepos,
+ MYF(MY_NABP | MY_WAIT_IF_FULL)))
+ error=1;
+ }
+
+end:
#if !defined(DBUG_OFF) && defined(EXTRA_DEBUG)
- DBUG_EXECUTE("check_keycache2",test_key_cache("end of find_key_block",0););
+ DBUG_EXECUTE("exec",
+ test_key_cache(keycache, "end of key_cache_write", 1););
#endif
- DBUG_RETURN(next);
-} /* find_key_block */
+ DBUG_RETURN(error);
+}
+
+/*
+ Free block: remove reference to it from hash table,
+ remove it from the chain file of dirty/clean blocks
+ and add it to the free list.
+*/
-static void free_block(SEC_LINK *used)
+static void free_block(KEY_CACHE *keycache, BLOCK_LINK *block)
{
- used->file= -1;
- used->changed=0;
- if (used != _my_used_first) /* Relink used-chain */
+ KEYCACHE_THREAD_TRACE("free block");
+ KEYCACHE_DBUG_PRINT("free_block",
+ ("block %u to be freed, hash_link %p",
+ BLOCK_NUMBER(block), block->hash_link));
+ if (block->hash_link)
{
- if (used == _my_used_last)
- _my_used_last=used->prev_used;
- else
- {
- used->prev_used->next_used = used->next_used;
- used->next_used->prev_used = used->prev_used;
- }
- used->next_used=_my_used_first;
- used->next_used->prev_used=used;
- _my_used_first=used;
+ /*
+ While waiting for readers to finish, new readers might request the
+ block. But since we set block->status|= BLOCK_REASSIGNED, they
+ will wait on block->wqueue[COND_FOR_SAVED]. They must be signalled
+ later.
+ */
+ block->status|= BLOCK_REASSIGNED;
+ wait_for_readers(keycache, block);
+ unlink_hash(keycache, block->hash_link);
}
- if ((*used->prev_hash=used->next_hash)) /* Relink hash-chain */
- used->next_hash->prev_hash= used->prev_hash;
- if (used->next_changed) /* Relink changed/file list */
- used->next_changed->prev_changed=used->prev_changed;
- *used->prev_changed=used->next_changed;
- used->prev_hash=0; used->next_hash=0; /* Safety */
-}
+ unlink_changed(block);
+ block->status= 0;
+ block->length= 0;
+ block->offset= keycache->key_cache_block_size;
+ KEYCACHE_THREAD_TRACE("free block");
+ KEYCACHE_DBUG_PRINT("free_block",
+ ("block is freed"));
+ unreg_request(keycache, block, 0);
+ block->hash_link= NULL;
+
+ /* Remove the free block from the LRU ring. */
+ unlink_block(keycache, block);
+ if (block->temperature == BLOCK_WARM)
+ keycache->warm_blocks--;
+ block->temperature= BLOCK_COLD;
+ /* Insert the free block in the free list. */
+ block->next_used= keycache->free_block_list;
+ keycache->free_block_list= block;
+ /* Keep track of the number of currently unused blocks. */
+ keycache->blocks_unused++;
+
+ /* All pending requests for this page must be resubmitted. */
+ if (block->wqueue[COND_FOR_SAVED].last_thread)
+ release_queue(&block->wqueue[COND_FOR_SAVED]);
+}
- /* Flush all changed blocks to disk. Free used blocks if requested */
-static int cmp_sec_link(SEC_LINK **a, SEC_LINK **b)
+static int cmp_sec_link(BLOCK_LINK **a, BLOCK_LINK **b)
{
- return (((*a)->diskpos < (*b)->diskpos) ? -1 :
- ((*a)->diskpos > (*b)->diskpos) ? 1 : 0);
+ return (((*a)->hash_link->diskpos < (*b)->hash_link->diskpos) ? -1 :
+ ((*a)->hash_link->diskpos > (*b)->hash_link->diskpos) ? 1 : 0);
}
-static int flush_cached_blocks(File file, SEC_LINK **cache, uint count)
+/*
+ Flush a portion of changed blocks to disk,
+ free used blocks if requested
+*/
+
+static int flush_cached_blocks(KEY_CACHE *keycache,
+ File file, BLOCK_LINK **cache,
+ BLOCK_LINK **end,
+ enum flush_type type)
{
- uint last_errno=0;
+ int error;
+ int last_errno= 0;
+ uint count= end-cache;
+
+ /* Don't lock the cache during the flush */
+ keycache_pthread_mutex_unlock(&keycache->cache_lock);
+ /*
+ As all blocks referred in 'cache' are marked by BLOCK_IN_FLUSH
+ we are guarunteed no thread will change them
+ */
qsort((byte*) cache, count, sizeof(*cache), (qsort_cmp) cmp_sec_link);
- for ( ; count-- ; cache++)
+
+ keycache_pthread_mutex_lock(&keycache->cache_lock);
+ for ( ; cache != end ; cache++)
{
- if (my_pwrite(file,(*cache)->buffer,key_cache_block_size,
- (*cache)->diskpos,
- MYF(MY_NABP | MY_WAIT_IF_FULL)))
+ BLOCK_LINK *block= *cache;
+
+ KEYCACHE_DBUG_PRINT("flush_cached_blocks",
+ ("block %u to be flushed", BLOCK_NUMBER(block)));
+ keycache_pthread_mutex_unlock(&keycache->cache_lock);
+ error= my_pwrite(file,
+ block->buffer+block->offset,
+ block->length - block->offset,
+ block->hash_link->diskpos+ block->offset,
+ MYF(MY_NABP | MY_WAIT_IF_FULL));
+ keycache_pthread_mutex_lock(&keycache->cache_lock);
+ keycache->global_cache_write++;
+ if (error)
{
+ block->status|= BLOCK_ERROR;
if (!last_errno)
- last_errno=errno ? errno : -1;
+ last_errno= errno ? errno : -1;
}
+ /*
+ Let to proceed for possible waiting requests to write to the block page.
+ It might happen only during an operation to resize the key cache.
+ */
+ if (block->wqueue[COND_FOR_SAVED].last_thread)
+ release_queue(&block->wqueue[COND_FOR_SAVED]);
+ /* type will never be FLUSH_IGNORE_CHANGED here */
+ if (! (type == FLUSH_KEEP || type == FLUSH_FORCE_WRITE))
+ {
+ keycache->blocks_changed--;
+ keycache->global_blocks_changed--;
+ free_block(keycache, block);
+ }
+ else
+ {
+ block->status&= ~BLOCK_IN_FLUSH;
+ link_to_file_list(keycache, block, file, 1);
+ unreg_request(keycache, block, 1);
+ }
+
}
return last_errno;
}
-static int flush_key_blocks_int(File file, enum flush_type type)
+/*
+ flush all key blocks for a file to disk, but don't do any mutex locks
+
+ flush_key_blocks_int()
+ keycache pointer to a key cache data structure
+ file handler for the file to flush to
+ flush_type type of the flush
+
+ NOTES
+ This function doesn't do any mutex locks because it needs to be called both
+ from flush_key_blocks and flush_all_key_blocks (the later one does the
+ mutex lock in the resize_key_cache() function).
+
+ RETURN
+ 0 ok
+ 1 error
+*/
+
+static int flush_key_blocks_int(KEY_CACHE *keycache,
+ File file, enum flush_type type)
{
- int error=0,last_errno=0;
- uint count=0;
- SEC_LINK *cache_buff[FLUSH_CACHE],**cache,**pos,**end;
- SEC_LINK *used,*next;
+ BLOCK_LINK *cache_buff[FLUSH_CACHE],**cache;
+ int last_errno= 0;
DBUG_ENTER("flush_key_blocks_int");
DBUG_PRINT("enter",("file: %d blocks_used: %d blocks_changed: %d",
- file,_my_blocks_used,_my_blocks_changed));
+ file, keycache->blocks_used, keycache->blocks_changed));
- cache=cache_buff; /* If no key cache */
- if (_my_disk_blocks > 0 &&
+#if !defined(DBUG_OFF) && defined(EXTRA_DEBUG)
+ DBUG_EXECUTE("check_keycache",
+ test_key_cache(keycache, "start of flush_key_blocks", 0););
+#endif
+
+ cache= cache_buff;
+ if (keycache->disk_blocks > 0 &&
(!my_disable_flush_key_blocks || type != FLUSH_KEEP))
{
-#if !defined(DBUG_OFF) && defined(EXTRA_DEBUG)
- DBUG_EXECUTE("check_keycache",test_key_cache("start of flush_key_blocks",0););
+ /* Key cache exists and flush is not disabled */
+ int error= 0;
+ uint count= 0;
+ BLOCK_LINK **pos,**end;
+ BLOCK_LINK *first_in_switch= NULL;
+ BLOCK_LINK *block, *next;
+#if defined(KEYCACHE_DEBUG)
+ uint cnt=0;
#endif
+
if (type != FLUSH_IGNORE_CHANGED)
{
- /* Count how many key blocks we have to cache to be able to
- write everything with so few seeks as possible */
-
- for (used=changed_blocks[(uint) file & CHANGED_BLOCKS_MASK];
- used ;
- used=used->next_changed)
+ /*
+ Count how many key blocks we have to cache to be able
+ to flush all dirty pages with minimum seek moves
+ */
+ for (block= keycache->changed_blocks[FILE_HASH(file)] ;
+ block ;
+ block= block->next_changed)
{
- if (used->file == file)
- count++;
- }
- /* Only allocate a new buffer if its bigger than the one we have */
- if (count > FLUSH_CACHE)
- {
- if (!(cache=(SEC_LINK**) my_malloc(sizeof(SEC_LINK*)*count,MYF(0))))
+ if (block->hash_link->file == file)
{
- cache=cache_buff; /* Fall back to safe buffer */
- count=FLUSH_CACHE;
+ count++;
+ KEYCACHE_DBUG_ASSERT(count<= keycache->blocks_used);
}
}
+ /* Allocate a new buffer only if its bigger than the one we have */
+ if (count > FLUSH_CACHE &&
+ !(cache= (BLOCK_LINK**) my_malloc(sizeof(BLOCK_LINK*)*count,
+ MYF(0))))
+ {
+ cache= cache_buff;
+ count= FLUSH_CACHE;
+ }
}
- /* Go through the keys and write them to buffer to be flushed */
- end=(pos=cache)+count;
- for (used=changed_blocks[(uint) file & CHANGED_BLOCKS_MASK];
- used ;
- used=next)
+ /* Retrieve the blocks and write them to a buffer to be flushed */
+restart:
+ end= (pos= cache)+count;
+ for (block= keycache->changed_blocks[FILE_HASH(file)] ;
+ block ;
+ block= next)
{
- next=used->next_changed;
- if (used->file == file)
+#if defined(KEYCACHE_DEBUG)
+ cnt++;
+ KEYCACHE_DBUG_ASSERT(cnt <= keycache->blocks_used);
+#endif
+ next= block->next_changed;
+ if (block->hash_link->file == file)
{
- if (type != FLUSH_IGNORE_CHANGED)
- {
- if (pos == end)
- {
- if ((error=flush_cached_blocks(file, cache, count)))
- last_errno=error;
- pos=cache;
- }
- *pos++=used;
- _my_cache_write++;
- }
- if (type != FLUSH_KEEP && type != FLUSH_FORCE_WRITE)
- {
- /* This will not destroy position or data */
- _my_blocks_changed--;
- free_block(used);
- }
- else
- link_changed_to_file(used,file);
+ /*
+ Mark the block with BLOCK_IN_FLUSH in order not to let
+ other threads to use it for new pages and interfere with
+ our sequence ot flushing dirty file pages
+ */
+ block->status|= BLOCK_IN_FLUSH;
+
+ if (! (block->status & BLOCK_IN_SWITCH))
+ {
+ /*
+ We care only for the blocks for which flushing was not
+ initiated by other threads as a result of page swapping
+ */
+ reg_requests(keycache, block, 1);
+ if (type != FLUSH_IGNORE_CHANGED)
+ {
+ /* It's not a temporary file */
+ if (pos == end)
+ {
+ /*
+ This happens only if there is not enough
+ memory for the big block
+ */
+ if ((error= flush_cached_blocks(keycache, file, cache,
+ end,type)))
+ last_errno=error;
+ /*
+ Restart the scan as some other thread might have changed
+ the changed blocks chain: the blocks that were in switch
+ state before the flush started have to be excluded
+ */
+ goto restart;
+ }
+ *pos++= block;
+ }
+ else
+ {
+ /* It's a temporary file */
+ keycache->blocks_changed--;
+ keycache->global_blocks_changed--;
+ free_block(keycache, block);
+ }
+ }
+ else
+ {
+ /* Link the block into a list of blocks 'in switch' */
+ unlink_changed(block);
+ link_changed(block, &first_in_switch);
+ }
}
}
if (pos != cache)
{
- if ((error=flush_cached_blocks(file, cache, (uint) (pos-cache))))
- last_errno=error;
+ if ((error= flush_cached_blocks(keycache, file, cache, pos, type)))
+ last_errno= error;
+ }
+ /* Wait until list of blocks in switch is empty */
+ while (first_in_switch)
+ {
+#if defined(KEYCACHE_DEBUG)
+ cnt= 0;
+#endif
+ block= first_in_switch;
+ {
+ struct st_my_thread_var *thread= my_thread_var;
+ add_to_queue(&block->wqueue[COND_FOR_SAVED], thread);
+ do
+ {
+ KEYCACHE_DBUG_PRINT("flush_key_blocks_int: wait",
+ ("suspend thread %ld", thread->id));
+ keycache_pthread_cond_wait(&thread->suspend,
+ &keycache->cache_lock);
+ }
+ while (thread->next);
+ }
+#if defined(KEYCACHE_DEBUG)
+ cnt++;
+ KEYCACHE_DBUG_ASSERT(cnt <= keycache->blocks_used);
+#endif
}
/* The following happens very seldom */
- if (type != FLUSH_KEEP && type != FLUSH_FORCE_WRITE)
+ if (! (type == FLUSH_KEEP || type == FLUSH_FORCE_WRITE))
{
- for (used=file_blocks[(uint) file & CHANGED_BLOCKS_MASK];
- used ;
- used=next)
+#if defined(KEYCACHE_DEBUG)
+ cnt=0;
+#endif
+ for (block= keycache->file_blocks[FILE_HASH(file)] ;
+ block ;
+ block= next)
{
- next=used->next_changed;
- if (used->file == file && (!used->changed ||
- type == FLUSH_IGNORE_CHANGED))
- free_block(used);
+#if defined(KEYCACHE_DEBUG)
+ cnt++;
+ KEYCACHE_DBUG_ASSERT(cnt <= keycache->blocks_used);
+#endif
+ next= block->next_changed;
+ if (block->hash_link->file == file &&
+ (! (block->status & BLOCK_CHANGED)
+ || type == FLUSH_IGNORE_CHANGED))
+ {
+ reg_requests(keycache, block, 1);
+ free_block(keycache, block);
+ }
}
}
+ }
+
#ifndef DBUG_OFF
- DBUG_EXECUTE("check_keycache",test_key_cache("end of flush_key_blocks",0););
+ DBUG_EXECUTE("check_keycache",
+ test_key_cache(keycache, "end of flush_key_blocks", 0););
#endif
- }
if (cache != cache_buff)
- my_free((gptr) cache,MYF(0));
+ my_free((gptr) cache, MYF(0));
if (last_errno)
- errno=last_errno; /* Return first error */
+ errno=last_errno; /* Return first error */
DBUG_RETURN(last_errno != 0);
}
/*
- Flush all blocks for a specific file to disk
+ Flush all blocks for a file to disk
SYNOPSIS
+
flush_key_blocks()
- file File descriptor
- type Type of flush operation
+ keycache pointer to a key cache data structure
+ file handler for the file to flush to
+ flush_type type of the flush
- RETURN VALUES
- 0 Ok
- 1 Error
+ RETURN
+ 0 ok
+ 1 error
*/
-int flush_key_blocks(File file, enum flush_type type)
+int flush_key_blocks(KEY_CACHE *keycache,
+ File file, enum flush_type type)
{
int res;
- pthread_mutex_lock(&THR_LOCK_keycache);
- res=flush_key_blocks_int(file, type);
- pthread_mutex_unlock(&THR_LOCK_keycache);
- return res;
+ DBUG_ENTER("flush_key_blocks");
+ DBUG_PRINT("enter", ("keycache: 0x%lx", keycache));
+
+ if (keycache->disk_blocks <= 0)
+ DBUG_RETURN(0);
+ keycache_pthread_mutex_lock(&keycache->cache_lock);
+ inc_counter_for_resize_op(keycache);
+ res= flush_key_blocks_int(keycache, file, type);
+ dec_counter_for_resize_op(keycache);
+ keycache_pthread_mutex_unlock(&keycache->cache_lock);
+ DBUG_RETURN(res);
}
/*
Flush all blocks in the key cache to disk
+*/
+
+static int flush_all_key_blocks(KEY_CACHE *keycache)
+{
+#if defined(KEYCACHE_DEBUG)
+ uint cnt=0;
+#endif
+ while (keycache->blocks_changed > 0)
+ {
+ BLOCK_LINK *block;
+ for (block= keycache->used_last->next_used ; ; block=block->next_used)
+ {
+ if (block->hash_link)
+ {
+#if defined(KEYCACHE_DEBUG)
+ cnt++;
+ KEYCACHE_DBUG_ASSERT(cnt <= keycache->blocks_used);
+#endif
+ if (flush_key_blocks_int(keycache, block->hash_link->file,
+ FLUSH_RELEASE))
+ return 1;
+ break;
+ }
+ if (block == keycache->used_last)
+ break;
+ }
+ }
+ return 0;
+}
+
+
+/*
+ Reset the counters of a key cache.
SYNOPSIS
- flush_all_key_blocks()
+ reset_key_cache_counters()
+ name the name of a key cache
+ key_cache pointer to the key kache to be reset
- NOTE
- We must have a lock on THR_LOCK_keycache before calling this function
+ DESCRIPTION
+ This procedure is used by process_key_caches() to reset the counters of all
+ currently used key caches, both the default one and the named ones.
- RETURN VALUES
- 0 Ok
- 1 Error
+ RETURN
+ 0 on success (always because it can't fail)
*/
-
-static int flush_all_key_blocks()
+int reset_key_cache_counters(const char *name, KEY_CACHE *key_cache)
{
- SEC_LINK **block, **end;
- for (block= changed_blocks, end= block+CHANGED_BLOCKS_HASH;
- block < end;
- block++
- )
+ DBUG_ENTER("reset_key_cache_counters");
+ if (!key_cache->key_cache_inited)
{
- while (*block)
- {
- if (flush_key_blocks_int((*block)->file, FLUSH_RELEASE))
- return 1;
- }
+ DBUG_PRINT("info", ("Key cache %s not initialized.", name));
+ DBUG_RETURN(0);
}
- return 0;
+ DBUG_PRINT("info", ("Resetting counters for key cache %s.", name));
+
+ key_cache->global_blocks_changed= 0; /* Key_blocks_not_flushed */
+ key_cache->global_cache_r_requests= 0; /* Key_read_requests */
+ key_cache->global_cache_read= 0; /* Key_reads */
+ key_cache->global_cache_w_requests= 0; /* Key_write_requests */
+ key_cache->global_cache_write= 0; /* Key_writes */
+ DBUG_RETURN(0);
}
#ifndef DBUG_OFF
+/*
+ Test if disk-cache is ok
+*/
+static void test_key_cache(KEY_CACHE *keycache __attribute__((unused)),
+ const char *where __attribute__((unused)),
+ my_bool lock __attribute__((unused)))
+{
+ /* TODO */
+}
+#endif
- /* Test if disk-cache is ok */
+#if defined(KEYCACHE_TIMEOUT)
-static void test_key_cache(const char *where, my_bool lock)
-{
- reg1 uint i,error;
- ulong found,changed;
- SEC_LINK *pos,**prev;
+#define KEYCACHE_DUMP_FILE "keycache_dump.txt"
+#define MAX_QUEUE_LEN 100
- if (lock)
- {
- pthread_mutex_lock(&THR_LOCK_keycache);
- if (_my_disk_blocks <= 0) /* No active key cache */
+
+static void keycache_dump(KEY_CACHE *keycache)
+{
+ FILE *keycache_dump_file=fopen(KEYCACHE_DUMP_FILE, "w");
+ struct st_my_thread_var *thread_var= my_thread_var;
+ struct st_my_thread_var *last;
+ struct st_my_thread_var *thread;
+ BLOCK_LINK *block;
+ HASH_LINK *hash_link;
+ KEYCACHE_PAGE *page;
+ uint i;
+
+ fprintf(keycache_dump_file, "thread:%u\n", thread->id);
+
+ i=0;
+ thread=last=waiting_for_hash_link.last_thread;
+ fprintf(keycache_dump_file, "queue of threads waiting for hash link\n");
+ if (thread)
+ do
{
- pthread_mutex_unlock(&THR_LOCK_keycache);
- return;
+ thread=thread->next;
+ page= (KEYCACHE_PAGE *) thread->opt_info;
+ fprintf(keycache_dump_file,
+ "thread:%u, (file,filepos)=(%u,%lu)\n",
+ thread->id,(uint) page->file,(ulong) page->filepos);
+ if (++i == MAX_QUEUE_LEN)
+ break;
}
- }
- found=error=0;
- for (i= 0 ; i < _my_hash_blocks ; i++)
- {
+ while (thread != last);
- for (pos= *(prev= &_my_hash_root[i]) ;
- pos && found < _my_blocks_used+2 ;
- found++, pos= *(prev= &pos->next_hash))
+ i=0;
+ thread=last=waiting_for_block.last_thread;
+ fprintf(keycache_dump_file, "queue of threads waiting for block\n");
+ if (thread)
+ do
{
- if (prev != pos->prev_hash)
- {
- error=1;
- DBUG_PRINT("error",
- ("hash: %d pos: %lx : prev: %lx != pos->prev: %lx",
- i,(ulong) pos,(ulong) prev,(ulong) pos->prev_hash));
- }
-
- if (((pos->diskpos >> key_cache_shift)+pos->file) % _my_hash_blocks != i)
- {
- DBUG_PRINT("error",("hash: %d pos: %lx : Wrong disk_buffer %ld",
- i,(ulong) pos,(ulong) pos->diskpos));
- error=1;
- }
+ thread=thread->next;
+ hash_link= (HASH_LINK *) thread->opt_info;
+ fprintf(keycache_dump_file,
+ "thread:%u hash_link:%u (file,filepos)=(%u,%lu)\n",
+ thread->id, (uint) HASH_LINK_NUMBER(hash_link),
+ (uint) hash_link->file,(ulong) hash_link->diskpos);
+ if (++i == MAX_QUEUE_LEN)
+ break;
}
- }
- if (found > _my_blocks_used)
+ while (thread != last);
+
+ for (i=0 ; i< keycache->blocks_used ; i++)
{
- DBUG_PRINT("error",("Found too many hash_pointers"));
- error=1;
- }
- if (error && !_my_printed)
- { /* Write all hash-pointers */
- _my_printed=1;
- for (i=0 ; i < _my_hash_blocks ; i++)
+ int j;
+ block= &keycache->block_root[i];
+ hash_link= block->hash_link;
+ fprintf(keycache_dump_file,
+ "block:%u hash_link:%d status:%x #requests=%u waiting_for_readers:%d\n",
+ i, (int) (hash_link ? HASH_LINK_NUMBER(hash_link) : -1),
+ block->status, block->requests, block->condvar ? 1 : 0);
+ for (j=0 ; j < 2; j++)
{
- DBUG_PRINT("loop",("hash: %d _my_hash_root: %lx",i,&_my_hash_root[i]));
- pos= _my_hash_root[i]; found=0;
- while (pos && found < 10)
+ KEYCACHE_WQUEUE *wqueue=&block->wqueue[j];
+ thread= last= wqueue->last_thread;
+ fprintf(keycache_dump_file, "queue #%d\n", j);
+ if (thread)
{
- DBUG_PRINT("loop",("pos: %lx prev: %lx next: %lx file: %d disk_buffer: %ld", (ulong) pos, (ulong) pos->prev_hash, (ulong) pos->next_hash, (ulong) pos->file, (ulong) pos->diskpos));
- found++; pos= pos->next_hash;
+ do
+ {
+ thread=thread->next;
+ fprintf(keycache_dump_file,
+ "thread:%u\n", thread->id);
+ if (++i == MAX_QUEUE_LEN)
+ break;
+ }
+ while (thread != last);
}
}
}
-
- found=changed=0;
-
- if ((pos=_my_used_first))
+ fprintf(keycache_dump_file, "LRU chain:");
+ block= keycache= used_last;
+ if (block)
{
- while (pos != _my_used_last && found < _my_blocks_used+2)
+ do
{
- found++;
- if (pos->changed)
- changed++;
- if (pos->next_used->prev_used != pos)
- {
- DBUG_PRINT("error",("pos: %lx next_used: %lx next_used->prev: %lx",
- (ulong) pos,
- (ulong) pos->next_used,
- (ulong) pos->next_used->prev_hash));
- error=1;
- }
- pos=pos->next_used;
+ block= block->next_used;
+ fprintf(keycache_dump_file,
+ "block:%u, ", BLOCK_NUMBER(block));
}
- found++;
- if (pos->changed)
- changed++;
+ while (block != keycache->used_last);
}
- if (found != _my_blocks_used)
+ fprintf(keycache_dump_file, "\n");
+
+ fclose(keycache_dump_file);
+}
+
+#endif /* defined(KEYCACHE_TIMEOUT) */
+
+#if defined(KEYCACHE_TIMEOUT) && !defined(__WIN__)
+
+
+static int keycache_pthread_cond_wait(pthread_cond_t *cond,
+ pthread_mutex_t *mutex)
+{
+ int rc;
+ struct timeval now; /* time when we started waiting */
+ struct timespec timeout; /* timeout value for the wait function */
+ struct timezone tz;
+#if defined(KEYCACHE_DEBUG)
+ int cnt=0;
+#endif
+
+ /* Get current time */
+ gettimeofday(&now, &tz);
+ /* Prepare timeout value */
+ timeout.tv_sec= now.tv_sec + KEYCACHE_TIMEOUT;
+ timeout.tv_nsec= now.tv_usec * 1000; /* timeval uses microseconds. */
+ /* timespec uses nanoseconds. */
+ /* 1 nanosecond = 1000 micro seconds. */
+ KEYCACHE_THREAD_TRACE_END("started waiting");
+#if defined(KEYCACHE_DEBUG)
+ cnt++;
+ if (cnt % 100 == 0)
+ fprintf(keycache_debug_log, "waiting...\n");
+ fflush(keycache_debug_log);
+#endif
+ rc= pthread_cond_timedwait(cond, mutex, &timeout);
+ KEYCACHE_THREAD_TRACE_BEGIN("finished waiting");
+#if defined(KEYCACHE_DEBUG)
+ if (rc == ETIMEDOUT)
{
- DBUG_PRINT("error",("Found %lu of %lu keyblocks",found,_my_blocks_used));
- error=1;
+ fprintf(keycache_debug_log,"aborted by keycache timeout\n");
+ fclose(keycache_debug_log);
+ abort();
}
+#endif
- for (i= 0 ; i < CHANGED_BLOCKS_HASH ; i++)
- {
- found=0;
- prev= &changed_blocks[i];
- for (pos= *prev ; pos && found < _my_blocks_used+2; pos=pos->next_changed)
- {
- found++;
- if (pos->prev_changed != prev)
- {
- DBUG_PRINT("error",("changed_block list %d doesn't point backwards properly",i));
- error=1;
- }
- prev= &pos->next_changed;
- if (((uint) pos->file & CHANGED_BLOCKS_MASK) != i)
- {
- DBUG_PRINT("error",("Wrong file %d in changed blocks: %d",pos->file,i));
- error=1;
- }
- changed--;
- }
- if (pos)
- {
- DBUG_PRINT("error",("changed_blocks %d has recursive link",i));
- error=1;
- }
+ if (rc == ETIMEDOUT)
+ keycache_dump();
- found=0;
- prev= &file_blocks[i];
- for (pos= *prev ; pos && found < _my_blocks_used+2; pos=pos->next_changed)
- {
- found++;
- if (pos->prev_changed != prev)
- {
- DBUG_PRINT("error",("file_block list %d doesn't point backwards properly",i));
- error=1;
- }
- prev= &pos->next_changed;
- if (((uint) pos->file & CHANGED_BLOCKS_MASK) != i)
- {
- DBUG_PRINT("error",("Wrong file %d in file_blocks: %d",pos->file,i));
- error=1;
- }
- }
- if (pos)
- {
- DBUG_PRINT("error",("File_blocks %d has recursive link",i));
- error=1;
- }
- }
- if (changed != 0)
+#if defined(KEYCACHE_DEBUG)
+ KEYCACHE_DBUG_ASSERT(rc != ETIMEDOUT);
+#else
+ assert(rc != ETIMEDOUT);
+#endif
+ return rc;
+}
+#else
+#if defined(KEYCACHE_DEBUG)
+static int keycache_pthread_cond_wait(pthread_cond_t *cond,
+ pthread_mutex_t *mutex)
+{
+ int rc;
+ KEYCACHE_THREAD_TRACE_END("started waiting");
+ rc= pthread_cond_wait(cond, mutex);
+ KEYCACHE_THREAD_TRACE_BEGIN("finished waiting");
+ return rc;
+}
+#endif
+#endif /* defined(KEYCACHE_TIMEOUT) && !defined(__WIN__) */
+
+#if defined(KEYCACHE_DEBUG)
+
+
+static int keycache_pthread_mutex_lock(pthread_mutex_t *mutex)
+{
+ int rc;
+ rc= pthread_mutex_lock(mutex);
+ KEYCACHE_THREAD_TRACE_BEGIN("");
+ return rc;
+}
+
+
+static void keycache_pthread_mutex_unlock(pthread_mutex_t *mutex)
+{
+ KEYCACHE_THREAD_TRACE_END("");
+ pthread_mutex_unlock(mutex);
+}
+
+
+static int keycache_pthread_cond_signal(pthread_cond_t *cond)
+{
+ int rc;
+ KEYCACHE_THREAD_TRACE("signal");
+ rc= pthread_cond_signal(cond);
+ return rc;
+}
+
+
+#if defined(KEYCACHE_DEBUG_LOG)
+
+
+static void keycache_debug_print(const char * fmt,...)
+{
+ va_list args;
+ va_start(args,fmt);
+ if (keycache_debug_log)
{
- DBUG_PRINT("error",("Found %lu blocks that wasn't in changed blocks",
- changed));
- error=1;
+ VOID(vfprintf(keycache_debug_log, fmt, args));
+ VOID(fputc('\n',keycache_debug_log));
}
- if (error)
- DBUG_PRINT("error",("Found error at %s",where));
- if (lock)
- pthread_mutex_unlock(&THR_LOCK_keycache);
- return;
-} /* test_key_cache */
-#endif
+ va_end(args);
+}
+#endif /* defined(KEYCACHE_DEBUG_LOG) */
+
+#if defined(KEYCACHE_DEBUG_LOG)
+
+
+void keycache_debug_log_close(void)
+{
+ if (keycache_debug_log)
+ fclose(keycache_debug_log);
+}
+#endif /* defined(KEYCACHE_DEBUG_LOG) */
+
+#endif /* defined(KEYCACHE_DEBUG) */
diff --git a/mysys/mf_keycaches.c b/mysys/mf_keycaches.c
new file mode 100644
index 00000000000..38fef31fdd4
--- /dev/null
+++ b/mysys/mf_keycaches.c
@@ -0,0 +1,364 @@
+/* Copyright (C) 2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+/*
+ Handling of multiple key caches
+
+ The idea is to have a thread safe hash on the table name,
+ with a default key cache value that is returned if the table name is not in
+ the cache.
+*/
+
+#include "mysys_priv.h"
+#include <keycache.h>
+#include <hash.h>
+#include <m_string.h>
+
+/*****************************************************************************
+ General functions to handle SAFE_HASH objects.
+
+ A SAFE_HASH object is used to store the hash, the mutex and default value
+ needed by the rest of the key cache code.
+ This is a separate struct to make it easy to later reuse the code for other
+ purposes
+
+ All entries are linked in a list to allow us to traverse all elements
+ and delete selected ones. (HASH doesn't allow any easy ways to do this).
+*****************************************************************************/
+
+/*
+ Struct to store a key and pointer to object
+*/
+
+typedef struct st_safe_hash_entry
+{
+ byte *key;
+ uint length;
+ byte *data;
+ struct st_safe_hash_entry *next, **prev;
+} SAFE_HASH_ENTRY;
+
+
+typedef struct st_safe_hash_with_default
+{
+#ifdef THREAD
+ rw_lock_t mutex;
+#endif
+ HASH hash;
+ byte *default_value;
+ SAFE_HASH_ENTRY *root;
+} SAFE_HASH;
+
+
+/*
+ Free a SAFE_HASH_ENTRY
+
+ This function is called by the hash object on delete
+*/
+
+static void safe_hash_entry_free(SAFE_HASH_ENTRY *entry)
+{
+ DBUG_ENTER("free_assign_entry");
+ my_free((gptr) entry, MYF(0));
+ DBUG_VOID_RETURN;
+}
+
+
+/* Get key and length for a SAFE_HASH_ENTRY */
+
+static byte *safe_hash_entry_get(SAFE_HASH_ENTRY *entry, uint *length,
+ my_bool not_used __attribute__((unused)))
+{
+ *length=entry->length;
+ return (byte*) entry->key;
+}
+
+
+/*
+ Init a SAFE_HASH object
+
+ SYNOPSIS
+ safe_hash_init()
+ hash safe_hash handler
+ elements Expected max number of elements
+ default_value default value
+
+ NOTES
+ In case of error we set hash->default_value to 0 to allow one to call
+ safe_hash_free on an object that couldn't be initialized.
+
+ RETURN
+ 0 ok
+ 1 error
+*/
+
+static my_bool safe_hash_init(SAFE_HASH *hash, uint elements,
+ byte *default_value)
+{
+ DBUG_ENTER("safe_hash");
+ if (hash_init(&hash->hash, &my_charset_bin, elements,
+ 0, 0, (hash_get_key) safe_hash_entry_get,
+ (void (*)(void*)) safe_hash_entry_free, 0))
+ {
+ hash->default_value= 0;
+ DBUG_RETURN(1);
+ }
+ my_rwlock_init(&hash->mutex, 0);
+ hash->default_value= default_value;
+ hash->root= 0;
+ DBUG_RETURN(0);
+}
+
+
+/*
+ Free a SAFE_HASH object
+
+ NOTES
+ This is safe to call on any object that has been sent to safe_hash_init()
+*/
+
+static void safe_hash_free(SAFE_HASH *hash)
+{
+ /*
+ Test if safe_hash_init succeeded. This will also guard us against multiple
+ free calls.
+ */
+ if (hash->default_value)
+ {
+ hash_free(&hash->hash);
+ rwlock_destroy(&hash->mutex);
+ hash->default_value=0;
+ }
+}
+
+/*
+ Return the value stored for a key or default value if no key
+*/
+
+static byte *safe_hash_search(SAFE_HASH *hash, const byte *key, uint length)
+{
+ byte *result;
+ DBUG_ENTER("safe_hash_search");
+ rw_rdlock(&hash->mutex);
+ result= hash_search(&hash->hash, key, length);
+ rw_unlock(&hash->mutex);
+ if (!result)
+ result= hash->default_value;
+ else
+ result= ((SAFE_HASH_ENTRY*) result)->data;
+ DBUG_PRINT("exit",("data: 0x%lx", result));
+ DBUG_RETURN(result);
+}
+
+
+/*
+ Associate a key with some data
+
+ SYONOPSIS
+ safe_hash_set()
+ hash Hash handle
+ key key (path to table etc..)
+ length Length of key
+ data data to to associate with the data
+
+ NOTES
+ This can be used both to insert a new entry and change an existing
+ entry.
+ If one associates a key with the default key cache, the key is deleted
+
+ RETURN
+ 0 ok
+ 1 error (Can only be EOM). In this case my_message() is called.
+*/
+
+static my_bool safe_hash_set(SAFE_HASH *hash, const byte *key, uint length,
+ byte *data)
+{
+ SAFE_HASH_ENTRY *entry;
+ my_bool error= 0;
+ DBUG_ENTER("safe_hash_set");
+ DBUG_PRINT("enter",("key: %.*s data: 0x%lx", length, key, data));
+
+ rw_wrlock(&hash->mutex);
+ entry= (SAFE_HASH_ENTRY*) hash_search(&hash->hash, key, length);
+
+ if (data == hash->default_value)
+ {
+ /*
+ The key is to be associated with the default entry. In this case
+ we can just delete the entry (if it existed) from the hash as a
+ search will return the default entry
+ */
+ if (!entry) /* nothing to do */
+ goto end;
+ /* unlink entry from list */
+ if ((*entry->prev= entry->next))
+ entry->next->prev= entry->prev;
+ hash_delete(&hash->hash, (byte*) entry);
+ goto end;
+ }
+ if (entry)
+ {
+ /* Entry existed; Just change the pointer to point at the new data */
+ entry->data= data;
+ }
+ else
+ {
+ if (!(entry= (SAFE_HASH_ENTRY *) my_malloc(sizeof(*entry) + length,
+ MYF(MY_WME))))
+ {
+ error= 1;
+ goto end;
+ }
+ entry->key= (byte*) (entry +1);
+ memcpy((char*) entry->key, (char*) key, length);
+ entry->length= length;
+ entry->data= data;
+ /* Link entry to list */
+ if ((entry->next= hash->root))
+ entry->next->prev= &entry->next;
+ entry->prev= &hash->root;
+ hash->root= entry;
+ if (my_hash_insert(&hash->hash, (byte*) entry))
+ {
+ /* This can only happen if hash got out of memory */
+ my_free((char*) entry, MYF(0));
+ error= 1;
+ goto end;
+ }
+ }
+
+end:
+ rw_unlock(&hash->mutex);
+ DBUG_RETURN(error);
+}
+
+
+/*
+ Change all entres with one data value to another data value
+
+ SYONOPSIS
+ safe_hash_change()
+ hash Hash handle
+ old_data Old data
+ new_data Change all 'old_data' to this
+
+ NOTES
+ We use the linked list to traverse all elements in the hash as
+ this allows us to delete elements in the case where 'new_data' is the
+ default value.
+*/
+
+static void safe_hash_change(SAFE_HASH *hash, byte *old_data, byte *new_data)
+{
+ SAFE_HASH_ENTRY *entry, *next;
+ DBUG_ENTER("safe_hash_set");
+
+ rw_wrlock(&hash->mutex);
+
+ for (entry= hash->root ; entry ; entry= next)
+ {
+ next= entry->next;
+ if (entry->data == old_data)
+ {
+ if (new_data == hash->default_value)
+ {
+ if ((*entry->prev= entry->next))
+ entry->next->prev= entry->prev;
+ hash_delete(&hash->hash, (byte*) entry);
+ }
+ else
+ entry->data= new_data;
+ }
+ }
+
+ rw_unlock(&hash->mutex);
+ DBUG_VOID_RETURN;
+}
+
+
+/*****************************************************************************
+ Functions to handle the key cache objects
+*****************************************************************************/
+
+/* Variable to store all key cache objects */
+static SAFE_HASH key_cache_hash;
+
+
+my_bool multi_keycache_init(void)
+{
+ return safe_hash_init(&key_cache_hash, 16, (byte*) dflt_key_cache);
+}
+
+
+void multi_keycache_free(void)
+{
+ safe_hash_free(&key_cache_hash);
+}
+
+/*
+ Get a key cache to be used for a specific table.
+
+ SYNOPSIS
+ multi_key_cache_search()
+ key key to find (usually table path)
+ uint length Length of key.
+
+ NOTES
+ This function is coded in such a way that we will return the
+ default key cache even if one never called multi_keycache_init.
+ This will ensure that it works with old MyISAM clients.
+
+ RETURN
+ key cache to use
+*/
+
+KEY_CACHE *multi_key_cache_search(byte *key, uint length)
+{
+ if (!key_cache_hash.hash.records)
+ return dflt_key_cache;
+ return (KEY_CACHE*) safe_hash_search(&key_cache_hash, key, length);
+}
+
+
+/*
+ Assosiate a key cache with a key
+
+
+ SYONOPSIS
+ multi_key_cache_set()
+ key key (path to table etc..)
+ length Length of key
+ key_cache cache to assococite with the table
+
+ NOTES
+ This can be used both to insert a new entry and change an existing
+ entry
+*/
+
+
+my_bool multi_key_cache_set(const byte *key, uint length,
+ KEY_CACHE *key_cache)
+{
+ return safe_hash_set(&key_cache_hash, key, length, (byte*) key_cache);
+}
+
+
+void multi_key_cache_change(KEY_CACHE *old_data,
+ KEY_CACHE *new_data)
+{
+ safe_hash_change(&key_cache_hash, (byte*) old_data, (byte*) new_data);
+}
diff --git a/mysys/mf_loadpath.c b/mysys/mf_loadpath.c
index 291ad62e297..a46b43c34d4 100644
--- a/mysys/mf_loadpath.c
+++ b/mysys/mf_loadpath.c
@@ -28,6 +28,7 @@ my_string my_load_path(my_string to, const char *path,
const char *own_path_prefix)
{
char buff[FN_REFLEN];
+ int is_cur;
DBUG_ENTER("my_load_path");
DBUG_PRINT("enter",("path: %s prefix: %s",path,
own_path_prefix ? own_path_prefix : ""));
@@ -35,14 +36,16 @@ my_string my_load_path(my_string to, const char *path,
if ((path[0] == FN_HOMELIB && path[1] == FN_LIBCHAR) ||
test_if_hard_path(path))
VOID(strmov(buff,path));
- else if ((path[0] == FN_CURLIB && path[1] == FN_LIBCHAR) ||
+ else if ((is_cur=(path[0] == FN_CURLIB && path[1] == FN_LIBCHAR)) ||
(is_prefix((gptr) path,FN_PARENTDIR)) ||
! own_path_prefix)
{
- if (! my_getwd(buff,(uint) (FN_REFLEN-strlen(path)),MYF(0)))
- VOID(strcat(buff,path));
+ if (is_cur)
+ is_cur=2; /* Remove current dir */
+ if (! my_getwd(buff,(uint) (FN_REFLEN-strlen(path)+is_cur),MYF(0)))
+ VOID(strcat(buff,path+is_cur));
else
- VOID(strmov(buff,path));
+ VOID(strmov(buff,path)); /* Return org file name */
}
else
VOID(strxmov(buff,own_path_prefix,path,NullS));
diff --git a/mysys/mf_pack.c b/mysys/mf_pack.c
index 95a1d68507e..049aa59a578 100644
--- a/mysys/mf_pack.c
+++ b/mysys/mf_pack.c
@@ -43,7 +43,7 @@ void pack_dirname(my_string to, const char *from)
(void) intern_filename(to,from); /* Change to intern name */
#ifdef FN_DEVCHAR
- if ((start=strrchr(to,FN_DEVCHAR)) != 0) /* Skipp device part */
+ if ((start=strrchr(to,FN_DEVCHAR)) != 0) /* Skip device part */
start++;
else
#endif
@@ -124,6 +124,9 @@ uint cleanup_dirname(register my_string to, const char *from)
reg4 my_string start;
char parent[5], /* for "FN_PARENTDIR" */
buff[FN_REFLEN+1],*end_parentdir;
+#ifdef BACKSLASH_MBTAIL
+ CHARSET_INFO *fs= fs_character_set();
+#endif
DBUG_ENTER("cleanup_dirname");
DBUG_PRINT("enter",("from: '%s'",from));
@@ -131,7 +134,7 @@ uint cleanup_dirname(register my_string to, const char *from)
from_ptr=(my_string) from;
#ifdef FN_DEVCHAR
if ((pos=strrchr(from_ptr,FN_DEVCHAR)) != 0)
- { /* Skipp device part */
+ { /* Skip device part */
length=(uint) (pos-from_ptr)+1;
start=strnmov(buff,from_ptr,length); from_ptr+=length;
}
@@ -141,12 +144,21 @@ uint cleanup_dirname(register my_string to, const char *from)
length=(uint) (strmov(parent+1,FN_PARENTDIR)-parent);
for (pos=start ; (*pos= *from_ptr++) != 0 ; pos++)
{
+#ifdef BACKSLASH_MBTAIL
+ uint l;
+ if (use_mb(fs) && (l= my_ismbchar(fs, from_ptr - 1, from_ptr + 2)))
+ {
+ for (l-- ; l ; *++pos= *from_ptr++, l--);
+ start= pos + 1; /* Don't look inside multi-byte char */
+ continue;
+ }
+#endif
if (*pos == '/')
*pos = FN_LIBCHAR;
if (*pos == FN_LIBCHAR)
{
if ((uint) (pos-start) > length && bcmp(pos-length,parent,length) == 0)
- { /* If .../../; skipp prev */
+ { /* If .../../; skip prev */
pos-=length;
if (pos != start)
{ /* not /../ */
@@ -195,7 +207,7 @@ uint cleanup_dirname(register my_string to, const char *from)
pos--; /* Remove dupplicate '/' */
}
else if (pos-start > 1 && pos[-1] == FN_CURLIB && pos[-2] == FN_LIBCHAR)
- pos-=2; /* Skipp /./ */
+ pos-=2; /* Skip /./ */
else if (pos > buff+1 && pos[-1] == FN_HOMELIB && pos[-2] == FN_LIBCHAR)
{ /* Found ..../~/ */
buff[0]=FN_HOMELIB;
@@ -226,7 +238,7 @@ void symdirget(char *dir)
{
char buff[FN_REFLEN];
char *pos=strend(dir);
- if (dir[0] && pos[-1] != FN_DEVCHAR && access(dir, F_OK))
+ if (dir[0] && pos[-1] != FN_DEVCHAR && my_access(dir, F_OK))
{
File file;
uint length;
@@ -409,7 +421,7 @@ uint system_filename(my_string to, const char *from)
libchar_found=0;
(void) strmov(buff,from); /* If to == from */
from_pos= buff;
- if ((pos=strrchr(from_pos,FN_DEVCHAR))) /* Skipp device part */
+ if ((pos=strrchr(from_pos,FN_DEVCHAR))) /* Skip device part */
{
pos++;
to_pos=strnmov(to,from_pos,(size_s) (pos-from_pos));
@@ -419,7 +431,7 @@ uint system_filename(my_string to, const char *from)
to_pos=to;
if (from_pos[0] == FN_CURLIB && from_pos[1] == FN_LIBCHAR)
- from_pos+=2; /* Skipp './' */
+ from_pos+=2; /* Skip './' */
if (strchr(from_pos,FN_LIBCHAR))
{
*(to_pos++) = FN_C_BEFORE_DIR;
@@ -487,7 +499,7 @@ my_string intern_filename(my_string to, const char *from)
convert_dirname(buff,from,NullS); /* change '<>' to '[]' */
from_pos=buff;
- if ((pos=strrchr(from_pos,FN_DEVCHAR))) /* Skipp device part */
+ if ((pos=strrchr(from_pos,FN_DEVCHAR))) /* Skip device part */
{
pos++;
to_pos=strnmov(to,from_pos,(size_s) (pos-from_pos));
diff --git a/mysys/mf_soundex.c b/mysys/mf_soundex.c
index 9fe54ffafa0..c0c6105a6eb 100644
--- a/mysys/mf_soundex.c
+++ b/mysys/mf_soundex.c
@@ -38,24 +38,25 @@
#include <m_ctype.h>
#include "my_static.h"
-static char get_scode(char **ptr,pbool remove_garbage);
+static char get_scode(CHARSET_INFO * cs, char **ptr,pbool remove_garbage);
/* outputed string is 4 byte long */
/* out_pntr can be == in_pntr */
-void soundex(register my_string out_pntr, my_string in_pntr,
+void soundex(CHARSET_INFO * cs,register my_string out_pntr, my_string in_pntr,
pbool remove_garbage)
{
char ch,last_ch;
reg3 my_string end;
+ register uchar *map=cs->to_upper;
if (remove_garbage)
{
- while (*in_pntr && !isalpha(*in_pntr))
+ while (*in_pntr && !my_isalpha(cs,*in_pntr)) /* Skip pre-space */
in_pntr++;
}
- *out_pntr++ = toupper(*in_pntr); /* Copy first letter */
- last_ch = get_scode(&in_pntr,0); /* code of the first letter */
+ *out_pntr++ = map[(uchar)*in_pntr]; /* Copy first letter */
+ last_ch = get_scode(cs,&in_pntr,0); /* code of the first letter */
/* for the first 'double-letter */
/* check. */
end=out_pntr+3; /* Loop on input letters until */
@@ -63,7 +64,7 @@ void soundex(register my_string out_pntr, my_string in_pntr,
/* letter code count = 3 */
in_pntr++;
- while (out_pntr < end && (ch = get_scode(&in_pntr,remove_garbage)) != 0)
+ while (out_pntr < end && (ch = get_scode(cs,&in_pntr,remove_garbage)) != 0)
{
in_pntr++;
if ((ch != '0') && (ch != last_ch)) /* if not skipped or double */
@@ -81,23 +82,23 @@ void soundex(register my_string out_pntr, my_string in_pntr,
/*
If alpha, map input letter to soundex code.
- If not alpha and remove_garbage is set then skipp to next char
+ If not alpha and remove_garbage is set then skip to next char
else return 0
*/
-static char get_scode(char **ptr, pbool remove_garbage)
+static char get_scode(CHARSET_INFO * cs,char **ptr, pbool remove_garbage)
{
uchar ch;
if (remove_garbage)
{
- while (**ptr && !isalpha(**ptr))
+ while (**ptr && !my_isalpha(cs,**ptr))
(*ptr)++;
}
- ch=toupper(**ptr);
+ ch=my_toupper(cs,**ptr);
if (ch < 'A' || ch > 'Z')
{
- if (isalpha(ch)) /* If extended alpha (country spec) */
+ if (my_isalpha(cs,ch)) /* If extended alfa (country spec) */
return '0'; /* threat as vokal */
return 0; /* Can't map */
}
diff --git a/mysys/mf_tempdir.c b/mysys/mf_tempdir.c
new file mode 100644
index 00000000000..4d244aa7d74
--- /dev/null
+++ b/mysys/mf_tempdir.c
@@ -0,0 +1,83 @@
+/* Copyright (C) 2000 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#include "mysys_priv.h"
+#include <m_string.h>
+
+#if defined( __WIN__) || defined(OS2) || defined(__NETWARE__)
+#define DELIM ';'
+#else
+#define DELIM ':'
+#endif
+
+my_bool init_tmpdir(MY_TMPDIR *tmpdir, const char *pathlist)
+{
+ char *end, *copy;
+ char buff[FN_REFLEN];
+ DYNAMIC_ARRAY t_arr;
+ pthread_mutex_init(&tmpdir->mutex, MY_MUTEX_INIT_FAST);
+ if (my_init_dynamic_array(&t_arr, sizeof(char*), 1, 5))
+ return TRUE;
+ if (!pathlist || !pathlist[0])
+ {
+ /* Get default temporary directory */
+ pathlist=getenv("TMPDIR"); /* Use this if possible */
+#if defined( __WIN__) || defined(OS2) || defined(__NETWARE__)
+ if (!pathlist)
+ pathlist=getenv("TEMP");
+ if (!pathlist)
+ pathlist=getenv("TMP");
+#endif
+ if (!pathlist || !pathlist[0])
+ pathlist=(char*) P_tmpdir;
+ }
+ do
+ {
+ end=strcend(pathlist, DELIM);
+ convert_dirname(buff, pathlist, end);
+ if (!(copy=my_strdup(buff, MYF(MY_WME))))
+ return TRUE;
+ if (insert_dynamic(&t_arr, (gptr)&copy))
+ return TRUE;
+ pathlist=end+1;
+ }
+ while (*end);
+ freeze_size(&t_arr);
+ tmpdir->list=(char **)t_arr.buffer;
+ tmpdir->max=t_arr.elements-1;
+ tmpdir->cur=0;
+ return FALSE;
+}
+
+char *my_tmpdir(MY_TMPDIR *tmpdir)
+{
+ char *dir;
+ pthread_mutex_lock(&tmpdir->mutex);
+ dir=tmpdir->list[tmpdir->cur];
+ tmpdir->cur= (tmpdir->cur == tmpdir->max) ? 0 : tmpdir->cur+1;
+ pthread_mutex_unlock(&tmpdir->mutex);
+ return dir;
+}
+
+void free_tmpdir(MY_TMPDIR *tmpdir)
+{
+ uint i;
+ for (i=0; i<=tmpdir->max; i++)
+ my_free(tmpdir->list[i], MYF(0));
+ my_free((gptr)tmpdir->list, MYF(0));
+ pthread_mutex_destroy(&tmpdir->mutex);
+}
+
diff --git a/mysys/mf_tempfile.c b/mysys/mf_tempfile.c
index c84222ba77f..a15bda4da6d 100644
--- a/mysys/mf_tempfile.c
+++ b/mysys/mf_tempfile.c
@@ -181,7 +181,7 @@ File create_temp_file(char *to, const char *dir, const char *prefix,
for (length=0 ; length < 8 && uniq ; length++)
{
- *end_pos++= _dig_vec[(int) (uniq & 31)];
+ *end_pos++= _dig_vec_upper[(int) (uniq & 31)];
uniq >>= 5;
}
(void) strmov(end_pos,TMP_EXT);
diff --git a/mysys/mf_wfile.c b/mysys/mf_wfile.c
index 067e4b7acc5..7d537eaa06a 100644
--- a/mysys/mf_wfile.c
+++ b/mysys/mf_wfile.c
@@ -39,7 +39,7 @@ WF_PACK *wf_comp(my_string str)
WF_PACK *ret;
DBUG_ENTER("wf_comp");
- not_pos= -1; /* Skipp space and '!' in front */
+ not_pos= -1; /* Skip space and '!' in front */
while (*str == ' ')
str++;
if (*str == '!')
@@ -54,13 +54,6 @@ WF_PACK *wf_comp(my_string str)
for (pos=str ; *pos ; pos++)
ant+= test(*pos == ' ' || *pos == ',');
-#ifdef FN_UPPER_CASE
- caseup(str,(int) (pos-str));
-#endif
-#ifdef FN_LOWER_CASE
- casedn(str,(int) (pos-str));
-#endif
-
if ((ret= (WF_PACK*) my_malloc((uint) ant*(sizeof(my_string*)+2)+
sizeof(WF_PACK)+ (uint) strlen(str)+1,
MYF(MY_WME)))
diff --git a/mysys/mulalloc.c b/mysys/mulalloc.c
index 6b025bca699..e1eb1c00602 100644
--- a/mysys/mulalloc.c
+++ b/mysys/mulalloc.c
@@ -19,16 +19,18 @@
/*
Malloc many pointers at the same time
+ Only ptr1 can be free'd, and doing this will free all
+ the memory allocated. ptr2, etc all point inside big allocated
+ memory area.
SYNOPSIS
my_multi_malloc()
- myFlags Flags
- ... Multiple arguments terminated by null ptr
-
- ptr, length
- ptr, length
+ myFlags Flags
+ ptr1, length1 Multiple arguments terminated by null ptr
+ ptr2, length2 ...
+ ...
NULL
-*/
+*/
gptr my_multi_malloc(myf myFlags, ...)
{
diff --git a/mysys/my_access.c b/mysys/my_access.c
new file mode 100644
index 00000000000..237312b5c9b
--- /dev/null
+++ b/mysys/my_access.c
@@ -0,0 +1,122 @@
+/* Copyright (C) 2000 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#include "mysys_priv.h"
+#include <m_string.h>
+
+#ifdef __WIN__
+
+/*
+ Check a file or path for accessability.
+
+ SYNOPSIS
+ file_access()
+ path Path to file
+ amode Access method
+
+ DESCRIPTION
+ This function wraps the normal access method because the access
+ available in MSVCRT> +reports that filenames such as LPT1 and
+ COM1 are valid (they are but should not be so for us).
+
+ RETURN VALUES
+ 0 ok
+ -1 error (We use -1 as my_access is mapped to access on other platforms)
+*/
+
+int my_access(const char *path, int amode)
+{
+ WIN32_FILE_ATTRIBUTE_DATA fileinfo;
+ BOOL result;
+
+ result= GetFileAttributesEx(path, GetFileExInfoStandard, &fileinfo);
+ if (! result ||
+ (fileinfo.dwFileAttributes & FILE_ATTRIBUTE_READONLY) && (amode & F_OK))
+ {
+ my_errno= errno= EACCES;
+ return -1;
+ }
+ return 0;
+}
+
+#endif /* __WIN__ */
+
+#if defined(MSDOS) || defined(__WIN__) || defined(__EMX__)
+
+/*
+ List of file names that causes problem on windows
+
+ NOTE that one can also not have file names of type CON.TXT
+*/
+
+static const char *reserved_names[]=
+{
+ "CON", "PRN", "AUX", "NUL", "COM1", "COM2", "COM3", "COM4", "COM5", "COM6",
+ "COM7", "COM8", "COM9", "LPT1", "LPT2", "LPT3", "LPT4", "LPT5", "LPT6",
+ "LPT7", "LPT8", "LPT9", "CLOCK$",
+ NullS
+};
+
+#define MAX_RESERVED_NAME_LENGTH 6
+
+/*
+ Check if a path will access a reserverd file name that may cause problems
+
+ SYNOPSIS
+ check_if_legal_filename
+ path Path to file
+
+ RETURN
+ 0 ok
+ 1 reserved file name
+*/
+
+int check_if_legal_filename(const char *path)
+{
+ const char *end;
+ const char **reserved_name;
+ DBUG_ENTER("check_if_legal_filename");
+
+ path+= dirname_length(path); /* To start of filename */
+ if (!(end= strchr(path, FN_EXTCHAR)))
+ end= strend(path);
+ if (path == end || (uint) (end - path) > MAX_RESERVED_NAME_LENGTH)
+ DBUG_RETURN(0); /* Simplify inner loop */
+
+ for (reserved_name= reserved_names; *reserved_name; reserved_name++)
+ {
+ const char *reserved= *reserved_name; /* never empty */
+ const char *name= path;
+
+ do
+ {
+ if (*reserved != my_toupper(&my_charset_latin1, *name))
+ break;
+ if (++name == end && !reserved[1])
+ DBUG_RETURN(1); /* Found wrong path */
+ } while (*++reserved);
+ }
+ DBUG_RETURN(0);
+}
+#endif
+
+
+#ifdef OS2
+int check_if_legal_filename(const char *path)
+{
+ return 0;
+}
+#endif /* OS2 */
diff --git a/mysys/my_alloc.c b/mysys/my_alloc.c
index d03ec5841af..d52a8efeed2 100644
--- a/mysys/my_alloc.c
+++ b/mysys/my_alloc.c
@@ -22,12 +22,35 @@
#undef EXTRA_DEBUG
#define EXTRA_DEBUG
+
+/*
+ Initialize memory root
+
+ SYNOPSIS
+ init_alloc_root()
+ mem_root - memory root to initialize
+ block_size - size of chunks (blocks) used for memory allocation
+ (It is external size of chunk i.e. it should include
+ memory required for internal structures, thus it
+ should be no less than ALLOC_ROOT_MIN_BLOCK_SIZE)
+ pre_alloc_size - if non-0, then size of block that should be
+ pre-allocated during memory root initialization.
+
+ DESCRIPTION
+ This function prepares memory root for further use, sets initial size of
+ chunk for memory allocation and pre-allocates first block if specified.
+ Altough error can happen during execution of this function if pre_alloc_size
+ is non-0 it won't be reported. Instead it will be reported as error in first
+ alloc_root() on this memory root.
+*/
void init_alloc_root(MEM_ROOT *mem_root, uint block_size,
uint pre_alloc_size __attribute__((unused)))
{
+ DBUG_ENTER("init_alloc_root");
+ DBUG_PRINT("enter",("root: 0x%lx", mem_root));
mem_root->free= mem_root->used= mem_root->pre_alloc= 0;
mem_root->min_malloc= 32;
- mem_root->block_size= block_size-MALLOC_OVERHEAD-sizeof(USED_MEM)-8;
+ mem_root->block_size= block_size - ALLOC_ROOT_MIN_BLOCK_SIZE;
mem_root->error_handler= 0;
mem_root->block_num= 4; /* We shift this with >>2 */
mem_root->first_block_usage= 0;
@@ -45,15 +68,16 @@ void init_alloc_root(MEM_ROOT *mem_root, uint block_size,
}
}
#endif
+ DBUG_VOID_RETURN;
}
/*
SYNOPSIS
reset_root_defaults()
mem_root memory root to change defaults of
- block_size new value of block size. Must be
- greater than ~68 bytes (the exact value depends on
- platform and compilation flags)
+ block_size new value of block size. Must be greater or equal
+ than ALLOC_ROOT_MIN_BLOCK_SIZE (this value is about
+ 68 bytes and depends on platform and compilation flags)
pre_alloc_size new size of preallocated block. If not zero,
must be equal to or greater than block size,
otherwise means 'no prealloc'.
@@ -65,9 +89,11 @@ void init_alloc_root(MEM_ROOT *mem_root, uint block_size,
*/
void reset_root_defaults(MEM_ROOT *mem_root, uint block_size,
- uint pre_alloc_size)
+ uint pre_alloc_size __attribute__((unused)))
{
- mem_root->block_size= block_size-MALLOC_OVERHEAD-sizeof(USED_MEM)-8;
+ DBUG_ASSERT(alloc_root_inited(mem_root));
+
+ mem_root->block_size= block_size - ALLOC_ROOT_MIN_BLOCK_SIZE;
#if !(defined(HAVE_purify) && defined(EXTRA_DEBUG))
if (pre_alloc_size)
{
@@ -117,24 +143,32 @@ gptr alloc_root(MEM_ROOT *mem_root,unsigned int Size)
{
#if defined(HAVE_purify) && defined(EXTRA_DEBUG)
reg1 USED_MEM *next;
- Size+=ALIGN_SIZE(sizeof(USED_MEM));
+ DBUG_ENTER("alloc_root");
+ DBUG_PRINT("enter",("root: 0x%lx", mem_root));
+
+ DBUG_ASSERT(alloc_root_inited(mem_root));
+ Size+=ALIGN_SIZE(sizeof(USED_MEM));
if (!(next = (USED_MEM*) my_malloc(Size,MYF(MY_WME))))
{
if (mem_root->error_handler)
(*mem_root->error_handler)();
- return((gptr) 0); /* purecov: inspected */
+ DBUG_RETURN((gptr) 0); /* purecov: inspected */
}
next->next= mem_root->used;
next->size= Size;
mem_root->used= next;
- return (gptr) (((char*) next)+ALIGN_SIZE(sizeof(USED_MEM)));
+ DBUG_PRINT("exit",("ptr: 0x%lx", (((char*) next)+
+ ALIGN_SIZE(sizeof(USED_MEM)))));
+ DBUG_RETURN((gptr) (((char*) next)+ALIGN_SIZE(sizeof(USED_MEM))));
#else
uint get_size, block_size;
gptr point;
reg1 USED_MEM *next= 0;
reg2 USED_MEM **prev;
+ DBUG_ASSERT(alloc_root_inited(mem_root));
+
Size= ALIGN_SIZE(Size);
if ((*(prev= &mem_root->free)) != NULL)
{
@@ -169,7 +203,7 @@ gptr alloc_root(MEM_ROOT *mem_root,unsigned int Size)
next->left= get_size-ALIGN_SIZE(sizeof(USED_MEM));
*prev=next;
}
-
+
point= (gptr) ((char*) next+ (next->size-next->left));
/*TODO: next part may be unneded due to mem_root->first_block_usage counter*/
if ((next->left-= Size) < mem_root->min_malloc)
@@ -183,6 +217,12 @@ gptr alloc_root(MEM_ROOT *mem_root,unsigned int Size)
#endif
}
+#ifdef SAFEMALLOC
+#define TRASH(X) bfill(((char*)(X) + ((X)->size-(X)->left)), (X)->left, 0xa5)
+#else
+#define TRASH /* no-op */
+#endif
+
/* Mark all data in blocks free for reusage */
static inline void mark_blocks_free(MEM_ROOT* root)
@@ -193,14 +233,20 @@ static inline void mark_blocks_free(MEM_ROOT* root)
/* iterate through (partially) free blocks, mark them free */
last= &root->free;
for (next= root->free; next; next= *(last= &next->next))
+ {
next->left= next->size - ALIGN_SIZE(sizeof(USED_MEM));
+ TRASH(next);
+ }
/* Combine the free and the used list */
*last= next=root->used;
/* now go through the used blocks and mark them free */
for (; next; next= next->next)
+ {
next->left= next->size - ALIGN_SIZE(sizeof(USED_MEM));
+ TRASH(next);
+ }
/* Now everything is set; Indicate that nothing is used anymore */
root->used= 0;
@@ -217,8 +263,9 @@ void free_root(MEM_ROOT *root, myf MyFlags)
{
reg1 USED_MEM *next,*old;
DBUG_ENTER("free_root");
+ DBUG_PRINT("enter",("root: 0x%lx flags: %u", root, (uint) MyFlags));
- if (!root)
+ if (!root) /* QQ: Should be deleted */
DBUG_VOID_RETURN; /* purecov: inspected */
if (MyFlags & MY_MARK_BLOCKS_FREE)
{
@@ -245,6 +292,7 @@ void free_root(MEM_ROOT *root, myf MyFlags)
{
root->free=root->pre_alloc;
root->free->left=root->pre_alloc->size-ALIGN_SIZE(sizeof(USED_MEM));
+ TRASH(root->pre_alloc);
root->free->next=0;
}
root->block_num= 4;
diff --git a/mysys/my_append.c b/mysys/my_append.c
index dc5ed084bb3..c3549c670c3 100644
--- a/mysys/my_append.c
+++ b/mysys/my_append.c
@@ -18,10 +18,10 @@
#include "mysys_priv.h"
#include <sys/stat.h>
#include <m_string.h>
-#if defined(HAVE_SYS_UTIME_H)
-#include <sys/utime.h>
-#elif defined(HAVE_UTIME_H)
+#if defined(HAVE_UTIME_H)
#include <utime.h>
+#elif defined(HAVE_SYS_UTIME_H)
+#include <sys/utime.h>
#elif !defined(HPUX10)
struct utimbuf {
time_t actime;
diff --git a/mysys/my_bitmap.c b/mysys/my_bitmap.c
index 9662f219418..2af4edbf1a5 100644
--- a/mysys/my_bitmap.c
+++ b/mysys/my_bitmap.c
@@ -16,7 +16,18 @@
/*
Handling of uchar arrays as large bitmaps.
- We assume that the size of the used bitmap is less than ~(uint) 0
+
+ API limitations (or, rather asserted safety assumptions,
+ to encourage correct programming)
+
+ * the size of the used bitmap is less than ~(uint) 0
+ * it's a multiple of 8 (for efficiency reasons)
+ * when arguments are a bitmap and a bit number, the number
+ must be within bitmap size
+ * bitmap_set_prefix() is an exception - one can use ~0 to set all bits
+ * when both arguments are bitmaps, they must be of the same size
+ * bitmap_intersect() is an exception :)
+ (for for Bitmap::intersect(ulonglong map2buff))
TODO:
Make assembler THREAD safe versions of these using test-and-set instructions
@@ -24,60 +35,74 @@
#include "mysys_priv.h"
#include <my_bitmap.h>
-#include <assert.h>
#include <m_string.h>
static inline void bitmap_lock(MY_BITMAP* map)
{
#ifdef THREAD
- if (map->thread_safe)
- pthread_mutex_lock(&map->mutex);
+ if (map->mutex)
+ pthread_mutex_lock(map->mutex);
#endif
}
static inline void bitmap_unlock(MY_BITMAP* map)
{
#ifdef THREAD
- if (map->thread_safe)
- pthread_mutex_unlock(&map->mutex);
+ if (map->mutex)
+ pthread_mutex_unlock(map->mutex);
#endif
}
-my_bool bitmap_init(MY_BITMAP *map, uint bitmap_size, my_bool thread_safe)
+
+my_bool bitmap_init(MY_BITMAP *map, uchar *buf, uint bitmap_size,
+ my_bool thread_safe)
{
- if (!(map->bitmap=(uchar*) my_malloc((bitmap_size+7)/8,
- MYF(MY_WME | MY_ZEROFILL))))
+ DBUG_ENTER("bitmap_init");
+
+ DBUG_ASSERT((bitmap_size & 7) == 0);
+ bitmap_size/=8;
+ if (!(map->bitmap=buf) &&
+ !(map->bitmap= (uchar*) my_malloc(bitmap_size +
+ (thread_safe ?
+ sizeof(pthread_mutex_t) : 0),
+ MYF(MY_WME | MY_ZEROFILL))))
return 1;
- DBUG_ASSERT(bitmap_size != ~(uint) 0);
+ map->bitmap_size=bitmap_size;
#ifdef THREAD
- if ((map->thread_safe = thread_safe))
- pthread_mutex_init(&map->mutex, MY_MUTEX_INIT_FAST);
+ if (thread_safe)
+ {
+ map->mutex=(pthread_mutex_t *)(map->bitmap+bitmap_size);
+ pthread_mutex_init(map->mutex, MY_MUTEX_INIT_FAST);
+ }
+ else
+ map->mutex=0;
#endif
- map->bitmap_size=bitmap_size;
- return 0;
+ DBUG_RETURN(0);
}
+
void bitmap_free(MY_BITMAP *map)
{
+ DBUG_ENTER("bitmap_free");
if (map->bitmap)
{
- my_free((char*) map->bitmap, MYF(0));
- map->bitmap=0;
#ifdef THREAD
- if (map->thread_safe)
- pthread_mutex_destroy(&map->mutex);
+ if (map->mutex)
+ pthread_mutex_destroy(map->mutex);
#endif
+ my_free((char*) map->bitmap, MYF(0));
+ map->bitmap=0;
}
+ DBUG_VOID_RETURN;
}
+
void bitmap_set_bit(MY_BITMAP *map, uint bitmap_bit)
{
- if (bitmap_bit < map->bitmap_size)
- {
- bitmap_lock(map);
- map->bitmap[bitmap_bit / 8] |= (1 << (bitmap_bit & 7));
- bitmap_unlock(map);
- }
+ DBUG_ASSERT(map->bitmap && bitmap_bit < map->bitmap_size*8);
+ bitmap_lock(map);
+ bitmap_fast_set_bit(map, bitmap_bit);
+ bitmap_unlock(map);
}
@@ -88,6 +113,7 @@ uint bitmap_set_next(MY_BITMAP *map)
uint bitmap_size=map->bitmap_size;
uint i;
+ DBUG_ASSERT(map->bitmap);
bitmap_lock(map);
for (i=0; i < bitmap_size ; i++, bitmap++)
{
@@ -113,32 +139,191 @@ uint bitmap_set_next(MY_BITMAP *map)
void bitmap_clear_bit(MY_BITMAP *map, uint bitmap_bit)
{
- if (bitmap_bit < map->bitmap_size)
+ DBUG_ASSERT(map->bitmap && bitmap_bit < map->bitmap_size*8);
+ bitmap_lock(map);
+ bitmap_fast_clear_bit(map, bitmap_bit);
+ bitmap_unlock(map);
+}
+
+
+void bitmap_set_prefix(MY_BITMAP *map, uint prefix_size)
+{
+ uint prefix_bytes, prefix_bits;
+
+ DBUG_ASSERT(map->bitmap &&
+ (prefix_size <= map->bitmap_size*8 || prefix_size == (uint) ~0));
+ bitmap_lock(map);
+ set_if_smaller(prefix_size, map->bitmap_size*8);
+ if ((prefix_bytes= prefix_size / 8))
+ memset(map->bitmap, 0xff, prefix_bytes);
+ if ((prefix_bits= prefix_size & 7))
+ map->bitmap[prefix_bytes++]= (1 << prefix_bits)-1;
+ if (prefix_bytes < map->bitmap_size)
+ bzero(map->bitmap+prefix_bytes, map->bitmap_size-prefix_bytes);
+ bitmap_unlock(map);
+}
+
+
+void bitmap_clear_all(MY_BITMAP *map)
+{
+ bitmap_set_prefix(map, 0);
+}
+
+
+void bitmap_set_all(MY_BITMAP *map)
+{
+ bitmap_set_prefix(map, ~0);
+}
+
+
+my_bool bitmap_is_prefix(const MY_BITMAP *map, uint prefix_size)
+{
+ uint prefix_bits= prefix_size & 7, res= 0;
+ uchar *m= map->bitmap, *end_prefix= map->bitmap+prefix_size/8,
+ *end= map->bitmap+map->bitmap_size;
+
+ DBUG_ASSERT(map->bitmap && prefix_size <= map->bitmap_size*8);
+
+ bitmap_lock((MY_BITMAP *)map);
+ while (m < end_prefix)
+ if (*m++ != 0xff)
+ goto ret;
+
+ if (prefix_bits && *m++ != (1 << prefix_bits)-1)
+ goto ret;
+
+ while (m < end)
+ if (*m++ != 0)
+ goto ret;
+
+ res=1;
+ret:
+ bitmap_unlock((MY_BITMAP *)map);
+ return res;
+}
+
+
+my_bool bitmap_is_clear_all(const MY_BITMAP *map)
+{
+ return bitmap_is_prefix(map, 0);
+}
+
+my_bool bitmap_is_set_all(const MY_BITMAP *map)
+{
+ return bitmap_is_prefix(map, map->bitmap_size*8);
+}
+
+
+my_bool bitmap_is_set(const MY_BITMAP *map, uint bitmap_bit)
+{
+ DBUG_ASSERT(map->bitmap && bitmap_bit < map->bitmap_size*8);
+ return bitmap_fast_is_set(map, bitmap_bit);
+}
+
+
+my_bool bitmap_is_subset(const MY_BITMAP *map1, const MY_BITMAP *map2)
+{
+ uint res=0;
+ uchar *m1=map1->bitmap, *m2=map2->bitmap, *end;
+
+ DBUG_ASSERT(map1->bitmap && map2->bitmap &&
+ map1->bitmap_size==map2->bitmap_size);
+ bitmap_lock((MY_BITMAP *)map1);
+ bitmap_lock((MY_BITMAP *)map2);
+
+ end= m1+map1->bitmap_size;
+
+ while (m1 < end)
{
- bitmap_lock(map);
- map->bitmap[bitmap_bit / 8] &= ~ (1 << (bitmap_bit & 7));
- bitmap_unlock(map);
+ if ((*m1++) & ~(*m2++))
+ goto ret;
}
+
+ res=1;
+ret:
+ bitmap_unlock((MY_BITMAP *)map2);
+ bitmap_unlock((MY_BITMAP *)map1);
+ return res;
}
-void bitmap_set_all(MY_BITMAP* map)
+my_bool bitmap_cmp(const MY_BITMAP *map1, const MY_BITMAP *map2)
{
+ uint res;
+
+ DBUG_ASSERT(map1->bitmap && map2->bitmap &&
+ map1->bitmap_size==map2->bitmap_size);
+ bitmap_lock((MY_BITMAP *)map1);
+ bitmap_lock((MY_BITMAP *)map2);
+
+ res= memcmp(map1->bitmap, map2->bitmap, map1->bitmap_size)==0;
+
+ bitmap_unlock((MY_BITMAP *)map2);
+ bitmap_unlock((MY_BITMAP *)map1);
+ return res;
+}
+
+
+void bitmap_intersect(MY_BITMAP *map, const MY_BITMAP *map2)
+{
+ uchar *to=map->bitmap, *from=map2->bitmap, *end;
+ uint len=map->bitmap_size, len2=map2->bitmap_size;
+
+ DBUG_ASSERT(map->bitmap && map2->bitmap);
bitmap_lock(map);
- memset(map->bitmap, 0xff, (map->bitmap_size+7)/8);
+ bitmap_lock((MY_BITMAP *)map2);
+
+ end= to+min(len,len2);
+
+ while (to < end)
+ *to++ &= *from++;
+
+ if (len2 < len)
+ {
+ end+=len-len2;
+ while (to < end)
+ *to++=0;
+ }
+
+ bitmap_unlock((MY_BITMAP *)map2);
bitmap_unlock(map);
}
-my_bool bitmap_is_set(MY_BITMAP* map, uint bitmap_bit)
+
+void bitmap_subtract(MY_BITMAP *map, const MY_BITMAP *map2)
{
- return (bitmap_bit < map->bitmap_size) ?
- (map->bitmap[bitmap_bit / 8] & (1 << (bitmap_bit & 7))) :
- 0;
+ uchar *to=map->bitmap, *from=map2->bitmap, *end;
+
+ DBUG_ASSERT(map->bitmap && map2->bitmap &&
+ map->bitmap_size==map2->bitmap_size);
+ bitmap_lock(map);
+ bitmap_lock((MY_BITMAP *)map2);
+
+ end= to+map->bitmap_size;
+
+ while (to < end)
+ *to++ &= ~(*from++);
+
+ bitmap_unlock((MY_BITMAP *)map2);
+ bitmap_unlock(map);
}
-void bitmap_clear_all(MY_BITMAP* map)
+
+void bitmap_union(MY_BITMAP *map, const MY_BITMAP *map2)
{
+ uchar *to=map->bitmap, *from=map2->bitmap, *end;
+
+ DBUG_ASSERT(map->bitmap && map2->bitmap &&
+ map->bitmap_size==map2->bitmap_size);
bitmap_lock(map);
- bzero(map->bitmap,(map->bitmap_size+7)/8);
+ bitmap_lock((MY_BITMAP *)map2);
+
+ end= to+map->bitmap_size;
+
+ while (to < end)
+ *to++ |= *from++;
+
+ bitmap_unlock((MY_BITMAP *)map2);
bitmap_unlock(map);
}
+
diff --git a/mysys/my_chsize.c b/mysys/my_chsize.c
index 653ea569172..149a5c81e08 100644
--- a/mysys/my_chsize.c
+++ b/mysys/my_chsize.c
@@ -44,7 +44,9 @@ int my_chsize(File fd, my_off_t newlength, int filler, myf MyFlags)
DBUG_PRINT("my",("fd: %d length: %lu MyFlags: %d",fd,(ulong) newlength,
MyFlags));
- oldsize = my_seek(fd, 0L, MY_SEEK_END, MYF(MY_WME+MY_FAE));
+ if ((oldsize = my_seek(fd, 0L, MY_SEEK_END, MYF(MY_WME+MY_FAE))) == newlength)
+ DBUG_RETURN(0);
+
DBUG_PRINT("info",("old_size: %ld", (ulong) oldsize));
if (oldsize > newlength)
@@ -88,8 +90,12 @@ int my_chsize(File fd, my_off_t newlength, int filler, myf MyFlags)
Fill space between requested length and true length with 'filler'
We should never come here on any modern machine
*/
- VOID(my_seek(fd, newlength, MY_SEEK_SET, MYF(MY_WME+MY_FAE)));
- swap(my_off_t, newlength, oldsize);
+ if (my_seek(fd, newlength, MY_SEEK_SET, MYF(MY_WME+MY_FAE))
+ == MY_FILEPOS_ERROR)
+ {
+ goto err;
+ }
+ swap_variables(my_off_t, newlength, oldsize);
}
#endif
diff --git a/mysys/my_compress.c b/mysys/my_compress.c
index dd076311188..0e37d2fef9b 100644
--- a/mysys/my_compress.c
+++ b/mysys/my_compress.c
@@ -68,7 +68,7 @@ byte *my_compress_alloc(const byte *packet, ulong *len, ulong *complen)
DBUG_PRINT("note",("Packet got longer on compression; Not compressed"));
return 0;
}
- swap(ulong, *len, *complen); /* *len is now packet length */
+ swap_variables(ulong, *len, *complen); /* *len is now packet length */
return compbuf;
}
diff --git a/mysys/my_conio.c b/mysys/my_conio.c
new file mode 100644
index 00000000000..e381f9f23ef
--- /dev/null
+++ b/mysys/my_conio.c
@@ -0,0 +1,217 @@
+/* Copyright (C) 2000 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+
+#include "mysys_priv.h"
+
+#ifdef __WIN__
+
+static HANDLE my_coninpfh= 0; /* console input */
+
+/*
+ functions my_pthread_auto_mutex_lock & my_pthread_auto_mutex_free
+ are experimental at this moment, they are intended to bring
+ ability of protecting code sections without necessity to explicitly
+ initialize synchronization object in one of threads
+
+ if found useful they are to be exported in mysys
+*/
+
+/*
+ int my_pthread_auto_mutex_lock(HANDLE* ph, const char* name,
+ int id, int time)
+
+ NOTES
+ creates a mutex with given name and tries to lock it time msec.
+ mutex name is appended with id to allow system wide or process wide
+ locks. Handle to created mutex returned in ph argument.
+
+ RETURN
+ 0 thread owns mutex
+ <>0 error
+
+*/
+static
+int my_pthread_auto_mutex_lock(HANDLE* ph, const char* name, int id, int time)
+{
+ int res;
+ char tname[FN_REFLEN];
+
+ sprintf(tname, "%s-%08X", name, id);
+
+ *ph= CreateMutex(NULL, FALSE, tname);
+ if (*ph == NULL)
+ return GetLastError();
+
+ res= WaitForSingleObject(*ph, time);
+
+ if (res == WAIT_TIMEOUT)
+ return ERROR_SEM_TIMEOUT;
+
+ if (res == WAIT_FAILED)
+ return GetLastError();
+
+ return 0;
+}
+
+/*
+ int my_pthread_auto_mutex_free(HANDLE* ph)
+
+
+ NOTES
+ releases a mutex.
+
+ RETURN
+ 0 thread released mutex
+ <>0 error
+
+*/
+static
+int my_pthread_auto_mutex_free(HANDLE* ph)
+{
+ if (*ph)
+ {
+ ReleaseMutex(*ph);
+ CloseHandle(*ph);
+ *ph= NULL;
+ }
+
+ return 0;
+}
+
+
+#define pthread_auto_mutex_decl(name) \
+ HANDLE __h##name= NULL;
+
+#define pthread_auto_mutex_lock(name, proc, time) \
+ my_pthread_auto_mutex_lock(&__h##name, #name, (proc), (time))
+
+#define pthread_auto_mutex_free(name) \
+ my_pthread_auto_mutex_free(&__h##name)
+
+
+/*
+ char* my_cgets(char *string, unsigned long clen, unsigned long* plen)
+
+ NOTES
+ Replaces _cgets from libc to support input of more than 255 chars.
+ Reads from the console via ReadConsole into buffer which
+ should be at least clen characters.
+ Actual length of string returned in plen.
+
+ WARNING
+ my_cgets() does NOT check the pushback character buffer (i.e., _chbuf).
+ Thus, my_cgets() will not return any character that is pushed back by
+ the _ungetch() call.
+
+ RETURN
+ string pointer ok
+ NULL Error
+
+*/
+char* my_cgets(char *buffer, unsigned long clen, unsigned long* plen)
+{
+ ULONG state;
+ char *result;
+ CONSOLE_SCREEN_BUFFER_INFO csbi;
+
+ pthread_auto_mutex_decl(my_conio_cs);
+
+ /* lock the console for the current process*/
+ if (pthread_auto_mutex_lock(my_conio_cs, GetCurrentProcessId(), INFINITE))
+ {
+ /* can not lock console */
+ pthread_auto_mutex_free(my_conio_cs);
+ return NULL;
+ }
+
+ /* init console input */
+ if (my_coninpfh == 0)
+ {
+ /* same handle will be used until process termination */
+ my_coninpfh= CreateFile("CONIN$", GENERIC_READ | GENERIC_WRITE,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ NULL, OPEN_EXISTING, 0, NULL);
+ }
+
+ if (my_coninpfh == INVALID_HANDLE_VALUE)
+ {
+ /* unlock the console */
+ pthread_auto_mutex_free(my_conio_cs);
+ return(NULL);
+ }
+
+ GetConsoleMode((HANDLE)my_coninpfh, &state);
+ SetConsoleMode((HANDLE)my_coninpfh, ENABLE_LINE_INPUT |
+ ENABLE_PROCESSED_INPUT | ENABLE_ECHO_INPUT);
+
+ GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi);
+
+ /*
+ there is no known way to determine allowed buffer size for input
+ though it is known it should not be more than 64K
+ so we cut 64K and try first size of screen buffer
+ if it is still to large we cut half of it and try again
+ later we may want to cycle from min(clen, 65535) to allowed size
+ with small decrement to determine exact allowed buffer
+ */
+ clen= min(clen, 65535);
+ do
+ {
+ clen= min(clen, (unsigned long)csbi.dwSize.X*csbi.dwSize.Y);
+ if (!ReadConsole((HANDLE)my_coninpfh, (LPVOID)buffer, clen - 1, plen, NULL))
+ {
+ result= NULL;
+ clen>>= 1;
+ }
+ else
+ {
+ result= buffer;
+ break;
+ }
+ }
+ while (GetLastError() == ERROR_NOT_ENOUGH_MEMORY);
+
+
+ if (result != NULL)
+ {
+ if (buffer[*plen - 2] == '\r')
+ {
+ *plen= *plen - 2;
+ }
+ else
+ {
+ if (buffer[*plen - 1] == '\r')
+ {
+ char tmp[3];
+ int tmplen= sizeof(tmp);
+
+ *plen= *plen - 1;
+ /* read /n left in the buffer */
+ ReadConsole((HANDLE)my_coninpfh, (LPVOID)tmp, tmplen, &tmplen, NULL);
+ }
+ }
+ buffer[*plen]= '\0';
+ }
+
+ SetConsoleMode((HANDLE)my_coninpfh, state);
+ /* unlock the console */
+ pthread_auto_mutex_free(my_conio_cs);
+
+ return result;
+}
+
+#endif /* __WIN__ */
diff --git a/mysys/my_copy.c b/mysys/my_copy.c
index 84eda781a09..03f3feb54d3 100644
--- a/mysys/my_copy.c
+++ b/mysys/my_copy.c
@@ -18,10 +18,10 @@
#include "mysys_priv.h"
#include <sys/stat.h>
#include <m_string.h>
-#if defined(HAVE_SYS_UTIME_H)
-#include <sys/utime.h>
-#elif defined(HAVE_UTIME_H)
+#if defined(HAVE_UTIME_H)
#include <utime.h>
+#elif defined(HAVE_SYS_UTIME_H)
+#include <sys/utime.h>
#elif !defined(HPUX10)
#include <time.h>
struct utimbuf {
diff --git a/mysys/my_crc32.c b/mysys/my_crc32.c
new file mode 100644
index 00000000000..db1beb58263
--- /dev/null
+++ b/mysys/my_crc32.c
@@ -0,0 +1,23 @@
+/* Copyright (C) 2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#include "mysys_priv.h"
+
+#ifndef HAVE_COMPRESS
+#undef DYNAMIC_CRC_TABLE
+#include "../zlib/crc32.c"
+#endif
+
diff --git a/mysys/my_div.c b/mysys/my_div.c
index 777ffe403d7..9141ff4fcc5 100644
--- a/mysys/my_div.c
+++ b/mysys/my_div.c
@@ -27,7 +27,7 @@
my_string my_filename(File fd)
{
DBUG_ENTER("my_filename");
- if (fd >= MY_NFILE)
+ if ((uint) fd >= (uint) my_file_limit)
DBUG_RETURN((char*) "UNKNOWN");
if (fd >= 0 && my_file_info[fd].type != UNOPEN)
{
diff --git a/mysys/my_dup.c b/mysys/my_dup.c
index df298780e3e..4b7434e29ea 100644
--- a/mysys/my_dup.c
+++ b/mysys/my_dup.c
@@ -32,7 +32,7 @@ File my_dup(File file, myf MyFlags)
DBUG_ENTER("my_dup");
DBUG_PRINT("my",("file: %d MyFlags: %d", MyFlags));
fd = dup(file);
- filename= (((int) file < MY_NFILE) ?
+ filename= (((uint) file < my_file_limit) ?
my_file_info[(int) file].name : "Unknown");
DBUG_RETURN(my_register_filename(fd, filename, FILE_BY_DUP,
EE_FILENOTFOUND, MyFlags));
diff --git a/mysys/my_error.c b/mysys/my_error.c
index 5ce061212b5..0f8ffb7c05f 100644
--- a/mysys/my_error.c
+++ b/mysys/my_error.c
@@ -33,6 +33,12 @@ char NEAR errbuff[NRERRBUFFS][ERRMSGSIZE];
nr Errno
MyFlags Flags
... variable list
+ NOTE
+ The following subset of printf format is supported:
+ "%[0-9.-]*l?[sdu]", where all length flags are parsed but ignored.
+
+ Additionally "%.*s" is supported and "%.*[ud]" is correctly parsed but
+ the length value is ignored.
*/
int my_error(int nr,myf MyFlags, ...)
@@ -43,7 +49,10 @@ int my_error(int nr,myf MyFlags, ...)
reg2 char *endpos;
char * par;
char ebuff[ERRMSGSIZE+20];
+ int prec_chars; /* output precision */
+ my_bool prec_supplied;
DBUG_ENTER("my_error");
+ LINT_INIT(prec_chars); /* protected by prec_supplied */
va_start(ap,MyFlags);
DBUG_PRINT("my", ("nr: %d MyFlags: %d errno: %d", nr, MyFlags, errno));
@@ -59,7 +68,6 @@ int my_error(int nr,myf MyFlags, ...)
if (tpos[0] != '%')
{
*endpos++= *tpos++; /* Copy ordinary char */
- olen++;
continue;
}
if (*++tpos == '%') /* test if %% */
@@ -68,43 +76,84 @@ int my_error(int nr,myf MyFlags, ...)
}
else
{
- /* Skipp if max size is used (to be compatible with printf) */
- while (isdigit(*tpos) || *tpos == '.' || *tpos == '-')
- tpos++;
- if (*tpos == 'l') /* Skipp 'l' argument */
- tpos++;
+ /*
+ Skip size/precision flags to be compatible with printf.
+ The only size/precision flag supported is "%.*s".
+ If "%.*u" or "%.*d" are encountered, the precision number is read
+ from the variable argument list but its value is ignored.
+ */
+ if (*tpos == '-')
+ {
+ tpos++;
+ olen--;
+ }
+ prec_supplied= 0;
+ if (*tpos== '.')
+ {
+ tpos++;
+ olen--;
+ if (*tpos == '*')
+ {
+ tpos++;
+ olen--;
+ prec_chars= va_arg(ap, int); /* get length parameter */
+ prec_supplied= 1;
+ }
+ else
+ {
+ for (prec_chars= 0; my_isdigit(&my_charset_latin1, *tpos); tpos++, olen--)
+ {
+ prec_supplied= 1;
+ prec_chars= prec_chars * 10 + *tpos - '0';
+ }
+ }
+ }
+
+ if (!prec_supplied)
+ {
+ while (my_isdigit(&my_charset_latin1, *tpos) || *tpos == '.' ||
+ *tpos == '-')
+ tpos++;
+
+ if (*tpos == 'l') /* Skip 'l' argument */
+ tpos++;
+ }
+
if (*tpos == 's') /* String parameter */
{
- par = va_arg(ap, char *);
- plen = (uint) strlen(par);
+ par= va_arg(ap, char *);
+ plen= (uint) strlen(par);
+ if (prec_supplied && prec_chars > 0)
+ plen= min((uint)prec_chars, plen);
if (olen + plen < ERRMSGSIZE+2) /* Replace if possible */
{
- endpos=strmov(endpos,par);
- tpos++;
- olen+=plen-2;
- continue;
+ strmake(endpos, par, plen);
+ endpos+= plen;
+ tpos++;
+ olen+= plen-2;
+ continue;
}
}
else if (*tpos == 'd' || *tpos == 'u') /* Integer parameter */
{
register int iarg;
- iarg = va_arg(ap, int);
+ iarg= va_arg(ap, int);
if (*tpos == 'd')
- plen= (uint) (int2str((long) iarg,endpos, -10) - endpos);
+ plen= (uint) (int10_to_str((long) iarg, endpos, -10) - endpos);
else
- plen= (uint) (int2str((long) (uint) iarg,endpos,10)- endpos);
+ plen= (uint) (int10_to_str((long) (uint) iarg, endpos, 10) - endpos);
if (olen + plen < ERRMSGSIZE+2) /* Replace parameter if possible */
{
- endpos+=plen;
+ endpos+= plen;
tpos++;
- olen+=plen-2;
+ olen+= plen-2;
continue;
}
}
}
- *endpos++='%'; /* % used as % or unknown code */
+ *endpos++= '%'; /* % used as % or unknown code */
}
- *endpos='\0'; /* End of errmessage */
+ *endpos= '\0'; /* End of errmessage */
va_end(ap);
DBUG_RETURN((*error_handler_hook)(nr, ebuff, MyFlags));
}
@@ -120,7 +169,7 @@ int my_error(int nr,myf MyFlags, ...)
... variable list
*/
-int my_printf_error (uint error, const char *format, myf MyFlags, ...)
+int my_printf_error(uint error, const char *format, myf MyFlags, ...)
{
va_list args;
char ebuff[ERRMSGSIZE+20];
diff --git a/mysys/my_file.c b/mysys/my_file.c
new file mode 100644
index 00000000000..6a9d39cf944
--- /dev/null
+++ b/mysys/my_file.c
@@ -0,0 +1,147 @@
+/* Copyright (C) 2000 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#include "mysys_priv.h"
+#include "my_static.h"
+#include <m_string.h>
+
+/*
+ set how many open files we want to be able to handle
+
+ SYNOPSIS
+ set_maximum_open_files()
+ max_file_limit Files to open
+
+ NOTES
+ The request may not fulfilled becasue of system limitations
+
+ RETURN
+ Files available to open.
+ May be more or less than max_file_limit!
+*/
+
+#if defined(HAVE_GETRLIMIT) && defined(RLIMIT_NOFILE) && !defined(HAVE_mit_thread)
+
+#ifndef RLIM_INFINITY
+#define RLIM_INFINITY ((uint) 0xffffffff)
+#endif
+
+static uint set_max_open_files(uint max_file_limit)
+{
+ struct rlimit rlimit;
+ uint old_cur;
+ DBUG_ENTER("set_max_open_files");
+ DBUG_PRINT("enter",("files: %u", max_file_limit));
+
+ if (!getrlimit(RLIMIT_NOFILE,&rlimit))
+ {
+ old_cur= (uint) rlimit.rlim_cur;
+ DBUG_PRINT("info", ("rlim_cur: %u rlim_max: %u",
+ (uint) rlimit.rlim_cur,
+ (uint) rlimit.rlim_max));
+ if (rlimit.rlim_cur == RLIM_INFINITY)
+ rlimit.rlim_cur = max_file_limit;
+ if (rlimit.rlim_cur >= max_file_limit)
+ DBUG_RETURN(rlimit.rlim_cur); /* purecov: inspected */
+ rlimit.rlim_cur= rlimit.rlim_max= max_file_limit;
+ if (setrlimit(RLIMIT_NOFILE, &rlimit))
+ max_file_limit= old_cur; /* Use original value */
+ else
+ {
+ rlimit.rlim_cur= 0; /* Safety if next call fails */
+ (void) getrlimit(RLIMIT_NOFILE,&rlimit);
+ DBUG_PRINT("info", ("rlim_cur: %u", (uint) rlimit.rlim_cur));
+ if (rlimit.rlim_cur) /* If call didn't fail */
+ max_file_limit= (uint) rlimit.rlim_cur;
+ }
+ }
+ DBUG_PRINT("exit",("max_file_limit: %u", max_file_limit));
+ DBUG_RETURN(max_file_limit);
+}
+
+#elif defined (OS2)
+
+static uint set_max_open_files(uint max_file_limit)
+{
+ LONG cbReqCount;
+ ULONG cbCurMaxFH0;
+ APIRET ulrc;
+ DBUG_ENTER("set_max_open_files");
+
+ /* get current limit */
+ cbReqCount = 0;
+ DosSetRelMaxFH( &cbReqCount, &cbCurMaxFH0);
+
+ /* set new limit */
+ if ((cbReqCount = max_file_limit - cbCurMaxFH0) > 0)
+ ulrc = DosSetRelMaxFH( &cbReqCount, &cbCurMaxFH);
+ DBUG_RETURN(cbCurMaxFH0);
+}
+
+#else
+static int set_max_open_files(uint max_file_limit)
+{
+ /* We don't know the limit. Return best guess */
+ return min(max_file_limit, OS_FILE_LIMIT);
+}
+#endif
+
+
+/*
+ Change number of open files
+
+ SYNOPSIS:
+ my_set_max_open_files()
+ files Number of requested files
+
+ RETURN
+ number of files available for open
+*/
+
+uint my_set_max_open_files(uint files)
+{
+ struct st_my_file_info *tmp;
+ DBUG_ENTER("my_set_max_open_files");
+ DBUG_PRINT("enter",("files: %u my_file_limit: %u", files, my_file_limit));
+
+ files= set_max_open_files(min(files, OS_FILE_LIMIT));
+ if (files <= MY_NFILE)
+ DBUG_RETURN(files);
+
+ if (!(tmp= (struct st_my_file_info*) my_malloc(sizeof(*tmp) * files,
+ MYF(MY_WME))))
+ DBUG_RETURN(MY_NFILE);
+
+ /* Copy any initialized files */
+ memcpy((char*) tmp, (char*) my_file_info, sizeof(*tmp) * my_file_limit);
+ my_free_open_file_info(); /* Free if already allocated */
+ my_file_info= tmp;
+ my_file_limit= files;
+ DBUG_PRINT("exit",("files: %u", files));
+ DBUG_RETURN(files);
+}
+
+
+void my_free_open_file_info()
+{
+ DBUG_ENTER("my_free_file_info");
+ if (my_file_info != my_file_info_default)
+ {
+ my_free((char*) my_file_info, MYF(0));
+ my_file_info= my_file_info_default;
+ }
+ DBUG_VOID_RETURN;
+}
diff --git a/mysys/my_fopen.c b/mysys/my_fopen.c
index d3b0b90f9c5..f07beec9f39 100644
--- a/mysys/my_fopen.c
+++ b/mysys/my_fopen.c
@@ -19,42 +19,67 @@
#include <errno.h>
#include "mysys_err.h"
-static void make_ftype(my_string to,int flag);
+static void make_ftype(my_string to,int flag);
- /* Open a file as stream */
+/*
+ Open a file as stream
-FILE *my_fopen(const char *FileName, int Flags, myf MyFlags)
- /* Path-name of file */
- /* Read | write .. */
- /* Special flags */
+ SYNOPSIS
+ my_fopen()
+ FileName Path-name of file
+ Flags Read | write | append | trunc (like for open())
+ MyFlags Flags for handling errors
+
+ RETURN
+ 0 Error
+ # File handler
+*/
+
+FILE *my_fopen(const char *filename, int flags, myf MyFlags)
{
FILE *fd;
char type[5];
DBUG_ENTER("my_fopen");
- DBUG_PRINT("my",("Name: '%s' Flags: %d MyFlags: %d",
- FileName, Flags, MyFlags));
-
- make_ftype(type,Flags);
- if ((fd = fopen(FileName, type)) != 0)
+ DBUG_PRINT("my",("Name: '%s' flags: %d MyFlags: %d",
+ filename, flags, MyFlags));
+ /*
+ if we are not creating, then we need to use my_access to make sure
+ the file exists since Windows doesn't handle files like "com1.sym"
+ very well
+ */
+#ifdef __WIN__
+ if (check_if_legal_filename(filename))
+ {
+ errno= EACCES;
+ fd= 0;
+ }
+ else
+#endif
+ {
+ make_ftype(type,flags);
+ fd = fopen(filename, type);
+ }
+
+ if (fd != 0)
{
/*
The test works if MY_NFILE < 128. The problem is that fileno() is char
on some OS (SUNOS). Actually the filename save isn't that important
so we can ignore if this doesn't work.
*/
- if ((uint) fileno(fd) >= MY_NFILE)
+ if ((uint) fileno(fd) >= my_file_limit)
{
thread_safe_increment(my_stream_opened,&THR_LOCK_open);
DBUG_RETURN(fd); /* safeguard */
}
pthread_mutex_lock(&THR_LOCK_open);
if ((my_file_info[fileno(fd)].name = (char*)
- my_strdup(FileName,MyFlags)))
+ my_strdup(filename,MyFlags)))
{
my_stream_opened++;
my_file_info[fileno(fd)].type = STREAM_BY_FOPEN;
pthread_mutex_unlock(&THR_LOCK_open);
- DBUG_PRINT("exit",("stream: %lx",fd));
+ DBUG_PRINT("exit",("stream: 0x%lx",fd));
DBUG_RETURN(fd);
}
pthread_mutex_unlock(&THR_LOCK_open);
@@ -65,9 +90,9 @@ FILE *my_fopen(const char *FileName, int Flags, myf MyFlags)
my_errno=errno;
DBUG_PRINT("error",("Got error %d on open",my_errno));
if (MyFlags & (MY_FFNF | MY_FAE | MY_WME))
- my_error((Flags & O_RDONLY) || (Flags == O_RDONLY ) ? EE_FILENOTFOUND :
+ my_error((flags & O_RDONLY) || (flags == O_RDONLY ) ? EE_FILENOTFOUND :
EE_CANTCREATEFILE,
- MYF(ME_BELL+ME_WAITTANG), FileName,my_errno);
+ MYF(ME_BELL+ME_WAITTANG), filename, my_errno);
DBUG_RETURN((FILE*) 0);
} /* my_fopen */
@@ -78,7 +103,7 @@ int my_fclose(FILE *fd, myf MyFlags)
{
int err,file;
DBUG_ENTER("my_fclose");
- DBUG_PRINT("my",("stream: %lx MyFlags: %d",fd, MyFlags));
+ DBUG_PRINT("my",("stream: 0x%lx MyFlags: %d",fd, MyFlags));
pthread_mutex_lock(&THR_LOCK_open);
file=fileno(fd);
@@ -91,7 +116,7 @@ int my_fclose(FILE *fd, myf MyFlags)
}
else
my_stream_opened--;
- if ((uint) file < MY_NFILE && my_file_info[file].type != UNOPEN)
+ if ((uint) file < my_file_limit && my_file_info[file].type != UNOPEN)
{
my_file_info[file].type = UNOPEN;
my_free(my_file_info[file].name, MYF(MY_ALLOW_ZERO_PTR));
@@ -123,11 +148,11 @@ FILE *my_fdopen(File Filedes, const char *name, int Flags, myf MyFlags)
{
pthread_mutex_lock(&THR_LOCK_open);
my_stream_opened++;
- if (Filedes < MY_NFILE)
+ if ((uint) Filedes < (uint) my_file_limit)
{
if (my_file_info[Filedes].type != UNOPEN)
{
- my_file_opened--; /* File is opened with my_open ! */
+ my_file_opened--; /* File is opened with my_open ! */
}
else
{
@@ -138,36 +163,61 @@ FILE *my_fdopen(File Filedes, const char *name, int Flags, myf MyFlags)
pthread_mutex_unlock(&THR_LOCK_open);
}
- DBUG_PRINT("exit",("stream: %lx",fd));
+ DBUG_PRINT("exit",("stream: 0x%lx",fd));
DBUG_RETURN(fd);
} /* my_fdopen */
- /* Make a filehandler-open-typestring from ordinary inputflags */
+/*
+ Make a fopen() typestring from a open() type bitmap
+
+ SYNOPSIS
+ make_ftype()
+ to String for fopen() is stored here
+ flag Flag used by open()
+
+ IMPLEMENTATION
+ This routine attempts to find the best possible match
+ between a numeric option and a string option that could be
+ fed to fopen. There is not a 1 to 1 mapping between the two.
+
+ NOTE
+ On Unix, O_RDONLY is usually 0
+
+ MAPPING
+ r == O_RDONLY
+ w == O_WRONLY|O_TRUNC|O_CREAT
+ a == O_WRONLY|O_APPEND|O_CREAT
+ r+ == O_RDWR
+ w+ == O_RDWR|O_TRUNC|O_CREAT
+ a+ == O_RDWR|O_APPEND|O_CREAT
+*/
static void make_ftype(register my_string to, register int flag)
{
-#if FILE_BINARY /* If we have binary-files */
- reg3 int org_flag=flag;
-#endif
- flag&= ~FILE_BINARY; /* remove binary bit */
- if (flag == O_RDONLY)
- *to++= 'r';
- else if (flag == O_WRONLY)
- *to++= 'w';
- else
- { /* Add '+' after theese */
- if (flag == O_RDWR)
+ /* check some possible invalid combinations */
+ DBUG_ASSERT((flag & (O_TRUNC | O_APPEND)) != (O_TRUNC | O_APPEND));
+ DBUG_ASSERT((flag & (O_WRONLY | O_RDWR)) != (O_WRONLY | O_RDWR));
+
+ if ((flag & (O_RDONLY|O_WRONLY)) == O_WRONLY)
+ *to++= (flag & O_APPEND) ? 'a' : 'w';
+ else if (flag & O_RDWR)
+ {
+ /* Add '+' after theese */
+ if (flag & (O_TRUNC | O_CREAT))
+ *to++= 'w';
+ else if (flag & O_APPEND)
+ *to++= 'a';
+ else
*to++= 'r';
- else if (flag & O_APPEND)
- *to++= 'a';
- else
- *to++= 'w'; /* Create file */
- *to++= '+';
- }
-#if FILE_BINARY /* If we have binary-files */
- if (org_flag & FILE_BINARY)
+ *to++= '+';
+ }
+ else
+ *to++= 'r';
+
+#if FILE_BINARY /* If we have binary-files */
+ if (flag & FILE_BINARY)
*to++='b';
-#endif
+#endif
*to='\0';
} /* make_ftype */
diff --git a/mysys/my_fstream.c b/mysys/my_fstream.c
index 0ad789e98ac..5b17e3ff51c 100644
--- a/mysys/my_fstream.c
+++ b/mysys/my_fstream.c
@@ -39,7 +39,7 @@ uint my_fread(FILE *stream, byte *Buffer, uint Count, myf MyFlags)
{
uint readbytes;
DBUG_ENTER("my_fread");
- DBUG_PRINT("my",("stream: %lx Buffer: %lx Count: %u MyFlags: %d",
+ DBUG_PRINT("my",("stream: 0x%lx Buffer: 0x%lx Count: %u MyFlags: %d",
stream, Buffer, Count, MyFlags));
if ((readbytes = (uint) fread(Buffer,sizeof(char),(size_t) Count,stream))
@@ -80,7 +80,7 @@ uint my_fwrite(FILE *stream, const byte *Buffer, uint Count, myf MyFlags)
uint errors;
#endif
DBUG_ENTER("my_fwrite");
- DBUG_PRINT("my",("stream: %lx Buffer: %lx Count: %u MyFlags: %d",
+ DBUG_PRINT("my",("stream: 0x%lx Buffer: 0x%lx Count: %u MyFlags: %d",
stream, Buffer, Count, MyFlags));
#if !defined(NO_BACKGROUND) && defined(USE_MY_STREAM)
@@ -152,7 +152,7 @@ my_off_t my_fseek(FILE *stream, my_off_t pos, int whence,
myf MyFlags __attribute__((unused)))
{
DBUG_ENTER("my_fseek");
- DBUG_PRINT("my",("stream: %lx pos: %lu whence: %d MyFlags: %d",
+ DBUG_PRINT("my",("stream: 0x%lx pos: %lu whence: %d MyFlags: %d",
stream, pos, whence, MyFlags));
DBUG_RETURN(fseek(stream, (off_t) pos, whence) ?
MY_FILEPOS_ERROR : (my_off_t) ftell(stream));
@@ -166,7 +166,7 @@ my_off_t my_ftell(FILE *stream, myf MyFlags __attribute__((unused)))
{
off_t pos;
DBUG_ENTER("my_ftell");
- DBUG_PRINT("my",("stream: %lx MyFlags: %d",stream, MyFlags));
+ DBUG_PRINT("my",("stream: 0x%lx MyFlags: %d",stream, MyFlags));
pos=ftell(stream);
DBUG_PRINT("exit",("ftell: %lu",(ulong) pos));
DBUG_RETURN((my_off_t) pos);
diff --git a/mysys/my_gethostbyname.c b/mysys/my_gethostbyname.c
index 5044a505054..27281f3489d 100644
--- a/mysys/my_gethostbyname.c
+++ b/mysys/my_gethostbyname.c
@@ -18,7 +18,6 @@
/* Thread safe version of gethostbyname_r() */
#include "mysys_priv.h"
-#include <assert.h>
#if !defined(MSDOS) && !defined(__WIN__)
#include <netdb.h>
#endif
diff --git a/mysys/my_gethwaddr.c b/mysys/my_gethwaddr.c
new file mode 100644
index 00000000000..222abe81933
--- /dev/null
+++ b/mysys/my_gethwaddr.c
@@ -0,0 +1,130 @@
+/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+/* get hardware address for an interface */
+/* if there are many available, any non-zero one can be used */
+
+#include "mysys_priv.h"
+#include <m_string.h>
+
+#ifndef MAIN
+static my_bool memcpy_and_test(uchar *to, uchar *from, uint len)
+{
+ uint i, res=1;
+
+ for (i=0; i < len; i++)
+ if ((*to++= *from++))
+ res=0;
+ return res;
+}
+
+#ifdef __FreeBSD__
+
+#include <net/ethernet.h>
+#include <sys/sysctl.h>
+#include <net/route.h>
+#include <net/if.h>
+#include <net/if_dl.h>
+
+my_bool my_gethwaddr(uchar *to)
+{
+ size_t len;
+ uchar *buf, *next, *end, *addr;
+ struct if_msghdr *ifm;
+ struct sockaddr_dl *sdl;
+ int i, res=1, mib[6]={CTL_NET, AF_ROUTE, 0, AF_LINK, NET_RT_IFLIST, 0};
+
+ if (sysctl(mib, 6, NULL, &len, NULL, 0) == -1)
+ goto err;
+ if (!(buf = alloca(len)))
+ goto err;
+ if (sysctl(mib, 6, buf, &len, NULL, 0) < 0)
+ goto err;
+
+ end = buf + len;
+
+ for (next = buf ; res && next < end ; next += ifm->ifm_msglen)
+ {
+ ifm = (struct if_msghdr *)next;
+ if (ifm->ifm_type == RTM_IFINFO)
+ {
+ sdl = (struct sockaddr_dl *)(ifm + 1);
+ addr=LLADDR(sdl);
+ res=memcpy_and_test(to, addr, ETHER_ADDR_LEN);
+ }
+ }
+
+err:
+ return res;
+}
+
+#elif __linux__
+
+#include <net/if.h>
+#include <sys/ioctl.h>
+#include <net/ethernet.h>
+
+my_bool my_gethwaddr(uchar *to)
+{
+ int fd, res=1;
+ struct ifreq ifr;
+
+ fd = socket(AF_INET, SOCK_DGRAM, 0);
+ if (fd < 0)
+ goto err;
+
+ bzero(&ifr, sizeof(ifr));
+ strnmov(ifr.ifr_name, "eth0", sizeof(ifr.ifr_name) - 1);
+
+ do {
+ if (ioctl(fd, SIOCGIFHWADDR, &ifr) >= 0)
+ res=memcpy_and_test(to, (uchar *)&ifr.ifr_hwaddr.sa_data, ETHER_ADDR_LEN);
+ } while (res && (errno == 0 || errno == ENODEV) && ifr.ifr_name[3]++ < '6');
+
+ close(fd);
+err:
+ return res;
+}
+
+#else
+/* just fail */
+my_bool my_gethwaddr(uchar *to __attribute__((unused)))
+{
+ return 1;
+}
+#endif
+
+#else /* MAIN */
+int main(int argc __attribute__((unused)),char **argv)
+{
+ uchar mac[6];
+ uint i;
+ MY_INIT(argv[0]);
+ if (my_gethwaddr(mac))
+ {
+ printf("my_gethwaddr failed with errno %d\n", errno);
+ exit(1);
+ }
+ for (i=0; i < sizeof(mac); i++)
+ {
+ if (i) printf(":");
+ printf("%02x", mac[i]);
+ }
+ printf("\n");
+ return 0;
+}
+#endif
+
diff --git a/mysys/my_getopt.c b/mysys/my_getopt.c
index 71f8819756a..075956c7af9 100644
--- a/mysys/my_getopt.c
+++ b/mysys/my_getopt.c
@@ -17,7 +17,6 @@
#include <my_global.h>
#include <m_string.h>
#include <stdlib.h>
-#include <assert.h>
#include <my_sys.h>
#include <mysys_err.h>
#include <my_getopt.h>
@@ -35,8 +34,9 @@ static longlong getopt_ll(char *arg, const struct my_option *optp, int *err);
static ulonglong getopt_ull(char *arg, const struct my_option *optp,
int *err);
static void init_variables(const struct my_option *options);
-static int setval(const struct my_option *opts,char *argument,
+static int setval(const struct my_option *opts, gptr *value, char *argument,
my_bool set_maximum_value);
+static char *check_struct_option(char *cur_arg, char *key_name);
/*
The following three variables belong to same group and the number and
@@ -59,13 +59,18 @@ char *disabled_my_option= (char*) "0";
my_bool my_getopt_print_errors= 1;
-static void default_reporter(enum loglevel level __attribute__((unused)),
+static void default_reporter(enum loglevel level,
const char *format, ...)
{
va_list args;
va_start(args, format);
+ if (level == WARNING_LEVEL)
+ fprintf(stderr, "%s", "Warning: ");
+ else if (level == INFORMATION_LEVEL)
+ fprintf(stderr, "%s", "Info: ");
vfprintf(stderr, format, args);
va_end(args);
+ fflush(stderr);
}
/*
@@ -78,15 +83,25 @@ static void default_reporter(enum loglevel level __attribute__((unused)),
one. Call function 'get_one_option()' once for each option.
*/
+static gptr* (*getopt_get_addr)(const char *, uint, const struct my_option *);
+
+void my_getopt_register_get_addr(gptr* (*func_addr)(const char *, uint,
+ const struct my_option *))
+{
+ getopt_get_addr= func_addr;
+}
+
int handle_options(int *argc, char ***argv,
const struct my_option *longopts,
my_get_one_option get_one_option)
{
uint opt_found, argvpos= 0, length, i;
- my_bool end_of_options= 0, must_be_var, set_maximum_value, special_used,
+ my_bool end_of_options= 0, must_be_var, set_maximum_value,
option_is_loose;
- char *progname= *(*argv), **pos, **pos_end, *optend, *prev_found;
+ char **pos, **pos_end, *optend, *prev_found,
+ *opt_str, key_name[FN_REFLEN];
const struct my_option *optp;
+ gptr *value;
int error;
LINT_INIT(opt_found);
@@ -102,7 +117,6 @@ int handle_options(int *argc, char ***argv,
char *argument= 0;
must_be_var= 0;
set_maximum_value= 0;
- special_used= 0;
option_is_loose= 0;
cur_arg++; /* skip '-' */
@@ -120,7 +134,7 @@ int handle_options(int *argc, char ***argv,
if (my_getopt_print_errors)
my_getopt_error_reporter(ERROR_LEVEL,
"%s: Option '-O' requires an argument\n",
- progname);
+ my_progname);
return EXIT_ARGUMENT_REQUIRED;
}
cur_arg= *pos;
@@ -138,7 +152,7 @@ int handle_options(int *argc, char ***argv,
if (my_getopt_print_errors)
my_getopt_error_reporter(ERROR_LEVEL,
"%s: Option '--set-variable' requires an argument\n",
- progname);
+ my_progname);
return EXIT_ARGUMENT_REQUIRED;
}
}
@@ -152,7 +166,7 @@ int handle_options(int *argc, char ***argv,
if (my_getopt_print_errors)
my_getopt_error_reporter(ERROR_LEVEL,
"%s: Option '--set-variable' requires an argument\n",
- progname);
+ my_progname);
return EXIT_ARGUMENT_REQUIRED;
}
cur_arg= *pos;
@@ -169,19 +183,20 @@ int handle_options(int *argc, char ***argv,
continue;
}
}
- optend= strcend(cur_arg, '=');
- length= optend - cur_arg;
+ opt_str= check_struct_option(cur_arg, key_name);
+ optend= strcend(opt_str, '=');
+ length= optend - opt_str;
if (*optend == '=')
optend++;
else
- optend=0;
+ optend= 0;
/*
Find first the right option. Return error in case of an ambiguous,
or unknown option
*/
optp= longopts;
- if (!(opt_found= findopt(cur_arg, length, &optp, &prev_found)))
+ if (!(opt_found= findopt(opt_str, length, &optp, &prev_found)))
{
/*
Didn't find any matching option. Let's see if someone called
@@ -193,18 +208,17 @@ int handle_options(int *argc, char ***argv,
must_be_var= 1; /* option is followed by an argument */
for (i= 0; special_opt_prefix[i]; i++)
{
- if (!getopt_compare_strings(special_opt_prefix[i], cur_arg,
+ if (!getopt_compare_strings(special_opt_prefix[i], opt_str,
special_opt_prefix_lengths[i]) &&
- cur_arg[special_opt_prefix_lengths[i]] == '-')
+ opt_str[special_opt_prefix_lengths[i]] == '-')
{
/*
We were called with a special prefix, we can reuse opt_found
*/
- special_used= 1;
- cur_arg+= (special_opt_prefix_lengths[i] + 1);
+ opt_str+= (special_opt_prefix_lengths[i] + 1);
if (i == OPT_LOOSE)
option_is_loose= 1;
- if ((opt_found= findopt(cur_arg, length -
+ if ((opt_found= findopt(opt_str, length -
(special_opt_prefix_lengths[i] + 1),
&optp, &prev_found)))
{
@@ -213,7 +227,7 @@ int handle_options(int *argc, char ***argv,
if (my_getopt_print_errors)
my_getopt_error_reporter(ERROR_LEVEL,
"%s: ambiguous option '--%s-%s' (--%s-%s)\n",
- progname, special_opt_prefix[i],
+ my_progname, special_opt_prefix[i],
cur_arg, special_opt_prefix[i],
prev_found);
return EXIT_AMBIGUOUS_OPTION;
@@ -250,7 +264,7 @@ int handle_options(int *argc, char ***argv,
my_getopt_error_reporter(option_is_loose ?
WARNING_LEVEL : ERROR_LEVEL,
"%s: unknown variable '%s'\n",
- progname, cur_arg);
+ my_progname, cur_arg);
if (!option_is_loose)
return EXIT_UNKNOWN_VARIABLE;
}
@@ -260,7 +274,7 @@ int handle_options(int *argc, char ***argv,
my_getopt_error_reporter(option_is_loose ?
WARNING_LEVEL : ERROR_LEVEL,
"%s: unknown option '--%s'\n",
- progname, cur_arg);
+ my_progname, cur_arg);
if (!option_is_loose)
return EXIT_UNKNOWN_OPTION;
}
@@ -278,7 +292,7 @@ int handle_options(int *argc, char ***argv,
if (my_getopt_print_errors)
my_getopt_error_reporter(ERROR_LEVEL,
"%s: variable prefix '%s' is not unique\n",
- progname, cur_arg);
+ my_progname, opt_str);
return EXIT_VAR_PREFIX_NOT_UNIQUE;
}
else
@@ -286,30 +300,46 @@ int handle_options(int *argc, char ***argv,
if (my_getopt_print_errors)
my_getopt_error_reporter(ERROR_LEVEL,
"%s: ambiguous option '--%s' (%s, %s)\n",
- progname, cur_arg, prev_found,
+ my_progname, opt_str, prev_found,
optp->name);
return EXIT_AMBIGUOUS_OPTION;
}
}
- if (must_be_var && optp->var_type == GET_NO_ARG)
+ if ((optp->var_type & GET_TYPE_MASK) == GET_DISABLED)
+ {
+ if (my_getopt_print_errors)
+ fprintf(stderr,
+ "%s: %s: Option '%s' used, but is disabled\n", my_progname,
+ option_is_loose ? "WARNING" : "ERROR", opt_str);
+ if (option_is_loose)
+ {
+ (*argc)--;
+ continue;
+ }
+ return EXIT_OPTION_DISABLED;
+ }
+ if (must_be_var && (optp->var_type & GET_TYPE_MASK) == GET_NO_ARG)
{
if (my_getopt_print_errors)
my_getopt_error_reporter(ERROR_LEVEL,
"%s: option '%s' cannot take an argument\n",
- progname, optp->name);
+ my_progname, optp->name);
return EXIT_NO_ARGUMENT_ALLOWED;
}
+ value= optp->var_type & GET_ASK_ADDR ?
+ (*getopt_get_addr)(key_name, strlen(key_name), optp) : optp->value;
+
if (optp->arg_type == NO_ARG)
{
- if (optend && optp->var_type != GET_BOOL)
+ if (optend && (optp->var_type & GET_TYPE_MASK) != GET_BOOL)
{
if (my_getopt_print_errors)
my_getopt_error_reporter(ERROR_LEVEL,
"%s: option '--%s' cannot take an argument\n",
- progname, optp->name);
+ my_progname, optp->name);
return EXIT_NO_ARGUMENT_ALLOWED;
}
- if (optp->var_type == GET_BOOL)
+ if ((optp->var_type & GET_TYPE_MASK) == GET_BOOL)
{
/*
Set bool to 1 if no argument or if the user has used
@@ -317,7 +347,7 @@ int handle_options(int *argc, char ***argv,
*optend was set to '0' if one used --disable-option
*/
my_bool tmp= (my_bool) (!optend || *optend == '1');
- *((my_bool*) optp->value)= tmp;
+ *((my_bool*) value)= tmp;
(*argc)--;
get_one_option(optp->id, optp,
tmp ? (char*) "1" : disabled_my_option);
@@ -325,14 +355,15 @@ int handle_options(int *argc, char ***argv,
}
argument= optend;
}
- else if (optp->arg_type == OPT_ARG && optp->var_type == GET_BOOL)
+ else if (optp->arg_type == OPT_ARG &&
+ (optp->var_type & GET_TYPE_MASK) == GET_BOOL)
{
if (optend == disabled_my_option)
- *((my_bool*) optp->value)= (my_bool) 0;
+ *((my_bool*) value)= (my_bool) 0;
else
{
if (!optend) /* No argument -> enable option */
- *((my_bool*) optp->value)= (my_bool) 1;
+ *((my_bool*) value)= (my_bool) 1;
else
argument= optend;
}
@@ -345,7 +376,7 @@ int handle_options(int *argc, char ***argv,
if (my_getopt_print_errors)
my_getopt_error_reporter(ERROR_LEVEL,
"%s: option '--%s' requires an argument\n",
- progname, optp->name);
+ my_progname, optp->name);
return EXIT_ARGUMENT_REQUIRED;
}
argument= *pos;
@@ -365,7 +396,16 @@ int handle_options(int *argc, char ***argv,
{
/* Option recognized. Find next what to do with it */
opt_found= 1;
- if (optp->var_type == GET_BOOL && optp->arg_type == NO_ARG)
+ if ((optp->var_type & GET_TYPE_MASK) == GET_DISABLED)
+ {
+ if (my_getopt_print_errors)
+ fprintf(stderr,
+ "%s: ERROR: Option '-%c' used, but is disabled\n",
+ my_progname, optp->id);
+ return EXIT_OPTION_DISABLED;
+ }
+ if ((optp->var_type & GET_TYPE_MASK) == GET_BOOL &&
+ optp->arg_type == NO_ARG)
{
*((my_bool*) optp->value)= (my_bool) 1;
get_one_option(optp->id, optp, argument);
@@ -396,7 +436,7 @@ int handle_options(int *argc, char ***argv,
if (my_getopt_print_errors)
my_getopt_error_reporter(ERROR_LEVEL,
"%s: option '-%c' requires an argument\n",
- progname, optp->id);
+ my_progname, optp->id);
return EXIT_ARGUMENT_REQUIRED;
}
argument= *++pos;
@@ -404,11 +444,12 @@ int handle_options(int *argc, char ***argv,
/* the other loop will break, because *optend + 1 == 0 */
}
}
- if ((error= setval(optp, argument, set_maximum_value)))
+ if ((error= setval(optp, optp->value, argument,
+ set_maximum_value)))
{
my_getopt_error_reporter(ERROR_LEVEL,
"%s: Error while setting value '%s' to '%s'\n",
- progname, argument, optp->name);
+ my_progname, argument, optp->name);
return error;
}
get_one_option(optp->id, optp, argument);
@@ -420,18 +461,18 @@ int handle_options(int *argc, char ***argv,
if (my_getopt_print_errors)
my_getopt_error_reporter(ERROR_LEVEL,
"%s: unknown option '-%c'\n",
- progname, *optend);
+ my_progname, *optend);
return EXIT_UNKNOWN_OPTION;
}
}
(*argc)--; /* option handled (short), decrease argument count */
continue;
}
- if ((error= setval(optp, argument, set_maximum_value)))
+ if ((error= setval(optp, value, argument, set_maximum_value)))
{
my_getopt_error_reporter(ERROR_LEVEL,
"%s: Error while setting value '%s' to '%s'\n",
- progname, argument, optp->name);
+ my_progname, argument, optp->name);
return error;
}
get_one_option(optp->id, optp, argument);
@@ -451,6 +492,49 @@ int handle_options(int *argc, char ***argv,
return 0;
}
+
+/*
+ function: check_struct_option
+
+ Arguments: Current argument under processing from argv and a variable
+ where to store the possible key name.
+
+ Return value: In case option is a struct option, returns a pointer to
+ the current argument at the position where the struct option (key_name)
+ ends, the next character after the dot. In case argument is not a struct
+ option, returns a pointer to the argument.
+
+ key_name will hold the name of the key, or 0 if not found.
+*/
+
+static char *check_struct_option(char *cur_arg, char *key_name)
+{
+ char *ptr, *end;
+
+ ptr= strcend(cur_arg + 1, '.'); /* Skip the first character */
+ end= strcend(cur_arg, '=');
+
+ /*
+ If the first dot is after an equal sign, then it is part
+ of a variable value and the option is not a struct option.
+ Also, if the last character in the string before the ending
+ NULL, or the character right before equal sign is the first
+ dot found, the option is not a struct option.
+ */
+ if (end - ptr > 1)
+ {
+ uint len= ptr - cur_arg;
+ set_if_smaller(len, FN_REFLEN-1);
+ strmake(key_name, cur_arg, len);
+ return ++ptr;
+ }
+ else
+ {
+ key_name[0]= 0;
+ return cur_arg;
+ }
+}
+
/*
function: setval
@@ -458,20 +542,20 @@ int handle_options(int *argc, char ***argv,
Will set the option value to given value
*/
-static int setval(const struct my_option *opts, char *argument,
+static int setval(const struct my_option *opts, gptr *value, char *argument,
my_bool set_maximum_value)
{
int err= 0;
- if (opts->value && argument)
+ if (value && argument)
{
gptr *result_pos= ((set_maximum_value) ?
- opts->u_max_value : opts->value);
+ opts->u_max_value : value);
if (!result_pos)
return EXIT_NO_PTR_TO_VARIABLE;
- switch (opts->var_type) {
+ switch ((opts->var_type & GET_TYPE_MASK)) {
case GET_BOOL: /* If argument differs from 0, enable option, else disable */
*((my_bool*) result_pos)= (my_bool) atoi(argument) != 0;
break;
@@ -523,7 +607,7 @@ static int findopt(char *optpat, uint length,
const struct my_option **opt_res,
char **ffname)
{
- int count;
+ uint count;
struct my_option *opt= (struct my_option *) *opt_res;
for (count= 0; opt->name; opt++)
@@ -535,7 +619,8 @@ static int findopt(char *optpat, uint length,
*ffname= (char *) opt->name; /* We only need to know one prev */
if (!opt->name[length]) /* Exact match */
return 1;
- count++;
+ if (!count || strcmp(*ffname, opt->name)) /* Don't count synonyms */
+ count++;
}
}
return count;
@@ -609,10 +694,10 @@ static longlong getopt_ll(char *arg, const struct my_option *optp, int *err)
ulonglong block_size= (optp->block_size ? (ulonglong) optp->block_size : 1L);
num= eval_num_suffix(arg, err, (char*) optp->name);
- if (num > 0 && (ulonglong) num > (ulonglong) (ulong) optp->max_value &&
+ if (num > 0 && (ulonglong) num > (ulonglong) optp->max_value &&
optp->max_value) /* if max value is not set -> no upper limit */
- num= (longlong) (ulong) optp->max_value;
- num= ((num - (longlong) optp->sub_size) / block_size);
+ num= (ulonglong) optp->max_value;
+ num= ((num - optp->sub_size) / block_size);
num= (longlong) (num * block_size);
return max(num, optp->min_value);
}
@@ -648,58 +733,77 @@ ulonglong getopt_ull_limit_value(ulonglong num, const struct my_option *optp)
return num;
}
-/*
- function: init_variables
+/*
+ Init one value to it's default values
+
+ SYNOPSIS
+ init_one_value()
+ option Option to initialize
+ value Pointer to variable
+*/
+
+static void init_one_value(const struct my_option *option, gptr *variable,
+ longlong value)
+{
+ switch ((option->var_type & GET_TYPE_MASK)) {
+ case GET_BOOL:
+ *((my_bool*) variable)= (my_bool) value;
+ break;
+ case GET_INT:
+ *((int*) variable)= (int) value;
+ break;
+ case GET_UINT:
+ *((uint*) variable)= (uint) value;
+ break;
+ case GET_LONG:
+ *((long*) variable)= (long) value;
+ break;
+ case GET_ULONG:
+ *((ulong*) variable)= (ulong) value;
+ break;
+ case GET_LL:
+ *((longlong*) variable)= (longlong) value;
+ break;
+ case GET_ULL:
+ *((ulonglong*) variable)= (ulonglong) value;
+ break;
+ default: /* dummy default to avoid compiler warnings */
+ break;
+ }
+}
+
+
+/*
initialize all variables to their default values
+
+ SYNOPSIS
+ init_variables()
+ options Array of options
+
+ NOTES
+ We will initialize the value that is pointed to by options->value.
+ If the value is of type GET_ASK_ADDR, we will also ask for the address
+ for a value and initialize.
*/
static void init_variables(const struct my_option *options)
{
for (; options->name; options++)
{
+ gptr *variable;
+ /*
+ We must set u_max_value first as for some variables
+ options->u_max_value == options->value and in this case we want to
+ set the value to default value.
+ */
+ if (options->u_max_value)
+ init_one_value(options, options->u_max_value, options->max_value);
if (options->value)
- {
- switch (options->var_type) {
- case GET_BOOL:
- if (options->u_max_value)
- *((my_bool*) options->u_max_value)= (my_bool) options->max_value;
- *((my_bool*) options->value)= (my_bool) options->def_value;
- break;
- case GET_INT:
- if (options->u_max_value)
- *((int*) options->u_max_value)= (int) options->max_value;
- *((int*) options->value)= (int) options->def_value;
- break;
- case GET_UINT:
- if (options->u_max_value)
- *((uint*) options->u_max_value)= (uint) options->max_value;
- *((uint*) options->value)= (uint) options->def_value;
- break;
- case GET_LONG:
- if (options->u_max_value)
- *((long*) options->u_max_value)= (long) options->max_value;
- *((long*) options->value)= (long) options->def_value;
- break;
- case GET_ULONG:
- if (options->u_max_value)
- *((ulong*) options->u_max_value)= (ulong) options->max_value;
- *((ulong*) options->value)= (ulong) options->def_value;
- break;
- case GET_LL:
- if (options->u_max_value)
- *((longlong*) options->u_max_value)= (longlong) options->max_value;
- *((longlong*) options->value)= (longlong) options->def_value;
- break;
- case GET_ULL:
- if (options->u_max_value)
- *((ulonglong*) options->u_max_value)= (ulonglong) options->max_value;
- *((ulonglong*) options->value)= (ulonglong) options->def_value;
- break;
- default: /* dummy default to avoid compiler warnings */
- break;
- }
- }
+ init_one_value(options, options->value, options->def_value);
+ if (options->var_type & GET_ASK_ADDR &&
+ (variable= (*getopt_get_addr)("", 0, options)))
+ init_one_value(options, variable, options->def_value);
}
}
@@ -734,13 +838,15 @@ void my_print_help(const struct my_option *options)
{
printf("--%s", optp->name);
col+= 2 + strlen(optp->name);
- if (optp->var_type == GET_STR || optp->var_type == GET_STR_ALLOC)
+ if ((optp->var_type & GET_TYPE_MASK) == GET_STR ||
+ (optp->var_type & GET_TYPE_MASK) == GET_STR_ALLOC)
{
printf("%s=name%s ", optp->arg_type == OPT_ARG ? "[" : "",
optp->arg_type == OPT_ARG ? "]" : "");
col+= (optp->arg_type == OPT_ARG) ? 8 : 6;
}
- else if (optp->var_type == GET_NO_ARG || optp->var_type == GET_BOOL)
+ else if ((optp->var_type & GET_TYPE_MASK) == GET_NO_ARG ||
+ (optp->var_type & GET_TYPE_MASK) == GET_BOOL)
{
putchar(' ');
col++;
@@ -797,41 +903,44 @@ void my_print_variables(const struct my_option *options)
printf("--------------------------------- -----------------------------\n");
for (optp= options; optp->id; optp++)
{
- if (optp->value)
+ gptr *value= (optp->var_type & GET_ASK_ADDR ?
+ (*getopt_get_addr)("", 0, optp) : optp->value);
+ if (value)
{
printf("%s", optp->name);
length= strlen(optp->name);
for (; length < name_space; length++)
putchar(' ');
- switch (optp->var_type) {
+ switch ((optp->var_type & GET_TYPE_MASK)) {
case GET_STR:
case GET_STR_ALLOC: /* fall through */
- printf("%s\n", *((char**) optp->value) ? *((char**) optp->value) :
+ printf("%s\n", *((char**) value) ? *((char**) value) :
"(No default value)");
break;
case GET_BOOL:
- printf("%s\n", *((my_bool*) optp->value) ? "TRUE" : "FALSE");
+ printf("%s\n", *((my_bool*) value) ? "TRUE" : "FALSE");
break;
case GET_INT:
- printf("%d\n", *((int*) optp->value));
+ printf("%d\n", *((int*) value));
break;
case GET_UINT:
- printf("%d\n", *((uint*) optp->value));
+ printf("%d\n", *((uint*) value));
break;
case GET_LONG:
- printf("%lu\n", *((long*) optp->value));
+ printf("%lu\n", *((long*) value));
break;
case GET_ULONG:
- printf("%lu\n", *((ulong*) optp->value));
+ printf("%lu\n", *((ulong*) value));
break;
case GET_LL:
- printf("%s\n", llstr(*((longlong*) optp->value), buff));
+ printf("%s\n", llstr(*((longlong*) value), buff));
break;
case GET_ULL:
- longlong2str(*((ulonglong*) optp->value), buff, 10);
+ longlong2str(*((ulonglong*) value), buff, 10);
printf("%s\n", buff);
break;
- default: /* dummy default to avoid compiler warnings */
+ default:
+ printf("(Disabled)\n");
break;
}
}
diff --git a/mysys/my_getsystime.c b/mysys/my_getsystime.c
new file mode 100644
index 00000000000..91c977f0b5a
--- /dev/null
+++ b/mysys/my_getsystime.c
@@ -0,0 +1,65 @@
+/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+/* get time since epoc in 100 nanosec units */
+/* thus to get the current time we should use the system function
+ with the highest possible resolution */
+
+#ifdef __NETWARE__
+#include <nks/time.h>
+#endif
+
+#include "mysys_priv.h"
+ulonglong my_getsystime()
+{
+#ifdef HAVE_CLOCK_GETTIME
+ struct timespec tp;
+ clock_gettime(CLOCK_REALTIME, &tp);
+ return (ulonglong)tp.tv_sec*10000000+(ulonglong)tp.tv_nsec/100;
+#elif defined(__WIN__)
+#define OFFSET_TO_EPOC ((__int64) 134774 * 24 * 60 * 60 * 1000 * 1000 * 10)
+ static __int64 offset=0, freq;
+ LARGE_INTEGER t_cnt;
+ if (!offset)
+ {
+ /* strictly speaking there should be a mutex to protect
+ initialization section. But my_getsystime() is called from
+ UUID() code, and UUID() calls are serialized with a mutex anyway
+ */
+ LARGE_INTEGER li;
+ FILETIME ft;
+ GetSystemTimeAsFileTime(&ft);
+ li.LowPart=ft.dwLowDateTime;
+ li.HighPart=ft.dwHighDateTime;
+ offset=li.QuadPart-OFFSET_TO_EPOC;
+ QueryPerformanceFrequency(&li);
+ freq=li.QuadPart;
+ QueryPerformanceCounter(&t_cnt);
+ offset-=t_cnt.QuadPart/freq*10000000+t_cnt.QuadPart%freq*10000000/freq;
+ }
+ QueryPerformanceCounter(&t_cnt);
+ return t_cnt.QuadPart/freq*10000000+t_cnt.QuadPart%freq*10000000/freq+offset;
+#elif defined(__NETWARE__)
+ NXTime_t tm;
+ NXGetTime(NX_SINCE_1970, NX_NSECONDS, &tm);
+ return (ulonglong)tm/100;
+#else
+ /* TODO: check for other possibilities for hi-res timestamping */
+ struct timeval tv;
+ gettimeofday(&tv,NULL);
+ return (ulonglong)tv.tv_sec*10000000+(ulonglong)tv.tv_usec*10;
+#endif
+}
diff --git a/mysys/my_getwd.c b/mysys/my_getwd.c
index 63ab17b4c51..5663ceaa60e 100644
--- a/mysys/my_getwd.c
+++ b/mysys/my_getwd.c
@@ -45,7 +45,7 @@ int my_getwd(my_string buf, uint size, myf MyFlags)
{
my_string pos;
DBUG_ENTER("my_getwd");
- DBUG_PRINT("my",("buf: %lx size: %d MyFlags %d", buf,size,MyFlags));
+ DBUG_PRINT("my",("buf: 0x%lx size: %d MyFlags %d", buf,size,MyFlags));
#if ! defined(MSDOS)
if (curr_dir[0]) /* Current pos is saved here */
@@ -108,8 +108,9 @@ int my_setwd(const char *dir, myf MyFlags)
{
uint drive,drives;
- pos++; /* Skipp FN_DEVCHAR */
- drive=(uint) (toupper(dir[0])-'A'+1); drives= (uint) -1;
+ pos++; /* Skip FN_DEVCHAR */
+ drive=(uint) (my_toupper(&my_charset_latin1,dir[0])-'A'+1);
+ drives= (uint) -1;
if ((pos-(byte*) dir) == 2 && drive > 0 && drive < 32)
{
#ifdef OS2
@@ -191,3 +192,28 @@ int test_if_hard_path(register const char *dir_name)
return FALSE;
#endif
} /* test_if_hard_path */
+
+
+/*
+ Test if a name contains an (absolute or relative) path.
+
+ SYNOPSIS
+ has_path()
+ name The name to test.
+
+ RETURN
+ TRUE name contains a path.
+ FALSE name does not contain a path.
+*/
+
+my_bool has_path(const char *name)
+{
+ return test(strchr(name, FN_LIBCHAR))
+#if FN_LIBCHAR != '/'
+ || test(strchr(name,'/'))
+#endif
+#ifdef FN_DEVCHAR
+ || test(strchr(name, FN_DEVCHAR))
+#endif
+ ;
+}
diff --git a/mysys/my_handler.c b/mysys/my_handler.c
new file mode 100644
index 00000000000..156e7892580
--- /dev/null
+++ b/mysys/my_handler.c
@@ -0,0 +1,550 @@
+/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA */
+
+#include <my_global.h>
+#include "my_handler.h"
+
+int mi_compare_text(CHARSET_INFO *charset_info, uchar *a, uint a_length,
+ uchar *b, uint b_length, my_bool part_key,
+ my_bool skip_end_space)
+{
+ if (skip_end_space)
+ return charset_info->coll->strnncollsp(charset_info, a, a_length,
+ b, b_length);
+ return charset_info->coll->strnncoll(charset_info, a, a_length,
+ b, b_length, part_key);
+}
+
+
+static int compare_bin(uchar *a, uint a_length, uchar *b, uint b_length,
+ my_bool part_key, my_bool skip_end_space)
+{
+ uint length= min(a_length,b_length);
+ uchar *end= a+ length;
+ int flag;
+
+ while (a < end)
+ if ((flag= (int) *a++ - (int) *b++))
+ return flag;
+ if (part_key && b_length < a_length)
+ return 0;
+ if (skip_end_space && a_length != b_length)
+ {
+ int swap= 1;
+ /*
+ We are using space compression. We have to check if longer key
+ has next character < ' ', in which case it's less than the shorter
+ key that has an implicite space afterwards.
+
+ This code is identical to the one in
+ strings/ctype-simple.c:my_strnncollsp_simple
+ */
+ if (a_length < b_length)
+ {
+ /* put shorter key in a */
+ a_length= b_length;
+ a= b;
+ swap= -1; /* swap sign of result */
+ }
+ for (end= a + a_length-length; a < end ; a++)
+ {
+ if (*a != ' ')
+ return (*a < ' ') ? -swap : swap;
+ }
+ return 0;
+ }
+ return (int) (a_length-b_length);
+}
+
+
+/*
+ Compare two keys
+
+ SYNOPSIS
+ ha_key_cmp()
+ keyseg Array of key segments of key to compare
+ a First key to compare, in format from _mi_pack_key()
+ This is normally key specified by user
+ b Second key to compare. This is always from a row
+ key_length Length of key to compare. This can be shorter than
+ a to just compare sub keys
+ next_flag How keys should be compared
+ If bit SEARCH_FIND is not set the keys includes the row
+ position and this should also be compared
+ diff_pos OUT Number of first keypart where values differ, counting
+ from one.
+ diff_pos[1] OUT (b + diff_pos[1]) points to first value in tuple b
+ that is different from corresponding value in tuple a.
+
+ EXAMPLES
+ Example1: if the function is called for tuples
+ ('aaa','bbb') and ('eee','fff'), then
+ diff_pos[0] = 1 (as 'aaa' != 'eee')
+ diff_pos[1] = 0 (offset from beggining of tuple b to 'eee' keypart).
+
+ Example2: if the index function is called for tuples
+ ('aaa','bbb') and ('aaa','fff'),
+ diff_pos[0] = 2 (as 'aaa' != 'eee')
+ diff_pos[1] = 3 (offset from beggining of tuple b to 'fff' keypart,
+ here we assume that first key part is CHAR(3) NOT NULL)
+
+ NOTES
+ Number-keys can't be splited
+
+ RETURN VALUES
+ <0 If a < b
+ 0 If a == b
+ >0 If a > b
+*/
+
+#define FCMP(A,B) ((int) (A) - (int) (B))
+
+int ha_key_cmp(register HA_KEYSEG *keyseg, register uchar *a,
+ register uchar *b, uint key_length, uint nextflag,
+ uint *diff_pos)
+{
+ int flag;
+ int16 s_1,s_2;
+ int32 l_1,l_2;
+ uint32 u_1,u_2;
+ float f_1,f_2;
+ double d_1,d_2;
+ uint next_key_length;
+ uchar *orig_b= b;
+
+ *diff_pos=0;
+ for ( ; (int) key_length >0 ; key_length=next_key_length, keyseg++)
+ {
+ uchar *end;
+ uint piks=! (keyseg->flag & HA_NO_SORT);
+ (*diff_pos)++;
+ diff_pos[1]= (uint)(b - orig_b);
+
+ /* Handle NULL part */
+ if (keyseg->null_bit)
+ {
+ key_length--;
+ if (*a != *b && piks)
+ {
+ flag = (int) *a - (int) *b;
+ return ((keyseg->flag & HA_REVERSE_SORT) ? -flag : flag);
+ }
+ b++;
+ if (!*a++) /* If key was NULL */
+ {
+ if (nextflag == (SEARCH_FIND | SEARCH_UPDATE))
+ nextflag=SEARCH_SAME; /* Allow duplicate keys */
+ else if (nextflag & SEARCH_NULL_ARE_NOT_EQUAL)
+ {
+ /*
+ This is only used from mi_check() to calculate cardinality.
+ It can't be used when searching for a key as this would cause
+ compare of (a,b) and (b,a) to return the same value.
+ */
+ return -1;
+ }
+ next_key_length=key_length;
+ continue; /* To next key part */
+ }
+ }
+ end= a+ min(keyseg->length,key_length);
+ next_key_length=key_length-keyseg->length;
+
+ switch ((enum ha_base_keytype) keyseg->type) {
+ case HA_KEYTYPE_TEXT: /* Ascii; Key is converted */
+ if (keyseg->flag & HA_SPACE_PACK)
+ {
+ int a_length,b_length,pack_length;
+ get_key_length(a_length,a);
+ get_key_pack_length(b_length,pack_length,b);
+ next_key_length=key_length-b_length-pack_length;
+
+ if (piks &&
+ (flag=mi_compare_text(keyseg->charset,a,a_length,b,b_length,
+ (my_bool) ((nextflag & SEARCH_PREFIX) &&
+ next_key_length <= 0),
+ (my_bool)!(nextflag & SEARCH_PREFIX))))
+ return ((keyseg->flag & HA_REVERSE_SORT) ? -flag : flag);
+ a+=a_length;
+ b+=b_length;
+ break;
+ }
+ else
+ {
+ uint length=(uint) (end-a), a_length=length, b_length=length;
+ if (piks &&
+ (flag= mi_compare_text(keyseg->charset, a, a_length, b, b_length,
+ (my_bool) ((nextflag & SEARCH_PREFIX) &&
+ next_key_length <= 0),
+ (my_bool)!(nextflag & SEARCH_PREFIX))))
+ return ((keyseg->flag & HA_REVERSE_SORT) ? -flag : flag);
+ a=end;
+ b+=length;
+ }
+ break;
+ case HA_KEYTYPE_BINARY:
+ if (keyseg->flag & HA_SPACE_PACK)
+ {
+ int a_length,b_length,pack_length;
+ get_key_length(a_length,a);
+ get_key_pack_length(b_length,pack_length,b);
+ next_key_length=key_length-b_length-pack_length;
+
+ if (piks &&
+ (flag=compare_bin(a,a_length,b,b_length,
+ (my_bool) ((nextflag & SEARCH_PREFIX) &&
+ next_key_length <= 0),1)))
+ return ((keyseg->flag & HA_REVERSE_SORT) ? -flag : flag);
+ a+=a_length;
+ b+=b_length;
+ break;
+ }
+ else
+ {
+ uint length=keyseg->length;
+ if (piks &&
+ (flag=compare_bin(a,length,b,length,
+ (my_bool) ((nextflag & SEARCH_PREFIX) &&
+ next_key_length <= 0),0)))
+ return ((keyseg->flag & HA_REVERSE_SORT) ? -flag : flag);
+ a+=length;
+ b+=length;
+ }
+ break;
+ case HA_KEYTYPE_VARTEXT:
+ {
+ int a_length,full_a_length,b_length,full_b_length,pack_length;
+ get_key_length(a_length,a);
+ get_key_pack_length(b_length,pack_length,b);
+ full_a_length= a_length;
+ full_b_length= b_length;
+ next_key_length=key_length-b_length-pack_length;
+
+ if (piks &&
+ (flag= mi_compare_text(keyseg->charset,a,a_length,b,b_length,
+ (my_bool) ((nextflag & SEARCH_PREFIX) &&
+ next_key_length <= 0),
+ (my_bool) ((nextflag & (SEARCH_FIND |
+ SEARCH_UPDATE)) ==
+ SEARCH_FIND))))
+ return ((keyseg->flag & HA_REVERSE_SORT) ? -flag : flag);
+ a+= full_a_length;
+ b+= full_b_length;
+ break;
+ }
+ break;
+ case HA_KEYTYPE_VARBINARY:
+ {
+ int a_length,b_length,pack_length;
+ get_key_length(a_length,a);
+ get_key_pack_length(b_length,pack_length,b);
+ next_key_length=key_length-b_length-pack_length;
+
+ if (piks &&
+ (flag=compare_bin(a,a_length,b,b_length,
+ (my_bool) ((nextflag & SEARCH_PREFIX) &&
+ next_key_length <= 0), 0)))
+ return ((keyseg->flag & HA_REVERSE_SORT) ? -flag : flag);
+ a+=a_length;
+ b+=b_length;
+ break;
+ }
+ break;
+ case HA_KEYTYPE_INT8:
+ {
+ int i_1= (int) *((signed char*) a);
+ int i_2= (int) *((signed char*) b);
+ if (piks && (flag = CMP_NUM(i_1,i_2)))
+ return ((keyseg->flag & HA_REVERSE_SORT) ? -flag : flag);
+ a= end;
+ b++;
+ break;
+ }
+ case HA_KEYTYPE_SHORT_INT:
+ s_1= mi_sint2korr(a);
+ s_2= mi_sint2korr(b);
+ if (piks && (flag = CMP_NUM(s_1,s_2)))
+ return ((keyseg->flag & HA_REVERSE_SORT) ? -flag : flag);
+ a= end;
+ b+= 2; /* sizeof(short int); */
+ break;
+ case HA_KEYTYPE_USHORT_INT:
+ {
+ uint16 us_1,us_2;
+ us_1= mi_sint2korr(a);
+ us_2= mi_sint2korr(b);
+ if (piks && (flag = CMP_NUM(us_1,us_2)))
+ return ((keyseg->flag & HA_REVERSE_SORT) ? -flag : flag);
+ a= end;
+ b+=2; /* sizeof(short int); */
+ break;
+ }
+ case HA_KEYTYPE_LONG_INT:
+ l_1= mi_sint4korr(a);
+ l_2= mi_sint4korr(b);
+ if (piks && (flag = CMP_NUM(l_1,l_2)))
+ return ((keyseg->flag & HA_REVERSE_SORT) ? -flag : flag);
+ a= end;
+ b+= 4; /* sizeof(long int); */
+ break;
+ case HA_KEYTYPE_ULONG_INT:
+ u_1= mi_sint4korr(a);
+ u_2= mi_sint4korr(b);
+ if (piks && (flag = CMP_NUM(u_1,u_2)))
+ return ((keyseg->flag & HA_REVERSE_SORT) ? -flag : flag);
+ a= end;
+ b+= 4; /* sizeof(long int); */
+ break;
+ case HA_KEYTYPE_INT24:
+ l_1=mi_sint3korr(a);
+ l_2=mi_sint3korr(b);
+ if (piks && (flag = CMP_NUM(l_1,l_2)))
+ return ((keyseg->flag & HA_REVERSE_SORT) ? -flag : flag);
+ a= end;
+ b+= 3;
+ break;
+ case HA_KEYTYPE_UINT24:
+ l_1=mi_uint3korr(a);
+ l_2=mi_uint3korr(b);
+ if (piks && (flag = CMP_NUM(l_1,l_2)))
+ return ((keyseg->flag & HA_REVERSE_SORT) ? -flag : flag);
+ a= end;
+ b+= 3;
+ break;
+ case HA_KEYTYPE_FLOAT:
+ mi_float4get(f_1,a);
+ mi_float4get(f_2,b);
+ /*
+ The following may give a compiler warning about floating point
+ comparison not being safe, but this is ok in this context as
+ we are bascily doing sorting
+ */
+ if (piks && (flag = CMP_NUM(f_1,f_2)))
+ return ((keyseg->flag & HA_REVERSE_SORT) ? -flag : flag);
+ a= end;
+ b+= 4; /* sizeof(float); */
+ break;
+ case HA_KEYTYPE_DOUBLE:
+ mi_float8get(d_1,a);
+ mi_float8get(d_2,b);
+ /*
+ The following may give a compiler warning about floating point
+ comparison not being safe, but this is ok in this context as
+ we are bascily doing sorting
+ */
+ if (piks && (flag = CMP_NUM(d_1,d_2)))
+ return ((keyseg->flag & HA_REVERSE_SORT) ? -flag : flag);
+ a= end;
+ b+= 8; /* sizeof(double); */
+ break;
+ case HA_KEYTYPE_NUM: /* Numeric key */
+ {
+ int swap_flag= 0;
+ int alength,blength;
+
+ if (keyseg->flag & HA_REVERSE_SORT)
+ {
+ swap_variables(uchar*, a, b);
+ swap_flag=1; /* Remember swap of a & b */
+ end= a+ (int) (end-b);
+ }
+ if (keyseg->flag & HA_SPACE_PACK)
+ {
+ alength= *a++; blength= *b++;
+ end=a+alength;
+ next_key_length=key_length-blength-1;
+ }
+ else
+ {
+ alength= (int) (end-a);
+ blength=keyseg->length;
+ /* remove pre space from keys */
+ for ( ; alength && *a == ' ' ; a++, alength--) ;
+ for ( ; blength && *b == ' ' ; b++, blength--) ;
+ }
+ if (piks)
+ {
+ if (*a == '-')
+ {
+ if (*b != '-')
+ return -1;
+ a++; b++;
+ swap_variables(uchar*, a, b);
+ swap_variables(int, alength, blength);
+ swap_flag=1-swap_flag;
+ alength--; blength--;
+ end=a+alength;
+ }
+ else if (*b == '-')
+ return 1;
+ while (alength && (*a == '+' || *a == '0'))
+ {
+ a++; alength--;
+ }
+ while (blength && (*b == '+' || *b == '0'))
+ {
+ b++; blength--;
+ }
+ if (alength != blength)
+ return (alength < blength) ? -1 : 1;
+ while (a < end)
+ if (*a++ != *b++)
+ return ((int) a[-1] - (int) b[-1]);
+ }
+ else
+ {
+ b+=(end-a);
+ a=end;
+ }
+
+ if (swap_flag) /* Restore pointers */
+ swap_variables(uchar*, a, b);
+ break;
+ }
+#ifdef HAVE_LONG_LONG
+ case HA_KEYTYPE_LONGLONG:
+ {
+ longlong ll_a,ll_b;
+ ll_a= mi_sint8korr(a);
+ ll_b= mi_sint8korr(b);
+ if (piks && (flag = CMP_NUM(ll_a,ll_b)))
+ return ((keyseg->flag & HA_REVERSE_SORT) ? -flag : flag);
+ a= end;
+ b+= 8;
+ break;
+ }
+ case HA_KEYTYPE_ULONGLONG:
+ {
+ ulonglong ll_a,ll_b;
+ ll_a= mi_uint8korr(a);
+ ll_b= mi_uint8korr(b);
+ if (piks && (flag = CMP_NUM(ll_a,ll_b)))
+ return ((keyseg->flag & HA_REVERSE_SORT) ? -flag : flag);
+ a= end;
+ b+= 8;
+ break;
+ }
+#endif
+ case HA_KEYTYPE_END: /* Ready */
+ goto end; /* diff_pos is incremented */
+ }
+ }
+ (*diff_pos)++;
+end:
+ if (!(nextflag & SEARCH_FIND))
+ {
+ uint i;
+ if (nextflag & (SEARCH_NO_FIND | SEARCH_LAST)) /* Find record after key */
+ return (nextflag & (SEARCH_BIGGER | SEARCH_LAST)) ? -1 : 1;
+ flag=0;
+ for (i=keyseg->length ; i-- > 0 ; )
+ {
+ if (*a++ != *b++)
+ {
+ flag= FCMP(a[-1],b[-1]);
+ break;
+ }
+ }
+ if (nextflag & SEARCH_SAME)
+ return (flag); /* read same */
+ if (nextflag & SEARCH_BIGGER)
+ return (flag <= 0 ? -1 : 1); /* read next */
+ return (flag < 0 ? -1 : 1); /* read previous */
+ }
+ return 0;
+} /* ha_key_cmp */
+
+
+/*
+ Find the first NULL value in index-suffix values tuple
+
+ SYNOPSIS
+ ha_find_null()
+ keyseg Array of keyparts for key suffix
+ a Key suffix value tuple
+
+ DESCRIPTION
+ Find the first NULL value in index-suffix values tuple.
+ TODO Consider optimizing this fuction or its use so we don't search for
+ NULL values in completely NOT NULL index suffixes.
+
+ RETURN
+ First key part that has NULL as value in values tuple, or the last key part
+ (with keyseg->type==HA_TYPE_END) if values tuple doesn't contain NULLs.
+*/
+
+HA_KEYSEG *ha_find_null(HA_KEYSEG *keyseg, uchar *a)
+{
+ for (; (enum ha_base_keytype) keyseg->type != HA_KEYTYPE_END; keyseg++)
+ {
+ uchar *end;
+ if (keyseg->null_bit)
+ {
+ if (!*a++)
+ return keyseg;
+ }
+ end= a+ keyseg->length;
+
+ switch ((enum ha_base_keytype) keyseg->type) {
+ case HA_KEYTYPE_TEXT:
+ case HA_KEYTYPE_BINARY:
+ if (keyseg->flag & HA_SPACE_PACK)
+ {
+ int a_length;
+ get_key_length(a_length, a);
+ a += a_length;
+ break;
+ }
+ else
+ a= end;
+ break;
+ case HA_KEYTYPE_VARTEXT:
+ case HA_KEYTYPE_VARBINARY:
+ {
+ int a_length;
+ get_key_length(a_length, a);
+ a+= a_length;
+ break;
+ }
+ case HA_KEYTYPE_NUM:
+ if (keyseg->flag & HA_SPACE_PACK)
+ {
+ int alength= *a++;
+ end= a+alength;
+ }
+ a= end;
+ break;
+ case HA_KEYTYPE_INT8:
+ case HA_KEYTYPE_SHORT_INT:
+ case HA_KEYTYPE_USHORT_INT:
+ case HA_KEYTYPE_LONG_INT:
+ case HA_KEYTYPE_ULONG_INT:
+ case HA_KEYTYPE_INT24:
+ case HA_KEYTYPE_UINT24:
+#ifdef HAVE_LONG_LONG
+ case HA_KEYTYPE_LONGLONG:
+ case HA_KEYTYPE_ULONGLONG:
+#endif
+ case HA_KEYTYPE_FLOAT:
+ case HA_KEYTYPE_DOUBLE:
+ a= end;
+ break;
+ }
+ }
+ return keyseg;
+}
+
diff --git a/mysys/my_init.c b/mysys/my_init.c
index dd06ae41dcb..d68c76bef1b 100644
--- a/mysys/my_init.c
+++ b/mysys/my_init.c
@@ -17,13 +17,8 @@
#include "mysys_priv.h"
#include "my_static.h"
#include "mysys_err.h"
-#include "m_ctype.h"
#include <m_string.h>
#include <m_ctype.h>
-#ifdef HAVE_GETRUSAGE
-#include <sys/resource.h>
-/* extern int getrusage(int, struct rusage *); */
-#endif
#include <signal.h>
#ifdef VMS
#include <my_static.c>
@@ -47,16 +42,16 @@ static void netware_init();
#define netware_init()
#endif
-
-my_bool my_init_done=0;
+my_bool my_init_done= 0;
+uint mysys_usage_id= 0; /* Incremented for each my_init() */
static ulong atoi_octal(const char *str)
{
long int tmp;
- while (*str && isspace(*str))
+ while (*str && my_isspace(&my_charset_latin1, *str))
str++;
str2int(str,
- (*str == '0' ? 8 : 10), /* Octalt or decimalt */
+ (*str == '0' ? 8 : 10), /* Octalt or decimalt */
0, INT_MAX, &tmp);
return (ulong) tmp;
}
@@ -79,6 +74,9 @@ my_bool my_init(void)
if (my_init_done)
return 0;
my_init_done=1;
+ mysys_usage_id++;
+ my_umask= 0660; /* Default umask for new files */
+ my_umask_dir= 0700; /* Default umask for new directories */
#if defined(THREAD) && defined(SAFE_MUTEX)
safe_mutex_global_init(); /* Must be called early */
#endif
@@ -129,10 +127,23 @@ my_bool my_init(void)
void my_end(int infoflag)
{
- FILE *info_file;
- if (!(info_file=DBUG_FILE))
- info_file=stderr;
- if (infoflag & MY_CHECK_ERROR || info_file != stderr)
+ /*
+ this code is suboptimal to workaround a bug in
+ Sun CC: Sun C++ 5.6 2004/06/02 for x86, and should not be
+ optimized until this compiler is not in use anymore
+ */
+ FILE *info_file= DBUG_FILE;
+ my_bool print_info= (info_file != stderr);
+ DBUG_ENTER("my_end");
+ if (!info_file)
+ {
+ info_file= stderr;
+ print_info= 0;
+ }
+
+ DBUG_PRINT("info",("Shutting down: print_info: %d", print_info));
+ if ((infoflag & MY_CHECK_ERROR) || print_info)
+
{ /* Test if some file is left open */
if (my_file_opened | my_stream_opened)
{
@@ -142,10 +153,16 @@ void my_end(int infoflag)
}
}
free_charsets();
- if (infoflag & MY_GIVE_INFO || info_file != stderr)
+ my_once_free();
+
+ if ((infoflag & MY_GIVE_INFO) || print_info)
{
#ifdef HAVE_GETRUSAGE
struct rusage rus;
+#ifdef HAVE_purify
+ /* Purify assumes that rus is uninitialized after getrusage call */
+ bzero((char*) &rus, sizeof(rus));
+#endif
if (!getrusage(RUSAGE_SELF, &rus))
fprintf(info_file,"\n\
User time %.2f, System time %.2f\n\
@@ -181,7 +198,6 @@ Voluntary context switches %ld, Involuntary context switches %ld\n",
}
#ifdef THREAD
DBUG_POP(); /* Must be done before my_thread_end */
- my_once_free();
my_thread_end();
my_thread_global_end();
#if defined(SAFE_MUTEX)
@@ -244,8 +260,13 @@ static void my_win_init(void)
setlocale(LC_CTYPE, ""); /* To get right sortorder */
- /* Clear the OS system variable TZ and avoid the 100% CPU usage */
+#if defined(_MSC_VER) && (_MSC_VER < 1300)
+ /*
+ Clear the OS system variable TZ and avoid the 100% CPU usage
+ Only for old versions of Visual C++
+ */
_putenv( "TZ=" );
+#endif
_tzset();
/* apre la chiave HKEY_LOCAL_MACHINES\software\MySQL */
diff --git a/mysys/my_lib.c b/mysys/my_lib.c
index 055e00d2efc..1908c70f407 100644
--- a/mysys/my_lib.c
+++ b/mysys/my_lib.c
@@ -427,6 +427,18 @@ MY_DIR *my_dir(const char *path, myf MyFlags)
do
{
#ifdef __BORLANDC__
+ attrib= find.ff_attrib;
+#else
+ attrib= find.attrib;
+ /*
+ Do not show hidden and system files which Windows sometimes create.
+ Note. Because Borland's findfirst() is called with the third
+ argument = 0 hidden/system files are excluded from the search.
+ */
+ if (attrib & (_A_HIDDEN | _A_SYSTEM))
+ continue;
+#endif
+#ifdef __BORLANDC__
if (!(finfo.name= strdup_root(names_storage, find.ff_name)))
goto error;
#else
@@ -442,11 +454,10 @@ MY_DIR *my_dir(const char *path, myf MyFlags)
bzero(finfo.mystat, sizeof(MY_STAT));
#ifdef __BORLANDC__
finfo.mystat->st_size=find.ff_fsize;
- mode=MY_S_IREAD; attrib=find.ff_attrib;
#else
finfo.mystat->st_size=find.size;
- mode=MY_S_IREAD; attrib=find.attrib;
#endif
+ mode=MY_S_IREAD;
if (!(attrib & _A_RDONLY))
mode|=MY_S_IWRITE;
if (attrib & _A_SUBDIR)
@@ -613,7 +624,7 @@ MY_STAT *my_stat(const char *path, MY_STAT *stat_area, myf my_flags)
{
int m_used;
DBUG_ENTER("my_stat");
- DBUG_PRINT("my", ("path: '%s', stat_area: %lx, MyFlags: %d", path,
+ DBUG_PRINT("my", ("path: '%s', stat_area: 0x%lx, MyFlags: %d", path,
(byte *) stat_area, my_flags));
if ((m_used= (stat_area == NULL)))
diff --git a/mysys/my_lock.c b/mysys/my_lock.c
index add274bdcec..b8307f366c0 100644
--- a/mysys/my_lock.c
+++ b/mysys/my_lock.c
@@ -35,7 +35,14 @@
#include <nks/fsio.h>
#endif
- /* Lock a part of a file */
+/*
+ Lock a part of a file
+
+ RETURN VALUE
+ 0 Success
+ -1 An error has occured and 'my_errno' is set
+ to indicate the actual error code.
+*/
int my_lock(File fd, int locktype, my_off_t start, my_off_t length,
myf MyFlags)
@@ -104,10 +111,22 @@ int my_lock(File fd, int locktype, my_off_t start, my_off_t length,
#elif defined(HAVE_LOCKING)
/* Windows */
{
- my_bool error;
+ my_bool error= FALSE;
pthread_mutex_lock(&my_file_info[fd].mutex);
- if (MyFlags & MY_SEEK_NOT_DONE)
- VOID(my_seek(fd,start,MY_SEEK_SET,MYF(MyFlags & ~MY_SEEK_NOT_DONE)));
+ if (MyFlags & MY_SEEK_NOT_DONE)
+ {
+ if( my_seek(fd,start,MY_SEEK_SET,MYF(MyFlags & ~MY_SEEK_NOT_DONE))
+ == MY_FILEPOS_ERROR )
+ {
+ /*
+ If my_seek fails my_errno will already contain an error code;
+ just unlock and return error code.
+ */
+ DBUG_PRINT("error",("my_errno: %d (%d)",my_errno,errno));
+ pthread_mutex_unlock(&my_file_info[fd].mutex);
+ DBUG_RETURN(-1);
+ }
+ }
error= locking(fd,locktype,(ulong) length) && errno != EINVAL;
pthread_mutex_unlock(&my_file_info[fd].mutex);
if (!error)
@@ -117,10 +136,12 @@ int my_lock(File fd, int locktype, my_off_t start, my_off_t length,
#if defined(HAVE_FCNTL)
{
struct flock lock;
+
lock.l_type= (short) locktype;
lock.l_whence= SEEK_SET;
lock.l_start= (off_t) start;
lock.l_len= (off_t) length;
+
if (MyFlags & MY_DONT_WAIT)
{
if (fcntl(fd,F_SETLK,&lock) != -1) /* Check if we can lock */
@@ -143,7 +164,17 @@ int my_lock(File fd, int locktype, my_off_t start, my_off_t length,
}
#else
if (MyFlags & MY_SEEK_NOT_DONE)
- VOID(my_seek(fd,start,MY_SEEK_SET,MYF(MyFlags & ~MY_SEEK_NOT_DONE)));
+ {
+ if (my_seek(fd,start,MY_SEEK_SET,MYF(MyFlags & ~MY_SEEK_NOT_DONE))
+ == MY_FILEPOS_ERROR)
+ {
+ /*
+ If an error has occured in my_seek then we will already
+ have an error code in my_errno; Just return error code.
+ */
+ DBUG_RETURN(-1);
+ }
+ }
if (lockf(fd,locktype,length) != -1)
DBUG_RETURN(0);
#endif /* HAVE_FCNTL */
diff --git a/mysys/my_lwrite.c b/mysys/my_lwrite.c
index cfdbd5d4576..02c56a69ba4 100644
--- a/mysys/my_lwrite.c
+++ b/mysys/my_lwrite.c
@@ -23,7 +23,7 @@ uint32 my_lwrite(int Filedes, const byte *Buffer, uint32 Count, myf MyFlags)
{
uint32 writenbytes;
DBUG_ENTER("my_lwrite");
- DBUG_PRINT("my",("Fd: %d Buffer: %lx Count: %ld MyFlags: %d",
+ DBUG_PRINT("my",("Fd: %d Buffer: 0x%lx Count: %ld MyFlags: %d",
Filedes, Buffer, Count, MyFlags));
DBUG_PRINT("error", ("Deprecated my_lwrite() function should not be used."));
diff --git a/mysys/my_malloc.c b/mysys/my_malloc.c
index b273363aaf1..3f601a42dc9 100644
--- a/mysys/my_malloc.c
+++ b/mysys/my_malloc.c
@@ -24,27 +24,27 @@
/* My memory allocator */
-gptr my_malloc(unsigned int Size, myf MyFlags)
+gptr my_malloc(unsigned int size, myf my_flags)
{
gptr point;
DBUG_ENTER("my_malloc");
- DBUG_PRINT("my",("Size: %u MyFlags: %d",Size, MyFlags));
+ DBUG_PRINT("my",("size: %u my_flags: %d",size, my_flags));
- if (!Size)
- Size=1; /* Safety */
- if ((point = (char*)malloc(Size)) == NULL)
+ if (!size)
+ size=1; /* Safety */
+ if ((point = (char*)malloc(size)) == NULL)
{
my_errno=errno;
- if (MyFlags & MY_FAE)
+ if (my_flags & MY_FAE)
error_handler_hook=fatal_error_handler_hook;
- if (MyFlags & (MY_FAE+MY_WME))
- my_error(EE_OUTOFMEMORY, MYF(ME_BELL+ME_WAITTANG),Size);
- if (MyFlags & MY_FAE)
+ if (my_flags & (MY_FAE+MY_WME))
+ my_error(EE_OUTOFMEMORY, MYF(ME_BELL+ME_WAITTANG),size);
+ if (my_flags & MY_FAE)
exit(1);
}
- else if (MyFlags & MY_ZEROFILL)
- bzero(point,Size);
- DBUG_PRINT("exit",("ptr: %lx",point));
+ else if (my_flags & MY_ZEROFILL)
+ bzero(point,size);
+ DBUG_PRINT("exit",("ptr: 0x%lx",point));
DBUG_RETURN(point);
} /* my_malloc */
@@ -55,7 +55,7 @@ gptr my_malloc(unsigned int Size, myf MyFlags)
void my_no_flags_free(gptr ptr)
{
DBUG_ENTER("my_free");
- DBUG_PRINT("my",("ptr: %lx",ptr));
+ DBUG_PRINT("my",("ptr: 0x%lx",ptr));
if (ptr)
free(ptr);
DBUG_VOID_RETURN;
@@ -64,29 +64,29 @@ void my_no_flags_free(gptr ptr)
/* malloc and copy */
-gptr my_memdup(const byte *from, uint length, myf MyFlags)
+gptr my_memdup(const byte *from, uint length, myf my_flags)
{
gptr ptr;
- if ((ptr=my_malloc(length,MyFlags)) != 0)
+ if ((ptr=my_malloc(length,my_flags)) != 0)
memcpy((byte*) ptr, (byte*) from,(size_t) length);
return(ptr);
}
-char *my_strdup(const char *from, myf MyFlags)
+char *my_strdup(const char *from, myf my_flags)
{
gptr ptr;
uint length=(uint) strlen(from)+1;
- if ((ptr=my_malloc(length,MyFlags)) != 0)
+ if ((ptr=my_malloc(length,my_flags)) != 0)
memcpy((byte*) ptr, (byte*) from,(size_t) length);
return((my_string) ptr);
}
-char *my_strdup_with_length(const byte *from, uint length, myf MyFlags)
+char *my_strdup_with_length(const byte *from, uint length, myf my_flags)
{
gptr ptr;
- if ((ptr=my_malloc(length+1,MyFlags)) != 0)
+ if ((ptr=my_malloc(length+1,my_flags)) != 0)
{
memcpy((byte*) ptr, (byte*) from,(size_t) length);
((char*) ptr)[length]=0;
diff --git a/mysys/my_new.cc b/mysys/my_new.cc
index 5f2da90bbd1..14423c3afd5 100644
--- a/mysys/my_new.cc
+++ b/mysys/my_new.cc
@@ -21,7 +21,7 @@
#include "mysys_priv.h"
-#ifdef USE_MYSYS_NEW
+#ifdef USE_MYSYS_NEW
void *operator new (size_t sz)
{
diff --git a/mysys/my_once.c b/mysys/my_once.c
index 1250ce24994..a4201810b03 100644
--- a/mysys/my_once.c
+++ b/mysys/my_once.c
@@ -23,6 +23,7 @@
#include "mysys_priv.h"
#include "my_static.h"
#include "mysys_err.h"
+#include <m_string.h>
/*
Alloc for things we don't nead to free
@@ -78,6 +79,25 @@ gptr my_once_alloc(unsigned int Size, myf MyFlags)
} /* my_once_alloc */
+char *my_once_strdup(const char *src,myf myflags)
+{
+ uint len=strlen(src)+1;
+ char *dst=my_once_alloc(len, myflags);
+ if (dst)
+ memcpy(dst, src, len);
+ return dst;
+}
+
+
+char *my_once_memdup(const char *src, uint len, myf myflags)
+{
+ char *dst=my_once_alloc(len, myflags);
+ if (dst)
+ memcpy(dst, src, len);
+ return dst;
+}
+
+
/*
Deallocate everything used by my_once_alloc
diff --git a/mysys/my_open.c b/mysys/my_open.c
index 97f21724e1c..7cf40b57403 100644
--- a/mysys/my_open.c
+++ b/mysys/my_open.c
@@ -46,6 +46,16 @@ File my_open(const char *FileName, int Flags, myf MyFlags)
DBUG_PRINT("my",("Name: '%s' Flags: %d MyFlags: %d",
FileName, Flags, MyFlags));
#if defined(MSDOS) || defined(__WIN__) || defined(__EMX__) || defined(OS2)
+ /*
+ Check that we don't try to open or create a file name that may
+ cause problems for us in the future (like PRN)
+ */
+ if (check_if_legal_filename(FileName))
+ {
+ errno= EACCES;
+ DBUG_RETURN(my_register_filename(-1, FileName, FILE_BY_OPEN,
+ EE_FILENOTFOUND, MyFlags));
+ }
if (Flags & O_SHARE)
fd = sopen((my_string) FileName, (Flags & ~O_SHARE) | O_BINARY, SH_DENYNO,
MY_S_IREAD | MY_S_IWRITE);
@@ -86,7 +96,7 @@ int my_close(File fd, myf MyFlags)
if (MyFlags & (MY_FAE | MY_WME))
my_error(EE_BADCLOSE, MYF(ME_BELL+ME_WAITTANG),my_filename(fd),errno);
}
- if ((uint) fd < MY_NFILE && my_file_info[fd].type != UNOPEN)
+ if ((uint) fd < my_file_limit && my_file_info[fd].type != UNOPEN)
{
my_free(my_file_info[fd].name, MYF(0));
#if defined(THREAD) && !defined(HAVE_PREAD)
@@ -115,7 +125,7 @@ File my_register_filename(File fd, const char *FileName, enum file_type
{
if ((int) fd >= 0)
{
- if ((int) fd >= MY_NFILE)
+ if ((uint) fd >= my_file_limit)
{
#if defined(THREAD) && !defined(HAVE_PREAD)
(void) my_close(fd,MyFlags);
diff --git a/mysys/my_pread.c b/mysys/my_pread.c
index 70990ad12a6..f378d548731 100644
--- a/mysys/my_pread.c
+++ b/mysys/my_pread.c
@@ -29,7 +29,7 @@ uint my_pread(File Filedes, byte *Buffer, uint Count, my_off_t offset,
uint readbytes;
int error;
DBUG_ENTER("my_pread");
- DBUG_PRINT("my",("Fd: %d Seek: %lu Buffer: %lx Count: %u MyFlags: %d",
+ DBUG_PRINT("my",("Fd: %d Seek: %lu Buffer: 0x%lx Count: %u MyFlags: %d",
Filedes, (ulong) offset, Buffer, Count, MyFlags));
for (;;)
@@ -86,7 +86,7 @@ uint my_pwrite(int Filedes, const byte *Buffer, uint Count, my_off_t offset,
uint writenbytes,errors;
ulong written;
DBUG_ENTER("my_pwrite");
- DBUG_PRINT("my",("Fd: %d Seek: %lu Buffer: %lx Count: %d MyFlags: %d",
+ DBUG_PRINT("my",("Fd: %d Seek: %lu Buffer: 0x%lx Count: %d MyFlags: %d",
Filedes, (ulong) offset,Buffer, Count, MyFlags));
errors=0; written=0L;
diff --git a/mysys/my_pthread.c b/mysys/my_pthread.c
index fd716448e43..50c0a82d2d8 100644
--- a/mysys/my_pthread.c
+++ b/mysys/my_pthread.c
@@ -23,7 +23,6 @@
#include <signal.h>
#include <m_string.h>
#include <thr_alarm.h>
-#include <assert.h>
#if (defined(__BSD__) || defined(_BSDI_VERSION)) && !defined(HAVE_mit_thread)
#define SCHED_POLICY SCHED_RR
@@ -31,8 +30,7 @@
#define SCHED_POLICY SCHED_OTHER
#endif
-uint thd_lib_detected;
-uint thr_client_alarm;
+uint thd_lib_detected= 0;
#ifndef my_pthread_setprio
void my_pthread_setprio(pthread_t thread_id,int prior)
@@ -101,25 +99,23 @@ void *my_pthread_getspecific_imp(pthread_key_t key)
#undef pthread_exit
void my_pthread_exit(void *status)
{
- NXThreadId_t tid = NXThreadGetId();
+ NXThreadId_t tid;
NXContext_t ctx;
- char name[PATH_MAX] = "";
-
- /* Do not call pthread_exit if it is not a LibC thread */
- if (tid != 0)
- {
- NXThreadGetContext(tid, &ctx);
- NXContextGetName(ctx, name, PATH_MAX);
-
- /*
- "MYSQLD.NLM's LibC Reaper" or "MYSQLD.NLM's main thread"
- with a debug build of LibC the reaper can have different names
- */
- if (!strindex(name, "\'s"))
- {
- pthread_exit(status);
- }
- }
+ char name[NX_MAX_OBJECT_NAME_LEN+1] = "";
+
+ tid= NXThreadGetId();
+ if (tid == NX_INVALID_THREAD_ID || !tid)
+ return;
+ if (NXThreadGetContext(tid, &ctx) ||
+ NXContextGetName(ctx, name, sizeof(name)-1))
+ return;
+
+ /*
+ "MYSQLD.NLM's LibC Reaper" or "MYSQLD.NLM's main thread"
+ with a debug build of LibC the reaper can have different names
+ */
+ if (!strindex(name, "\'s"))
+ pthread_exit(status);
}
#endif
@@ -144,10 +140,13 @@ int my_sigwait(const sigset_t *set,int *sig)
/* localtime_r for SCO 3.2V4.2 */
-#ifndef HAVE_LOCALTIME_R
+#if !defined(HAVE_LOCALTIME_R) || !defined(HAVE_GMTIME_R)
extern pthread_mutex_t LOCK_localtime_r;
+#endif
+
+#if !defined(HAVE_LOCALTIME_R)
struct tm *localtime_r(const time_t *clock, struct tm *res)
{
struct tm *tmp;
@@ -159,6 +158,22 @@ struct tm *localtime_r(const time_t *clock, struct tm *res)
}
#endif
+#if !defined(HAVE_GMTIME_R)
+/*
+ Reentrant version of standard gmtime() function.
+ Needed on some systems which don't implement it.
+*/
+
+struct tm *gmtime_r(const time_t *clock, struct tm *res)
+{
+ struct tm *tmp;
+ pthread_mutex_lock(&LOCK_localtime_r);
+ tmp= gmtime(clock);
+ *res= *tmp;
+ pthread_mutex_unlock(&LOCK_localtime_r);
+ return res;
+}
+#endif
/****************************************************************************
** Replacement of sigwait if the system doesn't have one (like BSDI 3.0)
@@ -303,8 +318,6 @@ void sigwait_handle_sig(int sig)
pthread_mutex_unlock(&LOCK_sigwait);
}
-extern pthread_t alarm_thread;
-
void *sigwait_thread(void *set_arg)
{
sigset_t *set=(sigset_t*) set_arg;
@@ -393,23 +406,6 @@ int sigwait(sigset_t *setp, int *sigp)
#endif /* DONT_USE_SIGSUSPEND */
#endif /* HAVE_SIGWAIT */
-/*****************************************************************************
-** Implement pthread_signal for systems that can't use signal() with threads
-** Currently this is only used with BSDI 3.0
-*****************************************************************************/
-
-#ifdef USE_PTHREAD_SIGNAL
-
-int pthread_signal(int sig, void (*func)())
-{
- struct sigaction sact;
- sact.sa_flags= 0;
- sact.sa_handler= func;
- sigemptyset(&sact.sa_mask);
- sigaction(sig, &sact, (struct sigaction*) 0);
- return 0;
-}
-#endif
/****************************************************************************
The following functions fixes that all pthread functions should work
diff --git a/mysys/my_read.c b/mysys/my_read.c
index ff47a7c67f9..8b88e483fef 100644
--- a/mysys/my_read.c
+++ b/mysys/my_read.c
@@ -38,7 +38,7 @@ uint my_read(File Filedes, byte *Buffer, uint Count, myf MyFlags)
{
uint readbytes, save_count;
DBUG_ENTER("my_read");
- DBUG_PRINT("my",("Fd: %d Buffer: %lx Count: %u MyFlags: %d",
+ DBUG_PRINT("my",("Fd: %d Buffer: 0x%lx Count: %u MyFlags: %d",
Filedes, Buffer, Count, MyFlags));
save_count= Count;
diff --git a/mysys/my_realloc.c b/mysys/my_realloc.c
index 49d96c2eb4f..c8edb172890 100644
--- a/mysys/my_realloc.c
+++ b/mysys/my_realloc.c
@@ -23,42 +23,43 @@
/* My memory re allocator */
-gptr my_realloc(gptr oldpoint, uint Size, myf MyFlags)
+gptr my_realloc(gptr oldpoint, uint size, myf my_flags)
{
gptr point;
DBUG_ENTER("my_realloc");
- DBUG_PRINT("my",("ptr: %lx Size: %u MyFlags: %d",oldpoint, Size, MyFlags));
+ DBUG_PRINT("my",("ptr: 0x%lx size: %u my_flags: %d",oldpoint, size,
+ my_flags));
- if (!oldpoint && (MyFlags & MY_ALLOW_ZERO_PTR))
- DBUG_RETURN(my_malloc(Size,MyFlags));
+ if (!oldpoint && (my_flags & MY_ALLOW_ZERO_PTR))
+ DBUG_RETURN(my_malloc(size,my_flags));
#ifdef USE_HALLOC
- if (!(point = malloc(Size)))
+ if (!(point = malloc(size)))
{
- if (MyFlags & MY_FREE_ON_ERROR)
- my_free(oldpoint,MyFlags);
- if (MyFlags & MY_HOLD_ON_ERROR)
+ if (my_flags & MY_FREE_ON_ERROR)
+ my_free(oldpoint,my_flags);
+ if (my_flags & MY_HOLD_ON_ERROR)
DBUG_RETURN(oldpoint);
my_errno=errno;
- if (MyFlags & MY_FAE+MY_WME)
- my_error(EE_OUTOFMEMORY, MYF(ME_BELL+ME_WAITTANG),Size);
+ if (my_flags & MY_FAE+MY_WME)
+ my_error(EE_OUTOFMEMORY, MYF(ME_BELL+ME_WAITTANG),size);
}
else
{
- memcpy(point,oldpoint,Size);
+ memcpy(point,oldpoint,size);
free(oldpoint);
}
#else
- if ((point = (char*)realloc(oldpoint,Size)) == NULL)
+ if ((point = (char*)realloc(oldpoint,size)) == NULL)
{
- if (MyFlags & MY_FREE_ON_ERROR)
+ if (my_flags & MY_FREE_ON_ERROR)
my_free(oldpoint,MyFLAGS);
- if (MyFlags & MY_HOLD_ON_ERROR)
+ if (my_flags & MY_HOLD_ON_ERROR)
DBUG_RETURN(oldpoint);
my_errno=errno;
- if (MyFlags & (MY_FAE+MY_WME))
- my_error(EE_OUTOFMEMORY, MYF(ME_BELL+ME_WAITTANG), Size);
+ if (my_flags & (MY_FAE+MY_WME))
+ my_error(EE_OUTOFMEMORY, MYF(ME_BELL+ME_WAITTANG), size);
}
#endif
- DBUG_PRINT("exit",("ptr: %lx",point));
+ DBUG_PRINT("exit",("ptr: 0x%lx",point));
DBUG_RETURN(point);
} /* my_realloc */
diff --git a/mysys/my_redel.c b/mysys/my_redel.c
index 9ba03cd9526..9af360424b0 100644
--- a/mysys/my_redel.c
+++ b/mysys/my_redel.c
@@ -19,10 +19,10 @@
#include <my_dir.h>
#include <m_string.h>
#include "mysys_err.h"
-#if defined(HAVE_SYS_UTIME_H)
-#include <sys/utime.h>
-#elif defined(HAVE_UTIME_H)
+#if defined(HAVE_UTIME_H)
#include <utime.h>
+#elif defined(HAVE_SYS_UTIME_H)
+#include <sys/utime.h>
#elif !defined(HPUX10)
struct utimbuf {
time_t actime;
diff --git a/mysys/my_seek.c b/mysys/my_seek.c
index ec24a26b3d9..805a5654ffc 100644
--- a/mysys/my_seek.c
+++ b/mysys/my_seek.c
@@ -15,10 +15,33 @@
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
#include "mysys_priv.h"
-#include <assert.h>
- /* Seek to position in file */
- /*ARGSUSED*/
+/*
+ Seek to a position in a file.
+
+ ARGUMENTS
+ File fd The file descriptor
+ my_off_t pos The expected position (absolute or relative)
+ int whence A direction parameter and one of
+ {SEEK_SET, SEEK_CUR, SEEK_END}
+ myf MyFlags MY_THREADSAFE must be set in case my_seek may be mixed
+ with my_pread/my_pwrite calls and fd is shared among
+ threads.
+
+ DESCRIPTION
+ The my_seek function is a wrapper around the system call lseek and
+ repositions the offset of the file descriptor fd to the argument
+ offset according to the directive whence as follows:
+ SEEK_SET The offset is set to offset bytes.
+ SEEK_CUR The offset is set to its current location plus offset bytes
+ SEEK_END The offset is set to the size of the file plus offset bytes
+
+ RETURN VALUE
+ my_off_t newpos The new position in the file.
+ MY_FILEPOS_ERROR An error was encountered while performing
+ the seek. my_errno is set to indicate the
+ actual error.
+*/
my_off_t my_seek(File fd, my_off_t pos, int whence,
myf MyFlags __attribute__((unused)))
@@ -30,7 +53,16 @@ my_off_t my_seek(File fd, my_off_t pos, int whence,
whence, MyFlags));
DBUG_ASSERT(pos != MY_FILEPOS_ERROR); /* safety check */
- newpos=lseek(fd, pos, whence);
+#if defined(THREAD) && !defined(HAVE_PREAD)
+ if (MyFlags & MY_THREADSAFE)
+ {
+ pthread_mutex_lock(&my_file_info[fd].mutex);
+ newpos= lseek(fd, pos, whence);
+ pthread_mutex_unlock(&my_file_info[fd].mutex);
+ }
+ else
+#endif
+ newpos= lseek(fd, pos, whence);
if (newpos == (os_off_t) -1)
{
my_errno=errno;
diff --git a/mysys/my_sleep.c b/mysys/my_sleep.c
index 3de2d2abd13..31eaf7eeb96 100644
--- a/mysys/my_sleep.c
+++ b/mysys/my_sleep.c
@@ -23,6 +23,8 @@ void my_sleep(ulong m_seconds)
{
#ifdef __NETWARE__
delay(m_seconds/1000+1);
+#elif defined(__WIN__)
+ Sleep(m_seconds/1000+1); /* Sleep() has millisecond arg */
#elif defined(OS2)
DosSleep(m_seconds/1000+1);
#elif defined(HAVE_SELECT)
diff --git a/mysys/my_static.c b/mysys/my_static.c
index b24ef28b7b1..5f034555156 100644
--- a/mysys/my_static.c
+++ b/mysys/my_static.c
@@ -34,7 +34,9 @@ int NEAR my_umask=0664, NEAR my_umask_dir=0777;
#ifndef THREAD
int NEAR my_errno=0;
#endif
-struct my_file_info my_file_info[MY_NFILE]= {{0,UNOPEN}};
+struct st_my_file_info my_file_info_default[MY_NFILE]= {{0,UNOPEN}};
+uint my_file_limit= MY_NFILE;
+struct st_my_file_info *my_file_info= my_file_info_default;
/* From mf_brkhant */
int NEAR my_dont_interrupt=0;
diff --git a/mysys/my_static.h b/mysys/my_static.h
index 1a33bcf21f3..51f9fbc922f 100644
--- a/mysys/my_static.h
+++ b/mysys/my_static.h
@@ -19,7 +19,7 @@
a shared library
*/
-#include "mysys_priv.h"
+C_MODE_START
#include <signal.h>
#define MAX_SIGNALS 10 /* Max signals under a dont-allow */
@@ -69,6 +69,9 @@ extern byte *sf_min_adress,*sf_max_adress;
extern uint sf_malloc_count;
extern struct st_irem *sf_malloc_root;
+extern struct st_my_file_info my_file_info_default[MY_NFILE];
+
#if defined(THREAD) && !defined(__WIN__)
extern sigset_t my_signals; /* signals blocked by mf_brkhant */
#endif
+C_MODE_END
diff --git a/mysys/my_symlink.c b/mysys/my_symlink.c
index b366678769e..7be3fcd36f0 100644
--- a/mysys/my_symlink.c
+++ b/mysys/my_symlink.c
@@ -26,9 +26,11 @@
/*
Reads the content of a symbolic link
If the file is not a symbolic link, return the original file name in to.
- Returns: 0 if table was a symlink,
- 1 if table was a normal file
- -1 on error.
+
+ RETURN
+ 0 If filename was a symlink, (to will be set to value of symlink)
+ 1 If filename was a normal file (to will be set to filename)
+ -1 on error.
*/
int my_readlink(char *to, const char *filename, myf MyFlags)
@@ -58,6 +60,7 @@ int my_readlink(char *to, const char *filename, myf MyFlags)
}
else
to[length]=0;
+ DBUG_PRINT("exit" ,("result: %d", result));
DBUG_RETURN(result);
#endif /* HAVE_READLINK */
}
@@ -124,20 +127,22 @@ int my_realpath(char *to, const char *filename,
}
else
{
- /* Realpath didn't work; Use original name */
+ /*
+ Realpath didn't work; Use my_load_path() which is a poor substitute
+ original name but will at least be able to resolve paths that starts
+ with '.'.
+ */
DBUG_PRINT("error",("realpath failed with errno: %d", errno));
my_errno=errno;
if (MyFlags & MY_WME)
my_error(EE_REALPATH, MYF(0), filename, my_errno);
- if (to != filename)
- strmov(to,filename);
+ my_load_path(to, filename, NullS);
result= -1;
}
}
DBUG_RETURN(result);
#else
- if (to != filename)
- strmov(to,filename);
+ my_load_path(to, filename, NullS);
return 0;
#endif
}
diff --git a/mysys/my_symlink2.c b/mysys/my_symlink2.c
index 4d58699412a..7aa4e359bb9 100644
--- a/mysys/my_symlink2.c
+++ b/mysys/my_symlink2.c
@@ -32,6 +32,7 @@ File my_create_with_symlink(const char *linkname, const char *filename,
int tmp_errno;
/* Test if we should create a link */
int create_link;
+ char abs_linkname[FN_REFLEN];
DBUG_ENTER("my_create_with_symlink");
if (my_disable_symlinks)
@@ -42,7 +43,11 @@ File my_create_with_symlink(const char *linkname, const char *filename,
filename= linkname;
}
else
- create_link= (linkname && strcmp(linkname,filename));
+ {
+ if (linkname)
+ my_realpath(abs_linkname, linkname, MYF(0));
+ create_link= (linkname && strcmp(abs_linkname,filename));
+ }
if (!(MyFlags & MY_DELETE_OLD))
{
diff --git a/mysys/my_tempnam.c b/mysys/my_tempnam.c
index b4f76727ee0..9f765298fb6 100644
--- a/mysys/my_tempnam.c
+++ b/mysys/my_tempnam.c
@@ -161,7 +161,7 @@ my_string my_tempnam(const char *dir, const char *pfx,
for (length=0 ; length < 8 && uniq ; length++)
{
- *end_pos++= _dig_vec[(int) (uniq & 31)];
+ *end_pos++= _dig_vec_upper[(int) (uniq & 31)];
uniq >>= 5;
}
VOID(strmov(end_pos,TMP_EXT));
diff --git a/mysys/my_thr_init.c b/mysys/my_thr_init.c
index f4fd96b769d..42f025c39f3 100644
--- a/mysys/my_thr_init.c
+++ b/mysys/my_thr_init.c
@@ -29,10 +29,10 @@ pthread_key(struct st_my_thread_var*, THR_KEY_mysys);
#else
pthread_key(struct st_my_thread_var, THR_KEY_mysys);
#endif /* USE_TLS */
-pthread_mutex_t THR_LOCK_malloc,THR_LOCK_open,THR_LOCK_keycache,
+pthread_mutex_t THR_LOCK_malloc,THR_LOCK_open,
THR_LOCK_lock,THR_LOCK_isam,THR_LOCK_myisam,THR_LOCK_heap,
THR_LOCK_net, THR_LOCK_charset;
-#ifndef HAVE_LOCALTIME_R
+#if !defined(HAVE_LOCALTIME_R) || !defined(HAVE_GMTIME_R)
pthread_mutex_t LOCK_localtime_r;
#endif
#ifndef HAVE_GETHOSTBYNAME_R
@@ -42,7 +42,7 @@ pthread_mutex_t LOCK_gethostbyname_r;
pthread_mutexattr_t my_fast_mutexattr;
#endif
#ifdef PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP
-pthread_mutexattr_t my_errchk_mutexattr;
+pthread_mutexattr_t my_errorcheck_mutexattr;
#endif
static uint get_thread_lib(void);
@@ -60,33 +60,40 @@ static uint get_thread_lib(void);
my_bool my_thread_global_init(void)
{
-#if defined(SIGALRM) || defined(SIGUSR1)
- /* On Windows, these signals are not defined, but this whole part is not needed. */
thd_lib_detected= get_thread_lib();
- if (thd_lib_detected == THD_LIB_LT)
- thr_client_alarm= SIGALRM;
- else
- thr_client_alarm= SIGUSR1;
-#endif
if (pthread_key_create(&THR_KEY_mysys,0))
{
fprintf(stderr,"Can't initialize threads: error %d\n",errno);
return 1;
}
+
#ifdef PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP
+ /*
+ Set mutex type to "fast" a.k.a "adaptive"
+
+ The mutex kind determines what happens if a thread attempts to lock
+ a mutex it already owns with pthread_mutex_lock(3). If the mutex
+ is of the ``fast'' kind, pthread_mutex_lock(3) simply suspends
+ the calling thread forever. If the mutex is of the ``error checking''
+ kind, pthread_mutex_lock(3) returns immediately with the error
+ code EDEADLK.
+ */
pthread_mutexattr_init(&my_fast_mutexattr);
- pthread_mutexattr_setkind_np(&my_fast_mutexattr,PTHREAD_MUTEX_ADAPTIVE_NP);
+ pthread_mutexattr_settype(&my_fast_mutexattr,
+ PTHREAD_MUTEX_ADAPTIVE_NP);
#endif
#ifdef PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP
- pthread_mutexattr_init(&my_errchk_mutexattr);
- pthread_mutexattr_setkind_np(&my_errchk_mutexattr,
- PTHREAD_MUTEX_ERRORCHECK_NP);
+ /*
+ Set mutex type to "errorcheck" a.k.a "adaptive"
+ */
+ pthread_mutexattr_init(&my_errorcheck_mutexattr);
+ pthread_mutexattr_settype(&my_errorcheck_mutexattr,
+ PTHREAD_MUTEX_ERRORCHECK);
#endif
pthread_mutex_init(&THR_LOCK_malloc,MY_MUTEX_INIT_FAST);
pthread_mutex_init(&THR_LOCK_open,MY_MUTEX_INIT_FAST);
- pthread_mutex_init(&THR_LOCK_keycache,MY_MUTEX_INIT_FAST);
pthread_mutex_init(&THR_LOCK_lock,MY_MUTEX_INIT_FAST);
pthread_mutex_init(&THR_LOCK_isam,MY_MUTEX_INIT_SLOW);
pthread_mutex_init(&THR_LOCK_myisam,MY_MUTEX_INIT_SLOW);
@@ -96,7 +103,7 @@ my_bool my_thread_global_init(void)
#if defined( __WIN__) || defined(OS2)
win_pthread_init();
#endif
-#ifndef HAVE_LOCALTIME_R
+#if !defined(HAVE_LOCALTIME_R) || !defined(HAVE_GMTIME_R)
pthread_mutex_init(&LOCK_localtime_r,MY_MUTEX_INIT_SLOW);
#endif
#ifndef HAVE_GETHOSTBYNAME_R
@@ -118,18 +125,17 @@ void my_thread_global_end(void)
pthread_mutexattr_destroy(&my_fast_mutexattr);
#endif
#ifdef PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP
- pthread_mutexattr_destroy(&my_errchk_mutexattr);
+ pthread_mutexattr_destroy(&my_errorcheck_mutexattr);
#endif
pthread_mutex_destroy(&THR_LOCK_malloc);
pthread_mutex_destroy(&THR_LOCK_open);
- pthread_mutex_destroy(&THR_LOCK_keycache);
pthread_mutex_destroy(&THR_LOCK_lock);
pthread_mutex_destroy(&THR_LOCK_isam);
pthread_mutex_destroy(&THR_LOCK_myisam);
pthread_mutex_destroy(&THR_LOCK_heap);
pthread_mutex_destroy(&THR_LOCK_net);
pthread_mutex_destroy(&THR_LOCK_charset);
-#ifndef HAVE_LOCALTIME_R
+#if !defined(HAVE_LOCALTIME_R) || !defined(HAVE_GMTIME_R)
pthread_mutex_destroy(&LOCK_localtime_r);
#endif
#ifndef HAVE_GETHOSTBYNAME_R
@@ -217,7 +223,8 @@ void my_thread_end(void)
tmp->dbug=0;
}
#endif
-#if !defined(__bsdi__) || defined(HAVE_mit_thread) /* bsdi dumps core here */
+#if !defined(__bsdi__) && !defined(__OpenBSD__) || defined(HAVE_mit_thread)
+ /* bsdi and openbsd 3.5 dumps core here */
pthread_cond_destroy(&tmp->suspend);
#endif
pthread_mutex_destroy(&tmp->mutex);
diff --git a/mysys/my_vsnprintf.c b/mysys/my_vsnprintf.c
deleted file mode 100644
index e7cc780060c..00000000000
--- a/mysys/my_vsnprintf.c
+++ /dev/null
@@ -1,180 +0,0 @@
-/* Copyright (C) 2000 MySQL AB
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
-
-#include <my_global.h>
-#include <m_string.h>
-#include <stdarg.h>
-#include <m_ctype.h>
-
-/*
- Limited snprintf() implementations
-
- IMPLEMENTION:
- Supports following formats:
- %#[l]d
- %#[l]u
- %#[l]x
- %#.#s Note first # is ignored
-
- RETURN
- length of result string
-*/
-
-int my_vsnprintf(char *to, size_t n, const char* fmt, va_list ap)
-{
- char *start=to, *end=to+n-1;
- uint length, width, pre_zero, have_long;
-
- for (; *fmt ; fmt++)
- {
- if (fmt[0] != '%')
- {
- if (to == end) /* End of buffer */
- break;
- *to++= *fmt; /* Copy ordinary char */
- continue;
- }
- fmt++; /* skip '%' */
- /* Read max fill size (only used with %d and %u) */
- if (*fmt == '-')
- fmt++;
- length= width= pre_zero= have_long= 0;
- for (;isdigit(*fmt); fmt++)
- {
- length=length*10+ (uint) (*fmt-'0');
- if (!length)
- pre_zero= 1; /* first digit was 0 */
- }
- if (*fmt == '.')
- for (fmt++;isdigit(*fmt); fmt++)
- width=width*10+ (uint) (*fmt-'0');
- else
- width= ~0;
- if (*fmt == 'l')
- {
- fmt++;
- have_long= 1;
- }
- if (*fmt == 's') /* String parameter */
- {
- reg2 char *par = va_arg(ap, char *);
- uint plen,left_len = (uint)(end-to)+1;
- if (!par) par = (char*)"(null)";
- plen = (uint) strlen(par);
- set_if_smaller(plen,width);
- if (left_len <= plen)
- plen = left_len - 1;
- to=strnmov(to,par,plen);
- continue;
- }
- else if (*fmt == 'd' || *fmt == 'u'|| *fmt== 'x') /* Integer parameter */
- {
- register long larg;
- uint res_length, to_length;
- char *store_start= to, *store_end;
- char buff[32];
-
- if ((to_length= (uint) (end-to)) < 16 || length)
- store_start= buff;
- if (have_long)
- larg = va_arg(ap, long);
- else
- if (*fmt == 'd')
- larg = va_arg(ap, int);
- else
- larg= (long) (uint) va_arg(ap, int);
- if (*fmt == 'd')
- store_end= int10_to_str(larg, store_start, -10);
- else
- if (*fmt== 'u')
- store_end= int10_to_str(larg, store_start, 10);
- else
- store_end= int2str(larg, store_start, 16);
- if ((res_length= (uint) (store_end - store_start)) > to_length)
- break; /* num doesn't fit in output */
- /* If %#d syntax was used, we have to pre-zero/pre-space the string */
- if (store_start == buff)
- {
- length= min(length, to_length);
- if (res_length < length)
- {
- uint diff= (length- res_length);
- bfill(to, diff, pre_zero ? '0' : ' ');
- to+= diff;
- }
- bmove(to, store_start, res_length);
- }
- to+= res_length;
- continue;
- }
- /* We come here on '%%', unknown code or too long parameter */
- if (to == end)
- break;
- *to++='%'; /* % used as % or unknown code */
- }
- *to='\0'; /* End of errmessage */
- return (uint) (to - start);
-}
-
-int my_snprintf(char* to, size_t n, const char* fmt, ...)
-{
- int result;
- va_list args;
- va_start(args,fmt);
- result= my_vsnprintf(to, n, fmt, args);
- va_end(args);
- return result;
-}
-
-#ifdef MAIN
-#define OVERRUN_SENTRY 250
-static void my_printf(const char * fmt, ...)
-{
- char buf[33];
- int n;
- va_list ar;
- va_start(ar, fmt);
- buf[sizeof(buf)-1]=OVERRUN_SENTRY;
- n = my_vsnprintf(buf, sizeof(buf)-1,fmt, ar);
- printf(buf);
- printf("n=%d, strlen=%d\n", n, strlen(buf));
- if ((uchar) buf[sizeof(buf)-1] != OVERRUN_SENTRY)
- {
- fprintf(stderr, "Buffer overrun\n");
- abort();
- }
- va_end(ar);
-}
-
-
-int main()
-{
-
- my_printf("Hello\n");
- my_printf("Hello int, %d\n", 1);
- my_printf("Hello string '%s'\n", "I am a string");
- my_printf("Hello hack hack hack hack hack hack hack %d\n", 1);
- my_printf("Hello %d hack %d\n", 1, 4);
- my_printf("Hello %d hack hack hack hack hack %d\n", 1, 4);
- my_printf("Hello '%s' hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh\n", "hack");
- my_printf("Hello hhhhhhhhhhhhhh %d sssssssssssssss\n", 1);
- my_printf("Hello %u\n", 1);
- my_printf("Hex: %lx '%6lx'\n", 32, 65);
- my_printf("conn %ld to: '%-.64s' user: '%-.32s' host:\
- `%-.64s' (%-.64s)", 1, 0,0,0,0);
- return 0;
-}
-#endif
diff --git a/mysys/my_windac.c b/mysys/my_windac.c
new file mode 100644
index 00000000000..2c1027e4aa6
--- /dev/null
+++ b/mysys/my_windac.c
@@ -0,0 +1,224 @@
+/* Copyright (C) 2000-2005 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#include "mysys_priv.h"
+#include "m_string.h"
+#ifdef __WIN__
+
+/* Windows NT/2000 discretionary access control utility functions. */
+
+/*
+ Check if the operating system is built on NT technology.
+
+ RETURN
+ 0 Windows 95/98/Me
+ 1 otherwise
+*/
+
+static my_bool is_nt()
+{
+ return GetVersion() < 0x80000000;
+}
+
+/*
+ Auxilary structure to store pointers to the data which we need to keep
+ around while SECURITY_ATTRIBUTES is in use.
+*/
+
+typedef struct st_my_security_attr
+{
+ PSID everyone_sid;
+ PACL dacl;
+} My_security_attr;
+
+
+/*
+ Allocate and initialize SECURITY_ATTRIBUTES setting up access
+ rights for the owner and group `Everybody'.
+
+ SYNOPSIS
+ my_security_attr_create()
+ psa [OUT] pointer to store the pointer to SA in
+ perror [OUT] pointer to store error message if there was an
+ error
+ owner_rights [IN] access rights for the owner
+ everyone_rights [IN] access rights for group Everybody
+
+ DESCRIPTION
+ Set up the security attributes to provide clients with sufficient
+ access rights to a kernel object. We need this function
+ because if we simply grant all access to everybody (by installing
+ a NULL DACL) a mailicious user can attempt a denial of service
+ attack by taking ownership over the kernel object. Upon successful
+ return `psa' contains a pointer to SECUIRITY_ATTRIBUTES that can be used
+ to create kernel objects with proper access rights.
+
+ RETURN
+ 0 success, psa is 0 or points to a valid SA structure,
+ perror is left intact
+ !0 error, SA is set to 0, error message is stored in perror
+*/
+
+int my_security_attr_create(SECURITY_ATTRIBUTES **psa, const char **perror,
+ DWORD owner_rights, DWORD everyone_rights)
+{
+ /* Top-level SID authority */
+ SID_IDENTIFIER_AUTHORITY world_auth= SECURITY_WORLD_SID_AUTHORITY;
+ PSID everyone_sid= 0;
+ HANDLE htoken= 0;
+ SECURITY_ATTRIBUTES *sa= 0;
+ PACL dacl= 0;
+ DWORD owner_token_length, dacl_length;
+ SECURITY_DESCRIPTOR *sd;
+ PTOKEN_USER owner_token;
+ PSID owner_sid;
+ My_security_attr *attr;
+
+ if (! is_nt())
+ {
+ *psa= 0;
+ return 0;
+ }
+
+ /*
+ Get SID of Everyone group. Easier to retrieve all SIDs each time
+ this function is called than worry about thread safety.
+ */
+ if (! AllocateAndInitializeSid(&world_auth, 1, SECURITY_WORLD_RID,
+ 0, 0, 0, 0, 0, 0, 0, &everyone_sid))
+ {
+ *perror= "Failed to retrieve the SID of Everyone group";
+ goto error;
+ }
+
+ /*
+ Get SID of the owner. Using GetSecurityInfo this task can be done
+ in just one call instead of five, but GetSecurityInfo declared in
+ aclapi.h, so I hesitate to use it.
+ SIC: OpenThreadToken works only if there is an active impersonation
+ token, hence OpenProcessToken is used.
+ */
+ if (! OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &htoken))
+ {
+ *perror= "Failed to retrieve thread access token";
+ goto error;
+ }
+ GetTokenInformation(htoken, TokenUser, 0, 0, &owner_token_length);
+
+ if (! my_multi_malloc(MYF(MY_WME),
+ &sa, ALIGN_SIZE(sizeof(SECURITY_ATTRIBUTES)) +
+ sizeof(My_security_attr),
+ &sd, sizeof(SECURITY_DESCRIPTOR),
+ &owner_token, owner_token_length,
+ 0))
+ {
+ *perror= "Failed to allocate memory for SECURITY_ATTRIBUTES";
+ goto error;
+ }
+ bzero(owner_token, owner_token_length);
+ if (! GetTokenInformation(htoken, TokenUser, owner_token,
+ owner_token_length, &owner_token_length))
+ {
+ *perror= "GetTokenInformation failed";
+ goto error;
+ }
+ owner_sid= owner_token->User.Sid;
+
+ if (! IsValidSid(owner_sid))
+ {
+ *perror= "IsValidSid failed";
+ goto error;
+ }
+
+ /* Calculate the amount of memory that must be allocated for the DACL */
+ dacl_length= sizeof(ACL) + (sizeof(ACCESS_ALLOWED_ACE)-sizeof(DWORD)) * 2 +
+ GetLengthSid(everyone_sid) + GetLengthSid(owner_sid);
+
+ /* Create an ACL */
+ if (! (dacl= (PACL) my_malloc(dacl_length, MYF(MY_ZEROFILL|MY_WME))))
+ {
+ *perror= "Failed to allocate memory for DACL";
+ goto error;
+ }
+ if (! InitializeAcl(dacl, dacl_length, ACL_REVISION))
+ {
+ *perror= "Failed to initialize DACL";
+ goto error;
+ }
+ if (! AddAccessAllowedAce(dacl, ACL_REVISION, everyone_rights, everyone_sid))
+ {
+ *perror= "Failed to set up DACL";
+ goto error;
+ }
+ if (! AddAccessAllowedAce(dacl, ACL_REVISION, owner_rights, owner_sid))
+ {
+ *perror= "Failed to set up DACL";
+ goto error;
+ }
+ if (! InitializeSecurityDescriptor(sd, SECURITY_DESCRIPTOR_REVISION))
+ {
+ *perror= "Could not initialize security descriptor";
+ goto error;
+ }
+ if (! SetSecurityDescriptorDacl(sd, TRUE, dacl, FALSE))
+ {
+ *perror= "Failed to install DACL";
+ goto error;
+ }
+
+ sa->nLength= sizeof(*sa);
+ sa->bInheritHandle= TRUE;
+ sa->lpSecurityDescriptor= sd;
+ /* Save pointers to everyone_sid and dacl to be able to clean them up */
+ attr= (My_security_attr*) (((char*) sa) + ALIGN_SIZE(sizeof(*sa)));
+ attr->everyone_sid= everyone_sid;
+ attr->dacl= dacl;
+ *psa= sa;
+
+ CloseHandle(htoken);
+ return 0;
+error:
+ if (everyone_sid)
+ FreeSid(everyone_sid);
+ if (htoken)
+ CloseHandle(htoken);
+ my_free((gptr) sa, MYF(MY_ALLOW_ZERO_PTR));
+ my_free((gptr) dacl, MYF(MY_ALLOW_ZERO_PTR));
+ *psa= 0;
+ return 1;
+}
+
+/*
+ Cleanup security attributes freeing used memory.
+
+ SYNOPSIS
+ my_security_attr_free()
+ sa security attributes
+*/
+
+void my_security_attr_free(SECURITY_ATTRIBUTES *sa)
+{
+ if (sa)
+ {
+ My_security_attr *attr= (My_security_attr*)
+ (((char*)sa) + ALIGN_SIZE(sizeof(*sa)));
+ FreeSid(attr->everyone_sid);
+ my_free((gptr) attr->dacl, MYF(0));
+ my_free((gptr) sa, MYF(0));
+ }
+}
+
+#endif /* __WIN__ */
diff --git a/mysys/my_write.c b/mysys/my_write.c
index de762c16a07..ae8cb4ab02b 100644
--- a/mysys/my_write.c
+++ b/mysys/my_write.c
@@ -26,7 +26,7 @@ uint my_write(int Filedes, const byte *Buffer, uint Count, myf MyFlags)
uint writenbytes,errors;
ulong written;
DBUG_ENTER("my_write");
- DBUG_PRINT("my",("Fd: %d Buffer: %lx Count: %d MyFlags: %d",
+ DBUG_PRINT("my",("Fd: %d Buffer: 0x%lx Count: %d MyFlags: %d",
Filedes, Buffer, Count, MyFlags));
errors=0; written=0L;
diff --git a/mysys/mysys_priv.h b/mysys/mysys_priv.h
index d7aee04ae20..89a6d8aa2a7 100644
--- a/mysys/mysys_priv.h
+++ b/mysys/mysys_priv.h
@@ -21,6 +21,10 @@
#include "system_wrappers.h"
#endif
+#ifdef HAVE_GETRUSAGE
+#include <sys/resource.h>
+#endif
+
#ifdef THREAD
#include <my_pthread.h>
extern pthread_mutex_t THR_LOCK_malloc, THR_LOCK_open, THR_LOCK_keycache;
diff --git a/mysys/queues.c b/mysys/queues.c
index 93d4c303f22..6a285ce7417 100644
--- a/mysys/queues.c
+++ b/mysys/queues.c
@@ -24,7 +24,26 @@
#include <queues.h>
-/* Init queue */
+/*
+ Init queue
+
+ SYNOPSIS
+ init_queue()
+ queue Queue to initialise
+ max_elements Max elements that will be put in queue
+ offset_to_key Offset to key in element stored in queue
+ Used when sending pointers to compare function
+ max_at_top Set to 1 if you want biggest element on top.
+ compare Compare function for elements, takes 3 arguments.
+ first_cmp_arg First argument to compare function
+
+ NOTES
+ Will allocate max_element pointers for queue array
+
+ RETURN
+ 0 ok
+ 1 Could not allocate memory
+*/
int init_queue(QUEUE *queue, uint max_elements, uint offset_to_key,
pbool max_at_top, int (*compare) (void *, byte *, byte *),
@@ -43,13 +62,32 @@ int init_queue(QUEUE *queue, uint max_elements, uint offset_to_key,
DBUG_RETURN(0);
}
+
/*
- Reinitialize queue for new usage;
+ Reinitialize queue for other usage
+
+ SYNOPSIS
+ reinit_queue()
+ queue Queue to initialise
+ max_elements Max elements that will be put in queue
+ offset_to_key Offset to key in element stored in queue
+ Used when sending pointers to compare function
+ max_at_top Set to 1 if you want biggest element on top.
+ compare Compare function for elements, takes 3 arguments.
+ first_cmp_arg First argument to compare function
+
+ NOTES
+ This will delete all elements from the queue. If you don't want this,
+ use resize_queue() instead.
+
+ RETURN
+ 0 ok
+ EE_OUTOFMEMORY Wrong max_elements
*/
int reinit_queue(QUEUE *queue, uint max_elements, uint offset_to_key,
- pbool max_at_top, int (*compare) (void *, byte *, byte *),
- void *first_cmp_arg)
+ pbool max_at_top, int (*compare) (void *, byte *, byte *),
+ void *first_cmp_arg)
{
DBUG_ENTER("reinit_queue");
queue->elements=0;
@@ -96,6 +134,20 @@ int resize_queue(QUEUE *queue, uint max_elements)
}
+/*
+ Delete queue
+
+ SYNOPSIS
+ delete_queue()
+ queue Queue to delete
+
+ IMPLEMENTATION
+ Just free allocated memory.
+
+ NOTES
+ Can be called safely multiple times
+*/
+
void delete_queue(QUEUE *queue)
{
DBUG_ENTER("delete_queue");
@@ -112,28 +164,22 @@ void delete_queue(QUEUE *queue)
void queue_insert(register QUEUE *queue, byte *element)
{
- reg2 uint idx,next;
+ reg2 uint idx, next;
int cmp;
-
-#ifndef DBUG_OFF
- if (queue->elements < queue->max_elements)
-#endif
+ DBUG_ASSERT(queue->elements < queue->max_elements);
+ queue->root[0]= element;
+ idx= ++queue->elements;
+ /* max_at_top swaps the comparison if we want to order by desc */
+ while ((cmp= queue->compare(queue->first_cmp_arg,
+ element + queue->offset_to_key,
+ queue->root[(next= idx >> 1)] +
+ queue->offset_to_key)) &&
+ (cmp ^ queue->max_at_top) < 0)
{
- queue->root[0]=element;
- idx= ++queue->elements;
-
- /* max_at_top swaps the comparison if we want to order by desc */
- while ((cmp=queue->compare(queue->first_cmp_arg,
- element+queue->offset_to_key,
- queue->root[(next=idx >> 1)] +
- queue->offset_to_key)) &&
- (cmp ^ queue->max_at_top) < 0)
- {
- queue->root[idx]=queue->root[next];
- idx=next;
- }
- queue->root[idx]=element;
+ queue->root[idx]= queue->root[next];
+ idx= next;
}
+ queue->root[idx]= element;
}
/* Remove item from queue */
@@ -141,23 +187,18 @@ void queue_insert(register QUEUE *queue, byte *element)
byte *queue_remove(register QUEUE *queue, uint idx)
{
-#ifndef DBUG_OFF
- if (idx >= queue->max_elements)
- return 0;
-#endif
- {
- byte *element=queue->root[++idx]; /* Intern index starts from 1 */
- queue->root[idx]=queue->root[queue->elements--];
- _downheap(queue,idx);
- return element;
- }
+ byte *element;
+ DBUG_ASSERT(idx < queue->max_elements);
+ element= queue->root[++idx]; /* Intern index starts from 1 */
+ queue->root[idx]= queue->root[queue->elements--];
+ _downheap(queue, idx);
+ return element;
}
/* Fix when element on top has been replaced */
#ifndef queue_replaced
-void queue_replaced(queue)
-QUEUE *queue;
+void queue_replaced(QUEUE *queue)
{
_downheap(queue,1);
}
@@ -199,8 +240,8 @@ void _downheap(register QUEUE *queue, uint idx)
static int queue_fix_cmp(QUEUE *queue, void **a, void **b)
{
return queue->compare(queue->first_cmp_arg,
- (char*) (*a)+queue->offset_to_key,
- (char*) (*b)+queue->offset_to_key);
+ (byte*) (*a)+queue->offset_to_key,
+ (byte*) (*b)+queue->offset_to_key);
}
/*
diff --git a/mysys/raid.cc b/mysys/raid.cc
index 0b688464fb3..62587c438ca 100644
--- a/mysys/raid.cc
+++ b/mysys/raid.cc
@@ -70,7 +70,7 @@
tonu@mysql.com & monty@mysql.com
*/
-#ifdef __GNUC__
+#ifdef USE_PRAGMA_IMPLEMENTATION
#pragma implementation // gcc: Class implementation
#endif
diff --git a/mysys/rijndael.c b/mysys/rijndael.c
index dd0c45445d5..43cd14101ca 100644
--- a/mysys/rijndael.c
+++ b/mysys/rijndael.c
@@ -26,7 +26,6 @@
*/
#include <my_global.h>
-#include <assert.h>
#include "rijndael.h"
/*
diff --git a/mysys/safemalloc.c b/mysys/safemalloc.c
index 07c40fd91b6..6cdf98c5f5f 100644
--- a/mysys/safemalloc.c
+++ b/mysys/safemalloc.c
@@ -194,7 +194,7 @@ gptr _mymalloc(uint size, const char *filename, uint lineno, myf MyFlags)
if ((MyFlags & MY_ZEROFILL) || !sf_malloc_quick)
bfill(data, size, (char) (MyFlags & MY_ZEROFILL ? 0 : ALLOC_VAL));
/* Return a pointer to the real data */
- DBUG_PRINT("exit",("ptr: %lx", data));
+ DBUG_PRINT("exit",("ptr: 0x%lx", data));
if (sf_min_adress > data)
sf_min_adress= data;
if (sf_max_adress < data)
@@ -259,7 +259,7 @@ void _myfree(gptr ptr, const char *filename, uint lineno, myf myflags)
{
struct st_irem *irem;
DBUG_ENTER("_myfree");
- DBUG_PRINT("enter",("ptr: %lx", ptr));
+ DBUG_PRINT("enter",("ptr: 0x%lx", ptr));
if (!sf_malloc_quick)
(void) _sanity (filename, lineno);
@@ -446,7 +446,7 @@ static int _checkchunk(register struct st_irem *irem, const char *filename,
irem->filename, irem->linenum);
fprintf(stderr, " discovered at %s:%d\n", filename, lineno);
(void) fflush(stderr);
- DBUG_PRINT("safe",("Underrun at %lx, allocated at %s:%d",
+ DBUG_PRINT("safe",("Underrun at 0x%lx, allocated at %s:%d",
data, irem->filename, irem->linenum));
flag=1;
}
@@ -462,7 +462,7 @@ static int _checkchunk(register struct st_irem *irem, const char *filename,
irem->filename, irem->linenum);
fprintf(stderr, " discovered at '%s:%d'\n", filename, lineno);
(void) fflush(stderr);
- DBUG_PRINT("safe",("Overrun at %lx, allocated at %s:%d",
+ DBUG_PRINT("safe",("Overrun at 0x%lx, allocated at %s:%d",
data,
irem->filename,
irem->linenum));
diff --git a/mysys/sha1.c b/mysys/sha1.c
index d93b4571baf..110d24f8bfc 100644
--- a/mysys/sha1.c
+++ b/mysys/sha1.c
@@ -69,7 +69,7 @@ static void SHA1ProcessMessageBlock(SHA1_CONTEXT*);
Initialize SHA1Context
SYNOPSIS
- sha1_reset()
+ mysql_sha1_reset()
context [in/out] The context to reset.
DESCRIPTION
@@ -92,7 +92,7 @@ const uint32 sha_const_key[5]=
};
-int sha1_reset(SHA1_CONTEXT *context)
+int mysql_sha1_reset(SHA1_CONTEXT *context)
{
#ifndef DBUG_OFF
if (!context)
@@ -119,7 +119,7 @@ int sha1_reset(SHA1_CONTEXT *context)
Return the 160-bit message digest into the array provided by the caller
SYNOPSIS
- sha1_result()
+ mysql_sha1_result()
context [in/out] The context to use to calculate the SHA-1 hash.
Message_Digest: [out] Where the digest is returned.
@@ -132,8 +132,8 @@ int sha1_reset(SHA1_CONTEXT *context)
!= SHA_SUCCESS sha Error Code.
*/
-int sha1_result(SHA1_CONTEXT *context,
- uint8 Message_Digest[SHA1_HASH_SIZE])
+int mysql_sha1_result(SHA1_CONTEXT *context,
+ uint8 Message_Digest[SHA1_HASH_SIZE])
{
int i;
@@ -165,7 +165,7 @@ int sha1_result(SHA1_CONTEXT *context,
Accepts an array of octets as the next portion of the message.
SYNOPSIS
- sha1_input()
+ mysql_sha1_input()
context [in/out] The SHA context to update
message_array An array of characters representing the next portion
of the message.
@@ -176,8 +176,8 @@ int sha1_result(SHA1_CONTEXT *context,
!= SHA_SUCCESS sha Error Code.
*/
-int sha1_input(SHA1_CONTEXT *context, const uint8 *message_array,
- unsigned length)
+int mysql_sha1_input(SHA1_CONTEXT *context, const uint8 *message_array,
+ unsigned length)
{
if (!length)
return SHA_SUCCESS;
diff --git a/mysys/test_charset.c b/mysys/test_charset.c
index 15e46f3ff82..419332cb997 100644
--- a/mysys/test_charset.c
+++ b/mysys/test_charset.c
@@ -21,12 +21,44 @@
#include <stdio.h>
-extern void _print_csinfo(CHARSET_INFO *cs);
+static void _print_array(uint8 *data, uint size)
+{
+ uint i;
+ for (i = 0; i < size; ++i)
+ {
+ if (i == 0 || i % 16 == size % 16) printf(" ");
+ printf(" %02x", data[i]);
+ if ((i+1) % 16 == size % 16) printf("\n");
+ }
+}
+
+static void _print_csinfo(CHARSET_INFO *cs)
+{
+ printf("%s #%d\n", cs->name, cs->number);
+ printf("ctype:\n"); _print_array(cs->ctype, 257);
+ printf("to_lower:\n"); _print_array(cs->to_lower, 256);
+ printf("to_upper:\n"); _print_array(cs->to_upper, 256);
+ printf("sort_order:\n"); _print_array(cs->sort_order, 256);
+ printf("collate: %3s (%d, %p, %p, %p)\n",
+ cs->strxfrm_multiply ? "yes" : "no",
+ cs->strxfrm_multiply,
+ cs->strnncoll,
+ cs->strnxfrm,
+ cs->like_range);
+ printf("multi-byte: %3s (%d, %p, %p, %p)\n",
+ cs->mbmaxlen > 1 ? "yes" : "no",
+ cs->mbmaxlen,
+ cs->ismbchar,
+ cs->ismbhead,
+ cs->mbcharlen);
+}
+
int main(int argc, char **argv) {
const char *the_set = MYSQL_CHARSET;
char *cs_list;
int argcnt = 1;
+ CHARSET_INFO *cs;
my_init();
@@ -39,20 +71,22 @@ int main(int argc, char **argv) {
if (argc > argcnt)
charsets_dir = argv[argcnt++];
- if (set_default_charset_by_name(the_set, MYF(MY_WME)))
+ if (!(cs= get_charset_by_name(the_set, MYF(MY_WME))))
return 1;
puts("CHARSET INFO:");
- _print_csinfo(default_charset_info);
+ _print_csinfo(cs);
fflush(stdout);
- cs_list = list_charsets(MYF(MY_COMPILED_SETS | MY_CONFIG_SETS));
+#define NOT_USED_ANYMORE
+ cs_list = list_charsets(MYF(MY_CS_COMPILED | MY_CS_CONFIG));
printf("LIST OF CHARSETS (compiled + *.conf):\n%s\n", cs_list);
- free(cs_list);
+ my_free(cs_list,MYF(0));
- cs_list = list_charsets(MYF(MY_INDEX_SETS | MY_LOADED_SETS));
+ cs_list = list_charsets(MYF(MY_CS_INDEX | MY_CS_LOADED));
printf("LIST OF CHARSETS (index + loaded):\n%s\n", cs_list);
- free(cs_list);
+ my_free(cs_list,MYF(0));
+#endif
return 0;
}
diff --git a/mysys/test_xml.c b/mysys/test_xml.c
new file mode 100644
index 00000000000..2a679906cbf
--- /dev/null
+++ b/mysys/test_xml.c
@@ -0,0 +1,105 @@
+/* Copyright (C) 2000 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include "my_xml.h"
+
+static void mstr(char *str,const char *src,uint l1,uint l2)
+{
+ l1 = l1<l2 ? l1 : l2;
+ memcpy(str,src,l1);
+ str[l1]='\0';
+}
+
+static int dstr(MY_XML_PARSER *st,const char *attr, uint len)
+{
+ char str[1024];
+
+ mstr(str,attr,len,sizeof(str)-1);
+ printf("VALUE '%s'\n",str);
+ return MY_XML_OK;
+}
+
+static int bstr(MY_XML_PARSER *st,const char *attr, uint len)
+{
+ char str[1024];
+
+ mstr(str,attr,len,sizeof(str)-1);
+ printf("ENTER %s\n",str);
+ return MY_XML_OK;
+}
+
+
+static int estr(MY_XML_PARSER *st,const char *attr, uint len)
+{
+ char str[1024];
+
+ mstr(str,attr,len,sizeof(str)-1);
+ printf("LEAVE %s\n",str);
+ return MY_XML_OK;
+}
+
+static void usage(const char *prog)
+{
+ printf("Usage:\n");
+ printf("%s xmlfile\n",prog);
+}
+
+int main(int ac, char **av)
+{
+ char str[1024*64]="";
+ const char *fn;
+ int f;
+ uint len;
+ MY_XML_PARSER p;
+
+ if (ac<2)
+ {
+ usage(av[0]);
+ return 0;
+ }
+
+ fn=av[1]?av[1]:"test.xml";
+ if ((f=open(fn,O_RDONLY))<0)
+ {
+ fprintf(stderr,"Err '%s'\n",fn);
+ return 1;
+ }
+
+ len=read(f,str,sizeof(str)-1);
+ str[len]='\0';
+
+ my_xml_parser_create(&p);
+
+ my_xml_set_enter_handler(&p,bstr);
+ my_xml_set_value_handler(&p,dstr);
+ my_xml_set_leave_handler(&p,estr);
+
+ if (MY_XML_OK!=(f=my_xml_parse(&p,str,len)))
+ {
+ printf("ERROR at line %d pos %d '%s'\n",
+ my_xml_error_lineno(&p)+1,
+ my_xml_error_pos(&p),
+ my_xml_error_string(&p));
+ }
+
+ my_xml_parser_free(&p);
+
+ return 0;
+}
diff --git a/mysys/testhash.c b/mysys/testhash.c
index a1d14dc225d..d15016113cd 100644
--- a/mysys/testhash.c
+++ b/mysys/testhash.c
@@ -74,7 +74,7 @@ static int do_test()
bzero((char*) key1,sizeof(key1[0])*1000);
printf("- Creating hash\n");
- if (hash_init(&hash,recant/2,0,6,0,free_record,0))
+ if (hash_init(&hash, default_charset_info, recant/2, 0, 6, 0, free_record, 0))
goto err;
printf("- Writing records:\n");
@@ -83,7 +83,7 @@ static int do_test()
n1=rnd(1000); n2=rnd(100); n3=rnd(min(recant*5,MAX_RECORDS));
record= (char*) my_malloc(reclength,MYF(MY_FAE));
sprintf(record,"%6d:%4d:%8d:Pos: %4d ",n1,n2,n3,write_count);
- if (hash_insert(&hash,record))
+ if (my_hash_insert(&hash,record))
{
printf("Error: %d in write at record: %d\n",my_errno,i);
goto err;
@@ -172,15 +172,16 @@ static int do_test()
break;
if (key1[j] > 1)
{
+ HASH_SEARCH_STATE state;
printf("- Testing identical read\n");
sprintf(key,"%6d",j);
pos=1;
- if (!(recpos=hash_search(&hash,key,0)))
+ if (!(recpos= hash_first(&hash, key, 0, &state)))
{
printf("can't find key1: \"%s\"\n",key);
goto err;
}
- while (hash_next(&hash,key,0) && pos < (ulong) (key1[j]+10))
+ while (hash_next(&hash, key, 0, &state) && pos < (ulong) (key1[j]+10))
pos++;
if (pos != (ulong) key1[j])
{
@@ -189,7 +190,7 @@ static int do_test()
}
}
printf("- Creating output heap-file 2\n");
- if (hash_init(&hash2,hash.records,0,0,hash2_key,free_record,0))
+ if (hash_init(&hash2, default_charset_info, hash.records, 0, 0, hash2_key, free_record,0))
goto err;
printf("- Copying and removing records\n");
@@ -199,7 +200,7 @@ static int do_test()
record=(byte*) my_malloc(reclength,MYF(MY_FAE));
memcpy(record,recpos,reclength);
record[reclength-1]=rnd(5)+1;
- if (hash_insert(&hash2,record))
+ if (my_hash_insert(&hash2,record))
{
printf("Got error when inserting record: %*s",reclength,record);
goto err;
diff --git a/mysys/thr_alarm.c b/mysys/thr_alarm.c
index c8e1e55aac2..759544af17b 100644
--- a/mysys/thr_alarm.c
+++ b/mysys/thr_alarm.c
@@ -27,7 +27,6 @@
#include <m_string.h>
#include <queues.h>
#include "thr_alarm.h"
-#include <assert.h>
#ifdef HAVE_SYS_SELECT_H
#include <sys/select.h> /* AIX needs this for fd_set */
@@ -37,6 +36,7 @@
#define ETIME ETIMEDOUT
#endif
+uint thr_client_alarm;
static int alarm_aborted=1; /* No alarm thread */
my_bool thr_alarm_inited= 0;
volatile my_bool alarm_thread_running= 0;
@@ -75,7 +75,7 @@ void init_thr_alarm(uint max_alarms)
alarm_aborted=0;
init_queue(&alarm_queue,max_alarms+1,offsetof(ALARM,expire_time),0,
compare_ulong,NullS);
- sigfillset(&full_signal_set); /* Needed to block signals */
+ sigfillset(&full_signal_set); /* Neaded to block signals */
pthread_mutex_init(&LOCK_alarm,MY_MUTEX_INIT_FAST);
pthread_cond_init(&COND_alarm,NULL);
if (thd_lib_detected == THD_LIB_LT)
@@ -255,11 +255,10 @@ void thr_end_alarm(thr_alarm_t *alarmed)
if (!found)
{
if (*alarmed)
- fprintf(stderr,
- "Warning: Didn't find alarm 0x%lx in queue of %d alarms\n",
- (long) *alarmed, alarm_queue.elements);
+ fprintf(stderr,"Warning: Didn't find alarm 0x%lx in queue of %d alarms\n",
+ (long) *alarmed, alarm_queue.elements);
DBUG_PRINT("warning",("Didn't find alarm 0x%lx in queue\n",
- (long) *alarmed));
+ (long) *alarmed));
}
pthread_mutex_unlock(&LOCK_alarm);
pthread_sigmask(SIG_SETMASK,&old_mask,NULL);
@@ -307,7 +306,7 @@ sig_handler process_alarm(int sig __attribute__((unused)))
process_alarm_part2(sig);
#ifndef USE_ALARM_THREAD
#if defined(DONT_REMEMBER_SIGNAL) && !defined(USE_ONE_SIGNAL_HAND)
- my_sigset(THR_SERVER_ALARM, process_alarm);
+ my_sigset(THR_SERVER_ALARM,process_alarm);
#endif
pthread_mutex_unlock(&LOCK_alarm);
pthread_sigmask(SIG_SETMASK,&old_mask,NULL);
@@ -499,13 +498,14 @@ void thr_alarm_info(ALARM_INFO *info)
ARGSUSED
*/
+
static sig_handler thread_alarm(int sig)
{
#ifdef MAIN
printf("thread_alarm\n"); fflush(stdout);
#endif
#ifdef DONT_REMEMBER_SIGNAL
- my_sigset(sig, thread_alarm); /* int. thread system calls */
+ my_sigset(sig,thread_alarm); /* int. thread system calls */
#endif
}
@@ -908,7 +908,7 @@ static sig_handler print_signal_warning(int sig)
printf("Warning: Got signal %d from thread %s\n",sig,my_thread_name());
fflush(stdout);
#ifdef DONT_REMEMBER_SIGNAL
- my_sigset(sig, print_signal_warning); /* int. thread system calls */
+ my_sigset(sig,print_signal_warning); /* int. thread system calls */
#endif
#ifndef OS2
if (sig == SIGALRM)
diff --git a/mysys/thr_lock.c b/mysys/thr_lock.c
index a789162a56a..d6443539216 100644
--- a/mysys/thr_lock.c
+++ b/mysys/thr_lock.c
@@ -91,7 +91,7 @@ enum thr_lock_type thr_upgraded_concurrent_insert_lock = TL_WRITE;
#define MAX_LOCKS 100
-static LIST *thread_list; /* List of threads in use */
+LIST *thr_lock_thread_list; /* List of threads in use */
ulong max_write_lock_count= ~(ulong) 0L;
static inline pthread_cond_t *get_cond(void)
@@ -314,7 +314,7 @@ void thr_lock_init(THR_LOCK *lock)
pthread_mutex_lock(&THR_LOCK_lock); /* Add to locks in use */
lock->list.data=(void*) lock;
- thread_list=list_add(thread_list,&lock->list);
+ thr_lock_thread_list=list_add(thr_lock_thread_list,&lock->list);
pthread_mutex_unlock(&THR_LOCK_lock);
DBUG_VOID_RETURN;
}
@@ -325,7 +325,7 @@ void thr_lock_delete(THR_LOCK *lock)
DBUG_ENTER("thr_lock_delete");
VOID(pthread_mutex_destroy(&lock->mutex));
pthread_mutex_lock(&THR_LOCK_lock);
- thread_list=list_delete(thread_list,&lock->list);
+ thr_lock_thread_list=list_delete(thr_lock_thread_list,&lock->list);
pthread_mutex_unlock(&THR_LOCK_lock);
DBUG_VOID_RETURN;
}
@@ -435,7 +435,7 @@ int thr_lock(THR_LOCK_DATA *data,enum thr_lock_type lock_type)
data->thread=pthread_self(); /* Must be reset ! */
data->thread_id=my_thread_id(); /* Must be reset ! */
VOID(pthread_mutex_lock(&lock->mutex));
- DBUG_PRINT("lock",("data: %lx thread: %ld lock: %lx type: %d",
+ DBUG_PRINT("lock",("data: 0x%lx thread: %ld lock: 0x%lx type: %d",
data,data->thread_id,lock,(int) lock_type));
check_locks(lock,(uint) lock_type <= (uint) TL_READ_NO_INSERT ?
"enter read_lock" : "enter write_lock",0);
@@ -664,7 +664,7 @@ void thr_unlock(THR_LOCK_DATA *data)
THR_LOCK *lock=data->lock;
enum thr_lock_type lock_type=data->type;
DBUG_ENTER("thr_unlock");
- DBUG_PRINT("lock",("data: %lx thread: %ld lock: %lx",
+ DBUG_PRINT("lock",("data: 0x%lx thread: %ld lock: 0x%lx",
data,data->thread_id,lock));
pthread_mutex_lock(&lock->mutex);
check_locks(lock,"start of release lock",0);
@@ -835,7 +835,7 @@ int thr_multi_lock(THR_LOCK_DATA **data,uint count)
{
THR_LOCK_DATA **pos,**end;
DBUG_ENTER("thr_multi_lock");
- DBUG_PRINT("lock",("data: %lx count: %d",data,count));
+ DBUG_PRINT("lock",("data: 0x%lx count: %d",data,count));
if (count > 1)
sort_locks(data,count);
/* lock everything */
@@ -847,7 +847,7 @@ int thr_multi_lock(THR_LOCK_DATA **data,uint count)
DBUG_RETURN(1);
}
#ifdef MAIN
- printf("Thread: %s Got lock: %lx type: %d\n",my_thread_name(),
+ printf("Thread: %s Got lock: 0x%lx type: %d\n",my_thread_name(),
(long) pos[0]->lock, pos[0]->type); fflush(stdout);
#endif
}
@@ -907,12 +907,12 @@ void thr_multi_unlock(THR_LOCK_DATA **data,uint count)
{
THR_LOCK_DATA **pos,**end;
DBUG_ENTER("thr_multi_unlock");
- DBUG_PRINT("lock",("data: %lx count: %d",data,count));
+ DBUG_PRINT("lock",("data: 0x%lx count: %d",data,count));
for (pos=data,end=data+count; pos < end ; pos++)
{
#ifdef MAIN
- printf("Thread: %s Rel lock: %lx type: %d\n",
+ printf("Thread: %s Rel lock: 0x%lx type: %d\n",
my_thread_name(), (long) pos[0]->lock, pos[0]->type);
fflush(stdout);
#endif
@@ -920,7 +920,7 @@ void thr_multi_unlock(THR_LOCK_DATA **data,uint count)
thr_unlock(*pos);
else
{
- DBUG_PRINT("lock",("Free lock: data: %lx thread: %ld lock: %lx",
+ DBUG_PRINT("lock",("Free lock: data: 0x%lx thread: %ld lock: 0x%lx",
*pos,(*pos)->thread_id,(*pos)->lock));
}
}
@@ -1109,7 +1109,7 @@ static void thr_print_lock(const char* name,struct st_lock_list *list)
prev= &list->data;
for (data=list->data; data && count++ < MAX_LOCKS ; data=data->next)
{
- printf("%lx (%lu:%d); ",(ulong) data,data->thread_id,(int) data->type);
+ printf("0x%lx (%lu:%d); ",(ulong) data,data->thread_id,(int) data->type);
if (data->prev != prev)
printf("\nWarning: prev didn't point at previous lock\n");
prev= &data->next;
@@ -1127,11 +1127,12 @@ void thr_print_locks(void)
pthread_mutex_lock(&THR_LOCK_lock);
puts("Current locks:");
- for (list=thread_list ; list && count++ < MAX_THREADS ; list=rest(list))
+ for (list= thr_lock_thread_list; list && count++ < MAX_THREADS;
+ list= list_rest(list))
{
THR_LOCK *lock=(THR_LOCK*) list->data;
VOID(pthread_mutex_lock(&lock->mutex));
- printf("lock: %lx:",(ulong) lock);
+ printf("lock: 0x%lx:",(ulong) lock);
if ((lock->write_wait.data || lock->read_wait.data) &&
(! lock->read.data && ! lock->write.data))
printf(" WARNING: ");
diff --git a/mysys/thr_mutex.c b/mysys/thr_mutex.c
index 3abac2dc737..2facb4e18cf 100644
--- a/mysys/thr_mutex.c
+++ b/mysys/thr_mutex.c
@@ -17,7 +17,7 @@
/* This makes a wrapper for mutex handling to make it easier to debug mutex */
#include <my_global.h>
-#if defined(HAVE_LINUXTHREADS) && !defined (__USE_UNIX98)
+#if defined(TARGET_OS_LINUX) && !defined (__USE_UNIX98)
#define __USE_UNIX98 /* To get rw locks under Linux */
#endif
#if defined(THREAD) && defined(SAFE_MUTEX)
@@ -54,12 +54,15 @@ void safe_mutex_global_init(void)
int safe_mutex_init(safe_mutex_t *mp,
const pthread_mutexattr_t *attr __attribute__((unused)),
- const char *file __attribute__((unused)),
- uint line __attribute__((unused)))
+ const char *file,
+ uint line)
{
bzero((char*) mp,sizeof(*mp));
pthread_mutex_init(&mp->global,MY_MUTEX_INIT_ERRCHK);
pthread_mutex_init(&mp->mutex,attr);
+ /* Mark that mutex is initialized */
+ mp->file= file;
+ mp->line= line;
#ifdef SAFE_MUTEX_DETECT_DESTROY
/*
@@ -70,7 +73,7 @@ int safe_mutex_init(safe_mutex_t *mp,
{
struct st_safe_mutex_info_t *info =mp->info;
- info->init_file= (char *) file;
+ info->init_file= file;
info->init_line= line;
info->prev= NULL;
info->next= NULL;
@@ -92,6 +95,15 @@ int safe_mutex_init(safe_mutex_t *mp,
int safe_mutex_lock(safe_mutex_t *mp,const char *file, uint line)
{
int error;
+ if (!mp->file)
+ {
+ fprintf(stderr,
+ "safe_mutex: Trying to lock unitialized mutex at %s, line %d\n",
+ file, line);
+ fflush(stderr);
+ abort();
+ }
+
pthread_mutex_lock(&mp->global);
if (mp->count > 0 && pthread_equal(pthread_self(),mp->thread))
{
@@ -117,7 +129,7 @@ line %d more than 1 time\n", file,line);
abort();
}
mp->thread=pthread_self();
- mp->file= (char*) file;
+ mp->file= file;
mp->line=line;
pthread_mutex_unlock(&mp->global);
return error;
@@ -198,13 +210,13 @@ int safe_cond_wait(pthread_cond_t *cond, safe_mutex_t *mp, const char *file,
if (mp->count++)
{
fprintf(stderr,
- "safe_mutex: Count was %d in thread %lx when locking mutex at %s, line %d\n",
+ "safe_mutex: Count was %d in thread 0x%lx when locking mutex at %s, line %d\n",
mp->count-1, my_thread_id(), file, line);
fflush(stderr);
abort();
}
mp->thread=pthread_self();
- mp->file= (char*) file;
+ mp->file= file;
mp->line=line;
pthread_mutex_unlock(&mp->global);
return error;
@@ -236,13 +248,13 @@ int safe_cond_timedwait(pthread_cond_t *cond, safe_mutex_t *mp,
if (mp->count++)
{
fprintf(stderr,
- "safe_mutex: Count was %d in thread %lx when locking mutex at %s, line %d (error: %d (%d))\n",
+ "safe_mutex: Count was %d in thread 0x%lx when locking mutex at %s, line %d (error: %d (%d))\n",
mp->count-1, my_thread_id(), file, line, error, error);
fflush(stderr);
abort();
}
mp->thread=pthread_self();
- mp->file= (char*) file;
+ mp->file= file;
mp->line=line;
pthread_mutex_unlock(&mp->global);
return error;
@@ -252,6 +264,14 @@ int safe_cond_timedwait(pthread_cond_t *cond, safe_mutex_t *mp,
int safe_mutex_destroy(safe_mutex_t *mp, const char *file, uint line)
{
int error=0;
+ if (!mp->file)
+ {
+ fprintf(stderr,
+ "safe_mutex: Trying to destroy unitialized mutex at %s, line %d\n",
+ file, line);
+ fflush(stderr);
+ abort();
+ }
if (mp->count != 0)
{
fprintf(stderr,"safe_mutex: Trying to destroy a mutex that was locked at %s, line %d at %s, line %d\n",
@@ -268,6 +288,7 @@ int safe_mutex_destroy(safe_mutex_t *mp, const char *file, uint line)
if (pthread_mutex_destroy(&mp->mutex))
error=1;
#endif
+ mp->file= 0; /* Mark destroyed */
#ifdef SAFE_MUTEX_DETECT_DESTROY
if (mp->info)
diff --git a/mysys/tree.c b/mysys/tree.c
index 2b5ea717809..bec1ec680f1 100644
--- a/mysys/tree.c
+++ b/mysys/tree.c
@@ -17,31 +17,49 @@
/*
Code for handling red-black (balanced) binary trees.
key in tree is allocated accrding to following:
- 1) If free_element function is given to init_tree or size < 0 then tree
- will not allocate keys and only a pointer to each key is saved in tree.
- key_sizes must be 0 to init and search.
+
+ 1) If size < 0 then tree will not allocate keys and only a pointer to
+ each key is saved in tree.
+ compare and search functions uses and returns key-pointer
+
+ 2) If size == 0 then there are two options:
+ - key_size != 0 to tree_insert: The key will be stored in the tree.
+ - key_size == 0 to tree_insert: A pointer to the key is stored.
compare and search functions uses and returns key-pointer.
- 2) if key_size is given to init_tree then each node will continue the
+
+ 3) if key_size is given to init_tree then each node will continue the
key and calls to insert_key may increase length of key.
if key_size > sizeof(pointer) and key_size is a multiple of 8 (double
allign) then key will be put on a 8 alligned adress. Else
the key will be on adress (element+1). This is transparent for user
compare and search functions uses a pointer to given key-argument.
- 3) If init_tree - keysize is 0 then key_size must be given to tree_insert
- and tree_insert will alloc space for key.
- compare and search functions uses a pointer to given key-argument.
+
+ - If you use a free function for tree-elements and you are freeing
+ the element itself, you should use key_size = 0 to init_tree and
+ tree_search
The actual key in TREE_ELEMENT is saved as a pointer or after the
TREE_ELEMENT struct.
If one uses only pointers in tree one can use tree_set_pointer() to
change address of data.
- Copyright Monty Program KB.
- By monty.
+
+ Implemented by monty.
+*/
+
+/*
+ NOTE:
+ tree->compare function should be ALWAYS called as
+ (*tree->compare)(custom_arg, ELEMENT_KEY(tree,element), key)
+ and not other way around, as
+ (*tree->compare)(custom_arg, key, ELEMENT_KEY(tree,element))
+
+ ft_boolean_search.c (at least) relies on that.
*/
#include "mysys_priv.h"
#include <m_string.h>
#include <my_tree.h>
+#include "my_base.h"
#define BLACK 1
#define RED 0
@@ -71,7 +89,7 @@ void init_tree(TREE *tree, uint default_alloc_size, uint memory_limit,
tree_element_free free_element, void *custom_arg)
{
DBUG_ENTER("init_tree");
- DBUG_PRINT("enter",("tree: %lx size: %d",tree,size));
+ DBUG_PRINT("enter",("tree: 0x%lx size: %d",tree,size));
if (default_alloc_size < DEFAULT_ALLOC_SIZE)
default_alloc_size= DEFAULT_ALLOC_SIZE;
@@ -87,6 +105,7 @@ void init_tree(TREE *tree, uint default_alloc_size, uint memory_limit,
tree->custom_arg = custom_arg;
tree->null_element.colour=BLACK;
tree->null_element.left=tree->null_element.right=0;
+ tree->flag= 0;
if (!free_element && size >= 0 &&
((uint) size <= sizeof(void*) || ((uint) size & (sizeof(void*)-1))))
{
@@ -118,7 +137,7 @@ void init_tree(TREE *tree, uint default_alloc_size, uint memory_limit,
static void free_tree(TREE *tree, myf free_flags)
{
DBUG_ENTER("free_tree");
- DBUG_PRINT("enter",("tree: %lx",tree));
+ DBUG_PRINT("enter",("tree: 0x%lx",tree));
if (tree->root) /* If initialized */
{
@@ -151,8 +170,8 @@ void delete_tree(TREE* tree)
void reset_tree(TREE* tree)
{
+ /* do not free mem_root, just mark blocks as free */
free_tree(tree, MYF(MY_MARK_BLOCKS_FREE));
- /* do not my_free() mem_root if applicable, just mark blocks as free */
}
@@ -169,12 +188,17 @@ static void delete_tree_element(TREE *tree, TREE_ELEMENT *element)
}
}
- /* Code for insert, search and delete of elements */
- /* parent[0] = & parent[-1][0]->left ||
- parent[0] = & parent[-1][0]->right */
+/*
+ insert, search and delete of elements
+
+ The following should be true:
+ parent[0] = & parent[-1][0]->left ||
+ parent[0] = & parent[-1][0]->right
+*/
-TREE_ELEMENT *tree_insert(TREE *tree, void *key, uint key_size)
+TREE_ELEMENT *tree_insert(TREE *tree, void *key, uint key_size,
+ void* custom_arg)
{
int cmp;
TREE_ELEMENT *element,***parent;
@@ -184,8 +208,8 @@ TREE_ELEMENT *tree_insert(TREE *tree, void *key, uint key_size)
for (;;)
{
if (element == &tree->null_element ||
- (cmp=(*tree->compare)(tree->custom_arg,
- ELEMENT_KEY(tree,element),key)) == 0)
+ (cmp = (*tree->compare)(custom_arg, ELEMENT_KEY(tree,element),
+ key)) == 0)
break;
if (cmp < 0)
{
@@ -205,15 +229,14 @@ TREE_ELEMENT *tree_insert(TREE *tree, void *key, uint key_size)
&& tree->allocated > tree->memory_limit)
{
reset_tree(tree);
- return tree_insert(tree, key, key_size);
+ return tree_insert(tree, key, key_size, custom_arg);
}
key_size+=tree->size_of_element;
if (tree->with_delete)
element=(TREE_ELEMENT *) my_malloc(alloc_size, MYF(MY_WME));
else
- element=(TREE_ELEMENT *)
- alloc_root(&tree->mem_root,alloc_size);
+ element=(TREE_ELEMENT *) alloc_root(&tree->mem_root,alloc_size);
if (!element)
return(NULL);
**parent=element;
@@ -231,18 +254,21 @@ TREE_ELEMENT *tree_insert(TREE *tree, void *key, uint key_size)
}
else
memcpy((byte*) element+tree->offset_to_key,key,(size_t) key_size);
- element->count=1; /* May give warning in purify */
+ element->count=1; /* May give warning in purify */
tree->elements_in_tree++;
- rb_insert(tree,parent,element); /* rebalance tree */
+ rb_insert(tree,parent,element); /* rebalance tree */
}
else
+ {
+ if (tree->flag & TREE_NO_DUPS)
+ return(NULL);
element->count++;
+ }
DBUG_EXECUTE("check_tree", test_rb_tree(tree->root););
return element;
}
-
-int tree_delete(TREE *tree, void *key)
+int tree_delete(TREE *tree, void *key, void *custom_arg)
{
int cmp,remove_colour;
TREE_ELEMENT *element,***parent, ***org_parent, *nod;
@@ -255,8 +281,8 @@ int tree_delete(TREE *tree, void *key)
{
if (element == &tree->null_element)
return 1; /* Was not in tree */
- if ((cmp=(*tree->compare)(tree->custom_arg,
- ELEMENT_KEY(tree,element),key)) == 0)
+ if ((cmp = (*tree->compare)(custom_arg, ELEMENT_KEY(tree,element),
+ key)) == 0)
break;
if (cmp < 0)
{
@@ -297,13 +323,15 @@ int tree_delete(TREE *tree, void *key)
rb_delete_fixup(tree,parent);
if (tree->free)
(*tree->free)(ELEMENT_KEY(tree,element), free_free, tree->custom_arg);
+ /* This doesn't include key_size, but better than nothing */
+ tree->allocated-= sizeof(TREE_ELEMENT)+tree->size_of_element;
my_free((gptr) element,MYF(0));
tree->elements_in_tree--;
return 0;
}
-void *tree_search(TREE *tree, void *key)
+void *tree_search(TREE *tree, void *key, void *custom_arg)
{
int cmp;
TREE_ELEMENT *element=tree->root;
@@ -312,8 +340,8 @@ void *tree_search(TREE *tree, void *key)
{
if (element == &tree->null_element)
return (void*) 0;
- if ((cmp=(*tree->compare)(tree->custom_arg,
- ELEMENT_KEY(tree,element),key)) == 0)
+ if ((cmp = (*tree->compare)(custom_arg, ELEMENT_KEY(tree,element),
+ key)) == 0)
return ELEMENT_KEY(tree,element);
if (cmp < 0)
element=element->right;
@@ -322,6 +350,176 @@ void *tree_search(TREE *tree, void *key)
}
}
+void *tree_search_key(TREE *tree, const void *key,
+ TREE_ELEMENT **parents, TREE_ELEMENT ***last_pos,
+ enum ha_rkey_function flag, void *custom_arg)
+{
+ int cmp;
+ TREE_ELEMENT *element= tree->root;
+ TREE_ELEMENT **last_left_step_parent= NULL, **last_right_step_parent= NULL;
+ TREE_ELEMENT **last_equal_element= NULL;
+
+/*
+ TODO: support for HA_READ_KEY_OR_PREV, HA_READ_PREFIX flags if needed.
+*/
+
+ *parents = &tree->null_element;
+ while (element != &tree->null_element)
+ {
+ *++parents= element;
+ if ((cmp= (*tree->compare)(custom_arg, ELEMENT_KEY(tree, element),
+ key)) == 0)
+ {
+ switch (flag) {
+ case HA_READ_KEY_EXACT:
+ case HA_READ_KEY_OR_NEXT:
+ case HA_READ_BEFORE_KEY:
+ last_equal_element= parents;
+ cmp= 1;
+ break;
+ case HA_READ_AFTER_KEY:
+ cmp= -1;
+ break;
+ case HA_READ_PREFIX_LAST:
+ case HA_READ_PREFIX_LAST_OR_PREV:
+ last_equal_element= parents;
+ cmp= -1;
+ break;
+ default:
+ return NULL;
+ }
+ }
+ if (cmp < 0) /* element < key */
+ {
+ last_right_step_parent= parents;
+ element= element->right;
+ }
+ else
+ {
+ last_left_step_parent= parents;
+ element= element->left;
+ }
+ }
+ switch (flag) {
+ case HA_READ_KEY_EXACT:
+ case HA_READ_PREFIX_LAST:
+ *last_pos= last_equal_element;
+ break;
+ case HA_READ_KEY_OR_NEXT:
+ *last_pos= last_equal_element ? last_equal_element : last_left_step_parent;
+ break;
+ case HA_READ_AFTER_KEY:
+ *last_pos= last_left_step_parent;
+ break;
+ case HA_READ_PREFIX_LAST_OR_PREV:
+ *last_pos= last_equal_element ? last_equal_element : last_right_step_parent;
+ break;
+ case HA_READ_BEFORE_KEY:
+ *last_pos= last_right_step_parent;
+ break;
+ default:
+ return NULL;
+ }
+ return *last_pos ? ELEMENT_KEY(tree, **last_pos) : NULL;
+}
+
+/*
+ Search first (the most left) or last (the most right) tree element
+*/
+void *tree_search_edge(TREE *tree, TREE_ELEMENT **parents,
+ TREE_ELEMENT ***last_pos, int child_offs)
+{
+ TREE_ELEMENT *element= tree->root;
+
+ *parents= &tree->null_element;
+ while (element != &tree->null_element)
+ {
+ *++parents= element;
+ element= ELEMENT_CHILD(element, child_offs);
+ }
+ *last_pos= parents;
+ return **last_pos != &tree->null_element ?
+ ELEMENT_KEY(tree, **last_pos) : NULL;
+}
+
+void *tree_search_next(TREE *tree, TREE_ELEMENT ***last_pos, int l_offs,
+ int r_offs)
+{
+ TREE_ELEMENT *x= **last_pos;
+
+ if (ELEMENT_CHILD(x, r_offs) != &tree->null_element)
+ {
+ x= ELEMENT_CHILD(x, r_offs);
+ *++*last_pos= x;
+ while (ELEMENT_CHILD(x, l_offs) != &tree->null_element)
+ {
+ x= ELEMENT_CHILD(x, l_offs);
+ *++*last_pos= x;
+ }
+ return ELEMENT_KEY(tree, x);
+ }
+ else
+ {
+ TREE_ELEMENT *y= *--*last_pos;
+ while (y != &tree->null_element && x == ELEMENT_CHILD(y, r_offs))
+ {
+ x= y;
+ y= *--*last_pos;
+ }
+ return y == &tree->null_element ? NULL : ELEMENT_KEY(tree, y);
+ }
+}
+
+/*
+ Expected that tree is fully balanced
+ (each path from root to leaf has the same length)
+*/
+ha_rows tree_record_pos(TREE *tree, const void *key,
+ enum ha_rkey_function flag, void *custom_arg)
+{
+ int cmp;
+ TREE_ELEMENT *element= tree->root;
+ double left= 1;
+ double right= tree->elements_in_tree;
+
+ while (element != &tree->null_element)
+ {
+ if ((cmp= (*tree->compare)(custom_arg, ELEMENT_KEY(tree, element),
+ key)) == 0)
+ {
+ switch (flag) {
+ case HA_READ_KEY_EXACT:
+ case HA_READ_BEFORE_KEY:
+ cmp= 1;
+ break;
+ case HA_READ_AFTER_KEY:
+ cmp= -1;
+ break;
+ default:
+ return HA_POS_ERROR;
+ }
+ }
+ if (cmp < 0) /* element < key */
+ {
+ element= element->right;
+ left= (left + right) / 2;
+ }
+ else
+ {
+ element= element->left;
+ right= (left + right) / 2;
+ }
+ }
+ switch (flag) {
+ case HA_READ_KEY_EXACT:
+ case HA_READ_BEFORE_KEY:
+ return (ha_rows) right;
+ case HA_READ_AFTER_KEY:
+ return (ha_rows) left;
+ default:
+ return HA_POS_ERROR;
+ }
+}
int tree_walk(TREE *tree, tree_walk_action action, void *argument, TREE_WALK visit)
{
diff --git a/mysys/typelib.c b/mysys/typelib.c
index e2c8eade5c8..90a093b0b32 100644
--- a/mysys/typelib.c
+++ b/mysys/typelib.c
@@ -20,15 +20,28 @@
#include <m_string.h>
#include <m_ctype.h>
-/***************************************************************************
-** Search after a fieldtype. Endspace in x is not compared.
-** If part, uniq field is found and full_name == 0 then x is expanded
-** to full field.
-** full_name has the following bit values:
-** If & 1 accept only whole names
-** If & 2 don't expand if half field
-** If & 4 allow #number# as type
-****************************************************************************/
+
+/*
+ Search after a string in a list of strings. Endspace in x is not compared.
+
+ SYNOPSIS
+ find_type()
+ x String to find
+ lib TYPELIB (struct of pointer to values + count)
+ full_name bitmap of what to do
+ If & 1 accept only whole names
+ If & 2 don't expand if half field
+ If & 4 allow #number# as type
+
+ NOTES
+ If part, uniq field is found and full_name == 0 then x is expanded
+ to full field.
+
+ RETURN
+ -1 Too many matching values
+ 0 No matching value
+ >0 Offset+1 in typelib for matched string
+*/
int find_type(my_string x, TYPELIB *typelib, uint full_name)
{
@@ -36,7 +49,7 @@ int find_type(my_string x, TYPELIB *typelib, uint full_name)
reg1 my_string i;
reg2 const char *j;
DBUG_ENTER("find_type");
- DBUG_PRINT("enter",("x: '%s' lib: %lx",x,typelib));
+ DBUG_PRINT("enter",("x: '%s' lib: 0x%lx",x,typelib));
if (!typelib->count)
{
@@ -47,11 +60,13 @@ int find_type(my_string x, TYPELIB *typelib, uint full_name)
find=0;
for (pos=0 ; (j=typelib->type_names[pos]) ; pos++)
{
- for (i=x ; *i && toupper(*i) == toupper(*j) ; i++, j++) ;
+ for (i=x ;
+ *i && my_toupper(&my_charset_latin1,*i) ==
+ my_toupper(&my_charset_latin1,*j) ; i++, j++) ;
if (! *j)
{
while (*i == ' ')
- i++; /* skipp_end_space */
+ i++; /* skip_end_space */
if (! *i)
DBUG_RETURN(pos+1);
}