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