aboutsummaryrefslogtreecommitdiff
path: root/mysql/mysys
diff options
context:
space:
mode:
Diffstat (limited to 'mysql/mysys')
-rw-r--r--mysql/mysys/array.c281
-rw-r--r--mysql/mysys/base64.c464
-rw-r--r--mysql/mysys/charset-def.c409
-rw-r--r--mysql/mysys/charset.c968
-rw-r--r--mysql/mysys/checksum.c35
-rw-r--r--mysql/mysys/errors.c92
-rw-r--r--mysql/mysys/hash.c812
-rw-r--r--mysql/mysys/lf_alloc-pin.c470
-rw-r--r--mysql/mysys/lf_dynarray.c208
-rw-r--r--mysql/mysys/lf_hash.c722
-rw-r--r--mysql/mysys/list.c114
-rw-r--r--mysql/mysys/mf_arr_appstr.c62
-rw-r--r--mysql/mysys/mf_cache.c105
-rw-r--r--mysql/mysys/mf_dirname.c155
-rw-r--r--mysql/mysys/mf_fn_ext.c54
-rw-r--r--mysql/mysys/mf_format.c147
-rw-r--r--mysql/mysys/mf_getdate.c73
-rw-r--r--mysql/mysys/mf_iocache.c1733
-rw-r--r--mysql/mysys/mf_iocache2.c514
-rw-r--r--mysql/mysys/mf_keycache.c4051
-rw-r--r--mysql/mysys/mf_keycaches.c363
-rw-r--r--mysql/mysys/mf_loadpath.c72
-rw-r--r--mysql/mysys/mf_pack.c409
-rw-r--r--mysql/mysys/mf_path.c121
-rw-r--r--mysql/mysys/mf_qsort.c205
-rw-r--r--mysql/mysys/mf_qsort2.c20
-rw-r--r--mysql/mysys/mf_radix.c59
-rw-r--r--mysql/mysys/mf_same.c41
-rw-r--r--mysql/mysys/mf_soundex.c105
-rw-r--r--mysql/mysys/mf_tempfile.c140
-rw-r--r--mysql/mysys/mf_unixpath.c36
-rw-r--r--mysql/mysys/mf_wcomp.c89
-rw-r--r--mysql/mysys/mulalloc.c64
-rw-r--r--mysql/mysys/my_access.c268
-rw-r--r--mysql/mysys/my_alloc.c557
-rw-r--r--mysql/mysys/my_bit.c64
-rw-r--r--mysql/mysys/my_bitmap.c672
-rw-r--r--mysql/mysys/my_chmod.c102
-rw-r--r--mysql/mysys/my_chsize.c106
-rw-r--r--mysql/mysys/my_compare.c474
-rw-r--r--mysql/mysys/my_compress.c267
-rw-r--r--mysql/mysys/my_copy.c153
-rw-r--r--mysql/mysys/my_create.c74
-rw-r--r--mysql/mysys/my_delete.c133
-rw-r--r--mysql/mysys/my_div.c38
-rw-r--r--mysql/mysys/my_error.c477
-rw-r--r--mysql/mysys/my_file.c144
-rw-r--r--mysql/mysys/my_fopen.c379
-rw-r--r--mysql/mysys/my_fstream.c186
-rw-r--r--mysql/mysys/my_gethwaddr.c259
-rw-r--r--mysql/mysys/my_getsystime.c122
-rw-r--r--mysql/mysys/my_getwd.c162
-rw-r--r--mysql/mysys/my_handler_errors.h114
-rw-r--r--mysql/mysys/my_init.c564
-rw-r--r--mysql/mysys/my_lib.c378
-rw-r--r--mysql/mysys/my_lock.c221
-rw-r--r--mysql/mysys/my_malloc.c325
-rw-r--r--mysql/mysys/my_memmem.c84
-rw-r--r--mysql/mysys/my_mess.c65
-rw-r--r--mysql/mysys/my_mkdir.c48
-rw-r--r--mysql/mysys/my_mmap.c89
-rw-r--r--mysql/mysys/my_once.c120
-rw-r--r--mysql/mysys/my_open.c194
-rw-r--r--mysql/mysys/my_pread.c207
-rw-r--r--mysql/mysys/my_rdtsc.c902
-rw-r--r--mysql/mysys/my_read.c107
-rw-r--r--mysql/mysys/my_redel.c132
-rw-r--r--mysql/mysys/my_rename.c59
-rw-r--r--mysql/mysys/my_seek.c111
-rw-r--r--mysql/mysys/my_static.c143
-rw-r--r--mysql/mysys/my_static.h41
-rw-r--r--mysql/mysys/my_symlink.c208
-rw-r--r--mysql/mysys/my_symlink2.c195
-rw-r--r--mysql/mysys/my_sync.c195
-rw-r--r--mysql/mysys/my_syslog.c287
-rw-r--r--mysql/mysys/my_thr_init.c459
-rw-r--r--mysql/mysys/my_thread.c185
-rw-r--r--mysql/mysys/my_winerr.c130
-rw-r--r--mysql/mysys/my_winfile.c682
-rw-r--r--mysql/mysys/my_write.c129
-rw-r--r--mysql/mysys/mysys_priv.h130
-rw-r--r--mysql/mysys/posix_timers.c395
-rw-r--r--mysql/mysys/psi_noop.c1040
-rw-r--r--mysql/mysys/ptr_cmp.c55
-rw-r--r--mysql/mysys/queues.c622
-rw-r--r--mysql/mysys/sql_chars.c120
-rw-r--r--mysql/mysys/stacktrace.c802
-rw-r--r--mysql/mysys/string.c187
-rw-r--r--mysql/mysys/thr_cond.c113
-rw-r--r--mysql/mysys/thr_lock.c1522
-rw-r--r--mysql/mysys/thr_mutex.c195
-rw-r--r--mysql/mysys/thr_rwlock.c139
-rw-r--r--mysql/mysys/tree.c760
-rw-r--r--mysql/mysys/typelib.c388
94 files changed, 30672 insertions, 0 deletions
diff --git a/mysql/mysys/array.c b/mysql/mysys/array.c
new file mode 100644
index 0000000..cf54304
--- /dev/null
+++ b/mysql/mysys/array.c
@@ -0,0 +1,281 @@
+/* Copyright (c) 2000, 2015, Oracle and/or its affiliates. All rights reserved.
+
+ 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; version 2 of the License.
+
+ 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+/* Handling of arrays that can grow dynamicly. */
+
+#include "mysys_priv.h"
+#include "m_string.h"
+#include "my_sys.h"
+
+/*
+ Initiate dynamic array
+
+ SYNOPSIS
+ my_init_dynamic_array()
+ array Pointer to an array
+ element_size Size of element
+ init_buffer Initial buffer pointer
+ init_alloc Number of initial elements
+ alloc_increment Increment for adding new elements
+
+ DESCRIPTION
+ init_dynamic_array() initiates array and allocate space for
+ init_alloc eilements.
+ Array is usable even if space allocation failed, hence, the
+ function never returns TRUE.
+ Static buffers must begin immediately after the array structure.
+
+ RETURN VALUE
+ FALSE Ok
+*/
+
+my_bool my_init_dynamic_array(DYNAMIC_ARRAY *array,
+ PSI_memory_key psi_key,
+ uint element_size,
+ void *init_buffer,
+ uint init_alloc,
+ uint alloc_increment)
+{
+ DBUG_ENTER("init_dynamic_array");
+ if (!alloc_increment)
+ {
+ alloc_increment=MY_MAX((8192-MALLOC_OVERHEAD)/element_size,16);
+ if (init_alloc > 8 && alloc_increment > init_alloc * 2)
+ alloc_increment=init_alloc*2;
+ }
+
+ if (!init_alloc)
+ {
+ init_alloc=alloc_increment;
+ init_buffer= 0;
+ }
+ array->elements=0;
+ array->max_element=init_alloc;
+ array->alloc_increment=alloc_increment;
+ array->size_of_element=element_size;
+ array->m_psi_key= psi_key;
+ if ((array->buffer= init_buffer))
+ DBUG_RETURN(FALSE);
+ /*
+ Since the dynamic array is usable even if allocation fails here malloc
+ should not throw an error
+ */
+ if (!(array->buffer= (uchar*) my_malloc(psi_key,
+ element_size*init_alloc, MYF(0))))
+ array->max_element=0;
+ DBUG_RETURN(FALSE);
+}
+
+my_bool init_dynamic_array(DYNAMIC_ARRAY *array, uint element_size,
+ uint init_alloc, uint alloc_increment)
+{
+ /* placeholder to preserve ABI */
+ return my_init_dynamic_array(array,
+ PSI_INSTRUMENT_ME,
+ element_size,
+ NULL, /* init_buffer */
+ init_alloc,
+ alloc_increment);
+}
+/*
+ Insert element at the end of array. Allocate memory if needed.
+
+ SYNOPSIS
+ insert_dynamic()
+ array
+ element
+
+ RETURN VALUE
+ TRUE Insert failed
+ FALSE Ok
+*/
+
+my_bool insert_dynamic(DYNAMIC_ARRAY *array, const void *element)
+{
+ uchar* buffer;
+ if (array->elements == array->max_element)
+ { /* Call only when nessesary */
+ if (!(buffer=alloc_dynamic(array)))
+ return TRUE;
+ }
+ else
+ {
+ buffer=array->buffer+(array->elements * array->size_of_element);
+ array->elements++;
+ }
+ memcpy(buffer,element,(size_t) array->size_of_element);
+ return FALSE;
+}
+
+
+/*
+ Alloc space for next element(s)
+
+ SYNOPSIS
+ alloc_dynamic()
+ array
+
+ DESCRIPTION
+ alloc_dynamic() checks if there is empty space for at least
+ one element if not tries to allocate space for alloc_increment
+ elements at the end of array.
+
+ RETURN VALUE
+ pointer Pointer to empty space for element
+ 0 Error
+*/
+
+void *alloc_dynamic(DYNAMIC_ARRAY *array)
+{
+ if (array->elements == array->max_element)
+ {
+ char *new_ptr;
+ if (array->buffer == (uchar *)(array + 1))
+ {
+ /*
+ In this senerio, the buffer is statically preallocated,
+ so we have to create an all-new malloc since we overflowed
+ */
+ if (!(new_ptr= (char *) my_malloc(array->m_psi_key,
+ (array->max_element+
+ array->alloc_increment) *
+ array->size_of_element,
+ MYF(MY_WME))))
+ return 0;
+ memcpy(new_ptr, array->buffer,
+ array->elements * array->size_of_element);
+ }
+ else
+ if (!(new_ptr=(char*) my_realloc(array->m_psi_key,
+ array->buffer,(array->max_element+
+ array->alloc_increment)*
+ array->size_of_element,
+ MYF(MY_WME | MY_ALLOW_ZERO_PTR))))
+ return 0;
+ array->buffer= (uchar*) new_ptr;
+ array->max_element+=array->alloc_increment;
+ }
+ return array->buffer+(array->elements++ * array->size_of_element);
+}
+
+
+/*
+ Pop last element from array.
+
+ SYNOPSIS
+ pop_dynamic()
+ array
+
+ RETURN VALUE
+ pointer Ok
+ 0 Array is empty
+*/
+
+void *pop_dynamic(DYNAMIC_ARRAY *array)
+{
+ if (array->elements)
+ return array->buffer+(--array->elements * array->size_of_element);
+ return 0;
+}
+
+
+/*
+ Get an element from array by given index
+
+ SYNOPSIS
+ get_dynamic()
+ array
+ uchar* Element to be returned. If idx > elements contain zeroes.
+ idx Index of element wanted.
+*/
+
+void get_dynamic(DYNAMIC_ARRAY *array, void *element, uint idx)
+{
+ if (idx >= array->elements)
+ {
+ DBUG_PRINT("warning",("To big array idx: %d, array size is %d",
+ idx,array->elements));
+ memset(element, 0, array->size_of_element);
+ return;
+ }
+ memcpy(element,array->buffer+idx*array->size_of_element,
+ (size_t) array->size_of_element);
+}
+
+void claim_dynamic(DYNAMIC_ARRAY *array)
+{
+ /*
+ Check for a static buffer
+ */
+ if (array->buffer == (uchar *)(array + 1))
+ return;
+
+ my_claim(array->buffer);
+}
+
+
+/*
+ Empty array by freeing all memory
+
+ SYNOPSIS
+ delete_dynamic()
+ array Array to be deleted
+*/
+
+void delete_dynamic(DYNAMIC_ARRAY *array)
+{
+ /*
+ Just mark as empty if we are using a static buffer
+ */
+ if (array->buffer == (uchar *)(array + 1))
+ array->elements= 0;
+ else
+ if (array->buffer)
+ {
+ my_free(array->buffer);
+ array->buffer=0;
+ array->elements=array->max_element=0;
+ }
+}
+
+
+/*
+ Free unused memory
+
+ SYNOPSIS
+ freeze_size()
+ array Array to be freed
+
+*/
+
+void freeze_size(DYNAMIC_ARRAY *array)
+{
+ uint elements=MY_MAX(array->elements,1);
+
+ /*
+ Do nothing if we are using a static buffer
+ */
+ if (array->buffer == (uchar *)(array + 1))
+ return;
+
+ if (array->buffer && array->max_element != elements)
+ {
+ array->buffer=(uchar*) my_realloc(array->m_psi_key,
+ array->buffer,
+ elements*array->size_of_element,
+ MYF(MY_WME));
+ array->max_element=elements;
+ }
+}
diff --git a/mysql/mysys/base64.c b/mysql/mysys/base64.c
new file mode 100644
index 0000000..3deafea
--- /dev/null
+++ b/mysql/mysys/base64.c
@@ -0,0 +1,464 @@
+/* Copyright (c) 2003, 2015, Oracle and/or its affiliates. All rights reserved.
+
+ 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; version 2 of the License.
+
+ 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include <my_global.h>
+#include <m_string.h> /* strchr() */
+#include <m_ctype.h> /* my_isspace() */
+#include <base64.h>
+
+#ifndef MAIN
+
+static char base64_table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "abcdefghijklmnopqrstuvwxyz"
+ "0123456789+/";
+
+/**
+ * Maximum length base64_needed_encoded_length()
+ * can handle without overflow.
+ */
+uint64
+base64_encode_max_arg_length()
+{
+#if (SIZEOF_VOIDP == 8)
+ /*
+ 6827690988321067803 -> 9223372036854775805
+ 6827690988321067804 -> -9223372036854775807
+ */
+ return 0x5EC0D4C77B03531BLL;
+#else
+ /*
+ 1589695686 -> 2147483646
+ 1589695687 -> -2147483645
+ */
+ return 0x5EC0D4C6;
+#endif
+}
+
+
+uint64
+base64_needed_encoded_length(uint64 length_of_data)
+{
+ uint64 nb_base64_chars;
+ if (length_of_data == 0) return 1;
+ nb_base64_chars= (length_of_data + 2) / 3 * 4;
+
+ return
+ nb_base64_chars + /* base64 char incl padding */
+ (nb_base64_chars - 1)/ 76 + /* newlines */
+ 1; /* NUL termination of string */
+}
+
+
+/**
+ * Maximum length base64_needed_decoded_length()
+ * can handle without overflow.
+ */
+uint64
+base64_decode_max_arg_length()
+{
+#if (SIZEOF_VOIDP == 8)
+ return 0x2AAAAAAAAAAAAAAALL;
+#else
+ return 0x2AAAAAAA;
+#endif
+}
+
+
+uint64
+base64_needed_decoded_length(uint64 length_of_encoded_data)
+{
+ return (uint64) ceil(length_of_encoded_data * 3 / 4);
+}
+
+
+/*
+ Encode a data as base64.
+
+ Note: We require that dst is pre-allocated to correct size.
+ See base64_needed_encoded_length().
+*/
+
+int
+base64_encode(const void *src, size_t src_len, char *dst)
+{
+ const unsigned char *s= (const unsigned char*)src;
+ size_t i= 0;
+ size_t len= 0;
+
+ for (; i < src_len; len += 4)
+ {
+ unsigned c;
+
+ if (len == 76)
+ {
+ len= 0;
+ *dst++= '\n';
+ }
+
+ c= s[i++];
+ c <<= 8;
+
+ if (i < src_len)
+ c += s[i];
+ c <<= 8;
+ i++;
+
+ if (i < src_len)
+ c += s[i];
+ i++;
+
+ *dst++= base64_table[(c >> 18) & 0x3f];
+ *dst++= base64_table[(c >> 12) & 0x3f];
+
+ if (i > (src_len + 1))
+ *dst++= '=';
+ else
+ *dst++= base64_table[(c >> 6) & 0x3f];
+
+ if (i > src_len)
+ *dst++= '=';
+ else
+ *dst++= base64_table[(c >> 0) & 0x3f];
+ }
+ *dst= '\0';
+
+ return 0;
+}
+
+
+/*
+ Base64 decoder stream
+*/
+typedef struct my_base64_decoder_t
+{
+ const char *src; /* Pointer to the current input position */
+ const char *end; /* Pointer to the end of input buffer */
+ uint c; /* Collect bits into this number */
+ int error; /* Error code */
+ uchar state; /* Character number in the current group of 4 */
+ uchar mark; /* Number of padding marks in the current group */
+} MY_BASE64_DECODER;
+
+
+/*
+ Helper table for decoder.
+ -2 means "space character"
+ -1 means "bad character"
+ Non-negative values mean valid base64 encoding character.
+*/
+static int8
+from_base64_table[]=
+{
+/*00*/ -1,-1,-1,-1,-1,-1,-1,-1,-1,-2,-2,-2,-2,-2,-1,-1,
+/*10*/ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+/*20*/ -2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,62,-1,-1,-1,63, /* !"#$%&'()*+,-./ */
+/*30*/ 52,53,54,55,56,57,58,59,60,61,-1,-1,-1,-1,-1,-1, /* 0123456789:;<=>? */
+/*40*/ -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14, /* @ABCDEFGHIJKLMNO */
+/*50*/ 15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1, /* PQRSTUVWXYZ[\]^_ */
+/*60*/ -1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40, /* `abcdefghijklmno */
+/*70*/ 41,42,43,44,45,46,47,48,49,50,51,-1,-1,-1,-1,-1, /* pqrstuvwxyz{|}~ */
+/*80*/ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+/*90*/ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+/*A0*/ -2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+/*B0*/ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+/*C0*/ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+/*D0*/ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+/*E0*/ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+/*F0*/ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
+};
+
+
+/**
+ * Skip leading spaces in a base64 encoded stream
+ * and stop on the first non-space character.
+ * decoder->src will point to the first non-space character,
+ * or to the end of the input string.
+ * In case when end-of-input met on unexpected position,
+ * decoder->error is also set to 1.
+ *
+ * @param decoder Pointer to MY_BASE64_DECODER
+ *
+ * @return
+ * FALSE on success (there are some more non-space input characters)
+ * TRUE on error (end-of-input found)
+ */
+static inline my_bool
+my_base64_decoder_skip_spaces(MY_BASE64_DECODER *decoder)
+{
+ for ( ; decoder->src < decoder->end; decoder->src++)
+ {
+ if (from_base64_table[(uchar) *decoder->src] != -2)
+ return FALSE;
+ }
+ if (decoder->state > 0)
+ decoder->error= 1; /* Unexpected end-of-input found */
+ return TRUE;
+}
+
+
+/**
+ * Convert the next character in a base64 encoded stream
+ * to a number in the range [0..63]
+ * and mix it with the previously collected value in decoder->c.
+ *
+ * @param decode base64 decoding stream
+ *
+ * @return
+ * FALSE on success
+ * TRUE on error (invalid base64 character found)
+ */
+static inline my_bool
+my_base64_add(MY_BASE64_DECODER *decoder)
+{
+ int res;
+ decoder->c <<= 6;
+ if ((res= from_base64_table[(uchar) *decoder->src++]) < 0)
+ return (decoder->error= TRUE);
+ decoder->c+= (uint) res;
+ return FALSE;
+}
+
+
+/**
+ * Get the next character from a base64 encoded stream.
+ * Skip spaces, then scan the next base64 character or a pad character
+ * and collect bits into decoder->c.
+ *
+ * @param decoder Pointer to MY_BASE64_DECODER
+ * @return
+ * FALSE on success (a valid base64 encoding character found)
+ * TRUE on error (unexpected character or unexpected end-of-input found)
+ */
+static inline my_bool
+my_base64_decoder_getch(MY_BASE64_DECODER *decoder)
+{
+ if (my_base64_decoder_skip_spaces(decoder))
+ return TRUE; /* End-of-input */
+
+ if (!my_base64_add(decoder)) /* Valid base64 character found */
+ {
+ if (decoder->mark)
+ {
+ /* If we have scanned '=' already, then only '=' is valid */
+ DBUG_ASSERT(decoder->state == 3);
+ decoder->error= 1;
+ decoder->src--;
+ return TRUE; /* expected '=', but encoding character found */
+ }
+ decoder->state++;
+ return FALSE;
+ }
+
+ /* Process error */
+ switch (decoder->state)
+ {
+ case 0:
+ case 1:
+ decoder->src--;
+ return TRUE; /* base64 character expected */
+ break;
+
+ case 2:
+ case 3:
+ if (decoder->src[-1] == '=')
+ {
+ decoder->error= 0; /* Not an error - it's a pad character */
+ decoder->mark++;
+ }
+ else
+ {
+ decoder->src--;
+ return TRUE; /* base64 character or '=' expected */
+ }
+ break;
+
+ default:
+ DBUG_ASSERT(0);
+ return TRUE; /* Wrong state, should not happen */
+ }
+
+ decoder->state++;
+ return FALSE;
+}
+
+
+/**
+ * Decode a base64 string
+ * The base64-encoded data in the range ['src','*end_ptr') will be
+ * decoded and stored starting at 'dst'. The decoding will stop
+ * after 'len' characters have been read from 'src', or when padding
+ * occurs in the base64-encoded data. In either case: if 'end_ptr' is
+ * non-null, '*end_ptr' will be set to point to the character after
+ * the last read character, even in the presence of error.
+ *
+ * Note: We require that 'dst' is pre-allocated to correct size.
+ *
+ * @param src Pointer to base64-encoded string
+ * @param len Length of string at 'src'
+ * @param dst Pointer to location where decoded data will be stored
+ * @param end_ptr Pointer to variable that will refer to the character
+ * after the end of the encoded data that were decoded.
+ * Can be NULL.
+ * @flags flags e.g. allow multiple chunks
+ * @return Number of bytes written at 'dst', or -1 in case of failure
+ */
+int64
+base64_decode(const char *src_base, size_t len,
+ void *dst, const char **end_ptr, int flags)
+{
+ char *d= (char*) dst;
+ MY_BASE64_DECODER decoder;
+
+ decoder.src= src_base;
+ decoder.end= src_base + len;
+ decoder.error= 0;
+ decoder.mark= 0;
+
+ for ( ; ; )
+ {
+ decoder.c= 0;
+ decoder.state= 0;
+
+ if (my_base64_decoder_getch(&decoder) ||
+ my_base64_decoder_getch(&decoder) ||
+ my_base64_decoder_getch(&decoder) ||
+ my_base64_decoder_getch(&decoder))
+ break;
+
+ *d++= (decoder.c >> 16) & 0xff;
+ *d++= (decoder.c >> 8) & 0xff;
+ *d++= (decoder.c >> 0) & 0xff;
+
+ if (decoder.mark)
+ {
+ d-= decoder.mark;
+ if (!(flags & MY_BASE64_DECODE_ALLOW_MULTIPLE_CHUNKS))
+ break;
+ decoder.mark= 0;
+ }
+ }
+
+ /* Return error if there are more non-space characters */
+ decoder.state= 0;
+ if (!my_base64_decoder_skip_spaces(&decoder))
+ decoder.error= 1;
+
+ if (end_ptr != NULL)
+ *end_ptr= decoder.src;
+
+ return decoder.error ? -1 : (int) (d - (char*) dst);
+}
+
+
+#else /* MAIN */
+
+#define require(b) { \
+ if (!(b)) { \
+ printf("Require failed at %s:%d\n", __FILE__, __LINE__); \
+ abort(); \
+ } \
+}
+
+
+int
+main(void)
+{
+ int i;
+ size_t j;
+ size_t k, l;
+ size_t dst_len;
+ size_t needed_length;
+ char * src;
+ char * s;
+ char * str;
+ char * dst;
+ const char *end_ptr;
+ size_t src_len;
+
+ for (i= 0; i <= 500; i++)
+ {
+ /* Create source data */
+ if (i == 500)
+ {
+#if (SIZEOF_VOIDP == 8)
+ printf("Test case for base64 max event length: 2119594243\n");
+ src_len= 2119594243;
+#else
+ printf("Test case for base64 max event length: 536870912\n");
+ src_len= 536870912;
+#endif
+ }
+ else
+ src_len= rand() % 1000 + 1;
+
+ src= (char *) malloc(src_len);
+ s= src;
+
+ require(src);
+ for (j= 0; j<src_len; j++)
+ {
+ char c= rand();
+ *s++= c;
+ }
+
+ /* Encode */
+ needed_length= base64_needed_encoded_length(src_len);
+ str= (char *) malloc(needed_length);
+ require(str);
+ for (k= 0; k < needed_length; k++)
+ str[k]= 0xff; /* Fill memory to check correct NUL termination */
+ require(base64_encode(src, src_len, str) == 0);
+ require(needed_length == strlen(str) + 1);
+
+ /* Decode */
+ dst= (char *) malloc(base64_needed_decoded_length(strlen(str)));
+ require(dst);
+ dst_len= base64_decode(str, strlen(str), dst, &end_ptr, 0);
+ require(dst_len == src_len);
+
+ if (memcmp(src, dst, src_len) != 0)
+ {
+ printf(" --------- src --------- --------- dst ---------\n");
+ for (k= 0; k<src_len; k+=8)
+ {
+ printf("%.4x ", (uint) k);
+ for (l=0; l<8 && k+l<src_len; l++)
+ {
+ unsigned char c= src[k+l];
+ printf("%.2x ", (unsigned)c);
+ }
+
+ printf(" ");
+
+ for (l=0; l<8 && k+l<dst_len; l++)
+ {
+ unsigned char c= dst[k+l];
+ printf("%.2x ", (unsigned)c);
+ }
+ printf("\n");
+ }
+ printf("src length: %.8x, dst length: %.8x\n",
+ (uint) src_len, (uint) dst_len);
+ require(0);
+ }
+ free(src);
+ free(str);
+ free(dst);
+ }
+ printf("Test succeeded.\n");
+ return 0;
+}
+
+#endif
diff --git a/mysql/mysys/charset-def.c b/mysql/mysys/charset-def.c
new file mode 100644
index 0000000..098f555
--- /dev/null
+++ b/mysql/mysys/charset-def.c
@@ -0,0 +1,409 @@
+/* Copyright (c) 2000, 2016, Oracle and/or its affiliates. All rights reserved.
+
+ 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; version 2 of the License.
+
+ 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include "mysys_priv.h"
+#include "my_sys.h"
+
+/*
+ Include all compiled character sets into the client
+ If a client don't want to use all of them, he can define his own
+ init_compiled_charsets() that only adds those that he wants
+*/
+
+#ifdef HAVE_UCA_COLLATIONS
+
+# ifdef HAVE_CHARSET_ucs2
+extern CHARSET_INFO my_charset_ucs2_german2_uca_ci;
+extern CHARSET_INFO my_charset_ucs2_icelandic_uca_ci;
+extern CHARSET_INFO my_charset_ucs2_latvian_uca_ci;
+extern CHARSET_INFO my_charset_ucs2_romanian_uca_ci;
+extern CHARSET_INFO my_charset_ucs2_slovenian_uca_ci;
+extern CHARSET_INFO my_charset_ucs2_polish_uca_ci;
+extern CHARSET_INFO my_charset_ucs2_estonian_uca_ci;
+extern CHARSET_INFO my_charset_ucs2_spanish_uca_ci;
+extern CHARSET_INFO my_charset_ucs2_swedish_uca_ci;
+extern CHARSET_INFO my_charset_ucs2_turkish_uca_ci;
+extern CHARSET_INFO my_charset_ucs2_czech_uca_ci;
+extern CHARSET_INFO my_charset_ucs2_danish_uca_ci;
+extern CHARSET_INFO my_charset_ucs2_lithuanian_uca_ci;
+extern CHARSET_INFO my_charset_ucs2_slovak_uca_ci;
+extern CHARSET_INFO my_charset_ucs2_spanish2_uca_ci;
+extern CHARSET_INFO my_charset_ucs2_roman_uca_ci;
+extern CHARSET_INFO my_charset_ucs2_persian_uca_ci;
+extern CHARSET_INFO my_charset_ucs2_esperanto_uca_ci;
+extern CHARSET_INFO my_charset_ucs2_hungarian_uca_ci;
+extern CHARSET_INFO my_charset_ucs2_croatian_uca_ci;
+extern CHARSET_INFO my_charset_ucs2_sinhala_uca_ci;
+extern CHARSET_INFO my_charset_ucs2_unicode_520_ci;
+extern CHARSET_INFO my_charset_ucs2_vietnamese_ci;
+# endif /* HAVE_CHARSET_ucs2 */
+
+
+# ifdef HAVE_CHARSET_utf32
+extern CHARSET_INFO my_charset_utf32_german2_uca_ci;
+extern CHARSET_INFO my_charset_utf32_icelandic_uca_ci;
+extern CHARSET_INFO my_charset_utf32_latvian_uca_ci;
+extern CHARSET_INFO my_charset_utf32_romanian_uca_ci;
+extern CHARSET_INFO my_charset_utf32_slovenian_uca_ci;
+extern CHARSET_INFO my_charset_utf32_polish_uca_ci;
+extern CHARSET_INFO my_charset_utf32_estonian_uca_ci;
+extern CHARSET_INFO my_charset_utf32_spanish_uca_ci;
+extern CHARSET_INFO my_charset_utf32_swedish_uca_ci;
+extern CHARSET_INFO my_charset_utf32_turkish_uca_ci;
+extern CHARSET_INFO my_charset_utf32_czech_uca_ci;
+extern CHARSET_INFO my_charset_utf32_danish_uca_ci;
+extern CHARSET_INFO my_charset_utf32_lithuanian_uca_ci;
+extern CHARSET_INFO my_charset_utf32_slovak_uca_ci;
+extern CHARSET_INFO my_charset_utf32_spanish2_uca_ci;
+extern CHARSET_INFO my_charset_utf32_roman_uca_ci;
+extern CHARSET_INFO my_charset_utf32_persian_uca_ci;
+extern CHARSET_INFO my_charset_utf32_esperanto_uca_ci;
+extern CHARSET_INFO my_charset_utf32_hungarian_uca_ci;
+extern CHARSET_INFO my_charset_utf32_croatian_uca_ci;
+extern CHARSET_INFO my_charset_utf32_sinhala_uca_ci;
+extern CHARSET_INFO my_charset_utf32_unicode_520_ci;
+extern CHARSET_INFO my_charset_utf32_vietnamese_ci;
+# endif /* HAVE_CHARSET_utf32 */
+
+
+# ifdef HAVE_CHARSET_utf16
+extern CHARSET_INFO my_charset_utf16_german2_uca_ci;
+extern CHARSET_INFO my_charset_utf16_icelandic_uca_ci;
+extern CHARSET_INFO my_charset_utf16_latvian_uca_ci;
+extern CHARSET_INFO my_charset_utf16_romanian_uca_ci;
+extern CHARSET_INFO my_charset_utf16_slovenian_uca_ci;
+extern CHARSET_INFO my_charset_utf16_polish_uca_ci;
+extern CHARSET_INFO my_charset_utf16_estonian_uca_ci;
+extern CHARSET_INFO my_charset_utf16_spanish_uca_ci;
+extern CHARSET_INFO my_charset_utf16_swedish_uca_ci;
+extern CHARSET_INFO my_charset_utf16_turkish_uca_ci;
+extern CHARSET_INFO my_charset_utf16_czech_uca_ci;
+extern CHARSET_INFO my_charset_utf16_danish_uca_ci;
+extern CHARSET_INFO my_charset_utf16_lithuanian_uca_ci;
+extern CHARSET_INFO my_charset_utf16_slovak_uca_ci;
+extern CHARSET_INFO my_charset_utf16_spanish2_uca_ci;
+extern CHARSET_INFO my_charset_utf16_roman_uca_ci;
+extern CHARSET_INFO my_charset_utf16_persian_uca_ci;
+extern CHARSET_INFO my_charset_utf16_esperanto_uca_ci;
+extern CHARSET_INFO my_charset_utf16_hungarian_uca_ci;
+extern CHARSET_INFO my_charset_utf16_croatian_uca_ci;
+extern CHARSET_INFO my_charset_utf16_sinhala_uca_ci;
+extern CHARSET_INFO my_charset_utf16_unicode_520_ci;
+extern CHARSET_INFO my_charset_utf16_vietnamese_ci;
+# endif /* HAVE_CHARSET_utf16 */
+
+
+# ifdef HAVE_CHARSET_utf8
+extern CHARSET_INFO my_charset_utf8_german2_uca_ci;
+extern CHARSET_INFO my_charset_utf8_icelandic_uca_ci;
+extern CHARSET_INFO my_charset_utf8_latvian_uca_ci;
+extern CHARSET_INFO my_charset_utf8_romanian_uca_ci;
+extern CHARSET_INFO my_charset_utf8_slovenian_uca_ci;
+extern CHARSET_INFO my_charset_utf8_polish_uca_ci;
+extern CHARSET_INFO my_charset_utf8_estonian_uca_ci;
+extern CHARSET_INFO my_charset_utf8_spanish_uca_ci;
+extern CHARSET_INFO my_charset_utf8_swedish_uca_ci;
+extern CHARSET_INFO my_charset_utf8_turkish_uca_ci;
+extern CHARSET_INFO my_charset_utf8_czech_uca_ci;
+extern CHARSET_INFO my_charset_utf8_danish_uca_ci;
+extern CHARSET_INFO my_charset_utf8_lithuanian_uca_ci;
+extern CHARSET_INFO my_charset_utf8_slovak_uca_ci;
+extern CHARSET_INFO my_charset_utf8_spanish2_uca_ci;
+extern CHARSET_INFO my_charset_utf8_roman_uca_ci;
+extern CHARSET_INFO my_charset_utf8_persian_uca_ci;
+extern CHARSET_INFO my_charset_utf8_esperanto_uca_ci;
+extern CHARSET_INFO my_charset_utf8_hungarian_uca_ci;
+extern CHARSET_INFO my_charset_utf8_croatian_uca_ci;
+extern CHARSET_INFO my_charset_utf8_sinhala_uca_ci;
+extern CHARSET_INFO my_charset_utf8_unicode_520_ci;
+extern CHARSET_INFO my_charset_utf8_vietnamese_ci;
+# ifdef HAVE_UTF8_GENERAL_CS
+extern CHARSET_INFO my_charset_utf8_general_cs;
+# endif
+# endif
+
+# ifdef HAVE_CHARSET_utf8mb4
+extern CHARSET_INFO my_charset_utf8mb4_german2_uca_ci;
+extern CHARSET_INFO my_charset_utf8mb4_icelandic_uca_ci;
+extern CHARSET_INFO my_charset_utf8mb4_latvian_uca_ci;
+extern CHARSET_INFO my_charset_utf8mb4_romanian_uca_ci;
+extern CHARSET_INFO my_charset_utf8mb4_slovenian_uca_ci;
+extern CHARSET_INFO my_charset_utf8mb4_polish_uca_ci;
+extern CHARSET_INFO my_charset_utf8mb4_estonian_uca_ci;
+extern CHARSET_INFO my_charset_utf8mb4_spanish_uca_ci;
+extern CHARSET_INFO my_charset_utf8mb4_swedish_uca_ci;
+extern CHARSET_INFO my_charset_utf8mb4_turkish_uca_ci;
+extern CHARSET_INFO my_charset_utf8mb4_czech_uca_ci;
+extern CHARSET_INFO my_charset_utf8mb4_danish_uca_ci;
+extern CHARSET_INFO my_charset_utf8mb4_lithuanian_uca_ci;
+extern CHARSET_INFO my_charset_utf8mb4_slovak_uca_ci;
+extern CHARSET_INFO my_charset_utf8mb4_spanish2_uca_ci;
+extern CHARSET_INFO my_charset_utf8mb4_roman_uca_ci;
+extern CHARSET_INFO my_charset_utf8mb4_persian_uca_ci;
+extern CHARSET_INFO my_charset_utf8mb4_esperanto_uca_ci;
+extern CHARSET_INFO my_charset_utf8mb4_hungarian_uca_ci;
+extern CHARSET_INFO my_charset_utf8mb4_croatian_uca_ci;
+extern CHARSET_INFO my_charset_utf8mb4_sinhala_uca_ci;
+extern CHARSET_INFO my_charset_utf8mb4_unicode_520_ci;
+extern CHARSET_INFO my_charset_utf8mb4_vietnamese_ci;
+# endif /* HAVE_CHARSET_utf8mb4 */
+
+# ifdef HAVE_CHARSET_gb18030
+extern CHARSET_INFO my_charset_gb18030_unicode_520_ci;
+# endif /* HAVE_CHARSET_gb18030 */
+#endif /* HAVE_UCA_COLLATIONS */
+
+my_bool init_compiled_charsets(myf flags MY_ATTRIBUTE((unused)))
+{
+ CHARSET_INFO *cs;
+
+ add_compiled_collation(&my_charset_bin);
+
+ add_compiled_collation(&my_charset_latin1);
+ add_compiled_collation(&my_charset_latin1_bin);
+ add_compiled_collation(&my_charset_latin1_german2_ci);
+
+#ifdef HAVE_CHARSET_big5
+ add_compiled_collation(&my_charset_big5_chinese_ci);
+ add_compiled_collation(&my_charset_big5_bin);
+#endif
+
+#ifdef HAVE_CHARSET_cp1250
+ add_compiled_collation(&my_charset_cp1250_czech_ci);
+#endif
+
+#ifdef HAVE_CHARSET_cp932
+ add_compiled_collation(&my_charset_cp932_japanese_ci);
+ add_compiled_collation(&my_charset_cp932_bin);
+#endif
+
+#ifdef HAVE_CHARSET_latin2
+ add_compiled_collation(&my_charset_latin2_czech_ci);
+#endif
+
+#ifdef HAVE_CHARSET_eucjpms
+ add_compiled_collation(&my_charset_eucjpms_japanese_ci);
+ add_compiled_collation(&my_charset_eucjpms_bin);
+#endif
+
+#ifdef HAVE_CHARSET_euckr
+ add_compiled_collation(&my_charset_euckr_korean_ci);
+ add_compiled_collation(&my_charset_euckr_bin);
+#endif
+
+#ifdef HAVE_CHARSET_gb2312
+ add_compiled_collation(&my_charset_gb2312_chinese_ci);
+ add_compiled_collation(&my_charset_gb2312_bin);
+#endif
+
+#ifdef HAVE_CHARSET_gbk
+ add_compiled_collation(&my_charset_gbk_chinese_ci);
+ add_compiled_collation(&my_charset_gbk_bin);
+#endif
+
+#ifdef HAVE_CHARSET_gb18030
+# ifdef HAVE_UCA_COLLATIONS
+ add_compiled_collation(&my_charset_gb18030_unicode_520_ci);
+# endif /* HAVE_UCA_COLLATIONS */
+ add_compiled_collation(&my_charset_gb18030_chinese_ci);
+ add_compiled_collation(&my_charset_gb18030_bin);
+#endif /* HAVE_CHARSET_gb18030 */
+
+#ifdef HAVE_CHARSET_sjis
+ add_compiled_collation(&my_charset_sjis_japanese_ci);
+ add_compiled_collation(&my_charset_sjis_bin);
+#endif
+
+#ifdef HAVE_CHARSET_tis620
+ add_compiled_collation(&my_charset_tis620_thai_ci);
+ add_compiled_collation(&my_charset_tis620_bin);
+#endif
+
+#ifdef HAVE_CHARSET_ucs2
+ add_compiled_collation(&my_charset_ucs2_general_ci);
+ add_compiled_collation(&my_charset_ucs2_bin);
+ add_compiled_collation(&my_charset_ucs2_general_mysql500_ci);
+#ifdef HAVE_UCA_COLLATIONS
+ add_compiled_collation(&my_charset_ucs2_unicode_ci);
+ add_compiled_collation(&my_charset_ucs2_german2_uca_ci);
+ add_compiled_collation(&my_charset_ucs2_icelandic_uca_ci);
+ add_compiled_collation(&my_charset_ucs2_latvian_uca_ci);
+ add_compiled_collation(&my_charset_ucs2_romanian_uca_ci);
+ add_compiled_collation(&my_charset_ucs2_slovenian_uca_ci);
+ add_compiled_collation(&my_charset_ucs2_polish_uca_ci);
+ add_compiled_collation(&my_charset_ucs2_estonian_uca_ci);
+ add_compiled_collation(&my_charset_ucs2_spanish_uca_ci);
+ add_compiled_collation(&my_charset_ucs2_swedish_uca_ci);
+ add_compiled_collation(&my_charset_ucs2_turkish_uca_ci);
+ add_compiled_collation(&my_charset_ucs2_czech_uca_ci);
+ add_compiled_collation(&my_charset_ucs2_danish_uca_ci);
+ add_compiled_collation(&my_charset_ucs2_lithuanian_uca_ci);
+ add_compiled_collation(&my_charset_ucs2_slovak_uca_ci);
+ add_compiled_collation(&my_charset_ucs2_spanish2_uca_ci);
+ add_compiled_collation(&my_charset_ucs2_roman_uca_ci);
+ add_compiled_collation(&my_charset_ucs2_persian_uca_ci);
+ add_compiled_collation(&my_charset_ucs2_esperanto_uca_ci);
+ add_compiled_collation(&my_charset_ucs2_hungarian_uca_ci);
+ add_compiled_collation(&my_charset_ucs2_croatian_uca_ci);
+ add_compiled_collation(&my_charset_ucs2_sinhala_uca_ci);
+ add_compiled_collation(&my_charset_ucs2_unicode_520_ci);
+ add_compiled_collation(&my_charset_ucs2_vietnamese_ci);
+#endif
+#endif
+
+#ifdef HAVE_CHARSET_ujis
+ add_compiled_collation(&my_charset_ujis_japanese_ci);
+ add_compiled_collation(&my_charset_ujis_bin);
+#endif
+
+#ifdef HAVE_CHARSET_utf8
+ add_compiled_collation(&my_charset_utf8_general_ci);
+ add_compiled_collation(&my_charset_utf8_bin);
+ add_compiled_collation(&my_charset_utf8_general_mysql500_ci);
+#ifdef HAVE_UTF8_GENERAL_CS
+ add_compiled_collation(&my_charset_utf8_general_cs);
+#endif
+#ifdef HAVE_UCA_COLLATIONS
+ add_compiled_collation(&my_charset_utf8_unicode_ci);
+ add_compiled_collation(&my_charset_utf8_german2_uca_ci);
+ add_compiled_collation(&my_charset_utf8_icelandic_uca_ci);
+ add_compiled_collation(&my_charset_utf8_latvian_uca_ci);
+ add_compiled_collation(&my_charset_utf8_romanian_uca_ci);
+ add_compiled_collation(&my_charset_utf8_slovenian_uca_ci);
+ add_compiled_collation(&my_charset_utf8_polish_uca_ci);
+ add_compiled_collation(&my_charset_utf8_estonian_uca_ci);
+ add_compiled_collation(&my_charset_utf8_spanish_uca_ci);
+ add_compiled_collation(&my_charset_utf8_swedish_uca_ci);
+ add_compiled_collation(&my_charset_utf8_turkish_uca_ci);
+ add_compiled_collation(&my_charset_utf8_czech_uca_ci);
+ add_compiled_collation(&my_charset_utf8_danish_uca_ci);
+ add_compiled_collation(&my_charset_utf8_lithuanian_uca_ci);
+ add_compiled_collation(&my_charset_utf8_slovak_uca_ci);
+ add_compiled_collation(&my_charset_utf8_spanish2_uca_ci);
+ add_compiled_collation(&my_charset_utf8_roman_uca_ci);
+ add_compiled_collation(&my_charset_utf8_persian_uca_ci);
+ add_compiled_collation(&my_charset_utf8_esperanto_uca_ci);
+ add_compiled_collation(&my_charset_utf8_hungarian_uca_ci);
+ add_compiled_collation(&my_charset_utf8_croatian_uca_ci);
+ add_compiled_collation(&my_charset_utf8_sinhala_uca_ci);
+ add_compiled_collation(&my_charset_utf8_unicode_520_ci);
+ add_compiled_collation(&my_charset_utf8_vietnamese_ci);
+#endif
+#endif /* HAVE_CHARSET_utf8 */
+
+
+#ifdef HAVE_CHARSET_utf8mb4
+ add_compiled_collation(&my_charset_utf8mb4_general_ci);
+ add_compiled_collation(&my_charset_utf8mb4_bin);
+#ifdef HAVE_UCA_COLLATIONS
+ add_compiled_collation(&my_charset_utf8mb4_unicode_ci);
+ add_compiled_collation(&my_charset_utf8mb4_german2_uca_ci);
+ add_compiled_collation(&my_charset_utf8mb4_icelandic_uca_ci);
+ add_compiled_collation(&my_charset_utf8mb4_latvian_uca_ci);
+ add_compiled_collation(&my_charset_utf8mb4_romanian_uca_ci);
+ add_compiled_collation(&my_charset_utf8mb4_slovenian_uca_ci);
+ add_compiled_collation(&my_charset_utf8mb4_polish_uca_ci);
+ add_compiled_collation(&my_charset_utf8mb4_estonian_uca_ci);
+ add_compiled_collation(&my_charset_utf8mb4_spanish_uca_ci);
+ add_compiled_collation(&my_charset_utf8mb4_swedish_uca_ci);
+ add_compiled_collation(&my_charset_utf8mb4_turkish_uca_ci);
+ add_compiled_collation(&my_charset_utf8mb4_czech_uca_ci);
+ add_compiled_collation(&my_charset_utf8mb4_danish_uca_ci);
+ add_compiled_collation(&my_charset_utf8mb4_lithuanian_uca_ci);
+ add_compiled_collation(&my_charset_utf8mb4_slovak_uca_ci);
+ add_compiled_collation(&my_charset_utf8mb4_spanish2_uca_ci);
+ add_compiled_collation(&my_charset_utf8mb4_roman_uca_ci);
+ add_compiled_collation(&my_charset_utf8mb4_persian_uca_ci);
+ add_compiled_collation(&my_charset_utf8mb4_esperanto_uca_ci);
+ add_compiled_collation(&my_charset_utf8mb4_hungarian_uca_ci);
+ add_compiled_collation(&my_charset_utf8mb4_croatian_uca_ci);
+ add_compiled_collation(&my_charset_utf8mb4_sinhala_uca_ci);
+ add_compiled_collation(&my_charset_utf8mb4_unicode_520_ci);
+ add_compiled_collation(&my_charset_utf8mb4_vietnamese_ci);
+#endif /* HAVE_UCA_COLLATIONS */
+#endif /* HAVE_CHARSET_utf8mb4 */
+
+
+#ifdef HAVE_CHARSET_utf16
+ add_compiled_collation(&my_charset_utf16_general_ci);
+ add_compiled_collation(&my_charset_utf16_bin);
+ add_compiled_collation(&my_charset_utf16le_general_ci);
+ add_compiled_collation(&my_charset_utf16le_bin);
+#ifdef HAVE_UCA_COLLATIONS
+ add_compiled_collation(&my_charset_utf16_unicode_ci);
+ add_compiled_collation(&my_charset_utf16_german2_uca_ci);
+ add_compiled_collation(&my_charset_utf16_icelandic_uca_ci);
+ add_compiled_collation(&my_charset_utf16_latvian_uca_ci);
+ add_compiled_collation(&my_charset_utf16_romanian_uca_ci);
+ add_compiled_collation(&my_charset_utf16_slovenian_uca_ci);
+ add_compiled_collation(&my_charset_utf16_polish_uca_ci);
+ add_compiled_collation(&my_charset_utf16_estonian_uca_ci);
+ add_compiled_collation(&my_charset_utf16_spanish_uca_ci);
+ add_compiled_collation(&my_charset_utf16_swedish_uca_ci);
+ add_compiled_collation(&my_charset_utf16_turkish_uca_ci);
+ add_compiled_collation(&my_charset_utf16_czech_uca_ci);
+ add_compiled_collation(&my_charset_utf16_danish_uca_ci);
+ add_compiled_collation(&my_charset_utf16_lithuanian_uca_ci);
+ add_compiled_collation(&my_charset_utf16_slovak_uca_ci);
+ add_compiled_collation(&my_charset_utf16_spanish2_uca_ci);
+ add_compiled_collation(&my_charset_utf16_roman_uca_ci);
+ add_compiled_collation(&my_charset_utf16_persian_uca_ci);
+ add_compiled_collation(&my_charset_utf16_esperanto_uca_ci);
+ add_compiled_collation(&my_charset_utf16_hungarian_uca_ci);
+ add_compiled_collation(&my_charset_utf16_croatian_uca_ci);
+ add_compiled_collation(&my_charset_utf16_sinhala_uca_ci);
+ add_compiled_collation(&my_charset_utf16_unicode_520_ci);
+ add_compiled_collation(&my_charset_utf16_vietnamese_ci);
+#endif /* HAVE_UCA_COLLATIOINS */
+#endif /* HAVE_CHARSET_utf16 */
+
+
+#ifdef HAVE_CHARSET_utf32
+ add_compiled_collation(&my_charset_utf32_general_ci);
+ add_compiled_collation(&my_charset_utf32_bin);
+#ifdef HAVE_UCA_COLLATIONS
+ add_compiled_collation(&my_charset_utf32_unicode_ci);
+ add_compiled_collation(&my_charset_utf32_german2_uca_ci);
+ add_compiled_collation(&my_charset_utf32_icelandic_uca_ci);
+ add_compiled_collation(&my_charset_utf32_latvian_uca_ci);
+ add_compiled_collation(&my_charset_utf32_romanian_uca_ci);
+ add_compiled_collation(&my_charset_utf32_slovenian_uca_ci);
+ add_compiled_collation(&my_charset_utf32_polish_uca_ci);
+ add_compiled_collation(&my_charset_utf32_estonian_uca_ci);
+ add_compiled_collation(&my_charset_utf32_spanish_uca_ci);
+ add_compiled_collation(&my_charset_utf32_swedish_uca_ci);
+ add_compiled_collation(&my_charset_utf32_turkish_uca_ci);
+ add_compiled_collation(&my_charset_utf32_czech_uca_ci);
+ add_compiled_collation(&my_charset_utf32_danish_uca_ci);
+ add_compiled_collation(&my_charset_utf32_lithuanian_uca_ci);
+ add_compiled_collation(&my_charset_utf32_slovak_uca_ci);
+ add_compiled_collation(&my_charset_utf32_spanish2_uca_ci);
+ add_compiled_collation(&my_charset_utf32_roman_uca_ci);
+ add_compiled_collation(&my_charset_utf32_persian_uca_ci);
+ add_compiled_collation(&my_charset_utf32_esperanto_uca_ci);
+ add_compiled_collation(&my_charset_utf32_hungarian_uca_ci);
+ add_compiled_collation(&my_charset_utf32_croatian_uca_ci);
+ add_compiled_collation(&my_charset_utf32_sinhala_uca_ci);
+ add_compiled_collation(&my_charset_utf32_unicode_520_ci);
+ add_compiled_collation(&my_charset_utf32_vietnamese_ci);
+#endif /* HAVE_UCA_COLLATIONS */
+#endif /* HAVE_CHARSET_utf32 */
+
+ /* Copy compiled charsets */
+ for (cs=compiled_charsets; cs->name; cs++)
+ add_compiled_collation(cs);
+
+ return FALSE;
+}
diff --git a/mysql/mysys/charset.c b/mysql/mysys/charset.c
new file mode 100644
index 0000000..0ac55fc
--- /dev/null
+++ b/mysql/mysys/charset.c
@@ -0,0 +1,968 @@
+/* Copyright (c) 2000, 2016, Oracle and/or its affiliates. All rights reserved.
+
+ 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; version 2 of the License.
+
+ 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include "mysys_priv.h"
+#include "my_sys.h"
+#include "mysys_err.h"
+#include <m_ctype.h>
+#include <m_string.h>
+#include <my_dir.h>
+#include <my_xml.h>
+#include "mysql/psi/mysql_file.h"
+#include "sql_chars.h"
+
+/*
+ The code below implements this functionality:
+
+ - Initializing charset related structures
+ - Loading dynamic charsets
+ - Searching for a proper CHARSET_INFO
+ using charset name, collation name or collation ID
+ - Setting server default character set
+*/
+
+my_bool my_charset_same(const CHARSET_INFO *cs1, const CHARSET_INFO *cs2)
+{
+ return ((cs1 == cs2) || !strcmp(cs1->csname,cs2->csname));
+}
+
+
+static uint
+get_collation_number_internal(const char *name)
+{
+ CHARSET_INFO **cs;
+ for (cs= all_charsets;
+ cs < all_charsets + array_elements(all_charsets);
+ cs++)
+ {
+ if ( cs[0] && cs[0]->name &&
+ !my_strcasecmp(&my_charset_latin1, cs[0]->name, name))
+ return cs[0]->number;
+ }
+ return 0;
+}
+
+
+static void simple_cs_init_functions(CHARSET_INFO *cs)
+{
+ if (cs->state & MY_CS_BINSORT)
+ cs->coll= &my_collation_8bit_bin_handler;
+ else
+ cs->coll= &my_collation_8bit_simple_ci_handler;
+
+ cs->cset= &my_charset_8bit_handler;
+}
+
+
+
+static int cs_copy_data(CHARSET_INFO *to, CHARSET_INFO *from)
+{
+ to->number= from->number ? from->number : to->number;
+
+ if (from->csname)
+ if (!(to->csname= my_once_strdup(from->csname,MYF(MY_WME))))
+ goto err;
+
+ if (from->name)
+ if (!(to->name= my_once_strdup(from->name,MYF(MY_WME))))
+ goto err;
+
+ if (from->comment)
+ if (!(to->comment= my_once_strdup(from->comment,MYF(MY_WME))))
+ goto err;
+
+ if (from->ctype)
+ {
+ if (!(to->ctype= (uchar*) my_once_memdup((char*) from->ctype,
+ MY_CS_CTYPE_TABLE_SIZE,
+ MYF(MY_WME))))
+ goto err;
+ if (init_state_maps(to))
+ goto err;
+ }
+ if (from->to_lower)
+ if (!(to->to_lower= (uchar*) my_once_memdup((char*) from->to_lower,
+ MY_CS_TO_LOWER_TABLE_SIZE,
+ MYF(MY_WME))))
+ goto err;
+
+ if (from->to_upper)
+ if (!(to->to_upper= (uchar*) my_once_memdup((char*) from->to_upper,
+ MY_CS_TO_UPPER_TABLE_SIZE,
+ MYF(MY_WME))))
+ goto err;
+ if (from->sort_order)
+ {
+ if (!(to->sort_order= (uchar*) my_once_memdup((char*) from->sort_order,
+ MY_CS_SORT_ORDER_TABLE_SIZE,
+ MYF(MY_WME))))
+ goto err;
+
+ }
+ if (from->tab_to_uni)
+ {
+ uint sz= MY_CS_TO_UNI_TABLE_SIZE*sizeof(uint16);
+ if (!(to->tab_to_uni= (uint16*) my_once_memdup((char*)from->tab_to_uni,
+ sz, MYF(MY_WME))))
+ goto err;
+ }
+ if (from->tailoring)
+ if (!(to->tailoring= my_once_strdup(from->tailoring,MYF(MY_WME))))
+ goto err;
+
+ return 0;
+
+err:
+ return 1;
+}
+
+
+
+static my_bool simple_cs_is_full(CHARSET_INFO *cs)
+{
+ return ((cs->csname && cs->tab_to_uni && cs->ctype && cs->to_upper &&
+ cs->to_lower) &&
+ (cs->number && cs->name &&
+ (cs->sort_order || (cs->state & MY_CS_BINSORT) )));
+}
+
+
+static void
+copy_uca_collation(CHARSET_INFO *to, CHARSET_INFO *from)
+{
+ to->cset= from->cset;
+ to->coll= from->coll;
+ to->strxfrm_multiply= from->strxfrm_multiply;
+ to->min_sort_char= from->min_sort_char;
+ to->max_sort_char= from->max_sort_char;
+ to->mbminlen= from->mbminlen;
+ to->mbmaxlen= from->mbmaxlen;
+ to->caseup_multiply= from->caseup_multiply;
+ to->casedn_multiply= from->casedn_multiply;
+ to->state|= MY_CS_AVAILABLE | MY_CS_LOADED |
+ MY_CS_STRNXFRM | MY_CS_UNICODE;
+}
+
+
+static int add_collation(CHARSET_INFO *cs)
+{
+ if (cs->name && (cs->number ||
+ (cs->number=get_collation_number_internal(cs->name))) &&
+ cs->number < array_elements(all_charsets))
+ {
+ if (!all_charsets[cs->number])
+ {
+ if (!(all_charsets[cs->number]=
+ (CHARSET_INFO*) my_once_alloc(sizeof(CHARSET_INFO),MYF(0))))
+ return MY_XML_ERROR;
+ memset(all_charsets[cs->number], 0, sizeof(CHARSET_INFO));
+ }
+
+ if (cs->primary_number == cs->number)
+ cs->state |= MY_CS_PRIMARY;
+
+ if (cs->binary_number == cs->number)
+ cs->state |= MY_CS_BINSORT;
+
+ all_charsets[cs->number]->state|= cs->state;
+
+ if (!(all_charsets[cs->number]->state & MY_CS_COMPILED))
+ {
+ CHARSET_INFO *newcs= all_charsets[cs->number];
+ if (cs_copy_data(all_charsets[cs->number],cs))
+ return MY_XML_ERROR;
+
+ newcs->caseup_multiply= newcs->casedn_multiply= 1;
+ newcs->levels_for_compare= 1;
+ newcs->levels_for_order= 1;
+
+ if (!strcmp(cs->csname,"ucs2") )
+ {
+#if defined(HAVE_CHARSET_ucs2) && defined(HAVE_UCA_COLLATIONS)
+ copy_uca_collation(newcs, &my_charset_ucs2_unicode_ci);
+ newcs->state|= MY_CS_AVAILABLE | MY_CS_LOADED | MY_CS_NONASCII;
+#endif
+ }
+ else if (!strcmp(cs->csname, "utf8") || !strcmp(cs->csname, "utf8mb3"))
+ {
+#if defined (HAVE_CHARSET_utf8) && defined(HAVE_UCA_COLLATIONS)
+ copy_uca_collation(newcs, &my_charset_utf8_unicode_ci);
+ newcs->ctype= my_charset_utf8_unicode_ci.ctype;
+ if (init_state_maps(newcs))
+ return MY_XML_ERROR;
+#endif
+ }
+ else if (!strcmp(cs->csname, "utf8mb4"))
+ {
+#if defined (HAVE_CHARSET_utf8mb4) && defined(HAVE_UCA_COLLATIONS)
+ copy_uca_collation(newcs, &my_charset_utf8mb4_unicode_ci);
+ newcs->ctype= my_charset_utf8mb4_unicode_ci.ctype;
+ newcs->state|= MY_CS_AVAILABLE | MY_CS_LOADED;
+#endif
+ }
+ else if (!strcmp(cs->csname, "utf16"))
+ {
+#if defined (HAVE_CHARSET_utf16) && defined(HAVE_UCA_COLLATIONS)
+ copy_uca_collation(newcs, &my_charset_utf16_unicode_ci);
+ newcs->state|= MY_CS_AVAILABLE | MY_CS_LOADED | MY_CS_NONASCII;
+#endif
+ }
+ else if (!strcmp(cs->csname, "utf32"))
+ {
+#if defined (HAVE_CHARSET_utf32) && defined(HAVE_UCA_COLLATIONS)
+ copy_uca_collation(newcs, &my_charset_utf32_unicode_ci);
+ newcs->state|= MY_CS_AVAILABLE | MY_CS_LOADED | MY_CS_NONASCII;
+#endif
+ }
+ else
+ {
+ const uchar *sort_order= all_charsets[cs->number]->sort_order;
+ simple_cs_init_functions(all_charsets[cs->number]);
+ newcs->mbminlen= 1;
+ newcs->mbmaxlen= 1;
+ if (simple_cs_is_full(all_charsets[cs->number]))
+ {
+ all_charsets[cs->number]->state |= MY_CS_LOADED;
+ }
+ all_charsets[cs->number]->state|= MY_CS_AVAILABLE;
+
+ /*
+ Check if case sensitive sort order: A < a < B.
+ We need MY_CS_FLAG for regex library, and for
+ case sensitivity flag for 5.0 client protocol,
+ to support isCaseSensitive() method in JDBC driver
+ */
+ if (sort_order && sort_order['A'] < sort_order['a'] &&
+ sort_order['a'] < sort_order['B'])
+ all_charsets[cs->number]->state|= MY_CS_CSSORT;
+
+ if (my_charset_is_8bit_pure_ascii(all_charsets[cs->number]))
+ all_charsets[cs->number]->state|= MY_CS_PUREASCII;
+ if (!my_charset_is_ascii_compatible(cs))
+ all_charsets[cs->number]->state|= MY_CS_NONASCII;
+ }
+ }
+ else
+ {
+ /*
+ We need the below to make get_charset_name()
+ and get_charset_number() working even if a
+ character set has not been really incompiled.
+ The above functions are used for example
+ in error message compiler extra/comp_err.c.
+ If a character set was compiled, this information
+ will get lost and overwritten in add_compiled_collation().
+ */
+ CHARSET_INFO *dst= all_charsets[cs->number];
+ dst->number= cs->number;
+ if (cs->comment)
+ if (!(dst->comment= my_once_strdup(cs->comment,MYF(MY_WME))))
+ return MY_XML_ERROR;
+ if (cs->csname)
+ if (!(dst->csname= my_once_strdup(cs->csname,MYF(MY_WME))))
+ return MY_XML_ERROR;
+ if (cs->name)
+ if (!(dst->name= my_once_strdup(cs->name,MYF(MY_WME))))
+ return MY_XML_ERROR;
+ }
+ cs->number= 0;
+ cs->primary_number= 0;
+ cs->binary_number= 0;
+ cs->name= NULL;
+ cs->state= 0;
+ cs->sort_order= NULL;
+ cs->state= 0;
+ }
+ return MY_XML_OK;
+}
+
+
+/**
+ Report character set initialization errors and warnings.
+ Be silent by default: no warnings on the client side.
+*/
+static void
+default_reporter(enum loglevel level MY_ATTRIBUTE ((unused)),
+ const char *format MY_ATTRIBUTE ((unused)),
+ ...)
+{
+}
+my_error_reporter my_charset_error_reporter= default_reporter;
+
+
+/**
+ Wrappers for memory functions my_malloc (and friends)
+ with C-compatbile API without extra "myf" argument.
+*/
+static void *
+my_once_alloc_c(size_t size)
+{ return my_once_alloc(size, MYF(MY_WME)); }
+
+
+static void *
+my_malloc_c(size_t size)
+{ return my_malloc(key_memory_charset_loader, size, MYF(MY_WME)); }
+
+
+static void *
+my_realloc_c(void *old, size_t size)
+{ return my_realloc(key_memory_charset_loader,
+ old, size, MYF(MY_WME)); }
+
+static void
+my_free_c(void *ptr)
+{
+ my_free(ptr);
+}
+
+/**
+ Initialize character set loader to use mysys memory management functions.
+ @param loader Loader to initialize
+*/
+void
+my_charset_loader_init_mysys(MY_CHARSET_LOADER *loader)
+{
+ loader->error[0]= '\0';
+ loader->once_alloc= my_once_alloc_c;
+ loader->mem_malloc= my_malloc_c;
+ loader->mem_realloc= my_realloc_c;
+ loader->mem_free= my_free_c;
+ loader->reporter= my_charset_error_reporter;
+ loader->add_collation= add_collation;
+}
+
+
+#define MY_MAX_ALLOWED_BUF 1024*1024
+#define MY_CHARSET_INDEX "Index.xml"
+
+const char *charsets_dir= NULL;
+
+
+static my_bool
+my_read_charset_file(MY_CHARSET_LOADER *loader,
+ const char *filename,
+ myf myflags)
+{
+ uchar *buf;
+ int fd;
+ size_t len, tmp_len;
+ MY_STAT stat_info;
+
+ if (!my_stat(filename, &stat_info, MYF(myflags)) ||
+ ((len= (uint)stat_info.st_size) > MY_MAX_ALLOWED_BUF) ||
+ !(buf= (uchar*) my_malloc(key_memory_charset_file,
+ len,myflags)))
+ return TRUE;
+
+ if ((fd= mysql_file_open(key_file_charset, filename, O_RDONLY, myflags)) < 0)
+ goto error;
+ tmp_len= mysql_file_read(fd, buf, len, myflags);
+ mysql_file_close(fd, myflags);
+ if (tmp_len != len)
+ goto error;
+
+ if (my_parse_charset_xml(loader, (char *) buf, len))
+ {
+ my_printf_error(EE_UNKNOWN_CHARSET, "Error while parsing '%s': %s\n",
+ MYF(0), filename, loader->error);
+ goto error;
+ }
+
+ my_free(buf);
+ return FALSE;
+
+error:
+ my_free(buf);
+ return TRUE;
+}
+
+
+char *get_charsets_dir(char *buf)
+{
+ const char *sharedir= SHAREDIR;
+ char *res;
+ DBUG_ENTER("get_charsets_dir");
+
+ if (charsets_dir != NULL)
+ strmake(buf, charsets_dir, FN_REFLEN-1);
+ else
+ {
+ if (test_if_hard_path(sharedir) ||
+ is_prefix(sharedir, DEFAULT_CHARSET_HOME))
+ strxmov(buf, sharedir, "/", CHARSET_DIR, NullS);
+ else
+ strxmov(buf, DEFAULT_CHARSET_HOME, "/", sharedir, "/", CHARSET_DIR,
+ NullS);
+ }
+ res= convert_dirname(buf,buf,NullS);
+ DBUG_PRINT("info",("charsets dir: '%s'", buf));
+ DBUG_RETURN(res);
+}
+
+CHARSET_INFO *all_charsets[MY_ALL_CHARSETS_SIZE]={NULL};
+CHARSET_INFO *default_charset_info = &my_charset_latin1;
+
+void add_compiled_collation(CHARSET_INFO *cs)
+{
+ DBUG_ASSERT(cs->number < array_elements(all_charsets));
+ all_charsets[cs->number]= cs;
+ cs->state|= MY_CS_AVAILABLE;
+}
+
+
+static my_thread_once_t charsets_initialized= MY_THREAD_ONCE_INIT;
+static my_thread_once_t charsets_template= MY_THREAD_ONCE_INIT;
+
+static void init_available_charsets(void)
+{
+ char fname[FN_REFLEN + sizeof(MY_CHARSET_INDEX)];
+ MY_CHARSET_LOADER loader;
+
+ memset(&all_charsets, 0, sizeof(all_charsets));
+ init_compiled_charsets(MYF(0));
+
+ /* Copy compiled charsets */
+
+ my_charset_loader_init_mysys(&loader);
+ my_stpcpy(get_charsets_dir(fname), MY_CHARSET_INDEX);
+ my_read_charset_file(&loader, fname, MYF(0));
+}
+
+
+void free_charsets(void)
+{
+ charsets_initialized= charsets_template;
+}
+
+
+static const char*
+get_collation_name_alias(const char *name, char *buf, size_t bufsize)
+{
+ if (!native_strncasecmp(name, "utf8mb3_", 8))
+ {
+ my_snprintf(buf, bufsize, "utf8_%s", name + 8);
+ return buf;
+ }
+ return NULL;
+}
+
+
+uint get_collation_number(const char *name)
+{
+ uint id;
+ char alias[64];
+ my_thread_once(&charsets_initialized, init_available_charsets);
+ if ((id= get_collation_number_internal(name)))
+ return id;
+ if ((name= get_collation_name_alias(name, alias, sizeof(alias))))
+ return get_collation_number_internal(name);
+ return 0;
+}
+
+
+static uint
+get_charset_number_internal(const char *charset_name, uint cs_flags)
+{
+ CHARSET_INFO **cs;
+
+ for (cs= all_charsets;
+ cs < all_charsets + array_elements(all_charsets);
+ cs++)
+ {
+ if ( cs[0] && cs[0]->csname && (cs[0]->state & cs_flags) &&
+ !my_strcasecmp(&my_charset_latin1, cs[0]->csname, charset_name))
+ return cs[0]->number;
+ }
+ return 0;
+}
+
+
+static const char*
+get_charset_name_alias(const char *name)
+{
+ if (!my_strcasecmp(&my_charset_latin1, name, "utf8mb3"))
+ return "utf8";
+ return NULL;
+}
+
+
+uint get_charset_number(const char *charset_name, uint cs_flags)
+{
+ uint id;
+ my_thread_once(&charsets_initialized, init_available_charsets);
+ if ((id= get_charset_number_internal(charset_name, cs_flags)))
+ return id;
+ if ((charset_name= get_charset_name_alias(charset_name)))
+ return get_charset_number_internal(charset_name, cs_flags);
+ return 0;
+}
+
+
+const char *get_charset_name(uint charset_number)
+{
+ my_thread_once(&charsets_initialized, init_available_charsets);
+
+ if (charset_number < array_elements(all_charsets))
+ {
+ CHARSET_INFO *cs= all_charsets[charset_number];
+
+ if (cs && (cs->number == charset_number) && cs->name)
+ return (char*) cs->name;
+ }
+
+ return "?"; /* this mimics find_type() */
+}
+
+
+static CHARSET_INFO *
+get_internal_charset(MY_CHARSET_LOADER *loader, uint cs_number, myf flags)
+{
+ char buf[FN_REFLEN];
+ CHARSET_INFO *cs;
+
+ DBUG_ASSERT(cs_number < array_elements(all_charsets));
+
+ if ((cs= all_charsets[cs_number]))
+ {
+ if (cs->state & MY_CS_READY) /* if CS is already initialized */
+ return cs;
+
+ /*
+ To make things thread safe we are not allowing other threads to interfere
+ while we may changing the cs_info_table
+ */
+ mysql_mutex_lock(&THR_LOCK_charset);
+
+ if (!(cs->state & (MY_CS_COMPILED|MY_CS_LOADED))) /* if CS is not in memory */
+ {
+ MY_CHARSET_LOADER loader;
+ strxmov(get_charsets_dir(buf), cs->csname, ".xml", NullS);
+ my_charset_loader_init_mysys(&loader);
+ my_read_charset_file(&loader, buf, flags);
+ }
+
+ if (cs->state & MY_CS_AVAILABLE)
+ {
+ if (!(cs->state & MY_CS_READY))
+ {
+ if ((cs->cset->init && cs->cset->init(cs, loader)) ||
+ (cs->coll->init && cs->coll->init(cs, loader)))
+ {
+ cs= NULL;
+ }
+ else
+ cs->state|= MY_CS_READY;
+ }
+ }
+ else
+ cs= NULL;
+
+ mysql_mutex_unlock(&THR_LOCK_charset);
+ }
+ return cs;
+}
+
+
+CHARSET_INFO *get_charset(uint cs_number, myf flags)
+{
+ CHARSET_INFO *cs;
+ MY_CHARSET_LOADER loader;
+
+ if (cs_number == default_charset_info->number)
+ return default_charset_info;
+
+ my_thread_once(&charsets_initialized, init_available_charsets);
+
+ if (cs_number >= array_elements(all_charsets))
+ return NULL;
+
+ my_charset_loader_init_mysys(&loader);
+ cs= get_internal_charset(&loader, cs_number, flags);
+
+ if (!cs && (flags & MY_WME))
+ {
+ char index_file[FN_REFLEN + sizeof(MY_CHARSET_INDEX)], cs_string[23];
+ my_stpcpy(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(0), cs_string, index_file);
+ }
+ return cs;
+}
+
+
+/**
+ Find collation by name: extended version of get_charset_by_name()
+ to return error messages to the caller.
+ @param loader Character set loader
+ @param name Collation name
+ @param flags Flags
+ @return NULL on error, pointer to collation on success
+*/
+
+CHARSET_INFO *
+my_collation_get_by_name(MY_CHARSET_LOADER *loader,
+ const char *name, myf flags)
+{
+ uint cs_number;
+ CHARSET_INFO *cs;
+ my_thread_once(&charsets_initialized, init_available_charsets);
+
+ cs_number= get_collation_number(name);
+ my_charset_loader_init_mysys(loader);
+ cs= cs_number ? get_internal_charset(loader, cs_number, flags) : NULL;
+
+ if (!cs && (flags & MY_WME))
+ {
+ char index_file[FN_REFLEN + sizeof(MY_CHARSET_INDEX)];
+ my_stpcpy(get_charsets_dir(index_file),MY_CHARSET_INDEX);
+ my_error(EE_UNKNOWN_COLLATION, MYF(0), name, index_file);
+ }
+ return cs;
+}
+
+
+CHARSET_INFO *get_charset_by_name(const char *cs_name, myf flags)
+{
+ MY_CHARSET_LOADER loader;
+ my_charset_loader_init_mysys(&loader);
+ return my_collation_get_by_name(&loader, cs_name, flags);
+}
+
+
+/**
+ Find character set by name: extended version of get_charset_by_csname()
+ to return error messages to the caller.
+ @param loader Character set loader
+ @param name Collation name
+ @param cs_flags Character set flags (e.g. default or binary collation)
+ @param flags Flags
+ @return NULL on error, pointer to collation on success
+*/
+CHARSET_INFO *
+my_charset_get_by_name(MY_CHARSET_LOADER *loader,
+ const char *cs_name, uint cs_flags, myf flags)
+{
+ uint cs_number;
+ CHARSET_INFO *cs;
+ DBUG_ENTER("get_charset_by_csname");
+ DBUG_PRINT("enter",("name: '%s'", cs_name));
+
+ my_thread_once(&charsets_initialized, init_available_charsets);
+
+ cs_number= get_charset_number(cs_name, cs_flags);
+ cs= cs_number ? get_internal_charset(loader, cs_number, flags) : NULL;
+
+ if (!cs && (flags & MY_WME))
+ {
+ char index_file[FN_REFLEN + sizeof(MY_CHARSET_INDEX)];
+ my_stpcpy(get_charsets_dir(index_file),MY_CHARSET_INDEX);
+ my_error(EE_UNKNOWN_CHARSET, MYF(0), cs_name, index_file);
+ }
+
+ DBUG_RETURN(cs);
+}
+
+
+CHARSET_INFO *
+get_charset_by_csname(const char *cs_name, uint cs_flags, myf flags)
+{
+ MY_CHARSET_LOADER loader;
+ my_charset_loader_init_mysys(&loader);
+ return my_charset_get_by_name(&loader, cs_name, cs_flags, flags);
+}
+
+
+/**
+ Resolve character set by the character set name (utf8, latin1, ...).
+
+ The function tries to resolve character set by the specified name. If
+ there is character set with the given name, it is assigned to the "cs"
+ parameter and FALSE is returned. If there is no such character set,
+ "default_cs" is assigned to the "cs" and TRUE is returned.
+
+ @param[in] cs_name Character set name.
+ @param[in] default_cs Default character set.
+ @param[out] cs Variable to store character set.
+
+ @return FALSE if character set was resolved successfully; TRUE if there
+ is no character set with given name.
+*/
+
+my_bool resolve_charset(const char *cs_name,
+ const CHARSET_INFO *default_cs,
+ const CHARSET_INFO **cs)
+{
+ *cs= get_charset_by_csname(cs_name, MY_CS_PRIMARY, MYF(0));
+
+ if (*cs == NULL)
+ {
+ *cs= default_cs;
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+/**
+ Resolve collation by the collation name (utf8_general_ci, ...).
+
+ The function tries to resolve collation by the specified name. If there
+ is collation with the given name, it is assigned to the "cl" parameter
+ and FALSE is returned. If there is no such collation, "default_cl" is
+ assigned to the "cl" and TRUE is returned.
+
+ @param[out] cl Variable to store collation.
+ @param[in] cl_name Collation name.
+ @param[in] default_cl Default collation.
+
+ @return FALSE if collation was resolved successfully; TRUE if there is no
+ collation with given name.
+*/
+
+my_bool resolve_collation(const char *cl_name,
+ const CHARSET_INFO *default_cl,
+ const CHARSET_INFO **cl)
+{
+ *cl= get_charset_by_name(cl_name, MYF(0));
+
+ if (*cl == NULL)
+ {
+ *cl= default_cl;
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+/*
+ Escape string with backslashes (\)
+
+ SYNOPSIS
+ escape_string_for_mysql()
+ charset_info Charset of the strings
+ to Buffer for escaped string
+ to_length Length of destination buffer, or 0
+ from The string to escape
+ length The length of the string to escape
+
+ DESCRIPTION
+ This escapes the contents of a string by adding backslashes before special
+ characters, and turning others into specific escape sequences, such as
+ turning newlines into \n and null bytes into \0.
+
+ NOTE
+ To maintain compatibility with the old C API, to_length may be 0 to mean
+ "big enough"
+
+ RETURN VALUES
+ (size_t) -1 The escaped string did not fit in the to buffer
+ # The length of the escaped string
+*/
+
+size_t escape_string_for_mysql(const CHARSET_INFO *charset_info,
+ char *to, size_t to_length,
+ const char *from, size_t length)
+{
+ const char *to_start= to;
+ const char *end, *to_end=to_start + (to_length ? to_length-1 : 2*length);
+ my_bool overflow= FALSE;
+ my_bool use_mb_flag= use_mb(charset_info);
+ for (end= from + length; from < end; from++)
+ {
+ char escape= 0;
+ int tmp_length;
+ if (use_mb_flag && (tmp_length= my_ismbchar(charset_info, from, end)))
+ {
+ if (to + tmp_length > to_end)
+ {
+ overflow= TRUE;
+ break;
+ }
+ while (tmp_length--)
+ *to++= *from++;
+ from--;
+ continue;
+ }
+ /*
+ If the next character appears to begin a multi-byte character, we
+ escape that first byte of that apparent multi-byte character. (The
+ character just looks like a multi-byte character -- if it were actually
+ a multi-byte character, it would have been passed through in the test
+ above.)
+
+ Without this check, we can create a problem by converting an invalid
+ multi-byte character into a valid one. For example, 0xbf27 is not
+ a valid GBK character, but 0xbf5c is. (0x27 = ', 0x5c = \)
+ */
+ tmp_length= use_mb_flag ? my_mbcharlen_ptr(charset_info, from, end) : 0;
+ if (tmp_length > 1)
+ escape= *from;
+ else
+ switch (*from) {
+ case 0: /* Must be escaped for 'mysql' */
+ escape= '0';
+ break;
+ case '\n': /* Must be escaped for logs */
+ escape= 'n';
+ break;
+ case '\r':
+ escape= 'r';
+ break;
+ case '\\':
+ escape= '\\';
+ break;
+ case '\'':
+ escape= '\'';
+ break;
+ case '"': /* Better safe than sorry */
+ escape= '"';
+ break;
+ case '\032': /* This gives problems on Win32 */
+ escape= 'Z';
+ break;
+ }
+ if (escape)
+ {
+ if (to + 2 > to_end)
+ {
+ overflow= TRUE;
+ break;
+ }
+ *to++= '\\';
+ *to++= escape;
+ }
+ else
+ {
+ if (to + 1 > to_end)
+ {
+ overflow= TRUE;
+ break;
+ }
+ *to++= *from;
+ }
+ }
+ *to= 0;
+ return overflow ? (size_t) -1 : (size_t) (to - to_start);
+}
+
+
+#ifdef _WIN32
+static CHARSET_INFO *fs_cset_cache= NULL;
+
+CHARSET_INFO *fs_character_set()
+{
+ if (!fs_cset_cache)
+ {
+ char buf[10]= "cp";
+ GetLocaleInfo(LOCALE_SYSTEM_DEFAULT, LOCALE_IDEFAULTANSICODEPAGE,
+ buf+2, sizeof(buf)-3);
+ /*
+ We cannot call get_charset_by_name here
+ because fs_character_set() is executed before
+ LOCK_THD_charset mutex initialization, which
+ is used inside get_charset_by_name.
+ As we're now interested in cp932 only,
+ let's just detect it using strcmp().
+ */
+ fs_cset_cache=
+ #ifdef HAVE_CHARSET_cp932
+ !strcmp(buf, "cp932") ? &my_charset_cp932_japanese_ci :
+ #endif
+ &my_charset_bin;
+ }
+ return fs_cset_cache;
+}
+#endif
+
+/*
+ Escape apostrophes by doubling them up
+
+ SYNOPSIS
+ escape_quotes_for_mysql()
+ charset_info Charset of the strings
+ to Buffer for escaped string
+ to_length Length of destination buffer, or 0
+ from The string to escape
+ length The length of the string to escape
+ quote The quote the buffer will be escaped against
+
+ DESCRIPTION
+ This escapes the contents of a string by doubling up any character
+ specified by the quote parameter. This is used when the
+ NO_BACKSLASH_ESCAPES SQL_MODE is in effect on the server.
+
+ NOTE
+ To be consistent with escape_string_for_mysql(), to_length may be 0 to
+ mean "big enough"
+
+ RETURN VALUES
+ ~0 The escaped string did not fit in the to buffer
+ >=0 The length of the escaped string
+*/
+
+size_t escape_quotes_for_mysql(CHARSET_INFO *charset_info,
+ char *to, size_t to_length,
+ const char *from, size_t length, char quote)
+{
+ const char *to_start= to;
+ const char *end, *to_end=to_start + (to_length ? to_length-1 : 2*length);
+ my_bool overflow= FALSE;
+ my_bool use_mb_flag= use_mb(charset_info);
+ for (end= from + length; from < end; from++)
+ {
+ int tmp_length;
+ if (use_mb_flag && (tmp_length= my_ismbchar(charset_info, from, end)))
+ {
+ if (to + tmp_length > to_end)
+ {
+ overflow= TRUE;
+ break;
+ }
+ while (tmp_length--)
+ *to++= *from++;
+ from--;
+ continue;
+ }
+ /*
+ We don't have the same issue here with a non-multi-byte character being
+ turned into a multi-byte character by the addition of an escaping
+ character, because we are only escaping the ' character with itself.
+ */
+ if (*from == quote)
+ {
+ if (to + 2 > to_end)
+ {
+ overflow= TRUE;
+ break;
+ }
+ *to++= quote;
+ *to++= quote;
+ }
+ else
+ {
+ if (to + 1 > to_end)
+ {
+ overflow= TRUE;
+ break;
+ }
+ *to++= *from;
+ }
+ }
+ *to= 0;
+ return overflow ? (ulong)~0 : (ulong) (to - to_start);
+}
diff --git a/mysql/mysys/checksum.c b/mysql/mysys/checksum.c
new file mode 100644
index 0000000..87f3056
--- /dev/null
+++ b/mysql/mysys/checksum.c
@@ -0,0 +1,35 @@
+/* Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved.
+
+ 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; version 2 of the License.
+
+ 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+
+#include <my_global.h>
+#include <my_sys.h>
+#include <zlib.h>
+
+/*
+ Calculate a long checksum for a memoryblock.
+
+ SYNOPSIS
+ my_checksum()
+ crc start value for crc
+ pos pointer to memory block
+ length length of the block
+*/
+
+ha_checksum my_checksum(ha_checksum crc, const uchar *pos, size_t length)
+{
+ return (ha_checksum)crc32((uint)crc, pos, (uint)length);
+}
+
diff --git a/mysql/mysys/errors.c b/mysql/mysys/errors.c
new file mode 100644
index 0000000..b972102
--- /dev/null
+++ b/mysql/mysys/errors.c
@@ -0,0 +1,92 @@
+/* Copyright (c) 2000, 2016, Oracle and/or its affiliates. All rights reserved.
+
+ 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; version 2 of the License.
+
+ 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include "mysys_priv.h"
+#include "mysys_err.h"
+#include "my_sys.h"
+#include "my_thread_local.h"
+
+const char *globerrs[GLOBERRS]=
+{
+ "Can't create/write to file '%s' (Errcode: %d - %s)",
+ "Error reading file '%s' (Errcode: %d - %s)",
+ "Error writing file '%s' (Errcode: %d - %s)",
+ "Error on close of '%s' (Errcode: %d - %s)",
+ "Out of memory (Needed %u bytes)",
+ "Error on delete of '%s' (Errcode: %d - %s)",
+ "Error on rename of '%s' to '%s' (Errcode: %d - %s)",
+ "",
+ "Unexpected EOF found when reading file '%s' (Errcode: %d - %s)",
+ "Can't lock file (Errcode: %d - %s)",
+ "Can't unlock file (Errcode: %d - %s)",
+ "Can't read dir of '%s' (Errcode: %d - %s)",
+ "Can't get stat of '%s' (Errcode: %d - %s)",
+ "Can't change size of file (Errcode: %d - %s)",
+ "Can't open stream from handle (Errcode: %d - %s)",
+ "Can't get working directory (Errcode: %d - %s)",
+ "Can't change dir to '%s' (Errcode: %d - %s)",
+ "Warning: '%s' had %d links",
+ "Warning: %d files and %d streams is left open\n",
+ "Disk is full writing '%s' (Errcode: %d - %s). Waiting for someone to free space...",
+ "Can't create directory '%s' (Errcode: %d - %s)",
+ "Character set '%s' is not a compiled character set and is not specified in the '%s' file",
+ "Out of resources when opening file '%s' (Errcode: %d - %s)",
+ "Can't read value for symlink '%s' (Error %d - %s)",
+ "Can't create symlink '%s' pointing at '%s' (Error %d - %s)",
+ "Error on realpath() on '%s' (Error %d - %s)",
+ "Can't sync file '%s' to disk (Errcode: %d - %s)",
+ "Collation '%s' is not a compiled collation and is not specified in the '%s' file",
+ "File '%s' not found (Errcode: %d - %s)",
+ "File '%s' (fileno: %d) was not closed",
+ "Can't change ownership of the file '%s' (Errcode: %d - %s)",
+ "Can't change permissions of the file '%s' (Errcode: %d - %s)",
+ "Can't seek in file '%s' (Errcode: %d - %s)",
+ "Memory capacity exceeded (capacity %llu bytes)"
+};
+
+
+/*
+ We cannot call my_error/my_printf_error here in this function.
+ Those functions will set status variable in diagnostic area
+ and there is no provision to reset them back.
+ Here we are waiting for free space and will wait forever till
+ space is created. So just giving warning in the error file
+ should be enough.
+*/
+void wait_for_free_space(const char *filename, int errors)
+{
+ if (!(errors % MY_WAIT_GIVE_USER_A_MESSAGE))
+ {
+ char errbuf[MYSYS_STRERROR_SIZE];
+ my_message_local(ERROR_LEVEL, EE(EE_DISK_FULL),
+ filename,my_errno,
+ my_strerror(errbuf, sizeof(errbuf), my_errno()));
+ my_message_local(ERROR_LEVEL,
+ "Retry in %d secs. Message reprinted in %d secs",
+ MY_WAIT_FOR_USER_TO_FIX_PANIC,
+ MY_WAIT_GIVE_USER_A_MESSAGE * MY_WAIT_FOR_USER_TO_FIX_PANIC );
+ }
+ DBUG_EXECUTE_IF("simulate_no_free_space_error",
+ {
+ (void) sleep(1);
+ return;
+ });
+ (void) sleep(MY_WAIT_FOR_USER_TO_FIX_PANIC);
+}
+
+const char *get_global_errmsg(int nr)
+{
+ return globerrs[nr - EE_ERROR_FIRST];
+}
diff --git a/mysql/mysys/hash.c b/mysql/mysys/hash.c
new file mode 100644
index 0000000..b02d20a
--- /dev/null
+++ b/mysql/mysys/hash.c
@@ -0,0 +1,812 @@
+/* Copyright (c) 2000, 2015, Oracle and/or its affiliates. All rights reserved.
+
+ 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; version 2 of the License.
+
+ 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+/* The hash functions used for saveing keys */
+/* One of key_length or key_length_offset must be given */
+/* Key length of 0 isn't allowed */
+
+#include "mysys_priv.h"
+#include <m_string.h>
+#include <m_ctype.h>
+#include "hash.h"
+
+#define NO_RECORD ((uint) -1)
+#define LOWFIND 1
+#define LOWUSED 2
+#define HIGHFIND 4
+#define HIGHUSED 8
+
+typedef struct st_hash_info {
+ uint next; /* index to next key */
+ uchar *data; /* data for current entry */
+} HASH_LINK;
+
+static uint my_hash_mask(my_hash_value_type hashnr,
+ size_t buffmax, size_t maxlength);
+static void movelink(HASH_LINK *array,uint pos,uint next_link,uint newlink);
+static int hashcmp(const HASH *hash, HASH_LINK *pos, const uchar *key,
+ size_t length);
+
+static my_hash_value_type calc_hash(const HASH *hash,
+ const uchar *key, size_t length)
+{
+ return hash->hash_function(hash, key, length);
+}
+
+
+/**
+ Adaptor function which allows to use hash function from character
+ set with HASH.
+*/
+
+static my_hash_value_type cset_hash_sort_adapter(const HASH *hash,
+ const uchar *key,
+ size_t length)
+{
+ ulong nr1=1, nr2=4;
+ hash->charset->coll->hash_sort(hash->charset,(uchar*) key,length,&nr1,&nr2);
+ return (my_hash_value_type)nr1;
+}
+
+
+/**
+ @brief Initialize the hash
+
+ @details
+
+ Initialize the hash, by defining and giving valid values for
+ its elements. The failure to allocate memory for the
+ hash->array element will not result in a fatal failure. The
+ dynamic array that is part of the hash will allocate memory
+ as required during insertion.
+
+ @param[in,out] hash The hash that is initialized
+ @param[in] charset The charater set information
+ @param[in] hash_function Hash function to be used. NULL -
+ use standard hash from character
+ set.
+ @param[in] size The hash size
+ @param[in] key_offest The key offset for the hash
+ @param[in] key_length The length of the key used in
+ the hash
+ @param[in] get_key get the key for the hash
+ @param[in] free_element pointer to the function that
+ does cleanup
+ @param[in] flags flags set in the hash
+ @return inidicates success or failure of initialization
+ @retval 0 success
+ @retval 1 failure
+*/
+my_bool
+_my_hash_init(HASH *hash, uint growth_size, CHARSET_INFO *charset,
+ my_hash_function hash_function,
+ ulong size, size_t key_offset, size_t key_length,
+ my_hash_get_key get_key,
+ void (*free_element)(void*), uint flags,
+ PSI_memory_key psi_key)
+{
+ my_bool retval;
+
+ DBUG_ENTER("my_hash_init");
+ DBUG_PRINT("enter",("hash: 0x%lx size: %u", (long) hash, (uint) size));
+
+ hash->records=0;
+ hash->key_offset=key_offset;
+ hash->key_length=key_length;
+ hash->blength=1;
+ hash->get_key=get_key;
+ hash->free=free_element;
+ hash->flags=flags;
+ hash->charset=charset;
+ hash->hash_function= hash_function ? hash_function : cset_hash_sort_adapter;
+ hash->m_psi_key= psi_key;
+ retval= my_init_dynamic_array(&hash->array,
+ psi_key,
+ sizeof(HASH_LINK),
+ NULL, /* init_buffer */
+ size, growth_size);
+ DBUG_RETURN(retval);
+}
+
+
+static inline void my_hash_claim_elements(HASH *hash)
+{
+ HASH_LINK *data= dynamic_element(&hash->array, 0, HASH_LINK*);
+ HASH_LINK *end= data + hash->records;
+ while (data < end)
+ my_claim((data++)->data);
+}
+
+/*
+ Call hash->free on all elements in hash.
+
+ SYNOPSIS
+ my_hash_free_elements()
+ hash hash table
+
+ NOTES:
+ Sets records to 0
+*/
+
+static inline void my_hash_free_elements(HASH *hash)
+{
+ if (hash->free)
+ {
+ HASH_LINK *data=dynamic_element(&hash->array,0,HASH_LINK*);
+ HASH_LINK *end= data + hash->records;
+ while (data < end)
+ (*hash->free)((data++)->data);
+ }
+ hash->records=0;
+}
+
+void my_hash_claim(HASH *hash)
+{
+ DBUG_ENTER("my_hash_claim");
+ DBUG_PRINT("enter",("hash: 0x%lx", (long) hash));
+
+ my_hash_claim_elements(hash);
+ claim_dynamic(&hash->array);
+ DBUG_VOID_RETURN;
+}
+
+
+/*
+ Free memory used by hash.
+
+ SYNOPSIS
+ my_hash_free()
+ hash the hash to delete elements of
+
+ NOTES: Hash can't be reused without calling my_hash_init again.
+*/
+
+void my_hash_free(HASH *hash)
+{
+ DBUG_ENTER("my_hash_free");
+ DBUG_PRINT("enter",("hash: 0x%lx", (long) hash));
+
+ my_hash_free_elements(hash);
+ hash->free= 0;
+ delete_dynamic(&hash->array);
+ hash->blength= 0;
+ DBUG_VOID_RETURN;
+}
+
+
+/*
+ Delete all elements from the hash (the hash itself is to be reused).
+
+ SYNOPSIS
+ my_hash_reset()
+ hash the hash to delete elements of
+*/
+
+void my_hash_reset(HASH *hash)
+{
+ DBUG_ENTER("my_hash_reset");
+ DBUG_PRINT("enter",("hash: 0x%lxd", (long) hash));
+
+ my_hash_free_elements(hash);
+ reset_dynamic(&hash->array);
+ /* Set row pointers so that the hash can be reused at once */
+ hash->blength= 1;
+ DBUG_VOID_RETURN;
+}
+
+/* some helper functions */
+
+/*
+ This function is char* instead of uchar* as HPUX11 compiler can't
+ handle inline functions that are not defined as native types
+*/
+
+static inline char*
+my_hash_key(const HASH *hash, const uchar *record, size_t *length,
+ my_bool first)
+{
+ if (hash->get_key)
+ return (char*) (*hash->get_key)(record,length,first);
+ *length=hash->key_length;
+ return (char*) record+hash->key_offset;
+}
+
+ /* Calculate pos according to keys */
+
+static uint my_hash_mask(my_hash_value_type hashnr, size_t buffmax,
+ size_t maxlength)
+{
+ if ((hashnr & (buffmax-1)) < maxlength) return (hashnr & (buffmax-1));
+ return (hashnr & ((buffmax >> 1) -1));
+}
+
+static uint my_hash_rec_mask(const HASH *hash, HASH_LINK *pos,
+ size_t buffmax, size_t maxlength)
+{
+ size_t length;
+ uchar *key= (uchar*) my_hash_key(hash, pos->data, &length, 0);
+ return my_hash_mask(calc_hash(hash, key, length), buffmax, maxlength);
+}
+
+
+
+static inline my_hash_value_type rec_hashnr(HASH *hash,const uchar *record)
+{
+ size_t length;
+ uchar *key= (uchar*) my_hash_key(hash, record, &length, 0);
+ return calc_hash(hash,key,length);
+}
+
+
+uchar* my_hash_search(const HASH *hash, const uchar *key, size_t length)
+{
+ HASH_SEARCH_STATE state;
+ return my_hash_first(hash, key, length, &state);
+}
+
+uchar* my_hash_search_using_hash_value(const HASH *hash,
+ my_hash_value_type hash_value,
+ const uchar *key,
+ size_t length)
+{
+ HASH_SEARCH_STATE state;
+ return my_hash_first_from_hash_value(hash, hash_value,
+ key, length, &state);
+}
+
+my_hash_value_type my_calc_hash(const HASH *hash,
+ const uchar *key, size_t length)
+{
+ return calc_hash(hash, key, length ? length : hash->key_length);
+}
+
+
+/*
+ Search after a record based on a key
+
+ NOTE
+ Assigns the number of the found record to HASH_SEARCH_STATE state
+*/
+
+uchar* my_hash_first(const HASH *hash, const uchar *key, size_t length,
+ HASH_SEARCH_STATE *current_record)
+{
+ uchar *res;
+ if (my_hash_inited(hash))
+ res= my_hash_first_from_hash_value(hash,
+ calc_hash(hash, key, length ? length : hash->key_length),
+ key, length, current_record);
+ else
+ res= 0;
+ return res;
+}
+
+
+uchar* my_hash_first_from_hash_value(const HASH *hash,
+ my_hash_value_type hash_value,
+ const uchar *key,
+ size_t length,
+ HASH_SEARCH_STATE *current_record)
+{
+ HASH_LINK *pos;
+ uint flag,idx;
+ DBUG_ENTER("my_hash_first_from_hash_value");
+
+ flag=1;
+ if (hash->records)
+ {
+ idx= my_hash_mask(hash_value,
+ hash->blength, hash->records);
+ do
+ {
+ pos= dynamic_element(&hash->array,idx,HASH_LINK*);
+ if (!hashcmp(hash,pos,key,length))
+ {
+ DBUG_PRINT("exit",("found key at %d",idx));
+ *current_record= idx;
+ DBUG_RETURN (pos->data);
+ }
+ if (flag)
+ {
+ flag=0; /* Reset flag */
+ if (my_hash_rec_mask(hash, pos, hash->blength, hash->records) != idx)
+ break; /* Wrong link */
+ }
+ }
+ while ((idx=pos->next) != NO_RECORD);
+ }
+ *current_record= NO_RECORD;
+ DBUG_RETURN(0);
+}
+
+ /* Get next record with identical key */
+ /* Can only be called if previous calls was my_hash_search */
+
+uchar* my_hash_next(const HASH *hash, const uchar *key, size_t length,
+ HASH_SEARCH_STATE *current_record)
+{
+ HASH_LINK *pos;
+ uint idx;
+
+ if (*current_record != NO_RECORD)
+ {
+ HASH_LINK *data=dynamic_element(&hash->array,0,HASH_LINK*);
+ for (idx=data[*current_record].next; idx != NO_RECORD ; idx=pos->next)
+ {
+ pos=data+idx;
+ if (!hashcmp(hash,pos,key,length))
+ {
+ *current_record= idx;
+ return pos->data;
+ }
+ }
+ *current_record= NO_RECORD;
+ }
+ return 0;
+}
+
+
+ /* Change link from pos to new_link */
+
+static void movelink(HASH_LINK *array,uint find,uint next_link,uint newlink)
+{
+ HASH_LINK *old_link;
+ do
+ {
+ old_link=array+next_link;
+ }
+ while ((next_link=old_link->next) != find);
+ old_link->next= newlink;
+ return;
+}
+
+/*
+ Compare a key in a record to a whole key. Return 0 if identical
+
+ SYNOPSIS
+ hashcmp()
+ hash hash table
+ pos position of hash record to use in comparison
+ key key for comparison
+ length length of key
+
+ NOTES:
+ If length is 0, comparison is done using the length of the
+ record being compared against.
+
+ RETURN
+ = 0 key of record == key
+ != 0 key of record != key
+ */
+
+static int hashcmp(const HASH *hash, HASH_LINK *pos, const uchar *key,
+ size_t length)
+{
+ size_t rec_keylength;
+ uchar *rec_key= (uchar*) my_hash_key(hash, pos->data, &rec_keylength, 1);
+ return ((length && length != rec_keylength) ||
+ my_strnncoll(hash->charset, (uchar*) rec_key, rec_keylength,
+ (uchar*) key, rec_keylength));
+}
+
+
+ /* Write a hash-key to the hash-index */
+
+my_bool my_hash_insert(HASH *info, const uchar *record)
+{
+ int flag;
+ size_t idx,halfbuff,first_index;
+ my_hash_value_type hash_nr;
+ uchar *ptr_to_rec= NULL, *ptr_to_rec2= NULL;
+ HASH_LINK *data, *empty, *gpos= NULL, *gpos2= NULL, *pos;
+
+ if (HASH_UNIQUE & info->flags)
+ {
+ uchar *key= (uchar*) my_hash_key(info, record, &idx, 1);
+ if (my_hash_search(info, key, idx))
+ return(TRUE); /* Duplicate entry */
+ }
+
+ flag=0;
+ if (!(empty=(HASH_LINK*) alloc_dynamic(&info->array)))
+ return(TRUE); /* No more memory */
+
+ data=dynamic_element(&info->array,0,HASH_LINK*);
+ halfbuff= info->blength >> 1;
+
+ idx=first_index=info->records-halfbuff;
+ if (idx != info->records) /* If some records */
+ {
+ do
+ {
+ pos=data+idx;
+ hash_nr=rec_hashnr(info,pos->data);
+ if (flag == 0) /* First loop; Check if ok */
+ if (my_hash_mask(hash_nr, info->blength, info->records) != first_index)
+ break;
+ if (!(hash_nr & halfbuff))
+ { /* Key will not move */
+ if (!(flag & LOWFIND))
+ {
+ if (flag & HIGHFIND)
+ {
+ flag=LOWFIND | HIGHFIND;
+ /* key shall be moved to the current empty position */
+ gpos=empty;
+ ptr_to_rec=pos->data;
+ empty=pos; /* This place is now free */
+ }
+ else
+ {
+ flag=LOWFIND | LOWUSED; /* key isn't changed */
+ gpos=pos;
+ ptr_to_rec=pos->data;
+ }
+ }
+ else
+ {
+ if (!(flag & LOWUSED))
+ {
+ /* Change link of previous LOW-key */
+ gpos->data=ptr_to_rec;
+ gpos->next= (uint) (pos-data);
+ flag= (flag & HIGHFIND) | (LOWFIND | LOWUSED);
+ }
+ gpos=pos;
+ ptr_to_rec=pos->data;
+ }
+ }
+ else
+ { /* key will be moved */
+ if (!(flag & HIGHFIND))
+ {
+ flag= (flag & LOWFIND) | HIGHFIND;
+ /* key shall be moved to the last (empty) position */
+ gpos2 = empty; empty=pos;
+ ptr_to_rec2=pos->data;
+ }
+ else
+ {
+ if (!(flag & HIGHUSED))
+ {
+ /* Change link of previous hash-key and save */
+ gpos2->data=ptr_to_rec2;
+ gpos2->next=(uint) (pos-data);
+ flag= (flag & LOWFIND) | (HIGHFIND | HIGHUSED);
+ }
+ gpos2=pos;
+ ptr_to_rec2=pos->data;
+ }
+ }
+ }
+ while ((idx=pos->next) != NO_RECORD);
+
+ if ((flag & (LOWFIND | LOWUSED)) == LOWFIND)
+ {
+ gpos->data=ptr_to_rec;
+ gpos->next=NO_RECORD;
+ }
+ if ((flag & (HIGHFIND | HIGHUSED)) == HIGHFIND)
+ {
+ gpos2->data=ptr_to_rec2;
+ gpos2->next=NO_RECORD;
+ }
+ }
+ /* Check if we are at the empty position */
+
+ idx= my_hash_mask(rec_hashnr(info, record), info->blength, info->records + 1);
+ pos=data+idx;
+ if (pos == empty)
+ {
+ pos->data=(uchar*) record;
+ pos->next=NO_RECORD;
+ }
+ else
+ {
+ /* Check if more records in same hash-nr family */
+ empty[0]=pos[0];
+ gpos= data + my_hash_rec_mask(info, pos, info->blength, info->records + 1);
+ if (pos == gpos)
+ {
+ pos->data=(uchar*) record;
+ pos->next=(uint) (empty - data);
+ }
+ else
+ {
+ pos->data=(uchar*) record;
+ pos->next=NO_RECORD;
+ movelink(data,(uint) (pos-data),(uint) (gpos-data),(uint) (empty-data));
+ }
+ }
+ if (++info->records == info->blength)
+ info->blength+= info->blength;
+ return(0);
+}
+
+
+/******************************************************************************
+** Remove one record from hash-table. The record with the same record
+** ptr is removed.
+** if there is a free-function it's called for record if found
+******************************************************************************/
+
+my_bool my_hash_delete(HASH *hash, uchar *record)
+{
+ size_t blength;
+ uint pos2, idx, empty_index;
+ my_hash_value_type pos_hashnr, lastpos_hashnr;
+ HASH_LINK *data,*lastpos,*gpos,*pos,*pos3,*empty;
+ DBUG_ENTER("my_hash_delete");
+ if (!hash->records)
+ DBUG_RETURN(1);
+
+ blength=hash->blength;
+ data=dynamic_element(&hash->array,0,HASH_LINK*);
+ /* Search after record with key */
+ pos= data + my_hash_mask(rec_hashnr(hash, record), blength, hash->records);
+ gpos = 0;
+
+ while (pos->data != record)
+ {
+ gpos=pos;
+ if (pos->next == NO_RECORD)
+ DBUG_RETURN(1); /* Key not found */
+ pos=data+pos->next;
+ }
+
+ if ( --(hash->records) < hash->blength >> 1) hash->blength>>=1;
+ lastpos=data+hash->records;
+
+ /* Remove link to record */
+ empty=pos; empty_index=(uint) (empty-data);
+ if (gpos)
+ gpos->next=pos->next; /* unlink current ptr */
+ else if (pos->next != NO_RECORD)
+ {
+ empty=data+(empty_index=pos->next);
+ pos->data=empty->data;
+ pos->next=empty->next;
+ }
+
+ if (empty == lastpos) /* last key at wrong pos or no next link */
+ goto exit;
+
+ /* Move the last key (lastpos) */
+ lastpos_hashnr=rec_hashnr(hash,lastpos->data);
+ /* pos is where lastpos should be */
+ pos= data + my_hash_mask(lastpos_hashnr, hash->blength, hash->records);
+ if (pos == empty) /* Move to empty position. */
+ {
+ empty[0]=lastpos[0];
+ goto exit;
+ }
+ pos_hashnr=rec_hashnr(hash,pos->data);
+ /* pos3 is where the pos should be */
+ pos3= data + my_hash_mask(pos_hashnr, hash->blength, hash->records);
+ if (pos != pos3)
+ { /* pos is on wrong posit */
+ empty[0]=pos[0]; /* Save it here */
+ pos[0]=lastpos[0]; /* This should be here */
+ movelink(data,(uint) (pos-data),(uint) (pos3-data),empty_index);
+ goto exit;
+ }
+ pos2= my_hash_mask(lastpos_hashnr, blength, hash->records + 1);
+ if (pos2 == my_hash_mask(pos_hashnr, blength, hash->records + 1))
+ { /* Identical key-positions */
+ if (pos2 != hash->records)
+ {
+ empty[0]=lastpos[0];
+ movelink(data,(uint) (lastpos-data),(uint) (pos-data),empty_index);
+ goto exit;
+ }
+ idx= (uint) (pos-data); /* Link pos->next after lastpos */
+ }
+ else idx= NO_RECORD; /* Different positions merge */
+
+ empty[0]=lastpos[0];
+ movelink(data,idx,empty_index,pos->next);
+ pos->next=empty_index;
+
+exit:
+ (void) pop_dynamic(&hash->array);
+ if (hash->free)
+ (*hash->free)((uchar*) record);
+ DBUG_RETURN(0);
+}
+
+ /*
+ Update keys when record has changed.
+ This is much more efficent than using a delete & insert.
+ */
+
+my_bool my_hash_update(HASH *hash, uchar *record, uchar *old_key,
+ size_t old_key_length)
+{
+ uint new_index,new_pos_index,records;
+ size_t blength, idx, empty;
+ HASH_LINK org_link,*data,*previous,*pos;
+ DBUG_ENTER("my_hash_update");
+
+ if (HASH_UNIQUE & hash->flags)
+ {
+ HASH_SEARCH_STATE state;
+ uchar *found, *new_key= (uchar*) my_hash_key(hash, record, &idx, 1);
+ if ((found= my_hash_first(hash, new_key, idx, &state)))
+ {
+ do
+ {
+ if (found != record)
+ DBUG_RETURN(1); /* Duplicate entry */
+ }
+ while ((found= my_hash_next(hash, new_key, idx, &state)));
+ }
+ }
+
+ data=dynamic_element(&hash->array,0,HASH_LINK*);
+ blength=hash->blength; records=hash->records;
+
+ /* Search after record with key */
+
+ idx= my_hash_mask(calc_hash(hash, old_key, (old_key_length ?
+ old_key_length :
+ hash->key_length)),
+ blength, records);
+ new_index= my_hash_mask(rec_hashnr(hash, record), blength, records);
+ if (idx == new_index)
+ DBUG_RETURN(0); /* Nothing to do (No record check) */
+ previous=0;
+ for (;;)
+ {
+
+ if ((pos= data+idx)->data == record)
+ break;
+ previous=pos;
+ if ((idx=pos->next) == NO_RECORD)
+ DBUG_RETURN(1); /* Not found in links */
+ }
+ org_link= *pos;
+ empty=idx;
+
+ /* Relink record from current chain */
+
+ if (!previous)
+ {
+ if (pos->next != NO_RECORD)
+ {
+ empty=pos->next;
+ *pos= data[pos->next];
+ }
+ }
+ else
+ previous->next=pos->next; /* unlink pos */
+
+ /* Move data to correct position */
+ if (new_index == empty)
+ {
+ /*
+ At this point record is unlinked from the old chain, thus it holds
+ random position. By the chance this position is equal to position
+ for the first element in the new chain. That means updated record
+ is the only record in the new chain.
+ */
+ if (empty != idx)
+ {
+ /*
+ Record was moved while unlinking it from the old chain.
+ Copy data to a new position.
+ */
+ data[empty]= org_link;
+ }
+ data[empty].next= NO_RECORD;
+ DBUG_RETURN(0);
+ }
+ pos=data+new_index;
+ new_pos_index= my_hash_rec_mask(hash, pos, blength, records);
+ if (new_index != new_pos_index)
+ { /* Other record in wrong position */
+ data[empty] = *pos;
+ movelink(data,new_index,new_pos_index,(uint)empty);
+ org_link.next=NO_RECORD;
+ data[new_index]= org_link;
+ }
+ else
+ { /* Link in chain at right position */
+ org_link.next=data[new_index].next;
+ data[empty]=org_link;
+ data[new_index].next= (uint)empty;
+ }
+ DBUG_RETURN(0);
+}
+
+
+uchar *my_hash_element(HASH *hash, ulong idx)
+{
+ if (idx < hash->records)
+ return dynamic_element(&hash->array,idx,HASH_LINK*)->data;
+ return 0;
+}
+
+
+/*
+ Replace old row with new row. This should only be used when key
+ isn't changed
+*/
+
+void my_hash_replace(HASH *hash, HASH_SEARCH_STATE *current_record,
+ uchar *new_row)
+{
+ if (*current_record != NO_RECORD) /* Safety */
+ dynamic_element(&hash->array, *current_record, HASH_LINK*)->data= new_row;
+}
+
+
+#ifndef DBUG_OFF
+
+my_bool my_hash_check(HASH *hash)
+{
+ int error;
+ uint i,rec_link,found,max_links,seek,links,idx;
+ uint records;
+ size_t blength;
+ HASH_LINK *data,*hash_info;
+
+ records=hash->records; blength=hash->blength;
+ data=dynamic_element(&hash->array,0,HASH_LINK*);
+ error=0;
+
+ for (i=found=max_links=seek=0 ; i < records ; i++)
+ {
+ if (my_hash_rec_mask(hash, data + i, blength, records) == i)
+ {
+ found++; seek++; links=1;
+ for (idx=data[i].next ;
+ idx != NO_RECORD && found < records + 1;
+ idx=hash_info->next)
+ {
+ if (idx >= records)
+ {
+ DBUG_PRINT("error",
+ ("Found pointer outside array to %d from link starting at %d",
+ idx,i));
+ error=1;
+ }
+ hash_info=data+idx;
+ seek+= ++links;
+ if ((rec_link= my_hash_rec_mask(hash, hash_info,
+ blength, records)) != i)
+ {
+ DBUG_PRINT("error", ("Record in wrong link at %d: Start %d "
+ "Record: 0x%lx Record-link %d",
+ idx, i, (long) hash_info->data, rec_link));
+ error=1;
+ }
+ else
+ found++;
+ }
+ if (links > max_links) max_links=links;
+ }
+ }
+ if (found != records)
+ {
+ DBUG_PRINT("error",("Found %u of %u records", found, records));
+ error=1;
+ }
+ if (records)
+ DBUG_PRINT("info",
+ ("records: %u seeks: %d max links: %d hitrate: %.2f",
+ records,seek,max_links,(float) seek / (float) records));
+ return error;
+}
+#endif
diff --git a/mysql/mysys/lf_alloc-pin.c b/mysql/mysys/lf_alloc-pin.c
new file mode 100644
index 0000000..4f5dbed
--- /dev/null
+++ b/mysql/mysys/lf_alloc-pin.c
@@ -0,0 +1,470 @@
+/* QQ: TODO multi-pinbox */
+/* Copyright (c) 2006, 2015, Oracle and/or its affiliates. All rights reserved.
+
+ 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; version 2 of the License.
+
+ 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+/*
+ wait-free concurrent allocator based on pinning addresses
+
+ It works as follows: every thread (strictly speaking - every CPU, but
+ it's too difficult to do) has a small array of pointers. They're called
+ "pins". Before using an object its address must be stored in this array
+ (pinned). When an object is no longer necessary its address must be
+ removed from this array (unpinned). When a thread wants to free() an
+ object it scans all pins of all threads to see if somebody has this
+ object pinned. If yes - the object is not freed (but stored in a
+ "purgatory"). To reduce the cost of a single free() pins are not scanned
+ on every free() but only added to (thread-local) purgatory. On every
+ LF_PURGATORY_SIZE free() purgatory is scanned and all unpinned objects
+ are freed.
+
+ Pins are used to solve ABA problem. To use pins one must obey
+ a pinning protocol:
+
+ 1. Let's assume that PTR is a shared pointer to an object. Shared means
+ that any thread may modify it anytime to point to a different object
+ and free the old object. Later the freed object may be potentially
+ allocated by another thread. If we're unlucky that other thread may
+ set PTR to point to this object again. This is ABA problem.
+ 2. Create a local pointer LOCAL_PTR.
+ 3. Pin the PTR in a loop:
+ do
+ {
+ LOCAL_PTR= PTR;
+ pin(PTR, PIN_NUMBER);
+ } while (LOCAL_PTR != PTR)
+ 4. It is guaranteed that after the loop has ended, LOCAL_PTR
+ points to an object (or NULL, if PTR may be NULL), that
+ will never be freed. It is not guaranteed though
+ that LOCAL_PTR == PTR (as PTR can change any time)
+ 5. When done working with the object, remove the pin:
+ unpin(PIN_NUMBER)
+ 6. When copying pins (as in the list traversing loop:
+ pin(CUR, 1);
+ while ()
+ {
+ do // standard
+ { // pinning
+ NEXT=CUR->next; // loop
+ pin(NEXT, 0); // see #3
+ } while (NEXT != CUR->next); // above
+ ...
+ ...
+ CUR=NEXT;
+ pin(CUR, 1); // copy pin[0] to pin[1]
+ }
+ which keeps CUR address constantly pinned), note than pins may be
+ copied only upwards (!!!), that is pin[N] to pin[M], M > N.
+ 7. Don't keep the object pinned longer than necessary - the number of
+ pins you have is limited (and small), keeping an object pinned
+ prevents its reuse and cause unnecessary mallocs.
+
+ Explanations:
+
+ 3. The loop is important. The following can occur:
+ thread1> LOCAL_PTR= PTR
+ thread2> free(PTR); PTR=0;
+ thread1> pin(PTR, PIN_NUMBER);
+ now thread1 cannot access LOCAL_PTR, even if it's pinned,
+ because it points to a freed memory. That is, it *must*
+ verify that it has indeed pinned PTR, the shared pointer.
+
+ 6. When a thread wants to free some LOCAL_PTR, and it scans
+ all lists of pins to see whether it's pinned, it does it
+ upwards, from low pin numbers to high. Thus another thread
+ must copy an address from one pin to another in the same
+ direction - upwards, otherwise the scanning thread may
+ miss it.
+
+ Implementation details:
+
+ Pins are given away from a "pinbox". Pinbox is stack-based allocator.
+ It used dynarray for storing pins, new elements are allocated by dynarray
+ as necessary, old are pushed in the stack for reuse. ABA is solved by
+ versioning a pointer - because we use an array, a pointer to pins is 16 bit,
+ upper 16 bits are used for a version.
+*/
+#include "lf.h"
+#include "mysys_priv.h" /* key_memory_lf_node */
+
+#define LF_PINBOX_MAX_PINS 65536
+
+static void lf_pinbox_real_free(LF_PINS *pins);
+
+/*
+ Initialize a pinbox. Normally called from lf_alloc_init.
+ See the latter for details.
+*/
+void lf_pinbox_init(LF_PINBOX *pinbox, uint free_ptr_offset,
+ lf_pinbox_free_func *free_func, void *free_func_arg)
+{
+ DBUG_ASSERT(free_ptr_offset % sizeof(void *) == 0);
+ compile_time_assert(sizeof(LF_PINS) == 64);
+ lf_dynarray_init(&pinbox->pinarray, sizeof(LF_PINS));
+ pinbox->pinstack_top_ver= 0;
+ pinbox->pins_in_array= 0;
+ pinbox->free_ptr_offset= free_ptr_offset;
+ pinbox->free_func= free_func;
+ pinbox->free_func_arg= free_func_arg;
+}
+
+void lf_pinbox_destroy(LF_PINBOX *pinbox)
+{
+ lf_dynarray_destroy(&pinbox->pinarray);
+}
+
+/*
+ Get pins from a pinbox.
+
+ SYNOPSYS
+ pinbox -
+
+ DESCRIPTION
+ get a new LF_PINS structure from a stack of unused pins,
+ or allocate a new one out of dynarray.
+*/
+LF_PINS *lf_pinbox_get_pins(LF_PINBOX *pinbox)
+{
+ uint32 pins, next, top_ver;
+ LF_PINS *el;
+ /*
+ We have an array of max. 64k elements.
+ The highest index currently allocated is pinbox->pins_in_array.
+ Freed elements are in a lifo stack, pinstack_top_ver.
+ pinstack_top_ver is 32 bits; 16 low bits are the index in the
+ array, to the first element of the list. 16 high bits are a version
+ (every time the 16 low bits are updated, the 16 high bits are
+ incremented). Versioning prevents the ABA problem.
+ */
+ top_ver= pinbox->pinstack_top_ver;
+ do
+ {
+ if (!(pins= top_ver % LF_PINBOX_MAX_PINS))
+ {
+ /* the stack of free elements is empty */
+ pins= my_atomic_add32((int32 volatile*) &pinbox->pins_in_array, 1)+1;
+ if (unlikely(pins >= LF_PINBOX_MAX_PINS))
+ return 0;
+ /*
+ note that the first allocated element has index 1 (pins==1).
+ index 0 is reserved to mean "NULL pointer"
+ */
+ el= (LF_PINS *)lf_dynarray_lvalue(&pinbox->pinarray, pins);
+ if (unlikely(!el))
+ return 0;
+ break;
+ }
+ el= (LF_PINS *)lf_dynarray_value(&pinbox->pinarray, pins);
+ next= el->link;
+ } while (!my_atomic_cas32((int32 volatile*) &pinbox->pinstack_top_ver,
+ (int32*) &top_ver,
+ top_ver-pins+next+LF_PINBOX_MAX_PINS));
+ /*
+ set el->link to the index of el in the dynarray (el->link has two usages:
+ - if element is allocated, it's its own index
+ - if element is free, it's its next element in the free stack
+ */
+ el->link= pins;
+ el->purgatory_count= 0;
+ el->pinbox= pinbox;
+ return el;
+}
+
+/*
+ Put pins back to a pinbox.
+
+ DESCRIPTION
+ empty the purgatory (XXX deadlock warning below!),
+ push LF_PINS structure to a stack
+*/
+void lf_pinbox_put_pins(LF_PINS *pins)
+{
+ LF_PINBOX *pinbox= pins->pinbox;
+ uint32 top_ver, nr;
+ nr= pins->link;
+
+#ifndef DBUG_OFF
+ {
+ /* This thread should not hold any pin. */
+ int i;
+ for (i= 0; i < LF_PINBOX_PINS; i++)
+ DBUG_ASSERT(pins->pin[i] == 0);
+ }
+#endif /* DBUG_OFF */
+
+ /*
+ XXX this will deadlock if other threads will wait for
+ the caller to do something after _lf_pinbox_put_pins(),
+ and they would have pinned addresses that the caller wants to free.
+ Thus: only free pins when all work is done and nobody can wait for you!!!
+ */
+ while (pins->purgatory_count)
+ {
+ lf_pinbox_real_free(pins);
+ if (pins->purgatory_count)
+ {
+ my_thread_yield();
+ }
+ }
+ top_ver= pinbox->pinstack_top_ver;
+ do
+ {
+ pins->link= top_ver % LF_PINBOX_MAX_PINS;
+ } while (!my_atomic_cas32((int32 volatile*) &pinbox->pinstack_top_ver,
+ (int32*) &top_ver,
+ top_ver-pins->link+nr+LF_PINBOX_MAX_PINS));
+}
+
+/*
+ Get the next pointer in the purgatory list.
+ Note that next_node is not used to avoid the extra volatile.
+*/
+#define pnext_node(P, X) (*((void **)(((char *)(X)) + (P)->free_ptr_offset)))
+
+static inline void add_to_purgatory(LF_PINS *pins, void *addr)
+{
+ pnext_node(pins->pinbox, addr)= pins->purgatory;
+ pins->purgatory= addr;
+ pins->purgatory_count++;
+}
+
+/*
+ Free an object allocated via pinbox allocator
+
+ DESCRIPTION
+ add an object to purgatory. if necessary, call lf_pinbox_real_free()
+ to actually free something.
+*/
+void lf_pinbox_free(LF_PINS *pins, void *addr)
+{
+ add_to_purgatory(pins, addr);
+ if (pins->purgatory_count % LF_PURGATORY_SIZE == 0)
+ lf_pinbox_real_free(pins);
+}
+
+struct st_match_and_save_arg {
+ LF_PINS *pins;
+ LF_PINBOX *pinbox;
+ void *old_purgatory;
+};
+
+/*
+ Callback for lf_dynarray_iterate:
+ Scan all pins of all threads, for each active (non-null) pin,
+ scan the current thread's purgatory. If present there, move it
+ to a new purgatory. At the end, the old purgatory will contain
+ pointers not pinned by any thread.
+*/
+static int match_and_save(LF_PINS *el, struct st_match_and_save_arg *arg)
+{
+ int i;
+ LF_PINS *el_end= el + LF_DYNARRAY_LEVEL_LENGTH;
+ for (; el < el_end; el++)
+ {
+ for (i= 0; i < LF_PINBOX_PINS; i++)
+ {
+ void *p= el->pin[i];
+ if (p)
+ {
+ void *cur= arg->old_purgatory;
+ void **list_prev= &arg->old_purgatory;
+ while (cur)
+ {
+ void *next= pnext_node(arg->pinbox, cur);
+
+ if (p == cur)
+ {
+ /* pinned - keeping */
+ add_to_purgatory(arg->pins, cur);
+ /* unlink from old purgatory */
+ *list_prev= next;
+ }
+ else
+ list_prev= (void **)((char *)cur+arg->pinbox->free_ptr_offset);
+ cur= next;
+ }
+ if (!arg->old_purgatory)
+ return 1;
+ }
+ }
+ }
+ return 0;
+}
+
+/*
+ Scan the purgatory and free everything that can be freed
+*/
+static void lf_pinbox_real_free(LF_PINS *pins)
+{
+ LF_PINBOX *pinbox= pins->pinbox;
+
+ /* Store info about current purgatory. */
+ struct st_match_and_save_arg arg = {pins, pinbox, pins->purgatory};
+ /* Reset purgatory. */
+ pins->purgatory= NULL;
+ pins->purgatory_count= 0;
+
+ lf_dynarray_iterate(&pinbox->pinarray,
+ (lf_dynarray_func)match_and_save, &arg);
+
+ if (arg.old_purgatory)
+ {
+ /* Some objects in the old purgatory were not pinned, free them. */
+ void *last= arg.old_purgatory;
+ while (pnext_node(pinbox, last))
+ last= pnext_node(pinbox, last);
+ pinbox->free_func(arg.old_purgatory, last, pinbox->free_func_arg);
+ }
+}
+
+#define next_node(P, X) (*((uchar * volatile *)(((uchar *)(X)) + (P)->free_ptr_offset)))
+#define anext_node(X) next_node(&allocator->pinbox, (X))
+
+/* lock-free memory allocator for fixed-size objects */
+
+LF_REQUIRE_PINS(1)
+
+/*
+ callback for lf_pinbox_real_free to free a list of unpinned objects -
+ add it back to the allocator stack
+
+ DESCRIPTION
+ 'first' and 'last' are the ends of the linked list of nodes:
+ first->el->el->....->el->last. Use first==last to free only one element.
+*/
+static void alloc_free(uchar *first,
+ uchar volatile *last,
+ LF_ALLOCATOR *allocator)
+{
+ /*
+ we need a union here to access type-punned pointer reliably.
+ otherwise gcc -fstrict-aliasing will not see 'tmp' changed in the loop
+ */
+ union { uchar * node; void *ptr; } tmp;
+ tmp.node= allocator->top;
+ do
+ {
+ anext_node(last)= tmp.node;
+ } while (!my_atomic_casptr((void **)(char *)&allocator->top,
+ (void **)&tmp.ptr, first) && LF_BACKOFF);
+}
+
+/**
+ Initialize lock-free allocator.
+
+ @param allocator Allocator structure to initialize.
+ @param size A size of an object to allocate.
+ @param free_ptr_offset An offset inside the object to a sizeof(void *)
+ memory that is guaranteed to be unused after
+ the object is put in the purgatory. Unused by
+ ANY thread, not only the purgatory owner.
+ This memory will be used to link
+ waiting-to-be-freed objects in a purgatory list.
+ @param ctor Function to be called after object was
+ malloc()'ed.
+ @param dtor Function to be called before object is free()'d.
+*/
+
+void lf_alloc_init2(LF_ALLOCATOR *allocator, uint size, uint free_ptr_offset,
+ lf_allocator_func *ctor, lf_allocator_func *dtor)
+{
+ lf_pinbox_init(&allocator->pinbox, free_ptr_offset,
+ (lf_pinbox_free_func *)alloc_free, allocator);
+ allocator->top= 0;
+ allocator->mallocs= 0;
+ allocator->element_size= size;
+ allocator->constructor= ctor;
+ allocator->destructor= dtor;
+ DBUG_ASSERT(size >= sizeof(void*) + free_ptr_offset);
+}
+
+/*
+ destroy the allocator, free everything that's in it
+
+ NOTE
+ As every other init/destroy function here and elsewhere it
+ is not thread safe. No, this function is no different, ensure
+ that no thread needs the allocator before destroying it.
+ We are not responsible for any damage that may be caused by
+ accessing the allocator when it is being or has been destroyed.
+ Oh yes, and don't put your cat in a microwave.
+*/
+void lf_alloc_destroy(LF_ALLOCATOR *allocator)
+{
+ uchar *node= allocator->top;
+ while (node)
+ {
+ uchar *tmp= anext_node(node);
+ if (allocator->destructor)
+ allocator->destructor(node);
+ my_free(node);
+ node= tmp;
+ }
+ lf_pinbox_destroy(&allocator->pinbox);
+ allocator->top= 0;
+}
+
+/*
+ Allocate and return an new object.
+
+ DESCRIPTION
+ Pop an unused object from the stack or malloc it is the stack is empty.
+ pin[0] is used, it's removed on return.
+*/
+void *lf_alloc_new(LF_PINS *pins)
+{
+ LF_ALLOCATOR *allocator= (LF_ALLOCATOR *)(pins->pinbox->free_func_arg);
+ uchar *node;
+ for (;;)
+ {
+ do
+ {
+ node= allocator->top;
+ lf_pin(pins, 0, node);
+ } while (node != allocator->top && LF_BACKOFF);
+ if (!node)
+ {
+ node= (void *)my_malloc(key_memory_lf_node,
+ allocator->element_size, MYF(MY_WME));
+ if (allocator->constructor)
+ allocator->constructor(node);
+#ifdef MY_LF_EXTRA_DEBUG
+ if (likely(node != 0))
+ my_atomic_add32(&allocator->mallocs, 1);
+#endif
+ break;
+ }
+ if (my_atomic_casptr((void **)(char *)&allocator->top,
+ (void *)&node, anext_node(node)))
+ break;
+ }
+ lf_unpin(pins, 0);
+ return node;
+}
+
+/*
+ count the number of objects in a pool.
+
+ NOTE
+ This is NOT thread-safe !!!
+*/
+uint lf_alloc_pool_count(LF_ALLOCATOR *allocator)
+{
+ uint i;
+ uchar *node;
+ for (node= allocator->top, i= 0; node; node= anext_node(node), i++)
+ /* no op */;
+ return i;
+}
+
diff --git a/mysql/mysys/lf_dynarray.c b/mysql/mysys/lf_dynarray.c
new file mode 100644
index 0000000..c5cb50f
--- /dev/null
+++ b/mysql/mysys/lf_dynarray.c
@@ -0,0 +1,208 @@
+/* Copyright (c) 2006, 2013, Oracle and/or its affiliates. All rights reserved.
+
+ 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; version 2 of the License.
+
+ 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+/*
+ Analog of DYNAMIC_ARRAY that never reallocs
+ (so no pointer into the array may ever become invalid).
+
+ Memory is allocated in non-contiguous chunks.
+ This data structure is not space efficient for sparse arrays.
+
+ Every element is aligned to sizeof(element) boundary
+ (to avoid false sharing if element is big enough).
+
+ LF_DYNARRAY is a recursive structure. On the zero level
+ LF_DYNARRAY::level[0] it's an array of LF_DYNARRAY_LEVEL_LENGTH elements,
+ on the first level it's an array of LF_DYNARRAY_LEVEL_LENGTH pointers
+ to arrays of elements, on the second level it's an array of pointers
+ to arrays of pointers to arrays of elements. And so on.
+
+ With four levels the number of elements is limited to 4311810304
+ (but as in all functions index is uint, the real limit is 2^32-1)
+
+ Actually, it's wait-free, not lock-free ;-)
+*/
+
+#include <my_global.h>
+#include <m_string.h>
+#include <my_sys.h>
+#include <mysys_priv.h>
+#include <lf.h>
+
+void lf_dynarray_init(LF_DYNARRAY *array, uint element_size)
+{
+ memset(array, 0, sizeof(*array));
+ array->size_of_element= element_size;
+}
+
+static void recursive_free(void **alloc, int level)
+{
+ if (!alloc)
+ return;
+
+ if (level)
+ {
+ int i;
+ for (i= 0; i < LF_DYNARRAY_LEVEL_LENGTH; i++)
+ recursive_free(alloc[i], level-1);
+ my_free(alloc);
+ }
+ else
+ my_free(alloc[-1]);
+}
+
+void lf_dynarray_destroy(LF_DYNARRAY *array)
+{
+ int i;
+ for (i= 0; i < LF_DYNARRAY_LEVELS; i++)
+ recursive_free(array->level[i], i);
+}
+
+static const ulong dynarray_idxes_in_prev_levels[LF_DYNARRAY_LEVELS]=
+{
+ 0, /* +1 here to to avoid -1's below */
+ LF_DYNARRAY_LEVEL_LENGTH,
+ LF_DYNARRAY_LEVEL_LENGTH * LF_DYNARRAY_LEVEL_LENGTH +
+ LF_DYNARRAY_LEVEL_LENGTH,
+ LF_DYNARRAY_LEVEL_LENGTH * LF_DYNARRAY_LEVEL_LENGTH *
+ LF_DYNARRAY_LEVEL_LENGTH + LF_DYNARRAY_LEVEL_LENGTH *
+ LF_DYNARRAY_LEVEL_LENGTH + LF_DYNARRAY_LEVEL_LENGTH
+};
+
+static const ulong dynarray_idxes_in_prev_level[LF_DYNARRAY_LEVELS]=
+{
+ 0, /* +1 here to to avoid -1's below */
+ LF_DYNARRAY_LEVEL_LENGTH,
+ LF_DYNARRAY_LEVEL_LENGTH * LF_DYNARRAY_LEVEL_LENGTH,
+ LF_DYNARRAY_LEVEL_LENGTH * LF_DYNARRAY_LEVEL_LENGTH *
+ LF_DYNARRAY_LEVEL_LENGTH,
+};
+
+/*
+ Returns a valid lvalue pointer to the element number 'idx'.
+ Allocates memory if necessary.
+*/
+void *lf_dynarray_lvalue(LF_DYNARRAY *array, uint idx)
+{
+ void * ptr, * volatile * ptr_ptr= 0;
+ int i;
+
+ for (i= LF_DYNARRAY_LEVELS-1; idx < dynarray_idxes_in_prev_levels[i]; i--)
+ /* no-op */;
+ ptr_ptr= &array->level[i];
+ idx-= dynarray_idxes_in_prev_levels[i];
+ for (; i > 0; i--)
+ {
+ if (!(ptr= *ptr_ptr))
+ {
+ void *alloc= my_malloc(key_memory_lf_dynarray,
+ LF_DYNARRAY_LEVEL_LENGTH * sizeof(void *),
+ MYF(MY_WME|MY_ZEROFILL));
+ if (unlikely(!alloc))
+ return(NULL);
+ if (my_atomic_casptr(ptr_ptr, &ptr, alloc))
+ ptr= alloc;
+ else
+ my_free(alloc);
+ }
+ ptr_ptr= ((void **)ptr) + idx / dynarray_idxes_in_prev_level[i];
+ idx%= dynarray_idxes_in_prev_level[i];
+ }
+ if (!(ptr= *ptr_ptr))
+ {
+ uchar *alloc, *data;
+ alloc= my_malloc(key_memory_lf_dynarray,
+ LF_DYNARRAY_LEVEL_LENGTH * array->size_of_element +
+ MY_MAX(array->size_of_element, sizeof(void *)),
+ MYF(MY_WME|MY_ZEROFILL));
+ if (unlikely(!alloc))
+ return(NULL);
+ /* reserve the space for free() address */
+ data= alloc + sizeof(void *);
+ { /* alignment */
+ intptr mod= ((intptr)data) % array->size_of_element;
+ if (mod)
+ data+= array->size_of_element - mod;
+ }
+ ((void **)data)[-1]= alloc; /* free() will need the original pointer */
+ if (my_atomic_casptr(ptr_ptr, &ptr, data))
+ ptr= data;
+ else
+ my_free(alloc);
+ }
+ return ((uchar*)ptr) + array->size_of_element * idx;
+}
+
+/*
+ Returns a pointer to the element number 'idx'
+ or NULL if an element does not exists
+*/
+void *lf_dynarray_value(LF_DYNARRAY *array, uint idx)
+{
+ void * ptr, * volatile * ptr_ptr= 0;
+ int i;
+
+ for (i= LF_DYNARRAY_LEVELS-1; idx < dynarray_idxes_in_prev_levels[i]; i--)
+ /* no-op */;
+ ptr_ptr= &array->level[i];
+ idx-= dynarray_idxes_in_prev_levels[i];
+ for (; i > 0; i--)
+ {
+ if (!(ptr= *ptr_ptr))
+ return(NULL);
+ ptr_ptr= ((void **)ptr) + idx / dynarray_idxes_in_prev_level[i];
+ idx %= dynarray_idxes_in_prev_level[i];
+ }
+ if (!(ptr= *ptr_ptr))
+ return(NULL);
+ return ((uchar*)ptr) + array->size_of_element * idx;
+}
+
+static int recursive_iterate(LF_DYNARRAY *array, void *ptr, int level,
+ lf_dynarray_func func, void *arg)
+{
+ int res, i;
+ if (!ptr)
+ return 0;
+ if (!level)
+ return func(ptr, arg);
+ for (i= 0; i < LF_DYNARRAY_LEVEL_LENGTH; i++)
+ if ((res= recursive_iterate(array, ((void **)ptr)[i], level-1, func, arg)))
+ return res;
+ return 0;
+}
+
+/*
+ Calls func(array, arg) on every array of LF_DYNARRAY_LEVEL_LENGTH elements
+ in lf_dynarray.
+
+ DESCRIPTION
+ lf_dynarray consists of a set of arrays, LF_DYNARRAY_LEVEL_LENGTH elements
+ each. lf_dynarray_iterate() calls user-supplied function on every array
+ from the set. It is the fastest way to scan the array, faster than
+ for (i=0; i < N; i++) { func(lf_dynarray_value(dynarray, i)); }
+
+ NOTE
+ if func() returns non-zero, the scan is aborted
+*/
+int lf_dynarray_iterate(LF_DYNARRAY *array, lf_dynarray_func func, void *arg)
+{
+ int i, res;
+ for (i= 0; i < LF_DYNARRAY_LEVELS; i++)
+ if ((res= recursive_iterate(array, array->level[i], i, func, arg)))
+ return res;
+ return 0;
+}
+
diff --git a/mysql/mysys/lf_hash.c b/mysql/mysys/lf_hash.c
new file mode 100644
index 0000000..b55734f
--- /dev/null
+++ b/mysql/mysys/lf_hash.c
@@ -0,0 +1,722 @@
+/* Copyright (c) 2006, 2015, Oracle and/or its affiliates. All rights reserved.
+
+ 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; version 2 of the License.
+
+ 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+/*
+ extensible hash
+
+ TODO
+ try to get rid of dummy nodes ?
+ for non-unique hash, count only _distinct_ values
+ (but how to do it in lf_hash_delete ?)
+*/
+#include <my_global.h>
+#include <m_string.h>
+#include <my_sys.h>
+#include <mysys_priv.h>
+#include <my_bit.h>
+#include <lf.h>
+
+LF_REQUIRE_PINS(3)
+
+/* An element of the list */
+typedef struct {
+ intptr volatile link; /* a pointer to the next element in a listand a flag */
+ uint32 hashnr; /* reversed hash number, for sorting */
+ const uchar *key;
+ size_t keylen;
+ /*
+ data is stored here, directly after the keylen.
+ thus the pointer to data is (void*)(slist_element_ptr+1)
+ */
+} LF_SLIST;
+
+const int LF_HASH_OVERHEAD= sizeof(LF_SLIST);
+
+/*
+ a structure to pass the context (pointers two the three successive elements
+ in a list) from my_lfind to linsert/ldelete
+*/
+typedef struct {
+ intptr volatile *prev;
+ LF_SLIST *curr, *next;
+} CURSOR;
+
+/*
+ the last bit in LF_SLIST::link is a "deleted" flag.
+ the helper macros below convert it to a pure pointer or a pure flag
+*/
+#define PTR(V) (LF_SLIST *)((V) & (~(intptr)1))
+#define DELETED(V) ((V) & 1)
+
+/*
+ DESCRIPTION
+ Search for hashnr/key/keylen in the list starting from 'head' and
+ position the cursor. The list is ORDER BY hashnr, key
+
+ RETURN
+ 0 - not found
+ 1 - found
+
+ NOTE
+ cursor is positioned in either case
+ pins[0..2] are used, they are NOT removed on return
+*/
+static int my_lfind(LF_SLIST * volatile *head, CHARSET_INFO *cs, uint32 hashnr,
+ const uchar *key, size_t keylen, CURSOR *cursor, LF_PINS *pins)
+{
+ uint32 cur_hashnr;
+ const uchar *cur_key;
+ size_t cur_keylen;
+ intptr link;
+
+retry:
+ cursor->prev= (intptr *)head;
+ do { /* PTR() isn't necessary below, head is a dummy node */
+ cursor->curr= (LF_SLIST *)(*cursor->prev);
+ lf_pin(pins, 1, cursor->curr);
+ } while (*cursor->prev != (intptr)cursor->curr && LF_BACKOFF);
+ for (;;)
+ {
+ if (unlikely(!cursor->curr))
+ return 0; /* end of the list */
+ do {
+ /* QQ: XXX or goto retry ? */
+ link= cursor->curr->link;
+ cursor->next= PTR(link);
+ lf_pin(pins, 0, cursor->next);
+ } while (link != cursor->curr->link && LF_BACKOFF);
+ cur_hashnr= cursor->curr->hashnr;
+ cur_key= cursor->curr->key;
+ cur_keylen= cursor->curr->keylen;
+ if (*cursor->prev != (intptr)cursor->curr)
+ {
+ (void)LF_BACKOFF;
+ goto retry;
+ }
+ if (!DELETED(link))
+ {
+ if (cur_hashnr >= hashnr)
+ {
+ int r= 1;
+ if (cur_hashnr > hashnr ||
+ (r= my_strnncoll(cs, (uchar*) cur_key, cur_keylen, (uchar*) key,
+ keylen)) >= 0)
+ return !r;
+ }
+ cursor->prev= &(cursor->curr->link);
+ lf_pin(pins, 2, cursor->curr);
+ }
+ else
+ {
+ /*
+ we found a deleted node - be nice, help the other thread
+ and remove this deleted node
+ */
+ if (my_atomic_casptr((void **)cursor->prev,
+ (void **)&cursor->curr, cursor->next))
+ lf_pinbox_free(pins, cursor->curr);
+ else
+ {
+ (void)LF_BACKOFF;
+ goto retry;
+ }
+ }
+ cursor->curr= cursor->next;
+ lf_pin(pins, 1, cursor->curr);
+ }
+}
+
+
+/**
+ Search for list element satisfying condition specified by match
+ function and position cursor on it.
+
+ @param head Head of the list to search in.
+ @param first_hashnr Hash value to start search from.
+ @param last_hashnr Hash value to stop search after.
+ @param match Match function.
+ @param cursor Cursor to be position.
+ @param pins LF_PINS for the calling thread to be used during
+ search for pinning result.
+
+ @retval 0 - not found
+ @retval 1 - found
+*/
+
+static int my_lfind_match(LF_SLIST * volatile *head,
+ uint32 first_hashnr, uint32 last_hashnr,
+ lf_hash_match_func *match,
+ CURSOR *cursor, LF_PINS *pins)
+{
+ uint32 cur_hashnr;
+ intptr link;
+
+retry:
+ cursor->prev= (intptr *)head;
+ do { /* PTR() isn't necessary below, head is a dummy node */
+ cursor->curr= (LF_SLIST *)(*cursor->prev);
+ lf_pin(pins, 1, cursor->curr);
+ } while (*cursor->prev != (intptr)cursor->curr && LF_BACKOFF);
+ for (;;)
+ {
+ if (unlikely(!cursor->curr))
+ return 0; /* end of the list */
+ do {
+ /* QQ: XXX or goto retry ? */
+ link= cursor->curr->link;
+ cursor->next= PTR(link);
+ lf_pin(pins, 0, cursor->next);
+ } while (link != cursor->curr->link && LF_BACKOFF);
+ cur_hashnr= cursor->curr->hashnr;
+ if (*cursor->prev != (intptr)cursor->curr)
+ {
+ (void)LF_BACKOFF;
+ goto retry;
+ }
+ if (!DELETED(link))
+ {
+ if (cur_hashnr >= first_hashnr)
+ {
+ if (cur_hashnr > last_hashnr)
+ return 0;
+
+ if (cur_hashnr & 1)
+ {
+ /* Normal node. Check if element matches condition. */
+ if ((*match)((uchar *)(cursor->curr + 1)))
+ return 1;
+ }
+ else
+ {
+ /*
+ Dummy node. Nothing to check here.
+
+ Still thanks to the fact that dummy nodes are never deleted we
+ can save it as a safe place to restart iteration if ever needed.
+ */
+ head= (LF_SLIST * volatile *)&(cursor->curr->link);
+ }
+ }
+
+ cursor->prev= &(cursor->curr->link);
+ lf_pin(pins, 2, cursor->curr);
+ }
+ else
+ {
+ /*
+ we found a deleted node - be nice, help the other thread
+ and remove this deleted node
+ */
+ if (my_atomic_casptr((void **)cursor->prev,
+ (void **)&cursor->curr, cursor->next))
+ lf_pinbox_free(pins, cursor->curr);
+ else
+ {
+ (void)LF_BACKOFF;
+ goto retry;
+ }
+ }
+ cursor->curr= cursor->next;
+ lf_pin(pins, 1, cursor->curr);
+ }
+}
+
+
+/*
+ DESCRIPTION
+ insert a 'node' in the list that starts from 'head' in the correct
+ position (as found by my_lfind)
+
+ RETURN
+ 0 - inserted
+ not 0 - a pointer to a duplicate (not pinned and thus unusable)
+
+ NOTE
+ it uses pins[0..2], on return all pins are removed.
+ if there're nodes with the same key value, a new node is added before them.
+*/
+static LF_SLIST *linsert(LF_SLIST * volatile *head, CHARSET_INFO *cs,
+ LF_SLIST *node, LF_PINS *pins, uint flags)
+{
+ CURSOR cursor;
+ int res;
+
+ for (;;)
+ {
+ if (my_lfind(head, cs, node->hashnr, node->key, node->keylen,
+ &cursor, pins) &&
+ (flags & LF_HASH_UNIQUE))
+ {
+ res= 0; /* duplicate found */
+ break;
+ }
+ else
+ {
+ node->link= (intptr)cursor.curr;
+ DBUG_ASSERT(node->link != (intptr)node); /* no circular references */
+ DBUG_ASSERT(cursor.prev != &node->link); /* no circular references */
+ if (my_atomic_casptr((void **)cursor.prev, (void **)&cursor.curr, node))
+ {
+ res= 1; /* inserted ok */
+ break;
+ }
+ }
+ }
+ lf_unpin(pins, 0);
+ lf_unpin(pins, 1);
+ lf_unpin(pins, 2);
+ /*
+ Note that cursor.curr is not pinned here and the pointer is unreliable,
+ the object may dissapear anytime. But if it points to a dummy node, the
+ pointer is safe, because dummy nodes are never freed - initialize_bucket()
+ uses this fact.
+ */
+ return res ? 0 : cursor.curr;
+}
+
+/*
+ DESCRIPTION
+ deletes a node as identified by hashnr/keey/keylen from the list
+ that starts from 'head'
+
+ RETURN
+ 0 - ok
+ 1 - not found
+
+ NOTE
+ it uses pins[0..2], on return all pins are removed.
+*/
+static int ldelete(LF_SLIST * volatile *head, CHARSET_INFO *cs, uint32 hashnr,
+ const uchar *key, uint keylen, LF_PINS *pins)
+{
+ CURSOR cursor;
+ int res;
+
+ for (;;)
+ {
+ if (!my_lfind(head, cs, hashnr, key, keylen, &cursor, pins))
+ {
+ res= 1; /* not found */
+ break;
+ }
+ else
+ {
+ /* mark the node deleted */
+ if (my_atomic_casptr((void **)&(cursor.curr->link),
+ (void **)&cursor.next,
+ (void *)(((intptr)cursor.next) | 1)))
+ {
+ /* and remove it from the list */
+ if (my_atomic_casptr((void **)cursor.prev,
+ (void **)&cursor.curr, cursor.next))
+ lf_pinbox_free(pins, cursor.curr);
+ else
+ {
+ /*
+ somebody already "helped" us and removed the node ?
+ Let's check if we need to help that someone too!
+ (to ensure the number of "set DELETED flag" actions
+ is equal to the number of "remove from the list" actions)
+ */
+ my_lfind(head, cs, hashnr, key, keylen, &cursor, pins);
+ }
+ res= 0;
+ break;
+ }
+ }
+ }
+ lf_unpin(pins, 0);
+ lf_unpin(pins, 1);
+ lf_unpin(pins, 2);
+ return res;
+}
+
+/*
+ DESCRIPTION
+ searches for a node as identified by hashnr/keey/keylen in the list
+ that starts from 'head'
+
+ RETURN
+ 0 - not found
+ node - found
+
+ NOTE
+ it uses pins[0..2], on return the pin[2] keeps the node found
+ all other pins are removed.
+*/
+static LF_SLIST *my_lsearch(LF_SLIST * volatile *head, CHARSET_INFO *cs,
+ uint32 hashnr, const uchar *key, uint keylen,
+ LF_PINS *pins)
+{
+ CURSOR cursor;
+ int res= my_lfind(head, cs, hashnr, key, keylen, &cursor, pins);
+ if (res)
+ lf_pin(pins, 2, cursor.curr);
+ lf_unpin(pins, 0);
+ lf_unpin(pins, 1);
+ return res ? cursor.curr : 0;
+}
+
+static inline const uchar* hash_key(const LF_HASH *hash,
+ const uchar *record, size_t *length)
+{
+ if (hash->get_key)
+ return (*hash->get_key)(record, length, 0);
+ *length= hash->key_length;
+ return record + hash->key_offset;
+}
+
+/*
+ Compute the hash key value from the raw key.
+
+ @note, that the hash value is limited to 2^31, because we need one
+ bit to distinguish between normal and dummy nodes.
+*/
+static inline uint calc_hash(LF_HASH *hash, const uchar *key, size_t keylen)
+{
+ return (hash->hash_function(hash, key, keylen)) & INT_MAX32;
+}
+
+#define MAX_LOAD 1.0 /* average number of elements in a bucket */
+
+static int initialize_bucket(LF_HASH *, LF_SLIST * volatile*, uint, LF_PINS *);
+
+
+/**
+ Adaptor function which allows to use hash function from character
+ set with LF_HASH.
+*/
+static uint cset_hash_sort_adapter(const LF_HASH *hash, const uchar *key,
+ size_t length)
+{
+ ulong nr1=1, nr2=4;
+ hash->charset->coll->hash_sort(hash->charset, key, length, &nr1, &nr2);
+ return (uint)nr1;
+}
+
+
+/*
+ Initializes lf_hash, the arguments are compatible with hash_init
+
+ @note element_size sets both the size of allocated memory block for
+ lf_alloc and a size of memcpy'ed block size in lf_hash_insert. Typically
+ they are the same, indeed. But LF_HASH::element_size can be decreased
+ after lf_hash_init, and then lf_alloc will allocate larger block that
+ lf_hash_insert will copy over. It is desireable if part of the element
+ is expensive to initialize - for example if there is a mutex or
+ DYNAMIC_ARRAY. In this case they should be initialize in the
+ LF_ALLOCATOR::constructor, and lf_hash_insert should not overwrite them.
+ See wt_init() for example.
+ As an alternative to using the above trick with decreasing
+ LF_HASH::element_size one can provide an "initialize" hook that will finish
+ initialization of object provided by LF_ALLOCATOR and set element key from
+ object passed as parameter to lf_hash_insert instead of doing simple memcpy.
+*/
+void lf_hash_init2(LF_HASH *hash, uint element_size, uint flags,
+ uint key_offset, uint key_length, my_hash_get_key get_key,
+ CHARSET_INFO *charset, lf_hash_func *hash_function,
+ lf_allocator_func *ctor, lf_allocator_func *dtor,
+ lf_hash_init_func *init)
+{
+ lf_alloc_init2(&hash->alloc, sizeof(LF_SLIST)+element_size,
+ offsetof(LF_SLIST, key), ctor, dtor);
+ lf_dynarray_init(&hash->array, sizeof(LF_SLIST *));
+ hash->size= 1;
+ hash->count= 0;
+ hash->element_size= element_size;
+ hash->flags= flags;
+ hash->charset= charset ? charset : &my_charset_bin;
+ hash->key_offset= key_offset;
+ hash->key_length= key_length;
+ hash->get_key= get_key;
+ hash->hash_function= hash_function ? hash_function : cset_hash_sort_adapter;
+ hash->initialize= init;
+ DBUG_ASSERT(get_key ? !key_offset && !key_length : key_length);
+}
+
+void lf_hash_destroy(LF_HASH *hash)
+{
+ LF_SLIST *el, **head= (LF_SLIST **)lf_dynarray_value(&hash->array, 0);
+
+ if (unlikely(!head))
+ return;
+ el= *head;
+
+ while (el)
+ {
+ intptr next= el->link;
+ if (el->hashnr & 1)
+ lf_alloc_direct_free(&hash->alloc, el); /* normal node */
+ else
+ my_free(el); /* dummy node */
+ el= (LF_SLIST *)next;
+ }
+ lf_alloc_destroy(&hash->alloc);
+ lf_dynarray_destroy(&hash->array);
+}
+
+/*
+ DESCRIPTION
+ inserts a new element to a hash. it will have a _copy_ of
+ data, not a pointer to it.
+
+ RETURN
+ 0 - inserted
+ 1 - didn't (unique key conflict)
+ -1 - out of memory
+
+ NOTE
+ see linsert() for pin usage notes
+*/
+int lf_hash_insert(LF_HASH *hash, LF_PINS *pins, const void *data)
+{
+ int csize, bucket, hashnr;
+ LF_SLIST *node, * volatile *el;
+
+ node= (LF_SLIST *)lf_alloc_new(pins);
+ if (unlikely(!node))
+ return -1;
+ if (hash->initialize)
+ (*hash->initialize)((uchar*)(node + 1), (const uchar*)data);
+ else
+ memcpy(node+1, data, hash->element_size);
+ node->key= hash_key(hash, (uchar *)(node+1), &node->keylen);
+ hashnr= calc_hash(hash, node->key, node->keylen);
+ bucket= hashnr % hash->size;
+ el= lf_dynarray_lvalue(&hash->array, bucket);
+ if (unlikely(!el))
+ return -1;
+ if (*el == NULL && unlikely(initialize_bucket(hash, el, bucket, pins)))
+ return -1;
+ node->hashnr= my_reverse_bits(hashnr) | 1; /* normal node */
+ if (linsert(el, hash->charset, node, pins, hash->flags))
+ {
+ lf_pinbox_free(pins, node);
+ return 1;
+ }
+ csize= hash->size;
+ if ((my_atomic_add32(&hash->count, 1)+1.0) / csize > MAX_LOAD)
+ my_atomic_cas32(&hash->size, &csize, csize*2);
+ return 0;
+}
+
+/*
+ DESCRIPTION
+ deletes an element with the given key from the hash (if a hash is
+ not unique and there're many elements with this key - the "first"
+ matching element is deleted)
+ RETURN
+ 0 - deleted
+ 1 - didn't (not found)
+ -1 - out of memory
+ NOTE
+ see ldelete() for pin usage notes
+*/
+int lf_hash_delete(LF_HASH *hash, LF_PINS *pins, const void *key, uint keylen)
+{
+ LF_SLIST * volatile *el;
+ uint bucket, hashnr= calc_hash(hash, (uchar *)key, keylen);
+
+ bucket= hashnr % hash->size;
+ el= lf_dynarray_lvalue(&hash->array, bucket);
+ if (unlikely(!el))
+ return -1;
+ /*
+ note that we still need to initialize_bucket here,
+ we cannot return "node not found", because an old bucket of that
+ node may've been split and the node was assigned to a new bucket
+ that was never accessed before and thus is not initialized.
+ */
+ if (*el == NULL && unlikely(initialize_bucket(hash, el, bucket, pins)))
+ return -1;
+ if (ldelete(el, hash->charset, my_reverse_bits(hashnr) | 1,
+ (uchar *)key, keylen, pins))
+ {
+ return 1;
+ }
+ my_atomic_add32(&hash->count, -1);
+ return 0;
+}
+
+
+/**
+ Find hash element corresponding to the key.
+
+ @param hash The hash to search element in.
+ @param pins Pins for the calling thread which were earlier
+ obtained from this hash using lf_hash_get_pins().
+ @param key Key
+ @param keylen Key length
+
+ @retval A pointer to an element with the given key (if a hash is not unique
+ and there're many elements with this key - the "first" matching
+ element).
+ @retval NULL - if nothing is found
+ @retval MY_ERRPTR - if OOM
+
+ @note Uses pins[0..2]. On return pins[0..1] are removed and pins[2]
+ is used to pin object found. It is also not removed in case when
+ object is not found/error occurs but pin value is undefined in
+ this case.
+ So calling lf_hash_unpin() is mandatory after call to this function
+ in case of both success and failure.
+ @sa my_lsearch().
+*/
+
+void *lf_hash_search(LF_HASH *hash, LF_PINS *pins, const void *key, uint keylen)
+{
+ LF_SLIST * volatile *el, *found;
+ uint bucket, hashnr= calc_hash(hash, (uchar *)key, keylen);
+
+ bucket= hashnr % hash->size;
+ el= lf_dynarray_lvalue(&hash->array, bucket);
+ if (unlikely(!el))
+ return MY_ERRPTR;
+ if (*el == NULL && unlikely(initialize_bucket(hash, el, bucket, pins)))
+ return MY_ERRPTR;
+ found= my_lsearch(el, hash->charset, my_reverse_bits(hashnr) | 1,
+ (uchar *)key, keylen, pins);
+ return found ? found+1 : 0;
+}
+
+
+/**
+ Find random hash element which satisfies condition specified by
+ match function.
+
+ @param hash Hash to search element in.
+ @param pin Pins for calling thread to be used during search
+ and for pinning its result.
+ @param match Pointer to match function. This function takes
+ pointer to object stored in hash as parameter
+ and returns 0 if object doesn't satisfy its
+ condition (and non-0 value if it does).
+ @param rand_val Random value to be used for selecting hash
+ bucket from which search in sort-ordered
+ list needs to be started.
+
+ @retval A pointer to a random element matching condition.
+ @retval NULL - if nothing is found
+ @retval MY_ERRPTR - OOM.
+
+ @note This function follows the same pinning protocol as lf_hash_search(),
+ i.e. uses pins[0..2]. On return pins[0..1] are removed and pins[2]
+ is used to pin object found. It is also not removed in case when
+ object is not found/error occurs but its value is undefined in
+ this case.
+ So calling lf_hash_unpin() is mandatory after call to this function
+ in case of both success and failure.
+*/
+
+void *lf_hash_random_match(LF_HASH *hash, LF_PINS *pins,
+ lf_hash_match_func *match,
+ uint rand_val)
+{
+ /* Convert random value to valid hash value. */
+ uint hashnr= (rand_val & INT_MAX32);
+ uint bucket;
+ uint32 rev_hashnr;
+ LF_SLIST * volatile *el;
+ CURSOR cursor;
+ int res;
+
+ bucket= hashnr % hash->size;
+ rev_hashnr= my_reverse_bits(hashnr);
+
+ el= lf_dynarray_lvalue(&hash->array, bucket);
+ if (unlikely(!el))
+ return MY_ERRPTR;
+ /*
+ Bucket might be totally empty if it has not been accessed since last
+ time LF_HASH::size has been increased. In this case we initialize it
+ by inserting dummy node for this bucket to the correct position in
+ split-ordered list. This should help future lf_hash_* calls trying to
+ access the same bucket.
+ */
+ if (*el == NULL && unlikely(initialize_bucket(hash, el, bucket, pins)))
+ return MY_ERRPTR;
+
+ /*
+ To avoid bias towards the first matching element in the bucket, we start
+ looking for elements with inversed hash value greater or equal than
+ inversed value of our random hash.
+ */
+ res= my_lfind_match(el, rev_hashnr | 1, UINT_MAX32, match, &cursor, pins);
+
+ if (! res && hashnr != 0)
+ {
+ /*
+ We have not found matching element - probably we were too close to
+ the tail of our split-ordered list. To avoid bias against elements
+ at the head of the list we restart our search from its head. Unless
+ we were already searching from it.
+
+ To avoid going through elements at which we have already looked
+ twice we stop once we reach element from which we have begun our
+ first search.
+ */
+ el= lf_dynarray_lvalue(&hash->array, 0);
+ if (unlikely(!el))
+ return MY_ERRPTR;
+ res= my_lfind_match(el, 1, rev_hashnr, match, &cursor, pins);
+ }
+
+ if (res)
+ lf_pin(pins, 2, cursor.curr);
+ lf_unpin(pins, 0);
+ lf_unpin(pins, 1);
+
+ return res ? cursor.curr + 1 : 0;
+}
+
+static const uchar *dummy_key= (uchar*)"";
+
+/*
+ RETURN
+ 0 - ok
+ -1 - out of memory
+*/
+static int initialize_bucket(LF_HASH *hash, LF_SLIST * volatile *node,
+ uint bucket, LF_PINS *pins)
+{
+ uint parent= my_clear_highest_bit(bucket);
+ LF_SLIST *dummy= (LF_SLIST *)my_malloc(key_memory_lf_slist,
+ sizeof(LF_SLIST), MYF(MY_WME));
+ LF_SLIST **tmp= 0, *cur;
+ LF_SLIST * volatile *el= lf_dynarray_lvalue(&hash->array, parent);
+ if (unlikely(!el || !dummy))
+ return -1;
+ if (*el == NULL && bucket &&
+ unlikely(initialize_bucket(hash, el, parent, pins)))
+ return -1;
+ dummy->hashnr= my_reverse_bits(bucket) | 0; /* dummy node */
+ dummy->key= dummy_key;
+ dummy->keylen= 0;
+ if ((cur= linsert(el, hash->charset, dummy, pins, LF_HASH_UNIQUE)))
+ {
+ my_free(dummy);
+ dummy= cur;
+ }
+ my_atomic_casptr((void **)node, (void **)&tmp, dummy);
+ /*
+ note that if the CAS above failed (after linsert() succeeded),
+ it would mean that some other thread has executed linsert() for
+ the same dummy node, its linsert() failed, it picked up our
+ dummy node (in "dummy= cur") and executed the same CAS as above.
+ Which means that even if CAS above failed we don't need to retry,
+ and we should not free(dummy) - there's no memory leak here
+ */
+ return 0;
+}
diff --git a/mysql/mysys/list.c b/mysql/mysys/list.c
new file mode 100644
index 0000000..800149f
--- /dev/null
+++ b/mysql/mysys/list.c
@@ -0,0 +1,114 @@
+/* Copyright (c) 2000, 2015, Oracle and/or its affiliates. All rights reserved.
+
+ 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; version 2 of the License.
+
+ 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+/*
+ Code for handling dubble-linked lists in C
+*/
+
+#include "mysys_priv.h"
+#include "my_sys.h"
+#include <my_list.h>
+
+ /* Add a element to start of list */
+
+LIST *list_add(LIST *root, LIST *element)
+{
+ DBUG_ENTER("list_add");
+ DBUG_PRINT("enter",("root: 0x%lx element: 0x%lx", (long) root, (long) element));
+ if (root)
+ {
+ if (root->prev) /* If add in mid of list */
+ root->prev->next= element;
+ element->prev=root->prev;
+ root->prev=element;
+ }
+ else
+ element->prev=0;
+ element->next=root;
+ DBUG_RETURN(element); /* New root */
+}
+
+
+LIST *list_delete(LIST *root, LIST *element)
+{
+ if (element->prev)
+ element->prev->next=element->next;
+ else
+ root=element->next;
+ if (element->next)
+ element->next->prev=element->prev;
+ return root;
+}
+
+
+void list_free(LIST *root, uint free_data)
+{
+ LIST *next;
+ while (root)
+ {
+ next=root->next;
+ if (free_data)
+ my_free(root->data);
+ my_free(root);
+ root=next;
+ }
+}
+
+
+LIST *list_cons(void *data, LIST *list)
+{
+ LIST *new_charset=(LIST*) my_malloc(key_memory_LIST,
+ sizeof(LIST),MYF(MY_FAE));
+ if (!new_charset)
+ return 0;
+ new_charset->data=data;
+ return list_add(list,new_charset);
+}
+
+
+LIST *list_reverse(LIST *root)
+{
+ LIST *last;
+
+ last=root;
+ while (root)
+ {
+ last=root;
+ root=root->next;
+ last->next=last->prev;
+ last->prev=root;
+ }
+ return last;
+}
+
+uint list_length(LIST *list)
+{
+ uint count;
+ for (count=0 ; list ; list=list->next, count++) ;
+ return count;
+}
+
+
+int list_walk(LIST *list, list_walk_action action, uchar* argument)
+{
+ int error=0;
+ while (list)
+ {
+ if ((error = (*action)(list->data,argument)))
+ return error;
+ list=list_rest(list);
+ }
+ return 0;
+}
diff --git a/mysql/mysys/mf_arr_appstr.c b/mysql/mysys/mf_arr_appstr.c
new file mode 100644
index 0000000..71d4ad0
--- /dev/null
+++ b/mysql/mysys/mf_arr_appstr.c
@@ -0,0 +1,62 @@
+/* Copyright (C) 2007 MySQL AB
+ Use is subject to license terms
+
+ 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; version 2 of the License.
+
+ 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */
+
+#include "mysys_priv.h"
+#include <m_string.h> /* strcmp() */
+
+
+/**
+ Append str to array, or move to the end if it already exists
+
+ @param str String to be appended
+ @param array The array, terminated by a NULL element, all unused elements
+ pre-initialized to NULL
+ @param size Size of the array; array must be terminated by a NULL
+ pointer, so can hold size - 1 elements
+
+ @retval FALSE Success
+ @retval TRUE Failure, array is full
+*/
+
+my_bool array_append_string_unique(const char *str,
+ const char **array, size_t size)
+{
+ const char **p;
+ /* end points at the terminating NULL element */
+ const char **end= array + size - 1;
+ DBUG_ASSERT(*end == NULL);
+
+ for (p= array; *p; ++p)
+ {
+ if (strcmp(*p, str) == 0)
+ break;
+ }
+ if (p >= end)
+ return TRUE; /* Array is full */
+
+ DBUG_ASSERT(*p == NULL || strcmp(*p, str) == 0);
+
+ while (*(p + 1))
+ {
+ *p= *(p + 1);
+ ++p;
+ }
+
+ DBUG_ASSERT(p < end);
+ *p= str;
+
+ return FALSE; /* Success */
+}
diff --git a/mysql/mysys/mf_cache.c b/mysql/mysys/mf_cache.c
new file mode 100644
index 0000000..680f394
--- /dev/null
+++ b/mysql/mysys/mf_cache.c
@@ -0,0 +1,105 @@
+/* Copyright (c) 2000, 2016, Oracle and/or its affiliates. All rights reserved.
+
+ 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; version 2 of the License.
+
+ 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+/* Open a temporary file and cache it with io_cache. Delete it on close */
+
+#include "my_global.h"
+#include "mysys_priv.h"
+#include "mysql/psi/mysql_file.h"
+#include "my_sys.h"
+#include <m_string.h>
+#include "my_static.h"
+#include "mysys_err.h"
+
+ /*
+ Remove an open tempfile so that it doesn't survive
+ if we crash.
+ */
+
+static my_bool cache_remove_open_tmp(IO_CACHE *cache MY_ATTRIBUTE((unused)),
+ const char *name)
+{
+#if O_TEMPORARY == 0
+ /* The following should always succeed */
+ (void) my_delete(name,MYF(MY_WME));
+#endif /* O_TEMPORARY == 0 */
+ return 0;
+}
+
+ /*
+ ** Open tempfile cached by IO_CACHE
+ ** Should be used when no seeks are done (only reinit_io_buff)
+ ** Return 0 if cache is inited ok
+ ** The actual file is created when the IO_CACHE buffer gets filled
+ ** If dir is not given, use TMPDIR.
+ */
+
+my_bool open_cached_file(IO_CACHE *cache, const char* dir, const char *prefix,
+ size_t cache_size, myf cache_myflags)
+{
+ DBUG_ENTER("open_cached_file");
+ cache->dir= dir ? my_strdup(key_memory_IO_CACHE,
+ dir,MYF(cache_myflags & MY_WME)) : (char*) 0;
+ cache->prefix= (prefix ? my_strdup(key_memory_IO_CACHE,
+ prefix,MYF(cache_myflags & MY_WME)) :
+ (char*) 0);
+ cache->file_name=0;
+ cache->buffer=0; /* Mark that not open */
+ if (!init_io_cache(cache,-1,cache_size,WRITE_CACHE,0L,0,
+ MYF(cache_myflags | MY_NABP)))
+ {
+ DBUG_RETURN(0);
+ }
+ my_free(cache->dir);
+ my_free(cache->prefix);
+ DBUG_RETURN(1);
+}
+
+/* Create the temporary file */
+
+my_bool real_open_cached_file(IO_CACHE *cache)
+{
+ char name_buff[FN_REFLEN];
+ int error=1;
+ DBUG_ENTER("real_open_cached_file");
+ if ((cache->file= mysql_file_create_temp(cache->file_key, name_buff,
+ cache->dir, cache->prefix,
+ (O_RDWR | O_BINARY | O_TRUNC | O_TEMPORARY | O_SHORT_LIVED),
+ MYF(MY_WME))) >= 0)
+ {
+ error=0;
+ cache_remove_open_tmp(cache, name_buff);
+ }
+ DBUG_RETURN(error);
+}
+
+
+void close_cached_file(IO_CACHE *cache)
+{
+ DBUG_ENTER("close_cached_file");
+ if (my_b_inited(cache))
+ {
+ File file=cache->file;
+ cache->file= -1; /* Don't flush data */
+ (void) end_io_cache(cache);
+ if (file >= 0)
+ {
+ (void) mysql_file_close(file, MYF(0));
+ }
+ my_free(cache->dir);
+ my_free(cache->prefix);
+ }
+ DBUG_VOID_RETURN;
+}
diff --git a/mysql/mysys/mf_dirname.c b/mysql/mysys/mf_dirname.c
new file mode 100644
index 0000000..8ddd0ba
--- /dev/null
+++ b/mysql/mysys/mf_dirname.c
@@ -0,0 +1,155 @@
+/* Copyright (c) 2000, 2015, Oracle and/or its affiliates. All rights reserved.
+
+ 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; version 2 of the License.
+
+ 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include "mysys_priv.h"
+#include "my_sys.h"
+#include <m_string.h>
+
+ /* Functions definied in this file */
+
+size_t dirname_length(const char *name)
+{
+ char *pos, *gpos;
+#ifdef _WIN32
+ CHARSET_INFO *fs= fs_character_set();
+#endif
+#ifdef FN_DEVCHAR
+ if ((pos=(char*)strrchr(name,FN_DEVCHAR)) == 0)
+#endif
+ pos=(char*) name-1;
+
+ gpos= pos++;
+ for ( ; *pos ; pos++) /* Find last FN_LIBCHAR */
+ {
+#ifdef _WIN32
+ uint l;
+ if (use_mb(fs) && (l= my_ismbchar(fs, pos, pos + 3)))
+ {
+ pos+= l - 1;
+ continue;
+ }
+#endif
+ if (*pos == FN_LIBCHAR || *pos == '/')
+ gpos=pos;
+ }
+ return (size_t) (gpos+1-(char*) name);
+}
+
+
+/*
+ Gives directory part of filename. Directory ends with '/'
+
+ SYNOPSIS
+ dirname_part()
+ to Store directory name here
+ name Original name
+ to_length Store length of 'to' here
+
+ RETURN
+ # Length of directory part in 'name'
+*/
+
+size_t dirname_part(char *to, const char *name, size_t *to_res_length)
+{
+ size_t length;
+ DBUG_ENTER("dirname_part");
+ DBUG_PRINT("enter",("'%s'",name));
+
+ length=dirname_length(name);
+ *to_res_length= (size_t) (convert_dirname(to, name, name+length) - to);
+ DBUG_RETURN(length);
+} /* dirname */
+
+
+/*
+ Convert directory name to use under this system
+
+ SYNPOSIS
+ convert_dirname()
+ to Store result here. Must be at least of size
+ min(FN_REFLEN, strlen(from) + 1) to make room
+ for adding FN_LIBCHAR at the end.
+ from Original filename. May be == to
+ from_end Pointer at end of filename (normally end \0)
+
+ IMPLEMENTATION
+ If Windows converts '/' to '\'
+ Adds a FN_LIBCHAR to end if the result string if there isn't one
+ and the last isn't dev_char.
+ Copies data from 'from' until ASCII(0) for until from == from_end
+ If you want to use the whole 'from' string, just send NullS as the
+ last argument.
+
+ If the result string is larger than FN_REFLEN -1, then it's cut.
+
+ RETURN
+ Returns pointer to end \0 in to
+*/
+
+#ifndef FN_DEVCHAR
+#define FN_DEVCHAR '\0' /* For easier code */
+#endif
+
+char *convert_dirname(char *to, const char *from, const char *from_end)
+{
+ char *to_org=to;
+#ifdef _WIN32
+ CHARSET_INFO *fs= fs_character_set();
+#endif
+ DBUG_ENTER("convert_dirname");
+
+ /* We use -2 here, becasue we need place for the last FN_LIBCHAR */
+ if (!from_end || (from_end - from) > FN_REFLEN-2)
+ from_end=from+FN_REFLEN -2;
+
+#if FN_LIBCHAR != '/'
+ {
+ for (; from < from_end && *from ; from++)
+ {
+ if (*from == '/')
+ *to++= FN_LIBCHAR;
+ else
+ {
+#ifdef _WIN32
+ uint l;
+ if (use_mb(fs) && (l= my_ismbchar(fs, from, from + 3)))
+ {
+ memmove(to, from, l);
+ to+= l;
+ from+= l - 1;
+ to_org= to; /* Don't look inside mbchar */
+ }
+ else
+#endif
+ {
+ *to++= *from;
+ }
+ }
+ }
+ *to=0;
+ }
+#else
+ /* This is ok even if to == from, becasue we need to cut the string */
+ to= strmake(to, from, (size_t) (from_end-from));
+#endif
+
+ /* Add FN_LIBCHAR to the end of directory path */
+ if (to != to_org && (to[-1] != FN_LIBCHAR && to[-1] != FN_DEVCHAR))
+ {
+ *to++=FN_LIBCHAR;
+ *to=0;
+ }
+ DBUG_RETURN(to); /* Pointer to end of dir */
+} /* convert_dirname */
diff --git a/mysql/mysys/mf_fn_ext.c b/mysql/mysys/mf_fn_ext.c
new file mode 100644
index 0000000..807c473
--- /dev/null
+++ b/mysql/mysys/mf_fn_ext.c
@@ -0,0 +1,54 @@
+/* Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
+
+ 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; version 2 of the License.
+
+ 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+
+#include "mysys_priv.h"
+#include <m_string.h>
+
+/*
+ Return a pointer to the extension of the filename.
+
+ SYNOPSIS
+ fn_ext()
+ name Name of file
+
+ DESCRIPTION
+ The extension is defined as everything after the last extension character
+ (normally '.') after the directory name.
+
+ RETURN VALUES
+ Pointer to to the extension character. If there isn't any extension,
+ points at the end ASCII(0) of the filename.
+*/
+
+char *fn_ext(const char *name)
+{
+ const char *pos, *gpos;
+ DBUG_ENTER("fn_ext");
+ DBUG_PRINT("mfunkt",("name: '%s'",name));
+
+#if defined(FN_DEVCHAR) || defined(_WIN32)
+ {
+ char buff[FN_REFLEN];
+ size_t res_length;
+ gpos= name+ dirname_part(buff,(char*) name, &res_length);
+ }
+#else
+ if (!(gpos= strrchr(name, FN_LIBCHAR)))
+ gpos= name;
+#endif
+ pos=strrchr(gpos,FN_EXTCHAR);
+ DBUG_RETURN((char*) (pos ? pos : strend(gpos)));
+} /* fn_ext */
diff --git a/mysql/mysys/mf_format.c b/mysql/mysys/mf_format.c
new file mode 100644
index 0000000..e319615
--- /dev/null
+++ b/mysql/mysys/mf_format.c
@@ -0,0 +1,147 @@
+/* Copyright (c) 2000, 2015, Oracle and/or its affiliates. All rights reserved.
+
+ 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; version 2 of the License.
+
+ 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include "mysys_priv.h"
+#include "my_sys.h"
+#include <m_string.h>
+
+/*
+ Formats a filename with possible replace of directory of extension
+ Function can handle the case where 'to' == 'name'
+ For a description of the flag values, consult my_sys.h
+ The arguments should be in unix format.
+*/
+
+char * fn_format(char * to, const char *name, const char *dir,
+ const char *extension, uint flag)
+{
+ char dev[FN_REFLEN], buff[FN_REFLEN], *pos, *startpos;
+ const char *ext;
+ size_t length;
+ size_t dev_length;
+ DBUG_ENTER("fn_format");
+ DBUG_ASSERT(name != NULL);
+ DBUG_ASSERT(extension != NULL);
+ DBUG_PRINT("enter",("name: %s dir: %s extension: %s flag: %d",
+ name,dir,extension,flag));
+
+ /* Copy and skip directory */
+ name+=(length=dirname_part(dev, (startpos=(char *) name), &dev_length));
+ if (length == 0 || (flag & MY_REPLACE_DIR))
+ {
+ DBUG_ASSERT(dir != NULL);
+ /* Use given directory */
+ convert_dirname(dev,dir,NullS); /* Fix to this OS */
+ }
+ else if ((flag & MY_RELATIVE_PATH) && !test_if_hard_path(dev))
+ {
+ DBUG_ASSERT(dir != NULL);
+ /* Put 'dir' before the given path */
+ strmake(buff,dev,sizeof(buff)-1);
+ pos=convert_dirname(dev,dir,NullS);
+ strmake(pos,buff,sizeof(buff)-1- (int) (pos-dev));
+ }
+
+ if (flag & MY_PACK_FILENAME)
+ pack_dirname(dev,dev); /* Put in ./.. and ~/.. */
+ if (flag & MY_UNPACK_FILENAME)
+ (void) unpack_dirname(dev,dev); /* Replace ~/.. with dir */
+
+ if (!(flag & MY_APPEND_EXT) &&
+ (pos= (char*) strchr(name,FN_EXTCHAR)) != NullS)
+ {
+ if ((flag & MY_REPLACE_EXT) == 0) /* If we should keep old ext */
+ {
+ length=strlength(name); /* Use old extension */
+ ext = "";
+ }
+ else
+ {
+ length= (size_t) (pos-(char*) name); /* Change extension */
+ ext= extension;
+ }
+ }
+ else
+ {
+ length=strlength(name); /* No ext, use the now one */
+ ext=extension;
+ }
+
+ if (strlen(dev)+length+strlen(ext) >= FN_REFLEN || length >= FN_LEN )
+ {
+ /* To long path, return original or NULL */
+ size_t tmp_length;
+ if (flag & MY_SAFE_PATH)
+ DBUG_RETURN(NullS);
+ tmp_length= strlength(startpos);
+ DBUG_PRINT("error",("dev: '%s' ext: '%s' length: %u",dev,ext,
+ (uint) length));
+ (void) strmake(to, startpos, MY_MIN(tmp_length, FN_REFLEN-1));
+ }
+ else
+ {
+ if (to == startpos)
+ {
+ memmove(buff, name, length); /* Save name for last copy */
+ name=buff;
+ }
+ pos=strmake(my_stpcpy(to,dev),name,length);
+ (void) my_stpcpy(pos,ext); /* Don't convert extension */
+ }
+ /*
+ If MY_RETURN_REAL_PATH and MY_RESOLVE_SYMLINK is given, only do
+ realpath if the file is a symbolic link
+ */
+ if (flag & MY_RETURN_REAL_PATH)
+ (void) my_realpath(to, to, MYF(flag & MY_RESOLVE_SYMLINKS ?
+ MY_RESOLVE_LINK: 0));
+ else if (flag & MY_RESOLVE_SYMLINKS)
+ {
+ my_stpcpy(buff,to);
+ (void) my_readlink(to, buff, MYF(0));
+ }
+ DBUG_RETURN(to);
+} /* fn_format */
+
+
+/*
+ strlength(const string str)
+ Return length of string with end-space:s not counted.
+*/
+
+size_t strlength(const char *str)
+{
+ const char * pos;
+ const char * found;
+ DBUG_ENTER("strlength");
+
+ pos= found= str;
+
+ while (*pos)
+ {
+ if (*pos != ' ')
+ {
+ while (*++pos && *pos != ' ') {};
+ if (!*pos)
+ {
+ found=pos; /* String ends here */
+ break;
+ }
+ }
+ found=pos;
+ while (*++pos == ' ') {};
+ }
+ DBUG_RETURN((size_t) (found - str));
+} /* strlength */
diff --git a/mysql/mysys/mf_getdate.c b/mysql/mysys/mf_getdate.c
new file mode 100644
index 0000000..5efadcd
--- /dev/null
+++ b/mysql/mysys/mf_getdate.c
@@ -0,0 +1,73 @@
+/* Copyright (c) 2000, 2015, Oracle and/or its affiliates. All rights reserved.
+
+ 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; version 2 of the License.
+
+ 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+/* Get date in a printable form: yyyy-mm-dd hh:mm:ss */
+
+#include "mysys_priv.h"
+#include "my_sys.h"
+#include <m_string.h>
+
+/*
+ get date as string
+
+ SYNOPSIS
+ get_date()
+ to - string where date will be written
+ flag - format of date:
+ If flag & GETDATE_TIME Return date and time
+ If flag & GETDATE_SHORT_DATE Return short date format YYMMDD
+ If flag & GETDATE_HHMMSSTIME Return time in HHMMDD format.
+ If flag & GETDATE_GMT Date/time in GMT
+ If flag & GETDATE_FIXEDLENGTH Return fixed length date/time
+ date - for conversion
+*/
+
+
+void get_date(char * to, int flag, time_t date)
+{
+ struct tm *start_time;
+ time_t skr;
+ struct tm tm_tmp;
+
+ skr=date ? (time_t) date : my_time(0);
+ if (flag & GETDATE_GMT)
+ gmtime_r(&skr,&tm_tmp);
+ else
+ localtime_r(&skr,&tm_tmp);
+ start_time= &tm_tmp;
+ if (flag & GETDATE_SHORT_DATE)
+ sprintf(to,"%02d%02d%02d",
+ start_time->tm_year % 100,
+ start_time->tm_mon+1,
+ start_time->tm_mday);
+ else
+ sprintf(to, ((flag & GETDATE_FIXEDLENGTH) ?
+ "%4d-%02d-%02d" : "%d-%02d-%02d"),
+ start_time->tm_year+1900,
+ start_time->tm_mon+1,
+ start_time->tm_mday);
+ if (flag & GETDATE_DATE_TIME)
+ sprintf(strend(to),
+ ((flag & GETDATE_FIXEDLENGTH) ?
+ " %02d:%02d:%02d" : " %2d:%02d:%02d"),
+ start_time->tm_hour,
+ start_time->tm_min,
+ start_time->tm_sec);
+ else if (flag & GETDATE_HHMMSSTIME)
+ sprintf(strend(to),"%02d%02d%02d",
+ start_time->tm_hour,
+ start_time->tm_min,
+ start_time->tm_sec);
+} /* get_date */
diff --git a/mysql/mysys/mf_iocache.c b/mysql/mysys/mf_iocache.c
new file mode 100644
index 0000000..bc5f1bb
--- /dev/null
+++ b/mysql/mysys/mf_iocache.c
@@ -0,0 +1,1733 @@
+/* Copyright (c) 2000, 2016, Oracle and/or its affiliates. All rights reserved.
+
+ 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; version 2 of the License.
+
+ 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+/*
+ Cashing of files with only does (sequential) read or writes of fixed-
+ length records. A read isn't allowed to go over file-length. A read is ok
+ if it ends at file-length and next read can try to read after file-length
+ (and get a EOF-error).
+ Possibly use of asyncronic io.
+ macros for read and writes for faster io.
+ Used instead of FILE when reading or writing whole files.
+ This code makes mf_rec_cache obsolete (currently only used by ISAM)
+ One can change info->pos_in_file to a higher value to skip bytes in file if
+ also info->read_pos is set to info->read_end.
+ If called through open_cached_file(), then the temporary file will
+ only be created if a write exeeds the file buffer or if one calls
+ my_b_flush_io_cache().
+
+ If one uses SEQ_READ_APPEND, then two buffers are allocated, one for
+ reading and another for writing. Reads are first done from disk and
+ then done from the write buffer. This is an efficient way to read
+ from a log file when one is writing to it at the same time.
+ For this to work, the file has to be opened in append mode!
+ Note that when one uses SEQ_READ_APPEND, one MUST write using
+ my_b_append ! This is needed because we need to lock the mutex
+ every time we access the write buffer.
+
+TODO:
+ When one SEQ_READ_APPEND and we are reading and writing at the same time,
+ each time the write buffer gets full and it's written to disk, we will
+ always do a disk read to read a part of the buffer from disk to the
+ read buffer.
+ This should be fixed so that when we do a my_b_flush_io_cache() and
+ we have been reading the write buffer, we should transfer the rest of the
+ write buffer to the read buffer before we start to reuse it.
+*/
+
+#include "mysys_priv.h"
+#include "my_sys.h"
+#include <m_string.h>
+#include <errno.h>
+#include "my_thread_local.h"
+#include "mysql/psi/mysql_file.h"
+
+PSI_file_key key_file_io_cache;
+
+#define lock_append_buffer(info) \
+ mysql_mutex_lock(&(info)->append_buffer_lock)
+#define unlock_append_buffer(info) \
+ mysql_mutex_unlock(&(info)->append_buffer_lock)
+
+#define IO_ROUND_UP(X) (((X)+IO_SIZE-1) & ~(IO_SIZE-1))
+#define IO_ROUND_DN(X) ( (X) & ~(IO_SIZE-1))
+
+/*
+ Setup internal pointers inside IO_CACHE
+
+ SYNOPSIS
+ setup_io_cache()
+ info IO_CACHE handler
+
+ NOTES
+ This is called on automaticly on init or reinit of IO_CACHE
+ It must be called externally if one moves or copies an IO_CACHE
+ object.
+*/
+
+void setup_io_cache(IO_CACHE* info)
+{
+ /* Ensure that my_b_tell() and my_b_bytes_in_cache works */
+ if (info->type == WRITE_CACHE)
+ {
+ info->current_pos= &info->write_pos;
+ info->current_end= &info->write_end;
+ }
+ else
+ {
+ info->current_pos= &info->read_pos;
+ info->current_end= &info->read_end;
+ }
+}
+
+
+static void
+init_functions(IO_CACHE* info)
+{
+ enum cache_type type= info->type;
+ switch (type) {
+ case READ_NET:
+ /*
+ Must be initialized by the caller. The problem is that
+ _my_b_net_read has to be defined in sql directory because of
+ the dependency on THD, and therefore cannot be visible to
+ programs that link against mysys but know nothing about THD, such
+ as myisamchk
+ */
+ break;
+ case SEQ_READ_APPEND:
+ info->read_function = _my_b_seq_read;
+ info->write_function = 0; /* Force a core if used */
+ break;
+ default:
+ info->read_function = info->share ? _my_b_read_r : _my_b_read;
+ info->write_function = _my_b_write;
+ }
+
+ setup_io_cache(info);
+}
+
+
+/*
+ Initialize an IO_CACHE object
+
+ SYNOPSIS
+ init_io_cache_ext()
+ info cache handler to initialize
+ file File that should be associated to to the handler
+ If == -1 then real_open_cached_file()
+ will be called when it's time to open file.
+ cachesize Size of buffer to allocate for read/write
+ If == 0 then use my_default_record_cache_size
+ type Type of cache
+ seek_offset Where cache should start reading/writing
+ use_async_io Set to 1 of we should use async_io (if avaiable)
+ cache_myflags Bitmap of different flags
+ MY_WME | MY_FAE | MY_NABP | MY_FNABP |
+ MY_DONT_CHECK_FILESIZE
+ file_key Instrumented file key for temporary cache file
+
+ RETURN
+ 0 ok
+ # error
+*/
+
+int init_io_cache_ext(IO_CACHE *info, File file, size_t cachesize,
+ enum cache_type type, my_off_t seek_offset,
+ pbool use_async_io, myf cache_myflags,
+ PSI_file_key file_key)
+{
+ size_t min_cache;
+ my_off_t pos;
+ my_off_t end_of_file= ~(my_off_t) 0;
+ DBUG_ENTER("init_io_cache_ext");
+ DBUG_PRINT("enter",("cache: 0x%lx type: %d pos: %ld",
+ (ulong) info, (int) type, (ulong) seek_offset));
+
+ info->file= file;
+ info->file_key= file_key;
+ info->type= TYPE_NOT_SET; /* Don't set it until mutex are created */
+ info->pos_in_file= seek_offset;
+ info->pre_close = info->pre_read = info->post_read = 0;
+ info->arg = 0;
+ info->alloced_buffer = 0;
+ info->buffer=0;
+ info->seek_not_done= 0;
+
+ if (file >= 0)
+ {
+ pos= mysql_file_tell(file, MYF(0));
+ if ((pos == (my_off_t) -1) && (my_errno() == ESPIPE))
+ {
+ /*
+ This kind of object doesn't support seek() or tell(). Don't set a
+ flag that will make us again try to seek() later and fail.
+ */
+ info->seek_not_done= 0;
+ /*
+ Additionally, if we're supposed to start somewhere other than the
+ the beginning of whatever this file is, then somebody made a bad
+ assumption.
+ */
+ DBUG_ASSERT(seek_offset == 0);
+ }
+ else
+ info->seek_not_done= MY_TEST(seek_offset != pos);
+ }
+
+ info->disk_writes= 0;
+ info->share=0;
+
+ if (!cachesize && !(cachesize= my_default_record_cache_size))
+ DBUG_RETURN(1); /* No cache requested */
+ min_cache=use_async_io ? IO_SIZE*4 : IO_SIZE*2;
+ if (type == READ_CACHE || type == SEQ_READ_APPEND)
+ { /* Assume file isn't growing */
+ if (!(cache_myflags & MY_DONT_CHECK_FILESIZE))
+ {
+ /* Calculate end of file to avoid allocating oversized buffers */
+ end_of_file= mysql_file_seek(file, 0L, MY_SEEK_END, MYF(0));
+ /* Need to reset seek_not_done now that we just did a seek. */
+ info->seek_not_done= end_of_file == seek_offset ? 0 : 1;
+ if (end_of_file < seek_offset)
+ end_of_file=seek_offset;
+ /* Trim cache size if the file is very small */
+ if ((my_off_t) cachesize > end_of_file-seek_offset+IO_SIZE*2-1)
+ {
+ cachesize= (size_t) (end_of_file-seek_offset)+IO_SIZE*2-1;
+ use_async_io=0; /* No need to use async */
+ }
+ }
+ }
+ cache_myflags &= ~MY_DONT_CHECK_FILESIZE;
+ if (type != READ_NET && type != WRITE_NET)
+ {
+ /* Retry allocating memory in smaller blocks until we get one */
+ cachesize= ((cachesize + min_cache-1) & ~(min_cache-1));
+ for (;;)
+ {
+ size_t buffer_block;
+ /*
+ Unset MY_WAIT_IF_FULL bit if it is set, to prevent conflict with
+ MY_ZEROFILL.
+ */
+ myf flags= (myf) (cache_myflags & ~(MY_WME | MY_WAIT_IF_FULL));
+
+ if (cachesize < min_cache)
+ cachesize = min_cache;
+ buffer_block= cachesize;
+ if (type == SEQ_READ_APPEND)
+ buffer_block *= 2;
+ if (cachesize == min_cache)
+ flags|= (myf) MY_WME;
+
+ if ((info->buffer= (uchar*) my_malloc(key_memory_IO_CACHE,
+ buffer_block, flags)) != 0)
+ {
+ info->write_buffer=info->buffer;
+ if (type == SEQ_READ_APPEND)
+ info->write_buffer = info->buffer + cachesize;
+ info->alloced_buffer=1;
+ break; /* Enough memory found */
+ }
+ if (cachesize == min_cache)
+ DBUG_RETURN(2); /* Can't alloc cache */
+ /* Try with less memory */
+ cachesize= (cachesize*3/4 & ~(min_cache-1));
+ }
+ }
+
+ DBUG_PRINT("info",("init_io_cache: cachesize = %lu", (ulong) cachesize));
+ info->read_length=info->buffer_length=cachesize;
+ info->myflags=cache_myflags & ~(MY_NABP | MY_FNABP);
+ info->request_pos= info->read_pos= info->write_pos = info->buffer;
+ if (type == SEQ_READ_APPEND)
+ {
+ info->append_read_pos = info->write_pos = info->write_buffer;
+ info->write_end = info->write_buffer + info->buffer_length;
+ mysql_mutex_init(key_IO_CACHE_append_buffer_lock,
+ &info->append_buffer_lock, MY_MUTEX_INIT_FAST);
+ }
+#if defined(SAFE_MUTEX)
+ else
+ {
+ /* Clear mutex so that safe_mutex will notice that it's not initialized */
+ memset(&info->append_buffer_lock, 0, sizeof(info->append_buffer_lock));
+ }
+#endif
+
+ if (type == WRITE_CACHE)
+ info->write_end=
+ info->buffer+info->buffer_length- (seek_offset & (IO_SIZE-1));
+ else
+ info->read_end=info->buffer; /* Nothing in cache */
+
+ /* End_of_file may be changed by user later */
+ info->end_of_file= end_of_file;
+ info->error=0;
+ info->type= type;
+ init_functions(info);
+ DBUG_RETURN(0);
+} /* init_io_cache_ext */
+
+/*
+ Initialize an IO_CACHE object
+
+ SYNOPSIS
+ init_io_cache() - Wrapper for init_io_cache_ext()
+
+ NOTE
+ This function should be used if the IO_CACHE tempfile is not instrumented.
+*/
+
+int init_io_cache(IO_CACHE *info, File file, size_t cachesize,
+ enum cache_type type, my_off_t seek_offset,
+ pbool use_async_io, myf cache_myflags)
+{
+ return init_io_cache_ext(info, file, cachesize, type, seek_offset,
+ use_async_io, cache_myflags, key_file_io_cache);
+}
+
+/*
+ Use this to reset cache to re-start reading or to change the type
+ between READ_CACHE <-> WRITE_CACHE
+ If we are doing a reinit of a cache where we have the start of the file
+ in the cache, we are reusing this memory without flushing it to disk.
+*/
+
+my_bool reinit_io_cache(IO_CACHE *info, enum cache_type type,
+ my_off_t seek_offset,
+ pbool use_async_io MY_ATTRIBUTE((unused)),
+ pbool clear_cache)
+{
+ DBUG_ENTER("reinit_io_cache");
+ DBUG_PRINT("enter",("cache: 0x%lx type: %d seek_offset: %lu clear_cache: %d",
+ (ulong) info, type, (ulong) seek_offset,
+ (int) clear_cache));
+
+ /* One can't do reinit with the following types */
+ DBUG_ASSERT(type != READ_NET && info->type != READ_NET &&
+ type != WRITE_NET && info->type != WRITE_NET &&
+ type != SEQ_READ_APPEND && info->type != SEQ_READ_APPEND);
+
+ /* If the whole file is in memory, avoid flushing to disk */
+ if (! clear_cache &&
+ seek_offset >= info->pos_in_file &&
+ seek_offset <= my_b_tell(info))
+ {
+ /* Reuse current buffer without flushing it to disk */
+ uchar *pos;
+ if (info->type == WRITE_CACHE && type == READ_CACHE)
+ {
+ info->read_end=info->write_pos;
+ info->end_of_file=my_b_tell(info);
+ /*
+ Trigger a new seek only if we have a valid
+ file handle.
+ */
+ info->seek_not_done= (info->file != -1);
+ }
+ else if (type == WRITE_CACHE)
+ {
+ if (info->type == READ_CACHE)
+ {
+ info->write_end=info->write_buffer+info->buffer_length;
+ info->seek_not_done=1;
+ }
+ info->end_of_file = ~(my_off_t) 0;
+ }
+ pos=info->request_pos+(seek_offset-info->pos_in_file);
+ if (type == WRITE_CACHE)
+ info->write_pos=pos;
+ else
+ info->read_pos= pos;
+ }
+ else
+ {
+ /*
+ If we change from WRITE_CACHE to READ_CACHE, assume that everything
+ after the current positions should be ignored
+ */
+ if (info->type == WRITE_CACHE && type == READ_CACHE)
+ info->end_of_file=my_b_tell(info);
+ /* flush cache if we want to reuse it */
+ if (!clear_cache && my_b_flush_io_cache(info,1))
+ DBUG_RETURN(1);
+ info->pos_in_file=seek_offset;
+ /* Better to do always do a seek */
+ info->seek_not_done=1;
+ info->request_pos=info->read_pos=info->write_pos=info->buffer;
+ if (type == READ_CACHE)
+ {
+ info->read_end=info->buffer; /* Nothing in cache */
+ }
+ else
+ {
+ info->write_end=(info->buffer + info->buffer_length -
+ (seek_offset & (IO_SIZE-1)));
+ info->end_of_file= ~(my_off_t) 0;
+ }
+ }
+ info->type=type;
+ info->error=0;
+ init_functions(info);
+
+ DBUG_RETURN(0);
+} /* reinit_io_cache */
+
+
+
+/*
+ Read buffered.
+
+ SYNOPSIS
+ _my_b_read()
+ info IO_CACHE pointer
+ Buffer Buffer to retrieve count bytes from file
+ Count Number of bytes to read into Buffer
+
+ NOTE
+ This function is only called from the my_b_read() macro when there
+ isn't enough characters in the buffer to satisfy the request.
+
+ WARNING
+
+ When changing this function, be careful with handling file offsets
+ (end-of_file, pos_in_file). Do not cast them to possibly smaller
+ types than my_off_t unless you can be sure that their value fits.
+ Same applies to differences of file offsets.
+
+ When changing this function, check _my_b_read_r(). It might need the
+ same change.
+
+ RETURN
+ 0 we succeeded in reading all data
+ 1 Error: couldn't read requested characters. In this case:
+ If info->error == -1, we got a read error.
+ Otherwise info->error contains the number of bytes in Buffer.
+*/
+
+int _my_b_read(IO_CACHE *info, uchar *Buffer, size_t Count)
+{
+ size_t length,diff_length,left_length, max_length;
+ my_off_t pos_in_file;
+ DBUG_ENTER("_my_b_read");
+
+ /* If the buffer is not empty yet, copy what is available. */
+ if ((left_length= (size_t) (info->read_end-info->read_pos)))
+ {
+ DBUG_ASSERT(Count >= left_length); /* User is not using my_b_read() */
+ memcpy(Buffer,info->read_pos, left_length);
+ Buffer+=left_length;
+ Count-=left_length;
+ }
+
+ /* pos_in_file always point on where info->buffer was read */
+ pos_in_file=info->pos_in_file+ (size_t) (info->read_end - info->buffer);
+
+ /*
+ Whenever a function which operates on IO_CACHE flushes/writes
+ some part of the IO_CACHE to disk it will set the property
+ "seek_not_done" to indicate this to other functions operating
+ on the IO_CACHE.
+ */
+ if (info->seek_not_done)
+ {
+ if ((mysql_file_seek(info->file, pos_in_file, MY_SEEK_SET, MYF(0))
+ != MY_FILEPOS_ERROR))
+ {
+ /* No error, reset seek_not_done flag. */
+ info->seek_not_done= 0;
+ }
+ else
+ {
+ /*
+ If the seek failed and the error number is ESPIPE, it is because
+ info->file is a pipe or socket or FIFO. We never should have tried
+ to seek on that. See Bugs#25807 and #22828 for more info.
+ */
+ DBUG_ASSERT(my_errno() != ESPIPE);
+ info->error= -1;
+ DBUG_RETURN(1);
+ }
+ }
+
+ /*
+ Calculate, how much we are within a IO_SIZE block. Ideally this
+ should be zero.
+ */
+ diff_length= (size_t) (pos_in_file & (IO_SIZE-1));
+
+ /*
+ If more than a block plus the rest of the current block is wanted,
+ we do read directly, without filling the buffer.
+ */
+ if (Count >= (size_t) (IO_SIZE+(IO_SIZE-diff_length)))
+ { /* Fill first intern buffer */
+ size_t read_length;
+ if (info->end_of_file <= pos_in_file)
+ {
+ /* End of file. Return, what we did copy from the buffer. */
+ info->error= (int) left_length;
+ DBUG_RETURN(1);
+ }
+ /*
+ Crop the wanted count to a multiple of IO_SIZE and subtract,
+ what we did already read from a block. That way, the read will
+ end aligned with a block.
+ */
+ length=(Count & (size_t) ~(IO_SIZE-1))-diff_length;
+ if ((read_length= mysql_file_read(info->file,Buffer, length, info->myflags))
+ != length)
+ {
+ /*
+ If we didn't get, what we wanted, we either return -1 for a read
+ error, or (it's end of file), how much we got in total.
+ */
+ info->error= (read_length == (size_t) -1 ? -1 :
+ (int) (read_length+left_length));
+ DBUG_RETURN(1);
+ }
+ Count-=length;
+ Buffer+=length;
+ pos_in_file+=length;
+ left_length+=length;
+ diff_length=0;
+ }
+
+ /*
+ At this point, we want less than one and a partial block.
+ We will read a full cache, minus the number of bytes, we are
+ within a block already. So we will reach new alignment.
+ */
+ max_length= info->read_length-diff_length;
+ /* We will not read past end of file. */
+ if (info->type != READ_FIFO &&
+ max_length > (info->end_of_file - pos_in_file))
+ max_length= (size_t) (info->end_of_file - pos_in_file);
+ /*
+ If there is nothing left to read,
+ we either are done, or we failed to fulfill the request.
+ Otherwise, we read max_length into the cache.
+ */
+ if (!max_length)
+ {
+ if (Count)
+ {
+ /* We couldn't fulfil the request. Return, how much we got. */
+ info->error= (int)left_length;
+ DBUG_RETURN(1);
+ }
+ length=0; /* Didn't read any chars */
+ }
+ else if ((length= mysql_file_read(info->file,info->buffer, max_length,
+ info->myflags)) < Count ||
+ length == (size_t) -1)
+ {
+ /*
+ We got an read error, or less than requested (end of file).
+ If not a read error, copy, what we got.
+ */
+ if (length != (size_t) -1)
+ memcpy(Buffer, info->buffer, length);
+ info->pos_in_file= pos_in_file;
+ /* For a read error, return -1, otherwise, what we got in total. */
+ info->error= length == (size_t) -1 ? -1 : (int) (length+left_length);
+ info->read_pos=info->read_end=info->buffer;
+ DBUG_RETURN(1);
+ }
+ /*
+ Count is the remaining number of bytes requested.
+ length is the amount of data in the cache.
+ Read Count bytes from the cache.
+ */
+ info->read_pos=info->buffer+Count;
+ info->read_end=info->buffer+length;
+ info->pos_in_file=pos_in_file;
+ memcpy(Buffer, info->buffer, Count);
+ DBUG_RETURN(0);
+}
+
+
+/*
+ Prepare IO_CACHE for shared use.
+
+ SYNOPSIS
+ init_io_cache_share()
+ read_cache A read cache. This will be copied for
+ every thread after setup.
+ cshare The share.
+ write_cache If non-NULL a write cache that is to be
+ synchronized with the read caches.
+ num_threads Number of threads sharing the cache
+ including the write thread if any.
+
+ DESCRIPTION
+
+ The shared cache is used so: One IO_CACHE is initialized with
+ init_io_cache(). This includes the allocation of a buffer. Then a
+ share is allocated and init_io_cache_share() is called with the io
+ cache and the share. Then the io cache is copied for each thread. So
+ every thread has its own copy of IO_CACHE. But the allocated buffer
+ is shared because cache->buffer is the same for all caches.
+
+ One thread reads data from the file into the buffer. All threads
+ read from the buffer, but every thread maintains its own set of
+ pointers into the buffer. When all threads have used up the buffer
+ contents, one of the threads reads the next block of data into the
+ buffer. To accomplish this, each thread enters the cache lock before
+ accessing the buffer. They wait in lock_io_cache() until all threads
+ joined the lock. The last thread entering the lock is in charge of
+ reading from file to buffer. It wakes all threads when done.
+
+ Synchronizing a write cache to the read caches works so: Whenever
+ the write buffer needs a flush, the write thread enters the lock and
+ waits for all other threads to enter the lock too. They do this when
+ they have used up the read buffer. When all threads are in the lock,
+ the write thread copies the write buffer to the read buffer and
+ wakes all threads.
+
+ share->running_threads is the number of threads not being in the
+ cache lock. When entering lock_io_cache() the number is decreased.
+ When the thread that fills the buffer enters unlock_io_cache() the
+ number is reset to the number of threads. The condition
+ running_threads == 0 means that all threads are in the lock. Bumping
+ up the number to the full count is non-intuitive. But increasing the
+ number by one for each thread that leaves the lock could lead to a
+ solo run of one thread. The last thread to join a lock reads from
+ file to buffer, wakes the other threads, processes the data in the
+ cache and enters the lock again. If no other thread left the lock
+ meanwhile, it would think it's the last one again and read the next
+ block...
+
+ The share has copies of 'error', 'buffer', 'read_end', and
+ 'pos_in_file' from the thread that filled the buffer. We may not be
+ able to access this information directly from its cache because the
+ thread may be removed from the share before the variables could be
+ copied by all other threads. Or, if a write buffer is synchronized,
+ it would change its 'pos_in_file' after waking the other threads,
+ possibly before they could copy its value.
+
+ However, the 'buffer' variable in the share is for a synchronized
+ write cache. It needs to know where to put the data. Otherwise it
+ would need access to the read cache of one of the threads that is
+ not yet removed from the share.
+
+ RETURN
+ void
+*/
+
+void init_io_cache_share(IO_CACHE *read_cache, IO_CACHE_SHARE *cshare,
+ IO_CACHE *write_cache, uint num_threads)
+{
+ DBUG_ENTER("init_io_cache_share");
+ DBUG_PRINT("io_cache_share", ("read_cache: 0x%lx share: 0x%lx "
+ "write_cache: 0x%lx threads: %u",
+ (long) read_cache, (long) cshare,
+ (long) write_cache, num_threads));
+
+ DBUG_ASSERT(num_threads > 1);
+ DBUG_ASSERT(read_cache->type == READ_CACHE);
+ DBUG_ASSERT(!write_cache || (write_cache->type == WRITE_CACHE));
+
+ mysql_mutex_init(key_IO_CACHE_SHARE_mutex,
+ &cshare->mutex, MY_MUTEX_INIT_FAST);
+ mysql_cond_init(key_IO_CACHE_SHARE_cond, &cshare->cond);
+ mysql_cond_init(key_IO_CACHE_SHARE_cond_writer, &cshare->cond_writer);
+
+ cshare->running_threads= num_threads;
+ cshare->total_threads= num_threads;
+ cshare->error= 0; /* Initialize. */
+ cshare->buffer= read_cache->buffer;
+ cshare->read_end= NULL; /* See function comment of lock_io_cache(). */
+ cshare->pos_in_file= 0; /* See function comment of lock_io_cache(). */
+ cshare->source_cache= write_cache; /* Can be NULL. */
+
+ read_cache->share= cshare;
+ read_cache->read_function= _my_b_read_r;
+ read_cache->current_pos= NULL;
+ read_cache->current_end= NULL;
+
+ if (write_cache)
+ write_cache->share= cshare;
+
+ DBUG_VOID_RETURN;
+}
+
+
+/*
+ Remove a thread from shared access to IO_CACHE.
+
+ SYNOPSIS
+ remove_io_thread()
+ cache The IO_CACHE to be removed from the share.
+
+ NOTE
+
+ Every thread must do that on exit for not to deadlock other threads.
+
+ The last thread destroys the pthread resources.
+
+ A writer flushes its cache first.
+
+ RETURN
+ void
+*/
+
+void remove_io_thread(IO_CACHE *cache)
+{
+ IO_CACHE_SHARE *cshare= cache->share;
+ uint total;
+ DBUG_ENTER("remove_io_thread");
+
+ /* If the writer goes, it needs to flush the write cache. */
+ if (cache == cshare->source_cache)
+ flush_io_cache(cache);
+
+ mysql_mutex_lock(&cshare->mutex);
+ DBUG_PRINT("io_cache_share", ("%s: 0x%lx",
+ (cache == cshare->source_cache) ?
+ "writer" : "reader", (long) cache));
+
+ /* Remove from share. */
+ total= --cshare->total_threads;
+ DBUG_PRINT("io_cache_share", ("remaining threads: %u", total));
+
+ /* Detach from share. */
+ cache->share= NULL;
+
+ /* If the writer goes, let the readers know. */
+ if (cache == cshare->source_cache)
+ {
+ DBUG_PRINT("io_cache_share", ("writer leaves"));
+ cshare->source_cache= NULL;
+ }
+
+ /* If all threads are waiting for me to join the lock, wake them. */
+ if (!--cshare->running_threads)
+ {
+ DBUG_PRINT("io_cache_share", ("the last running thread leaves, wake all"));
+ mysql_cond_signal(&cshare->cond_writer);
+ mysql_cond_broadcast(&cshare->cond);
+ }
+
+ mysql_mutex_unlock(&cshare->mutex);
+
+ if (!total)
+ {
+ DBUG_PRINT("io_cache_share", ("last thread removed, destroy share"));
+ mysql_cond_destroy (&cshare->cond_writer);
+ mysql_cond_destroy (&cshare->cond);
+ mysql_mutex_destroy(&cshare->mutex);
+ }
+
+ DBUG_VOID_RETURN;
+}
+
+
+/*
+ Lock IO cache and wait for all other threads to join.
+
+ SYNOPSIS
+ lock_io_cache()
+ cache The cache of the thread entering the lock.
+ pos File position of the block to read.
+ Unused for the write thread.
+
+ DESCRIPTION
+
+ Wait for all threads to finish with the current buffer. We want
+ all threads to proceed in concert. The last thread to join
+ lock_io_cache() will read the block from file and all threads start
+ to use it. Then they will join again for reading the next block.
+
+ The waiting threads detect a fresh buffer by comparing
+ cshare->pos_in_file with the position they want to process next.
+ Since the first block may start at position 0, we take
+ cshare->read_end as an additional condition. This variable is
+ initialized to NULL and will be set after a block of data is written
+ to the buffer.
+
+ RETURN
+ 1 OK, lock in place, go ahead and read.
+ 0 OK, unlocked, another thread did the read.
+*/
+
+static int lock_io_cache(IO_CACHE *cache, my_off_t pos)
+{
+ IO_CACHE_SHARE *cshare= cache->share;
+ DBUG_ENTER("lock_io_cache");
+
+ /* Enter the lock. */
+ mysql_mutex_lock(&cshare->mutex);
+ cshare->running_threads--;
+ DBUG_PRINT("io_cache_share", ("%s: 0x%lx pos: %lu running: %u",
+ (cache == cshare->source_cache) ?
+ "writer" : "reader", (long) cache, (ulong) pos,
+ cshare->running_threads));
+
+ if (cshare->source_cache)
+ {
+ /* A write cache is synchronized to the read caches. */
+
+ if (cache == cshare->source_cache)
+ {
+ /* The writer waits until all readers are here. */
+ while (cshare->running_threads)
+ {
+ DBUG_PRINT("io_cache_share", ("writer waits in lock"));
+ mysql_cond_wait(&cshare->cond_writer, &cshare->mutex);
+ }
+ DBUG_PRINT("io_cache_share", ("writer awoke, going to copy"));
+
+ /* Stay locked. Leave the lock later by unlock_io_cache(). */
+ DBUG_RETURN(1);
+ }
+
+ /* The last thread wakes the writer. */
+ if (!cshare->running_threads)
+ {
+ DBUG_PRINT("io_cache_share", ("waking writer"));
+ mysql_cond_signal(&cshare->cond_writer);
+ }
+
+ /*
+ Readers wait until the data is copied from the writer. Another
+ reason to stop waiting is the removal of the write thread. If this
+ happens, we leave the lock with old data in the buffer.
+ */
+ while ((!cshare->read_end || (cshare->pos_in_file < pos)) &&
+ cshare->source_cache)
+ {
+ DBUG_PRINT("io_cache_share", ("reader waits in lock"));
+ mysql_cond_wait(&cshare->cond, &cshare->mutex);
+ }
+
+ /*
+ If the writer was removed from the share while this thread was
+ asleep, we need to simulate an EOF condition. The writer cannot
+ reset the share variables as they might still be in use by readers
+ of the last block. When we awake here then because the last
+ joining thread signalled us. If the writer is not the last, it
+ will not signal. So it is safe to clear the buffer here.
+ */
+ if (!cshare->read_end || (cshare->pos_in_file < pos))
+ {
+ DBUG_PRINT("io_cache_share", ("reader found writer removed. EOF"));
+ cshare->read_end= cshare->buffer; /* Empty buffer. */
+ cshare->error= 0; /* EOF is not an error. */
+ }
+ }
+ else
+ {
+ /*
+ There are read caches only. The last thread arriving in
+ lock_io_cache() continues with a locked cache and reads the block.
+ */
+ if (!cshare->running_threads)
+ {
+ DBUG_PRINT("io_cache_share", ("last thread joined, going to read"));
+ /* Stay locked. Leave the lock later by unlock_io_cache(). */
+ DBUG_RETURN(1);
+ }
+
+ /*
+ All other threads wait until the requested block is read by the
+ last thread arriving. Another reason to stop waiting is the
+ removal of a thread. If this leads to all threads being in the
+ lock, we have to continue also. The first of the awaken threads
+ will then do the read.
+ */
+ while ((!cshare->read_end || (cshare->pos_in_file < pos)) &&
+ cshare->running_threads)
+ {
+ DBUG_PRINT("io_cache_share", ("reader waits in lock"));
+ mysql_cond_wait(&cshare->cond, &cshare->mutex);
+ }
+
+ /* If the block is not yet read, continue with a locked cache and read. */
+ if (!cshare->read_end || (cshare->pos_in_file < pos))
+ {
+ DBUG_PRINT("io_cache_share", ("reader awoke, going to read"));
+ /* Stay locked. Leave the lock later by unlock_io_cache(). */
+ DBUG_RETURN(1);
+ }
+
+ /* Another thread did read the block already. */
+ }
+ DBUG_PRINT("io_cache_share", ("reader awoke, going to process %u bytes",
+ (uint) (cshare->read_end ? (size_t)
+ (cshare->read_end - cshare->buffer) :
+ 0)));
+
+ /*
+ Leave the lock. Do not call unlock_io_cache() later. The thread that
+ filled the buffer did this and marked all threads as running.
+ */
+ mysql_mutex_unlock(&cshare->mutex);
+ DBUG_RETURN(0);
+}
+
+
+/*
+ Unlock IO cache.
+
+ SYNOPSIS
+ unlock_io_cache()
+ cache The cache of the thread leaving the lock.
+
+ NOTE
+ This is called by the thread that filled the buffer. It marks all
+ threads as running and awakes them. This must not be done by any
+ other thread.
+
+ Do not signal cond_writer. Either there is no writer or the writer
+ is the only one who can call this function.
+
+ The reason for resetting running_threads to total_threads before
+ waking all other threads is that it could be possible that this
+ thread is so fast with processing the buffer that it enters the lock
+ before even one other thread has left it. If every awoken thread
+ would increase running_threads by one, this thread could think that
+ he is again the last to join and would not wait for the other
+ threads to process the data.
+
+ RETURN
+ void
+*/
+
+static void unlock_io_cache(IO_CACHE *cache)
+{
+ IO_CACHE_SHARE *cshare= cache->share;
+ DBUG_ENTER("unlock_io_cache");
+ DBUG_PRINT("io_cache_share", ("%s: 0x%lx pos: %lu running: %u",
+ (cache == cshare->source_cache) ?
+ "writer" : "reader",
+ (long) cache, (ulong) cshare->pos_in_file,
+ cshare->total_threads));
+
+ cshare->running_threads= cshare->total_threads;
+ mysql_cond_broadcast(&cshare->cond);
+ mysql_mutex_unlock(&cshare->mutex);
+ DBUG_VOID_RETURN;
+}
+
+
+/*
+ Read from IO_CACHE when it is shared between several threads.
+
+ SYNOPSIS
+ _my_b_read_r()
+ cache IO_CACHE pointer
+ Buffer Buffer to retrieve count bytes from file
+ Count Number of bytes to read into Buffer
+
+ NOTE
+ This function is only called from the my_b_read() macro when there
+ isn't enough characters in the buffer to satisfy the request.
+
+ IMPLEMENTATION
+
+ It works as follows: when a thread tries to read from a file (that
+ is, after using all the data from the (shared) buffer), it just
+ hangs on lock_io_cache(), waiting for other threads. When the very
+ last thread attempts a read, lock_io_cache() returns 1, the thread
+ does actual IO and unlock_io_cache(), which signals all the waiting
+ threads that data is in the buffer.
+
+ WARNING
+
+ When changing this function, be careful with handling file offsets
+ (end-of_file, pos_in_file). Do not cast them to possibly smaller
+ types than my_off_t unless you can be sure that their value fits.
+ Same applies to differences of file offsets. (Bug #11527)
+
+ When changing this function, check _my_b_read(). It might need the
+ same change.
+
+ RETURN
+ 0 we succeeded in reading all data
+ 1 Error: can't read requested characters
+*/
+
+int _my_b_read_r(IO_CACHE *cache, uchar *Buffer, size_t Count)
+{
+ my_off_t pos_in_file;
+ size_t length, diff_length, left_length;
+ IO_CACHE_SHARE *cshare= cache->share;
+ DBUG_ENTER("_my_b_read_r");
+
+ if ((left_length= (size_t) (cache->read_end - cache->read_pos)))
+ {
+ DBUG_ASSERT(Count >= left_length); /* User is not using my_b_read() */
+ memcpy(Buffer, cache->read_pos, left_length);
+ Buffer+= left_length;
+ Count-= left_length;
+ }
+ while (Count)
+ {
+ size_t cnt, len;
+
+ pos_in_file= cache->pos_in_file + (cache->read_end - cache->buffer);
+ diff_length= (size_t) (pos_in_file & (IO_SIZE-1));
+ length=IO_ROUND_UP(Count+diff_length)-diff_length;
+ length= ((length <= cache->read_length) ?
+ length + IO_ROUND_DN(cache->read_length - length) :
+ length - IO_ROUND_UP(length - cache->read_length));
+ if (cache->type != READ_FIFO &&
+ (length > (cache->end_of_file - pos_in_file)))
+ length= (size_t) (cache->end_of_file - pos_in_file);
+ if (length == 0)
+ {
+ cache->error= (int) left_length;
+ DBUG_RETURN(1);
+ }
+ if (lock_io_cache(cache, pos_in_file))
+ {
+ /* With a synchronized write/read cache we won't come here... */
+ DBUG_ASSERT(!cshare->source_cache);
+ /*
+ ... unless the writer has gone before this thread entered the
+ lock. Simulate EOF in this case. It can be distinguished by
+ cache->file.
+ */
+ if (cache->file < 0)
+ len= 0;
+ else
+ {
+ /*
+ Whenever a function which operates on IO_CACHE flushes/writes
+ some part of the IO_CACHE to disk it will set the property
+ "seek_not_done" to indicate this to other functions operating
+ on the IO_CACHE.
+ */
+ if (cache->seek_not_done)
+ {
+ if (mysql_file_seek(cache->file, pos_in_file, MY_SEEK_SET, MYF(0))
+ == MY_FILEPOS_ERROR)
+ {
+ cache->error= -1;
+ unlock_io_cache(cache);
+ DBUG_RETURN(1);
+ }
+ }
+ len= mysql_file_read(cache->file, cache->buffer, length, cache->myflags);
+ }
+ DBUG_PRINT("io_cache_share", ("read %lu bytes", (ulong) len));
+
+ cache->read_end= cache->buffer + (len == (size_t) -1 ? 0 : len);
+ cache->error= (len == length ? 0 : (int) len);
+ cache->pos_in_file= pos_in_file;
+
+ /* Copy important values to the share. */
+ cshare->error= cache->error;
+ cshare->read_end= cache->read_end;
+ cshare->pos_in_file= pos_in_file;
+
+ /* Mark all threads as running and wake them. */
+ unlock_io_cache(cache);
+ }
+ else
+ {
+ /*
+ With a synchronized write/read cache readers always come here.
+ Copy important values from the share.
+ */
+ cache->error= cshare->error;
+ cache->read_end= cshare->read_end;
+ cache->pos_in_file= cshare->pos_in_file;
+
+ len= ((cache->error == -1) ? (size_t) -1 :
+ (size_t) (cache->read_end - cache->buffer));
+ }
+ cache->read_pos= cache->buffer;
+ cache->seek_not_done= 0;
+ if (len == 0 || len == (size_t) -1)
+ {
+ DBUG_PRINT("io_cache_share", ("reader error. len %lu left %lu",
+ (ulong) len, (ulong) left_length));
+ cache->error= (int) left_length;
+ DBUG_RETURN(1);
+ }
+ cnt= (len > Count) ? Count : len;
+ memcpy(Buffer, cache->read_pos, cnt);
+ Count -= cnt;
+ Buffer+= cnt;
+ left_length+= cnt;
+ cache->read_pos+= cnt;
+ }
+ DBUG_RETURN(0);
+}
+
+
+/*
+ Copy data from write cache to read cache.
+
+ SYNOPSIS
+ copy_to_read_buffer()
+ write_cache The write cache.
+ write_buffer The source of data, mostly the cache buffer.
+ write_length The number of bytes to copy.
+
+ NOTE
+ The write thread will wait for all read threads to join the cache
+ lock. Then it copies the data over and wakes the read threads.
+
+ RETURN
+ void
+*/
+
+static void copy_to_read_buffer(IO_CACHE *write_cache,
+ const uchar *write_buffer, size_t write_length)
+{
+ IO_CACHE_SHARE *cshare= write_cache->share;
+
+ DBUG_ASSERT(cshare->source_cache == write_cache);
+ /*
+ write_length is usually less or equal to buffer_length.
+ It can be bigger if _my_b_write() is called with a big length.
+ */
+ while (write_length)
+ {
+ size_t copy_length= MY_MIN(write_length, write_cache->buffer_length);
+ int MY_ATTRIBUTE((unused)) rc;
+
+ rc= lock_io_cache(write_cache, write_cache->pos_in_file);
+ /* The writing thread does always have the lock when it awakes. */
+ DBUG_ASSERT(rc);
+
+ memcpy(cshare->buffer, write_buffer, copy_length);
+
+ cshare->error= 0;
+ cshare->read_end= cshare->buffer + copy_length;
+ cshare->pos_in_file= write_cache->pos_in_file;
+
+ /* Mark all threads as running and wake them. */
+ unlock_io_cache(write_cache);
+
+ write_buffer+= copy_length;
+ write_length-= copy_length;
+ }
+}
+
+
+/*
+ Do sequential read from the SEQ_READ_APPEND cache.
+
+ We do this in three stages:
+ - first read from info->buffer
+ - then if there are still data to read, try the file descriptor
+ - afterwards, if there are still data to read, try append buffer
+
+ RETURNS
+ 0 Success
+ 1 Failed to read
+*/
+
+int _my_b_seq_read(IO_CACHE *info, uchar *Buffer, size_t Count)
+{
+ size_t length, diff_length, left_length, save_count, max_length;
+ my_off_t pos_in_file;
+ save_count=Count;
+
+ /* first, read the regular buffer */
+ if ((left_length=(size_t) (info->read_end-info->read_pos)))
+ {
+ DBUG_ASSERT(Count > left_length); /* User is not using my_b_read() */
+ memcpy(Buffer,info->read_pos, left_length);
+ Buffer+=left_length;
+ Count-=left_length;
+ }
+ lock_append_buffer(info);
+
+ /* pos_in_file always point on where info->buffer was read */
+ if ((pos_in_file=info->pos_in_file +
+ (size_t) (info->read_end - info->buffer)) >= info->end_of_file)
+ goto read_append_buffer;
+
+ /*
+ With read-append cache we must always do a seek before we read,
+ because the write could have moved the file pointer astray
+ */
+ if (mysql_file_seek(info->file, pos_in_file, MY_SEEK_SET, MYF(0)) == MY_FILEPOS_ERROR)
+ {
+ info->error= -1;
+ unlock_append_buffer(info);
+ return (1);
+ }
+ info->seek_not_done=0;
+
+ diff_length= (size_t) (pos_in_file & (IO_SIZE-1));
+
+ /* now the second stage begins - read from file descriptor */
+ if (Count >= (size_t) (IO_SIZE+(IO_SIZE-diff_length)))
+ {
+ /* Fill first intern buffer */
+ size_t read_length;
+
+ length=(Count & (size_t) ~(IO_SIZE-1))-diff_length;
+ if ((read_length= mysql_file_read(info->file,Buffer, length,
+ info->myflags)) == (size_t) -1)
+ {
+ info->error= -1;
+ unlock_append_buffer(info);
+ return 1;
+ }
+ Count-=read_length;
+ Buffer+=read_length;
+ pos_in_file+=read_length;
+
+ if (read_length != length)
+ {
+ /*
+ We only got part of data; Read the rest of the data from the
+ write buffer
+ */
+ goto read_append_buffer;
+ }
+ left_length+=length;
+ diff_length=0;
+ }
+
+ max_length= info->read_length-diff_length;
+ if (max_length > (info->end_of_file - pos_in_file))
+ max_length= (size_t) (info->end_of_file - pos_in_file);
+ if (!max_length)
+ {
+ if (Count)
+ goto read_append_buffer;
+ length=0; /* Didn't read any more chars */
+ }
+ else
+ {
+ length= mysql_file_read(info->file,info->buffer, max_length, info->myflags);
+ if (length == (size_t) -1)
+ {
+ info->error= -1;
+ unlock_append_buffer(info);
+ return 1;
+ }
+ if (length < Count)
+ {
+ memcpy(Buffer, info->buffer, length);
+ Count -= length;
+ Buffer += length;
+
+ /*
+ added the line below to make
+ DBUG_ASSERT(pos_in_file==info->end_of_file) pass.
+ otherwise this does not appear to be needed
+ */
+ pos_in_file += length;
+ goto read_append_buffer;
+ }
+ }
+ unlock_append_buffer(info);
+ info->read_pos=info->buffer+Count;
+ info->read_end=info->buffer+length;
+ info->pos_in_file=pos_in_file;
+ memcpy(Buffer,info->buffer,(size_t) Count);
+ return 0;
+
+read_append_buffer:
+
+ /*
+ Read data from the current write buffer.
+ Count should never be == 0 here (The code will work even if count is 0)
+ */
+
+ {
+ /* First copy the data to Count */
+ size_t len_in_buff = (size_t) (info->write_pos - info->append_read_pos);
+ size_t copy_len;
+ size_t transfer_len;
+
+ DBUG_ASSERT(info->append_read_pos <= info->write_pos);
+ /*
+ TODO: figure out if the assert below is needed or correct.
+ */
+ DBUG_ASSERT(pos_in_file == info->end_of_file);
+ copy_len= MY_MIN(Count, len_in_buff);
+ memcpy(Buffer, info->append_read_pos, copy_len);
+ info->append_read_pos += copy_len;
+ Count -= copy_len;
+ if (Count)
+ info->error = (int)(save_count - Count);
+
+ /* Fill read buffer with data from write buffer */
+ memcpy(info->buffer, info->append_read_pos,
+ (size_t) (transfer_len=len_in_buff - copy_len));
+ info->read_pos= info->buffer;
+ info->read_end= info->buffer+transfer_len;
+ info->append_read_pos=info->write_pos;
+ info->pos_in_file=pos_in_file+copy_len;
+ info->end_of_file+=len_in_buff;
+ }
+ unlock_append_buffer(info);
+ return Count ? 1 : 0;
+}
+
+
+/* Read one byte when buffer is empty */
+
+int _my_b_get(IO_CACHE *info)
+{
+ uchar buff;
+ IO_CACHE_CALLBACK pre_read,post_read;
+ if ((pre_read = info->pre_read))
+ (*pre_read)(info);
+ if ((*(info)->read_function)(info,&buff,1))
+ return my_b_EOF;
+ if ((post_read = info->post_read))
+ (*post_read)(info);
+ return (int) (uchar) buff;
+}
+
+/*
+ Write a byte buffer to IO_CACHE and flush to disk
+ if IO_CACHE is full.
+
+ RETURN VALUE
+ 1 On error on write
+ 0 On success
+ -1 On error; my_errno contains error code.
+*/
+
+int _my_b_write(IO_CACHE *info, const uchar *Buffer, size_t Count)
+{
+ size_t rest_length,length;
+ my_off_t pos_in_file= info->pos_in_file;
+
+ DBUG_EXECUTE_IF("simulate_huge_load_data_file",
+ {
+ pos_in_file=(my_off_t)(5000000000ULL);
+ });
+ if (pos_in_file+info->buffer_length > info->end_of_file)
+ {
+ errno=EFBIG;
+ set_my_errno(EFBIG);
+ return info->error = -1;
+ }
+
+ rest_length= (size_t) (info->write_end - info->write_pos);
+ memcpy(info->write_pos,Buffer,(size_t) rest_length);
+ Buffer+=rest_length;
+ Count-=rest_length;
+ info->write_pos+=rest_length;
+
+ if (my_b_flush_io_cache(info,1))
+ return 1;
+ if (Count >= IO_SIZE)
+ { /* Fill first intern buffer */
+ length=Count & (size_t) ~(IO_SIZE-1);
+ if (info->seek_not_done)
+ {
+ /*
+ Whenever a function which operates on IO_CACHE flushes/writes
+ some part of the IO_CACHE to disk it will set the property
+ "seek_not_done" to indicate this to other functions operating
+ on the IO_CACHE.
+ */
+ if (mysql_file_seek(info->file, info->pos_in_file, MY_SEEK_SET, MYF(0)))
+ {
+ info->error= -1;
+ return (1);
+ }
+ info->seek_not_done=0;
+ }
+ if (mysql_file_write(info->file, Buffer, length, info->myflags | MY_NABP))
+ return info->error= -1;
+
+ /*
+ In case of a shared I/O cache with a writer we normally do direct
+ write cache to read cache copy. Simulate this here by direct
+ caller buffer to read cache copy. Do it after the write so that
+ the cache readers actions on the flushed part can go in parallel
+ with the write of the extra stuff. copy_to_read_buffer()
+ synchronizes writer and readers so that after this call the
+ readers can act on the extra stuff while the writer can go ahead
+ and prepare the next output. copy_to_read_buffer() relies on
+ info->pos_in_file.
+ */
+ if (info->share)
+ copy_to_read_buffer(info, Buffer, length);
+
+ Count-=length;
+ Buffer+=length;
+ info->pos_in_file+=length;
+ }
+ memcpy(info->write_pos,Buffer,(size_t) Count);
+ info->write_pos+=Count;
+ return 0;
+}
+
+
+/*
+ Append a block to the write buffer.
+ This is done with the buffer locked to ensure that we don't read from
+ the write buffer before we are ready with it.
+*/
+
+int my_b_append(IO_CACHE *info, const uchar *Buffer, size_t Count)
+{
+ size_t rest_length,length;
+
+ /*
+ Assert that we cannot come here with a shared cache. If we do one
+ day, we might need to add a call to copy_to_read_buffer().
+ */
+ DBUG_ASSERT(!info->share);
+
+ lock_append_buffer(info);
+ rest_length= (size_t) (info->write_end - info->write_pos);
+ if (Count <= rest_length)
+ goto end;
+ memcpy(info->write_pos, Buffer, rest_length);
+ Buffer+=rest_length;
+ Count-=rest_length;
+ info->write_pos+=rest_length;
+ if (my_b_flush_io_cache(info,0))
+ {
+ unlock_append_buffer(info);
+ return 1;
+ }
+ if (Count >= IO_SIZE)
+ { /* Fill first intern buffer */
+ length=Count & (size_t) ~(IO_SIZE-1);
+ if (mysql_file_write(info->file,Buffer, length, info->myflags | MY_NABP))
+ {
+ unlock_append_buffer(info);
+ return info->error= -1;
+ }
+ Count-=length;
+ Buffer+=length;
+ info->end_of_file+=length;
+ }
+
+end:
+ memcpy(info->write_pos,Buffer,(size_t) Count);
+ info->write_pos+=Count;
+ unlock_append_buffer(info);
+ return 0;
+}
+
+
+int my_b_safe_write(IO_CACHE *info, const uchar *Buffer, size_t Count)
+{
+ /*
+ Sasha: We are not writing this with the ? operator to avoid hitting
+ a possible compiler bug. At least gcc 2.95 cannot deal with
+ several layers of ternary operators that evaluated comma(,) operator
+ expressions inside - I do have a test case if somebody wants it
+ */
+ if (info->type == SEQ_READ_APPEND)
+ return my_b_append(info, Buffer, Count);
+ return my_b_write(info, Buffer, Count);
+}
+
+
+/*
+ Write a block to disk where part of the data may be inside the record
+ buffer. As all write calls to the data goes through the cache,
+ we will never get a seek over the end of the buffer
+*/
+
+int my_block_write(IO_CACHE *info, const uchar *Buffer, size_t Count,
+ my_off_t pos)
+{
+ size_t length;
+ int error=0;
+
+ /*
+ Assert that we cannot come here with a shared cache. If we do one
+ day, we might need to add a call to copy_to_read_buffer().
+ */
+ DBUG_ASSERT(!info->share);
+
+ if (pos < info->pos_in_file)
+ {
+ /* Of no overlap, write everything without buffering */
+ if (pos + Count <= info->pos_in_file)
+ return (int)mysql_file_pwrite(info->file, Buffer, Count, pos,
+ info->myflags | MY_NABP);
+ /* Write the part of the block that is before buffer */
+ length= (uint) (info->pos_in_file - pos);
+ if (mysql_file_pwrite(info->file, Buffer, length, pos, info->myflags | MY_NABP))
+ info->error= error= -1;
+ Buffer+=length;
+ pos+= length;
+ Count-= length;
+#ifdef _WIN32
+ info->seek_not_done=1;
+#endif
+ }
+
+ /* Check if we want to write inside the used part of the buffer.*/
+ length= (size_t) (info->write_end - info->buffer);
+ if (pos < info->pos_in_file + length)
+ {
+ size_t offset= (size_t) (pos - info->pos_in_file);
+ length-=offset;
+ if (length > Count)
+ length=Count;
+ memcpy(info->buffer+offset, Buffer, length);
+ Buffer+=length;
+ Count-= length;
+ /* Fix length of buffer if the new data was larger */
+ if (info->buffer+length > info->write_pos)
+ info->write_pos=info->buffer+length;
+ if (!Count)
+ return (error);
+ }
+ /* Write at the end of the current buffer; This is the normal case */
+ if (_my_b_write(info, Buffer, Count))
+ error= -1;
+ return error;
+}
+
+
+ /* Flush write cache */
+
+#define LOCK_APPEND_BUFFER if (need_append_buffer_lock) \
+ lock_append_buffer(info);
+#define UNLOCK_APPEND_BUFFER if (need_append_buffer_lock) \
+ unlock_append_buffer(info);
+
+int my_b_flush_io_cache(IO_CACHE *info,
+ int need_append_buffer_lock MY_ATTRIBUTE((unused)))
+{
+ size_t length;
+ my_off_t pos_in_file;
+ my_bool append_cache= (info->type == SEQ_READ_APPEND);
+ DBUG_ENTER("my_b_flush_io_cache");
+ DBUG_PRINT("enter", ("cache: 0x%lx", (long) info));
+
+ DBUG_EXECUTE_IF("simulate_error_during_flush_cache_to_file",
+ {
+ DBUG_RETURN(TRUE);
+ });
+ if (!append_cache)
+ need_append_buffer_lock= 0;
+
+ if (info->type == WRITE_CACHE || append_cache)
+ {
+ if (info->file == -1)
+ {
+ if (real_open_cached_file(info))
+ DBUG_RETURN((info->error= -1));
+ }
+ LOCK_APPEND_BUFFER;
+
+ if ((length=(size_t) (info->write_pos - info->write_buffer)))
+ {
+ /*
+ In case of a shared I/O cache with a writer we do direct write
+ cache to read cache copy. Do it before the write here so that
+ the readers can work in parallel with the write.
+ copy_to_read_buffer() relies on info->pos_in_file.
+ */
+ if (info->share)
+ copy_to_read_buffer(info, info->write_buffer, length);
+
+ pos_in_file=info->pos_in_file;
+ /*
+ If we have append cache, we always open the file with
+ O_APPEND which moves the pos to EOF automatically on every write
+ */
+ if (!append_cache && info->seek_not_done)
+ { /* File touched, do seek */
+ if (mysql_file_seek(info->file, pos_in_file, MY_SEEK_SET, MYF(0)) ==
+ MY_FILEPOS_ERROR)
+ {
+ UNLOCK_APPEND_BUFFER;
+ DBUG_RETURN((info->error= -1));
+ }
+ if (!append_cache)
+ info->seek_not_done=0;
+ }
+ if (!append_cache)
+ info->pos_in_file+=length;
+ info->write_end= (info->write_buffer+info->buffer_length-
+ ((pos_in_file+length) & (IO_SIZE-1)));
+
+ if (mysql_file_write(info->file,info->write_buffer,length,
+ info->myflags | MY_NABP))
+ info->error= -1;
+ else
+ info->error= 0;
+ if (!append_cache)
+ {
+ set_if_bigger(info->end_of_file,(pos_in_file+length));
+ }
+ else
+ {
+ info->end_of_file+=(info->write_pos-info->append_read_pos);
+ DBUG_ASSERT(info->end_of_file == mysql_file_tell(info->file, MYF(0)));
+ }
+
+ info->append_read_pos=info->write_pos=info->write_buffer;
+ ++info->disk_writes;
+ UNLOCK_APPEND_BUFFER;
+ DBUG_RETURN(info->error);
+ }
+ }
+ UNLOCK_APPEND_BUFFER;
+ DBUG_RETURN(0);
+}
+
+/*
+ Free an IO_CACHE object
+
+ SYNOPSOS
+ end_io_cache()
+ info IO_CACHE Handle to free
+
+ NOTES
+ It's currently safe to call this if one has called init_io_cache()
+ on the 'info' object, even if init_io_cache() failed.
+ This function is also safe to call twice with the same handle.
+
+ RETURN
+ 0 ok
+ # Error
+*/
+
+int end_io_cache(IO_CACHE *info)
+{
+ int error=0;
+ IO_CACHE_CALLBACK pre_close;
+ DBUG_ENTER("end_io_cache");
+ DBUG_PRINT("enter",("cache: 0x%lx", (ulong) info));
+
+ /*
+ Every thread must call remove_io_thread(). The last one destroys
+ the share elements.
+ */
+ DBUG_ASSERT(!info->share || !info->share->total_threads);
+
+ if ((pre_close=info->pre_close))
+ {
+ (*pre_close)(info);
+ info->pre_close= 0;
+ }
+ if (info->alloced_buffer)
+ {
+ info->alloced_buffer=0;
+ if (info->file != -1) /* File doesn't exist */
+ error= my_b_flush_io_cache(info,1);
+ my_free(info->buffer);
+ info->buffer=info->read_pos=(uchar*) 0;
+ }
+ if (info->type == SEQ_READ_APPEND)
+ {
+ /* Destroy allocated mutex */
+ info->type= TYPE_NOT_SET;
+ mysql_mutex_destroy(&info->append_buffer_lock);
+ }
+ DBUG_RETURN(error);
+} /* end_io_cache */
+
+
+/**********************************************************************
+ Testing of MF_IOCACHE
+**********************************************************************/
+
+#ifdef MAIN
+
+#include <my_dir.h>
+
+void die(const char* fmt, ...)
+{
+ va_list va_args;
+ va_start(va_args,fmt);
+ fprintf(stderr,"Error:");
+ vfprintf(stderr, fmt,va_args);
+ fprintf(stderr,", errno=%d\n", errno);
+ exit(1);
+}
+
+int open_file(const char* fname, IO_CACHE* info, int cache_size)
+{
+ int fd;
+ if ((fd=my_open(fname,O_CREAT | O_RDWR,MYF(MY_WME))) < 0)
+ die("Could not open %s", fname);
+ if (init_io_cache(info, fd, cache_size, SEQ_READ_APPEND, 0,0,MYF(MY_WME)))
+ die("failed in init_io_cache()");
+ return fd;
+}
+
+void close_file(IO_CACHE* info)
+{
+ end_io_cache(info);
+ my_close(info->file, MYF(MY_WME));
+}
+
+int main(int argc, char** argv)
+{
+ IO_CACHE sra_cache; /* SEQ_READ_APPEND */
+ MY_STAT status;
+ const char* fname="/tmp/iocache.test";
+ int cache_size=16384;
+ char llstr_buf[22];
+ int max_block,total_bytes=0;
+ int i,num_loops=100,error=0;
+ char *p;
+ char* block, *block_end;
+ MY_INIT(argv[0]);
+ max_block = cache_size*3;
+ if (!(block=(char*)my_malloc(PSI_NOT_INSTRUMENTED,
+ max_block,MYF(MY_WME))))
+ die("Not enough memory to allocate test block");
+ block_end = block + max_block;
+ for (p = block,i=0; p < block_end;i++)
+ {
+ *p++ = (char)i;
+ }
+ if (my_stat(fname,&status, MYF(0)) &&
+ my_delete(fname,MYF(MY_WME)))
+ {
+ die("Delete of %s failed, aborting", fname);
+ }
+ open_file(fname,&sra_cache, cache_size);
+ for (i = 0; i < num_loops; i++)
+ {
+ char buf[4];
+ int block_size = abs(rand() % max_block);
+ int4store(buf, block_size);
+ if (my_b_append(&sra_cache,buf,4) ||
+ my_b_append(&sra_cache, block, block_size))
+ die("write failed");
+ total_bytes += 4+block_size;
+ }
+ close_file(&sra_cache);
+ my_free(block);
+ if (!my_stat(fname,&status,MYF(MY_WME)))
+ die("%s failed to stat, but I had just closed it,\
+ wonder how that happened");
+ printf("Final size of %s is %s, wrote %d bytes\n",fname,
+ llstr(status.st_size,llstr_buf),
+ total_bytes);
+ my_delete(fname, MYF(MY_WME));
+ /* check correctness of tests */
+ if (total_bytes != status.st_size)
+ {
+ fprintf(stderr,"Not the same number of bytes acutally in file as bytes \
+supposedly written\n");
+ error=1;
+ }
+ exit(error);
+ return 0;
+}
+#endif
diff --git a/mysql/mysys/mf_iocache2.c b/mysql/mysys/mf_iocache2.c
new file mode 100644
index 0000000..c3505e6
--- /dev/null
+++ b/mysql/mysys/mf_iocache2.c
@@ -0,0 +1,514 @@
+/* Copyright (c) 2000, 2017, Oracle and/or its affiliates. All rights reserved.
+
+ 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; version 2 of the License.
+
+ 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+/*
+ More functions to be used with IO_CACHE files
+*/
+
+#include "mysys_priv.h"
+#include "my_sys.h"
+#include <m_string.h>
+#include <stdarg.h>
+#include <m_ctype.h>
+
+/*
+ Copy contents of an IO_CACHE to a file.
+
+ SYNOPSIS
+ my_b_copy_to_file()
+ cache IO_CACHE to copy from
+ file File to copy to
+
+ DESCRIPTION
+ Copy the contents of the cache to the file. The cache will be
+ re-inited to a read cache and will read from the beginning of the
+ cache.
+
+ If a failure to write fully occurs, the cache is only copied
+ partially.
+
+ TODO
+ Make this function solid by handling partial reads from the cache
+ in a correct manner: it should be atomic.
+
+ RETURN VALUE
+ 0 All OK
+ 1 An error occured
+*/
+int
+my_b_copy_to_file(IO_CACHE *cache, FILE *file)
+{
+ size_t bytes_in_cache;
+ DBUG_ENTER("my_b_copy_to_file");
+
+ /* Reinit the cache to read from the beginning of the cache */
+ if (reinit_io_cache(cache, READ_CACHE, 0L, FALSE, FALSE))
+ DBUG_RETURN(1);
+ bytes_in_cache= my_b_bytes_in_cache(cache);
+ do
+ {
+ if (my_fwrite(file, cache->read_pos, bytes_in_cache,
+ MYF(MY_WME | MY_NABP)) == (size_t) -1)
+ DBUG_RETURN(1);
+ cache->read_pos= cache->read_end;
+ } while ((bytes_in_cache= my_b_fill(cache)));
+ if(cache->error == -1)
+ DBUG_RETURN(1);
+ DBUG_RETURN(0);
+}
+
+
+my_off_t my_b_append_tell(IO_CACHE* info)
+{
+ /*
+ Sometimes we want to make sure that the variable is not put into
+ a register in debugging mode so we can see its value in the core
+ */
+#ifndef DBUG_OFF
+# define dbug_volatile volatile
+#else
+# define dbug_volatile
+#endif
+
+ /*
+ Prevent optimizer from putting res in a register when debugging
+ we need this to be able to see the value of res when the assert fails
+ */
+ dbug_volatile my_off_t res;
+
+ /*
+ We need to lock the append buffer mutex to keep flush_io_cache()
+ from messing with the variables that we need in order to provide the
+ answer to the question.
+ */
+ mysql_mutex_lock(&info->append_buffer_lock);
+
+#ifndef DBUG_OFF
+ /*
+ Make sure EOF is where we think it is. Note that we cannot just use
+ my_tell() because we have a reader thread that could have left the
+ file offset in a non-EOF location
+ */
+ {
+ volatile my_off_t save_pos;
+ save_pos = my_tell(info->file,MYF(0));
+ my_seek(info->file,(my_off_t)0,MY_SEEK_END,MYF(0));
+ /*
+ Save the value of my_tell in res so we can see it when studying coredump
+ */
+ DBUG_ASSERT(info->end_of_file - (info->append_read_pos-info->write_buffer)
+ == (res=my_tell(info->file,MYF(0))));
+ my_seek(info->file,save_pos,MY_SEEK_SET,MYF(0));
+ }
+#endif
+ res = info->end_of_file + (info->write_pos-info->append_read_pos);
+ mysql_mutex_unlock(&info->append_buffer_lock);
+ return res;
+}
+
+my_off_t my_b_safe_tell(IO_CACHE *info)
+{
+ if (unlikely(info->type == SEQ_READ_APPEND))
+ return my_b_append_tell(info);
+ return my_b_tell(info);
+}
+
+/*
+ Make next read happen at the given position
+ For write cache, make next write happen at the given position
+*/
+
+void my_b_seek(IO_CACHE *info,my_off_t pos)
+{
+ my_off_t offset;
+ DBUG_ENTER("my_b_seek");
+ DBUG_PRINT("enter",("pos: %lu", (ulong) pos));
+
+ /*
+ TODO:
+ Verify that it is OK to do seek in the non-append
+ area in SEQ_READ_APPEND cache
+ a) see if this always works
+ b) see if there is a better way to make it work
+ */
+ if (info->type == SEQ_READ_APPEND)
+ (void) flush_io_cache(info);
+
+ offset=(pos - info->pos_in_file);
+
+ if (info->type == READ_CACHE || info->type == SEQ_READ_APPEND)
+ {
+ /* TODO: explain why this works if pos < info->pos_in_file */
+ if ((ulonglong) offset < (ulonglong) (info->read_end - info->buffer))
+ {
+ /* The read is in the current buffer; Reuse it */
+ info->read_pos = info->buffer + offset;
+ DBUG_VOID_RETURN;
+ }
+ else
+ {
+ /* Force a new read on next my_b_read */
+ info->read_pos=info->read_end=info->buffer;
+ }
+ }
+ else if (info->type == WRITE_CACHE)
+ {
+ /* If write is in current buffer, reuse it */
+ if ((ulonglong) offset <=
+ (ulonglong) (info->write_end - info->write_buffer))
+ {
+ info->write_pos = info->write_buffer + offset;
+ DBUG_VOID_RETURN;
+ }
+ (void) flush_io_cache(info);
+ /* Correct buffer end so that we write in increments of IO_SIZE */
+ info->write_end=(info->write_buffer+info->buffer_length-
+ (pos & (IO_SIZE-1)));
+ }
+ info->pos_in_file=pos;
+ info->seek_not_done=1;
+ DBUG_VOID_RETURN;
+}
+
+
+/*
+ Fill buffer of the cache.
+
+ NOTES
+ This assumes that you have already used all characters in the CACHE,
+ independent of the read_pos value!
+
+ RETURN
+ 0 On error or EOF (info->error = -1 on error)
+ # Number of characters
+*/
+
+
+size_t my_b_fill(IO_CACHE *info)
+{
+ my_off_t pos_in_file=(info->pos_in_file+
+ (size_t) (info->read_end - info->buffer));
+ size_t diff_length, length, max_length;
+
+ if (info->seek_not_done)
+ { /* File touched, do seek */
+ if (my_seek(info->file,pos_in_file,MY_SEEK_SET,MYF(0)) ==
+ MY_FILEPOS_ERROR)
+ {
+ info->error= 0;
+ return 0;
+ }
+ info->seek_not_done=0;
+ }
+ diff_length=(size_t) (pos_in_file & (IO_SIZE-1));
+ max_length=(info->read_length-diff_length);
+ if (max_length >= (info->end_of_file - pos_in_file))
+ max_length= (size_t) (info->end_of_file - pos_in_file);
+
+ if (!max_length)
+ {
+ info->error= 0;
+ return 0; /* EOF */
+ }
+ DBUG_EXECUTE_IF ("simulate_my_b_fill_error",
+ {DBUG_SET("+d,simulate_file_read_error");});
+ if ((length= my_read(info->file,info->buffer,max_length,
+ info->myflags)) == (size_t) -1)
+ {
+ info->error= -1;
+ return 0;
+ }
+ info->read_pos=info->buffer;
+ info->read_end=info->buffer+length;
+ info->pos_in_file=pos_in_file;
+ return length;
+}
+
+
+/*
+ Read a string ended by '\n' into a buffer of 'max_length' size.
+ Returns number of characters read, 0 on error.
+ last byte is set to '\0'
+ If buffer is full then to[max_length-1] will be set to \0.
+*/
+
+size_t my_b_gets(IO_CACHE *info, char *to, size_t max_length)
+{
+ char *start = to;
+ size_t length;
+ max_length--; /* Save place for end \0 */
+
+ /* Calculate number of characters in buffer */
+ if (!(length= my_b_bytes_in_cache(info)) &&
+ !(length= my_b_fill(info)))
+ return 0;
+
+ for (;;)
+ {
+ uchar *pos, *end;
+ if (length > max_length)
+ length=max_length;
+ for (pos=info->read_pos,end=pos+length ; pos < end ;)
+ {
+ if ((*to++ = *pos++) == '\n')
+ {
+ info->read_pos=pos;
+ *to='\0';
+ return (size_t) (to-start);
+ }
+ }
+ if (!(max_length-=length))
+ {
+ /* Found enough charcters; Return found string */
+ info->read_pos=pos;
+ *to='\0';
+ return (size_t) (to-start);
+ }
+ if (!(length=my_b_fill(info)))
+ return 0;
+ }
+}
+
+
+my_off_t my_b_filelength(IO_CACHE *info)
+{
+ if (info->type == WRITE_CACHE)
+ return my_b_tell(info);
+
+ info->seek_not_done= 1;
+ return my_seek(info->file, 0L, MY_SEEK_END, MYF(0));
+}
+
+
+/**
+ Simple printf version. Used for logging in MySQL.
+ Supports '%c', '%s', '%b', '%d', '%u', '%ld', '%lu' and '%llu'.
+ Returns number of written characters, or (size_t) -1 on error.
+
+ @param info The IO_CACHE to write to
+ @param fmt format string
+ @param ... variable list of arguments
+ @return number of bytes written, -1 if an error occurred
+*/
+
+size_t my_b_printf(IO_CACHE *info, const char* fmt, ...)
+{
+ size_t result;
+ va_list args;
+ va_start(args,fmt);
+ result=my_b_vprintf(info, fmt, args);
+ va_end(args);
+ return result;
+}
+
+
+/**
+ Implementation of my_b_printf.
+
+ @param info The IO_CACHE to write to
+ @param fmt format string
+ @param args variable list of arguments
+ @return number of bytes written, -1 if an error occurred
+*/
+
+size_t my_b_vprintf(IO_CACHE *info, const char* fmt, va_list args)
+{
+ size_t out_length= 0;
+ uint minimum_width; /* as yet unimplemented */
+ uint minimum_width_sign;
+ uint precision; /* as yet unimplemented for anything but %b */
+ my_bool is_zero_padded;
+
+ /*
+ Store the location of the beginning of a format directive, for the
+ case where we learn we shouldn't have been parsing a format string
+ at all, and we don't want to lose the flag/precision/width/size
+ information.
+ */
+ const char* backtrack;
+
+ for (; *fmt != '\0'; fmt++)
+ {
+ /* Copy everything until '%' or end of string */
+ const char *start=fmt;
+ size_t length;
+
+ for (; (*fmt != '\0') && (*fmt != '%'); fmt++) ;
+
+ length= (size_t) (fmt - start);
+ out_length+=length;
+ if (my_b_write(info, (const uchar*) start, length))
+ goto err;
+
+ if (*fmt == '\0') /* End of format */
+ return out_length;
+
+ /*
+ By this point, *fmt must be a percent; Keep track of this location and
+ skip over the percent character.
+ */
+ DBUG_ASSERT(*fmt == '%');
+ backtrack= fmt;
+ fmt++;
+
+ is_zero_padded= FALSE;
+ minimum_width_sign= 1;
+ minimum_width= 0;
+ precision= 0;
+ /* Skip if max size is used (to be compatible with printf) */
+
+process_flags:
+ switch (*fmt)
+ {
+ case '-':
+ minimum_width_sign= -1; fmt++; goto process_flags;
+ case '0':
+ is_zero_padded= TRUE; fmt++; goto process_flags;
+ case '#':
+ /** @todo Implement "#" conversion flag. */ fmt++; goto process_flags;
+ case ' ':
+ /** @todo Implement " " conversion flag. */ fmt++; goto process_flags;
+ case '+':
+ /** @todo Implement "+" conversion flag. */ fmt++; goto process_flags;
+ }
+
+ if (*fmt == '*')
+ {
+ minimum_width= (int) va_arg(args, int);
+ fmt++;
+ }
+ else
+ {
+ while (my_isdigit(&my_charset_latin1, *fmt)) {
+ minimum_width=(minimum_width * 10) + (*fmt - '0');
+ fmt++;
+ }
+ }
+ minimum_width*= minimum_width_sign;
+
+ if (*fmt == '.')
+ {
+ fmt++;
+ if (*fmt == '*') {
+ precision= (int) va_arg(args, int);
+ fmt++;
+ }
+ else
+ {
+ while (my_isdigit(&my_charset_latin1, *fmt)) {
+ precision=(precision * 10) + (*fmt - '0');
+ fmt++;
+ }
+ }
+ }
+
+ if (*fmt == 's') /* String parameter */
+ {
+ char *par = va_arg(args, char *);
+ size_t length2 = strlen(par);
+ /* TODO: implement precision */
+ out_length+= length2;
+ if (my_b_write(info, (uchar*) par, length2))
+ goto err;
+ }
+ else if (*fmt == 'c') /* char type parameter */
+ {
+ char par[2];
+ par[0] = va_arg(args, int);
+ out_length++;
+ if (my_b_write(info, (uchar*) par, 1))
+ goto err;
+ }
+ else if (*fmt == 'b') /* Sized buffer parameter, only precision makes sense */
+ {
+ char *par = va_arg(args, char *);
+ out_length+= precision;
+ if (my_b_write(info, (uchar*) par, precision))
+ goto err;
+ }
+ else if (*fmt == 'd' || *fmt == 'u') /* Integer parameter */
+ {
+ int iarg;
+ size_t length2;
+ char buff[32];
+
+ iarg = va_arg(args, int);
+ if (*fmt == 'd')
+ length2= (size_t) (int10_to_str((long) iarg,buff, -10) - buff);
+ else
+ length2= (uint) (int10_to_str((long) (uint) iarg,buff,10)- buff);
+
+ /* minimum width padding */
+ if (minimum_width > length2)
+ {
+ char *buffz;
+
+ buffz= my_alloca(minimum_width - length2);
+ if (is_zero_padded)
+ memset(buffz, '0', minimum_width - length2);
+ else
+ memset(buffz, ' ', minimum_width - length2);
+ if (my_b_write(info, buffz, minimum_width - length2))
+ {
+ goto err;
+ }
+ }
+
+ out_length+= length2;
+ if (my_b_write(info, (uchar*) buff, length2))
+ goto err;
+ }
+ else if ((*fmt == 'l' && fmt[1] == 'd') || fmt[1] == 'u')
+ /* long parameter */
+ {
+ long iarg;
+ size_t length2;
+ char buff[32];
+
+ iarg = va_arg(args, long);
+ if (*++fmt == 'd')
+ length2= (size_t) (int10_to_str(iarg,buff, -10) - buff);
+ else
+ length2= (size_t) (int10_to_str(iarg,buff,10)- buff);
+ out_length+= length2;
+ if (my_b_write(info, (uchar*) buff, length2))
+ goto err;
+ }
+ else if (fmt[0] == 'l' && fmt[1] == 'l' && fmt[2] == 'u')
+ {
+ ulonglong iarg;
+ size_t length2;
+ char buff[32];
+
+ iarg = va_arg(args, ulonglong);
+ length2= (size_t) (longlong10_to_str(iarg, buff, 10) - buff);
+ out_length+= length2;
+ fmt+= 2;
+ if (my_b_write(info, (uchar *) buff, length2))
+ goto err;
+ }
+ else
+ {
+ /* %% or unknown code */
+ if (my_b_write(info, (uchar*) backtrack, (size_t) (fmt-backtrack)))
+ goto err;
+ out_length+= fmt-backtrack;
+ }
+ }
+ return out_length;
+
+err:
+ return (size_t) -1;
+}
diff --git a/mysql/mysys/mf_keycache.c b/mysql/mysys/mf_keycache.c
new file mode 100644
index 0000000..cb712ed
--- /dev/null
+++ b/mysql/mysys/mf_keycache.c
@@ -0,0 +1,4051 @@
+/* Copyright (c) 2000, 2016, Oracle and/or its affiliates. All rights reserved.
+
+ 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; version 2 of the License.
+
+ 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+/**
+ @file
+ These functions handle keyblock cacheing for ISAM and MyISAM tables.
+
+ One cache can handle many files.
+ It must contain buffers of the same blocksize.
+ init_key_cache() should be used to init cache handler.
+
+ The free list (free_block_list) is a stack like structure.
+ When a block is freed by free_block(), it is pushed onto the stack.
+ When a new block is required it is first tried to pop one from the stack.
+ If the stack is empty, it is tried to get a never-used block from the pool.
+ If this is empty too, then a block is taken from the LRU ring, flushing it
+ to disk, if neccessary. This is handled in find_key_block().
+ With the new free list, the blocks can have three temperatures:
+ hot, warm and cold (which is free). This is remembered in the block header
+ by the enum BLOCK_TEMPERATURE temperature variable. Remembering the
+ temperature is neccessary to correctly count the number of warm blocks,
+ which is required to decide when blocks are allowed to become hot. Whenever
+ a block is inserted to another (sub-)chain, we take the old and new
+ temperature into account to decide if we got one more or less warm block.
+ blocks_unused is the sum of never used blocks in the pool and of currently
+ free blocks. blocks_used is the number of blocks fetched from the pool and
+ as such gives the maximum number of in-use blocks at any time.
+*/
+
+/*
+ Key Cache Locking
+ =================
+
+ All key cache locking is done with a single mutex per key cache:
+ keycache->cache_lock. This mutex is locked almost all the time
+ when executing code in this file (mf_keycache.c).
+ However it is released for I/O and some copy operations.
+
+ The cache_lock is also released when waiting for some event. Waiting
+ and signalling is done via condition variables. In most cases the
+ thread waits on its thread->suspend condition variable. Every thread
+ has a my_thread_var structure, which contains this variable and a
+ '*next' and '**prev' pointer. These pointers are used to insert the
+ thread into a wait queue.
+
+ A thread can wait for one block and thus be in one wait queue at a
+ time only.
+
+ Before starting to wait on its condition variable with
+ mysql_cond_wait(), the thread enters itself to a specific wait queue
+ with link_into_queue() (double linked with '*next' + '**prev') or
+ wait_on_queue() (single linked with '*next').
+
+ Another thread, when releasing a resource, looks up the waiting thread
+ in the related wait queue. It sends a signal with
+ mysql_cond_signal() to the waiting thread.
+
+ NOTE: Depending on the particular wait situation, either the sending
+ thread removes the waiting thread from the wait queue with
+ unlink_from_queue() or release_whole_queue() respectively, or the waiting
+ thread removes itself.
+
+ There is one exception from this locking scheme when one thread wants
+ to reuse a block for some other address. This works by first marking
+ the block reserved (status= BLOCK_IN_SWITCH) and then waiting for all
+ threads that are reading the block to finish. Each block has a
+ reference to a condition variable (condvar). It holds a reference to
+ the thread->suspend condition variable for the waiting thread (if such
+ a thread exists). When that thread is signaled, the reference is
+ cleared. The number of readers of a block is registered in
+ block->hash_link->requests. See wait_for_readers() / remove_reader()
+ for details. This is similar to the above, but it clearly means that
+ only one thread can wait for a particular block. There is no queue in
+ this case. Strangely enough block->convar is used for waiting for the
+ assigned hash_link only. More precisely it is used to wait for all
+ requests to be unregistered from the assigned hash_link.
+
+ The resize_queue serves two purposes:
+ 1. Threads that want to do a resize wait there if in_resize is set.
+ This is not used in the server. The server refuses a second resize
+ request if one is already active. keycache->in_init is used for the
+ synchronization. See set_var.cc.
+ 2. Threads that want to access blocks during resize wait here during
+ the re-initialization phase.
+ When the resize is done, all threads on the queue are signalled.
+ Hypothetical resizers can compete for resizing, and read/write
+ requests will restart to request blocks from the freshly resized
+ cache. If the cache has been resized too small, it is disabled and
+ 'can_be_used' is false. In this case read/write requests bypass the
+ cache. Since they increment and decrement 'cnt_for_resize_op', the
+ next resizer can wait on the queue 'waiting_for_resize_cnt' until all
+ I/O finished.
+*/
+
+#include "mysys_priv.h"
+#include "mysys_err.h"
+#include <keycache.h>
+#include "my_static.h"
+#include <m_string.h>
+#include <my_bit.h>
+#include <errno.h>
+#include <stdarg.h>
+#include "probes_mysql.h"
+#include "my_thread_local.h"
+
+#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 mysql_cond_t KEYCACHE_CONDVAR;
+
+/* descriptor of the page in the key cache block buffer */
+struct st_keycache_page
+{
+ int file; /* file to which the page belongs to */
+ my_off_t filepos; /* position of the page in the file */
+};
+typedef struct st_keycache_page KEYCACHE_PAGE;
+
+/* element in the chain of a hash table bucket */
+struct st_hash_link
+{
+ struct st_hash_link *next, **prev; /* to connect links in the same bucket */
+ struct st_block_link *block; /* reference to the block for the page: */
+ File file; /* from such a file */
+ my_off_t diskpos; /* with such an offset */
+ uint requests; /* number of requests for the page */
+};
+
+/* simple states of a block */
+#define BLOCK_ERROR 1 /* an error occured when performing file i/o */
+#define BLOCK_READ 2 /* file block is in the block buffer */
+#define BLOCK_IN_SWITCH 4 /* block is preparing to read new page */
+#define BLOCK_REASSIGNED 8 /* blk does not accept requests for old page */
+#define BLOCK_IN_FLUSH 16 /* block is selected for flush */
+#define BLOCK_CHANGED 32 /* block buffer contains a dirty page */
+#define BLOCK_IN_USE 64 /* block is not free */
+#define BLOCK_IN_EVICTION 128 /* block is selected for eviction */
+#define BLOCK_IN_FLUSHWRITE 256 /* block is in write to file */
+#define BLOCK_FOR_UPDATE 512 /* block is selected for buffer modification */
+
+/* page status, returned by find_key_block */
+#define PAGE_READ 0
+#define PAGE_TO_BE_READ 1
+#define PAGE_WAIT_TO_BE_READ 2
+
+/* block temperature determines in which (sub-)chain the block currently is */
+enum BLOCK_TEMPERATURE { BLOCK_COLD /*free*/ , BLOCK_WARM , BLOCK_HOT };
+
+/* key cache block */
+struct st_block_link
+{
+ struct st_block_link
+ *next_used, **prev_used; /* to connect links in the LRU chain (ring) */
+ struct st_block_link
+ *next_changed, **prev_changed; /* for lists of file dirty/clean blocks */
+ struct st_hash_link *hash_link; /* backward ptr to referring hash_link */
+ KEYCACHE_WQUEUE wqueue[2]; /* queues on waiting requests for new/old pages */
+ uint requests; /* number of requests for the block */
+ uchar *buffer; /* buffer for the block page */
+ uint offset; /* beginning of modified data in the buffer */
+ uint length; /* end of data in the buffer */
+ uint status; /* state of the block */
+ enum BLOCK_TEMPERATURE temperature; /* block temperature: cold, warm, hot */
+ uint hits_left; /* number of hits left until promotion */
+ ulonglong last_hit_time; /* timestamp of the last hit */
+ KEYCACHE_CONDVAR *condvar; /* condition variable for 'no readers' event */
+};
+
+KEY_CACHE dflt_key_cache_var;
+KEY_CACHE *dflt_key_cache= &dflt_key_cache_var;
+
+#define FLUSH_CACHE 2000 /* sort this many blocks at once */
+
+static void change_key_cache_param(KEY_CACHE *keycache,
+ ulonglong division_limit,
+ ulonglong age_threshold);
+static int flush_all_key_blocks(KEY_CACHE *keycache,
+ st_keycache_thread_var *thread_var);
+
+static void wait_on_queue(KEYCACHE_WQUEUE *wqueue,
+ mysql_mutex_t *mutex,
+ st_keycache_thread_var *thread);
+static void release_whole_queue(KEYCACHE_WQUEUE *wqueue);
+
+static void free_block(KEY_CACHE *keycache,
+ st_keycache_thread_var *thread_var,
+ BLOCK_LINK *block);
+
+#define KEYCACHE_HASH(f, pos) \
+(((ulong) ((pos) / keycache->key_cache_block_size) + \
+ (ulong) (f)) & (keycache->hash_entries-1))
+#define FILE_HASH(f) ((uint) (f) & (CHANGED_BLOCKS_HASH-1))
+
+#define BLOCK_NUMBER(b) \
+ ((uint) (((char*)(b)-(char *) keycache->block_root)/sizeof(BLOCK_LINK)))
+#define HASH_LINK_NUMBER(h) \
+ ((uint) (((char*)(h)-(char *) keycache->hash_link_root)/sizeof(HASH_LINK)))
+
+#if !defined(DBUG_OFF)
+static int fail_block(BLOCK_LINK *block);
+static int fail_hlink(HASH_LINK *hlink);
+static int cache_empty(KEY_CACHE *keycache);
+#endif
+
+static inline uint next_power(uint value)
+{
+ return (uint) my_round_up_to_next_power((uint32) value) << 1;
+}
+
+
+/*
+ Initialize a key cache
+
+ SYNOPSIS
+ init_key_cache()
+ keycache pointer to a key cache data structure
+ key_cache_block_size size of blocks to keep cached data
+ use_mem total memory to use for the key cache
+ division_limit division limit (may be zero)
+ age_threshold age threshold (may be zero)
+
+ RETURN VALUE
+ number of blocks in the key cache, if successful,
+ 0 - otherwise.
+
+ NOTES.
+ if keycache->key_cache_inited != 0 we assume that the key cache
+ is already initialized. This is for now used by myisamchk, but shouldn't
+ be something that a program should rely on!
+
+ It's assumed that no two threads call this function simultaneously
+ referring to the same key cache handle.
+
+*/
+
+int init_key_cache(KEY_CACHE *keycache, ulonglong key_cache_block_size,
+ size_t use_mem, ulonglong division_limit,
+ ulonglong age_threshold)
+{
+ ulong blocks, hash_links;
+ size_t length;
+ int error;
+ DBUG_ENTER("init_key_cache");
+ DBUG_ASSERT(key_cache_block_size >= 512);
+
+ if (keycache->key_cache_inited && keycache->disk_blocks > 0)
+ {
+ DBUG_PRINT("warning",("key cache already in use"));
+ DBUG_RETURN(0);
+ }
+
+ keycache->global_cache_w_requests= keycache->global_cache_r_requests= 0;
+ keycache->global_cache_read= keycache->global_cache_write= 0;
+ keycache->disk_blocks= -1;
+ if (! keycache->key_cache_inited)
+ {
+ keycache->key_cache_inited= 1;
+ /*
+ Initialize these variables once only.
+ Their value must survive re-initialization during resizing.
+ */
+ keycache->in_resize= 0;
+ keycache->resize_in_flush= 0;
+ keycache->cnt_for_resize_op= 0;
+ keycache->waiting_for_resize_cnt.last_thread= NULL;
+ keycache->in_init= 0;
+ mysql_mutex_init(key_KEY_CACHE_cache_lock,
+ &keycache->cache_lock, MY_MUTEX_INIT_FAST);
+ keycache->resize_queue.last_thread= NULL;
+ }
+
+ keycache->key_cache_mem_size= use_mem;
+ keycache->key_cache_block_size= (uint)key_cache_block_size;
+ DBUG_PRINT("info", ("key_cache_block_size: %llu",
+ key_cache_block_size));
+
+ blocks= (ulong) (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)
+ {
+ for ( ; ; )
+ {
+ /* Set my_hash_entries to the next bigger 2 power */
+ if ((keycache->hash_entries= next_power(blocks)) < blocks * 5/4)
+ keycache->hash_entries<<= 1;
+ hash_links= 2 * blocks;
+ while ((length= (ALIGN_SIZE(blocks * sizeof(BLOCK_LINK)) +
+ ALIGN_SIZE(hash_links * sizeof(HASH_LINK)) +
+ ALIGN_SIZE(sizeof(HASH_LINK*) *
+ keycache->hash_entries))) +
+ ((size_t) blocks * keycache->key_cache_block_size) > use_mem)
+ blocks--;
+ /* Allocate memory for cache page buffers */
+ if ((keycache->block_mem=
+ my_large_malloc(key_memory_KEY_CACHE,
+ (size_t) blocks * keycache->key_cache_block_size,
+ MYF(0))))
+ {
+ /*
+ Allocate memory for blocks, hash_links and hash entries;
+ For each block 2 hash links are allocated
+ */
+ if ((keycache->block_root= (BLOCK_LINK*) my_malloc(key_memory_KEY_CACHE,
+ length,
+ MYF(0))))
+ break;
+ my_large_free(keycache->block_mem);
+ keycache->block_mem= 0;
+ }
+ if (blocks < 8)
+ {
+ set_my_errno(ENOMEM);
+ my_error(EE_OUTOFMEMORY, MYF(ME_FATALERROR),
+ blocks * keycache->key_cache_block_size);
+ goto err;
+ }
+ blocks= blocks / 4*3;
+ }
+ keycache->blocks_unused= blocks;
+ keycache->disk_blocks= (int) blocks;
+ keycache->hash_links= hash_links;
+ keycache->hash_root= (HASH_LINK**) ((char*) keycache->block_root +
+ ALIGN_SIZE(blocks*sizeof(BLOCK_LINK)));
+ keycache->hash_link_root= (HASH_LINK*) ((char*) keycache->hash_root +
+ ALIGN_SIZE((sizeof(HASH_LINK*) *
+ keycache->hash_entries)));
+ memset(keycache->block_root, 0,
+ keycache->disk_blocks * sizeof(BLOCK_LINK));
+ memset(keycache->hash_root, 0,
+ keycache->hash_entries * sizeof(HASH_LINK*));
+ memset(keycache->hash_link_root, 0,
+ keycache->hash_links * sizeof(HASH_LINK));
+ keycache->hash_links_used= 0;
+ keycache->free_hash_list= NULL;
+ keycache->blocks_used= keycache->blocks_changed= 0;
+
+ keycache->global_blocks_changed= 0;
+ keycache->blocks_available=0; /* For debugging */
+
+ /* The LRU chain is empty after initialization */
+ keycache->used_last= NULL;
+ keycache->used_ins= NULL;
+ keycache->free_block_list= NULL;
+ keycache->keycache_time= 0;
+ keycache->warm_blocks= 0;
+ keycache->min_warm_blocks= (division_limit ?
+ blocks * division_limit / 100 + 1 :
+ blocks);
+ keycache->age_threshold= (age_threshold ?
+ blocks * age_threshold / 100 :
+ blocks);
+
+ keycache->can_be_used= 1;
+
+ keycache->waiting_for_hash_link.last_thread= NULL;
+ keycache->waiting_for_block.last_thread= NULL;
+ DBUG_PRINT("exit",
+ ("disk_blocks: %d block_root: 0x%lx hash_entries: %d\
+ hash_root: 0x%lx hash_links: %d hash_link_root: 0x%lx",
+ keycache->disk_blocks, (long) keycache->block_root,
+ keycache->hash_entries, (long) keycache->hash_root,
+ keycache->hash_links, (long) keycache->hash_link_root));
+ memset(keycache->changed_blocks, 0,
+ sizeof(keycache->changed_blocks[0]) * CHANGED_BLOCKS_HASH);
+ memset(keycache->file_blocks, 0,
+ sizeof(keycache->file_blocks[0]) * CHANGED_BLOCKS_HASH);
+ }
+ else
+ {
+ /* key_buffer_size is specified too small. Disable the cache. */
+ keycache->can_be_used= 0;
+ }
+
+ keycache->blocks= keycache->disk_blocks > 0 ? keycache->disk_blocks : 0;
+ DBUG_RETURN((int) keycache->disk_blocks);
+
+err:
+ error= my_errno();
+ keycache->disk_blocks= 0;
+ keycache->blocks= 0;
+ if (keycache->block_mem)
+ {
+ my_large_free((uchar*) keycache->block_mem);
+ keycache->block_mem= NULL;
+ }
+ if (keycache->block_root)
+ {
+ my_free(keycache->block_root);
+ keycache->block_root= NULL;
+ }
+ set_my_errno(error);
+ keycache->can_be_used= 0;
+ DBUG_RETURN(0);
+}
+
+
+/*
+ Resize a key cache
+
+ SYNOPSIS
+ resize_key_cache()
+ keycache pointer to a key cache data structure
+ thread_var pointer to thread specific variables
+ key_cache_block_size size of blocks to keep cached data
+ use_mem total memory to use for the new key cache
+ division_limit new division limit (if not zero)
+ age_threshold new age threshold (if not zero)
+
+ RETURN VALUE
+ number of blocks in the key cache, if successful,
+ 0 - otherwise.
+
+ NOTES.
+ The function first compares the memory size and the block size parameters
+ with the key cache values.
+
+ If they differ the function free the the memory allocated for the
+ old key cache blocks by calling the end_key_cache function and
+ then rebuilds the key cache with new blocks by calling
+ init_key_cache.
+
+ The function starts the operation only when all other threads
+ performing operations with the key cache let her to proceed
+ (when cnt_for_resize=0).
+*/
+
+int resize_key_cache(KEY_CACHE *keycache,
+ st_keycache_thread_var *thread_var,
+ ulonglong key_cache_block_size,
+ size_t use_mem, ulonglong division_limit,
+ ulonglong age_threshold)
+{
+ int blocks;
+ DBUG_ENTER("resize_key_cache");
+
+ if (!keycache->key_cache_inited)
+ DBUG_RETURN(keycache->disk_blocks);
+
+ if(key_cache_block_size == keycache->key_cache_block_size &&
+ use_mem == keycache->key_cache_mem_size)
+ {
+ change_key_cache_param(keycache, division_limit, age_threshold);
+ DBUG_RETURN(keycache->disk_blocks);
+ }
+
+ mysql_mutex_lock(&keycache->cache_lock);
+
+ /*
+ We may need to wait for another thread which is doing a resize
+ already. This cannot happen in the MySQL server though. It allows
+ one resizer only. In set_var.cc keycache->in_init is used to block
+ multiple attempts.
+ */
+ while (keycache->in_resize)
+ {
+ /* purecov: begin inspected */
+ wait_on_queue(&keycache->resize_queue, &keycache->cache_lock,
+ thread_var);
+ /* purecov: end */
+ }
+
+ /*
+ Mark the operation in progress. This blocks other threads from doing
+ a resize in parallel. It prohibits new blocks to enter the cache.
+ Read/write requests can bypass the cache during the flush phase.
+ */
+ keycache->in_resize= 1;
+
+ /* Need to flush only if keycache is enabled. */
+ if (keycache->can_be_used)
+ {
+ /* Start the flush phase. */
+ keycache->resize_in_flush= 1;
+
+ if (flush_all_key_blocks(keycache, thread_var))
+ {
+ /* TODO: if this happens, we should write a warning in the log file ! */
+ keycache->resize_in_flush= 0;
+ blocks= 0;
+ keycache->can_be_used= 0;
+ goto finish;
+ }
+ DBUG_ASSERT(cache_empty(keycache));
+
+ /* End the flush phase. */
+ keycache->resize_in_flush= 0;
+ }
+
+ /*
+ Some direct read/write operations (bypassing the cache) may still be
+ unfinished. Wait until they are done. If the key cache can be used,
+ direct I/O is done in increments of key_cache_block_size. That is,
+ every block is checked if it is in the cache. We need to wait for
+ pending I/O before re-initializing the cache, because we may change
+ the block size. Otherwise they could check for blocks at file
+ positions where the new block division has none. We do also want to
+ wait for I/O done when (if) the cache was disabled. It must not
+ run in parallel with normal cache operation.
+ */
+ while (keycache->cnt_for_resize_op)
+ wait_on_queue(&keycache->waiting_for_resize_cnt, &keycache->cache_lock,
+ thread_var);
+
+ /*
+ Free old cache structures, allocate new structures, and initialize
+ them. Note that the cache_lock mutex and the resize_queue are left
+ untouched. We do not lose the cache_lock and will release it only at
+ the end of this function.
+ */
+ end_key_cache(keycache, 0); /* Don't free mutex */
+ /* The following will work even if use_mem is 0 */
+ blocks= init_key_cache(keycache, key_cache_block_size, use_mem,
+ division_limit, age_threshold);
+
+finish:
+ /*
+ Mark the resize finished. This allows other threads to start a
+ resize or to request new cache blocks.
+ */
+ keycache->in_resize= 0;
+
+ /* Signal waiting threads. */
+ release_whole_queue(&keycache->resize_queue);
+
+ mysql_mutex_unlock(&keycache->cache_lock);
+ DBUG_RETURN(blocks);
+}
+
+
+/*
+ Increment counter blocking resize key cache operation
+*/
+static inline void inc_counter_for_resize_op(KEY_CACHE *keycache)
+{
+ keycache->cnt_for_resize_op++;
+}
+
+
+/*
+ Decrement counter blocking resize key cache operation;
+ Signal the operation to proceed when counter becomes equal zero
+*/
+static inline void dec_counter_for_resize_op(KEY_CACHE *keycache)
+{
+ if (!--keycache->cnt_for_resize_op)
+ release_whole_queue(&keycache->waiting_for_resize_cnt);
+}
+
+/*
+ Change the key cache parameters
+
+ SYNOPSIS
+ change_key_cache_param()
+ keycache pointer to a key cache data structure
+ division_limit new division limit (if not zero)
+ age_threshold new age threshold (if not zero)
+
+ RETURN VALUE
+ none
+
+ NOTES.
+ Presently the function resets the key cache parameters
+ concerning midpoint insertion strategy - division_limit and
+ age_threshold.
+*/
+
+static void change_key_cache_param(KEY_CACHE *keycache,
+ ulonglong division_limit,
+ ulonglong age_threshold)
+{
+ DBUG_ENTER("change_key_cache_param");
+
+ mysql_mutex_lock(&keycache->cache_lock);
+ if (division_limit)
+ keycache->min_warm_blocks= (keycache->disk_blocks *
+ division_limit / 100 + 1);
+ if (age_threshold)
+ keycache->age_threshold= (keycache->disk_blocks *
+ age_threshold / 100);
+ mysql_mutex_unlock(&keycache->cache_lock);
+ DBUG_VOID_RETURN;
+}
+
+
+/*
+ Remove key_cache from memory
+
+ SYNOPSIS
+ end_key_cache()
+ keycache key cache handle
+ cleanup Complete free (Free also mutex for key cache)
+
+ RETURN VALUE
+ none
+*/
+
+void end_key_cache(KEY_CACHE *keycache, my_bool cleanup)
+{
+ DBUG_ENTER("end_key_cache");
+ DBUG_PRINT("enter", ("key_cache: 0x%lx", (long) keycache));
+
+ if (!keycache->key_cache_inited)
+ DBUG_VOID_RETURN;
+
+ if (keycache->disk_blocks > 0)
+ {
+ if (keycache->block_mem)
+ {
+ my_large_free((uchar*) keycache->block_mem);
+ keycache->block_mem= NULL;
+ my_free(keycache->block_root);
+ keycache->block_root= NULL;
+ }
+ keycache->disk_blocks= -1;
+ /* Reset blocks_changed to be safe if flush_all_key_blocks is called */
+ keycache->blocks_changed= 0;
+ }
+
+ DBUG_PRINT("status", ("used: %lu changed: %lu w_requests: %lu "
+ "writes: %lu r_requests: %lu reads: %lu",
+ keycache->blocks_used, keycache->global_blocks_changed,
+ (ulong) keycache->global_cache_w_requests,
+ (ulong) keycache->global_cache_write,
+ (ulong) keycache->global_cache_r_requests,
+ (ulong) keycache->global_cache_read));
+
+ /*
+ Reset these values to be able to detect a disabled key cache.
+ See Bug#44068 (RESTORE can disable the MyISAM Key Cache).
+ */
+ keycache->blocks_used= 0;
+ keycache->blocks_unused= 0;
+
+ if (cleanup)
+ {
+ mysql_mutex_destroy(&keycache->cache_lock);
+ keycache->key_cache_inited= keycache->can_be_used= 0;
+ }
+ DBUG_VOID_RETURN;
+} /* end_key_cache */
+
+
+/**
+ Link a thread into double-linked queue of waiting threads.
+
+ @param wqueue pointer to the queue structure
+ @param thread pointer to the keycache variables for the
+ thread to be added to the queue
+
+ Queue is represented by a circular list of the keycache variable structures.
+ Since each thread has its own keycache variables, this is equal to a list
+ of threads. The list is double-linked of the type (**prev,*next), accessed by
+ a pointer to the last element.
+*/
+
+static void link_into_queue(KEYCACHE_WQUEUE *wqueue,
+ st_keycache_thread_var *thread)
+{
+ st_keycache_thread_var *last;
+
+ DBUG_ASSERT(!thread->next && !thread->prev);
+ 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
+
+ @param wqueue pointer to the queue structure
+ @param thread pointer to the keycache variables for the
+ thread to be removed to the queue
+
+ @note See link_into_queue
+*/
+
+static void unlink_from_queue(KEYCACHE_WQUEUE *wqueue,
+ st_keycache_thread_var *thread)
+{
+ DBUG_ASSERT(thread->next && thread->prev);
+ 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(st_keycache_thread_var, next,
+ thread->prev);
+ }
+ thread->next= NULL;
+#if !defined(DBUG_OFF)
+ /*
+ This makes it easier to see it's not in a chain during debugging.
+ And some DBUG_ASSERT() rely on it.
+ */
+ thread->prev= NULL;
+#endif
+}
+
+
+/*
+ Add a thread to single-linked queue of waiting threads
+
+ SYNOPSIS
+ wait_on_queue()
+ wqueue Pointer to the queue structure.
+ mutex Cache_lock to acquire after awake.
+ thread Thread to be added
+
+ RETURN VALUE
+ none
+
+ NOTES.
+ Queue is represented by a circular list of the thread structures
+ The list is single-linked of the type (*next), accessed by a pointer
+ to the last element.
+
+ The function protects against stray signals by verifying that the
+ current thread is unlinked from the queue when awaking. However,
+ since several threads can wait for the same event, it might be
+ necessary for the caller of the function to check again if the
+ condition for awake is indeed matched.
+*/
+
+static void wait_on_queue(KEYCACHE_WQUEUE *wqueue,
+ mysql_mutex_t *mutex,
+ st_keycache_thread_var *thread)
+{
+ st_keycache_thread_var *last;
+
+ /* Add to queue. */
+ DBUG_ASSERT(!thread->next);
+ DBUG_ASSERT(!thread->prev); /* Not required, but must be true anyway. */
+ if (! (last= wqueue->last_thread))
+ thread->next= thread;
+ else
+ {
+ thread->next= last->next;
+ last->next= thread;
+ }
+ wqueue->last_thread= thread;
+
+ /*
+ Wait until thread is removed from queue by the signalling thread.
+ The loop protects against stray signals.
+ */
+ do
+ {
+ mysql_cond_wait(&thread->suspend, mutex);
+ }
+ while (thread->next);
+}
+
+
+/*
+ Remove all threads from queue signaling them to proceed
+
+ SYNOPSIS
+ release_whole_queue()
+ wqueue pointer to the queue structure
+
+ RETURN VALUE
+ none
+
+ NOTES.
+ See notes for wait_on_queue().
+ When removed from the queue each thread is signaled via condition
+ variable thread->suspend.
+*/
+
+static void release_whole_queue(KEYCACHE_WQUEUE *wqueue)
+{
+ st_keycache_thread_var *last;
+ st_keycache_thread_var *next;
+ st_keycache_thread_var *thread;
+
+ /* Queue may be empty. */
+ if (!(last= wqueue->last_thread))
+ return;
+
+ next= last->next;
+ do
+ {
+ thread=next;
+ /* Signal the thread. */
+ mysql_cond_signal(&thread->suspend);
+ /* Take thread from queue. */
+ next=thread->next;
+ thread->next= NULL;
+ }
+ while (thread != last);
+
+ /* Now queue is definitely empty. */
+ wqueue->last_thread= NULL;
+}
+
+
+/*
+ Unlink a block from the chain of dirty/clean blocks
+*/
+
+static inline void unlink_changed(BLOCK_LINK *block)
+{
+ DBUG_ASSERT(block->prev_changed && *block->prev_changed == block);
+ if (block->next_changed)
+ block->next_changed->prev_changed= block->prev_changed;
+ *block->prev_changed= block->next_changed;
+
+#if !defined(DBUG_OFF)
+ /*
+ This makes it easier to see it's not in a chain during debugging.
+ And some DBUG_ASSERT() rely on it.
+ */
+ block->next_changed= NULL;
+ block->prev_changed= NULL;
+#endif
+}
+
+
+/*
+ Link a block into the chain of dirty/clean blocks
+*/
+
+static inline void link_changed(BLOCK_LINK *block, BLOCK_LINK **phead)
+{
+ DBUG_ASSERT(!block->next_changed);
+ DBUG_ASSERT(!block->prev_changed);
+ block->prev_changed= phead;
+ if ((block->next_changed= *phead))
+ (*phead)->prev_changed= &block->next_changed;
+ *phead= block;
+}
+
+
+/*
+ Link a block in a chain of clean blocks of a file.
+
+ SYNOPSIS
+ link_to_file_list()
+ keycache Key cache handle
+ block Block to relink
+ file File to be linked to
+ unlink If to unlink first
+
+ DESCRIPTION
+ Unlink a block from whichever chain it is linked in, if it's
+ asked for, and link it to the chain of clean blocks of the
+ specified file.
+
+ NOTE
+ Please do never set/clear BLOCK_CHANGED outside of
+ link_to_file_list() or link_to_changed_list().
+ You would risk to damage correct counting of changed blocks
+ and to find blocks in the wrong hash.
+
+ RETURN
+ void
+*/
+
+static void link_to_file_list(KEY_CACHE *keycache,
+ BLOCK_LINK *block, int file,
+ my_bool unlink_block)
+{
+ DBUG_ASSERT(block->status & BLOCK_IN_USE);
+ DBUG_ASSERT(block->hash_link && block->hash_link->block == block);
+ DBUG_ASSERT(block->hash_link->file == file);
+ if (unlink_block)
+ unlink_changed(block);
+ link_changed(block, &keycache->file_blocks[FILE_HASH(file)]);
+ if (block->status & BLOCK_CHANGED)
+ {
+ block->status&= ~BLOCK_CHANGED;
+ keycache->blocks_changed--;
+ keycache->global_blocks_changed--;
+ }
+}
+
+
+/*
+ Re-link a block from the clean chain to the dirty chain of a file.
+
+ SYNOPSIS
+ link_to_changed_list()
+ keycache key cache handle
+ block block to relink
+
+ DESCRIPTION
+ Unlink a block from the chain of clean blocks of a file
+ and link it to the chain of dirty blocks of the same file.
+
+ NOTE
+ Please do never set/clear BLOCK_CHANGED outside of
+ link_to_file_list() or link_to_changed_list().
+ You would risk to damage correct counting of changed blocks
+ and to find blocks in the wrong hash.
+
+ RETURN
+ void
+*/
+
+static void link_to_changed_list(KEY_CACHE *keycache,
+ BLOCK_LINK *block)
+{
+ DBUG_ASSERT(block->status & BLOCK_IN_USE);
+ DBUG_ASSERT(!(block->status & BLOCK_CHANGED));
+ DBUG_ASSERT(block->hash_link && block->hash_link->block == block);
+
+ unlink_changed(block);
+ link_changed(block,
+ &keycache->changed_blocks[FILE_HASH(block->hash_link->file)]);
+ block->status|=BLOCK_CHANGED;
+ keycache->blocks_changed++;
+ keycache->global_blocks_changed++;
+}
+
+
+/*
+ Link a block to the LRU chain at the beginning or at the end of
+ one of two parts.
+
+ SYNOPSIS
+ link_block()
+ keycache pointer to a key cache data structure
+ block pointer to the block to link to the LRU chain
+ hot <-> to link the block into the hot subchain
+ at_end <-> to link the block at the end of the subchain
+
+ RETURN VALUE
+ none
+
+ NOTES.
+ The LRU ring is represented by a circular list of block structures.
+ The list is double-linked of the type (**prev,*next) type.
+ The LRU ring is divided into two parts - hot and warm.
+ There are two pointers to access the last blocks of these two
+ parts. The beginning of the warm part follows right after the
+ end of the hot part.
+ Only blocks of the warm part can be used for eviction.
+ The first block from the beginning of this subchain is always
+ taken for eviction (keycache->last_used->next)
+
+ LRU chain: +------+ H O T +------+
+ +----| end |----...<----| beg |----+
+ | +------+last +------+ |
+ v<-link in latest hot (new end) |
+ | link in latest warm (new end)->^
+ | +------+ W A R M +------+ |
+ +----| beg |---->...----| end |----+
+ +------+ +------+ins
+ first for eviction
+
+ It is also possible that the block is selected for eviction and thus
+ not linked in the LRU ring.
+*/
+
+static void link_block(KEY_CACHE *keycache, BLOCK_LINK *block, my_bool hot,
+ my_bool at_end)
+{
+ BLOCK_LINK *ins;
+ BLOCK_LINK **pins;
+
+ DBUG_ASSERT((block->status & ~BLOCK_CHANGED) == (BLOCK_READ | BLOCK_IN_USE));
+ DBUG_ASSERT(block->hash_link); /*backptr to block NULL from free_block()*/
+ DBUG_ASSERT(!block->requests);
+ DBUG_ASSERT(block->prev_changed && *block->prev_changed == block);
+ DBUG_ASSERT(!block->next_used);
+ DBUG_ASSERT(!block->prev_used);
+
+ if (!hot && keycache->waiting_for_block.last_thread)
+ {
+ /* Signal that in the LRU warm sub-chain an available block has appeared */
+ st_keycache_thread_var *last_thread=
+ keycache->waiting_for_block.last_thread;
+ st_keycache_thread_var *first_thread= last_thread->next;
+ st_keycache_thread_var *next_thread= first_thread;
+ HASH_LINK *hash_link= (HASH_LINK *) first_thread->opt_info;
+ st_keycache_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)
+ {
+ mysql_cond_signal(&thread->suspend);
+ unlink_from_queue(&keycache->waiting_for_block, thread);
+ block->requests++;
+ }
+ }
+ while (thread != last_thread);
+ hash_link->block= block;
+ /*
+ NOTE: We assigned the block to the hash_link and signalled the
+ requesting thread(s). But it is possible that other threads runs
+ first. These threads see the hash_link assigned to a block which
+ is assigned to another hash_link and not marked BLOCK_IN_SWITCH.
+ This can be a problem for functions that do not select the block
+ via its hash_link: flush and free. They do only see a block which
+ is in a "normal" state and don't know that it will be evicted soon.
+
+ We cannot set BLOCK_IN_SWITCH here because only one of the
+ requesting threads must handle the eviction. All others must wait
+ for it to complete. If we set the flag here, the threads would not
+ know who is in charge of the eviction. Without the flag, the first
+ thread takes the stick and sets the flag.
+
+ But we need to note in the block that is has been selected for
+ eviction. It must not be freed. The evicting thread will not
+ expect the block in the free list. Before freeing we could also
+ check if block->requests > 1. But I think including another flag
+ in the check of block->status is slightly more efficient and
+ probably easier to read.
+ */
+ block->status|= BLOCK_IN_EVICTION;
+ return;
+ }
+
+ pins= hot ? &keycache->used_ins : &keycache->used_last;
+ ins= *pins;
+ if (ins)
+ {
+ ins->next_used->prev_used= &block->next_used;
+ block->next_used= ins->next_used;
+ block->prev_used= &ins->next_used;
+ ins->next_used= block;
+ if (at_end)
+ *pins= block;
+ }
+ else
+ {
+ /* The LRU ring is empty. Let the block point to itself. */
+ keycache->used_last= keycache->used_ins= block->next_used= block;
+ block->prev_used= &block->next_used;
+ }
+ DBUG_ASSERT((ulong) keycache->blocks_available <=
+ keycache->blocks_used);
+}
+
+
+/*
+ Unlink a block from the LRU chain
+
+ SYNOPSIS
+ unlink_block()
+ keycache pointer to a key cache data structure
+ block pointer to the block to unlink from the LRU chain
+
+ RETURN VALUE
+ none
+
+ NOTES.
+ See NOTES for link_block
+*/
+
+static void unlink_block(KEY_CACHE *keycache, BLOCK_LINK *block)
+{
+ DBUG_ASSERT((block->status & ~BLOCK_CHANGED) == (BLOCK_READ | BLOCK_IN_USE));
+ DBUG_ASSERT(block->hash_link); /*backptr to block NULL from free_block()*/
+ DBUG_ASSERT(!block->requests);
+ DBUG_ASSERT(block->prev_changed && *block->prev_changed == block);
+ DBUG_ASSERT(block->next_used && block->prev_used &&
+ (block->next_used->prev_used == &block->next_used) &&
+ (*block->prev_used == block));
+ if (block->next_used == block)
+ /* The list contains only one member */
+ keycache->used_last= keycache->used_ins= NULL;
+ else
+ {
+ block->next_used->prev_used= block->prev_used;
+ *block->prev_used= block->next_used;
+ if (keycache->used_last == block)
+ keycache->used_last= STRUCT_PTR(BLOCK_LINK, next_used, block->prev_used);
+ if (keycache->used_ins == block)
+ keycache->used_ins=STRUCT_PTR(BLOCK_LINK, next_used, block->prev_used);
+ }
+ block->next_used= NULL;
+#if !defined(DBUG_OFF)
+ /*
+ This makes it easier to see it's not in a chain during debugging.
+ And some DBUG_ASSERT() rely on it.
+ */
+ block->prev_used= NULL;
+#endif
+}
+
+
+/*
+ Register requests for a block.
+
+ SYNOPSIS
+ reg_requests()
+ keycache Pointer to a key cache data structure.
+ block Pointer to the block to register a request on.
+ count Number of requests. Always 1.
+
+ NOTE
+ The first request unlinks the block from the LRU ring. This means
+ that it is protected against eveiction.
+
+ RETURN
+ void
+*/
+static void reg_requests(KEY_CACHE *keycache, BLOCK_LINK *block, int count)
+{
+ DBUG_ASSERT(block->status & BLOCK_IN_USE);
+ DBUG_ASSERT(block->hash_link);
+
+ if (!block->requests)
+ unlink_block(keycache, block);
+ block->requests+=count;
+}
+
+
+/*
+ Unregister request for a block
+ linking it to the LRU chain if it's the last request
+
+ SYNOPSIS
+ unreg_request()
+ keycache pointer to a key cache data structure
+ block pointer to the block to link to the LRU chain
+ at_end <-> to link the block at the end of the LRU chain
+
+ RETURN VALUE
+ none
+
+ NOTES.
+ Every linking to the LRU ring decrements by one a special block
+ counter (if it's positive). If the at_end parameter is TRUE the block is
+ added either at the end of warm sub-chain or at the end of hot sub-chain.
+ It is added to the hot subchain if its counter is zero and number of
+ blocks in warm sub-chain is not less than some low limit (determined by
+ the division_limit parameter). Otherwise the block is added to the warm
+ sub-chain. If the at_end parameter is FALSE the block is always added
+ at beginning of the warm sub-chain.
+ Thus a warm block can be promoted to the hot sub-chain when its counter
+ becomes zero for the first time.
+ At the same time the block at the very beginning of the hot subchain
+ might be moved to the beginning of the warm subchain if it stays untouched
+ for a too long time (this time is determined by parameter age_threshold).
+
+ It is also possible that the block is selected for eviction and thus
+ not linked in the LRU ring.
+*/
+
+static void unreg_request(KEY_CACHE *keycache,
+ BLOCK_LINK *block, int at_end)
+{
+ DBUG_ASSERT(block->status & (BLOCK_READ | BLOCK_IN_USE));
+ DBUG_ASSERT(block->hash_link); /*backptr to block NULL from free_block()*/
+ DBUG_ASSERT(block->requests);
+ DBUG_ASSERT(block->prev_changed && *block->prev_changed == block);
+ DBUG_ASSERT(!block->next_used);
+ DBUG_ASSERT(!block->prev_used);
+ /*
+ Unregister the request, but do not link erroneous blocks into the
+ LRU ring.
+ */
+ if (!--block->requests && !(block->status & BLOCK_ERROR))
+ {
+ my_bool hot;
+ if (block->hits_left)
+ block->hits_left--;
+ hot= !block->hits_left && at_end &&
+ keycache->warm_blocks > keycache->min_warm_blocks;
+ if (hot)
+ {
+ if (block->temperature == BLOCK_WARM)
+ keycache->warm_blocks--;
+ block->temperature= BLOCK_HOT;
+ }
+ link_block(keycache, block, hot, (my_bool)at_end);
+ block->last_hit_time= keycache->keycache_time;
+ keycache->keycache_time++;
+ /*
+ At this place, the block might be in the LRU ring or not. If an
+ evicter was waiting for a block, it was selected for eviction and
+ not linked in the LRU ring.
+ */
+
+ /*
+ Check if we should link a hot block to the warm block sub-chain.
+ It is possible that we select the same block as above. But it can
+ also be another block. In any case a block from the LRU ring is
+ selected. In other words it works even if the above block was
+ selected for eviction and not linked in the LRU ring. Since this
+ happens only if the LRU ring is empty, the block selected below
+ would be NULL and the rest of the function skipped.
+ */
+ block= keycache->used_ins;
+ if (block && keycache->keycache_time - block->last_hit_time >
+ keycache->age_threshold)
+ {
+ unlink_block(keycache, block);
+ link_block(keycache, block, 0, 0);
+ if (block->temperature != BLOCK_WARM)
+ {
+ keycache->warm_blocks++;
+ block->temperature= BLOCK_WARM;
+ }
+ }
+ }
+}
+
+/*
+ Remove a reader of the page in block
+*/
+
+static void remove_reader(BLOCK_LINK *block)
+{
+ DBUG_ASSERT(block->status & (BLOCK_READ | BLOCK_IN_USE));
+ DBUG_ASSERT(block->hash_link && block->hash_link->block == block);
+ DBUG_ASSERT(block->prev_changed && *block->prev_changed == block);
+ DBUG_ASSERT(!block->next_used);
+ DBUG_ASSERT(!block->prev_used);
+ DBUG_ASSERT(block->hash_link->requests);
+
+ if (! --block->hash_link->requests && block->condvar)
+ mysql_cond_signal(block->condvar);
+}
+
+
+/*
+ Wait until the last reader of the page in block
+ signals on its termination
+*/
+
+static void wait_for_readers(KEY_CACHE *keycache,
+ BLOCK_LINK *block,
+ st_keycache_thread_var *thread)
+{
+ DBUG_ASSERT(block->status & (BLOCK_READ | BLOCK_IN_USE));
+ DBUG_ASSERT(!(block->status & (BLOCK_IN_FLUSH | BLOCK_CHANGED)));
+ DBUG_ASSERT(block->hash_link);
+ DBUG_ASSERT(block->hash_link->block == block);
+ /* Linked in file_blocks or changed_blocks hash. */
+ DBUG_ASSERT(block->prev_changed && *block->prev_changed == block);
+ /* Not linked in LRU ring. */
+ DBUG_ASSERT(!block->next_used);
+ DBUG_ASSERT(!block->prev_used);
+ while (block->hash_link->requests)
+ {
+ /* There must be no other waiter. We have no queue here. */
+ DBUG_ASSERT(!block->condvar);
+ block->condvar= &thread->suspend;
+ mysql_cond_wait(&thread->suspend, &keycache->cache_lock);
+ block->condvar= NULL;
+ }
+}
+
+
+/*
+ Add a hash link to a bucket in the hash_table
+*/
+
+static inline void link_hash(HASH_LINK **start, HASH_LINK *hash_link)
+{
+ if (*start)
+ (*start)->prev= &hash_link->next;
+ hash_link->next= *start;
+ hash_link->prev= start;
+ *start= hash_link;
+}
+
+
+/*
+ Remove a hash link from the hash table
+*/
+
+static void unlink_hash(KEY_CACHE *keycache, HASH_LINK *hash_link)
+{
+ DBUG_ASSERT(hash_link->requests == 0);
+ if ((*hash_link->prev= hash_link->next))
+ hash_link->next->prev= hash_link->prev;
+ hash_link->block= NULL;
+
+ if (keycache->waiting_for_hash_link.last_thread)
+ {
+ /* Signal that a free hash link has appeared */
+ st_keycache_thread_var *last_thread=
+ keycache->waiting_for_hash_link.last_thread;
+ st_keycache_thread_var *first_thread= last_thread->next;
+ st_keycache_thread_var *next_thread= first_thread;
+ KEYCACHE_PAGE *first_page= (KEYCACHE_PAGE *) (first_thread->opt_info);
+ st_keycache_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)
+ {
+ mysql_cond_signal(&thread->suspend);
+ unlink_from_queue(&keycache->waiting_for_hash_link, thread);
+ }
+ }
+ while (thread != last_thread);
+ link_hash(&keycache->hash_root[KEYCACHE_HASH(hash_link->file,
+ hash_link->diskpos)],
+ hash_link);
+ return;
+ }
+ hash_link->next= keycache->free_hash_list;
+ keycache->free_hash_list= hash_link;
+}
+
+
+/*
+ Get the hash link for a page
+*/
+
+static HASH_LINK *get_hash_link(KEY_CACHE *keycache,
+ int file, my_off_t filepos,
+ st_keycache_thread_var *thread)
+{
+ HASH_LINK *hash_link, **start;
+#ifndef DBUG_OFF
+ int cnt;
+#endif
+
+restart:
+ /*
+ Find the bucket in the hash table for the pair (file, filepos);
+ start contains the head of the bucket list,
+ hash_link points to the first member of the list
+ */
+ hash_link= *(start= &keycache->hash_root[KEYCACHE_HASH(file, filepos)]);
+#ifndef DBUG_OFF
+ 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;
+#ifndef DBUG_OFF
+ cnt++;
+ DBUG_ASSERT(cnt <= keycache->hash_links_used);
+#endif
+ }
+ if (! hash_link)
+ {
+ /* There is no hash link in the hash table for the pair (file, filepos) */
+ if (keycache->free_hash_list)
+ {
+ hash_link= keycache->free_hash_list;
+ keycache->free_hash_list= hash_link->next;
+ }
+ else if (keycache->hash_links_used < keycache->hash_links)
+ {
+ hash_link= &keycache->hash_link_root[keycache->hash_links_used++];
+ }
+ else
+ {
+ /* Wait for a free hash link */
+ KEYCACHE_PAGE page;
+ page.file= file;
+ page.filepos= filepos;
+ thread->opt_info= (void *) &page;
+ link_into_queue(&keycache->waiting_for_hash_link, thread);
+ mysql_cond_wait(&thread->suspend,
+ &keycache->cache_lock);
+ thread->opt_info= NULL;
+ goto restart;
+ }
+ hash_link->file= file;
+ hash_link->diskpos= filepos;
+ link_hash(start, hash_link);
+ }
+ /* Register the request for the page */
+ hash_link->requests++;
+
+ return hash_link;
+}
+
+
+/*
+ Get a block for the file page requested by a keycache read/write operation;
+ If the page is not in the cache return a free block, if there is none
+ return the lru block after saving its buffer if the page is dirty.
+
+ SYNOPSIS
+
+ find_key_block()
+ keycache pointer to a key cache data structure
+ thread pointer to thread specific variables
+ file handler for the file to read page from
+ filepos position of the page in the file
+ init_hits_left how initialize the block counter for the page
+ wrmode <-> get for writing
+ page_st out {PAGE_READ,PAGE_TO_BE_READ,PAGE_WAIT_TO_BE_READ}
+
+ RETURN VALUE
+ Pointer to the found block if successful, 0 - otherwise
+
+ NOTES.
+ For the page from file positioned at filepos the function checks whether
+ the page is in the key cache specified by the first parameter.
+ If this is the case it immediately returns the block.
+ If not, the function first chooses a block for this page. If there is
+ no not used blocks in the key cache yet, the function takes the block
+ at the very beginning of the warm sub-chain. It saves the page in that
+ block if it's dirty before returning the pointer to it.
+ The function returns in the page_st parameter the following values:
+ PAGE_READ - if page already in the block,
+ PAGE_TO_BE_READ - if it is to be read yet by the current thread
+ WAIT_TO_BE_READ - if it is to be read by another thread
+ If an error occurs THE BLOCK_ERROR bit is set in the block status.
+ It might happen that there are no blocks in LRU chain (in warm part) -
+ all blocks are unlinked for some read/write operations. Then the function
+ waits until first of this operations links any block back.
+*/
+
+static BLOCK_LINK *find_key_block(KEY_CACHE *keycache,
+ st_keycache_thread_var *thread,
+ File file, my_off_t filepos,
+ int init_hits_left,
+ int wrmode, int *page_st)
+{
+ HASH_LINK *hash_link;
+ BLOCK_LINK *block;
+ int error= 0;
+ int page_status;
+
+ DBUG_ENTER("find_key_block");
+ DBUG_PRINT("enter", ("fd: %d pos: %lu wrmode: %d",
+ file, (ulong) filepos, wrmode));
+
+restart:
+ /*
+ If the flush phase of a resize operation fails, the cache is left
+ unusable. This will be detected only after "goto restart".
+ */
+ if (!keycache->can_be_used)
+ DBUG_RETURN(0);
+
+ /*
+ Find the hash_link for the requested file block (file, filepos). We
+ do always get a hash_link here. It has registered our request so
+ that no other thread can use it for another file block until we
+ release the request (which is done by remove_reader() usually). The
+ hash_link can have a block assigned to it or not. If there is a
+ block, it may be assigned to this hash_link or not. In cases where a
+ block is evicted from the cache, it is taken from the LRU ring and
+ referenced by the new hash_link. But the block can still be assigned
+ to its old hash_link for some time if it needs to be flushed first,
+ or if there are other threads still reading it.
+
+ Summary:
+ hash_link is always returned.
+ hash_link->block can be:
+ - NULL or
+ - not assigned to this hash_link or
+ - assigned to this hash_link. If assigned, the block can have
+ - invalid data (when freshly assigned) or
+ - valid data. Valid data can be
+ - changed over the file contents (dirty) or
+ - not changed (clean).
+ */
+ hash_link= get_hash_link(keycache, file, filepos, thread);
+ DBUG_ASSERT((hash_link->file == file) && (hash_link->diskpos == filepos));
+
+ page_status= -1;
+ if ((block= hash_link->block) &&
+ block->hash_link == hash_link && (block->status & BLOCK_READ))
+ {
+ /* Assigned block with valid (changed or unchanged) contents. */
+ page_status= PAGE_READ;
+ }
+ /*
+ else (page_status == -1)
+ - block == NULL or
+ - block not assigned to this hash_link or
+ - block assigned but not yet read from file (invalid data).
+ */
+
+ if (keycache->in_resize)
+ {
+ /* This is a request during a resize operation */
+
+ if (!block)
+ {
+ /*
+ The file block is not in the cache. We don't need it in the
+ cache: we are going to read or write directly to file. Cancel
+ the request. We can simply decrement hash_link->requests because
+ we did not release cache_lock since increasing it. So no other
+ thread can wait for our request to become released.
+ */
+ if (hash_link->requests == 1)
+ {
+ /*
+ We are the only one to request this hash_link (this file/pos).
+ Free the hash_link.
+ */
+ hash_link->requests--;
+ unlink_hash(keycache, hash_link);
+ DBUG_RETURN(0);
+ }
+
+ /*
+ More requests on the hash_link. Someone tries to evict a block
+ for this hash_link (could have started before resizing started).
+ This means that the LRU ring is empty. Otherwise a block could
+ be assigned immediately. Behave like a thread that wants to
+ evict a block for this file/pos. Add to the queue of threads
+ waiting for a block. Wait until there is one assigned.
+
+ Refresh the request on the hash-link so that it cannot be reused
+ for another file/pos.
+ */
+ thread->opt_info= (void *) hash_link;
+ link_into_queue(&keycache->waiting_for_block, thread);
+ do
+ {
+ mysql_cond_wait(&thread->suspend,
+ &keycache->cache_lock);
+ } while (thread->next);
+ thread->opt_info= NULL;
+ /*
+ A block should now be assigned to the hash_link. But it may
+ still need to be evicted. Anyway, we should re-check the
+ situation. page_status must be set correctly.
+ */
+ hash_link->requests--;
+ goto restart;
+ } /* end of if (!block) */
+
+ /*
+ There is a block for this file/pos in the cache. Register a
+ request on it. This unlinks it from the LRU ring (if it is there)
+ and hence protects it against eviction (if not already in
+ eviction). We need this for returning the block to the caller, for
+ calling remove_reader() (for debugging purposes), and for calling
+ free_block(). The only case where we don't need the request is if
+ the block is in eviction. In that case we have to unregister the
+ request later.
+ */
+ reg_requests(keycache, block, 1);
+
+ if (page_status != PAGE_READ)
+ {
+ /*
+ - block not assigned to this hash_link or
+ - block assigned but not yet read from file (invalid data).
+
+ This must be a block in eviction. It will be read soon. We need
+ to wait here until this happened. Otherwise the caller could
+ access a wrong block or a block which is in read. While waiting
+ we cannot lose hash_link nor block. We have registered a request
+ on the hash_link. Everything can happen to the block but changes
+ in the hash_link -> block relationship. In other words:
+ everything can happen to the block but free or another completed
+ eviction.
+
+ Note that we bahave like a secondary requestor here. We just
+ cannot return with PAGE_WAIT_TO_BE_READ. This would work for
+ read requests and writes on dirty blocks that are not in flush
+ only. Waiting here on COND_FOR_REQUESTED works in all
+ situations.
+ */
+ DBUG_ASSERT(((block->hash_link != hash_link) &&
+ (block->status & (BLOCK_IN_EVICTION | BLOCK_IN_SWITCH))) ||
+ ((block->hash_link == hash_link) &&
+ !(block->status & BLOCK_READ)));
+ wait_on_queue(&block->wqueue[COND_FOR_REQUESTED], &keycache->cache_lock,
+ thread);
+ /*
+ Here we can trust that the block has been assigned to this
+ hash_link (block->hash_link == hash_link) and read into the
+ buffer (BLOCK_READ). The worst things possible here are that the
+ block is in free (BLOCK_REASSIGNED). But the block is still
+ assigned to the hash_link. The freeing thread waits until we
+ release our request on the hash_link. The block must not be
+ again in eviction because we registered an request on it before
+ starting to wait.
+ */
+ DBUG_ASSERT(block->hash_link == hash_link);
+ DBUG_ASSERT(block->status & (BLOCK_READ | BLOCK_IN_USE));
+ DBUG_ASSERT(!(block->status & (BLOCK_IN_EVICTION | BLOCK_IN_SWITCH)));
+ }
+ /*
+ The block is in the cache. Assigned to the hash_link. Valid data.
+ Note that in case of page_st == PAGE_READ, the block can be marked
+ for eviction. In any case it can be marked for freeing.
+ */
+
+ if (!wrmode)
+ {
+ /* A reader can just read the block. */
+ *page_st= PAGE_READ;
+ DBUG_ASSERT((hash_link->file == file) &&
+ (hash_link->diskpos == filepos) &&
+ (block->hash_link == hash_link));
+ DBUG_RETURN(block);
+ }
+
+ /*
+ This is a writer. No two writers for the same block can exist.
+ This must be assured by locks outside of the key cache.
+ */
+ DBUG_ASSERT(!(block->status & BLOCK_FOR_UPDATE) || fail_block(block));
+
+ while (block->status & BLOCK_IN_FLUSH)
+ {
+ /*
+ Wait until the block is flushed to file. Do not release the
+ request on the hash_link yet to prevent that the block is freed
+ or reassigned while we wait. While we wait, several things can
+ happen to the block, including another flush. But the block
+ cannot be reassigned to another hash_link until we release our
+ request on it. But it can be marked BLOCK_REASSIGNED from free
+ or eviction, while they wait for us to release the hash_link.
+ */
+ wait_on_queue(&block->wqueue[COND_FOR_SAVED], &keycache->cache_lock,
+ thread);
+ /*
+ If the flush phase failed, the resize could have finished while
+ we waited here.
+ */
+ if (!keycache->in_resize)
+ {
+ remove_reader(block);
+ unreg_request(keycache, block, 1);
+ goto restart;
+ }
+ DBUG_ASSERT(block->status & (BLOCK_READ | BLOCK_IN_USE));
+ DBUG_ASSERT(!(block->status & BLOCK_FOR_UPDATE) || fail_block(block));
+ DBUG_ASSERT(block->hash_link == hash_link);
+ }
+
+ if (block->status & BLOCK_CHANGED)
+ {
+ /*
+ We want to write a block with changed contents. If the cache
+ block size is bigger than the callers block size (e.g. MyISAM),
+ the caller may replace part of the block only. Changes of the
+ other part of the block must be preserved. Since the block has
+ not yet been selected for flush, we can still add our changes.
+ */
+ *page_st= PAGE_READ;
+ DBUG_ASSERT((hash_link->file == file) &&
+ (hash_link->diskpos == filepos) &&
+ (block->hash_link == hash_link));
+ DBUG_RETURN(block);
+ }
+
+ /*
+ This is a write request for a clean block. We do not want to have
+ new dirty blocks in the cache while resizing. We will free the
+ block and write directly to file. If the block is in eviction or
+ in free, we just let it go.
+
+ Unregister from the hash_link. This must be done before freeing
+ the block. And it must be done if not freeing the block. Because
+ we could have waited above, we need to call remove_reader(). Other
+ threads could wait for us to release our request on the hash_link.
+ */
+ remove_reader(block);
+
+ /* If the block is not in eviction and not in free, we can free it. */
+ if (!(block->status & (BLOCK_IN_EVICTION | BLOCK_IN_SWITCH |
+ BLOCK_REASSIGNED)))
+ {
+ /*
+ Free block as we are going to write directly to file.
+ Although we have an exlusive lock for the updated key part,
+ the control can be yielded by the current thread as we might
+ have unfinished readers of other key parts in the block
+ buffer. Still we are guaranteed not to have any readers
+ of the key part we are writing into until the block is
+ removed from the cache as we set the BLOCK_REASSIGNED
+ flag (see the code below that handles reading requests).
+ */
+ free_block(keycache, thread, block);
+ }
+ else
+ {
+ /*
+ The block will be evicted/freed soon. Don't touch it in any way.
+ Unregister the request that we registered above.
+ */
+ unreg_request(keycache, block, 1);
+
+ /*
+ The block is still assigned to the hash_link (the file/pos that
+ we are going to write to). Wait until the eviction/free is
+ complete. Otherwise the direct write could complete before all
+ readers are done with the block. So they could read outdated
+ data.
+
+ Since we released our request on the hash_link, it can be reused
+ for another file/pos. Hence we cannot just check for
+ block->hash_link == hash_link. As long as the resize is
+ proceeding the block cannot be reassigned to the same file/pos
+ again. So we can terminate the loop when the block is no longer
+ assigned to this file/pos.
+ */
+ do
+ {
+ wait_on_queue(&block->wqueue[COND_FOR_SAVED],
+ &keycache->cache_lock, thread);
+ /*
+ If the flush phase failed, the resize could have finished
+ while we waited here.
+ */
+ if (!keycache->in_resize)
+ goto restart;
+ } while (block->hash_link &&
+ (block->hash_link->file == file) &&
+ (block->hash_link->diskpos == filepos));
+ }
+ DBUG_RETURN(0);
+ }
+
+ if (page_status == PAGE_READ &&
+ (block->status & (BLOCK_IN_EVICTION | BLOCK_IN_SWITCH |
+ BLOCK_REASSIGNED)))
+ {
+ /*
+ This is a request for a block to be removed from cache. The block
+ is assigned to this hash_link and contains valid data, but is
+ marked for eviction or to be freed. Possible reasons why it has
+ not yet been evicted/freed can be a flush before reassignment
+ (BLOCK_IN_SWITCH), readers of the block have not finished yet
+ (BLOCK_REASSIGNED), or the evicting thread did not yet awake after
+ the block has been selected for it (BLOCK_IN_EVICTION).
+ */
+
+ /*
+ 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))
+ {
+ /*
+ This is a read request and the block not yet reassigned. We can
+ register our request and proceed. This unlinks the block from
+ the LRU ring and protects it against eviction.
+ */
+ reg_requests(keycache, block, 1);
+ }
+ else
+ {
+ /*
+ Either this is a write request for a block that is in eviction
+ or in free. We must not use it any more. Instead we must evict
+ another block. But we cannot do this before the eviction/free is
+ done. Otherwise we would find the same hash_link + block again
+ and again.
+
+ Or this is a read request for a block in eviction/free that does
+ not require a flush, but waits for readers to finish with the
+ block. We do not read this block to let the eviction/free happen
+ as soon as possible. Again we must wait so that we don't find
+ the same hash_link + block again and again.
+ */
+ DBUG_ASSERT(hash_link->requests);
+ hash_link->requests--;
+ wait_on_queue(&block->wqueue[COND_FOR_SAVED], &keycache->cache_lock,
+ thread);
+ /*
+ The block is no longer assigned to this hash_link.
+ Get another one.
+ */
+ goto restart;
+ }
+ }
+ else
+ {
+ /*
+ This is a request for a new block or for a block not to be removed.
+ Either
+ - block == NULL or
+ - block not assigned to this hash_link or
+ - block assigned but not yet read from file,
+ or
+ - block assigned with valid (changed or unchanged) data and
+ - it will not be reassigned/freed.
+ */
+ if (! block)
+ {
+ /* No block is assigned to the hash_link yet. */
+ if (keycache->blocks_unused)
+ {
+ if (keycache->free_block_list)
+ {
+ /* There is a block in the free list. */
+ block= keycache->free_block_list;
+ keycache->free_block_list= block->next_used;
+ block->next_used= NULL;
+ }
+ else
+ {
+ size_t block_mem_offset;
+ /* There are some never used blocks, take first of them */
+ DBUG_ASSERT(keycache->blocks_used <
+ (ulong) keycache->disk_blocks);
+ block= &keycache->block_root[keycache->blocks_used];
+ block_mem_offset=
+ ((size_t) keycache->blocks_used) * keycache->key_cache_block_size;
+ block->buffer= ADD_TO_PTR(keycache->block_mem,
+ block_mem_offset,
+ uchar*);
+ keycache->blocks_used++;
+ DBUG_ASSERT(!block->next_used);
+ }
+ DBUG_ASSERT(!block->prev_used);
+ DBUG_ASSERT(!block->next_changed);
+ DBUG_ASSERT(!block->prev_changed);
+ DBUG_ASSERT(!block->hash_link);
+ DBUG_ASSERT(!block->status);
+ DBUG_ASSERT(!block->requests);
+ keycache->blocks_unused--;
+ block->status= BLOCK_IN_USE;
+ block->length= 0;
+ block->offset= keycache->key_cache_block_size;
+ block->requests= 1;
+ block->temperature= BLOCK_COLD;
+ block->hits_left= init_hits_left;
+ block->last_hit_time= 0;
+ block->hash_link= hash_link;
+ hash_link->block= block;
+ link_to_file_list(keycache, block, file, 0);
+ page_status= PAGE_TO_BE_READ;
+ }
+ else
+ {
+ /*
+ There are no free blocks and no never used blocks, use a block
+ from the LRU ring.
+ */
+
+ if (! keycache->used_last)
+ {
+ /*
+ The LRU ring is empty. Wait until a new block is added to
+ it. Several threads might wait here for the same hash_link,
+ all of them must get the same block. While waiting for a
+ block, after a block is selected for this hash_link, other
+ threads can run first before this one awakes. During this
+ time interval other threads find this hash_link pointing to
+ the block, which is still assigned to another hash_link. In
+ this case the block is not marked BLOCK_IN_SWITCH yet, but
+ it is marked BLOCK_IN_EVICTION.
+ */
+
+ thread->opt_info= (void *) hash_link;
+ link_into_queue(&keycache->waiting_for_block, thread);
+ do
+ {
+ mysql_cond_wait(&thread->suspend,
+ &keycache->cache_lock);
+ }
+ while (thread->next);
+ thread->opt_info= NULL;
+ /* Assert that block has a request registered. */
+ DBUG_ASSERT(hash_link->block->requests);
+ /* Assert that block is not in LRU ring. */
+ DBUG_ASSERT(!hash_link->block->next_used);
+ DBUG_ASSERT(!hash_link->block->prev_used);
+ }
+
+ /*
+ If we waited above, hash_link->block has been assigned by
+ link_block(). Otherwise it is still NULL. In the latter case
+ we need to grab a block from the LRU ring ourselves.
+ */
+ block= hash_link->block;
+ if (! block)
+ {
+ /* Select the last block from the LRU ring. */
+ block= keycache->used_last->next_used;
+ block->hits_left= init_hits_left;
+ block->last_hit_time= 0;
+ hash_link->block= block;
+ /*
+ Register a request on the block. This unlinks it from the
+ LRU ring and protects it against eviction.
+ */
+ DBUG_ASSERT(!block->requests);
+ reg_requests(keycache, block,1);
+ /*
+ We do not need to set block->status|= BLOCK_IN_EVICTION here
+ because we will set block->status|= BLOCK_IN_SWITCH
+ immediately without releasing the lock in between. This does
+ also support debugging. When looking at the block, one can
+ see if the block has been selected by link_block() after the
+ LRU ring was empty, or if it was grabbed directly from the
+ LRU ring in this branch.
+ */
+ }
+
+ /*
+ If we had to wait above, there is a small chance that another
+ thread grabbed this block for the same file block already. But
+ in most cases the first condition is true.
+ */
+ 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;
+
+ if (block->status & BLOCK_CHANGED)
+ {
+ /* The block contains a dirty page - push it out of the cache */
+
+ if (block->status & BLOCK_IN_FLUSH)
+ {
+ /*
+ The block is marked for flush. If we do not wait here,
+ it could happen that we write the block, reassign it to
+ another file block, then, before the new owner can read
+ the new file block, the flusher writes the cache block
+ (which still has the old contents) to the new file block!
+ */
+ wait_on_queue(&block->wqueue[COND_FOR_SAVED],
+ &keycache->cache_lock, thread);
+ /*
+ The block is marked BLOCK_IN_SWITCH. It should be left
+ alone except for reading. No free, no write.
+ */
+ DBUG_ASSERT(block->status & (BLOCK_READ | BLOCK_IN_USE));
+ DBUG_ASSERT(!(block->status & (BLOCK_REASSIGNED |
+ BLOCK_CHANGED |
+ BLOCK_FOR_UPDATE)));
+ }
+ else
+ {
+ block->status|= BLOCK_IN_FLUSH | BLOCK_IN_FLUSHWRITE;
+ /*
+ BLOCK_IN_EVICTION may be true or not. Other flags must
+ have a fixed value.
+ */
+ DBUG_ASSERT((block->status & ~BLOCK_IN_EVICTION) ==
+ (BLOCK_READ | BLOCK_IN_SWITCH |
+ BLOCK_IN_FLUSH | BLOCK_IN_FLUSHWRITE |
+ BLOCK_CHANGED | BLOCK_IN_USE));
+ DBUG_ASSERT(block->hash_link);
+
+ mysql_mutex_unlock(&keycache->cache_lock);
+ /*
+ The call is thread safe because only the current
+ thread might change the block->hash_link value
+ */
+ error= (int)my_pwrite(block->hash_link->file,
+ block->buffer + block->offset,
+ block->length - block->offset,
+ block->hash_link->diskpos + block->offset,
+ MYF(MY_NABP | MY_WAIT_IF_FULL));
+ mysql_mutex_lock(&keycache->cache_lock);
+
+ /* Block status must not have changed. */
+ DBUG_ASSERT((block->status & ~BLOCK_IN_EVICTION) ==
+ (BLOCK_READ | BLOCK_IN_SWITCH |
+ BLOCK_IN_FLUSH | BLOCK_IN_FLUSHWRITE |
+ BLOCK_CHANGED | BLOCK_IN_USE) || fail_block(block));
+ keycache->global_cache_write++;
+ }
+ }
+
+ block->status|= BLOCK_REASSIGNED;
+ /*
+ The block comes from the LRU ring. It must have a hash_link
+ assigned.
+ */
+ DBUG_ASSERT(block->hash_link);
+ if (block->hash_link)
+ {
+ /*
+ All pending requests for this page must be resubmitted.
+ This must be done before waiting for readers. They could
+ wait for the flush to complete. And we must also do it
+ after the wait. Flushers might try to free the block while
+ we wait. They would wait until the reassignment is
+ complete. Also the block status must reflect the correct
+ situation: The block is not changed nor in flush any more.
+ Note that we must not change the BLOCK_CHANGED flag
+ outside of link_to_file_list() so that it is always in the
+ correct queue and the *blocks_changed counters are
+ correct.
+ */
+ block->status&= ~(BLOCK_IN_FLUSH | BLOCK_IN_FLUSHWRITE);
+ link_to_file_list(keycache, block, block->hash_link->file, 1);
+ release_whole_queue(&block->wqueue[COND_FOR_SAVED]);
+ /*
+ The block is still assigned to its old hash_link.
+ Wait until all pending read requests
+ for this page are executed
+ (we could have avoided this waiting, if we had read
+ a page in the cache in a sweep, without yielding control)
+ */
+ wait_for_readers(keycache, block, thread);
+ DBUG_ASSERT(block->hash_link && block->hash_link->block == block &&
+ block->prev_changed);
+ /* The reader must not have been a writer. */
+ DBUG_ASSERT(!(block->status & BLOCK_CHANGED));
+
+ /* Wake flushers that might have found the block in between. */
+ release_whole_queue(&block->wqueue[COND_FOR_SAVED]);
+
+ /* Remove the hash link for the old file block from the hash. */
+ unlink_hash(keycache, block->hash_link);
+
+ /*
+ For sanity checks link_to_file_list() asserts that block
+ and hash_link refer to each other. Hence we need to assign
+ the hash_link first, but then we would not know if it was
+ linked before. Hence we would not know if to unlink it. So
+ unlink it here and call link_to_file_list(..., FALSE).
+ */
+ unlink_changed(block);
+ }
+ block->status= error ? BLOCK_ERROR : BLOCK_IN_USE ;
+ block->length= 0;
+ block->offset= keycache->key_cache_block_size;
+ block->hash_link= hash_link;
+ link_to_file_list(keycache, block, file, 0);
+ page_status= PAGE_TO_BE_READ;
+
+ DBUG_ASSERT(block->hash_link->block == block);
+ DBUG_ASSERT(hash_link->block->hash_link == hash_link);
+ }
+ else
+ {
+ /*
+ Either (block->hash_link == hash_link),
+ or (block->status & BLOCK_IN_SWITCH).
+
+ This is for secondary requests for a new file block only.
+ Either it is already assigned to the new hash_link meanwhile
+ (if we had to wait due to empty LRU), or it is already in
+ eviction by another thread. Since this block has been
+ grabbed from the LRU ring and attached to this hash_link,
+ another thread cannot grab the same block from the LRU ring
+ anymore. If the block is in eviction already, it must become
+ attached to the same hash_link and as such destined for the
+ same file block.
+ */
+ page_status= (((block->hash_link == hash_link) &&
+ (block->status & BLOCK_READ)) ?
+ PAGE_READ : PAGE_WAIT_TO_BE_READ);
+ }
+ }
+ }
+ else
+ {
+ /*
+ Block is not NULL. This hash_link points to a block.
+ Either
+ - block not assigned to this hash_link (yet) or
+ - block assigned but not yet read from file,
+ or
+ - block assigned with valid (changed or unchanged) data and
+ - it will not be reassigned/freed.
+
+ The first condition means hash_link points to a block in
+ eviction. This is not necessarily marked by BLOCK_IN_SWITCH yet.
+ But then it is marked BLOCK_IN_EVICTION. See the NOTE in
+ link_block(). In both cases it is destined for this hash_link
+ and its file block address. When this hash_link got its block
+ address, the block was removed from the LRU ring and cannot be
+ selected for eviction (for another hash_link) again.
+
+ Register a request on the block. This is another protection
+ against eviction.
+ */
+ DBUG_ASSERT(((block->hash_link != hash_link) &&
+ (block->status & (BLOCK_IN_EVICTION | BLOCK_IN_SWITCH))) ||
+ ((block->hash_link == hash_link) &&
+ !(block->status & BLOCK_READ)) ||
+ ((block->status & BLOCK_READ) &&
+ !(block->status & (BLOCK_IN_EVICTION | BLOCK_IN_SWITCH))));
+ reg_requests(keycache, block, 1);
+ page_status= (((block->hash_link == hash_link) &&
+ (block->status & BLOCK_READ)) ?
+ PAGE_READ : PAGE_WAIT_TO_BE_READ);
+ }
+ }
+
+ DBUG_ASSERT(page_status != -1);
+ /* Same assert basically, but be very sure. */
+ DBUG_ASSERT(block);
+ /* Assert that block has a request and is not in LRU ring. */
+ DBUG_ASSERT(block->requests);
+ DBUG_ASSERT(!block->next_used);
+ DBUG_ASSERT(!block->prev_used);
+ /* Assert that we return the correct block. */
+ DBUG_ASSERT((page_status == PAGE_WAIT_TO_BE_READ) ||
+ ((block->hash_link->file == file) &&
+ (block->hash_link->diskpos == filepos)));
+ *page_st=page_status;
+ DBUG_RETURN(block);
+}
+
+
+/*
+ Read into a key cache block buffer from disk.
+
+ SYNOPSIS
+
+ read_block()
+ keycache pointer to a key cache data structure
+ thread_var pointer to thread specific variables
+ block block to which buffer the data is to be read
+ read_length size of data to be read
+ min_length at least so much data must be read
+ primary <-> the current thread will read the data
+
+ RETURN VALUE
+ None
+
+ NOTES.
+ The function either reads a page data from file to the block buffer,
+ or waits until another thread reads it. What page to read is determined
+ by a block parameter - reference to a hash link for this page.
+ If an error occurs THE BLOCK_ERROR bit is set in the block status.
+ We do not report error when the size of successfully read
+ portion is less than read_length, but not less than min_length.
+*/
+
+static void read_block(KEY_CACHE *keycache,
+ st_keycache_thread_var *thread_var,
+ BLOCK_LINK *block, uint read_length,
+ uint min_length, my_bool primary)
+{
+ size_t got_length;
+
+ /* On entry cache_lock is locked */
+
+ if (primary)
+ {
+ /*
+ This code is executed only by threads that submitted primary
+ requests. Until block->status contains BLOCK_READ, all other
+ request for the block become secondary requests. For a primary
+ request the block must be properly initialized.
+ */
+ DBUG_ASSERT(((block->status & ~BLOCK_FOR_UPDATE) == BLOCK_IN_USE) ||
+ fail_block(block));
+ DBUG_ASSERT((block->length == 0) || fail_block(block));
+ DBUG_ASSERT((block->offset == keycache->key_cache_block_size) ||
+ fail_block(block));
+ DBUG_ASSERT((block->requests > 0) || fail_block(block));
+
+ keycache->global_cache_read++;
+ /* Page is not in buffer yet, is to be read from disk */
+ mysql_mutex_unlock(&keycache->cache_lock);
+ /*
+ Here other threads may step in and register as secondary readers.
+ They will register in block->wqueue[COND_FOR_REQUESTED].
+ */
+ got_length= my_pread(block->hash_link->file, block->buffer,
+ read_length, block->hash_link->diskpos, MYF(0));
+ mysql_mutex_lock(&keycache->cache_lock);
+ /*
+ The block can now have been marked for free (in case of
+ FLUSH_RELEASE). Otherwise the state must be unchanged.
+ */
+ DBUG_ASSERT(((block->status & ~(BLOCK_REASSIGNED |
+ BLOCK_FOR_UPDATE)) == BLOCK_IN_USE) ||
+ fail_block(block));
+ DBUG_ASSERT((block->length == 0) || fail_block(block));
+ DBUG_ASSERT((block->offset == keycache->key_cache_block_size) ||
+ fail_block(block));
+ DBUG_ASSERT((block->requests > 0) || fail_block(block));
+
+ if (got_length < min_length)
+ block->status|= BLOCK_ERROR;
+ else
+ {
+ block->status|= BLOCK_READ;
+ block->length= (int)got_length;
+ /*
+ Do not set block->offset here. If this block is marked
+ BLOCK_CHANGED later, we want to flush only the modified part. So
+ only a writer may set block->offset down from
+ keycache->key_cache_block_size.
+ */
+ }
+ /* Signal that all pending requests for this page now can be processed */
+ release_whole_queue(&block->wqueue[COND_FOR_REQUESTED]);
+ }
+ else
+ {
+ /*
+ This code is executed only by threads that submitted secondary
+ requests. At this point it could happen that the cache block is
+ not yet assigned to the hash_link for the requested file block.
+ But at awake from the wait this should be the case. Unfortunately
+ we cannot assert this here because we do not know the hash_link
+ for the requested file block nor the file and position. So we have
+ to assert this in the caller.
+ */
+ wait_on_queue(&block->wqueue[COND_FOR_REQUESTED], &keycache->cache_lock,
+ thread_var);
+ }
+}
+
+
+/*
+ Read a block of data from a cached file into a buffer;
+
+ SYNOPSIS
+
+ key_cache_read()
+ keycache pointer to a key cache data structure
+ thread_var pointer to thread specific variables
+ file handler for the file for the block of data to be read
+ filepos position of the block of data in the file
+ level determines the weight of the data
+ buff buffer to where the data must be placed
+ length length of the buffer
+ block_length length of the block in the key cache buffer
+ return_buffer return pointer to the key cache buffer with the data
+
+ RETURN VALUE
+ Returns address from where the data is placed if sucessful, 0 - otherwise.
+
+ NOTES.
+ The function ensures that a block of data of size length from file
+ positioned at filepos is in the buffers for some key cache blocks.
+ Then the function either copies the data into the buffer buff, or,
+ if return_buffer is TRUE, it just returns the pointer to the key cache
+ buffer with the data.
+ Filepos must be a multiple of 'block_length', but it doesn't
+ have to be a multiple of key_cache_block_size;
+*/
+
+uchar *key_cache_read(KEY_CACHE *keycache,
+ st_keycache_thread_var *thread_var,
+ File file, my_off_t filepos, int level,
+ uchar *buff, uint length,
+ uint block_length MY_ATTRIBUTE((unused)),
+ int return_buffer MY_ATTRIBUTE((unused)))
+{
+ my_bool locked_and_incremented= FALSE;
+ int error=0;
+ uchar *start= buff;
+ DBUG_ENTER("key_cache_read");
+ DBUG_PRINT("enter", ("fd: %u pos: %lu length: %u",
+ (uint) file, (ulong) filepos, length));
+
+ if (keycache->key_cache_inited)
+ {
+ /* Key cache is used */
+ BLOCK_LINK *block;
+ uint read_length;
+ uint offset;
+ int page_st;
+
+ if (MYSQL_KEYCACHE_READ_START_ENABLED())
+ {
+ MYSQL_KEYCACHE_READ_START(my_filename(file), length,
+ (ulong) (keycache->blocks_used *
+ keycache->key_cache_block_size),
+ (ulong) (keycache->blocks_unused *
+ keycache->key_cache_block_size));
+ }
+
+ /*
+ When the key cache is once initialized, we use the cache_lock to
+ reliably distinguish the cases of normal operation, resizing, and
+ disabled cache. We always increment and decrement
+ 'cnt_for_resize_op' so that a resizer can wait for pending I/O.
+ */
+ mysql_mutex_lock(&keycache->cache_lock);
+ /*
+ Cache resizing has two phases: Flushing and re-initializing. In
+ the flush phase read requests are allowed to bypass the cache for
+ blocks not in the cache. find_key_block() returns NULL in this
+ case.
+
+ After the flush phase new I/O requests must wait until the
+ re-initialization is done. The re-initialization can be done only
+ if no I/O request is in progress. The reason is that
+ key_cache_block_size can change. With enabled cache, I/O is done
+ in chunks of key_cache_block_size. Every chunk tries to use a
+ cache block first. If the block size changes in the middle, a
+ block could be missed and old data could be read.
+ */
+ while (keycache->in_resize && !keycache->resize_in_flush)
+ wait_on_queue(&keycache->resize_queue, &keycache->cache_lock,
+ thread_var);
+ /* Register the I/O for the next resize. */
+ inc_counter_for_resize_op(keycache);
+ locked_and_incremented= TRUE;
+ /* Requested data may not always be aligned to cache blocks. */
+ offset= (uint) (filepos % keycache->key_cache_block_size);
+ /* Read data in key_cache_block_size increments */
+ do
+ {
+ /* Cache could be disabled in a later iteration. */
+ if (!keycache->can_be_used)
+ {
+ goto no_key_cache;
+ }
+ /* Start reading at the beginning of the cache block. */
+ filepos-= offset;
+ /* Do not read beyond the end of the cache block. */
+ read_length= length;
+ set_if_smaller(read_length, keycache->key_cache_block_size-offset);
+ DBUG_ASSERT(read_length > 0);
+
+ if (block_length > keycache->key_cache_block_size || offset)
+ return_buffer=0;
+
+ /* Request the cache block that matches file/pos. */
+ keycache->global_cache_r_requests++;
+
+ MYSQL_KEYCACHE_READ_BLOCK(keycache->key_cache_block_size);
+
+ block= find_key_block(keycache, thread_var, file, filepos, level, 0,
+ &page_st);
+ if (!block)
+ {
+ /*
+ This happens only for requests submitted during key cache
+ resize. The block is not in the cache and shall not go in.
+ Read directly from file.
+ */
+ keycache->global_cache_read++;
+ mysql_mutex_unlock(&keycache->cache_lock);
+ error= (my_pread(file, (uchar*) buff, read_length,
+ filepos + offset, MYF(MY_NABP)) != 0);
+ mysql_mutex_lock(&keycache->cache_lock);
+ goto next_block;
+ }
+ if (!(block->status & BLOCK_ERROR))
+ {
+ if (page_st != PAGE_READ)
+ {
+ MYSQL_KEYCACHE_READ_MISS();
+ /* The requested page is to be read into the block buffer */
+ read_block(keycache, thread_var, block,
+ keycache->key_cache_block_size, read_length+offset,
+ (my_bool)(page_st == PAGE_TO_BE_READ));
+ /*
+ A secondary request must now have the block assigned to the
+ requested file block. It does not hurt to check it for
+ primary requests too.
+ */
+ DBUG_ASSERT(keycache->can_be_used);
+ DBUG_ASSERT(block->hash_link->file == file);
+ DBUG_ASSERT(block->hash_link->diskpos == filepos);
+ DBUG_ASSERT(block->status & (BLOCK_READ | BLOCK_IN_USE));
+ }
+ else if (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
+ */
+ set_my_errno(-1);
+ block->status|= BLOCK_ERROR;
+ }
+ else
+ {
+ MYSQL_KEYCACHE_READ_HIT();
+ }
+ }
+
+ /* block status may have added BLOCK_ERROR in the above 'if'. */
+ if (!(block->status & BLOCK_ERROR))
+ {
+ {
+ DBUG_ASSERT(block->status & (BLOCK_READ | BLOCK_IN_USE));
+ mysql_mutex_unlock(&keycache->cache_lock);
+
+ /* Copy data from the cache buffer */
+ memcpy(buff, block->buffer+offset, (size_t) read_length);
+
+ mysql_mutex_lock(&keycache->cache_lock);
+ DBUG_ASSERT(block->status & (BLOCK_READ | BLOCK_IN_USE));
+ }
+ }
+
+ remove_reader(block);
+
+ /* Error injection for coverage testing. */
+ DBUG_EXECUTE_IF("key_cache_read_block_error",
+ block->status|= BLOCK_ERROR;);
+
+ /* Do not link erroneous blocks into the LRU ring, but free them. */
+ if (!(block->status & BLOCK_ERROR))
+ {
+ /*
+ Link the block into the LRU ring if it's the last submitted
+ request for the block. This enables eviction for the block.
+ */
+ unreg_request(keycache, block, 1);
+ }
+ else
+ {
+ free_block(keycache, thread_var, block);
+ error= 1;
+ break;
+ }
+
+ next_block:
+ buff+= read_length;
+ filepos+= read_length+offset;
+ offset= 0;
+
+ } while ((length-= read_length));
+ if (MYSQL_KEYCACHE_READ_DONE_ENABLED())
+ {
+ MYSQL_KEYCACHE_READ_DONE((ulong) (keycache->blocks_used *
+ keycache->key_cache_block_size),
+ (ulong) (keycache->blocks_unused *
+ keycache->key_cache_block_size));
+ }
+ goto end;
+ }
+
+no_key_cache:
+ /* Key cache is not used */
+
+ keycache->global_cache_r_requests++;
+ keycache->global_cache_read++;
+
+ if (locked_and_incremented)
+ mysql_mutex_unlock(&keycache->cache_lock);
+ if (my_pread(file, (uchar*) buff, length, filepos, MYF(MY_NABP)))
+ error= 1;
+ if (locked_and_incremented)
+ mysql_mutex_lock(&keycache->cache_lock);
+
+end:
+ if (locked_and_incremented)
+ {
+ dec_counter_for_resize_op(keycache);
+ mysql_mutex_unlock(&keycache->cache_lock);
+ }
+ DBUG_PRINT("exit", ("error: %d", error ));
+ DBUG_RETURN(error ? (uchar*) 0 : start);
+}
+
+
+/*
+ Insert a block of file data from a buffer into key cache
+
+ SYNOPSIS
+ key_cache_insert()
+ keycache pointer to a key cache data structure
+ thread_var pointer to thread specific variables
+ file handler for the file to insert data from
+ filepos position of the block of data in the file to insert
+ level determines the weight of the data
+ buff buffer to read data from
+ length length of the data in the buffer
+
+ NOTES
+ This is used by MyISAM to move all blocks from a index file to the key
+ cache
+
+ RETURN VALUE
+ 0 if a success, 1 - otherwise.
+*/
+
+int key_cache_insert(KEY_CACHE *keycache,
+ st_keycache_thread_var *thread_var,
+ File file, my_off_t filepos, int level,
+ uchar *buff, uint length)
+{
+ int error= 0;
+ DBUG_ENTER("key_cache_insert");
+ DBUG_PRINT("enter", ("fd: %u pos: %lu length: %u",
+ (uint) file,(ulong) filepos, length));
+
+ if (keycache->key_cache_inited)
+ {
+ /* Key cache is used */
+ BLOCK_LINK *block;
+ uint read_length;
+ uint offset;
+ int page_st;
+ my_bool locked_and_incremented= FALSE;
+
+ /*
+ When the keycache is once initialized, we use the cache_lock to
+ reliably distinguish the cases of normal operation, resizing, and
+ disabled cache. We always increment and decrement
+ 'cnt_for_resize_op' so that a resizer can wait for pending I/O.
+ */
+ mysql_mutex_lock(&keycache->cache_lock);
+ /*
+ We do not load index data into a disabled cache nor into an
+ ongoing resize.
+ */
+ if (!keycache->can_be_used || keycache->in_resize)
+ goto no_key_cache;
+ /* Register the pseudo I/O for the next resize. */
+ inc_counter_for_resize_op(keycache);
+ locked_and_incremented= TRUE;
+ /* Loaded data may not always be aligned to cache blocks. */
+ offset= (uint) (filepos % keycache->key_cache_block_size);
+ /* Load data in key_cache_block_size increments. */
+ do
+ {
+ /* Cache could be disabled or resizing in a later iteration. */
+ if (!keycache->can_be_used || keycache->in_resize)
+ goto no_key_cache;
+ /* Start loading at the beginning of the cache block. */
+ filepos-= offset;
+ /* Do not load beyond the end of the cache block. */
+ read_length= length;
+ set_if_smaller(read_length, keycache->key_cache_block_size-offset);
+ DBUG_ASSERT(read_length > 0);
+
+ /* The block has been read by the caller already. */
+ keycache->global_cache_read++;
+ /* Request the cache block that matches file/pos. */
+ keycache->global_cache_r_requests++;
+ block= find_key_block(keycache, thread_var, file, filepos, level, 0,
+ &page_st);
+ if (!block)
+ {
+ /*
+ This happens only for requests submitted during key cache
+ resize. The block is not in the cache and shall not go in.
+ Stop loading index data.
+ */
+ goto no_key_cache;
+ }
+ if (!(block->status & BLOCK_ERROR))
+ {
+ if ((page_st == PAGE_WAIT_TO_BE_READ) ||
+ ((page_st == PAGE_TO_BE_READ) &&
+ (offset || (read_length < keycache->key_cache_block_size))))
+ {
+ /*
+ Either
+
+ this is a secondary request for a block to be read into the
+ cache. The block is in eviction. It is not yet assigned to
+ the requested file block (It does not point to the right
+ hash_link). So we cannot call remove_reader() on the block.
+ And we cannot access the hash_link directly here. We need to
+ wait until the assignment is complete. read_block() executes
+ the correct wait when called with primary == FALSE.
+
+ Or
+
+ this is a primary request for a block to be read into the
+ cache and the supplied data does not fill the whole block.
+
+ This function is called on behalf of a LOAD INDEX INTO CACHE
+ statement, which is a read-only task and allows other
+ readers. It is possible that a parallel running reader tries
+ to access this block. If it needs more data than has been
+ supplied here, it would report an error. To be sure that we
+ have all data in the block that is available in the file, we
+ read the block ourselves.
+
+ Though reading again what the caller did read already is an
+ expensive operation, we need to do this for correctness.
+ */
+ read_block(keycache, thread_var, block,
+ keycache->key_cache_block_size,
+ read_length + offset, (page_st == PAGE_TO_BE_READ));
+ /*
+ A secondary request must now have the block assigned to the
+ requested file block. It does not hurt to check it for
+ primary requests too.
+ */
+ DBUG_ASSERT(keycache->can_be_used);
+ DBUG_ASSERT(block->hash_link->file == file);
+ DBUG_ASSERT(block->hash_link->diskpos == filepos);
+ DBUG_ASSERT(block->status & (BLOCK_READ | BLOCK_IN_USE));
+ }
+ else if (page_st == PAGE_TO_BE_READ)
+ {
+ /*
+ This is a new block in the cache. If we come here, we have
+ data for the whole block.
+ */
+ DBUG_ASSERT(block->hash_link->requests);
+ DBUG_ASSERT(block->status & BLOCK_IN_USE);
+ DBUG_ASSERT((page_st == PAGE_TO_BE_READ) ||
+ (block->status & BLOCK_READ));
+
+ mysql_mutex_unlock(&keycache->cache_lock);
+ /*
+ Here other threads may step in and register as secondary readers.
+ They will register in block->wqueue[COND_FOR_REQUESTED].
+ */
+
+ /* Copy data from buff */
+ memcpy(block->buffer+offset, buff, (size_t) read_length);
+
+ mysql_mutex_lock(&keycache->cache_lock);
+ DBUG_ASSERT(block->status & BLOCK_IN_USE);
+ DBUG_ASSERT((page_st == PAGE_TO_BE_READ) ||
+ (block->status & BLOCK_READ));
+ /*
+ After the data is in the buffer, we can declare the block
+ valid. Now other threads do not need to register as
+ secondary readers any more. They can immediately access the
+ block.
+ */
+ block->status|= BLOCK_READ;
+ block->length= read_length+offset;
+ /*
+ Do not set block->offset here. If this block is marked
+ BLOCK_CHANGED later, we want to flush only the modified part. So
+ only a writer may set block->offset down from
+ keycache->key_cache_block_size.
+ */
+ /* Signal all pending requests. */
+ release_whole_queue(&block->wqueue[COND_FOR_REQUESTED]);
+ }
+ else
+ {
+ /*
+ page_st == PAGE_READ. The block is in the buffer. All data
+ must already be present. Blocks are always read with all
+ data available on file. Assert that the block does not have
+ less contents than the preloader supplies. If the caller has
+ data beyond block->length, it means that a file write has
+ been done while this block was in cache and not extended
+ with the new data. If the condition is met, we can simply
+ ignore the block.
+ */
+ DBUG_ASSERT((page_st == PAGE_READ) &&
+ (read_length + offset <= block->length));
+ }
+
+ /*
+ A secondary request must now have the block assigned to the
+ requested file block. It does not hurt to check it for primary
+ requests too.
+ */
+ DBUG_ASSERT(block->hash_link->file == file);
+ DBUG_ASSERT(block->hash_link->diskpos == filepos);
+ DBUG_ASSERT(block->status & (BLOCK_READ | BLOCK_IN_USE));
+ } /* end of if (!(block->status & BLOCK_ERROR)) */
+
+ remove_reader(block);
+
+ /* Error injection for coverage testing. */
+ DBUG_EXECUTE_IF("key_cache_insert_block_error",
+ block->status|= BLOCK_ERROR; errno=EIO;);
+
+ /* Do not link erroneous blocks into the LRU ring, but free them. */
+ if (!(block->status & BLOCK_ERROR))
+ {
+ /*
+ Link the block into the LRU ring if it's the last submitted
+ request for the block. This enables eviction for the block.
+ */
+ unreg_request(keycache, block, 1);
+ }
+ else
+ {
+ free_block(keycache, thread_var, block);
+ error= 1;
+ break;
+ }
+
+ buff+= read_length;
+ filepos+= read_length+offset;
+ offset= 0;
+
+ } while ((length-= read_length));
+
+ no_key_cache:
+ if (locked_and_incremented)
+ dec_counter_for_resize_op(keycache);
+ mysql_mutex_unlock(&keycache->cache_lock);
+ }
+ DBUG_RETURN(error);
+}
+
+
+/*
+ Write a buffer into a cached file.
+
+ SYNOPSIS
+
+ key_cache_write()
+ keycache pointer to a key cache data structure
+ thread_var pointer to thread specific variables
+ file handler for the file to write data to
+ filepos position in the file to write data to
+ level determines the weight of the data
+ buff buffer with the data
+ length length of the buffer
+ dont_write if is 0 then all dirty pages involved in writing
+ should have been flushed from key cache
+
+ RETURN VALUE
+ 0 if a success, 1 - otherwise.
+
+ NOTES.
+ The function copies the data of size length from buff into buffers
+ for key cache blocks that are assigned to contain the portion of
+ the file starting with position filepos.
+ It ensures that this data is flushed to the file if dont_write is FALSE.
+ Filepos must be a multiple of 'block_length', but it doesn't
+ have to be a multiple of key_cache_block_size;
+
+ dont_write is always TRUE in the server (info->lock_type is never F_UNLCK).
+*/
+
+int key_cache_write(KEY_CACHE *keycache,
+ st_keycache_thread_var *thread_var,
+ File file, my_off_t filepos, int level,
+ uchar *buff, uint length,
+ uint block_length MY_ATTRIBUTE((unused)),
+ int dont_write)
+{
+ my_bool locked_and_incremented= FALSE;
+ int error=0;
+ DBUG_ENTER("key_cache_write");
+ DBUG_PRINT("enter",
+ ("fd: %u pos: %lu length: %u block_length: %u"
+ " key_block_length: %u",
+ (uint) file, (ulong) filepos, length, block_length,
+ keycache ? keycache->key_cache_block_size : 0));
+
+ if (!dont_write)
+ {
+ /* purecov: begin inspected */
+ /* Not used in the server. */
+ /* Force writing from buff into disk. */
+ keycache->global_cache_w_requests++;
+ keycache->global_cache_write++;
+ if (my_pwrite(file, buff, length, filepos, MYF(MY_NABP | MY_WAIT_IF_FULL)))
+ DBUG_RETURN(1);
+ /* purecov: end */
+ }
+
+ if (keycache->key_cache_inited)
+ {
+ /* Key cache is used */
+ BLOCK_LINK *block;
+ uint read_length;
+ uint offset;
+ int page_st;
+
+ if (MYSQL_KEYCACHE_WRITE_START_ENABLED())
+ {
+ MYSQL_KEYCACHE_WRITE_START(my_filename(file), length,
+ (ulong) (keycache->blocks_used *
+ keycache->key_cache_block_size),
+ (ulong) (keycache->blocks_unused *
+ keycache->key_cache_block_size));
+ }
+
+ /*
+ When the key cache is once initialized, we use the cache_lock to
+ reliably distinguish the cases of normal operation, resizing, and
+ disabled cache. We always increment and decrement
+ 'cnt_for_resize_op' so that a resizer can wait for pending I/O.
+ */
+ mysql_mutex_lock(&keycache->cache_lock);
+ /*
+ Cache resizing has two phases: Flushing and re-initializing. In
+ the flush phase write requests can modify dirty blocks that are
+ not yet in flush. Otherwise they are allowed to bypass the cache.
+ find_key_block() returns NULL in both cases (clean blocks and
+ non-cached blocks).
+
+ After the flush phase new I/O requests must wait until the
+ re-initialization is done. The re-initialization can be done only
+ if no I/O request is in progress. The reason is that
+ key_cache_block_size can change. With enabled cache I/O is done in
+ chunks of key_cache_block_size. Every chunk tries to use a cache
+ block first. If the block size changes in the middle, a block
+ could be missed and data could be written below a cached block.
+ */
+ while (keycache->in_resize && !keycache->resize_in_flush)
+ wait_on_queue(&keycache->resize_queue, &keycache->cache_lock,
+ thread_var);
+ /* Register the I/O for the next resize. */
+ inc_counter_for_resize_op(keycache);
+ locked_and_incremented= TRUE;
+ /* Requested data may not always be aligned to cache blocks. */
+ offset= (uint) (filepos % keycache->key_cache_block_size);
+ /* Write data in key_cache_block_size increments. */
+ do
+ {
+ /* Cache could be disabled in a later iteration. */
+ if (!keycache->can_be_used)
+ goto no_key_cache;
+
+ MYSQL_KEYCACHE_WRITE_BLOCK(keycache->key_cache_block_size);
+ /* Start writing at the beginning of the cache block. */
+ filepos-= offset;
+ /* Do not write beyond the end of the cache block. */
+ read_length= length;
+ set_if_smaller(read_length, keycache->key_cache_block_size-offset);
+ DBUG_ASSERT(read_length > 0);
+
+ /* Request the cache block that matches file/pos. */
+ keycache->global_cache_w_requests++;
+ block= find_key_block(keycache, thread_var, file, filepos, level, 1,
+ &page_st);
+ if (!block)
+ {
+ /*
+ This happens only for requests submitted during key cache
+ resize. The block is not in the cache and shall not go in.
+ Write directly to file.
+ */
+ if (dont_write)
+ {
+ /* Used in the server. */
+ keycache->global_cache_write++;
+ mysql_mutex_unlock(&keycache->cache_lock);
+ if (my_pwrite(file, (uchar*) buff, read_length, filepos + offset,
+ MYF(MY_NABP | MY_WAIT_IF_FULL)))
+ error=1;
+ mysql_mutex_lock(&keycache->cache_lock);
+ }
+ goto next_block;
+ }
+ /*
+ Prevent block from flushing and from being selected for to be
+ freed. This must be set when we release the cache_lock.
+ However, we must not set the status of the block before it is
+ assigned to this file/pos.
+ */
+ if (page_st != PAGE_WAIT_TO_BE_READ)
+ block->status|= BLOCK_FOR_UPDATE;
+ /*
+ We must read the file block first if it is not yet in the cache
+ and we do not replace all of its contents.
+
+ In cases where the cache block is big enough to contain (parts
+ of) index blocks of different indexes, our request can be
+ secondary (PAGE_WAIT_TO_BE_READ). In this case another thread is
+ reading the file block. If the read completes after us, it
+ overwrites our new contents with the old contents. So we have to
+ wait for the other thread to complete the read of this block.
+ read_block() takes care for the wait.
+ */
+ if (!(block->status & BLOCK_ERROR) &&
+ ((page_st == PAGE_TO_BE_READ &&
+ (offset || read_length < keycache->key_cache_block_size)) ||
+ (page_st == PAGE_WAIT_TO_BE_READ)))
+ {
+ read_block(keycache, thread_var, block,
+ offset + read_length >= keycache->key_cache_block_size?
+ offset : keycache->key_cache_block_size,
+ offset, (page_st == PAGE_TO_BE_READ));
+ DBUG_ASSERT(keycache->can_be_used);
+ DBUG_ASSERT(block->status & (BLOCK_READ | BLOCK_IN_USE));
+ /*
+ Prevent block from flushing and from being selected for to be
+ freed. This must be set when we release the cache_lock.
+ Here we set it in case we could not set it above.
+ */
+ block->status|= BLOCK_FOR_UPDATE;
+ }
+ /*
+ The block should always be assigned to the requested file block
+ here. It need not be BLOCK_READ when overwriting the whole block.
+ */
+ DBUG_ASSERT(block->hash_link->file == file);
+ DBUG_ASSERT(block->hash_link->diskpos == filepos);
+ DBUG_ASSERT(block->status & BLOCK_IN_USE);
+ DBUG_ASSERT((page_st == PAGE_TO_BE_READ) || (block->status & BLOCK_READ));
+ /*
+ The block to be written must not be marked BLOCK_REASSIGNED.
+ Otherwise it could be freed in dirty state or reused without
+ another flush during eviction. It must also not be in flush.
+ Otherwise the old contens may have been flushed already and
+ the flusher could clear BLOCK_CHANGED without flushing the
+ new changes again.
+ */
+ DBUG_ASSERT(!(block->status & BLOCK_REASSIGNED));
+
+ while (block->status & BLOCK_IN_FLUSHWRITE)
+ {
+ /*
+ Another thread is flushing the block. It was dirty already.
+ Wait until the block is flushed to file. Otherwise we could
+ modify the buffer contents just while it is written to file.
+ An unpredictable file block contents would be the result.
+ While we wait, several things can happen to the block,
+ including another flush. But the block cannot be reassigned to
+ another hash_link until we release our request on it.
+ */
+ wait_on_queue(&block->wqueue[COND_FOR_SAVED], &keycache->cache_lock,
+ thread_var);
+ DBUG_ASSERT(keycache->can_be_used);
+ DBUG_ASSERT(block->status & (BLOCK_READ | BLOCK_IN_USE));
+ /* Still must not be marked for free. */
+ DBUG_ASSERT(!(block->status & BLOCK_REASSIGNED));
+ DBUG_ASSERT(block->hash_link && (block->hash_link->block == block));
+ }
+
+ /*
+ We could perhaps release the cache_lock during access of the
+ data like in the other functions. Locks outside of the key cache
+ assure that readers and a writer do not access the same range of
+ data. Parallel accesses should happen only if the cache block
+ contains multiple index block(fragment)s. So different parts of
+ the buffer would be read/written. An attempt to flush during
+ memcpy() is prevented with BLOCK_FOR_UPDATE.
+ */
+ if (!(block->status & BLOCK_ERROR))
+ {
+ mysql_mutex_unlock(&keycache->cache_lock);
+ memcpy(block->buffer+offset, buff, (size_t) read_length);
+
+ mysql_mutex_lock(&keycache->cache_lock);
+ }
+
+ if (!dont_write)
+ {
+ /* Not used in the server. buff has been written to disk at start. */
+ if ((block->status & BLOCK_CHANGED) &&
+ (!offset && read_length >= keycache->key_cache_block_size))
+ link_to_file_list(keycache, block, block->hash_link->file, 1);
+ }
+ else if (! (block->status & BLOCK_CHANGED))
+ link_to_changed_list(keycache, block);
+ block->status|=BLOCK_READ;
+ /*
+ Allow block to be selected for to be freed. Since it is marked
+ BLOCK_CHANGED too, it won't be selected for to be freed without
+ a flush.
+ */
+ block->status&= ~BLOCK_FOR_UPDATE;
+ set_if_smaller(block->offset, offset);
+ set_if_bigger(block->length, read_length+offset);
+
+ /* Threads may be waiting for the changes to be complete. */
+ release_whole_queue(&block->wqueue[COND_FOR_REQUESTED]);
+
+ /*
+ If only a part of the cache block is to be replaced, and the
+ rest has been read from file, then the cache lock has been
+ released for I/O and it could be possible that another thread
+ wants to evict or free the block and waits for it to be
+ released. So we must not just decrement hash_link->requests, but
+ also wake a waiting thread.
+ */
+ remove_reader(block);
+
+ /* Error injection for coverage testing. */
+ DBUG_EXECUTE_IF("key_cache_write_block_error",
+ block->status|= BLOCK_ERROR;);
+
+ /* Do not link erroneous blocks into the LRU ring, but free them. */
+ if (!(block->status & BLOCK_ERROR))
+ {
+ /*
+ Link the block into the LRU ring if it's the last submitted
+ request for the block. This enables eviction for the block.
+ */
+ unreg_request(keycache, block, 1);
+ }
+ else
+ {
+ /* Pretend a "clean" block to avoid complications. */
+ block->status&= ~(BLOCK_CHANGED);
+ free_block(keycache, thread_var, block);
+ error= 1;
+ break;
+ }
+
+ next_block:
+ buff+= read_length;
+ filepos+= read_length+offset;
+ offset= 0;
+
+ } while ((length-= read_length));
+ goto end;
+ }
+
+no_key_cache:
+ /* Key cache is not used */
+ if (dont_write)
+ {
+ /* Used in the server. */
+ keycache->global_cache_w_requests++;
+ keycache->global_cache_write++;
+ if (locked_and_incremented)
+ mysql_mutex_unlock(&keycache->cache_lock);
+ if (my_pwrite(file, (uchar*) buff, length, filepos,
+ MYF(MY_NABP | MY_WAIT_IF_FULL)))
+ error=1;
+ if (locked_and_incremented)
+ mysql_mutex_lock(&keycache->cache_lock);
+ }
+
+end:
+ if (locked_and_incremented)
+ {
+ dec_counter_for_resize_op(keycache);
+ mysql_mutex_unlock(&keycache->cache_lock);
+ }
+
+ if (MYSQL_KEYCACHE_WRITE_DONE_ENABLED())
+ {
+ MYSQL_KEYCACHE_WRITE_DONE((ulong) (keycache->blocks_used *
+ keycache->key_cache_block_size),
+ (ulong) (keycache->blocks_unused *
+ keycache->key_cache_block_size));
+ }
+
+ DBUG_RETURN(error);
+}
+
+
+/*
+ Free block.
+
+ SYNOPSIS
+ free_block()
+ keycache Pointer to a key cache data structure
+ thread_var Pointer to thread specific variables
+ block Pointer to the block to free
+
+ DESCRIPTION
+ Remove reference to block from hash table.
+ Remove block from the chain of clean blocks.
+ Add block to the free list.
+
+ NOTE
+ Block must not be free (status == 0).
+ Block must not be in free_block_list.
+ Block must not be in the LRU ring.
+ Block must not be in eviction (BLOCK_IN_EVICTION | BLOCK_IN_SWITCH).
+ Block must not be in free (BLOCK_REASSIGNED).
+ Block must not be in flush (BLOCK_IN_FLUSH).
+ Block must not be dirty (BLOCK_CHANGED).
+ Block must not be in changed_blocks (dirty) hash.
+ Block must be in file_blocks (clean) hash.
+ Block must refer to a hash_link.
+ Block must have a request registered on it.
+*/
+
+static void free_block(KEY_CACHE *keycache,
+ st_keycache_thread_var *thread_var,
+ BLOCK_LINK *block)
+{
+ /*
+ Assert that the block is not free already. And that it is in a clean
+ state. Note that the block might just be assigned to a hash_link and
+ not yet read (BLOCK_READ may not be set here). In this case a reader
+ is registered in the hash_link and free_block() will wait for it
+ below.
+ */
+ DBUG_ASSERT((block->status & BLOCK_IN_USE) &&
+ !(block->status & (BLOCK_IN_EVICTION | BLOCK_IN_SWITCH |
+ BLOCK_REASSIGNED | BLOCK_IN_FLUSH |
+ BLOCK_CHANGED | BLOCK_FOR_UPDATE)));
+ /* Assert that the block is in a file_blocks chain. */
+ DBUG_ASSERT(block->prev_changed && *block->prev_changed == block);
+ /* Assert that the block is not in the LRU ring. */
+ DBUG_ASSERT(!block->next_used && !block->prev_used);
+ /*
+ IMHO the below condition (if()) makes no sense. I can't see how it
+ could be possible that free_block() is entered with a NULL hash_link
+ pointer. The only place where it can become NULL is in free_block()
+ (or before its first use ever, but for those blocks free_block() is
+ not called). I don't remove the conditional as it cannot harm, but
+ place an DBUG_ASSERT to confirm my hypothesis. Eventually the
+ condition (if()) can be removed.
+ */
+ DBUG_ASSERT(block->hash_link && block->hash_link->block == block);
+ if (block->hash_link)
+ {
+ /*
+ While waiting for readers to finish, new readers might request the
+ block. But since we set block->status|= BLOCK_REASSIGNED, they
+ will wait on block->wqueue[COND_FOR_SAVED]. They must be signalled
+ later.
+ */
+ block->status|= BLOCK_REASSIGNED;
+ wait_for_readers(keycache, block, thread_var);
+ /*
+ The block must not have been freed by another thread. Repeat some
+ checks. An additional requirement is that it must be read now
+ (BLOCK_READ).
+ */
+ DBUG_ASSERT(block->hash_link && block->hash_link->block == block);
+ DBUG_ASSERT((block->status & (BLOCK_READ | BLOCK_IN_USE |
+ BLOCK_REASSIGNED)) &&
+ !(block->status & (BLOCK_IN_EVICTION | BLOCK_IN_SWITCH |
+ BLOCK_IN_FLUSH | BLOCK_CHANGED |
+ BLOCK_FOR_UPDATE)));
+ DBUG_ASSERT(block->prev_changed && *block->prev_changed == block);
+ DBUG_ASSERT(!block->prev_used);
+ /*
+ Unset BLOCK_REASSIGNED again. If we hand the block to an evicting
+ thread (through unreg_request() below), other threads must not see
+ this flag. They could become confused.
+ */
+ block->status&= ~BLOCK_REASSIGNED;
+ /*
+ Do not release the hash_link until the block is off all lists.
+ At least not if we hand it over for eviction in unreg_request().
+ */
+ }
+
+ /*
+ Unregister the block request and link the block into the LRU ring.
+ This enables eviction for the block. If the LRU ring was empty and
+ threads are waiting for a block, then the block wil be handed over
+ for eviction immediately. Otherwise we will unlink it from the LRU
+ ring again, without releasing the lock in between. So decrementing
+ the request counter and updating statistics are the only relevant
+ operation in this case. Assert that there are no other requests
+ registered.
+ */
+ DBUG_ASSERT(block->requests == 1);
+ unreg_request(keycache, block, 0);
+ /*
+ Note that even without releasing the cache lock it is possible that
+ the block is immediately selected for eviction by link_block() and
+ thus not added to the LRU ring. In this case we must not touch the
+ block any more.
+ */
+ if (block->status & BLOCK_IN_EVICTION)
+ return;
+
+ /* Error blocks are not put into the LRU ring. */
+ if (!(block->status & BLOCK_ERROR))
+ {
+ /* Here the block must be in the LRU ring. Unlink it again. */
+ DBUG_ASSERT(block->next_used && block->prev_used &&
+ *block->prev_used == block);
+ unlink_block(keycache, block);
+ }
+ if (block->temperature == BLOCK_WARM)
+ keycache->warm_blocks--;
+ block->temperature= BLOCK_COLD;
+
+ /* Remove from file_blocks hash. */
+ unlink_changed(block);
+
+ /* Remove reference to block from hash table. */
+ unlink_hash(keycache, block->hash_link);
+ block->hash_link= NULL;
+
+ block->status= 0;
+ block->length= 0;
+ block->offset= keycache->key_cache_block_size;
+
+ /* Enforced by unlink_changed(), but just to be sure. */
+ DBUG_ASSERT(!block->next_changed && !block->prev_changed);
+ /* Enforced by unlink_block(): not in LRU ring nor in free_block_list. */
+ DBUG_ASSERT(!block->next_used && !block->prev_used);
+ /* Insert the free block in the free list. */
+ block->next_used= keycache->free_block_list;
+ keycache->free_block_list= block;
+ /* Keep track of the number of currently unused blocks. */
+ keycache->blocks_unused++;
+
+ /* All pending requests for this page must be resubmitted. */
+ release_whole_queue(&block->wqueue[COND_FOR_SAVED]);
+}
+
+
+static int cmp_sec_link(BLOCK_LINK **a, BLOCK_LINK **b)
+{
+ return (((*a)->hash_link->diskpos < (*b)->hash_link->diskpos) ? -1 :
+ ((*a)->hash_link->diskpos > (*b)->hash_link->diskpos) ? 1 : 0);
+}
+
+
+/*
+ Flush a portion of changed blocks to disk,
+ free used blocks if requested
+*/
+
+static int flush_cached_blocks(KEY_CACHE *keycache,
+ st_keycache_thread_var *thread_var,
+ File file, BLOCK_LINK **cache,
+ BLOCK_LINK **end,
+ enum flush_type type)
+{
+ int error;
+ int last_errno= 0;
+ uint count= (uint) (end-cache);
+
+ /* Don't lock the cache during the flush */
+ mysql_mutex_unlock(&keycache->cache_lock);
+ /*
+ As all blocks referred in 'cache' are marked by BLOCK_IN_FLUSH
+ we are guarunteed no thread will change them
+ */
+ my_qsort((uchar*) cache, count, sizeof(*cache), (qsort_cmp) cmp_sec_link);
+
+ mysql_mutex_lock(&keycache->cache_lock);
+ /*
+ Note: Do not break the loop. We have registered a request on every
+ block in 'cache'. These must be unregistered by free_block() or
+ unreg_request().
+ */
+ for ( ; cache != end ; cache++)
+ {
+ BLOCK_LINK *block= *cache;
+
+ /*
+ If the block contents is going to be changed, we abandon the flush
+ for this block. flush_key_blocks_int() will restart its search and
+ handle the block properly.
+ */
+ if (!(block->status & BLOCK_FOR_UPDATE))
+ {
+ /* Blocks coming here must have a certain status. */
+ DBUG_ASSERT(block->hash_link);
+ DBUG_ASSERT(block->hash_link->block == block);
+ DBUG_ASSERT(block->hash_link->file == file);
+ DBUG_ASSERT((block->status & ~BLOCK_IN_EVICTION) ==
+ (BLOCK_READ | BLOCK_IN_FLUSH | BLOCK_CHANGED | BLOCK_IN_USE));
+ block->status|= BLOCK_IN_FLUSHWRITE;
+ mysql_mutex_unlock(&keycache->cache_lock);
+ error= (int)my_pwrite(file, block->buffer+block->offset,
+ block->length - block->offset,
+ block->hash_link->diskpos+ block->offset,
+ MYF(MY_NABP | MY_WAIT_IF_FULL));
+ mysql_mutex_lock(&keycache->cache_lock);
+ keycache->global_cache_write++;
+ if (error)
+ {
+ block->status|= BLOCK_ERROR;
+ if (!last_errno)
+ last_errno= errno ? errno : -1;
+ }
+ block->status&= ~BLOCK_IN_FLUSHWRITE;
+ /* Block must not have changed status except BLOCK_FOR_UPDATE. */
+ DBUG_ASSERT(block->hash_link);
+ DBUG_ASSERT(block->hash_link->block == block);
+ DBUG_ASSERT(block->hash_link->file == file);
+ DBUG_ASSERT((block->status & ~(BLOCK_FOR_UPDATE | BLOCK_IN_EVICTION)) ==
+ (BLOCK_READ | BLOCK_IN_FLUSH | BLOCK_CHANGED | BLOCK_IN_USE));
+ /*
+ Set correct status and link in right queue for free or later use.
+ free_block() must not see BLOCK_CHANGED and it may need to wait
+ for readers of the block. These should not see the block in the
+ wrong hash. If not freeing the block, we need to have it in the
+ right queue anyway.
+ */
+ link_to_file_list(keycache, block, file, 1);
+ }
+ block->status&= ~BLOCK_IN_FLUSH;
+ /*
+ Let to proceed for possible waiting requests to write to the block page.
+ It might happen only during an operation to resize the key cache.
+ */
+ release_whole_queue(&block->wqueue[COND_FOR_SAVED]);
+ /* type will never be FLUSH_IGNORE_CHANGED here */
+ if (!(type == FLUSH_KEEP || type == FLUSH_FORCE_WRITE) &&
+ !(block->status & (BLOCK_IN_EVICTION | BLOCK_IN_SWITCH |
+ BLOCK_FOR_UPDATE)))
+ {
+ /*
+ Note that a request has been registered against the block in
+ flush_key_blocks_int().
+ */
+ free_block(keycache, thread_var, block);
+ }
+ else
+ {
+ /*
+ Link the block into the LRU ring if it's the last submitted
+ request for the block. This enables eviction for the block.
+ Note that a request has been registered against the block in
+ flush_key_blocks_int().
+ */
+ unreg_request(keycache, block, 1);
+ }
+
+ } /* end of for ( ; cache != end ; cache++) */
+ return last_errno;
+}
+
+
+/*
+ Flush all key blocks for a file to disk, but don't do any mutex locks.
+
+ SYNOPSIS
+ flush_key_blocks_int()
+ keycache pointer to a key cache data structure
+ thread_var pointer to thread specific variables
+ file handler for the file to flush to
+ flush_type type of the flush
+
+ NOTES
+ This function doesn't do any mutex locks because it needs to be called both
+ from flush_key_blocks and flush_all_key_blocks (the later one does the
+ mutex lock in the resize_key_cache() function).
+
+ We do only care about changed blocks that exist when the function is
+ entered. We do not guarantee that all changed blocks of the file are
+ flushed if more blocks change while this function is running.
+
+ RETURN
+ 0 ok
+ 1 error
+*/
+
+static int flush_key_blocks_int(KEY_CACHE *keycache,
+ st_keycache_thread_var *thread_var,
+ File file, enum flush_type type)
+{
+ BLOCK_LINK *cache_buff[FLUSH_CACHE],**cache;
+ int last_errno= 0;
+ int last_errcnt= 0;
+ DBUG_ENTER("flush_key_blocks_int");
+ DBUG_PRINT("enter",("file: %d blocks_used: %lu blocks_changed: %lu",
+ file, keycache->blocks_used, keycache->blocks_changed));
+
+ cache= cache_buff;
+ if (keycache->disk_blocks > 0)
+ {
+ /* Key cache exists and flush is not disabled */
+ int error= 0;
+ uint count= FLUSH_CACHE;
+ BLOCK_LINK **pos,**end;
+ BLOCK_LINK *first_in_switch= NULL;
+ BLOCK_LINK *last_in_flush;
+ BLOCK_LINK *last_for_update;
+ BLOCK_LINK *block, *next;
+#ifndef DBUG_OFF
+ uint cnt=0;
+#endif
+
+ if (type != FLUSH_IGNORE_CHANGED)
+ {
+ /*
+ Count how many key blocks we have to cache to be able
+ to flush all dirty pages with minimum seek moves
+ */
+ count= 0;
+ for (block= keycache->changed_blocks[FILE_HASH(file)] ;
+ block ;
+ block= block->next_changed)
+ {
+ if ((block->hash_link->file == file) &&
+ !(block->status & BLOCK_IN_FLUSH))
+ {
+ count++;
+ DBUG_ASSERT(count<= keycache->blocks_used);
+ }
+ }
+ /*
+ Allocate a new buffer only if its bigger than the one we have.
+ Assure that we always have some entries for the case that new
+ changed blocks appear while we need to wait for something.
+ */
+ if ((count > FLUSH_CACHE) &&
+ !(cache= (BLOCK_LINK**) my_malloc(key_memory_KEY_CACHE,
+ sizeof(BLOCK_LINK*)*count,
+ MYF(0))))
+ cache= cache_buff;
+ /*
+ After a restart there could be more changed blocks than now.
+ So we should not let count become smaller than the fixed buffer.
+ */
+ if (cache == cache_buff)
+ count= FLUSH_CACHE;
+ }
+
+ /* Retrieve the blocks and write them to a buffer to be flushed */
+restart:
+ last_in_flush= NULL;
+ last_for_update= NULL;
+ end= (pos= cache)+count;
+ for (block= keycache->changed_blocks[FILE_HASH(file)] ;
+ block ;
+ block= next)
+ {
+#ifndef DBUG_OFF
+ cnt++;
+ DBUG_ASSERT(cnt <= keycache->blocks_used);
+#endif
+ next= block->next_changed;
+ if (block->hash_link->file == file)
+ {
+ if (!(block->status & (BLOCK_IN_FLUSH | BLOCK_FOR_UPDATE)))
+ {
+ /*
+ Note: The special handling of BLOCK_IN_SWITCH is obsolete
+ since we set BLOCK_IN_FLUSH if the eviction includes a
+ flush. It can be removed in a later version.
+ */
+ if (!(block->status & BLOCK_IN_SWITCH))
+ {
+ /*
+ We care only for the blocks for which flushing was not
+ initiated by another thread and which are not in eviction.
+ Registering a request on the block unlinks it from the LRU
+ ring and protects against eviction.
+ */
+ reg_requests(keycache, block, 1);
+ if (type != FLUSH_IGNORE_CHANGED)
+ {
+ /* It's not a temporary file */
+ if (pos == end)
+ {
+ /*
+ This should happen relatively seldom. Remove the
+ request because we won't do anything with the block
+ but restart and pick it again in the next iteration.
+ */
+ unreg_request(keycache, block, 0);
+ /*
+ This happens only if there is not enough
+ memory for the big block
+ */
+ if ((error= flush_cached_blocks(keycache, thread_var, file,
+ cache, end, type)))
+ {
+ /* Do not loop infinitely trying to flush in vain. */
+ if ((last_errno == error) && (++last_errcnt > 5))
+ goto err;
+ 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;
+ }
+ /*
+ 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 of flushing dirty file pages. We must not
+ set this flag before actually putting the block on the
+ write burst array called 'cache'.
+ */
+ block->status|= BLOCK_IN_FLUSH;
+ /* Add block to the array for a write burst. */
+ *pos++= block;
+ }
+ else
+ {
+ /* It's a temporary file */
+ DBUG_ASSERT(!(block->status & BLOCK_REASSIGNED));
+ /*
+ free_block() must not be called with BLOCK_CHANGED. Note
+ that we must not change the BLOCK_CHANGED flag outside of
+ link_to_file_list() so that it is always in the correct
+ queue and the *blocks_changed counters are correct.
+ */
+ link_to_file_list(keycache, block, file, 1);
+ if (!(block->status & (BLOCK_IN_EVICTION | BLOCK_IN_SWITCH)))
+ {
+ /* A request has been registered against the block above. */
+ free_block(keycache, thread_var, block);
+ }
+ else
+ {
+ /*
+ Link the block into the LRU ring if it's the last
+ submitted request for the block. This enables eviction
+ for the block. A request has been registered against
+ the block above.
+ */
+ unreg_request(keycache, block, 1);
+ }
+ }
+ }
+ else
+ {
+ /*
+ Link the block into a list of blocks 'in switch'.
+
+ WARNING: Here we introduce a place where a changed block
+ is not in the changed_blocks hash! This is acceptable for
+ a BLOCK_IN_SWITCH. Never try this for another situation.
+ Other parts of the key cache code rely on changed blocks
+ being in the changed_blocks hash.
+ */
+ unlink_changed(block);
+ link_changed(block, &first_in_switch);
+ }
+ }
+ else if (type != FLUSH_KEEP)
+ {
+ /*
+ During the normal flush at end of statement (FLUSH_KEEP) we
+ do not need to ensure that blocks in flush or update by
+ other threads are flushed. They will be flushed by them
+ later. In all other cases we must assure that we do not have
+ any changed block of this file in the cache when this
+ function returns.
+ */
+ if (block->status & BLOCK_IN_FLUSH)
+ {
+ /* Remember the last block found to be in flush. */
+ last_in_flush= block;
+ }
+ else
+ {
+ /* Remember the last block found to be selected for update. */
+ last_for_update= block;
+ }
+ }
+ }
+ }
+ if (pos != cache)
+ {
+ if ((error=
+ flush_cached_blocks(keycache, thread_var, file, cache, pos, type)))
+ {
+ /* Do not loop inifnitely trying to flush in vain. */
+ if ((last_errno == error) && (++last_errcnt > 5))
+ goto err;
+ last_errno= error;
+ }
+ /*
+ Do not restart here during the normal flush at end of statement
+ (FLUSH_KEEP). We have now flushed at least all blocks that were
+ changed when entering this function. In all other cases we must
+ assure that we do not have any changed block of this file in the
+ cache when this function returns.
+ */
+ if (type != FLUSH_KEEP)
+ goto restart;
+ }
+ if (last_in_flush)
+ {
+ /*
+ There are no blocks to be flushed by this thread, but blocks in
+ flush by other threads. Wait until one of the blocks is flushed.
+ Re-check the condition for last_in_flush. We may have unlocked
+ the cache_lock in flush_cached_blocks(). The state of the block
+ could have changed.
+ */
+ if (last_in_flush->status & BLOCK_IN_FLUSH)
+ wait_on_queue(&last_in_flush->wqueue[COND_FOR_SAVED],
+ &keycache->cache_lock, thread_var);
+ /* Be sure not to lose a block. They may be flushed in random order. */
+ goto restart;
+ }
+ if (last_for_update)
+ {
+ /*
+ There are no blocks to be flushed by this thread, but blocks for
+ update by other threads. Wait until one of the blocks is updated.
+ Re-check the condition for last_for_update. We may have unlocked
+ the cache_lock in flush_cached_blocks(). The state of the block
+ could have changed.
+ */
+ if (last_for_update->status & BLOCK_FOR_UPDATE)
+ wait_on_queue(&last_for_update->wqueue[COND_FOR_REQUESTED],
+ &keycache->cache_lock, thread_var);
+ /* The block is now changed. Flush it. */
+ goto restart;
+ }
+
+ /*
+ Wait until the list of blocks in switch is empty. The threads that
+ are switching these blocks will relink them to clean file chains
+ while we wait and thus empty the 'first_in_switch' chain.
+ */
+ while (first_in_switch)
+ {
+#ifndef DBUG_OFF
+ cnt= 0;
+#endif
+ wait_on_queue(&first_in_switch->wqueue[COND_FOR_SAVED],
+ &keycache->cache_lock, thread_var);
+#ifndef DBUG_OFF
+ cnt++;
+ DBUG_ASSERT(cnt <= keycache->blocks_used);
+#endif
+ /*
+ Do not restart here. We have flushed all blocks that were
+ changed when entering this function and were not marked for
+ eviction. Other threads have now flushed all remaining blocks in
+ the course of their eviction.
+ */
+ }
+
+ if (! (type == FLUSH_KEEP || type == FLUSH_FORCE_WRITE))
+ {
+ BLOCK_LINK *last_for_update= NULL;
+ BLOCK_LINK *last_in_switch= NULL;
+ uint total_found= 0;
+ uint found;
+
+ /*
+ Finally free all clean blocks for this file.
+ During resize this may be run by two threads in parallel.
+ */
+ do
+ {
+ found= 0;
+ for (block= keycache->file_blocks[FILE_HASH(file)] ;
+ block ;
+ block= next)
+ {
+ /* Remember the next block. After freeing we cannot get at it. */
+ next= block->next_changed;
+
+ /* Changed blocks cannot appear in the file_blocks hash. */
+ DBUG_ASSERT(!(block->status & BLOCK_CHANGED));
+ if (block->hash_link->file == file)
+ {
+ /* We must skip blocks that will be changed. */
+ if (block->status & BLOCK_FOR_UPDATE)
+ {
+ last_for_update= block;
+ continue;
+ }
+
+ /*
+ We must not free blocks in eviction (BLOCK_IN_EVICTION |
+ BLOCK_IN_SWITCH) or blocks intended to be freed
+ (BLOCK_REASSIGNED).
+ */
+ if (!(block->status & (BLOCK_IN_EVICTION | BLOCK_IN_SWITCH |
+ BLOCK_REASSIGNED)))
+ {
+ struct st_hash_link *next_hash_link= NULL;
+ my_off_t next_diskpos= 0;
+ File next_file= 0;
+ uint next_status= 0;
+ uint hash_requests= 0;
+
+ total_found++;
+ found++;
+ DBUG_ASSERT(found <= keycache->blocks_used);
+
+ /*
+ Register a request. This unlinks the block from the LRU
+ ring and protects it against eviction. This is required
+ by free_block().
+ */
+ reg_requests(keycache, block, 1);
+
+ /*
+ free_block() may need to wait for readers of the block.
+ This is the moment where the other thread can move the
+ 'next' block from the chain. free_block() needs to wait
+ if there are requests for the block pending.
+ */
+ if (next && (hash_requests= block->hash_link->requests))
+ {
+ /* Copy values from the 'next' block and its hash_link. */
+ next_status= next->status;
+ next_hash_link= next->hash_link;
+ next_diskpos= next_hash_link->diskpos;
+ next_file= next_hash_link->file;
+ DBUG_ASSERT(next == next_hash_link->block);
+ }
+
+ free_block(keycache, thread_var, block);
+ /*
+ If we had to wait and the state of the 'next' block
+ changed, break the inner loop. 'next' may no longer be
+ part of the current chain.
+
+ We do not want to break the loop after every free_block(),
+ not even only after waits. The chain might be quite long
+ and contain blocks for many files. Traversing it again and
+ again to find more blocks for this file could become quite
+ inefficient.
+ */
+ if (next && hash_requests &&
+ ((next_status != next->status) ||
+ (next_hash_link != next->hash_link) ||
+ (next_file != next_hash_link->file) ||
+ (next_diskpos != next_hash_link->diskpos) ||
+ (next != next_hash_link->block)))
+ break;
+ }
+ else
+ {
+ last_in_switch= block;
+ }
+ }
+ } /* end for block in file_blocks */
+ } while (found);
+
+ /*
+ If any clean block has been found, we may have waited for it to
+ become free. In this case it could be possible that another clean
+ block became dirty. This is possible if the write request existed
+ before the flush started (BLOCK_FOR_UPDATE). Re-check the hashes.
+ */
+ if (total_found)
+ goto restart;
+
+ /*
+ To avoid an infinite loop, wait until one of the blocks marked
+ for update is updated.
+ */
+ if (last_for_update)
+ {
+ /* We did not wait. Block must not have changed status. */
+ DBUG_ASSERT(last_for_update->status & BLOCK_FOR_UPDATE);
+ wait_on_queue(&last_for_update->wqueue[COND_FOR_REQUESTED],
+ &keycache->cache_lock, thread_var);
+ goto restart;
+ }
+
+ /*
+ To avoid an infinite loop wait until one of the blocks marked
+ for eviction is switched.
+ */
+ if (last_in_switch)
+ {
+ /* We did not wait. Block must not have changed status. */
+ DBUG_ASSERT(last_in_switch->status & (BLOCK_IN_EVICTION |
+ BLOCK_IN_SWITCH |
+ BLOCK_REASSIGNED));
+ wait_on_queue(&last_in_switch->wqueue[COND_FOR_SAVED],
+ &keycache->cache_lock, thread_var);
+ goto restart;
+ }
+
+ } /* if (! (type == FLUSH_KEEP || type == FLUSH_FORCE_WRITE)) */
+
+ } /* if (keycache->disk_blocks > 0 */
+
+err:
+ if (cache != cache_buff)
+ my_free(cache);
+ if (last_errno)
+ errno=last_errno; /* Return first error */
+ DBUG_RETURN(last_errno != 0);
+}
+
+
+/*
+ Flush all blocks for a file to disk
+
+ SYNOPSIS
+
+ flush_key_blocks()
+ keycache pointer to a key cache data structure
+ thread_var pointer to thread specific variables
+ file handler for the file to flush to
+ flush_type type of the flush
+
+ RETURN
+ 0 ok
+ 1 error
+*/
+
+int flush_key_blocks(KEY_CACHE *keycache,
+ st_keycache_thread_var *thread_var,
+ File file, enum flush_type type)
+{
+ int res= 0;
+ DBUG_ENTER("flush_key_blocks");
+ DBUG_PRINT("enter", ("keycache: 0x%lx", (long) keycache));
+
+ if (!keycache->key_cache_inited)
+ DBUG_RETURN(0);
+
+ mysql_mutex_lock(&keycache->cache_lock);
+ /* While waiting for lock, keycache could have been ended. */
+ if (keycache->disk_blocks > 0)
+ {
+ inc_counter_for_resize_op(keycache);
+ res= flush_key_blocks_int(keycache, thread_var, file, type);
+ dec_counter_for_resize_op(keycache);
+ }
+ mysql_mutex_unlock(&keycache->cache_lock);
+ DBUG_RETURN(res);
+}
+
+
+/*
+ Flush all blocks in the key cache to disk.
+
+ SYNOPSIS
+ flush_all_key_blocks()
+ keycache pointer to key cache root structure
+ thread_var pointer to thread specific variables
+
+ DESCRIPTION
+
+ Flushing of the whole key cache is done in two phases.
+
+ 1. Flush all changed blocks, waiting for them if necessary. Loop
+ until there is no changed block left in the cache.
+
+ 2. Free all clean blocks. Normally this means free all blocks. The
+ changed blocks were flushed in phase 1 and became clean. However we
+ may need to wait for blocks that are read by other threads. While we
+ wait, a clean block could become changed if that operation started
+ before the resize operation started. To be safe we must restart at
+ phase 1.
+
+ When we can run through the changed_blocks and file_blocks hashes
+ without finding a block any more, then we are done.
+
+ Note that we hold keycache->cache_lock all the time unless we need
+ to wait for something.
+
+ RETURN
+ 0 OK
+ != 0 Error
+*/
+
+static int flush_all_key_blocks(KEY_CACHE *keycache,
+ st_keycache_thread_var *thread_var)
+{
+ BLOCK_LINK *block;
+ uint total_found;
+ uint found;
+ uint idx;
+ DBUG_ENTER("flush_all_key_blocks");
+
+ do
+ {
+ mysql_mutex_assert_owner(&keycache->cache_lock);
+ total_found= 0;
+
+ /*
+ Phase1: Flush all changed blocks, waiting for them if necessary.
+ Loop until there is no changed block left in the cache.
+ */
+ do
+ {
+ found= 0;
+ /* Step over the whole changed_blocks hash array. */
+ for (idx= 0; idx < CHANGED_BLOCKS_HASH; idx++)
+ {
+ /*
+ If an array element is non-empty, use the first block from its
+ chain to find a file for flush. All changed blocks for this
+ file are flushed. So the same block will not appear at this
+ place again with the next iteration. New writes for blocks are
+ not accepted during the flush. If multiple files share the
+ same hash bucket, one of them will be flushed per iteration
+ of the outer loop of phase 1.
+ */
+ if ((block= keycache->changed_blocks[idx]))
+ {
+ found++;
+ /*
+ Flush dirty blocks but do not free them yet. They can be used
+ for reading until all other blocks are flushed too.
+ */
+ if (flush_key_blocks_int(keycache, thread_var,
+ block->hash_link->file,
+ FLUSH_FORCE_WRITE))
+ DBUG_RETURN(1);
+ }
+ }
+
+ } while (found);
+
+ /*
+ Phase 2: Free all clean blocks. Normally this means free all
+ blocks. The changed blocks were flushed in phase 1 and became
+ clean. However we may need to wait for blocks that are read by
+ other threads. While we wait, a clean block could become changed
+ if that operation started before the resize operation started. To
+ be safe we must restart at phase 1.
+ */
+ do
+ {
+ found= 0;
+ /* Step over the whole file_blocks hash array. */
+ for (idx= 0; idx < CHANGED_BLOCKS_HASH; idx++)
+ {
+ /*
+ If an array element is non-empty, use the first block from its
+ chain to find a file for flush. All blocks for this file are
+ freed. So the same block will not appear at this place again
+ with the next iteration. If multiple files share the
+ same hash bucket, one of them will be flushed per iteration
+ of the outer loop of phase 2.
+ */
+ if ((block= keycache->file_blocks[idx]))
+ {
+ total_found++;
+ found++;
+ if (flush_key_blocks_int(keycache, thread_var,
+ block->hash_link->file,
+ FLUSH_RELEASE))
+ DBUG_RETURN(1);
+ }
+ }
+
+ } while (found);
+
+ /*
+ If any clean block has been found, we may have waited for it to
+ become free. In this case it could be possible that another clean
+ block became dirty. This is possible if the write request existed
+ before the resize started (BLOCK_FOR_UPDATE). Re-check the hashes.
+ */
+ } while (total_found);
+
+#ifndef DBUG_OFF
+ /* Now there should not exist any block any more. */
+ for (idx= 0; idx < CHANGED_BLOCKS_HASH; idx++)
+ {
+ DBUG_ASSERT(!keycache->changed_blocks[idx]);
+ DBUG_ASSERT(!keycache->file_blocks[idx]);
+ }
+#endif
+
+ DBUG_RETURN(0);
+}
+
+
+/*
+ Reset the counters of a key cache.
+
+ SYNOPSIS
+ reset_key_cache_counters()
+ name the name of a key cache
+ key_cache pointer to the key kache to be reset
+
+ DESCRIPTION
+ This procedure is used by process_key_caches() to reset the counters of all
+ currently used key caches, both the default one and the named ones.
+
+ RETURN
+ 0 on success (always because it can't fail)
+*/
+
+int reset_key_cache_counters(const char *name MY_ATTRIBUTE((unused)),
+ KEY_CACHE *key_cache)
+{
+ DBUG_ENTER("reset_key_cache_counters");
+ if (!key_cache->key_cache_inited)
+ {
+ DBUG_PRINT("info", ("Key cache %s not initialized.", name));
+ DBUG_RETURN(0);
+ }
+ DBUG_PRINT("info", ("Resetting counters for key cache %s.", name));
+
+ key_cache->global_blocks_changed= 0; /* Key_blocks_not_flushed */
+ key_cache->global_cache_r_requests= 0; /* Key_read_requests */
+ key_cache->global_cache_read= 0; /* Key_reads */
+ key_cache->global_cache_w_requests= 0; /* Key_write_requests */
+ key_cache->global_cache_write= 0; /* Key_writes */
+ DBUG_RETURN(0);
+}
+
+
+#if !defined(DBUG_OFF)
+#define F_B_PRT(_f_, _v_) DBUG_PRINT("assert_fail", (_f_, _v_))
+
+static int fail_block(BLOCK_LINK *block)
+{
+ F_B_PRT("block->next_used: %lx\n", (ulong) block->next_used);
+ F_B_PRT("block->prev_used: %lx\n", (ulong) block->prev_used);
+ F_B_PRT("block->next_changed: %lx\n", (ulong) block->next_changed);
+ F_B_PRT("block->prev_changed: %lx\n", (ulong) block->prev_changed);
+ F_B_PRT("block->hash_link: %lx\n", (ulong) block->hash_link);
+ F_B_PRT("block->status: %u\n", block->status);
+ F_B_PRT("block->length: %u\n", block->length);
+ F_B_PRT("block->offset: %u\n", block->offset);
+ F_B_PRT("block->requests: %u\n", block->requests);
+ F_B_PRT("block->temperature: %u\n", block->temperature);
+ return 0; /* Let the assert fail. */
+}
+
+static int fail_hlink(HASH_LINK *hlink)
+{
+ F_B_PRT("hlink->next: %lx\n", (ulong) hlink->next);
+ F_B_PRT("hlink->prev: %lx\n", (ulong) hlink->prev);
+ F_B_PRT("hlink->block: %lx\n", (ulong) hlink->block);
+ F_B_PRT("hlink->diskpos: %lu\n", (ulong) hlink->diskpos);
+ F_B_PRT("hlink->file: %d\n", hlink->file);
+ return 0; /* Let the assert fail. */
+}
+
+static int cache_empty(KEY_CACHE *keycache)
+{
+ int errcnt= 0;
+ int idx;
+ if (keycache->disk_blocks <= 0)
+ return 1;
+ for (idx= 0; idx < keycache->disk_blocks; idx++)
+ {
+ BLOCK_LINK *block= keycache->block_root + idx;
+ if (block->status || block->requests || block->hash_link)
+ {
+ my_message_local(INFORMATION_LEVEL, "block index: %u", idx);
+ fail_block(block);
+ errcnt++;
+ }
+ }
+ for (idx= 0; idx < keycache->hash_links; idx++)
+ {
+ HASH_LINK *hash_link= keycache->hash_link_root + idx;
+ if (hash_link->requests || hash_link->block)
+ {
+ my_message_local(INFORMATION_LEVEL, "hash_link index: %u", idx);
+ fail_hlink(hash_link);
+ errcnt++;
+ }
+ }
+ if (errcnt)
+ {
+ my_message_local(INFORMATION_LEVEL, "blocks: %d used: %lu",
+ keycache->disk_blocks, keycache->blocks_used);
+ my_message_local(INFORMATION_LEVEL, "hash_links: %d used: %d",
+ keycache->hash_links, keycache->hash_links_used);
+ }
+ return !errcnt;
+}
+#endif
+
diff --git a/mysql/mysys/mf_keycaches.c b/mysql/mysys/mf_keycaches.c
new file mode 100644
index 0000000..8622105
--- /dev/null
+++ b/mysql/mysys/mf_keycaches.c
@@ -0,0 +1,363 @@
+/* Copyright (c) 2003, 2016, Oracle and/or its affiliates. All rights reserved.
+
+ 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; version 2 of the License.
+
+ 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+/*
+ Handling of multiple key caches
+
+ The idea is to have a thread safe hash on the table name,
+ with a default key cache value that is returned if the table name is not in
+ the cache.
+*/
+
+#include "mysys_priv.h"
+#include <keycache.h>
+#include <hash.h>
+#include <m_string.h>
+
+/*****************************************************************************
+ General functions to handle SAFE_HASH objects.
+
+ A SAFE_HASH object is used to store the hash, the lock and default value
+ needed by the rest of the key cache code.
+ This is a separate struct to make it easy to later reuse the code for other
+ purposes
+
+ All entries are linked in a list to allow us to traverse all elements
+ and delete selected ones. (HASH doesn't allow any easy ways to do this).
+*****************************************************************************/
+
+/*
+ Struct to store a key and pointer to object
+*/
+
+typedef struct st_safe_hash_entry
+{
+ uchar *key;
+ uint length;
+ uchar *data;
+ struct st_safe_hash_entry *next, **prev;
+} SAFE_HASH_ENTRY;
+
+
+typedef struct st_safe_hash_with_default
+{
+ mysql_rwlock_t lock;
+ HASH hash;
+ uchar *default_value;
+ SAFE_HASH_ENTRY *root;
+} SAFE_HASH;
+
+
+/*
+ Free a SAFE_HASH_ENTRY
+
+ This function is called by the hash object on delete
+*/
+
+static void safe_hash_entry_free(SAFE_HASH_ENTRY *entry)
+{
+ DBUG_ENTER("free_assign_entry");
+ my_free(entry);
+ DBUG_VOID_RETURN;
+}
+
+
+/* Get key and length for a SAFE_HASH_ENTRY */
+
+static uchar *safe_hash_entry_get(SAFE_HASH_ENTRY *entry, size_t *length,
+ my_bool not_used MY_ATTRIBUTE((unused)))
+{
+ *length=entry->length;
+ return (uchar*) entry->key;
+}
+
+
+/*
+ Init a SAFE_HASH object
+
+ SYNOPSIS
+ safe_hash_init()
+ hash safe_hash handler
+ elements Expected max number of elements
+ default_value default value
+
+ NOTES
+ In case of error we set hash->default_value to 0 to allow one to call
+ safe_hash_free on an object that couldn't be initialized.
+
+ RETURN
+ 0 ok
+ 1 error
+*/
+
+static my_bool safe_hash_init(SAFE_HASH *hash, uint elements,
+ uchar *default_value)
+{
+ DBUG_ENTER("safe_hash");
+ if (my_hash_init(&hash->hash, &my_charset_bin, elements,
+ 0, 0, (my_hash_get_key) safe_hash_entry_get,
+ (void (*)(void*)) safe_hash_entry_free, 0,
+ key_memory_SAFE_HASH_ENTRY))
+ {
+ hash->default_value= 0;
+ DBUG_RETURN(1);
+ }
+ mysql_rwlock_init(key_SAFE_HASH_lock, &hash->lock);
+ hash->default_value= default_value;
+ hash->root= 0;
+ DBUG_RETURN(0);
+}
+
+
+/*
+ Free a SAFE_HASH object
+
+ NOTES
+ This is safe to call on any object that has been sent to safe_hash_init()
+*/
+
+static void safe_hash_free(SAFE_HASH *hash)
+{
+ /*
+ Test if safe_hash_init succeeded. This will also guard us against multiple
+ free calls.
+ */
+ if (hash->default_value)
+ {
+ my_hash_free(&hash->hash);
+ mysql_rwlock_destroy(&hash->lock);
+ hash->default_value=0;
+ }
+}
+
+/*
+ Return the value stored for a key or default value if no key
+*/
+
+static uchar *safe_hash_search(SAFE_HASH *hash, const uchar *key, uint length)
+{
+ uchar *result;
+ DBUG_ENTER("safe_hash_search");
+ mysql_rwlock_rdlock(&hash->lock);
+ result= my_hash_search(&hash->hash, key, length);
+ mysql_rwlock_unlock(&hash->lock);
+ if (!result)
+ result= hash->default_value;
+ else
+ result= ((SAFE_HASH_ENTRY*) result)->data;
+ DBUG_PRINT("exit",("data: 0x%lx", (long) result));
+ DBUG_RETURN(result);
+}
+
+
+/*
+ Associate a key with some data
+
+ SYONOPSIS
+ safe_hash_set()
+ hash Hash handle
+ key key (path to table etc..)
+ length Length of key
+ data data to to associate with the data
+
+ NOTES
+ This can be used both to insert a new entry and change an existing
+ entry.
+ If one associates a key with the default key cache, the key is deleted
+
+ RETURN
+ 0 ok
+ 1 error (Can only be EOM). In this case my_message() is called.
+*/
+
+static my_bool safe_hash_set(SAFE_HASH *hash, const uchar *key, uint length,
+ uchar *data)
+{
+ SAFE_HASH_ENTRY *entry;
+ my_bool error= 0;
+ DBUG_ENTER("safe_hash_set");
+ DBUG_PRINT("enter",("key: %.*s data: 0x%lx", length, key, (long) data));
+
+ mysql_rwlock_wrlock(&hash->lock);
+ entry= (SAFE_HASH_ENTRY*) my_hash_search(&hash->hash, key, length);
+
+ if (data == hash->default_value)
+ {
+ /*
+ The key is to be associated with the default entry. In this case
+ we can just delete the entry (if it existed) from the hash as a
+ search will return the default entry
+ */
+ if (!entry) /* nothing to do */
+ goto end;
+ /* unlink entry from list */
+ if ((*entry->prev= entry->next))
+ entry->next->prev= entry->prev;
+ my_hash_delete(&hash->hash, (uchar*) entry);
+ goto end;
+ }
+ if (entry)
+ {
+ /* Entry existed; Just change the pointer to point at the new data */
+ entry->data= data;
+ }
+ else
+ {
+ if (!(entry= (SAFE_HASH_ENTRY *) my_malloc(key_memory_SAFE_HASH_ENTRY,
+ sizeof(*entry) + length,
+ MYF(MY_WME))))
+ {
+ error= 1;
+ goto end;
+ }
+ entry->key= (uchar*) (entry +1);
+ memcpy((char*) entry->key, (char*) key, length);
+ entry->length= length;
+ entry->data= data;
+ /* Link entry to list */
+ if ((entry->next= hash->root))
+ entry->next->prev= &entry->next;
+ entry->prev= &hash->root;
+ hash->root= entry;
+ if (my_hash_insert(&hash->hash, (uchar*) entry))
+ {
+ /* This can only happen if hash got out of memory */
+ my_free(entry);
+ error= 1;
+ goto end;
+ }
+ }
+
+end:
+ mysql_rwlock_unlock(&hash->lock);
+ DBUG_RETURN(error);
+}
+
+
+/*
+ Change all entres with one data value to another data value
+
+ SYONOPSIS
+ safe_hash_change()
+ hash Hash handle
+ old_data Old data
+ new_data Change all 'old_data' to this
+
+ NOTES
+ We use the linked list to traverse all elements in the hash as
+ this allows us to delete elements in the case where 'new_data' is the
+ default value.
+*/
+
+static void safe_hash_change(SAFE_HASH *hash, uchar *old_data, uchar *new_data)
+{
+ SAFE_HASH_ENTRY *entry, *next;
+ DBUG_ENTER("safe_hash_set");
+
+ mysql_rwlock_wrlock(&hash->lock);
+
+ for (entry= hash->root ; entry ; entry= next)
+ {
+ next= entry->next;
+ if (entry->data == old_data)
+ {
+ if (new_data == hash->default_value)
+ {
+ if ((*entry->prev= entry->next))
+ entry->next->prev= entry->prev;
+ my_hash_delete(&hash->hash, (uchar*) entry);
+ }
+ else
+ entry->data= new_data;
+ }
+ }
+
+ mysql_rwlock_unlock(&hash->lock);
+ DBUG_VOID_RETURN;
+}
+
+
+/*****************************************************************************
+ Functions to handle the key cache objects
+*****************************************************************************/
+
+/* Variable to store all key cache objects */
+static SAFE_HASH key_cache_hash;
+
+
+my_bool multi_keycache_init(void)
+{
+ return safe_hash_init(&key_cache_hash, 16, (uchar*) dflt_key_cache);
+}
+
+
+void multi_keycache_free(void)
+{
+ safe_hash_free(&key_cache_hash);
+}
+
+/*
+ Get a key cache to be used for a specific table.
+
+ SYNOPSIS
+ multi_key_cache_search()
+ key key to find (usually table path)
+ uint length Length of key.
+
+ NOTES
+ This function is coded in such a way that we will return the
+ default key cache even if one never called multi_keycache_init.
+ This will ensure that it works with old MyISAM clients.
+
+ RETURN
+ key cache to use
+*/
+
+KEY_CACHE *multi_key_cache_search(uchar *key, uint length)
+{
+ if (!key_cache_hash.hash.records)
+ return dflt_key_cache;
+ return (KEY_CACHE*) safe_hash_search(&key_cache_hash, key, length);
+}
+
+
+/*
+ Assosiate a key cache with a key
+
+
+ SYONOPSIS
+ multi_key_cache_set()
+ key key (path to table etc..)
+ length Length of key
+ key_cache cache to assococite with the table
+
+ NOTES
+ This can be used both to insert a new entry and change an existing
+ entry
+*/
+
+
+my_bool multi_key_cache_set(const uchar *key, uint length,
+ KEY_CACHE *key_cache)
+{
+ return safe_hash_set(&key_cache_hash, key, length, (uchar*) key_cache);
+}
+
+
+void multi_key_cache_change(KEY_CACHE *old_data,
+ KEY_CACHE *new_data)
+{
+ safe_hash_change(&key_cache_hash, (uchar*) old_data, (uchar*) new_data);
+}
diff --git a/mysql/mysys/mf_loadpath.c b/mysql/mysys/mf_loadpath.c
new file mode 100644
index 0000000..41fb143
--- /dev/null
+++ b/mysql/mysys/mf_loadpath.c
@@ -0,0 +1,72 @@
+/* Copyright (c) 2000, 2015, Oracle and/or its affiliates. All rights reserved.
+
+ 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; version 2 of the License.
+
+ 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include "mysys_priv.h"
+#include "my_sys.h"
+#include <m_string.h>
+
+/**
+ Returns full load-path for a file. to may be = path.
+
+ @param to Pointer to destination which will hold the expanded path.
+ @param path Pointer to buffer containing the supplied path.
+ @param own_path_prefix Prefix to be appended to path.
+
+ @retval to Pointer to the supplied destination buffer
+ that will hold the full load-path.
+*/
+
+char * my_load_path(char * to, const char *path,
+ const char *own_path_prefix)
+{
+ char buff[FN_REFLEN];
+ int cur_prefix_len;
+ const char *buff_ptr= path;
+
+ DBUG_ENTER("my_load_path");
+ DBUG_PRINT("enter",("path: %s prefix: %s",path,
+ own_path_prefix ? own_path_prefix : ""));
+
+ cur_prefix_len= (path[0] == FN_CURLIB && path[1] == FN_LIBCHAR)?2:0;
+
+ // If path starts with current dir or parent-dir unpack path.
+ if ( cur_prefix_len || is_prefix(path, FN_PARENTDIR))
+ {
+ if ((strlen(path) + cur_prefix_len) < FN_REFLEN &&
+ !my_getwd(buff, (uint) (FN_REFLEN - strlen(path) + cur_prefix_len), MYF(0)))
+ {
+ (void) strncat(buff, path + cur_prefix_len, FN_REFLEN - strlen(buff) - 1);
+ buff_ptr= buff;
+ }
+ }
+ /*
+ Prepend the path with own_path_prefix if own_path_prefix is
+ specified and path doesn't start with home directory character
+ and path is not a hard-path.
+ If path is hard path or home dir, return the path.
+ */
+ else if (own_path_prefix != NULL &&
+ !(path[0] == FN_HOMELIB && path[1] == FN_LIBCHAR) &&
+ !test_if_hard_path(path))
+ {
+ (void) strxnmov(buff, sizeof(buff)-1, own_path_prefix, path, NullS);
+ buff_ptr= buff;
+ }
+
+ my_stpnmov(to, buff_ptr, FN_REFLEN);
+ to[FN_REFLEN-1]= '\0';
+ DBUG_PRINT("exit",("to: %s",to));
+ DBUG_RETURN(to);
+} /* my_load_path */
diff --git a/mysql/mysys/mf_pack.c b/mysql/mysys/mf_pack.c
new file mode 100644
index 0000000..bfdce0e
--- /dev/null
+++ b/mysql/mysys/mf_pack.c
@@ -0,0 +1,409 @@
+/* Copyright (c) 2000, 2015, Oracle and/or its affiliates. All rights reserved.
+
+ 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; version 2 of the License.
+
+ 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+#include "mysys_priv.h"
+#include "my_sys.h"
+#include <m_string.h>
+#ifdef HAVE_PWD_H
+#include <pwd.h>
+#endif
+
+static char * expand_tilde(char **path);
+
+ /* Pack a dirname ; Changes HOME to ~/ and current dev to ./ */
+ /* from is a dirname (from dirname() ?) ending with FN_LIBCHAR */
+ /* to may be == from */
+
+void pack_dirname(char * to, const char *from)
+{
+ int cwd_err;
+ size_t d_length, length, buff_length= 0;
+ char * start;
+ char buff[FN_REFLEN];
+ DBUG_ENTER("pack_dirname");
+
+ (void) intern_filename(to,from); /* Change to intern name */
+
+#ifdef FN_DEVCHAR
+ if ((start=strrchr(to,FN_DEVCHAR)) != 0) /* Skip device part */
+ start++;
+ else
+#endif
+ start=to;
+
+ if (!(cwd_err= my_getwd(buff,FN_REFLEN,MYF(0))))
+ {
+ buff_length= strlen(buff);
+ d_length= (size_t) (start-to);
+ if ((start == to ||
+ (buff_length == d_length && !memcmp(buff,start,d_length))) &&
+ *start != FN_LIBCHAR && *start)
+ { /* Put current dir before */
+ bchange((uchar*) to, d_length, (uchar*) buff, buff_length, strlen(to)+1);
+ }
+ }
+
+ if ((d_length= cleanup_dirname(to,to)) != 0)
+ {
+ length=0;
+ if (home_dir)
+ {
+ length= strlen(home_dir);
+ if (home_dir[length-1] == FN_LIBCHAR)
+ length--; /* Don't test last '/' */
+ }
+ if (length > 1 && length < d_length)
+ { /* test if /xx/yy -> ~/yy */
+ if (memcmp(to,home_dir,length) == 0 && to[length] == FN_LIBCHAR)
+ {
+ to[0]=FN_HOMELIB; /* Filename begins with ~ */
+ (void) my_stpmov(to+1,to+length);
+ }
+ }
+ if (! cwd_err)
+ { /* Test if cwd is ~/... */
+ if (length > 1 && length < buff_length)
+ {
+ if (memcmp(buff,home_dir,length) == 0 && buff[length] == FN_LIBCHAR)
+ {
+ buff[0]=FN_HOMELIB;
+ (void) my_stpmov(buff+1,buff+length);
+ }
+ }
+ if (is_prefix(to,buff))
+ {
+ length= strlen(buff);
+ if (to[length])
+ (void) my_stpmov(to,to+length); /* Remove everything before */
+ else
+ {
+ to[0]= FN_CURLIB; /* Put ./ instead of cwd */
+ to[1]= FN_LIBCHAR;
+ to[2]= '\0';
+ }
+ }
+ }
+ }
+ DBUG_PRINT("exit",("to: '%s'",to));
+ DBUG_VOID_RETURN;
+} /* pack_dirname */
+
+
+/*
+ remove unwanted chars from dirname
+
+ SYNOPSIS
+ cleanup_dirname()
+ to Store result here
+ from Dirname to fix. May be same as to
+
+ IMPLEMENTATION
+ "/../" removes prev dir
+ "/~/" removes all before ~
+ //" is same as "/", except on Win32 at start of a file
+ "/./" is removed
+ Unpacks home_dir if "~/.." used
+ Unpacks current dir if if "./.." used
+
+ RETURN
+ # length of new name
+*/
+
+size_t cleanup_dirname(char *to, const char *from)
+{
+ size_t length;
+ char *pos;
+ char *from_ptr;
+ char *start;
+ char parent[5], /* for "FN_PARENTDIR" */
+ buff[FN_REFLEN+1],*end_parentdir;
+#ifdef _WIN32
+ CHARSET_INFO *fs= fs_character_set();
+#endif
+ DBUG_ENTER("cleanup_dirname");
+ DBUG_PRINT("enter",("from: '%s'",from));
+
+ start=buff;
+ from_ptr=(char *) from;
+#ifdef FN_DEVCHAR
+ if ((pos=strrchr(from_ptr,FN_DEVCHAR)) != 0)
+ { /* Skip device part */
+ length=(size_t) (pos-from_ptr)+1;
+ start=my_stpnmov(buff,from_ptr,length); from_ptr+=length;
+ }
+#endif
+
+ parent[0]=FN_LIBCHAR;
+ length=(size_t) (my_stpcpy(parent+1,FN_PARENTDIR)-parent);
+ for (pos=start ; (*pos= *from_ptr++) != 0 ; pos++)
+ {
+#ifdef _WIN32
+ uint l;
+ if (use_mb(fs) && (l= my_ismbchar(fs, from_ptr - 1, from_ptr + 2)))
+ {
+ for (l-- ; l ; *++pos= *from_ptr++, l--);
+ start= pos + 1; /* Don't look inside multi-byte char */
+ continue;
+ }
+#endif
+ if (*pos == '/')
+ *pos = FN_LIBCHAR;
+ if (*pos == FN_LIBCHAR)
+ {
+ if ((size_t) (pos-start) > length && memcmp(pos-length,parent,length) == 0)
+ { /* If .../../; skip prev */
+ pos-=length;
+ if (pos != start)
+ { /* not /../ */
+ pos--;
+ if (*pos == FN_HOMELIB && (pos == start || pos[-1] == FN_LIBCHAR))
+ {
+ if (!home_dir)
+ {
+ pos+=length+1; /* Don't unpack ~/.. */
+ continue;
+ }
+ pos=my_stpcpy(buff,home_dir)-1; /* Unpacks ~/.. */
+ if (*pos == FN_LIBCHAR)
+ pos--; /* home ended with '/' */
+ }
+ if (*pos == FN_CURLIB && (pos == start || pos[-1] == FN_LIBCHAR))
+ {
+ if (my_getwd(curr_dir,FN_REFLEN,MYF(0)))
+ {
+ pos+=length+1; /* Don't unpack ./.. */
+ continue;
+ }
+ pos=my_stpcpy(buff,curr_dir)-1; /* Unpacks ./.. */
+ if (*pos == FN_LIBCHAR)
+ pos--; /* home ended with '/' */
+ }
+ end_parentdir=pos;
+ while (pos >= start && *pos != FN_LIBCHAR) /* remove prev dir */
+ pos--;
+ if (pos[1] == FN_HOMELIB ||
+ (pos >= start && memcmp(pos, parent, length) == 0))
+ { /* Don't remove ~user/ */
+ pos=my_stpcpy(end_parentdir+1,parent);
+ *pos=FN_LIBCHAR;
+ continue;
+ }
+ }
+ }
+ else if ((size_t) (pos-start) == length-1 &&
+ !memcmp(start,parent+1,length-1))
+ start=pos; /* Starts with "../" */
+ else if (pos-start > 0 && pos[-1] == FN_LIBCHAR)
+ {
+#ifdef FN_NETWORK_DRIVES
+ if (pos-start != 1)
+#endif
+ pos--; /* Remove dupplicate '/' */
+ }
+ else if (pos-start > 1 && pos[-1] == FN_CURLIB && pos[-2] == FN_LIBCHAR)
+ pos-=2; /* Skip /./ */
+ else if (pos > buff+1 && pos[-1] == FN_HOMELIB && pos[-2] == FN_LIBCHAR)
+ { /* Found ..../~/ */
+ buff[0]=FN_HOMELIB;
+ buff[1]=FN_LIBCHAR;
+ start=buff; pos=buff+1;
+ }
+ }
+ }
+ (void) my_stpcpy(to,buff);
+ DBUG_PRINT("exit",("to: '%s'",to));
+ DBUG_RETURN((size_t) (pos-buff));
+} /* cleanup_dirname */
+
+
+/**
+ Convert a directory name to a format which can be compared as strings
+
+ @param to result buffer, FN_REFLEN chars in length; may be == from
+ @param from 'packed' directory name, in whatever format
+ @returns size of the normalized name
+
+ @details
+ - Ensures that last char is FN_LIBCHAR, unless it is FN_DEVCHAR
+ - Uses cleanup_dirname
+
+ It does *not* expand ~/ (although, see cleanup_dirname). Nor does it do
+ any case folding. All case-insensitive normalization should be done by
+ the caller.
+*/
+
+size_t normalize_dirname(char *to, const char *from)
+{
+ size_t length;
+ char buff[FN_REFLEN];
+ DBUG_ENTER("normalize_dirname");
+
+ /*
+ Despite the name, this actually converts the name to the system's
+ format (TODO: name this properly).
+ */
+ (void) intern_filename(buff, from);
+ length= strlen(buff); /* Fix that '/' is last */
+ if (length &&
+#ifdef FN_DEVCHAR
+ buff[length - 1] != FN_DEVCHAR &&
+#endif
+ buff[length - 1] != FN_LIBCHAR && buff[length - 1] != '/')
+ {
+ /* we need reserve 2 bytes for the trailing slash and the zero */
+ if (length >= sizeof (buff) - 1)
+ length= sizeof (buff) - 2;
+ buff[length]= FN_LIBCHAR;
+ buff[length + 1]= '\0';
+ }
+
+ length=cleanup_dirname(to, buff);
+
+ DBUG_RETURN(length);
+}
+
+
+/**
+ Fixes a directory name so that can be used by open()
+
+ @param to Result buffer, FN_REFLEN characters. May be == from
+ @param from 'Packed' directory name (may contain ~)
+
+ @details
+ - Uses normalize_dirname()
+ - Expands ~/... to home_dir/...
+ - Changes a UNIX filename to system filename (replaces / with \ on windows)
+
+ @returns
+ Length of new directory name (= length of to)
+*/
+
+size_t unpack_dirname(char * to, const char *from)
+{
+ size_t length, h_length;
+ char buff[FN_REFLEN+1+4],*suffix,*tilde_expansion;
+ DBUG_ENTER("unpack_dirname");
+
+ length= normalize_dirname(buff, from);
+
+ if (buff[0] == FN_HOMELIB)
+ {
+ suffix=buff+1; tilde_expansion=expand_tilde(&suffix);
+ if (tilde_expansion)
+ {
+ length-= (size_t) (suffix-buff)-1;
+ if (length+(h_length= strlen(tilde_expansion)) <= FN_REFLEN)
+ {
+ if ((h_length > 0) && (tilde_expansion[h_length-1] == FN_LIBCHAR))
+ h_length--;
+ memmove(buff + h_length, suffix, length);
+ memmove(buff, tilde_expansion, h_length);
+ }
+ }
+ }
+ DBUG_RETURN(system_filename(to,buff)); /* Fix for open */
+} /* unpack_dirname */
+
+
+ /* Expand tilde to home or user-directory */
+ /* Path is reset to point at FN_LIBCHAR after ~xxx */
+
+static char * expand_tilde(char **path)
+{
+ if (path[0][0] == FN_LIBCHAR)
+ return home_dir; /* ~/ expanded to home */
+#ifdef HAVE_GETPWNAM
+ {
+ char *str,save;
+ struct passwd *user_entry;
+
+ if (!(str=strchr(*path,FN_LIBCHAR)))
+ str=strend(*path);
+ save= *str; *str= '\0';
+ user_entry=getpwnam(*path);
+ *str=save;
+ endpwent();
+ if (user_entry)
+ {
+ *path=str;
+ return user_entry->pw_dir;
+ }
+ }
+#endif
+ return (char *) 0;
+}
+
+
+/*
+ Fix filename so it can be used by open, create
+
+ SYNOPSIS
+ unpack_filename()
+ to Store result here. Must be at least of size FN_REFLEN.
+ from Filename in unix format (with ~)
+
+ RETURN
+ # length of to
+
+ NOTES
+ to may be == from
+ ~ will only be expanded if total length < FN_REFLEN
+*/
+
+
+size_t unpack_filename(char * to, const char *from)
+{
+ size_t length, n_length, buff_length;
+ char buff[FN_REFLEN];
+ DBUG_ENTER("unpack_filename");
+
+ length=dirname_part(buff, from, &buff_length);/* copy & convert dirname */
+ n_length=unpack_dirname(buff,buff);
+ if (n_length+strlen(from+length) < FN_REFLEN)
+ {
+ (void) my_stpcpy(buff+n_length,from+length);
+ length= system_filename(to,buff); /* Fix to usably filename */
+ }
+ else
+ length= system_filename(to,from); /* Fix to usably filename */
+ DBUG_RETURN(length);
+} /* unpack_filename */
+
+
+ /* Convert filename (unix standard) to system standard */
+ /* Used before system command's like open(), create() .. */
+ /* Returns used length of to; total length should be FN_REFLEN */
+
+size_t system_filename(char *to, const char *from)
+{
+ return (size_t) (strmake(to,from,FN_REFLEN-1)-to);
+}
+
+ /* Fix a filename to intern (UNIX format) */
+
+char *intern_filename(char *to, const char *from)
+{
+ size_t length, to_length;
+ char buff[FN_REFLEN];
+ if (from == to)
+ { /* Dirname may destroy from */
+ (void) my_stpnmov(buff, from, FN_REFLEN);
+ from=buff;
+ }
+ length= dirname_part(to, from, &to_length); /* Copy dirname & fix chars */
+ (void) my_stpnmov(to + to_length, from + length, FN_REFLEN - to_length);
+ return (to);
+} /* intern_filename */
diff --git a/mysql/mysys/mf_path.c b/mysql/mysys/mf_path.c
new file mode 100644
index 0000000..d3de3b2
--- /dev/null
+++ b/mysql/mysys/mf_path.c
@@ -0,0 +1,121 @@
+/* Copyright (c) 2000, 2015, Oracle and/or its affiliates. All rights reserved.
+
+ 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; version 2 of the License.
+
+ 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include "mysys_priv.h"
+#include "my_sys.h"
+#include <m_string.h>
+
+static char *find_file_in_path(char *to,const char *name);
+
+ /* Finds where program can find it's files.
+ pre_pathname is found by first locking at progname (argv[0]).
+ if progname contains path the path is returned.
+ else if progname is found in path, return it
+ else if progname is given and POSIX environment variable "_" is set
+ then path is taken from "_".
+ If filename doesn't contain a path append MY_BASEDIR_VERSION or
+ MY_BASEDIR if defined, else append "/my/running".
+ own_path_name_part is concatinated to result.
+ my_path puts result in to and returns to */
+
+char * my_path(char * to, const char *progname,
+ const char *own_pathname_part)
+{
+ char *start, *end, *prog;
+ size_t to_length;
+ DBUG_ENTER("my_path");
+
+ start=to; /* Return this */
+ if (progname && (dirname_part(to, progname, &to_length) ||
+ find_file_in_path(to,progname) ||
+ ((prog=getenv("_")) != 0 &&
+ dirname_part(to, prog, &to_length))))
+ {
+ (void) intern_filename(to,to);
+ if (!test_if_hard_path(to))
+ {
+ if (!my_getwd(curr_dir,FN_REFLEN,MYF(0)))
+ bchange((uchar*) to, 0, (uchar*) curr_dir, strlen(curr_dir), strlen(to)+1);
+ }
+ }
+ else
+ {
+ if ((end = getenv("MY_BASEDIR_VERSION")) == 0 &&
+ (end = getenv("MY_BASEDIR")) == 0)
+ {
+#ifdef DEFAULT_BASEDIR
+ end= (char*) DEFAULT_BASEDIR;
+#else
+ end= (char*) "/my/";
+#endif
+ }
+ (void) intern_filename(to,end);
+ to=strend(to);
+ if (to != start && to[-1] != FN_LIBCHAR)
+ *to++ = FN_LIBCHAR;
+ (void) my_stpcpy(to,own_pathname_part);
+ }
+ DBUG_PRINT("exit",("to: '%s'",start));
+ DBUG_RETURN(start);
+} /* my_path */
+
+
+ /* test if file without filename is found in path */
+ /* Returns to if found and to has dirpart if found, else NullS */
+
+#if defined(_WIN32)
+#define F_OK 0
+#define PATH_SEP ';'
+#define PROGRAM_EXTENSION ".exe"
+#else
+#define PATH_SEP ':'
+#endif
+
+static char *find_file_in_path(char *to, const char *name)
+{
+ char *path,*pos,dir[2];
+ const char *ext="";
+
+ if (!(path=getenv("PATH")))
+ return NullS;
+ dir[0]=FN_LIBCHAR; dir[1]=0;
+#ifdef PROGRAM_EXTENSION
+ if (!fn_ext(name)[0])
+ ext=PROGRAM_EXTENSION;
+#endif
+
+ for (pos=path ; (pos=strchr(pos,PATH_SEP)) ; path= ++pos)
+ {
+ if (path != pos)
+ {
+ strxmov(my_stpnmov(to,path,(uint) (pos-path)),dir,name,ext,NullS);
+ if (!access(to,F_OK))
+ {
+ to[(uint) (pos-path)+1]=0; /* Return path only */
+ return to;
+ }
+ }
+ }
+#ifdef _WIN32
+ to[0]=FN_CURLIB;
+ strxmov(to+1,dir,name,ext,NullS);
+ if (!access(to,F_OK)) /* Test in current dir */
+ {
+ to[2]=0; /* Leave ".\" */
+ return to;
+ }
+#endif
+ return NullS; /* File not found */
+}
diff --git a/mysql/mysys/mf_qsort.c b/mysql/mysys/mf_qsort.c
new file mode 100644
index 0000000..7d15fa6
--- /dev/null
+++ b/mysql/mysys/mf_qsort.c
@@ -0,0 +1,205 @@
+/* Copyright (c) 2000, 2015, Oracle and/or its affiliates. All rights reserved.
+
+ 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; version 2 of the License.
+
+ 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+/*
+ qsort implementation optimized for comparison of pointers
+ Inspired by the qsort implementations by Douglas C. Schmidt,
+ and Bentley & McIlroy's "Engineering a Sort Function".
+*/
+
+
+#include "mysys_priv.h"
+#include "my_sys.h"
+#include <m_string.h>
+
+/* We need to use qsort with 2 different compare functions */
+#ifdef QSORT_EXTRA_CMP_ARGUMENT
+#define CMP(A,B) ((*cmp)(cmp_argument,(A),(B)))
+#else
+#define CMP(A,B) ((*cmp)((A),(B)))
+#endif
+
+#define SWAP(A, B, size,swap_ptrs) \
+do { \
+ if (swap_ptrs) \
+ { \
+ char **a = (char**) (A), **b = (char**) (B); \
+ char *tmp = *a; *a++ = *b; *b++ = tmp; \
+ } \
+ else \
+ { \
+ char *a = (A), *b = (B); \
+ char *end= a+size; \
+ do \
+ { \
+ char tmp = *a; *a++ = *b; *b++ = tmp; \
+ } while (a < end); \
+ } \
+} while (0)
+
+/* Put the median in the middle argument */
+#define MEDIAN(low, mid, high) \
+{ \
+ if (CMP(high,low) < 0) \
+ SWAP(high, low, size, ptr_cmp); \
+ if (CMP(mid, low) < 0) \
+ SWAP(mid, low, size, ptr_cmp); \
+ else if (CMP(high, mid) < 0) \
+ SWAP(mid, high, size, ptr_cmp); \
+}
+
+/* The following node is used to store ranges to avoid recursive calls */
+
+typedef struct st_stack
+{
+ char *low,*high;
+} stack_node;
+
+#define PUSH(LOW,HIGH) {stack_ptr->low = LOW; stack_ptr++->high = HIGH;}
+#define POP(LOW,HIGH) {LOW = (--stack_ptr)->low; HIGH = stack_ptr->high;}
+
+/* The following stack size is enough for ulong ~0 elements */
+#define STACK_SIZE (8 * sizeof(unsigned long int))
+#define THRESHOLD_FOR_INSERT_SORT 10
+
+/****************************************************************************
+** 'standard' quicksort with the following extensions:
+**
+** Can be compiled with the qsort2_cmp compare function
+** Store ranges on stack to avoid recursion
+** Use insert sort on small ranges
+** Optimize for sorting of pointers (used often by MySQL)
+** Use median comparison to find partition element
+*****************************************************************************/
+
+#ifdef QSORT_EXTRA_CMP_ARGUMENT
+void my_qsort2(void *base_ptr, size_t count, size_t size, qsort2_cmp cmp,
+ const void *cmp_argument)
+#else
+void my_qsort(void *base_ptr, size_t count, size_t size, qsort_cmp cmp)
+#endif
+{
+ char *low, *high, *pivot;
+ stack_node stack[STACK_SIZE], *stack_ptr;
+ my_bool ptr_cmp;
+ /* Handle the simple case first */
+ /* This will also make the rest of the code simpler */
+ if (count <= 1)
+ return;
+
+ low = (char*) base_ptr;
+ high = low+ size * (count - 1);
+ stack_ptr = stack + 1;
+ pivot = (char *) my_alloca((int) size);
+ ptr_cmp= size == sizeof(char*) && !((low - (char*) 0)& (sizeof(char*)-1));
+
+ /* The following loop sorts elements between high and low */
+ do
+ {
+ char *low_ptr, *high_ptr, *mid;
+
+ count=((size_t) (high - low) / size)+1;
+ /* If count is small, then an insert sort is faster than qsort */
+ if (count < THRESHOLD_FOR_INSERT_SORT)
+ {
+ for (low_ptr = low + size; low_ptr <= high; low_ptr += size)
+ {
+ char *ptr;
+ for (ptr = low_ptr; ptr > low && CMP(ptr - size, ptr) > 0;
+ ptr -= size)
+ SWAP(ptr, ptr - size, size, ptr_cmp);
+ }
+ POP(low, high);
+ continue;
+ }
+
+ /* Try to find a good middle element */
+ mid= low + size * (count >> 1);
+ if (count > 40) /* Must be bigger than 24 */
+ {
+ size_t step = size* (count / 8);
+ MEDIAN(low, low + step, low+step*2);
+ MEDIAN(mid - step, mid, mid+step);
+ MEDIAN(high - 2 * step, high-step, high);
+ /* Put best median in 'mid' */
+ MEDIAN(low+step, mid, high-step);
+ low_ptr = low;
+ high_ptr = high;
+ }
+ else
+ {
+ MEDIAN(low, mid, high);
+ /* The low and high argument are already in sorted against 'pivot' */
+ low_ptr = low + size;
+ high_ptr = high - size;
+ }
+ memcpy(pivot, mid, size);
+
+ do
+ {
+ while (CMP(low_ptr, pivot) < 0)
+ low_ptr += size;
+ while (CMP(pivot, high_ptr) < 0)
+ high_ptr -= size;
+
+ if (low_ptr < high_ptr)
+ {
+ SWAP(low_ptr, high_ptr, size, ptr_cmp);
+ low_ptr += size;
+ high_ptr -= size;
+ }
+ else
+ {
+ if (low_ptr == high_ptr)
+ {
+ low_ptr += size;
+ high_ptr -= size;
+ }
+ break;
+ }
+ }
+ while (low_ptr <= high_ptr);
+
+ /*
+ Prepare for next iteration.
+ Skip partitions of size 1 as these doesn't have to be sorted
+ Push the larger partition and sort the smaller one first.
+ This ensures that the stack is keept small.
+ */
+
+ if ((int) (high_ptr - low) <= 0)
+ {
+ if ((int) (high - low_ptr) <= 0)
+ {
+ POP(low, high); /* Nothing more to sort */
+ }
+ else
+ low = low_ptr; /* Ignore small left part. */
+ }
+ else if ((int) (high - low_ptr) <= 0)
+ high = high_ptr; /* Ignore small right part. */
+ else if ((high_ptr - low) > (high - low_ptr))
+ {
+ PUSH(low, high_ptr); /* Push larger left part */
+ low = low_ptr;
+ }
+ else
+ {
+ PUSH(low_ptr, high); /* Push larger right part */
+ high = high_ptr;
+ }
+ } while (stack_ptr > stack);
+ return;
+}
diff --git a/mysql/mysys/mf_qsort2.c b/mysql/mysys/mf_qsort2.c
new file mode 100644
index 0000000..f54cdac
--- /dev/null
+++ b/mysql/mysys/mf_qsort2.c
@@ -0,0 +1,20 @@
+/* Copyright (C) 2000 MySQL AB
+ Use is subject to license terms
+
+ 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; version 2 of the License.
+
+ 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */
+
+/* qsort that sends one extra argument to the compare subrutine */
+
+#define QSORT_EXTRA_CMP_ARGUMENT
+#include "mf_qsort.c"
diff --git a/mysql/mysys/mf_radix.c b/mysql/mysys/mf_radix.c
new file mode 100644
index 0000000..aa112df
--- /dev/null
+++ b/mysql/mysys/mf_radix.c
@@ -0,0 +1,59 @@
+/* Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved.
+
+ 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; version 2 of the License.
+
+ 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+/*
+ Radixsort for pointers to fixed length strings.
+ A very quick sort for not to long (< 20 char) strings.
+ Neads a extra buffers of number_of_elements pointers but is
+ 2-3 times faster than quicksort
+*/
+
+#include "mysys_priv.h"
+#include <m_string.h>
+
+ /* Radixsort */
+
+my_bool radixsort_is_appliccable(uint n_items, size_t size_of_element)
+{
+ return size_of_element <= 20 && n_items >= 1000 && n_items < 100000;
+}
+
+void radixsort_for_str_ptr(uchar **base, uint number_of_elements, size_t size_of_element, uchar **buffer)
+{
+ uchar **end,**ptr,**buffer_ptr;
+ uint32 *count_ptr,*count_end,count[256];
+ int pass;
+
+ end=base+number_of_elements; count_end=count+256;
+ for (pass=(int) size_of_element-1 ; pass >= 0 ; pass--)
+ {
+ memset(count, 0, sizeof(uint32)*256);
+ for (ptr= base ; ptr < end ; ptr++)
+ count[ptr[0][pass]]++;
+ if (count[0] == number_of_elements)
+ goto next;
+ for (count_ptr=count+1 ; count_ptr < count_end ; count_ptr++)
+ {
+ if (*count_ptr == number_of_elements)
+ goto next;
+ (*count_ptr)+= *(count_ptr-1);
+ }
+ for (ptr= end ; ptr-- != base ;)
+ buffer[--count[ptr[0][pass]]]= *ptr;
+ for (ptr=base, buffer_ptr=buffer ; ptr < end ;)
+ (*ptr++) = *buffer_ptr++;
+ next:;
+ }
+}
diff --git a/mysql/mysys/mf_same.c b/mysql/mysys/mf_same.c
new file mode 100644
index 0000000..86c3eaf
--- /dev/null
+++ b/mysql/mysys/mf_same.c
@@ -0,0 +1,41 @@
+/* Copyright (c) 2000, 2015, Oracle and/or its affiliates. All rights reserved.
+
+ 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; version 2 of the License.
+
+ 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */
+
+/* Kopierar biblioteksstrukturen och extensionen fr}n ett filnamn */
+
+#include "mysys_priv.h"
+#include "my_sys.h"
+#include <m_string.h>
+
+ /*
+ Copy directory and/or extension between filenames.
+ (For the meaning of 'flag', check mf_format.c)
+ 'to' may be equal to 'name'.
+ Returns 'to'.
+ */
+
+char * fn_same(char *to, const char *name, int flag)
+{
+ char dev[FN_REFLEN];
+ const char *ext;
+ size_t dev_length;
+ DBUG_ENTER("fn_same");
+ DBUG_PRINT("enter",("to: %s name: %s flag: %d",to,name,flag));
+
+ if ((ext=strrchr(name+dirname_part(dev, name, &dev_length),FN_EXTCHAR)) == 0)
+ ext="";
+
+ DBUG_RETURN(fn_format(to,to,dev,ext,flag));
+} /* fn_same */
diff --git a/mysql/mysys/mf_soundex.c b/mysql/mysys/mf_soundex.c
new file mode 100644
index 0000000..88a76c2
--- /dev/null
+++ b/mysql/mysys/mf_soundex.c
@@ -0,0 +1,105 @@
+/* Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
+
+ 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; version 2 of the License.
+
+ 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */
+
+/****************************************************************
+* SOUNDEX ALGORITHM in C *
+* *
+* The basic Algorithm source is taken from EDN Nov. *
+* 14, 1985 pg. 36. *
+* *
+* As a test Those in Illinois will find that the *
+* first group of numbers in their drivers license *
+* number is the soundex number for their last name. *
+* *
+* RHW PC-IBBS ID. #1230 *
+* *
+* As an extension if remove_garbage is set then all non- *
+* alpha characters are skipped *
+* *
+* Note, that this implementation corresponds to the *
+* original version of the algorithm, not to the more *
+* popular "enhanced" version, described by Knuth. *
+****************************************************************/
+
+#include "mysys_priv.h"
+#include <m_ctype.h>
+#include "my_static.h"
+
+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(CHARSET_INFO * cs, char * out_pntr, char * in_pntr,
+ pbool remove_garbage)
+{
+ char ch,last_ch;
+ char *end;
+ const uchar *map=cs->to_upper;
+
+ if (remove_garbage)
+ {
+ while (*in_pntr && !my_isalpha(cs,*in_pntr)) /* Skip pre-space */
+ in_pntr++;
+ }
+ *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 */
+ /* end of input (null) or output */
+ /* letter code count = 3 */
+
+ in_pntr++;
+ 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 */
+ {
+ *out_pntr++ = ch; /* letter, copy to output */
+ } /* for next double-letter check */
+ last_ch = ch; /* save code of last input letter */
+ }
+ while (out_pntr < end)
+ *out_pntr++ = '0';
+ *out_pntr=0; /* end string */
+ return;
+} /* soundex */
+
+
+ /*
+ If alpha, map input letter to soundex code.
+ If not alpha and remove_garbage is set then skip to next char
+ else return 0
+ */
+
+static char get_scode(CHARSET_INFO * cs,char **ptr, pbool remove_garbage)
+{
+ uchar ch;
+
+ if (remove_garbage)
+ {
+ while (**ptr && !my_isalpha(cs,**ptr))
+ (*ptr)++;
+ }
+ ch=my_toupper(cs,**ptr);
+ if (ch < 'A' || ch > 'Z')
+ {
+ if (my_isalpha(cs,ch)) /* If extended alfa (country spec) */
+ return '0'; /* threat as vokal */
+ return 0; /* Can't map */
+ }
+ return(soundex_map[ch-'A']);
+} /* get_scode */
diff --git a/mysql/mysys/mf_tempfile.c b/mysql/mysys/mf_tempfile.c
new file mode 100644
index 0000000..beeabba
--- /dev/null
+++ b/mysql/mysys/mf_tempfile.c
@@ -0,0 +1,140 @@
+/* Copyright (c) 2000, 2015, Oracle and/or its affiliates. All rights reserved.
+
+ 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; version 2 of the License.
+
+ 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include "mysys_priv.h"
+#include <m_string.h>
+#include "my_static.h"
+#include "mysys_err.h"
+#include <errno.h>
+#include "my_thread_local.h"
+
+
+/*
+ @brief
+ Create a temporary file with unique name in a given directory
+
+ @details
+ create_temp_file
+ to pointer to buffer where temporary filename will be stored
+ dir directory where to create the file
+ prefix prefix the filename with this
+ mode Flags to use for my_create/my_open
+ MyFlags Magic flags
+
+ @return
+ File descriptor of opened file if success
+ -1 and sets errno if fails.
+
+ @note
+ The behaviour of this function differs a lot between
+ implementation, it's main use is to generate a file with
+ a name that does not already exist.
+
+ When passing O_TEMPORARY flag in "mode" the file should
+ be automatically deleted
+
+ The implementation using mkstemp should be considered the
+ reference implementation when adding a new or modifying an
+ existing one
+
+*/
+
+File create_temp_file(char *to, const char *dir, const char *prefix,
+ int mode, myf MyFlags)
+{
+ File file= -1;
+#ifdef _WIN32
+ TCHAR path_buf[MAX_PATH-14];
+#endif
+
+ DBUG_ENTER("create_temp_file");
+ DBUG_PRINT("enter", ("dir: %s, prefix: %s", dir, prefix));
+#if defined(_WIN32)
+
+ /*
+ Use GetTempPath to determine path for temporary files.
+ This is because the documentation for GetTempFileName
+ has the following to say about this parameter:
+ "If this parameter is NULL, the function fails."
+ */
+ if (!dir)
+ {
+ if(GetTempPath(sizeof(path_buf), path_buf) > 0)
+ dir = path_buf;
+ }
+ /*
+ Use GetTempFileName to generate a unique filename, create
+ the file and release it's handle
+ - uses up to the first three letters from prefix
+ */
+ if (GetTempFileName(dir, prefix, 0, to) == 0)
+ DBUG_RETURN(-1);
+
+ DBUG_PRINT("info", ("name: %s", to));
+
+ /*
+ Open the file without the "open only if file doesn't already exist"
+ since the file has already been created by GetTempFileName
+ */
+ if ((file= my_open(to, (mode & ~O_EXCL), MyFlags)) < 0)
+ {
+ /* Open failed, remove the file created by GetTempFileName */
+ int tmp= my_errno();
+ (void) my_delete(to, MYF(0));
+ set_my_errno(tmp);
+ }
+
+#else /* mkstemp() is available on all non-Windows supported platforms. */
+ {
+ char prefix_buff[30];
+ uint pfx_len;
+ File org_file;
+
+ pfx_len= (uint) (my_stpcpy(my_stpnmov(prefix_buff,
+ prefix ? prefix : "tmp.",
+ sizeof(prefix_buff)-7),"XXXXXX") -
+ prefix_buff);
+ if (!dir && ! (dir =getenv("TMPDIR")))
+ dir= DEFAULT_TMPDIR;
+ if (strlen(dir)+ pfx_len > FN_REFLEN-2)
+ {
+ errno=ENAMETOOLONG;
+ set_my_errno(ENAMETOOLONG);
+ DBUG_RETURN(file);
+ }
+ my_stpcpy(convert_dirname(to,dir,NullS),prefix_buff);
+ org_file=mkstemp(to);
+ if (mode & O_TEMPORARY)
+ (void) my_delete(to, MYF(MY_WME));
+ file=my_register_filename(org_file, to, FILE_BY_MKSTEMP,
+ EE_CANTCREATEFILE, MyFlags);
+ /* If we didn't manage to register the name, remove the temp file */
+ if (org_file >= 0 && file < 0)
+ {
+ int tmp=my_errno();
+ close(org_file);
+ (void) my_delete(to, MYF(MY_WME));
+ set_my_errno(tmp);
+ }
+ }
+#endif
+ if (file >= 0)
+ {
+ mysql_mutex_lock(&THR_LOCK_open);
+ my_tmp_file_created++;
+ mysql_mutex_unlock(&THR_LOCK_open);
+ }
+ DBUG_RETURN(file);
+}
diff --git a/mysql/mysys/mf_unixpath.c b/mysql/mysys/mf_unixpath.c
new file mode 100644
index 0000000..05df11e
--- /dev/null
+++ b/mysql/mysys/mf_unixpath.c
@@ -0,0 +1,36 @@
+/* Copyright (c) 2000, 2016, Oracle and/or its affiliates. All rights reserved.
+
+ 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; version 2 of the License.
+
+ 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include "mysys_priv.h"
+#include <m_string.h>
+
+/**
+ Convert filename to unix style filename.
+
+ @remark On Windows, converts '\' to '/'.
+
+ @param to A pathname.
+*/
+
+void to_unix_path(char *to MY_ATTRIBUTE((unused)))
+{
+#if FN_LIBCHAR != '/'
+ {
+ to--;
+ while ((to=strchr(to+1,FN_LIBCHAR)) != 0)
+ *to='/';
+ }
+#endif
+}
diff --git a/mysql/mysys/mf_wcomp.c b/mysql/mysys/mf_wcomp.c
new file mode 100644
index 0000000..3d2510b
--- /dev/null
+++ b/mysql/mysys/mf_wcomp.c
@@ -0,0 +1,89 @@
+/* Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved.
+
+ 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; version 2 of the License.
+
+ 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */
+
+/* Funktions for comparing with wild-cards */
+
+#include "mysys_priv.h"
+
+ /* Test if a string is "comparable" to a wild-card string */
+ /* returns 0 if the strings are "comparable" */
+
+char wild_many='*';
+char wild_one='?';
+char wild_prefix=0; /* QQ this can potentially cause a SIGSEGV */
+
+int wild_compare(const char *str, const char *wildstr,
+ pbool str_is_pattern)
+{
+ char cmp;
+ DBUG_ENTER("wild_compare");
+
+ while (*wildstr)
+ {
+ while (*wildstr && *wildstr != wild_many && *wildstr != wild_one)
+ {
+ if (*wildstr == wild_prefix && wildstr[1])
+ {
+ wildstr++;
+ if (str_is_pattern && *str++ != wild_prefix)
+ DBUG_RETURN(1);
+ }
+ if (*wildstr++ != *str++)
+ DBUG_RETURN(1);
+ }
+ if (! *wildstr )
+ DBUG_RETURN(*str != 0);
+ if (*wildstr++ == wild_one)
+ {
+ if (! *str || (str_is_pattern && *str == wild_many))
+ DBUG_RETURN(1); /* One char; skip */
+ if (*str++ == wild_prefix && str_is_pattern && *str)
+ str++;
+ }
+ else
+ { /* Found '*' */
+ while (str_is_pattern && *str == wild_many)
+ str++;
+ for (; *wildstr == wild_many || *wildstr == wild_one; wildstr++)
+ if (*wildstr == wild_many)
+ {
+ while (str_is_pattern && *str == wild_many)
+ str++;
+ }
+ else
+ {
+ if (str_is_pattern && *str == wild_prefix && str[1])
+ str+=2;
+ else if (! *str++)
+ DBUG_RETURN (1);
+ }
+ if (!*wildstr)
+ DBUG_RETURN(0); /* '*' as last char: OK */
+ if ((cmp= *wildstr) == wild_prefix && wildstr[1] && !str_is_pattern)
+ cmp=wildstr[1];
+ for (;;str++)
+ {
+ while (*str && *str != cmp)
+ str++;
+ if (!*str)
+ DBUG_RETURN (1);
+ if (wild_compare(str,wildstr,str_is_pattern) == 0)
+ DBUG_RETURN (0);
+ }
+ /* We will never come here */
+ }
+ }
+ DBUG_RETURN (*str != 0);
+} /* wild_compare */
diff --git a/mysql/mysys/mulalloc.c b/mysql/mysys/mulalloc.c
new file mode 100644
index 0000000..e47e9f4
--- /dev/null
+++ b/mysql/mysys/mulalloc.c
@@ -0,0 +1,64 @@
+/* Copyright (c) 2000, 2015, Oracle and/or its affiliates. All rights reserved.
+
+ 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; version 2 of the License.
+
+ 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */
+
+#include "mysys_priv.h"
+#include "my_sys.h"
+#include <stdarg.h>
+
+/*
+ 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
+ ptr1, length1 Multiple arguments terminated by null ptr
+ ptr2, length2 ...
+ ...
+ NULL
+*/
+
+void* my_multi_malloc(PSI_memory_key key, myf myFlags, ...)
+{
+ va_list args;
+ char **ptr,*start,*res;
+ size_t tot_length,length;
+ DBUG_ENTER("my_multi_malloc");
+
+ va_start(args,myFlags);
+ tot_length=0;
+ while ((ptr=va_arg(args, char **)))
+ {
+ length=va_arg(args,uint);
+ tot_length+=ALIGN_SIZE(length);
+ }
+ va_end(args);
+
+ if (!(start=(char *) my_malloc(key, tot_length,myFlags)))
+ DBUG_RETURN(0); /* purecov: inspected */
+
+ va_start(args,myFlags);
+ res=start;
+ while ((ptr=va_arg(args, char **)))
+ {
+ *ptr=res;
+ length=va_arg(args,uint);
+ res+=ALIGN_SIZE(length);
+ }
+ va_end(args);
+ DBUG_RETURN((void*) start);
+}
diff --git a/mysql/mysys/my_access.c b/mysql/mysys/my_access.c
new file mode 100644
index 0000000..0d66643
--- /dev/null
+++ b/mysql/mysys/my_access.c
@@ -0,0 +1,268 @@
+/* Copyright (c) 2000, 2016, Oracle and/or its affiliates. All rights reserved.
+
+ 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; version 2 of the License.
+
+ 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */
+
+#include "mysys_priv.h"
+#include "my_sys.h"
+#include <m_string.h>
+#include "my_thread_local.h"
+
+#ifdef _WIN32
+
+/*
+ Check a file or path for accessability.
+
+ SYNOPSIS
+ file_access()
+ path Path to file
+ amode Access method
+
+ DESCRIPTION
+ This function wraps the normal access method because the access
+ available in MSVCRT> +reports that filenames such as LPT1 and
+ COM1 are valid (they are but should not be so for us).
+
+ RETURN VALUES
+ 0 ok
+ -1 error (We use -1 as my_access is mapped to access on other platforms)
+*/
+
+int my_access(const char *path, int amode)
+{
+ WIN32_FILE_ATTRIBUTE_DATA fileinfo;
+ BOOL result;
+
+ result= GetFileAttributesEx(path, GetFileExInfoStandard, &fileinfo);
+ if (! result ||
+ (fileinfo.dwFileAttributes & FILE_ATTRIBUTE_READONLY) && (amode & W_OK))
+ {
+ errno= EACCES;
+ set_my_errno(EACCES);
+ return -1;
+ }
+ return 0;
+}
+
+#endif /* _WIN32 */
+
+
+/*
+ List of file names that causes problem on windows
+
+ NOTE that one can also not have file names of type CON.TXT
+
+ NOTE: it is important to keep "CLOCK$" on the first place,
+ we skip it in check_if_legal_tablename.
+*/
+static const char *reserved_names[]=
+{
+ "CLOCK$",
+ "CON", "PRN", "AUX", "NUL",
+ "COM1", "COM2", "COM3", "COM4", "COM5", "COM6", "COM7", "COM8", "COM9",
+ "LPT1", "LPT2", "LPT3", "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9",
+ NullS
+};
+
+#define MAX_RESERVED_NAME_LENGTH 6
+
+
+/*
+ Looks up a null-terminated string in a list,
+ case insensitively.
+
+ SYNOPSIS
+ str_list_find()
+ list list of items
+ str item to find
+
+ RETURN
+ 0 ok
+ 1 reserved file name
+*/
+static int str_list_find(const char **list, const char *str)
+{
+ const char **name;
+ for (name= list; *name; name++)
+ {
+ if (!my_strcasecmp(&my_charset_latin1, *name, str))
+ return 1;
+ }
+ return 0;
+}
+
+
+/*
+ A map for faster reserved_names lookup,
+ helps to avoid loops in many cases.
+ 1 - can be the first letter
+ 2 - can be the second letter
+ 4 - can be the third letter
+*/
+static char reserved_map[256]=
+{
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* ................ */
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* ................ */
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* !"#$%&'()*+,-./ */
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0123456789:;<=>? */
+ 0,1,0,1,0,0,0,0,0,0,0,0,7,4,5,2, /* @ABCDEFGHIJKLMNO */
+ 3,0,2,0,4,2,0,0,4,0,0,0,0,0,0,0, /* PQRSTUVWXYZ[\]^_ */
+ 0,1,0,1,0,0,0,0,0,0,0,0,7,4,5,2, /* bcdefghijklmno */
+ 3,0,2,0,4,2,0,0,4,0,0,0,0,0,0,0, /* pqrstuvwxyz{|}~. */
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* ................ */
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* ................ */
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* ................ */
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* ................ */
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* ................ */
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* ................ */
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* ................ */
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 /* ................ */
+};
+
+
+/*
+ Check if a table name may cause problems
+
+ SYNOPSIS
+ check_if_legal_tablename
+ name Table name (without any extensions)
+
+ DESCRIPTION
+ We don't check 'CLOCK$' because dollar sign is encoded as @0024,
+ making table file name 'CLOCK@0024', which is safe.
+ This is why we start lookup from the second element
+ (i.e. &reserver_name[1])
+
+ RETURN
+ 0 ok
+ 1 reserved file name
+*/
+
+int check_if_legal_tablename(const char *name)
+{
+ DBUG_ENTER("check_if_legal_tablename");
+ DBUG_RETURN(name[0] != 0 && name[1] != 0 &&
+ (reserved_map[(uchar) name[0]] & 1) &&
+ (reserved_map[(uchar) name[1]] & 2) &&
+ (reserved_map[(uchar) name[2]] & 4) &&
+ str_list_find(&reserved_names[1], name));
+}
+
+
+#ifdef _WIN32
+/**
+ Checks if the drive letter supplied is valid or not. Valid drive
+ letters are A to Z, both lower case and upper case.
+
+ @param drive_letter : The drive letter to validate.
+
+ @return TRUE if the drive exists, FALSE otherwise.
+*/
+static my_bool does_drive_exists(char drive_letter)
+{
+ DWORD drive_mask= GetLogicalDrives();
+ drive_letter= toupper(drive_letter);
+
+ return (drive_letter >= 'A' && drive_letter <= 'Z') &&
+ (drive_mask & (0x1 << (drive_letter - 'A')));
+}
+
+/**
+ Verifies if the file name supplied is allowed or not. On Windows
+ file names with a colon (:) are not allowed because such file names
+ store data in Alternate Data Streams which can be used to hide
+ the data.
+
+ @param name contains the file name with or without path
+ @param length contains the length of file name
+ @param allow_current_dir TRUE if paths like C:foobar are allowed,
+ FALSE otherwise
+
+ @return TRUE if the file name is allowed, FALSE otherwise.
+*/
+my_bool is_filename_allowed(const char *name MY_ATTRIBUTE((unused)),
+ size_t length MY_ATTRIBUTE((unused)),
+ my_bool allow_current_dir MY_ATTRIBUTE((unused)))
+{
+ /*
+ For Windows, check if the file name contains : character.
+ Start from end of path and search if the file name contains :
+ */
+ const char* ch = NULL;
+ for (ch= name + length - 1; ch >= name; --ch)
+ {
+ if (FN_LIBCHAR == *ch || '/' == *ch)
+ break;
+ else if (':' == *ch)
+ {
+ /*
+ File names like C:foobar.txt are allowed since the syntax means
+ file foobar.txt in current directory of C drive. However file
+ names likes CC:foobar are not allowed since this syntax means ADS
+ foobar in file CC.
+ */
+ return (allow_current_dir && (ch - name == 1) &&
+ does_drive_exists(*name));
+ }
+ }
+ return TRUE;
+} /* is_filename_allowed */
+#endif /* _WIN32 */
+
+#if defined(_WIN32)
+
+
+/*
+ Check if a path will access a reserverd file name that may cause problems
+
+ SYNOPSIS
+ check_if_legal_filename
+ path Path to file
+
+ RETURN
+ 0 ok
+ 1 reserved file name
+*/
+
+int check_if_legal_filename(const char *path)
+{
+ const char *end;
+ const char **reserved_name;
+ DBUG_ENTER("check_if_legal_filename");
+
+ if (!is_filename_allowed(path, strlen(path), TRUE))
+ DBUG_RETURN(1);
+
+ path+= dirname_length(path); /* To start of filename */
+ if (!(end= strchr(path, FN_EXTCHAR)))
+ end= strend(path);
+ if (path == end || (uint) (end - path) > MAX_RESERVED_NAME_LENGTH)
+ DBUG_RETURN(0); /* Simplify inner loop */
+
+ for (reserved_name= reserved_names; *reserved_name; reserved_name++)
+ {
+ const char *reserved= *reserved_name; /* never empty */
+ const char *name= path;
+
+ do
+ {
+ if (*reserved != my_toupper(&my_charset_latin1, *name))
+ break;
+ if (++name == end && !reserved[1])
+ DBUG_RETURN(1); /* Found wrong path */
+ } while (*++reserved);
+ }
+ DBUG_RETURN(0);
+}
+
+#endif /* defined(_WIN32) */
diff --git a/mysql/mysys/my_alloc.c b/mysql/mysys/my_alloc.c
new file mode 100644
index 0000000..1d72917
--- /dev/null
+++ b/mysql/mysys/my_alloc.c
@@ -0,0 +1,557 @@
+/* Copyright (c) 2000, 2016, Oracle and/or its affiliates. All rights reserved.
+
+ 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; version 2 of the License.
+
+ 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+/* Routines to handle mallocing of results which will be freed the same time */
+
+#include <my_global.h>
+#include <my_sys.h>
+#include <m_string.h>
+#include "mysys_err.h"
+
+static inline my_bool is_mem_available(MEM_ROOT *mem_root, size_t size);
+
+/*
+ For instrumented code: don't preallocate memory in alloc_root().
+ This gives a lot more memory chunks, each with a red-zone around them.
+ */
+#if !defined(HAVE_VALGRIND) && !defined(HAVE_ASAN)
+#define PREALLOCATE_MEMORY_CHUNKS
+#endif
+
+
+/*
+ Initialize memory root
+
+ SYNOPSIS
+ init_alloc_root()
+ mem_root - memory root to initialize
+ block_size - size of chunks (blocks) used for memory allocation
+ (It is external size of chunk i.e. it should include
+ memory required for internal structures, thus it
+ should be no less than ALLOC_ROOT_MIN_BLOCK_SIZE)
+ pre_alloc_size - if non-0, then size of block that should be
+ pre-allocated during memory root initialization.
+
+ DESCRIPTION
+ This function prepares memory root for further use, sets initial size of
+ chunk for memory allocation and pre-allocates first block if specified.
+ Altough error can happen during execution of this function if
+ pre_alloc_size is non-0 it won't be reported. Instead it will be
+ reported as error in first alloc_root() on this memory root.
+*/
+
+void init_alloc_root(PSI_memory_key key,
+ MEM_ROOT *mem_root, size_t block_size,
+ size_t pre_alloc_size MY_ATTRIBUTE((unused)))
+{
+ DBUG_ENTER("init_alloc_root");
+ DBUG_PRINT("enter",("root: 0x%lx", (long) mem_root));
+
+ mem_root->free= mem_root->used= mem_root->pre_alloc= 0;
+ mem_root->min_malloc= 32;
+ mem_root->block_size= block_size - ALLOC_ROOT_MIN_BLOCK_SIZE;
+ mem_root->error_handler= 0;
+ mem_root->block_num= 4; /* We shift this with >>2 */
+ mem_root->first_block_usage= 0;
+ mem_root->m_psi_key= key;
+ mem_root->max_capacity= 0;
+ mem_root->allocated_size= 0;
+ mem_root->error_for_capacity_exceeded= FALSE;
+
+#if defined(PREALLOCATE_MEMORY_CHUNKS)
+ if (pre_alloc_size)
+ {
+ if ((mem_root->free= mem_root->pre_alloc=
+ (USED_MEM*) my_malloc(key,
+ pre_alloc_size+ ALIGN_SIZE(sizeof(USED_MEM)),
+ MYF(0))))
+ {
+ mem_root->free->size= (uint)(pre_alloc_size+ALIGN_SIZE(sizeof(USED_MEM)));
+ mem_root->free->left= (uint)pre_alloc_size;
+ mem_root->free->next= 0;
+ mem_root->allocated_size+= pre_alloc_size+ ALIGN_SIZE(sizeof(USED_MEM));
+ }
+ }
+#endif
+ DBUG_VOID_RETURN;
+}
+
+/** This is a no-op unless the build is debug or for Valgrind. */
+#define TRASH_MEM(X) TRASH(((char*)(X) + ((X)->size-(X)->left)), (X)->left)
+
+
+/*
+ SYNOPSIS
+ reset_root_defaults()
+ mem_root memory root to change defaults of
+ block_size new value of block size. Must be greater or equal
+ than ALLOC_ROOT_MIN_BLOCK_SIZE (this value is about
+ 68 bytes and depends on platform and compilation flags)
+ pre_alloc_size new size of preallocated block. If not zero,
+ must be equal to or greater than block size,
+ otherwise means 'no prealloc'.
+ DESCRIPTION
+ Function aligns and assigns new value to block size; then it tries to
+ reuse one of existing blocks as prealloc block, or malloc new one of
+ requested size. If no blocks can be reused, all unused blocks are freed
+ before allocation.
+*/
+
+void reset_root_defaults(MEM_ROOT *mem_root, size_t block_size,
+ size_t pre_alloc_size MY_ATTRIBUTE((unused)))
+{
+ DBUG_ASSERT(alloc_root_inited(mem_root));
+
+ mem_root->block_size= block_size - ALLOC_ROOT_MIN_BLOCK_SIZE;
+#if defined(PREALLOCATE_MEMORY_CHUNKS)
+ if (pre_alloc_size)
+ {
+ size_t size= pre_alloc_size + ALIGN_SIZE(sizeof(USED_MEM));
+ if (!mem_root->pre_alloc || mem_root->pre_alloc->size != size)
+ {
+ USED_MEM *mem, **prev= &mem_root->free;
+ /*
+ Free unused blocks, so that consequent calls
+ to reset_root_defaults won't eat away memory.
+ */
+ while (*prev)
+ {
+ mem= *prev;
+ if (mem->size == (uint)size)
+ {
+ /* We found a suitable block, no need to do anything else */
+ mem_root->pre_alloc= mem;
+ return;
+ }
+ if (mem->left + ALIGN_SIZE(sizeof(USED_MEM)) == mem->size)
+ {
+ /* remove block from the list and free it */
+ *prev= mem->next;
+ {
+ mem->left= mem->size;
+ mem_root->allocated_size-= mem->size;
+ TRASH_MEM(mem);
+ my_free(mem);
+ }
+ }
+ else
+ prev= &mem->next;
+ }
+ /* Allocate new prealloc block and add it to the end of free list */
+ if (is_mem_available(mem_root, size) &&
+ (mem= (USED_MEM *) my_malloc(mem_root->m_psi_key,
+ size, MYF(0))))
+ {
+ mem->size= (uint)size;
+ mem->left= (uint)pre_alloc_size;
+ mem->next= *prev;
+ *prev= mem_root->pre_alloc= mem;
+ mem_root->allocated_size+= size;
+ }
+ else
+ {
+ mem_root->pre_alloc= 0;
+ }
+ }
+ }
+ else
+#endif
+ mem_root->pre_alloc= 0;
+}
+
+
+/**
+ Function allocates the requested memory in the mem_root specified.
+ If max_capacity is defined for the mem_root, it only allocates
+ if the requested size can be allocated without exceeding the limit.
+ However, when error_for_capacity_exceeded is set, an error is flagged
+ (see set_error_reporting), but allocation is still performed.
+
+ @param mem_root memory root to allocate memory from
+ @length size to be allocated
+
+ @retval
+ void * Pointer to the memory thats been allocated
+ @retval
+ NULL Memory is not available.
+*/
+
+void *alloc_root(MEM_ROOT *mem_root, size_t length)
+{
+#if !defined(PREALLOCATE_MEMORY_CHUNKS)
+ USED_MEM *next;
+ DBUG_ENTER("alloc_root");
+ DBUG_PRINT("enter",("root: 0x%lx", (long) mem_root));
+
+ DBUG_ASSERT(alloc_root_inited(mem_root));
+
+ DBUG_EXECUTE_IF("simulate_out_of_memory",
+ {
+ if (mem_root->error_handler)
+ (*mem_root->error_handler)();
+ DBUG_SET("-d,simulate_out_of_memory");
+ DBUG_RETURN((void*) 0); /* purecov: inspected */
+ });
+
+ length+=ALIGN_SIZE(sizeof(USED_MEM));
+ if (!is_mem_available(mem_root, length))
+ {
+ if (mem_root->error_for_capacity_exceeded)
+ my_error(EE_CAPACITY_EXCEEDED, MYF(0),
+ (ulonglong) mem_root->max_capacity);
+ else
+ DBUG_RETURN(NULL);
+ }
+ if (!(next = (USED_MEM*) my_malloc(mem_root->m_psi_key,
+ length,MYF(MY_WME | ME_FATALERROR))))
+ {
+ if (mem_root->error_handler)
+ (*mem_root->error_handler)();
+ DBUG_RETURN((uchar*) 0); /* purecov: inspected */
+ }
+ mem_root->allocated_size+= length;
+ next->next= mem_root->used;
+ next->size= (uint)length;
+ next->left= (uint)(length - ALIGN_SIZE(sizeof(USED_MEM)));
+ mem_root->used= next;
+ DBUG_PRINT("exit",("ptr: 0x%lx", (long) (((char*) next)+
+ ALIGN_SIZE(sizeof(USED_MEM)))));
+ DBUG_RETURN((uchar*) (((char*) next)+ALIGN_SIZE(sizeof(USED_MEM))));
+#else
+ size_t get_size, block_size;
+ uchar* point;
+ USED_MEM *next= 0;
+ USED_MEM **prev;
+ DBUG_ENTER("alloc_root");
+ DBUG_PRINT("enter",("root: 0x%lx", (long) mem_root));
+ DBUG_ASSERT(alloc_root_inited(mem_root));
+
+ DBUG_EXECUTE_IF("simulate_out_of_memory",
+ {
+ /* Avoid reusing an already allocated block */
+ if (mem_root->error_handler)
+ (*mem_root->error_handler)();
+ DBUG_SET("-d,simulate_out_of_memory");
+ DBUG_RETURN((void*) 0); /* purecov: inspected */
+ });
+ length= ALIGN_SIZE(length);
+ if ((*(prev= &mem_root->free)) != NULL)
+ {
+ if ((*prev)->left < length &&
+ mem_root->first_block_usage++ >= ALLOC_MAX_BLOCK_USAGE_BEFORE_DROP &&
+ (*prev)->left < ALLOC_MAX_BLOCK_TO_DROP)
+ {
+ next= *prev;
+ *prev= next->next; /* Remove block from list */
+ next->next= mem_root->used;
+ mem_root->used= next;
+ mem_root->first_block_usage= 0;
+ }
+ for (next= *prev ; next && next->left < length ; next= next->next)
+ prev= &next->next;
+ }
+ if (! next)
+ { /* Time to alloc new block */
+ block_size= mem_root->block_size * (mem_root->block_num >> 2);
+ get_size= length+ALIGN_SIZE(sizeof(USED_MEM));
+ get_size= MY_MAX(get_size, block_size);
+
+ if (!is_mem_available(mem_root, get_size))
+ {
+ if (mem_root->error_for_capacity_exceeded)
+ my_error(EE_CAPACITY_EXCEEDED, MYF(0),
+ (ulonglong) mem_root->max_capacity);
+ else
+ DBUG_RETURN(NULL);
+ }
+ if (!(next = (USED_MEM*) my_malloc(mem_root->m_psi_key,
+ get_size,MYF(MY_WME | ME_FATALERROR))))
+ {
+ if (mem_root->error_handler)
+ (*mem_root->error_handler)();
+ DBUG_RETURN((void*) 0); /* purecov: inspected */
+ }
+ mem_root->allocated_size+= get_size;
+ mem_root->block_num++;
+ next->next= *prev;
+ next->size= (uint)get_size;
+ next->left= (uint)(get_size-ALIGN_SIZE(sizeof(USED_MEM)));
+ *prev=next;
+ }
+
+ point= (uchar*) ((char*) next+ (next->size-next->left));
+ /*TODO: next part may be unneded due to mem_root->first_block_usage counter*/
+ if ((next->left-= (uint)length) < mem_root->min_malloc)
+ { /* Full block */
+ *prev= next->next; /* Remove block from list */
+ next->next= mem_root->used;
+ mem_root->used= next;
+ mem_root->first_block_usage= 0;
+ }
+ DBUG_PRINT("exit",("ptr: 0x%lx", (ulong) point));
+ DBUG_RETURN((void*) point);
+#endif
+}
+
+
+/*
+ Allocate many pointers at the same time.
+
+ DESCRIPTION
+ ptr1, ptr2, etc all point into big allocated memory area.
+
+ SYNOPSIS
+ multi_alloc_root()
+ root Memory root
+ ptr1, length1 Multiple arguments terminated by a NULL pointer
+ ptr2, length2 ...
+ ...
+ NULL
+
+ RETURN VALUE
+ A pointer to the beginning of the allocated memory block
+ in case of success or NULL if out of memory.
+*/
+
+void *multi_alloc_root(MEM_ROOT *root, ...)
+{
+ va_list args;
+ char **ptr, *start, *res;
+ size_t tot_length, length;
+ DBUG_ENTER("multi_alloc_root");
+
+ va_start(args, root);
+ tot_length= 0;
+ while ((ptr= va_arg(args, char **)))
+ {
+ length= va_arg(args, uint);
+ tot_length+= ALIGN_SIZE(length);
+ }
+ va_end(args);
+
+ if (!(start= (char*) alloc_root(root, tot_length)))
+ DBUG_RETURN(0); /* purecov: inspected */
+
+ va_start(args, root);
+ res= start;
+ while ((ptr= va_arg(args, char **)))
+ {
+ *ptr= res;
+ length= va_arg(args, uint);
+ res+= ALIGN_SIZE(length);
+ }
+ va_end(args);
+ DBUG_RETURN((void*) start);
+}
+
+/* Mark all data in blocks free for reusage */
+
+static inline void mark_blocks_free(MEM_ROOT* root)
+{
+ USED_MEM *next;
+ USED_MEM **last;
+
+ /* iterate through (partially) free blocks, mark them free */
+ last= &root->free;
+ for (next= root->free; next; next= *(last= &next->next))
+ {
+ next->left= next->size - (uint)ALIGN_SIZE(sizeof(USED_MEM));
+ TRASH_MEM(next);
+ }
+
+ /* Combine the free and the used list */
+ *last= next=root->used;
+
+ /* now go through the used blocks and mark them free */
+ for (; next; next= next->next)
+ {
+ next->left= next->size - (uint)ALIGN_SIZE(sizeof(USED_MEM));
+ TRASH_MEM(next);
+ }
+
+ /* Now everything is set; Indicate that nothing is used anymore */
+ root->used= 0;
+ root->first_block_usage= 0;
+}
+
+void claim_root(MEM_ROOT *root)
+{
+ USED_MEM *next,*old;
+ DBUG_ENTER("claim_root");
+ DBUG_PRINT("enter",("root: 0x%lx", (long) root));
+
+ for (next=root->used; next ;)
+ {
+ old=next; next= next->next ;
+ my_claim(old);
+ }
+
+ for (next=root->free ; next ;)
+ {
+ old=next; next= next->next;
+ my_claim(old);
+ }
+
+ DBUG_VOID_RETURN;
+}
+
+
+/*
+ Deallocate everything used by alloc_root or just move
+ used blocks to free list if called with MY_USED_TO_FREE
+
+ SYNOPSIS
+ free_root()
+ root Memory root
+ MyFlags Flags for what should be freed:
+
+ MY_MARK_BLOCKS_FREED Don't free blocks, just mark them free
+ MY_KEEP_PREALLOC If this is not set, then free also the
+ preallocated block
+
+ NOTES
+ One can call this function either with root block initialised with
+ init_alloc_root() or with a zero()-ed block.
+ It's also safe to call this multiple times with the same mem_root.
+*/
+
+void free_root(MEM_ROOT *root, myf MyFlags)
+{
+ USED_MEM *next,*old;
+ DBUG_ENTER("free_root");
+ DBUG_PRINT("enter",("root: 0x%lx flags: %u", (long) root, (uint) MyFlags));
+
+ if (MyFlags & MY_MARK_BLOCKS_FREE)
+ {
+ mark_blocks_free(root);
+ DBUG_VOID_RETURN;
+ }
+ if (!(MyFlags & MY_KEEP_PREALLOC))
+ root->pre_alloc=0;
+
+ for (next=root->used; next ;)
+ {
+ old=next; next= next->next ;
+ if (old != root->pre_alloc)
+ {
+ old->left= old->size;
+ TRASH_MEM(old);
+ my_free(old);
+ }
+ }
+ for (next=root->free ; next ;)
+ {
+ old=next; next= next->next;
+ if (old != root->pre_alloc)
+ {
+ old->left= old->size;
+ TRASH_MEM(old);
+ my_free(old);
+ }
+ }
+ root->used=root->free=0;
+ if (root->pre_alloc)
+ {
+ root->free=root->pre_alloc;
+ root->free->left=root->pre_alloc->size-(uint)ALIGN_SIZE(sizeof(USED_MEM));
+ root->allocated_size= root->pre_alloc->size;
+ TRASH_MEM(root->pre_alloc);
+ root->free->next=0;
+ }
+ else
+ root->allocated_size= 0;
+ root->block_num= 4;
+ root->first_block_usage= 0;
+ DBUG_VOID_RETURN;
+}
+
+
+char *strdup_root(MEM_ROOT *root, const char *str)
+{
+ return strmake_root(root, str, strlen(str));
+}
+
+
+char *strmake_root(MEM_ROOT *root, const char *str, size_t len)
+{
+ char *pos;
+ if ((pos=alloc_root(root,len+1)))
+ {
+ memcpy(pos,str,len);
+ pos[len]=0;
+ }
+ return pos;
+}
+
+
+void *memdup_root(MEM_ROOT *root, const void *str, size_t len)
+{
+ char *pos;
+ if ((pos=alloc_root(root,len)))
+ memcpy(pos,str,len);
+ return pos;
+}
+
+/**
+ Check if the set max_capacity is exceeded if the requested
+ size is allocated
+
+ @param mem_root memory root to check for allocation
+ @param size requested size to be allocated
+
+ @retval
+ 1 Memory is available
+ @retval
+ 0 Memory is not available
+*/
+static inline my_bool is_mem_available(MEM_ROOT *mem_root, size_t size)
+{
+ if (mem_root->max_capacity)
+ {
+ if ((mem_root->allocated_size + size) > mem_root->max_capacity)
+ return 0;
+ }
+ return 1;
+}
+
+/**
+ set max_capacity for this mem_root. Should be called after init_alloc_root.
+ If the max_capacity specified is less than what is already pre_alloced
+ in init_alloc_root, only the future allocations are affected.
+
+ @param mem_root memory root to set the max capacity
+ @param max_value Maximum capacity this mem_root can hold
+*/
+void set_memroot_max_capacity(MEM_ROOT *mem_root, size_t max_value)
+{
+ DBUG_ASSERT(alloc_root_inited(mem_root));
+ mem_root->max_capacity= max_value;
+}
+
+/**
+ Enable/disable error reporting for exceeding max_capacity. If error
+ reporting is enabled, an error is flagged to indicate that the capacity
+ is exceeded. However allocation will still happen for the requested memory.
+
+ @param mem_root memory root
+ @param report_eroor set to true if error should be reported
+ else set to false
+*/
+void set_memroot_error_reporting(MEM_ROOT *mem_root, my_bool report_error)
+{
+ DBUG_ASSERT(alloc_root_inited(mem_root));
+ mem_root->error_for_capacity_exceeded= report_error;
+}
+
diff --git a/mysql/mysys/my_bit.c b/mysql/mysys/my_bit.c
new file mode 100644
index 0000000..d36f52b
--- /dev/null
+++ b/mysql/mysys/my_bit.c
@@ -0,0 +1,64 @@
+/* Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved.
+
+ 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; version 2 of the License.
+
+ 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include <my_global.h>
+
+#include <my_bit.h>
+
+const char _my_bits_nbits[256] = {
+ 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4,
+ 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
+ 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
+ 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
+ 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
+ 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
+ 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
+ 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
+ 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
+ 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
+ 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
+ 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
+ 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
+ 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
+ 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
+ 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8,
+};
+
+/*
+ perl -e 'print map{", 0x".unpack H2,pack B8,unpack b8,chr$_}(0..255)'
+*/
+const uchar _my_bits_reverse_table[256]={
+0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0, 0x10, 0x90, 0x50, 0xD0, 0x30,
+0xB0, 0x70, 0xF0, 0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8, 0x18, 0x98,
+0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8, 0x04, 0x84, 0x44, 0xC4, 0x24, 0xA4, 0x64,
+0xE4, 0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4, 0x0C, 0x8C, 0x4C, 0xCC,
+0x2C, 0xAC, 0x6C, 0xEC, 0x1C, 0x9C, 0x5C, 0xDC, 0x3C, 0xBC, 0x7C, 0xFC, 0x02,
+0x82, 0x42, 0xC2, 0x22, 0xA2, 0x62, 0xE2, 0x12, 0x92, 0x52, 0xD2, 0x32, 0xB2,
+0x72, 0xF2, 0x0A, 0x8A, 0x4A, 0xCA, 0x2A, 0xAA, 0x6A, 0xEA, 0x1A, 0x9A, 0x5A,
+0xDA, 0x3A, 0xBA, 0x7A, 0xFA, 0x06, 0x86, 0x46, 0xC6, 0x26, 0xA6, 0x66, 0xE6,
+0x16, 0x96, 0x56, 0xD6, 0x36, 0xB6, 0x76, 0xF6, 0x0E, 0x8E, 0x4E, 0xCE, 0x2E,
+0xAE, 0x6E, 0xEE, 0x1E, 0x9E, 0x5E, 0xDE, 0x3E, 0xBE, 0x7E, 0xFE, 0x01, 0x81,
+0x41, 0xC1, 0x21, 0xA1, 0x61, 0xE1, 0x11, 0x91, 0x51, 0xD1, 0x31, 0xB1, 0x71,
+0xF1, 0x09, 0x89, 0x49, 0xC9, 0x29, 0xA9, 0x69, 0xE9, 0x19, 0x99, 0x59, 0xD9,
+0x39, 0xB9, 0x79, 0xF9, 0x05, 0x85, 0x45, 0xC5, 0x25, 0xA5, 0x65, 0xE5, 0x15,
+0x95, 0x55, 0xD5, 0x35, 0xB5, 0x75, 0xF5, 0x0D, 0x8D, 0x4D, 0xCD, 0x2D, 0xAD,
+0x6D, 0xED, 0x1D, 0x9D, 0x5D, 0xDD, 0x3D, 0xBD, 0x7D, 0xFD, 0x03, 0x83, 0x43,
+0xC3, 0x23, 0xA3, 0x63, 0xE3, 0x13, 0x93, 0x53, 0xD3, 0x33, 0xB3, 0x73, 0xF3,
+0x0B, 0x8B, 0x4B, 0xCB, 0x2B, 0xAB, 0x6B, 0xEB, 0x1B, 0x9B, 0x5B, 0xDB, 0x3B,
+0xBB, 0x7B, 0xFB, 0x07, 0x87, 0x47, 0xC7, 0x27, 0xA7, 0x67, 0xE7, 0x17, 0x97,
+0x57, 0xD7, 0x37, 0xB7, 0x77, 0xF7, 0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F,
+0xEF, 0x1F, 0x9F, 0x5F, 0xDF, 0x3F, 0xBF, 0x7F, 0xFF
+};
+
diff --git a/mysql/mysys/my_bitmap.c b/mysql/mysys/my_bitmap.c
new file mode 100644
index 0000000..10a345c
--- /dev/null
+++ b/mysql/mysys/my_bitmap.c
@@ -0,0 +1,672 @@
+/*
+ Copyright (c) 2001, 2016, Oracle and/or its affiliates. All rights reserved.
+
+ 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; version 2 of the License.
+
+ 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */
+
+/*
+ Handling of uchar arrays as large bitmaps.
+
+ API limitations (or, rather asserted safety assumptions,
+ to encourage correct programming)
+
+ * the internal size is a set of 32 bit words
+ * the number of bits specified in creation can be any number > 0
+ a bitmap with zero bits can be created and initialized, but not used.
+ * there are THREAD safe versions of most calls called bitmap_lock_*
+
+ TODO:
+ Make assembler THREAD safe versions of these using test-and-set instructions
+
+ Original version created by Sergei Golubchik 2001 - 2004.
+ New version written and test program added and some changes to the interface
+ was made by Mikael Ronström 2005, with assistance of Tomas Ulin and Mats
+ Kindahl.
+*/
+
+#include "mysys_priv.h"
+#include "my_sys.h"
+#include <my_bitmap.h>
+#include <m_string.h>
+#include <my_bit.h>
+
+void create_last_word_mask(MY_BITMAP *map)
+{
+ /* Get the number of used bits (1..8) in the last byte */
+ unsigned int const used= 1U + ((map->n_bits-1U) & 0x7U);
+
+ /*
+ Create a mask with the upper 'unused' bits set and the lower 'used'
+ bits clear. The bits within each byte is stored in big-endian order.
+ */
+ unsigned char const mask= (~((1 << used) - 1)) & 255;
+
+ /*
+ The first bytes are to be set to zero since they represent real bits
+ in the bitvector. The last bytes are set to 0xFF since they represent
+ bytes not used by the bitvector. Finally the last byte contains bits
+ as set by the mask above.
+ */
+ unsigned char *ptr= (unsigned char*)&map->last_word_mask;
+
+ /* Avoid out-of-bounds read/write if we have zero bits. */
+ map->last_word_ptr= map->n_bits == 0 ? map->bitmap :
+ map->bitmap + no_words_in_map(map) - 1;
+
+ switch (no_bytes_in_map(map) & 3) {
+ case 1:
+ map->last_word_mask= ~0U;
+ ptr[0]= mask;
+ return;
+ case 2:
+ map->last_word_mask= ~0U;
+ ptr[0]= 0;
+ ptr[1]= mask;
+ return;
+ case 3:
+ map->last_word_mask= 0U;
+ ptr[2]= mask;
+ ptr[3]= 0xFFU;
+ return;
+ case 0:
+ map->last_word_mask= 0U;
+ ptr[3]= mask;
+ return;
+ }
+}
+
+
+static inline void bitmap_lock(MY_BITMAP *map MY_ATTRIBUTE((unused)))
+{
+ if (map->mutex)
+ mysql_mutex_lock(map->mutex);
+}
+
+
+static inline void bitmap_unlock(MY_BITMAP *map MY_ATTRIBUTE((unused)))
+{
+ if (map->mutex)
+ mysql_mutex_unlock(map->mutex);
+}
+
+
+static inline uint get_first_set(uint32 value, uint word_pos)
+{
+ uchar *byte_ptr= (uchar*)&value;
+ uchar byte_value;
+ uint byte_pos, bit_pos;
+
+ for (byte_pos=0; byte_pos < 4; byte_pos++, byte_ptr++)
+ {
+ byte_value= *byte_ptr;
+ if (byte_value)
+ {
+ for (bit_pos=0; ; bit_pos++)
+ if (byte_value & (1 << bit_pos))
+ return (word_pos*32) + (byte_pos*8) + bit_pos;
+ }
+ }
+ return MY_BIT_NONE;
+}
+
+
+static inline uint get_first_not_set(uint32 value, uint word_pos)
+{
+ uchar *byte_ptr= (uchar*)&value;
+ uchar byte_value;
+ uint byte_pos, bit_pos;
+
+ for (byte_pos=0; byte_pos < 4; byte_pos++, byte_ptr++)
+ {
+ byte_value= *byte_ptr;
+ if (byte_value != 0xFF)
+ {
+ for (bit_pos=0; ; bit_pos++)
+ if (!(byte_value & (1 << bit_pos)))
+ return (word_pos*32) + (byte_pos*8) + bit_pos;
+ }
+ }
+ return MY_BIT_NONE;
+}
+
+
+my_bool bitmap_init(MY_BITMAP *map, my_bitmap_map *buf, uint n_bits,
+ my_bool thread_safe MY_ATTRIBUTE((unused)))
+{
+ DBUG_ENTER("bitmap_init");
+ if (!buf)
+ {
+ uint size_in_bytes= bitmap_buffer_size(n_bits);
+ uint extra= 0;
+
+ if (thread_safe)
+ {
+ size_in_bytes= ALIGN_SIZE(size_in_bytes);
+ extra= sizeof(mysql_mutex_t);
+ }
+ map->mutex= 0;
+
+ if (!(buf= (my_bitmap_map*) my_malloc(key_memory_MY_BITMAP_bitmap,
+ size_in_bytes+extra, MYF(MY_WME))))
+ DBUG_RETURN(1);
+
+ if (thread_safe)
+ {
+ map->mutex= (mysql_mutex_t *) ((char*) buf + size_in_bytes);
+ mysql_mutex_init(key_BITMAP_mutex, map->mutex, MY_MUTEX_INIT_FAST);
+ }
+
+ }
+
+ else
+ {
+ DBUG_ASSERT(thread_safe == 0);
+ map->mutex= NULL;
+ }
+
+
+ map->bitmap= buf;
+ map->n_bits= n_bits;
+ create_last_word_mask(map);
+ bitmap_clear_all(map);
+ DBUG_RETURN(0);
+}
+
+
+void bitmap_free(MY_BITMAP *map)
+{
+ DBUG_ENTER("bitmap_free");
+ if (map->bitmap)
+ {
+ if (map->mutex)
+ mysql_mutex_destroy(map->mutex);
+
+ my_free(map->bitmap);
+ map->bitmap=0;
+ }
+ DBUG_VOID_RETURN;
+}
+
+
+/*
+ test if bit already set and set it if it was not (thread unsafe method)
+
+ SYNOPSIS
+ bitmap_fast_test_and_set()
+ MAP bit map struct
+ BIT bit number
+
+ RETURN
+ 0 bit was not set
+ !=0 bit was set
+*/
+
+my_bool bitmap_fast_test_and_set(MY_BITMAP *map, uint bitmap_bit)
+{
+ uchar *value= ((uchar*) map->bitmap) + (bitmap_bit / 8);
+ uchar bit= 1 << ((bitmap_bit) & 7);
+ uchar res= (*value) & bit;
+ *value|= bit;
+ return res;
+}
+
+
+/*
+ test if bit already set and set it if it was not (thread safe method)
+
+ SYNOPSIS
+ bitmap_fast_test_and_set()
+ map bit map struct
+ bitmap_bit bit number
+
+ RETURN
+ 0 bit was not set
+ !=0 bit was set
+*/
+
+my_bool bitmap_test_and_set(MY_BITMAP *map, uint bitmap_bit)
+{
+ my_bool res;
+ DBUG_ASSERT(map->bitmap && bitmap_bit < map->n_bits);
+ bitmap_lock(map);
+ res= bitmap_fast_test_and_set(map, bitmap_bit);
+ bitmap_unlock(map);
+ return res;
+}
+
+/*
+ test if bit already set and clear it if it was set(thread unsafe method)
+
+ SYNOPSIS
+ bitmap_fast_test_and_set()
+ MAP bit map struct
+ BIT bit number
+
+ RETURN
+ 0 bit was not set
+ !=0 bit was set
+*/
+
+my_bool bitmap_fast_test_and_clear(MY_BITMAP *map, uint bitmap_bit)
+{
+ uchar *byte= (uchar*) map->bitmap + (bitmap_bit / 8);
+ uchar bit= 1 << ((bitmap_bit) & 7);
+ uchar res= (*byte) & bit;
+ *byte&= ~bit;
+ return res;
+}
+
+
+my_bool bitmap_test_and_clear(MY_BITMAP *map, uint bitmap_bit)
+{
+ my_bool res;
+ DBUG_ASSERT(map->bitmap && bitmap_bit < map->n_bits);
+ bitmap_lock(map);
+ res= bitmap_fast_test_and_clear(map, bitmap_bit);
+ bitmap_unlock(map);
+ return res;
+}
+
+
+uint bitmap_set_next(MY_BITMAP *map)
+{
+ uint bit_found;
+ DBUG_ASSERT(map->bitmap);
+ if ((bit_found= bitmap_get_first(map)) != MY_BIT_NONE)
+ bitmap_set_bit(map, bit_found);
+ return bit_found;
+}
+
+
+/**
+ Set the specified number of bits in the bitmap buffer.
+
+ @param map [IN] Bitmap
+ @param prefix_size [IN] Number of bits to be set
+
+ @return void
+*/
+void bitmap_set_prefix(MY_BITMAP *map, uint prefix_size)
+{
+ uint prefix_bytes, prefix_bits, d;
+ uchar *m= (uchar *)map->bitmap;
+
+ DBUG_ASSERT(map->bitmap &&
+ (prefix_size <= map->n_bits || prefix_size == (uint) ~0));
+ set_if_smaller(prefix_size, map->n_bits);
+ if ((prefix_bytes= prefix_size / 8))
+ memset(m, 0xff, prefix_bytes);
+ m+= prefix_bytes;
+ if ((prefix_bits= prefix_size & 7))
+ {
+ *(m++)= (1 << prefix_bits)-1;
+ // As the prefix bits are set, lets count this byte too as a prefix byte.
+ prefix_bytes ++;
+ }
+ if ((d= no_bytes_in_map(map)-prefix_bytes))
+ memset(m, 0, d);
+}
+
+
+my_bool bitmap_is_prefix(const MY_BITMAP *map, uint prefix_size)
+{
+ uint prefix_bits= prefix_size % 32;
+ my_bitmap_map *word_ptr= map->bitmap, last_word;
+ my_bitmap_map *end_prefix= word_ptr + prefix_size / 32;
+ DBUG_ASSERT(word_ptr && prefix_size <= map->n_bits);
+
+ /* 1: Words that should be filled with 1 */
+ for (; word_ptr < end_prefix; word_ptr++)
+ if (*word_ptr != 0xFFFFFFFF)
+ return FALSE;
+
+ DBUG_ASSERT(map->n_bits > 0);
+ last_word= *map->last_word_ptr & ~map->last_word_mask;
+
+ /* 2: Word which contains the end of the prefix (if any) */
+ if (prefix_bits)
+ {
+ if (word_ptr == map->last_word_ptr)
+ return uint4korr((uchar*)&last_word) == (uint32)((1 << prefix_bits) - 1);
+ else if (uint4korr((uchar*)word_ptr) != (uint32)((1 << prefix_bits) - 1))
+ return FALSE;
+ word_ptr++;
+ }
+
+ /* 3: Words that should be filled with 0 */
+ for (; word_ptr < map->last_word_ptr; word_ptr++)
+ if (*word_ptr != 0)
+ return FALSE;
+
+ /*
+ We can end up here in two situations:
+ 1) We went through the whole bitmap in step 1. This will happen if the
+ whole bitmap is filled with 1 and prefix_size is a multiple of 32
+ (i.e. the prefix does not end in the middle of a word).
+ In this case word_ptr will be larger than map->last_word_ptr.
+ 2) We have gone through steps 1-3 and just need to check that also
+ the last word is 0.
+ */
+ return word_ptr > map->last_word_ptr || last_word == 0;
+}
+
+
+my_bool bitmap_is_set_all(const MY_BITMAP *map)
+{
+ my_bitmap_map *data_ptr= map->bitmap;
+ my_bitmap_map *end= map->last_word_ptr;
+
+ DBUG_ASSERT(map->n_bits > 0);
+ for (; data_ptr < end; data_ptr++)
+ if (*data_ptr != 0xFFFFFFFF)
+ return FALSE;
+ if ((*map->last_word_ptr | map->last_word_mask) != 0xFFFFFFFF)
+ return FALSE;
+ return TRUE;
+}
+
+
+my_bool bitmap_is_clear_all(const MY_BITMAP *map)
+{
+ my_bitmap_map *data_ptr= map->bitmap;
+ my_bitmap_map *end= map->last_word_ptr;
+
+ DBUG_ASSERT(map->n_bits > 0);
+ for (; data_ptr < end; data_ptr++)
+ if (*data_ptr)
+ return FALSE;
+ if (*map->last_word_ptr & ~map->last_word_mask)
+ return FALSE;
+ return TRUE;
+}
+
+/* Return TRUE if map1 is a subset of map2 */
+
+my_bool bitmap_is_subset(const MY_BITMAP *map1, const MY_BITMAP *map2)
+{
+ my_bitmap_map *m1= map1->bitmap, *m2= map2->bitmap, *end;
+
+ DBUG_ASSERT(map1->bitmap && map2->bitmap &&
+ map1->n_bits==map2->n_bits);
+
+ end= map1->last_word_ptr;
+ for (; m1 < end; m1++, m2++)
+ if (*m1 & ~(*m2))
+ return FALSE;
+
+ DBUG_ASSERT(map1->n_bits > 0);
+ DBUG_ASSERT(map2->n_bits > 0);
+
+ if ((*map1->last_word_ptr & ~map1->last_word_mask) &
+ ~(*map2->last_word_ptr & ~map2->last_word_mask))
+ return FALSE;
+ return TRUE;
+}
+
+/* True if bitmaps has any common bits */
+
+my_bool bitmap_is_overlapping(const MY_BITMAP *map1, const MY_BITMAP *map2)
+{
+ my_bitmap_map *m1= map1->bitmap, *m2= map2->bitmap, *end;
+
+ DBUG_ASSERT(map1->bitmap && map2->bitmap &&
+ map1->n_bits==map2->n_bits);
+
+ DBUG_ASSERT(map1->n_bits > 0);
+ DBUG_ASSERT(map2->n_bits > 0);
+
+ end= map1->last_word_ptr;
+ for (; m1 < end; m1++, m2++)
+ if (*m1 & *m2)
+ return TRUE;
+
+ if ((*map1->last_word_ptr & ~map1->last_word_mask) &
+ (*map2->last_word_ptr & ~map2->last_word_mask))
+ return TRUE;
+ return FALSE;
+}
+
+
+void bitmap_intersect(MY_BITMAP *map, const MY_BITMAP *map2)
+{
+ my_bitmap_map *to= map->bitmap, *from= map2->bitmap, *end;
+ uint len= no_words_in_map(map), len2 = no_words_in_map(map2);
+
+ DBUG_ASSERT(map->bitmap && map2->bitmap);
+
+ end= to + MY_MIN(len, len2);
+ for (; to < end; to++, from++)
+ *to &= *from;
+
+ if (len >= len2)
+ map->bitmap[len2 - 1] &= ~map2->last_word_mask;
+
+ if (len2 < len)
+ {
+ end+=len-len2;
+ for (; to < end; to++)
+ *to= 0;
+ }
+}
+
+
+/*
+ Set/clear all bits above a bit.
+
+ SYNOPSIS
+ bitmap_set_above()
+ map RETURN The bitmap to change.
+ from_byte The bitmap buffer byte offset to start with.
+ use_bit The bit value (1/0) to use for all upper bits.
+
+ NOTE
+ You can only set/clear full bytes.
+ The function is meant for the situation that you copy a smaller bitmap
+ to a bigger bitmap. Bitmap lengths are always multiple of eigth (the
+ size of a byte). Using 'from_byte' saves multiplication and division
+ by eight during parameter passing.
+
+ RETURN
+ void
+*/
+
+void bitmap_set_above(MY_BITMAP *map, uint from_byte, uint use_bit)
+{
+ uchar use_byte= use_bit ? 0xff : 0;
+ uchar *to= (uchar *)map->bitmap + from_byte;
+ uchar *end= (uchar *)map->bitmap + (map->n_bits+7)/8;
+
+ for (; to < end; to++)
+ *to= use_byte;
+}
+
+
+void bitmap_subtract(MY_BITMAP *map, const MY_BITMAP *map2)
+{
+ my_bitmap_map *to= map->bitmap, *from= map2->bitmap, *end;
+ DBUG_ASSERT(map->bitmap && map2->bitmap &&
+ map->n_bits==map2->n_bits);
+ DBUG_ASSERT(map->n_bits > 0);
+ end= map->last_word_ptr;
+
+ for (; to <= end; to++, from++)
+ *to &= ~(*from);
+}
+
+
+void bitmap_union(MY_BITMAP *map, const MY_BITMAP *map2)
+{
+ my_bitmap_map *to= map->bitmap, *from= map2->bitmap, *end;
+ DBUG_ASSERT(map->bitmap && map2->bitmap &&
+ map->n_bits==map2->n_bits);
+ DBUG_ASSERT(map->n_bits > 0);
+ end= map->last_word_ptr;
+
+ for (; to <= end; to++, from++)
+ *to |= *from;
+}
+
+
+void bitmap_xor(MY_BITMAP *map, const MY_BITMAP *map2)
+{
+ my_bitmap_map *to= map->bitmap, *from= map2->bitmap, *end;
+ DBUG_ASSERT(map->bitmap && map2->bitmap &&
+ map->n_bits==map2->n_bits);
+ DBUG_ASSERT(map->n_bits > 0);
+ end= map->last_word_ptr;
+
+ for (; to <= end; to++, from++)
+ *to ^= *from;
+}
+
+
+void bitmap_invert(MY_BITMAP *map)
+{
+ my_bitmap_map *to= map->bitmap, *end;
+ DBUG_ASSERT(map->bitmap);
+ DBUG_ASSERT(map->n_bits > 0);
+ end= map->last_word_ptr;
+
+ for (; to <= end; to++)
+ *to ^= 0xFFFFFFFF;
+}
+
+
+uint bitmap_bits_set(const MY_BITMAP *map)
+{
+ my_bitmap_map *data_ptr= map->bitmap;
+ my_bitmap_map *end= map->last_word_ptr;
+ uint res= 0;
+ DBUG_ASSERT(map->bitmap);
+ DBUG_ASSERT(map->n_bits > 0);
+
+ for (; data_ptr < end; data_ptr++)
+ res+= my_count_bits_uint32(*data_ptr);
+
+ /*Reset last bits to zero*/
+ res+= my_count_bits_uint32(*map->last_word_ptr & ~map->last_word_mask);
+ return res;
+}
+
+
+void bitmap_copy(MY_BITMAP *map, const MY_BITMAP *map2)
+{
+ my_bitmap_map *to= map->bitmap, *from= map2->bitmap, *end;
+ DBUG_ASSERT(map->bitmap && map2->bitmap &&
+ map->n_bits==map2->n_bits);
+ DBUG_ASSERT(map->n_bits > 0);
+ end= map->last_word_ptr;
+
+ for (; to <= end; to++, from++)
+ *to = *from;
+}
+
+
+uint bitmap_get_first_set(const MY_BITMAP *map)
+{
+ uint word_pos;
+ my_bitmap_map *data_ptr, *end= map->last_word_ptr;
+
+ DBUG_ASSERT(map->bitmap);
+ DBUG_ASSERT(map->n_bits > 0);
+ data_ptr= map->bitmap;
+
+ for (word_pos=0; data_ptr < end; data_ptr++, word_pos++)
+ if (*data_ptr)
+ return get_first_set(*data_ptr, word_pos);
+
+ return get_first_set(*map->last_word_ptr & ~map->last_word_mask, word_pos);
+}
+
+
+/**
+ Get the next set bit.
+
+ @param map Bitmap
+ @param bitmap_bit Bit to start search from
+
+ @return Index to first bit set after bitmap_bit
+*/
+
+uint bitmap_get_next_set(const MY_BITMAP *map, uint bitmap_bit)
+{
+ uint word_pos, byte_to_mask, i;
+ my_bitmap_map first_word;
+ unsigned char *ptr= (unsigned char*) &first_word;
+ my_bitmap_map *data_ptr, *end= map->last_word_ptr;
+
+ DBUG_ASSERT(map->bitmap);
+ DBUG_ASSERT(map->n_bits > 0);
+
+ /* Look for the next bit */
+ bitmap_bit++;
+ if (bitmap_bit >= map->n_bits)
+ return MY_BIT_NONE;
+ word_pos= bitmap_bit / 32;
+ data_ptr= map->bitmap + word_pos;
+ first_word= *data_ptr;
+
+ /* Mask out previous bits */
+ byte_to_mask= (bitmap_bit % 32) / 8;
+ for (i= 0; i < byte_to_mask; i++)
+ ptr[i]= 0;
+ ptr[byte_to_mask]&= 0xFFU << (bitmap_bit & 7);
+
+ if (data_ptr == end)
+ return get_first_set(first_word & ~map->last_word_mask, word_pos);
+
+ if (first_word)
+ return get_first_set(first_word, word_pos);
+
+ for (data_ptr++, word_pos++; data_ptr < end; data_ptr++, word_pos++)
+ if (*data_ptr)
+ return get_first_set(*data_ptr, word_pos);
+
+ return get_first_set(*end & ~map->last_word_mask, word_pos);
+}
+
+
+uint bitmap_get_first(const MY_BITMAP *map)
+{
+ uint word_pos;
+ my_bitmap_map *data_ptr, *end= map->last_word_ptr;
+
+ DBUG_ASSERT(map->bitmap);
+ DBUG_ASSERT(map->n_bits > 0);
+ data_ptr= map->bitmap;
+
+ for (word_pos=0; data_ptr < end; data_ptr++, word_pos++)
+ if (*data_ptr != 0xFFFFFFFF)
+ return get_first_not_set(*data_ptr, word_pos);
+
+ return get_first_not_set(*map->last_word_ptr | map->last_word_mask, word_pos);
+}
+
+
+uint bitmap_lock_set_next(MY_BITMAP *map)
+{
+ uint bit_found;
+ bitmap_lock(map);
+ bit_found= bitmap_set_next(map);
+ bitmap_unlock(map);
+ return bit_found;
+}
+
+
+void bitmap_lock_clear_bit(MY_BITMAP *map, uint bitmap_bit)
+{
+ bitmap_lock(map);
+ DBUG_ASSERT(map->bitmap && bitmap_bit < map->n_bits);
+ bitmap_clear_bit(map, bitmap_bit);
+ bitmap_unlock(map);
+}
diff --git a/mysql/mysys/my_chmod.c b/mysql/mysys/my_chmod.c
new file mode 100644
index 0000000..389960d
--- /dev/null
+++ b/mysql/mysys/my_chmod.c
@@ -0,0 +1,102 @@
+/* Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved.
+
+ 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; version 2 of the License.
+
+ 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., 51 Franklin Street, Fifth Floor, Boston,
+ MA 02110-1301, USA */
+
+
+#include "mysys_priv.h"
+#include "my_sys.h"
+#include "mysys_err.h"
+#include <my_dir.h>
+#include "my_thread_local.h"
+
+/*
+ Generate MY_MODE representation from perm_flags.
+
+ @param perm_flags Permission information
+
+ @return Permission in MY_STAT format
+*/
+
+MY_MODE get_file_perm(ulong perm_flags)
+{
+ MY_MODE file_perm= 0;
+ if (perm_flags <= 0)
+ return file_perm;
+
+#if defined _WIN32
+ if (perm_flags & (USER_READ | GROUP_READ | OTHERS_READ))
+ file_perm|= _S_IREAD;
+ if (perm_flags & (USER_WRITE | GROUP_WRITE | OTHERS_WRITE))
+ file_perm|= _S_IWRITE;
+#else
+ if (perm_flags & USER_READ)
+ file_perm|= S_IRUSR;
+ if (perm_flags & USER_WRITE)
+ file_perm|= S_IWUSR;
+ if (perm_flags & USER_EXECUTE)
+ file_perm|= S_IXUSR;
+ if (perm_flags & GROUP_READ)
+ file_perm|= S_IRGRP;
+ if (perm_flags & GROUP_WRITE)
+ file_perm|= S_IWGRP;
+ if (perm_flags & GROUP_EXECUTE)
+ file_perm|= S_IXGRP;
+ if (perm_flags & OTHERS_READ)
+ file_perm|= S_IROTH;
+ if (perm_flags & OTHERS_WRITE)
+ file_perm|= S_IWOTH;
+ if (perm_flags & OTHERS_EXECUTE)
+ file_perm|= S_IXOTH;
+#endif
+
+ return file_perm;
+}
+
+/*
+ my_chmod : Change permission on a file
+
+ @param filename : Name of the file
+ @param perm_flags : Permission information
+ @param my_flags : Error handling
+
+ @return
+ @retval TRUE : Error changing file permission
+ @retval FALSE : File permission changed successfully
+*/
+
+my_bool my_chmod(const char *filename, ulong perm_flags, myf my_flags)
+{
+ int ret_val;
+ MY_MODE file_perm;
+ DBUG_ENTER("my_chmod");
+ DBUG_ASSERT(filename && filename[0]);
+
+ file_perm= get_file_perm(perm_flags);
+#ifdef _WIN32
+ ret_val= _chmod(filename, file_perm);
+#else
+ ret_val= chmod(filename, file_perm);
+#endif
+
+ if (ret_val && (my_flags & (MY_FAE+MY_WME)))
+ {
+ char errbuf[MYSYS_STRERROR_SIZE];
+ set_my_errno(errno);
+ my_error(EE_CHANGE_PERMISSIONS, MYF(0), filename,
+ errno, my_strerror(errbuf, sizeof(errbuf), errno));
+ }
+
+ DBUG_RETURN(ret_val ? TRUE : FALSE);
+}
diff --git a/mysql/mysys/my_chsize.c b/mysql/mysys/my_chsize.c
new file mode 100644
index 0000000..ca388dc
--- /dev/null
+++ b/mysql/mysys/my_chsize.c
@@ -0,0 +1,106 @@
+/* Copyright (c) 2000, 2015, Oracle and/or its affiliates. All rights reserved.
+
+ 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; version 2 of the License.
+
+ 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include "mysys_priv.h"
+#include "my_sys.h"
+#include "mysys_err.h"
+#include "m_string.h"
+#include "my_thread_local.h"
+
+/*
+ Change size of file.
+
+ SYNOPSIS
+ my_chsize()
+ fd File descriptor
+ new_length New file size
+ filler If we don't have truncate, fill up all bytes after
+ new_length with this character
+ MyFlags Flags
+
+ DESCRIPTION
+ my_chsize() truncates file if shorter else fill with the filler character.
+ The function also changes the file pointer. Usually it points to the end
+ of the file after execution.
+
+ RETURN VALUE
+ 0 Ok
+ 1 Error
+*/
+int my_chsize(File fd, my_off_t newlength, int filler, myf MyFlags)
+{
+ my_off_t oldsize;
+ uchar buff[IO_SIZE];
+ DBUG_ENTER("my_chsize");
+ DBUG_PRINT("my",("fd: %d length: %lu MyFlags: %d",fd,(ulong) newlength,
+ MyFlags));
+
+ if ((oldsize= my_seek(fd, 0L, MY_SEEK_END, MYF(MY_WME+MY_FAE))) == newlength)
+ DBUG_RETURN(0);
+
+ DBUG_PRINT("info",("old_size: %ld", (ulong) oldsize));
+
+ if (oldsize > newlength)
+ {
+#ifdef _WIN32
+ if (my_win_chsize(fd, newlength))
+ {
+ set_my_errno(errno);
+ goto err;
+ }
+ DBUG_RETURN(0);
+#elif defined(HAVE_FTRUNCATE)
+ if (ftruncate(fd, (off_t) newlength))
+ {
+ set_my_errno(errno);
+ goto err;
+ }
+ DBUG_RETURN(0);
+#else
+ /*
+ Fill space between requested length and true length with 'filler'
+ We should never come here on any modern machine
+ */
+ if (my_seek(fd, newlength, MY_SEEK_SET, MYF(MY_WME+MY_FAE))
+ == MY_FILEPOS_ERROR)
+ {
+ goto err;
+ }
+ swap_variables(my_off_t, newlength, oldsize);
+#endif
+ }
+
+ /* Full file with 'filler' until it's as big as requested */
+ memset(buff, filler, IO_SIZE);
+ while (newlength-oldsize > IO_SIZE)
+ {
+ if (my_write(fd, buff, IO_SIZE, MYF(MY_NABP)))
+ goto err;
+ oldsize+= IO_SIZE;
+ }
+ if (my_write(fd,buff,(size_t) (newlength-oldsize), MYF(MY_NABP)))
+ goto err;
+ DBUG_RETURN(0);
+
+err:
+ DBUG_PRINT("error", ("errno: %d", errno));
+ if (MyFlags & MY_WME)
+ {
+ char errbuf[MYSYS_STRERROR_SIZE];
+ my_error(EE_CANT_CHSIZE, MYF(0),
+ my_errno(), my_strerror(errbuf, sizeof(errbuf), my_errno()));
+ }
+ DBUG_RETURN(1);
+} /* my_chsize */
diff --git a/mysql/mysys/my_compare.c b/mysql/mysys/my_compare.c
new file mode 100644
index 0000000..a23c2a3
--- /dev/null
+++ b/mysql/mysys/my_compare.c
@@ -0,0 +1,474 @@
+/* Copyright (c) 2011, 2014, Oracle and/or its affiliates. All rights reserved.
+
+ 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; version 2 of the License.
+
+ 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */
+
+#include <my_global.h>
+#include <m_ctype.h>
+#include <my_base.h>
+#include <my_compare.h>
+#include <my_sys.h>
+
+#define CMP_NUM(a,b) (((a) < (b)) ? -1 : ((a) == (b)) ? 0 : 1)
+
+int ha_compare_text(const CHARSET_INFO *charset_info, uchar *a, uint a_length,
+ uchar *b, uint b_length, my_bool part_key,
+ my_bool skip_end_space)
+{
+ if (!part_key)
+ return charset_info->coll->strnncollsp(charset_info, a, a_length,
+ b, b_length, (my_bool)!skip_end_space);
+ return charset_info->coll->strnncoll(charset_info, a, a_length,
+ b, b_length, part_key);
+}
+
+
+static int compare_bin(uchar *a, uint a_length, uchar *b, uint b_length,
+ my_bool part_key, my_bool skip_end_space)
+{
+ uint length= MY_MIN(a_length, b_length);
+ uchar *end= a+ length;
+ int flag;
+
+ while (a < end)
+ if ((flag= (int) *a++ - (int) *b++))
+ return flag;
+ if (part_key && b_length < a_length)
+ return 0;
+ if (skip_end_space && a_length != b_length)
+ {
+ int swap= 1;
+ /*
+ We are using space compression. We have to check if longer key
+ has next character < ' ', in which case it's less than the shorter
+ key that has an implicite space afterwards.
+
+ This code is identical to the one in
+ strings/ctype-simple.c:my_strnncollsp_simple
+ */
+ if (a_length < b_length)
+ {
+ /* put shorter key in a */
+ a_length= b_length;
+ a= b;
+ swap= -1; /* swap sign of result */
+ }
+ for (end= a + a_length-length; a < end ; a++)
+ {
+ if (*a != ' ')
+ return (*a < ' ') ? -swap : swap;
+ }
+ return 0;
+ }
+ return (int) (a_length-b_length);
+}
+
+
+/*
+ Compare two keys
+
+ SYNOPSIS
+ ha_key_cmp()
+ keyseg Array of key segments of key to compare
+ a First key to compare, in format from _mi_pack_key()
+ This is normally key specified by user
+ b Second key to compare. This is always from a row
+ key_length Length of key to compare. This can be shorter than
+ a to just compare sub keys
+ next_flag How keys should be compared
+ If bit SEARCH_FIND is not set the keys includes the row
+ position and this should also be compared
+ diff_pos OUT Number of first keypart where values differ, counting
+ from one.
+ diff_pos[1] OUT (b + diff_pos[1]) points to first value in tuple b
+ that is different from corresponding value in tuple a.
+
+ EXAMPLES
+ Example1: if the function is called for tuples
+ ('aaa','bbb') and ('eee','fff'), then
+ diff_pos[0] = 1 (as 'aaa' != 'eee')
+ diff_pos[1] = 0 (offset from beggining of tuple b to 'eee' keypart).
+
+ Example2: if the index function is called for tuples
+ ('aaa','bbb') and ('aaa','fff'),
+ diff_pos[0] = 2 (as 'aaa' != 'eee')
+ diff_pos[1] = 3 (offset from beggining of tuple b to 'fff' keypart,
+ here we assume that first key part is CHAR(3) NOT NULL)
+
+ NOTES
+ Number-keys can't be splited
+
+ RETURN VALUES
+ <0 If a < b
+ 0 If a == b
+ >0 If a > b
+*/
+
+#define FCMP(A,B) ((int) (A) - (int) (B))
+
+int ha_key_cmp(HA_KEYSEG *keyseg, uchar *a,
+ uchar *b, uint key_length, uint nextflag,
+ uint *diff_pos)
+{
+ int flag;
+ int16 s_1,s_2;
+ int32 l_1,l_2;
+ uint32 u_1,u_2;
+ float f_1,f_2;
+ double d_1,d_2;
+ uint next_key_length;
+ uchar *orig_b= b;
+
+ *diff_pos=0;
+ for ( ; (int) key_length >0 ; key_length=next_key_length, keyseg++)
+ {
+ uchar *end;
+ uint piks=! (keyseg->flag & HA_NO_SORT);
+ (*diff_pos)++;
+ diff_pos[1]= (uint)(b - orig_b);
+
+ /* Handle NULL part */
+ if (keyseg->null_bit)
+ {
+ key_length--;
+ if (*a != *b && piks)
+ {
+ flag = (int) *a - (int) *b;
+ return ((keyseg->flag & HA_REVERSE_SORT) ? -flag : flag);
+ }
+ b++;
+ if (!*a++) /* If key was NULL */
+ {
+ if (nextflag == (SEARCH_FIND | SEARCH_UPDATE))
+ nextflag=SEARCH_SAME; /* Allow duplicate keys */
+ else if (nextflag & SEARCH_NULL_ARE_NOT_EQUAL)
+ {
+ /*
+ This is only used from mi_check() to calculate cardinality.
+ It can't be used when searching for a key as this would cause
+ compare of (a,b) and (b,a) to return the same value.
+ */
+ return -1;
+ }
+ next_key_length=key_length;
+ continue; /* To next key part */
+ }
+ }
+ end= a + MY_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=ha_compare_text(keyseg->charset,a,a_length,b,b_length,
+ (my_bool) ((nextflag & SEARCH_PREFIX) &&
+ next_key_length <= 0),
+ (my_bool)!(nextflag & SEARCH_PREFIX))))
+ return ((keyseg->flag & HA_REVERSE_SORT) ? -flag : flag);
+ a+=a_length;
+ b+=b_length;
+ break;
+ }
+ else
+ {
+ uint length=(uint) (end-a), a_length=length, b_length=length;
+ if (piks &&
+ (flag= ha_compare_text(keyseg->charset, a, a_length, b, b_length,
+ (my_bool) ((nextflag & SEARCH_PREFIX) &&
+ next_key_length <= 0),
+ (my_bool)!(nextflag & SEARCH_PREFIX))))
+ return ((keyseg->flag & HA_REVERSE_SORT) ? -flag : flag);
+ a=end;
+ b+=length;
+ }
+ break;
+ case HA_KEYTYPE_BINARY:
+ case HA_KEYTYPE_BIT:
+ if (keyseg->flag & HA_SPACE_PACK)
+ {
+ int a_length,b_length,pack_length;
+ get_key_length(a_length,a);
+ get_key_pack_length(b_length,pack_length,b);
+ next_key_length=key_length-b_length-pack_length;
+
+ if (piks &&
+ (flag=compare_bin(a,a_length,b,b_length,
+ (my_bool) ((nextflag & SEARCH_PREFIX) &&
+ next_key_length <= 0),1)))
+ return ((keyseg->flag & HA_REVERSE_SORT) ? -flag : flag);
+ a+=a_length;
+ b+=b_length;
+ break;
+ }
+ else
+ {
+ uint length=keyseg->length;
+ if (piks &&
+ (flag=compare_bin(a,length,b,length,
+ (my_bool) ((nextflag & SEARCH_PREFIX) &&
+ next_key_length <= 0),0)))
+ return ((keyseg->flag & HA_REVERSE_SORT) ? -flag : flag);
+ a+=length;
+ b+=length;
+ }
+ break;
+ case HA_KEYTYPE_VARTEXT1:
+ case HA_KEYTYPE_VARTEXT2:
+ {
+ 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= ha_compare_text(keyseg->charset,a,a_length,b,b_length,
+ (my_bool) ((nextflag & SEARCH_PREFIX) &&
+ next_key_length <= 0),
+ (my_bool) ((nextflag & (SEARCH_FIND |
+ SEARCH_UPDATE)) ==
+ SEARCH_FIND &&
+ ! (keyseg->flag &
+ HA_END_SPACE_ARE_EQUAL)))))
+ return ((keyseg->flag & HA_REVERSE_SORT) ? -flag : flag);
+ a+= a_length;
+ b+= b_length;
+ break;
+ }
+ break;
+ case HA_KEYTYPE_VARBINARY1:
+ case HA_KEYTYPE_VARBINARY2:
+ {
+ int a_length,b_length,pack_length;
+ get_key_length(a_length,a);
+ get_key_pack_length(b_length,pack_length,b);
+ next_key_length=key_length-b_length-pack_length;
+
+ if (piks &&
+ (flag=compare_bin(a,a_length,b,b_length,
+ (my_bool) ((nextflag & SEARCH_PREFIX) &&
+ next_key_length <= 0), 0)))
+ return ((keyseg->flag & HA_REVERSE_SORT) ? -flag : flag);
+ a+=a_length;
+ b+=b_length;
+ }
+ break;
+ case HA_KEYTYPE_INT8:
+ {
+ int i_1= (int) *((signed char*) a);
+ int i_2= (int) *((signed char*) b);
+ if (piks && (flag = CMP_NUM(i_1,i_2)))
+ return ((keyseg->flag & HA_REVERSE_SORT) ? -flag : flag);
+ a= end;
+ b++;
+ break;
+ }
+ case HA_KEYTYPE_SHORT_INT:
+ s_1= mi_sint2korr(a);
+ s_2= mi_sint2korr(b);
+ if (piks && (flag = CMP_NUM(s_1,s_2)))
+ return ((keyseg->flag & HA_REVERSE_SORT) ? -flag : flag);
+ a= end;
+ b+= 2; /* sizeof(short int); */
+ break;
+ case HA_KEYTYPE_USHORT_INT:
+ {
+ uint16 us_1,us_2;
+ us_1= mi_sint2korr(a);
+ us_2= mi_sint2korr(b);
+ if (piks && (flag = CMP_NUM(us_1,us_2)))
+ return ((keyseg->flag & HA_REVERSE_SORT) ? -flag : flag);
+ a= end;
+ b+=2; /* sizeof(short int); */
+ break;
+ }
+ case HA_KEYTYPE_LONG_INT:
+ l_1= mi_sint4korr(a);
+ l_2= mi_sint4korr(b);
+ if (piks && (flag = CMP_NUM(l_1,l_2)))
+ return ((keyseg->flag & HA_REVERSE_SORT) ? -flag : flag);
+ a= end;
+ b+= 4; /* sizeof(long int); */
+ break;
+ case HA_KEYTYPE_ULONG_INT:
+ u_1= mi_sint4korr(a);
+ u_2= mi_sint4korr(b);
+ if (piks && (flag = CMP_NUM(u_1,u_2)))
+ return ((keyseg->flag & HA_REVERSE_SORT) ? -flag : flag);
+ a= end;
+ b+= 4; /* sizeof(long int); */
+ break;
+ case HA_KEYTYPE_INT24:
+ l_1=mi_sint3korr(a);
+ l_2=mi_sint3korr(b);
+ if (piks && (flag = CMP_NUM(l_1,l_2)))
+ return ((keyseg->flag & HA_REVERSE_SORT) ? -flag : flag);
+ a= end;
+ b+= 3;
+ break;
+ case HA_KEYTYPE_UINT24:
+ l_1=mi_uint3korr(a);
+ l_2=mi_uint3korr(b);
+ if (piks && (flag = CMP_NUM(l_1,l_2)))
+ return ((keyseg->flag & HA_REVERSE_SORT) ? -flag : flag);
+ a= end;
+ b+= 3;
+ break;
+ case HA_KEYTYPE_FLOAT:
+ mi_float4get(f_1,a);
+ mi_float4get(f_2,b);
+ /*
+ The following may give a compiler warning about floating point
+ comparison not being safe, but this is ok in this context as
+ we are bascily doing sorting
+ */
+ if (piks && (flag = CMP_NUM(f_1,f_2)))
+ return ((keyseg->flag & HA_REVERSE_SORT) ? -flag : flag);
+ a= end;
+ b+= 4; /* sizeof(float); */
+ break;
+ case HA_KEYTYPE_DOUBLE:
+ mi_float8get(d_1,a);
+ mi_float8get(d_2,b);
+ /*
+ The following may give a compiler warning about floating point
+ comparison not being safe, but this is ok in this context as
+ we are bascily doing sorting
+ */
+ if (piks && (flag = CMP_NUM(d_1,d_2)))
+ return ((keyseg->flag & HA_REVERSE_SORT) ? -flag : flag);
+ a= end;
+ b+= 8; /* sizeof(double); */
+ break;
+ case HA_KEYTYPE_NUM: /* Numeric key */
+ {
+ int swap_flag= 0;
+ int alength,blength;
+
+ if (keyseg->flag & HA_REVERSE_SORT)
+ {
+ swap_variables(uchar*, a, b);
+ swap_flag=1; /* Remember swap of a & b */
+ end= a+ (int) (end-b);
+ }
+ if (keyseg->flag & HA_SPACE_PACK)
+ {
+ alength= *a++; blength= *b++;
+ end=a+alength;
+ next_key_length=key_length-blength-1;
+ }
+ else
+ {
+ alength= (int) (end-a);
+ blength=keyseg->length;
+ /* remove pre space from keys */
+ for ( ; alength && *a == ' ' ; a++, alength--) ;
+ for ( ; blength && *b == ' ' ; b++, blength--) ;
+ }
+ if (piks)
+ {
+ if (*a == '-')
+ {
+ if (*b != '-')
+ return -1;
+ a++; b++;
+ swap_variables(uchar*, a, b);
+ swap_variables(int, alength, blength);
+ swap_flag=1-swap_flag;
+ alength--; blength--;
+ end=a+alength;
+ }
+ else if (*b == '-')
+ return 1;
+ while (alength && (*a == '+' || *a == '0'))
+ {
+ a++; alength--;
+ }
+ while (blength && (*b == '+' || *b == '0'))
+ {
+ b++; blength--;
+ }
+ if (alength != blength)
+ return (alength < blength) ? -1 : 1;
+ while (a < end)
+ if (*a++ != *b++)
+ return ((int) a[-1] - (int) b[-1]);
+ }
+ else
+ {
+ b+=(end-a);
+ a=end;
+ }
+
+ if (swap_flag) /* Restore pointers */
+ swap_variables(uchar*, a, b);
+ break;
+ }
+ 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;
+ }
+ 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/mysql/mysys/my_compress.c b/mysql/mysys/my_compress.c
new file mode 100644
index 0000000..1869560
--- /dev/null
+++ b/mysql/mysys/my_compress.c
@@ -0,0 +1,267 @@
+/* Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
+
+ 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; version 2 of the License.
+
+ 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+/* Written by Sinisa Milivojevic <sinisa@mysql.com> */
+
+#include <my_global.h>
+#include <mysys_priv.h>
+#ifdef HAVE_COMPRESS
+#include <my_sys.h>
+#include <m_string.h>
+#include <zlib.h>
+
+/*
+ This replaces the packet with a compressed packet
+
+ SYNOPSIS
+ my_compress()
+ packet Data to compress. This is is replaced with the compressed data.
+ len Length of data to compress at 'packet'
+ complen out: 0 if packet was not compressed
+
+ RETURN
+ 1 error. 'len' is not changed'
+ 0 ok. In this case 'len' contains the size of the compressed packet
+*/
+
+my_bool my_compress(uchar *packet, size_t *len, size_t *complen)
+{
+ DBUG_ENTER("my_compress");
+ if (*len < MIN_COMPRESS_LENGTH)
+ {
+ *complen=0;
+ DBUG_PRINT("note",("Packet too short: Not compressed"));
+ }
+ else
+ {
+ uchar *compbuf=my_compress_alloc(packet,len,complen);
+ if (!compbuf)
+ DBUG_RETURN(*complen ? 0 : 1);
+ memcpy(packet,compbuf,*len);
+ my_free(compbuf);
+ }
+ DBUG_RETURN(0);
+}
+
+
+uchar *my_compress_alloc(const uchar *packet, size_t *len, size_t *complen)
+{
+ uchar *compbuf;
+ uLongf tmp_complen;
+ int res;
+ *complen= *len * 120 / 100 + 12;
+
+ if (!(compbuf= (uchar *) my_malloc(key_memory_my_compress_alloc,
+ *complen, MYF(MY_WME))))
+ return 0; /* Not enough memory */
+
+ tmp_complen= (uint) *complen;
+ res= compress((Bytef*) compbuf, &tmp_complen, (Bytef*) packet, (uLong) *len);
+ *complen= tmp_complen;
+
+ if (res != Z_OK)
+ {
+ my_free(compbuf);
+ return 0;
+ }
+
+ if (*complen >= *len)
+ {
+ *complen= 0;
+ my_free(compbuf);
+ DBUG_PRINT("note",("Packet got longer on compression; Not compressed"));
+ return 0;
+ }
+ /* Store length of compressed packet in *len */
+ swap_variables(size_t, *len, *complen);
+ return compbuf;
+}
+
+
+/*
+ Uncompress packet
+
+ SYNOPSIS
+ my_uncompress()
+ packet Compressed data. This is is replaced with the orignal data.
+ len Length of compressed data
+ complen Length of the packet buffer (must be enough for the original
+ data)
+
+ RETURN
+ 1 error
+ 0 ok. In this case 'complen' contains the updated size of the
+ real data.
+*/
+
+my_bool my_uncompress(uchar *packet, size_t len, size_t *complen)
+{
+ uLongf tmp_complen;
+ DBUG_ENTER("my_uncompress");
+
+ if (*complen) /* If compressed */
+ {
+ uchar *compbuf= (uchar *) my_malloc(key_memory_my_compress_alloc,
+ *complen,MYF(MY_WME));
+ int error;
+ if (!compbuf)
+ DBUG_RETURN(1); /* Not enough memory */
+
+ tmp_complen= (uint) *complen;
+ error= uncompress((Bytef*) compbuf, &tmp_complen, (Bytef*) packet,
+ (uLong) len);
+ *complen= tmp_complen;
+ if (error != Z_OK)
+ { /* Probably wrong packet */
+ DBUG_PRINT("error",("Can't uncompress packet, error: %d",error));
+ my_free(compbuf);
+ DBUG_RETURN(1);
+ }
+ memcpy(packet, compbuf, *complen);
+ my_free(compbuf);
+ }
+ else
+ *complen= len;
+ DBUG_RETURN(0);
+}
+
+/*
+ Internal representation of the frm blob is:
+
+ ver 4 bytes
+ orglen 4 bytes
+ complen 4 bytes
+*/
+
+#define BLOB_HEADER 12
+
+
+/*
+ packfrm is a method used to compress the frm file for storage in a
+ handler. This method was developed for the NDB handler and has been moved
+ here to serve also other uses.
+
+ SYNOPSIS
+ packfrm()
+ data Data reference to frm file data.
+ len Length of frm file data
+ out:pack_data Reference to the pointer to the packed frm data
+ out:pack_len Length of packed frm file data
+
+ NOTES
+ data is replaced with compressed content
+
+ RETURN VALUES
+ 0 Success
+ >0 Failure
+*/
+
+int packfrm(uchar *data, size_t len,
+ uchar **pack_data, size_t *pack_len)
+{
+ int error;
+ size_t org_len, comp_len, blob_len;
+ uchar *blob;
+ DBUG_ENTER("packfrm");
+ DBUG_PRINT("enter", ("data: 0x%lx len: %lu", (long) data, (ulong) len));
+
+ error= 1;
+ org_len= len;
+ if (my_compress((uchar*)data, &org_len, &comp_len))
+ goto err;
+
+ DBUG_PRINT("info", ("org_len: %lu comp_len: %lu", (ulong) org_len,
+ (ulong) comp_len));
+ DBUG_DUMP("compressed", data, org_len);
+
+ error= 2;
+ blob_len= BLOB_HEADER + org_len;
+ if (!(blob= (uchar*) my_malloc(key_memory_pack_frm,
+ blob_len,MYF(MY_WME))))
+ goto err;
+
+ /* Store compressed blob in machine independent format */
+ int4store(blob, 1);
+ int4store(blob+4, (uint32) len);
+ int4store(blob+8, (uint32) org_len); /* compressed length */
+
+ /* Copy frm data into blob, already in machine independent format */
+ memcpy(blob+BLOB_HEADER, data, org_len);
+
+ *pack_data= blob;
+ *pack_len= blob_len;
+ error= 0;
+
+ DBUG_PRINT("exit", ("pack_data: 0x%lx pack_len: %lu",
+ (long) *pack_data, (ulong) *pack_len));
+err:
+ DBUG_RETURN(error);
+
+}
+
+/*
+ unpackfrm is a method used to decompress the frm file received from a
+ handler. This method was developed for the NDB handler and has been moved
+ here to serve also other uses for other clustered storage engines.
+
+ SYNOPSIS
+ unpackfrm()
+ pack_data Data reference to packed frm file data
+ out:unpack_data Reference to the pointer to the unpacked frm data
+ out:unpack_len Length of unpacked frm file data
+
+ RETURN VALUES¨
+ 0 Success
+ >0 Failure
+*/
+
+int unpackfrm(uchar **unpack_data, size_t *unpack_len,
+ const uchar *pack_data)
+{
+ uchar *data;
+ size_t complen, orglen;
+ ulong ver;
+ DBUG_ENTER("unpackfrm");
+ DBUG_PRINT("enter", ("pack_data: 0x%lx", (long) pack_data));
+
+ ver= uint4korr(pack_data);
+ orglen= uint4korr(pack_data+4);
+ complen= uint4korr(pack_data+8);
+
+ DBUG_PRINT("blob",("ver: %lu complen: %lu orglen: %lu",
+ ver, (ulong) complen, (ulong) orglen));
+ DBUG_DUMP("blob->data", pack_data + BLOB_HEADER, complen);
+
+ if (ver != 1)
+ DBUG_RETURN(1);
+ if (!(data= my_malloc(key_memory_pack_frm,
+ MY_MAX(orglen, complen), MYF(MY_WME))))
+ DBUG_RETURN(2);
+ memcpy(data, pack_data + BLOB_HEADER, complen);
+
+ if (my_uncompress(data, complen, &orglen))
+ {
+ my_free(data);
+ DBUG_RETURN(3);
+ }
+
+ *unpack_data= data;
+ *unpack_len= orglen;
+
+ DBUG_PRINT("exit", ("frmdata: 0x%lx len: %lu", (long) *unpack_data,
+ (ulong) *unpack_len));
+ DBUG_RETURN(0);
+}
+#endif /* HAVE_COMPRESS */
diff --git a/mysql/mysys/my_copy.c b/mysql/mysys/my_copy.c
new file mode 100644
index 0000000..a416389
--- /dev/null
+++ b/mysql/mysys/my_copy.c
@@ -0,0 +1,153 @@
+/* Copyright (c) 2000, 2015, Oracle and/or its affiliates. All rights reserved.
+
+ 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; version 2 of the License.
+
+ 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include "mysys_priv.h"
+#include "my_sys.h"
+#include <my_dir.h> /* for stat */
+#include <m_string.h>
+#include "mysys_err.h"
+#include "my_thread_local.h"
+
+#ifndef _WIN32
+#include <utime.h>
+#else
+#include <sys/utime.h>
+#endif
+
+/*
+ int my_copy(const char *from, const char *to, myf MyFlags)
+
+ NOTES
+ Ordinary ownership and accesstimes are copied from 'from-file'
+ If MyFlags & MY_HOLD_ORIGINAL_MODES is set and to-file exists then
+ the modes of to-file isn't changed
+ If MyFlags & MY_DONT_OVERWRITE_FILE is set, we will give an error
+ if the file existed.
+
+ WARNING
+ Don't set MY_FNABP or MY_NABP bits on when calling this function !
+
+ RETURN
+ 0 ok
+ # Error
+
+*/
+
+int my_copy(const char *from, const char *to, myf MyFlags)
+{
+ size_t Count;
+ my_bool new_file_stat= 0; /* 1 if we could stat "to" */
+ int create_flag;
+ File from_file,to_file;
+ uchar buff[IO_SIZE];
+ MY_STAT stat_buff,new_stat_buff;
+ DBUG_ENTER("my_copy");
+ DBUG_PRINT("my",("from %s to %s MyFlags %d", from, to, MyFlags));
+
+ from_file=to_file= -1;
+ memset(&new_stat_buff, 0, sizeof(MY_STAT));
+ DBUG_ASSERT(!(MyFlags & (MY_FNABP | MY_NABP))); /* for my_read/my_write */
+ if (MyFlags & MY_HOLD_ORIGINAL_MODES) /* Copy stat if possible */
+ new_file_stat= MY_TEST(my_stat((char*) to, &new_stat_buff, MYF(0)));
+
+ if ((from_file=my_open(from,O_RDONLY | O_SHARE,MyFlags)) >= 0)
+ {
+ if (!my_stat(from, &stat_buff, MyFlags))
+ {
+ set_my_errno(errno);
+ goto err;
+ }
+ if (MyFlags & MY_HOLD_ORIGINAL_MODES && new_file_stat)
+ stat_buff=new_stat_buff;
+ create_flag= (MyFlags & MY_DONT_OVERWRITE_FILE) ? O_EXCL : O_TRUNC;
+
+ if ((to_file= my_create(to,(int) stat_buff.st_mode,
+ O_WRONLY | create_flag | O_BINARY | O_SHARE,
+ MyFlags)) < 0)
+ goto err;
+
+ while ((Count=my_read(from_file, buff, sizeof(buff), MyFlags)) != 0)
+ {
+ if (Count == (uint) -1 ||
+ my_write(to_file,buff,Count,MYF(MyFlags | MY_NABP)))
+ goto err;
+ }
+
+ /* sync the destination file */
+ if (MyFlags & MY_SYNC)
+ {
+ if (my_sync(to_file, MyFlags))
+ goto err;
+ }
+
+ if (my_close(from_file,MyFlags) | my_close(to_file,MyFlags))
+ DBUG_RETURN(-1); /* Error on close */
+
+ /* Reinitialize closed fd, so they won't be closed again. */
+ from_file= -1;
+ to_file= -1;
+
+ /* Copy modes if possible */
+
+ if (MyFlags & MY_HOLD_ORIGINAL_MODES && !new_file_stat)
+ DBUG_RETURN(0); /* File copyed but not stat */
+ /* Copy modes */
+ if (chmod(to, stat_buff.st_mode & 07777))
+ {
+ set_my_errno(errno);
+ if (MyFlags & (MY_FAE+MY_WME))
+ {
+ char errbuf[MYSYS_STRERROR_SIZE];
+ my_error(EE_CHANGE_PERMISSIONS, MYF(0), from,
+ errno, my_strerror(errbuf, sizeof(errbuf), errno));
+ }
+ goto err;
+ }
+#if !defined(_WIN32)
+ /* Copy ownership */
+ if (chown(to, stat_buff.st_uid, stat_buff.st_gid))
+ {
+ set_my_errno(errno);
+ if (MyFlags & (MY_FAE+MY_WME))
+ {
+ char errbuf[MYSYS_STRERROR_SIZE];
+ my_error(EE_CHANGE_OWNERSHIP, MYF(0), from,
+ errno, my_strerror(errbuf, sizeof(errbuf), errno));
+ }
+ goto err;
+ }
+#endif
+
+ if (MyFlags & MY_COPYTIME)
+ {
+ struct utimbuf timep;
+ timep.actime = stat_buff.st_atime;
+ timep.modtime = stat_buff.st_mtime;
+ (void) utime((char*) to, &timep); /* last accessed and modified times */
+ }
+
+ DBUG_RETURN(0);
+ }
+
+err:
+ if (from_file >= 0) (void) my_close(from_file,MyFlags);
+ if (to_file >= 0)
+ {
+ (void) my_close(to_file, MyFlags);
+ /* attempt to delete the to-file we've partially written */
+ (void) my_delete(to, MyFlags);
+ }
+ DBUG_RETURN(-1);
+} /* my_copy */
diff --git a/mysql/mysys/my_create.c b/mysql/mysys/my_create.c
new file mode 100644
index 0000000..a140eb7
--- /dev/null
+++ b/mysql/mysys/my_create.c
@@ -0,0 +1,74 @@
+/* Copyright (c) 2000, 2015, Oracle and/or its affiliates. All rights reserved.
+
+ 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; version 2 of the License.
+
+ 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include "mysys_priv.h"
+#include <my_dir.h>
+#include "mysys_err.h"
+#include <errno.h>
+#include <my_sys.h>
+#include "my_thread_local.h"
+#if defined(_WIN32)
+#include <share.h>
+#endif
+
+ /*
+ ** Create a new file
+ ** Arguments:
+ ** Path-name of file
+ ** Read | write on file (umask value)
+ ** Read & Write on open file
+ ** Special flags
+ */
+
+
+File my_create(const char *FileName, int CreateFlags, int access_flags,
+ myf MyFlags)
+{
+ int fd, rc;
+ DBUG_ENTER("my_create");
+ DBUG_PRINT("my",("Name: '%s' CreateFlags: %d AccessFlags: %d MyFlags: %d",
+ FileName, CreateFlags, access_flags, MyFlags));
+#if defined(_WIN32)
+ fd= my_win_open(FileName, access_flags | O_CREAT);
+#else
+ fd= open((char *) FileName, access_flags | O_CREAT,
+ CreateFlags ? CreateFlags : my_umask);
+#endif
+
+ if ((MyFlags & MY_SYNC_DIR) && (fd >=0) &&
+ my_sync_dir_by_file(FileName, MyFlags))
+ {
+ my_close(fd, MyFlags);
+ fd= -1;
+ }
+
+ rc= my_register_filename(fd, FileName, FILE_BY_CREATE,
+ EE_CANTCREATEFILE, MyFlags);
+ /*
+ my_register_filename() may fail on some platforms even if the call to
+ *open() above succeeds. In this case, don't leave the stale file because
+ callers assume the file to not exist if my_create() fails, so they don't
+ do any cleanups.
+ */
+ if (unlikely(fd >= 0 && rc < 0))
+ {
+ int tmp= my_errno();
+ my_close(fd, MyFlags);
+ my_delete(FileName, MyFlags);
+ set_my_errno(tmp);
+ }
+
+ DBUG_RETURN(rc);
+} /* my_create */
diff --git a/mysql/mysys/my_delete.c b/mysql/mysys/my_delete.c
new file mode 100644
index 0000000..7f82e60
--- /dev/null
+++ b/mysql/mysys/my_delete.c
@@ -0,0 +1,133 @@
+/* Copyright (c) 2000, 2015, Oracle and/or its affiliates. All rights reserved.
+
+ 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; version 2 of the License.
+
+ 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include "mysys_priv.h"
+#include "mysys_err.h"
+#include <my_sys.h>
+#include "my_thread_local.h"
+
+
+int my_delete(const char *name, myf MyFlags)
+{
+ int err;
+ DBUG_ENTER("my_delete");
+ DBUG_PRINT("my",("name %s MyFlags %d", name, MyFlags));
+
+ if ((err = unlink(name)) == -1)
+ {
+ set_my_errno(errno);
+ if (MyFlags & (MY_FAE+MY_WME))
+ {
+ char errbuf[MYSYS_STRERROR_SIZE];
+ my_error(EE_DELETE, MYF(0),
+ name, errno, my_strerror(errbuf, sizeof(errbuf), errno));
+ }
+ }
+ else if ((MyFlags & MY_SYNC_DIR) &&
+ my_sync_dir_by_file(name, MyFlags))
+ err= -1;
+ DBUG_RETURN(err);
+} /* my_delete */
+
+#if defined(_WIN32)
+/**
+ Delete file which is possibly not closed.
+
+ This function is intended to be used exclusively as a temporal solution
+ for Win NT in case when it is needed to delete a not closed file (note
+ that the file must be opened everywhere with FILE_SHARE_DELETE mode).
+ Deleting not-closed files can not be supported on Win 98|ME (and because
+ of that is considered harmful).
+
+ The function deletes the file with its preliminary renaming. This is
+ because when not-closed share-delete file is deleted it still lives on
+ a disk until it will not be closed everwhere. This may conflict with an
+ attempt to create a new file with the same name. The deleted file is
+ renamed to <name>.<num>.deleted where <name> - the initial name of the
+ file, <num> - a hexadecimal number chosen to make the temporal name to
+ be unique.
+
+ @param the name of the being deleted file
+ @param the flags instructing how to react on an error internally in
+ the function
+
+ @note The per-thread @c my_errno holds additional info for a caller to
+ decide how critical the error can be.
+
+ @retval
+ 0 ok
+ @retval
+ 1 error
+
+
+*/
+int nt_share_delete(const char *name, myf MyFlags)
+{
+ char buf[MAX_PATH + 20];
+ ulong cnt;
+ DBUG_ENTER("nt_share_delete");
+ DBUG_PRINT("my",("name %s MyFlags %d", name, MyFlags));
+
+ for (cnt= GetTickCount(); cnt; cnt--)
+ {
+ errno= 0;
+ sprintf(buf, "%s.%08X.deleted", name, cnt);
+ if (MoveFile(name, buf))
+ break;
+
+ if ((errno= GetLastError()) == ERROR_ALREADY_EXISTS)
+ continue;
+
+ /* This happened during tests with MERGE tables. */
+ if (errno == ERROR_ACCESS_DENIED)
+ continue;
+
+ DBUG_PRINT("warning", ("Failed to rename %s to %s, errno: %d",
+ name, buf, errno));
+ break;
+ }
+
+ if (errno == ERROR_FILE_NOT_FOUND)
+ {
+ set_my_errno(ENOENT); // marking, that `name' doesn't exist
+ }
+ else if (errno == 0)
+ {
+ if (DeleteFile(buf))
+ DBUG_RETURN(0);
+ /*
+ The below is more complicated than necessary. For some reason, the
+ assignment to my_errno clears the error number, which is retrieved
+ by GetLastError() (VC2005EE). Assigning to errno first, allows to
+ retrieve the correct value.
+ */
+ errno= GetLastError();
+ if (errno == 0)
+ set_my_errno(ENOENT); // marking, that `buf' doesn't exist
+ else
+ set_my_errno(errno);
+ }
+ else
+ set_my_errno(errno);
+
+ if (MyFlags & (MY_FAE+MY_WME))
+ {
+ char errbuf[MYSYS_STRERROR_SIZE];
+ my_error(EE_DELETE, MYF(0),
+ name, my_errno(), my_strerror(errbuf, sizeof(errbuf), my_errno()));
+ }
+ DBUG_RETURN(-1);
+}
+#endif
diff --git a/mysql/mysys/my_div.c b/mysql/mysys/my_div.c
new file mode 100644
index 0000000..a50e326
--- /dev/null
+++ b/mysql/mysys/my_div.c
@@ -0,0 +1,38 @@
+/* Copyright (c) 2000, 2015, Oracle and/or its affiliates. All rights reserved.
+
+ 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; version 2 of the License.
+
+ 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */
+
+#include "mysys_priv.h"
+#include "my_sys.h"
+
+/*
+ Get filename of file
+
+ SYNOPSIS
+ my_filename()
+ fd File descriptor
+*/
+
+char * my_filename(File fd)
+{
+ DBUG_ENTER("my_filename");
+ if ((uint) fd >= (uint) my_file_limit)
+ DBUG_RETURN((char*) "UNKNOWN");
+ if (fd >= 0 && my_file_info[fd].type != UNOPEN)
+ {
+ DBUG_RETURN(my_file_info[fd].name);
+ }
+ else
+ DBUG_RETURN((char*) "UNOPENED"); /* Debug message */
+}
diff --git a/mysql/mysys/my_error.c b/mysql/mysys/my_error.c
new file mode 100644
index 0000000..9262488
--- /dev/null
+++ b/mysql/mysys/my_error.c
@@ -0,0 +1,477 @@
+/* Copyright (c) 2000, 2015, Oracle and/or its affiliates. All rights reserved.
+
+ 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; version 2 of the License.
+
+ 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include "mysys_priv.h"
+#include "my_sys.h"
+#include "mysys_err.h"
+#include <m_string.h>
+#include <stdarg.h>
+#include <m_ctype.h>
+#include "my_base.h"
+#include "my_handler_errors.h"
+#include "my_thread_local.h"
+
+/* Max length of a error message. Should be kept in sync with MYSQL_ERRMSG_SIZE. */
+#define ERRMSGSIZE (512)
+
+/* Define some external variables for error handling */
+
+/*
+ WARNING!
+ my_error family functions have to be used according following rules:
+ - if message has no parameters, use my_message(ER_CODE, ER(ER_CODE), MYF(N))
+ - if message has parameters and is registered: my_error(ER_CODE, MYF(N), ...)
+ - for free-form messages use my_printf_error(ER_CODE, format, MYF(N), ...)
+
+ These three send their messages using error_handler_hook, which normally
+ means we'll send them to the client if we have one, or to error-log / stderr
+ otherwise.
+*/
+
+/*
+ Message texts are registered into a linked list of 'my_err_head' structs.
+ Each struct contains
+ (1.) a pointer to a function that returns C character strings with '\0'
+ termination
+ (2.) the error number for the first message in the array (array index 0)
+ (3.) the error number for the last message in the array
+ (array index (last - first)).
+ The function may return NULL pointers and pointers to empty strings.
+ Both kinds will be translated to "Unknown error %d.", if my_error()
+ is called with a respective error number.
+ The list of header structs is sorted in increasing order of error numbers.
+ Negative error numbers are allowed. Overlap of error numbers is not allowed.
+ Not registered error numbers will be translated to "Unknown error %d.".
+*/
+static struct my_err_head
+{
+ struct my_err_head *meh_next; /* chain link */
+ const char* (*get_errmsg) (int); /* returns error message format */
+ int meh_first; /* error number matching array slot 0 */
+ int meh_last; /* error number matching last slot */
+} my_errmsgs_globerrs = {NULL, get_global_errmsg, EE_ERROR_FIRST, EE_ERROR_LAST};
+
+static struct my_err_head *my_errmsgs_list= &my_errmsgs_globerrs;
+
+
+/**
+ Get a string describing a system or handler error. thread-safe.
+
+ @param buf a buffer in which to return the error message
+ @param len the size of the aforementioned buffer
+ @param nr the error number
+
+ @retval buf always buf. for signature compatibility with strerror(3).
+*/
+
+char *my_strerror(char *buf, size_t len, int nr)
+{
+ char *msg= NULL;
+
+ buf[0]= '\0'; /* failsafe */
+
+ /*
+ These (handler-) error messages are shared by perror, as required
+ by the principle of least surprise.
+ */
+ if ((nr >= HA_ERR_FIRST) && (nr <= HA_ERR_LAST))
+ msg= (char *) handler_error_messages[nr - HA_ERR_FIRST];
+
+ if (msg != NULL)
+ strmake(buf, msg, len - 1);
+ else
+ {
+ /*
+ On Windows, do things the Windows way. On a system that supports both
+ the GNU and the XSI variant, use whichever was configured (GNU); if
+ this choice is not advertised, use the default (POSIX/XSI). Testing
+ for __GNUC__ is not sufficient to determine whether this choice exists.
+ */
+#if defined(_WIN32)
+ strerror_s(buf, len, nr);
+ if (thr_winerr() != 0)
+ {
+ /*
+ If error code is EINVAL, and Windows Error code has been set, we append
+ the Windows error code to the message.
+ */
+ if (nr == EINVAL)
+ {
+ char tmp_buff[256] ;
+
+ my_snprintf(tmp_buff, sizeof(tmp_buff),
+ " [OS Error Code : 0x%x]", thr_winerr());
+
+ strcat_s(buf, len, tmp_buff);
+ }
+
+ set_thr_winerr(0);
+ }
+#elif ((defined _POSIX_C_SOURCE && (_POSIX_C_SOURCE >= 200112L)) || \
+ (defined _XOPEN_SOURCE && (_XOPEN_SOURCE >= 600))) && \
+ ! defined _GNU_SOURCE
+ strerror_r(nr, buf, len); /* I can build with or without GNU */
+#elif defined _GNU_SOURCE
+ char *r= strerror_r(nr, buf, len);
+ if (r != buf) /* Want to help, GNU? */
+ strmake(buf, r, len - 1); /* Then don't. */
+#else
+ strerror_r(nr, buf, len);
+#endif
+ }
+
+ /*
+ strerror() return values are implementation-dependent, so let's
+ be pragmatic.
+ */
+ if (!buf[0])
+ strmake(buf, "unknown error", len - 1);
+
+ return buf;
+}
+
+
+/**
+ @brief Get an error format string from one of the my_error_register()ed sets
+
+ @note
+ NULL values are possible even within a registered range.
+
+ @param nr Errno
+
+ @retval NULL if no message is registered for this error number
+ @retval str C-string
+*/
+
+const char *my_get_err_msg(int nr)
+{
+ const char *format;
+ struct my_err_head *meh_p;
+
+ /* Search for the range this error is in. */
+ for (meh_p= my_errmsgs_list; meh_p; meh_p= meh_p->meh_next)
+ if (nr <= meh_p->meh_last)
+ break;
+
+ /*
+ If we found the range this error number is in, get the format string.
+ If the string is empty, or a NULL pointer, or if we're out of return,
+ we return NULL.
+ */
+ if (!(format= (meh_p && (nr >= meh_p->meh_first)) ?
+ meh_p->get_errmsg(nr) : NULL) ||
+ !*format)
+ return NULL;
+
+ return format;
+}
+
+
+/**
+ Fill in and print a previously registered error message.
+
+ @note
+ Goes through the (sole) function registered in error_handler_hook
+
+ @param nr error number
+ @param MyFlags Flags
+ @param ... variable list matching that error format string
+*/
+
+void my_error(int nr, myf MyFlags, ...)
+{
+ const char *format;
+ va_list args;
+ char ebuff[ERRMSGSIZE];
+ DBUG_ENTER("my_error");
+ DBUG_PRINT("my", ("nr: %d MyFlags: %d errno: %d", nr, MyFlags, errno));
+
+ if (!(format = my_get_err_msg(nr)))
+ (void) my_snprintf(ebuff, sizeof(ebuff), "Unknown error %d", nr);
+ else
+ {
+ va_start(args,MyFlags);
+ (void) my_vsnprintf_ex(&my_charset_utf8_general_ci, ebuff,
+ sizeof(ebuff), format, args);
+ va_end(args);
+ }
+ (*error_handler_hook)(nr, ebuff, MyFlags);
+ DBUG_VOID_RETURN;
+}
+
+
+/**
+ Print an error message.
+
+ @note
+ Goes through the (sole) function registered in error_handler_hook
+
+ @param error error number
+ @param format format string
+ @param MyFlags Flags
+ @param ... variable list matching that error format string
+*/
+
+void my_printf_error(uint error, const char *format, myf MyFlags, ...)
+{
+ va_list args;
+ char ebuff[ERRMSGSIZE];
+ DBUG_ENTER("my_printf_error");
+ DBUG_PRINT("my", ("nr: %d MyFlags: %d errno: %d Format: %s",
+ error, MyFlags, errno, format));
+
+ va_start(args,MyFlags);
+ (void) my_vsnprintf_ex(&my_charset_utf8_general_ci, ebuff,
+ sizeof(ebuff), format, args);
+ va_end(args);
+ (*error_handler_hook)(error, ebuff, MyFlags);
+ DBUG_VOID_RETURN;
+}
+
+/**
+ Print an error message.
+
+ @note
+ Goes through the (sole) function registered in error_handler_hook
+
+ @param error error number
+ @param format format string
+ @param MyFlags Flags
+ @param ap variable list matching that error format string
+*/
+
+void my_printv_error(uint error, const char *format, myf MyFlags, va_list ap)
+{
+ char ebuff[ERRMSGSIZE];
+ DBUG_ENTER("my_printv_error");
+ DBUG_PRINT("my", ("nr: %d MyFlags: %d errno: %d format: %s",
+ error, MyFlags, errno, format));
+
+ (void) my_vsnprintf(ebuff, sizeof(ebuff), format, ap);
+ (*error_handler_hook)(error, ebuff, MyFlags);
+ DBUG_VOID_RETURN;
+}
+
+/**
+ Print an error message.
+
+ @note
+ Goes through the (sole) function registered in error_handler_hook
+
+ @param error error number
+ @param str error message
+ @param MyFlags Flags
+*/
+
+void my_message(uint error, const char *str, myf MyFlags)
+{
+ (*error_handler_hook)(error, str, MyFlags);
+}
+
+
+/**
+ Register error messages for use with my_error().
+
+ @description
+
+ The function is expected to return addresses to NUL-terminated
+ C character strings.
+ NULL pointers and empty strings ("") are allowed. These will be mapped to
+ "Unknown error" when my_error() is called with a matching error number.
+ This function registers the error numbers 'first' to 'last'.
+ No overlapping with previously registered error numbers is allowed.
+
+ @param get_errmsg function that returns error messages
+ @param first error number of first message in the array
+ @param last error number of last message in the array
+
+ @retval 0 OK
+ @retval != 0 Error
+*/
+
+int my_error_register(const char* (*get_errmsg) (int), int first, int last)
+{
+ struct my_err_head *meh_p;
+ struct my_err_head **search_meh_pp;
+
+ /* Allocate a new header structure. */
+ if (! (meh_p= (struct my_err_head*) my_malloc(key_memory_my_err_head,
+ sizeof(struct my_err_head),
+ MYF(MY_WME))))
+ return 1;
+ meh_p->get_errmsg= get_errmsg;
+ meh_p->meh_first= first;
+ meh_p->meh_last= last;
+
+ /* Search for the right position in the list. */
+ for (search_meh_pp= &my_errmsgs_list;
+ *search_meh_pp;
+ search_meh_pp= &(*search_meh_pp)->meh_next)
+ {
+ if ((*search_meh_pp)->meh_last > first)
+ break;
+ }
+
+ /* Error numbers must be unique. No overlapping is allowed. */
+ if (*search_meh_pp && ((*search_meh_pp)->meh_first <= last))
+ {
+ my_free(meh_p);
+ return 1;
+ }
+
+ /* Insert header into the chain. */
+ meh_p->meh_next= *search_meh_pp;
+ *search_meh_pp= meh_p;
+ return 0;
+}
+
+
+/**
+ Unregister formerly registered error messages.
+
+ @description
+
+ This function unregisters the error numbers 'first' to 'last'.
+ These must have been previously registered by my_error_register().
+ 'first' and 'last' must exactly match the registration.
+ If a matching registration is present, the header is removed from the
+ list.
+
+ @param first error number of first message
+ @param last error number of last message
+
+ @retval TRUE Error, no such number range registered.
+ @retval FALSE OK
+*/
+
+my_bool my_error_unregister(int first, int last)
+{
+ struct my_err_head *meh_p;
+ struct my_err_head **search_meh_pp;
+
+ /* Search for the registration in the list. */
+ for (search_meh_pp= &my_errmsgs_list;
+ *search_meh_pp;
+ search_meh_pp= &(*search_meh_pp)->meh_next)
+ {
+ if (((*search_meh_pp)->meh_first == first) &&
+ ((*search_meh_pp)->meh_last == last))
+ break;
+ }
+ if (! *search_meh_pp)
+ return TRUE;
+
+ /* Remove header from the chain. */
+ meh_p= *search_meh_pp;
+ *search_meh_pp= meh_p->meh_next;
+
+ /* Free the header. */
+ my_free(meh_p);
+
+ return FALSE;
+}
+
+
+/**
+ Unregister all formerly registered error messages.
+
+ @description
+
+ This function unregisters all error numbers that previously have
+ been previously registered by my_error_register().
+ All headers are removed from the list; the messages themselves are
+ not released here as they may be static.
+*/
+
+void my_error_unregister_all(void)
+{
+ struct my_err_head *cursor, *saved_next;
+
+ for (cursor= my_errmsgs_globerrs.meh_next; cursor != NULL; cursor= saved_next)
+ {
+ /* We need this ptr, but we're about to free its container, so save it. */
+ saved_next= cursor->meh_next;
+
+ my_free(cursor);
+ }
+ my_errmsgs_globerrs.meh_next= NULL; /* Freed in first iteration above. */
+
+ my_errmsgs_list= &my_errmsgs_globerrs;
+}
+
+/**
+ Issue a message locally (i.e. on the same host the program is
+ running on, don't transmit to a client).
+
+ This is the default value for local_message_hook, and therefore
+ the default printer for my_message_local(). mysys users should
+ not call this directly, but go through my_message_local() instead.
+
+ This printer prepends an Error/Warning/Note label to the string,
+ then prints it to stderr using my_message_stderr().
+ Since my_message_stderr() appends a '\n', the format string
+ should not end in a newline.
+
+ @param ll log level: (ERROR|WARNING|INFORMATION)_LEVEL
+ the printer may use these to filter for verbosity
+ @param format a format string a la printf. Should not end in '\n'
+ @param args parameters to go with that format string
+*/
+void my_message_local_stderr(enum loglevel ll,
+ const char *format, va_list args)
+{
+ char buff[1024];
+ size_t len;
+
+ DBUG_ENTER("my_message_local_stderr");
+
+ len= my_snprintf(buff, sizeof(buff), "[%s] ",
+ (ll == ERROR_LEVEL ? "ERROR" : ll == WARNING_LEVEL ?
+ "Warning" : "Note"));
+ my_vsnprintf(buff + len, sizeof(buff) - len, format, args);
+
+ my_message_stderr(0, buff, MYF(0));
+
+ DBUG_VOID_RETURN;
+}
+
+/**
+ Issue a message locally (i.e. on the same host the program is
+ running on, don't transmit to a client).
+
+ This goes through local_message_hook, i.e. by default, it calls
+ my_message_local_stderr() which prepends an Error/Warning/Note
+ label to the string, then prints it to stderr. More advanced
+ programs can use their own printers; mysqld for instance uses
+ its own error log facilities which prepend an ISO 8601 / RFC 3339
+ compliant timestamp etc.
+
+ @param ll log level: (ERROR|WARNING|INFORMATION)_LEVEL
+ the printer may use these to filter for verbosity
+ @param format a format string a la printf. Should not end in '\n'.
+ @param ... parameters to go with that format string
+*/
+
+void my_message_local(enum loglevel ll, const char *format, ...)
+{
+ va_list args;
+ DBUG_ENTER("local_print_error");
+
+ va_start(args, format);
+ (*local_message_hook)(ll, format, args);
+ va_end(args);
+
+ DBUG_VOID_RETURN;
+}
diff --git a/mysql/mysys/my_file.c b/mysql/mysys/my_file.c
new file mode 100644
index 0000000..b2faed0
--- /dev/null
+++ b/mysql/mysys/my_file.c
@@ -0,0 +1,144 @@
+/* Copyright (c) 2000, 2015, Oracle and/or its affiliates. All rights reserved.
+
+ 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; version 2 of the License.
+
+ 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include "mysys_priv.h"
+#include "my_static.h"
+#include <m_string.h>
+#ifdef HAVE_SYS_RESOURCE_H
+#include <sys/resource.h> /* RLIMIT_NOFILE */
+#endif
+
+/*
+ set how many open files we want to be able to handle
+
+ SYNOPSIS
+ set_maximum_open_files()
+ max_file_limit Files to open
+
+ NOTES
+ The request may not fulfilled becasue of system limitations
+
+ RETURN
+ Files available to open.
+ May be more or less than max_file_limit!
+*/
+
+#if defined(HAVE_GETRLIMIT)
+
+/*
+ This value is certainly wrong on all 64bit platforms,
+ and also wrong on many 32bit platforms.
+ It is better to get a compile error, than to use a wrong value.
+#ifndef RLIM_INFINITY
+#define RLIM_INFINITY ((uint) 0xffffffff)
+#endif
+*/
+
+static uint set_max_open_files(uint max_file_limit)
+{
+ struct rlimit rlimit;
+ uint old_cur;
+ DBUG_ENTER("set_max_open_files");
+ DBUG_PRINT("enter",("files: %u", max_file_limit));
+
+ if (!getrlimit(RLIMIT_NOFILE,&rlimit))
+ {
+ old_cur= (uint) rlimit.rlim_cur;
+ DBUG_PRINT("info", ("rlim_cur: %u rlim_max: %u",
+ (uint) rlimit.rlim_cur,
+ (uint) rlimit.rlim_max));
+ if (rlimit.rlim_cur == (rlim_t) RLIM_INFINITY)
+ rlimit.rlim_cur = max_file_limit;
+ if (rlimit.rlim_cur >= max_file_limit)
+ DBUG_RETURN(rlimit.rlim_cur); /* purecov: inspected */
+ rlimit.rlim_cur= rlimit.rlim_max= max_file_limit;
+ if (setrlimit(RLIMIT_NOFILE, &rlimit))
+ max_file_limit= old_cur; /* Use original value */
+ else
+ {
+ rlimit.rlim_cur= 0; /* Safety if next call fails */
+ (void) getrlimit(RLIMIT_NOFILE,&rlimit);
+ DBUG_PRINT("info", ("rlim_cur: %u", (uint) rlimit.rlim_cur));
+ if (rlimit.rlim_cur) /* If call didn't fail */
+ max_file_limit= (uint) rlimit.rlim_cur;
+ }
+ }
+ DBUG_PRINT("exit",("max_file_limit: %u", max_file_limit));
+ DBUG_RETURN(max_file_limit);
+}
+
+#else
+static uint set_max_open_files(uint max_file_limit)
+{
+ /* We don't know the limit. Return best guess */
+ return MY_MIN(max_file_limit, OS_FILE_LIMIT);
+}
+#endif
+
+
+/*
+ Change number of open files
+
+ SYNOPSIS:
+ my_set_max_open_files()
+ files Number of requested files
+
+ RETURN
+ number of files available for open
+*/
+
+uint my_set_max_open_files(uint files)
+{
+ struct st_my_file_info *tmp;
+ DBUG_ENTER("my_set_max_open_files");
+ DBUG_PRINT("enter",("files: %u my_file_limit: %u", files, my_file_limit));
+
+ files+= MY_FILE_MIN;
+ files= set_max_open_files(MY_MIN(files, OS_FILE_LIMIT));
+ if (files <= MY_NFILE)
+ DBUG_RETURN(files);
+
+ if (!(tmp= (struct st_my_file_info*) my_malloc(key_memory_my_file_info,
+ sizeof(*tmp) * files,
+ MYF(MY_WME))))
+ DBUG_RETURN(MY_NFILE);
+
+ /* Copy any initialized files */
+ memcpy((char*) tmp, (char*) my_file_info,
+ sizeof(*tmp) * MY_MIN(my_file_limit, files));
+ memset((tmp + my_file_limit), 0,
+ MY_MAX((int) (files - my_file_limit), 0) * sizeof(*tmp));
+ my_free_open_file_info(); /* Free if already allocated */
+ my_file_info= tmp;
+ my_file_limit= files;
+ DBUG_PRINT("exit",("files: %u", files));
+ DBUG_RETURN(files);
+}
+
+
+void my_free_open_file_info()
+{
+ DBUG_ENTER("my_free_file_info");
+ if (my_file_info != my_file_info_default)
+ {
+ /* Copy data back for my_print_open_files */
+ memcpy((char*) my_file_info_default, my_file_info,
+ sizeof(*my_file_info_default)* MY_NFILE);
+ my_free(my_file_info);
+ my_file_info= my_file_info_default;
+ my_file_limit= MY_NFILE;
+ }
+ DBUG_VOID_RETURN;
+}
diff --git a/mysql/mysys/my_fopen.c b/mysql/mysys/my_fopen.c
new file mode 100644
index 0000000..2196b47
--- /dev/null
+++ b/mysql/mysys/my_fopen.c
@@ -0,0 +1,379 @@
+/* Copyright (c) 2000, 2017, Oracle and/or its affiliates. All rights reserved.
+
+ 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; version 2 of the License.
+
+ 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */
+
+#include "mysys_priv.h"
+#include "my_static.h"
+#include <errno.h>
+#include "mysys_err.h"
+#include "my_thread_local.h"
+
+
+static void make_ftype(char * to,int flag);
+
+/*
+ Open a file as stream
+
+ SYNOPSIS
+ my_fopen()
+ FileName Path-name of file
+ Flags Read | write | append | trunc (like for open())
+ MyFlags Flags for handling errors
+
+ RETURN
+ 0 Error
+ # File handler
+*/
+
+FILE *my_fopen(const char *filename, int flags, myf MyFlags)
+{
+ FILE *fd;
+ char type[5];
+ char *dup_filename= NULL;
+ DBUG_ENTER("my_fopen");
+ DBUG_PRINT("my",("Name: '%s' flags: %d MyFlags: %d",
+ filename, flags, MyFlags));
+
+ make_ftype(type,flags);
+
+#ifdef _WIN32
+ fd= my_win_fopen(filename, type);
+#else
+ fd= fopen(filename, type);
+#endif
+ if (fd != 0)
+ {
+ /*
+ The test works if MY_NFILE < 128. The problem is that fileno() is char
+ on some OS (SUNOS). Actually the filename save isn't that important
+ so we can ignore if this doesn't work.
+ */
+
+ int filedesc= my_fileno(fd);
+ if ((uint)filedesc >= my_file_limit)
+ {
+ mysql_mutex_lock(&THR_LOCK_open);
+ my_stream_opened++;
+ mysql_mutex_unlock(&THR_LOCK_open);
+ DBUG_RETURN(fd); /* safeguard */
+ }
+ dup_filename= my_strdup(key_memory_my_file_info, filename, MyFlags);
+ if (dup_filename != NULL)
+ {
+ mysql_mutex_lock(&THR_LOCK_open);
+ my_file_info[filedesc].name= dup_filename;
+ my_stream_opened++;
+ my_file_total_opened++;
+ my_file_info[filedesc].type= STREAM_BY_FOPEN;
+ mysql_mutex_unlock(&THR_LOCK_open);
+ DBUG_PRINT("exit",("stream: 0x%lx", (long) fd));
+ DBUG_RETURN(fd);
+ }
+ (void) my_fclose(fd,MyFlags);
+ set_my_errno(ENOMEM);
+ }
+ else
+ set_my_errno(errno);
+ DBUG_PRINT("error",("Got error %d on open",my_errno()));
+ if (MyFlags & (MY_FFNF | MY_FAE | MY_WME))
+ {
+ char errbuf[MYSYS_STRERROR_SIZE];
+ my_error((flags & O_RDONLY) || (flags == O_RDONLY ) ? EE_FILENOTFOUND :
+ EE_CANTCREATEFILE,
+ MYF(0), filename,
+ my_errno(), my_strerror(errbuf, sizeof(errbuf), my_errno()));
+ }
+ DBUG_RETURN((FILE*) 0);
+} /* my_fopen */
+
+
+#if defined(_WIN32)
+
+static FILE *my_win_freopen(const char *path, const char *mode, FILE *stream)
+{
+ int handle_fd, fd= _fileno(stream);
+ HANDLE osfh;
+
+ DBUG_ASSERT(path && stream);
+
+ /* Services don't have stdout/stderr on Windows, so _fileno returns -1. */
+ if (fd < 0)
+ {
+ if (!freopen(path, mode, stream))
+ return NULL;
+
+ fd= _fileno(stream);
+ }
+
+ if ((osfh= CreateFile(path, GENERIC_READ | GENERIC_WRITE,
+ FILE_SHARE_READ | FILE_SHARE_WRITE |
+ FILE_SHARE_DELETE, NULL,
+ OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL,
+ NULL)) == INVALID_HANDLE_VALUE)
+ {
+ _close(fd);
+ return NULL;
+ }
+
+ if ((handle_fd= _open_osfhandle((intptr_t)osfh,
+ _O_APPEND | _O_TEXT)) == -1)
+ {
+ CloseHandle(osfh);
+ return NULL;
+ }
+
+ if (_dup2(handle_fd, fd) < 0)
+ {
+ CloseHandle(osfh);
+ return NULL;
+ }
+
+ _close(handle_fd);
+
+ return stream;
+}
+#else
+
+/**
+ Replacement for freopen() which will not close the stream until the
+ new file has been successfully opened. This gives the ability log
+ the reason for reopen's failure to the stream, and also to flush any
+ buffered messages already written to the stream, before terminating
+ the process.
+
+ After a new stream to path has been opened, its file descriptor is
+ obtained, and dup2() is used to associate the original stream with
+ the new file. The newly opened stream and associated file descriptor
+ is then closed with fclose().
+
+ @param path file which the stream should be opened to
+ @param mode FILE mode to use when opening new file
+ @param stream stream to reopen
+
+ @retval FILE stream being passed in if successful,
+ @retval nullptr otherwise
+ */
+static FILE *my_safe_freopen(const char *path, const char *mode, FILE *stream)
+{
+ int streamfd= -1;
+ FILE *pathstream= NULL;
+ int pathfd= -1;
+ int ds= -1;
+
+ DBUG_ASSERT(path != NULL && stream != NULL);
+ streamfd= fileno(stream);
+ if (streamfd == -1)
+ {
+ /* We have not done anything to the stream, but if we cannot
+ get its fd, it is probably in a bad state anyway... */
+ return NULL;
+ }
+ pathstream= fopen(path, mode);
+ if (pathstream == NULL)
+ {
+ /* Failed to open file for some reason. stream should still be
+ usable. */
+ return NULL;
+ }
+
+ pathfd= fileno(pathstream);
+ if (pathfd == -1)
+ {
+ fclose(pathstream);
+ return NULL;
+ }
+ do
+ {
+ ds= fflush(stream);
+ if (ds == 0)
+ {
+ ds= dup2(pathfd, streamfd);
+ }
+ }
+ while (ds == -1 && errno == EINTR);
+ fclose(pathstream);
+ return (ds == -1 ? NULL : stream);
+}
+#endif
+
+
+/**
+ Change the file associated with a file stream.
+
+ @param path Path to file.
+ @param mode Mode of the stream.
+ @param stream File stream.
+
+ @note
+ This function is used to redirect stdout and stderr to a file and
+ subsequently to close and reopen that file for log rotation.
+
+ @retval A FILE pointer on success. Otherwise, NULL.
+*/
+
+FILE *my_freopen(const char *path, const char *mode, FILE *stream)
+{
+ FILE *result;
+
+#if defined(_WIN32)
+ result= my_win_freopen(path, mode, stream);
+#else
+ result= my_safe_freopen(path, mode, stream);
+#endif
+
+ return result;
+}
+
+
+/* Close a stream */
+int my_fclose(FILE *fd, myf MyFlags)
+{
+ int err,file;
+ DBUG_ENTER("my_fclose");
+ DBUG_PRINT("my",("stream: 0x%lx MyFlags: %d", (long) fd, MyFlags));
+
+ mysql_mutex_lock(&THR_LOCK_open);
+ file= my_fileno(fd);
+#ifndef _WIN32
+ err= fclose(fd);
+#else
+ err= my_win_fclose(fd);
+#endif
+ if(err < 0)
+ {
+ set_my_errno(errno);
+ if (MyFlags & (MY_FAE | MY_WME))
+ {
+ char errbuf[MYSYS_STRERROR_SIZE];
+ my_error(EE_BADCLOSE, MYF(0), my_filename(file),
+ my_errno(), my_strerror(errbuf, sizeof(errbuf), my_errno()));
+ }
+ }
+ else
+ my_stream_opened--;
+ if ((uint) file < my_file_limit && my_file_info[file].type != UNOPEN)
+ {
+ my_file_info[file].type = UNOPEN;
+ my_free(my_file_info[file].name);
+ }
+ mysql_mutex_unlock(&THR_LOCK_open);
+ DBUG_RETURN(err);
+} /* my_fclose */
+
+
+ /* Make a stream out of a file handle */
+ /* Name may be 0 */
+
+FILE *my_fdopen(File Filedes, const char *name, int Flags, myf MyFlags)
+{
+ FILE *fd;
+ char type[5];
+ DBUG_ENTER("my_fdopen");
+ DBUG_PRINT("my",("Fd: %d Flags: %d MyFlags: %d",
+ Filedes, Flags, MyFlags));
+
+ make_ftype(type,Flags);
+#ifdef _WIN32
+ fd= my_win_fdopen(Filedes, type);
+#else
+ fd= fdopen(Filedes, type);
+#endif
+ if (!fd)
+ {
+ set_my_errno(errno);
+ if (MyFlags & (MY_FAE | MY_WME))
+ {
+ char errbuf[MYSYS_STRERROR_SIZE];
+ my_error(EE_CANT_OPEN_STREAM, MYF(0),
+ my_errno(), my_strerror(errbuf, sizeof(errbuf), my_errno()));
+ }
+ }
+ else
+ {
+ mysql_mutex_lock(&THR_LOCK_open);
+ my_stream_opened++;
+ if ((uint) Filedes < (uint) my_file_limit)
+ {
+ if (my_file_info[Filedes].type != UNOPEN)
+ {
+ my_file_opened--; /* File is opened with my_open ! */
+ }
+ else
+ {
+ my_file_info[Filedes].name= my_strdup(key_memory_my_file_info,
+ name,MyFlags);
+ }
+ my_file_info[Filedes].type = STREAM_BY_FDOPEN;
+ }
+ mysql_mutex_unlock(&THR_LOCK_open);
+ }
+
+ DBUG_PRINT("exit",("stream: 0x%lx", (long) fd));
+ DBUG_RETURN(fd);
+} /* my_fdopen */
+
+
+/*
+ Make a fopen() typestring from a open() type bitmap
+
+ SYNOPSIS
+ make_ftype()
+ to String for fopen() is stored here
+ flag Flag used by open()
+
+ IMPLEMENTATION
+ This routine attempts to find the best possible match
+ between a numeric option and a string option that could be
+ fed to fopen. There is not a 1 to 1 mapping between the two.
+
+ NOTE
+ On Unix, O_RDONLY is usually 0
+
+ MAPPING
+ r == O_RDONLY
+ w == O_WRONLY|O_TRUNC|O_CREAT
+ a == O_WRONLY|O_APPEND|O_CREAT
+ r+ == O_RDWR
+ w+ == O_RDWR|O_TRUNC|O_CREAT
+ a+ == O_RDWR|O_APPEND|O_CREAT
+*/
+
+static void make_ftype(char * to, int flag)
+{
+ /* check some possible invalid combinations */
+ DBUG_ASSERT((flag & (O_TRUNC | O_APPEND)) != (O_TRUNC | O_APPEND));
+ DBUG_ASSERT((flag & (O_WRONLY | O_RDWR)) != (O_WRONLY | O_RDWR));
+
+ if ((flag & (O_RDONLY|O_WRONLY)) == O_WRONLY)
+ *to++= (flag & O_APPEND) ? 'a' : 'w';
+ else if (flag & O_RDWR)
+ {
+ /* Add '+' after theese */
+ if (flag & (O_TRUNC | O_CREAT))
+ *to++= 'w';
+ else if (flag & O_APPEND)
+ *to++= 'a';
+ else
+ *to++= 'r';
+ *to++= '+';
+ }
+ else
+ *to++= 'r';
+
+#if FILE_BINARY /* If we have binary-files */
+ if (flag & FILE_BINARY)
+ *to++='b';
+#endif
+ *to='\0';
+} /* make_ftype */
diff --git a/mysql/mysys/my_fstream.c b/mysql/mysys/my_fstream.c
new file mode 100644
index 0000000..83d1ebb
--- /dev/null
+++ b/mysql/mysys/my_fstream.c
@@ -0,0 +1,186 @@
+/* Copyright (c) 2000, 2016, Oracle and/or its affiliates. All rights reserved.
+
+ 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; version 2 of the License.
+
+ 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include "mysys_priv.h"
+#include "my_sys.h"
+#include "mysys_err.h"
+#include <errno.h>
+#include <stdio.h>
+#include "my_thread_local.h"
+
+
+#ifdef HAVE_FSEEKO
+#undef ftell
+#undef fseek
+#define ftell(A) ftello(A)
+#define fseek(A,B,C) fseeko((A),(B),(C))
+#endif
+
+/*
+ Read a chunk of bytes from a FILE
+
+ SYNOPSIS
+ my_fread()
+ stream File descriptor
+ Buffer Buffer to read to
+ Count Number of bytes to read
+ MyFlags Flags on what to do on error
+
+ RETURN
+ (size_t) -1 Error
+ # Number of bytes read
+ */
+
+size_t my_fread(FILE *stream, uchar *Buffer, size_t Count, myf MyFlags)
+{
+ size_t readbytes;
+ DBUG_ENTER("my_fread");
+ DBUG_PRINT("my",("stream: 0x%lx Buffer: 0x%lx Count: %u MyFlags: %d",
+ (long) stream, (long) Buffer, (uint) Count, MyFlags));
+
+ if ((readbytes= fread(Buffer, sizeof(char), Count, stream)) != Count)
+ {
+ DBUG_PRINT("error",("Read only %d bytes", (int) readbytes));
+ if (MyFlags & (MY_WME | MY_FAE | MY_FNABP))
+ {
+ if (ferror(stream))
+ {
+ char errbuf[MYSYS_STRERROR_SIZE];
+ my_error(EE_READ, MYF(0),
+ my_filename(my_fileno(stream)),
+ errno, my_strerror(errbuf, sizeof(errbuf), errno));
+ }
+ else
+ if (MyFlags & (MY_NABP | MY_FNABP))
+ {
+ char errbuf[MYSYS_STRERROR_SIZE];
+ my_error(EE_EOFERR, MYF(0),
+ my_filename(my_fileno(stream)), errno,
+ my_strerror(errbuf, sizeof(errbuf), errno));
+ }
+ }
+ set_my_errno(errno ? errno : -1);
+ if (ferror(stream) || MyFlags & (MY_NABP | MY_FNABP))
+ DBUG_RETURN((size_t) -1); /* Return with error */
+ }
+ if (MyFlags & (MY_NABP | MY_FNABP))
+ DBUG_RETURN(0); /* Read ok */
+ DBUG_RETURN(readbytes);
+} /* my_fread */
+
+
+/*
+ Write a chunk of bytes to a stream
+
+ my_fwrite()
+ stream File descriptor
+ Buffer Buffer to write from
+ Count Number of bytes to write
+ MyFlags Flags on what to do on error
+
+ RETURN
+ (size_t) -1 Error
+ # Number of bytes written
+*/
+
+size_t my_fwrite(FILE *stream, const uchar *Buffer, size_t Count, myf MyFlags)
+{
+ size_t writtenbytes =0;
+ my_off_t seekptr;
+
+ DBUG_ENTER("my_fwrite");
+ DBUG_PRINT("my",("stream: 0x%lx Buffer: 0x%lx Count: %u MyFlags: %d",
+ (long) stream, (long) Buffer, (uint) Count, MyFlags));
+ DBUG_EXECUTE_IF("simulate_fwrite_error", DBUG_RETURN(-1););
+
+ seekptr= ftell(stream);
+ for (;;)
+ {
+ size_t written;
+ if ((written = (size_t) fwrite((char*) Buffer,sizeof(char),
+ Count, stream)) != Count)
+ {
+ DBUG_PRINT("error",("Write only %d bytes", (int) writtenbytes));
+ set_my_errno(errno);
+ if (written != (size_t) -1)
+ {
+ seekptr+=written;
+ Buffer+=written;
+ writtenbytes+=written;
+ Count-=written;
+ }
+ if (errno == EINTR)
+ {
+ (void) my_fseek(stream,seekptr,MY_SEEK_SET,MYF(0));
+ continue;
+ }
+ if (ferror(stream) || (MyFlags & (MY_NABP | MY_FNABP)))
+ {
+ if (MyFlags & (MY_WME | MY_FAE | MY_FNABP))
+ {
+ char errbuf[MYSYS_STRERROR_SIZE];
+ my_error(EE_WRITE, MYF(0),
+ my_filename(my_fileno(stream)),
+ errno, my_strerror(errbuf, sizeof(errbuf), errno));
+ }
+ writtenbytes= (size_t) -1; /* Return that we got error */
+ break;
+ }
+ }
+ if (MyFlags & (MY_NABP | MY_FNABP))
+ writtenbytes= 0; /* Everything OK */
+ else
+ writtenbytes+= written;
+ break;
+ }
+ DBUG_RETURN(writtenbytes);
+} /* my_fwrite */
+
+
+/* Seek to position in file */
+
+my_off_t my_fseek(FILE *stream, my_off_t pos, int whence,
+ myf MyFlags MY_ATTRIBUTE((unused)))
+{
+ DBUG_ENTER("my_fseek");
+ DBUG_PRINT("my",("stream: 0x%lx pos: %lu whence: %d MyFlags: %d",
+ (long) stream, (long) pos, whence, MyFlags));
+ DBUG_RETURN(fseek(stream, (off_t) pos, whence) ?
+ MY_FILEPOS_ERROR : (my_off_t) ftell(stream));
+} /* my_seek */
+
+
+/* Tell current position of file */
+
+my_off_t my_ftell(FILE *stream, myf MyFlags MY_ATTRIBUTE((unused)))
+{
+ off_t pos;
+ DBUG_ENTER("my_ftell");
+ DBUG_PRINT("my",("stream: 0x%lx MyFlags: %d", (long) stream, MyFlags));
+ pos=ftell(stream);
+ DBUG_PRINT("exit",("ftell: %lu",(ulong) pos));
+ DBUG_RETURN((my_off_t) pos);
+} /* my_ftell */
+
+
+/* Get a File corresponding to the stream*/
+int my_fileno(FILE *f)
+{
+#ifdef _WIN32
+ return my_win_fileno(f);
+#else
+ return fileno(f);
+#endif
+}
diff --git a/mysql/mysys/my_gethwaddr.c b/mysql/mysys/my_gethwaddr.c
new file mode 100644
index 0000000..5b48e5b
--- /dev/null
+++ b/mysql/mysys/my_gethwaddr.c
@@ -0,0 +1,259 @@
+/* Copyright (c) 2004, 2016, Oracle and/or its affiliates. All rights reserved.
+
+ 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; version 2 of the License.
+
+ 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+/* get hardware address for an interface */
+/* if there are many available, any non-zero one can be used */
+
+#include "mysys_priv.h"
+#include "my_sys.h"
+#include <m_string.h>
+
+#ifndef MAIN
+
+#ifdef __FreeBSD__
+
+#include <net/ethernet.h>
+#include <sys/sysctl.h>
+#include <net/route.h>
+#include <net/if.h>
+#include <net/if_dl.h>
+
+my_bool my_gethwaddr(uchar *to)
+{
+ size_t len;
+ char *buf, *next, *end;
+ struct if_msghdr *ifm;
+ struct sockaddr_dl *sdl;
+ int res=1, mib[6]={CTL_NET, AF_ROUTE, 0, AF_LINK, NET_RT_IFLIST, 0};
+ char zero_array[ETHER_ADDR_LEN] = {0};
+
+ if (sysctl(mib, 6, NULL, &len, NULL, 0) == -1)
+ goto err;
+ if (!(buf = alloca(len)))
+ goto err;
+ if (sysctl(mib, 6, buf, &len, NULL, 0) < 0)
+ goto err;
+
+ end = buf + len;
+
+ for (next = buf ; res && next < end ; next += ifm->ifm_msglen)
+ {
+ ifm = (struct if_msghdr *)next;
+ if (ifm->ifm_type == RTM_IFINFO)
+ {
+ sdl= (struct sockaddr_dl *)(ifm + 1);
+ memcpy(to, LLADDR(sdl), ETHER_ADDR_LEN);
+ res= memcmp(to, zero_array, ETHER_ADDR_LEN) ? 0 : 1;
+ }
+ }
+
+err:
+ return res;
+}
+
+#elif __linux__
+
+#include <net/if.h>
+#include <sys/ioctl.h>
+#include <net/ethernet.h>
+
+#define MAX_IFS 64
+
+my_bool my_gethwaddr(uchar *to)
+{
+ int fd= -1;
+ int res= 1;
+ struct ifreq ifr;
+ struct ifreq ifs[MAX_IFS];
+ struct ifreq *ifri= NULL;
+ struct ifreq *ifend= NULL;
+
+ char zero_array[ETHER_ADDR_LEN] = {0};
+ struct ifconf ifc;
+
+ fd = socket(AF_INET, SOCK_DGRAM, 0);
+ if (fd < 0)
+ return 1;
+
+ /* Retrieve interfaces */
+ ifc.ifc_len= sizeof(ifs);
+ ifc.ifc_req= ifs;
+ if (ioctl(fd, SIOCGIFCONF, &ifc) < 0)
+ {
+ close(fd);
+ return 1;
+ }
+
+ /* Initialize out parameter */
+ memcpy(to, zero_array, ETHER_ADDR_LEN);
+
+ /* Calculate first address after array */
+ ifend= ifs + (ifc.ifc_len / sizeof(struct ifreq));
+
+ /* Loop over all interfaces */
+ for (ifri= ifc.ifc_req; ifri < ifend; ifri++)
+ {
+ if (ifri->ifr_addr.sa_family == AF_INET)
+ {
+ /* Reset struct, copy interface name */
+ memset(&ifr, 0, sizeof(ifr));
+ strncpy(ifr.ifr_name, ifri->ifr_name, sizeof(ifr.ifr_name));
+
+ /* Get HW address, break if not 0 */
+ if (ioctl(fd, SIOCGIFHWADDR, &ifr) >= 0)
+ {
+ memcpy(to, &ifr.ifr_hwaddr.sa_data, ETHER_ADDR_LEN);
+ if (memcmp(to, zero_array, ETHER_ADDR_LEN))
+ {
+ res= 0;
+ break;
+ }
+ }
+ }
+ }
+ close(fd);
+ return res;
+}
+
+#elif defined(_WIN32)
+
+/*
+ Workaround for BUG#32082 (Definition of VOID in my_global.h conflicts with
+ windows headers)
+*/
+#ifdef VOID
+#undef VOID
+#define VOID void
+#endif
+
+#include <iphlpapi.h>
+
+/*
+ The following typedef is for dynamically loading iphlpapi.dll /
+ GetAdaptersAddresses. Dynamic loading is used because
+ GetAdaptersAddresses is not available on Windows 2000 which MySQL
+ still supports. Static linking would cause an unresolved export.
+*/
+typedef DWORD (WINAPI *pfnGetAdaptersAddresses)(IN ULONG Family,
+ IN DWORD Flags,IN PVOID Reserved,
+ OUT PIP_ADAPTER_ADDRESSES pAdapterAddresses,
+ IN OUT PULONG pOutBufLen);
+
+/*
+ my_gethwaddr - Windows version
+
+ @brief Retrieve MAC address from network hardware
+
+ @param[out] to MAC address exactly six bytes
+
+ @return Operation status
+ @retval 0 OK
+ @retval <>0 FAILED
+*/
+my_bool my_gethwaddr(uchar *to)
+{
+ PIP_ADAPTER_ADDRESSES pAdapterAddresses;
+ PIP_ADAPTER_ADDRESSES pCurrAddresses;
+ IP_ADAPTER_ADDRESSES adapterAddresses;
+ ULONG address_len;
+ my_bool return_val= 1;
+ static pfnGetAdaptersAddresses fnGetAdaptersAddresses=
+ (pfnGetAdaptersAddresses)-1;
+
+ if(fnGetAdaptersAddresses == (pfnGetAdaptersAddresses)-1)
+ {
+ /* Get the function from the DLL */
+ fnGetAdaptersAddresses= (pfnGetAdaptersAddresses)
+ GetProcAddress(LoadLibrary("iphlpapi.dll"),
+ "GetAdaptersAddresses");
+ }
+ if (!fnGetAdaptersAddresses)
+ return 1; /* failed to get function */
+ address_len= sizeof (IP_ADAPTER_ADDRESSES);
+
+ /* Get the required size for the address data. */
+ if (fnGetAdaptersAddresses(AF_UNSPEC, 0, 0, &adapterAddresses, &address_len)
+ == ERROR_BUFFER_OVERFLOW)
+ {
+ pAdapterAddresses= my_malloc(key_memory_win_IP_ADAPTER_ADDRESSES,
+ address_len, 0);
+ if (!pAdapterAddresses)
+ return 1; /* error, alloc failed */
+ }
+ else
+ pAdapterAddresses= &adapterAddresses; /* one is enough don't alloc */
+
+ /* Get the hardware info. */
+ if (fnGetAdaptersAddresses(AF_UNSPEC, 0, 0, pAdapterAddresses, &address_len)
+ == NO_ERROR)
+ {
+ pCurrAddresses= pAdapterAddresses;
+
+ while (pCurrAddresses)
+ {
+ /* Look for ethernet cards. */
+ if (pCurrAddresses->IfType == IF_TYPE_ETHERNET_CSMACD)
+ {
+ /* check for a good address */
+ if (pCurrAddresses->PhysicalAddressLength < 6)
+ continue; /* bad address */
+
+ /* save 6 bytes of the address in the 'to' parameter */
+ memcpy(to, pCurrAddresses->PhysicalAddress, 6);
+
+ /* Network card found, we're done. */
+ return_val= 0;
+ break;
+ }
+ pCurrAddresses= pCurrAddresses->Next;
+ }
+ }
+
+ /* Clean up memory allocation. */
+ if (pAdapterAddresses != &adapterAddresses)
+ my_free(pAdapterAddresses);
+
+ return return_val;
+}
+
+#else /* __FreeBSD__ || __linux__ || _WIN32 */
+/* just fail */
+my_bool my_gethwaddr(uchar *to MY_ATTRIBUTE((unused)))
+{
+ return 1;
+}
+#endif
+
+#else /* MAIN */
+int main(int argc MY_ATTRIBUTE((unused)),char **argv)
+{
+ uchar mac[6];
+ uint i;
+ MY_INIT(argv[0]);
+ if (my_gethwaddr(mac))
+ {
+ printf("my_gethwaddr failed with errno %d\n", errno);
+ exit(1);
+ }
+ for (i=0; i < sizeof(mac); i++)
+ {
+ if (i) printf(":");
+ printf("%02x", mac[i]);
+ }
+ printf("\n");
+ return 0;
+}
+#endif
+
diff --git a/mysql/mysys/my_getsystime.c b/mysql/mysys/my_getsystime.c
new file mode 100644
index 0000000..3007c4e
--- /dev/null
+++ b/mysql/mysys/my_getsystime.c
@@ -0,0 +1,122 @@
+/* Copyright (c) 2004, 2014, Oracle and/or its affiliates. All rights reserved.
+
+ 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; version 2 of the License.
+
+ 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */
+
+/* get time since epoc in 100 nanosec units */
+/* thus to get the current time we should use the system function
+ with the highest possible resolution */
+
+#include "mysys_priv.h"
+#include "my_static.h"
+
+#if HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+
+/**
+ Get high-resolution time.
+
+ @remark For windows platforms we need the frequency value of
+ the CPU. This is initialized in my_init.c through
+ QueryPerformanceFrequency(). If the Windows platform
+ doesn't support QueryPerformanceFrequency(), zero is
+ returned.
+
+ @retval current high-resolution time.
+*/
+
+ulonglong my_getsystime()
+{
+#ifdef HAVE_CLOCK_GETTIME
+ struct timespec tp;
+ clock_gettime(CLOCK_REALTIME, &tp);
+ return (ulonglong)tp.tv_sec*10000000+(ulonglong)tp.tv_nsec/100;
+#elif defined(_WIN32)
+ LARGE_INTEGER t_cnt;
+ if (query_performance_frequency)
+ {
+ QueryPerformanceCounter(&t_cnt);
+ return ((t_cnt.QuadPart / query_performance_frequency * 10000000) +
+ ((t_cnt.QuadPart % query_performance_frequency) * 10000000 /
+ query_performance_frequency) + query_performance_offset);
+ }
+ return 0;
+#else
+ /* TODO: check for other possibilities for hi-res timestamping */
+ struct timeval tv;
+ gettimeofday(&tv,NULL);
+ return (ulonglong)tv.tv_sec*10000000+(ulonglong)tv.tv_usec*10;
+#endif
+}
+
+
+/**
+ Return current time.
+
+ @param flags If MY_WME is set, write error if time call fails.
+
+ @retval current time.
+*/
+
+time_t my_time(myf flags)
+{
+ time_t t;
+ /*
+ The following loop is here beacuse time() may fail on some systems.
+ We're using a hardcoded my_message_stderr() here rather than going
+ through the hook in my_message_local() because it's far too easy to
+ come full circle with any logging function that writes timestamps ...
+ */
+ while ((t= time(0)) == (time_t) -1)
+ {
+ if (flags & MY_WME)
+ my_message_stderr(0, "time() call failed", MYF(0));
+ }
+ return t;
+}
+
+
+#define OFFSET_TO_EPOCH 116444736000000000ULL
+
+/**
+ Return time in microseconds.
+
+ @remark This function is to be used to measure performance in
+ micro seconds. As it's not defined whats the start time
+ for the clock, this function us only useful to measure
+ time between two moments.
+
+ @retval Value in microseconds from some undefined point in time.
+*/
+
+ulonglong my_micro_time()
+{
+#ifdef _WIN32
+ ulonglong newtime;
+ GetSystemTimeAsFileTime((FILETIME*)&newtime);
+ newtime-= OFFSET_TO_EPOCH;
+ return (newtime/10);
+#else
+ ulonglong newtime;
+ struct timeval t;
+ /*
+ The following loop is here because gettimeofday may fail on some systems
+ */
+ while (gettimeofday(&t, NULL) != 0)
+ {}
+ newtime= (ulonglong)t.tv_sec * 1000000 + t.tv_usec;
+ return newtime;
+#endif
+}
+
diff --git a/mysql/mysys/my_getwd.c b/mysql/mysys/my_getwd.c
new file mode 100644
index 0000000..6d650a8
--- /dev/null
+++ b/mysql/mysys/my_getwd.c
@@ -0,0 +1,162 @@
+/* Copyright (c) 2000, 2015, Oracle and/or its affiliates. All rights reserved.
+
+ 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; version 2 of the License.
+
+ 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+/* my_setwd() and my_getwd() works with intern_filenames !! */
+
+#include "mysys_priv.h"
+#include "my_sys.h"
+#include <m_string.h>
+#include "mysys_err.h"
+#include "my_thread_local.h"
+#if defined(_WIN32)
+#include <m_ctype.h>
+#include <dos.h>
+#include <direct.h>
+#endif
+
+/* Gets current working directory in buff.
+
+ SYNPOSIS
+ my_getwd()
+ buf Buffer to store result. Can be curr_dir[].
+ size Size of buffer
+ MyFlags Flags
+
+ NOTES
+ Directory is allways ended with FN_LIBCHAR
+
+ RESULT
+ 0 ok
+ # error
+*/
+
+int my_getwd(char * buf, size_t size, myf MyFlags)
+{
+ char * pos;
+ DBUG_ENTER("my_getwd");
+ DBUG_PRINT("my",("buf: 0x%lx size: %u MyFlags %d",
+ (long) buf, (uint) size, MyFlags));
+
+ if (size < 1)
+ DBUG_RETURN(-1);
+
+ if (curr_dir[0]) /* Current pos is saved here */
+ (void) strmake(buf,&curr_dir[0],size-1);
+ else
+ {
+ if (size < 2)
+ DBUG_RETURN(-1);
+ if (!getcwd(buf,(uint) (size-2)) && MyFlags & MY_WME)
+ {
+ char errbuf[MYSYS_STRERROR_SIZE];
+ set_my_errno(errno);
+ my_error(EE_GETWD, MYF(0),
+ errno, my_strerror(errbuf, sizeof(errbuf), errno));
+ DBUG_RETURN(-1);
+ }
+ if (*((pos=strend(buf))-1) != FN_LIBCHAR) /* End with FN_LIBCHAR */
+ {
+ pos[0]= FN_LIBCHAR;
+ pos[1]=0;
+ }
+ (void) strmake(&curr_dir[0],buf, (size_t) (FN_REFLEN-1));
+ }
+ DBUG_RETURN(0);
+} /* my_getwd */
+
+
+/* Set new working directory */
+
+int my_setwd(const char *dir, myf MyFlags)
+{
+ int res;
+ size_t length;
+ char *start, *pos;
+ DBUG_ENTER("my_setwd");
+ DBUG_PRINT("my",("dir: '%s' MyFlags %d", dir, MyFlags));
+
+ start=(char *) dir;
+ if (! dir[0] || (dir[0] == FN_LIBCHAR && dir[1] == 0))
+ dir=FN_ROOTDIR;
+ if ((res=chdir((char*) dir)) != 0)
+ {
+ set_my_errno(errno);
+ if (MyFlags & MY_WME)
+ {
+ char errbuf[MYSYS_STRERROR_SIZE];
+ my_error(EE_SETWD, MYF(0), start,
+ errno, my_strerror(errbuf, sizeof(errbuf), errno));
+ }
+ }
+ else
+ {
+ if (test_if_hard_path(start))
+ { /* Hard pathname */
+ pos= strmake(&curr_dir[0],start,(size_t) FN_REFLEN-1);
+ if (pos[-1] != FN_LIBCHAR)
+ {
+ length=(uint) (pos-(char*) curr_dir);
+ curr_dir[length]=FN_LIBCHAR; /* must end with '/' */
+ curr_dir[length+1]='\0';
+ }
+ }
+ else
+ curr_dir[0]='\0'; /* Don't save name */
+ }
+ DBUG_RETURN(res);
+} /* my_setwd */
+
+
+
+ /* Test if hard pathname */
+ /* Returns 1 if dirname is a hard path */
+
+int test_if_hard_path(const char *dir_name)
+{
+ if (dir_name[0] == FN_HOMELIB && dir_name[1] == FN_LIBCHAR)
+ return (home_dir != NullS && test_if_hard_path(home_dir));
+ if (dir_name[0] == FN_LIBCHAR)
+ return (TRUE);
+#ifdef FN_DEVCHAR
+ return (strchr(dir_name,FN_DEVCHAR) != 0);
+#else
+ return FALSE;
+#endif
+} /* test_if_hard_path */
+
+
+/*
+ Test if a name contains an (absolute or relative) path.
+
+ SYNOPSIS
+ has_path()
+ name The name to test.
+
+ RETURN
+ TRUE name contains a path.
+ FALSE name does not contain a path.
+*/
+
+my_bool has_path(const char *name)
+{
+ return MY_TEST(strchr(name, FN_LIBCHAR))
+#if FN_LIBCHAR != '/'
+ || MY_TEST(strchr(name,'/'))
+#endif
+#ifdef FN_DEVCHAR
+ || MY_TEST(strchr(name, FN_DEVCHAR))
+#endif
+ ;
+}
diff --git a/mysql/mysys/my_handler_errors.h b/mysql/mysys/my_handler_errors.h
new file mode 100644
index 0000000..00bec2e
--- /dev/null
+++ b/mysql/mysys/my_handler_errors.h
@@ -0,0 +1,114 @@
+#ifndef MYSYS_MY_HANDLER_ERRORS_INCLUDED
+#define MYSYS_MY_HANDLER_ERRORS_INCLUDED
+
+/* Copyright (c) 2008, 2015, Oracle and/or its affiliates. All rights reserved.
+
+ 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; version 2 of the License.
+
+ 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., 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA */
+
+/*
+ Errors a handler can give you
+*/
+
+static const char *handler_error_messages[]=
+{
+ "Didn't find key on read or update",
+ "Duplicate key on write or update",
+ "Internal (unspecified) error in handler",
+ "Someone has changed the row since it was read (while the table was locked to prevent it)",
+ "Wrong index given to function",
+ "Undefined handler error 125",
+ "Index file is crashed",
+ "Record file is crashed",
+ "Out of memory in engine",
+ "Undefined handler error 129",
+ "Incorrect file format",
+ "Command not supported by database",
+ "Old database file",
+ "No record read before update",
+ "Record was already deleted (or record file crashed)",
+ "No more room in record file",
+ "No more room in index file",
+ "No more records (read after end of file)",
+ "Unsupported extension used for table",
+ "Too big row",
+ "Wrong create options",
+ "Duplicate unique key or constraint on write or update",
+ "Unknown character set used in table",
+ "Conflicting table definitions in sub-tables of MERGE table",
+ "Table is crashed and last repair failed",
+ "Table was marked as crashed and should be repaired",
+ "Lock timed out; Retry transaction",
+ "Lock table is full; Restart program with a larger locktable",
+ "Updates are not allowed under a read only transactions",
+ "Lock deadlock; Retry transaction",
+ "Foreign key constraint is incorrectly formed",
+ "Cannot add a child row",
+ "Cannot delete a parent row",
+ "No savepoint with that name",
+ "Non unique key block size",
+ "The table does not exist in engine",
+ "The table already existed in storage engine",
+ "Could not connect to storage engine",
+ "Unexpected null pointer found when using spatial index",
+ "The table changed in storage engine",
+ "There's no partition in table for the given value",
+ "Row-based binlogging of row failed",
+ "Index needed in foreign key constraint",
+ "Upholding foreign key constraints would lead to a duplicate key error in "
+ "some other table",
+ "Table needs to be upgraded before it can be used",
+ "Table is read only",
+ "Failed to get next auto increment value",
+ "Failed to set row auto increment value",
+ "Unknown (generic) error from engine",
+ "Record is the same",
+ "It is not possible to log this statement",
+ "The event was corrupt, leading to illegal data being read",
+ "The table is of a new format not supported by this version",
+ "The event could not be processed no other hanlder error happened",
+ "Got a fatal error during initialzaction of handler",
+ "File to short; Expected more data in file",
+ "Read page with wrong checksum",
+ "Too many active concurrent transactions",
+ "Record not matching the given partition set",
+ "Index column length exceeds limit",
+ "Index corrupted",
+ "Undo record too big",
+ "Invalid InnoDB FTS Doc ID",
+ "Table is being used in foreign key check",
+ "Tablespace already exists",
+ "Too many columns",
+ "Row in wrong partition",
+ "InnoDB is in read only mode",
+ "FTS query exceeds result cache memory limit",
+ "Temporary file write failure",
+ "Operation not allowed when innodb_forced_recovery > 0",
+ "Too many words in a FTS phrase or proximity search",
+ "Foreign key cascade delete/update exceeds max depth",
+ "Required Create option missing",
+ "Out of memory in storage engine",
+ "Table corrupted",
+ "Query interrupted",
+ "Tablespace cannot be accessed",
+ "Tablespace is not empty",
+ "Incorrect file name",
+ "Operation is not allowed",
+ "Compute generate value failed"
+};
+
+extern void my_handler_error_register(void);
+extern void my_handler_error_unregister(void);
+
+
+#endif /* MYSYS_MY_HANDLER_ERRORS_INCLUDED */
diff --git a/mysql/mysys/my_init.c b/mysql/mysys/my_init.c
new file mode 100644
index 0000000..54d44e6
--- /dev/null
+++ b/mysql/mysys/my_init.c
@@ -0,0 +1,564 @@
+/* Copyright (c) 2000, 2015, Oracle and/or its affiliates. All rights reserved.
+
+ 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; version 2 of the License.
+
+ 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include "mysys_priv.h"
+#include "my_sys.h"
+#include "my_static.h"
+#include "mysys_err.h"
+#include "m_string.h"
+#include "mysql/psi/mysql_stage.h"
+#include "mysql/psi/mysql_file.h"
+
+#ifdef HAVE_SYS_RESOURCE_H
+#include <sys/resource.h>
+#endif
+
+#ifdef _WIN32
+#include <locale.h>
+#include <crtdbg.h>
+/* WSAStartup needs winsock library*/
+#pragma comment(lib, "ws2_32")
+my_bool have_tcpip=0;
+static void my_win_init();
+#endif
+
+#define SCALE_SEC 100
+#define SCALE_USEC 10000
+
+my_bool my_init_done= FALSE;
+ulong my_thread_stack_size= 65536;
+MYSQL_FILE *mysql_stdin= NULL;
+static MYSQL_FILE instrumented_stdin;
+
+
+static ulong atoi_octal(const char *str)
+{
+ long int tmp;
+ while (*str && my_isspace(&my_charset_latin1, *str))
+ str++;
+ str2int(str,
+ (*str == '0' ? 8 : 10), /* Octalt or decimalt */
+ 0, INT_MAX, &tmp);
+ return (ulong) tmp;
+}
+
+
+#if defined(MY_MSCRT_DEBUG)
+int set_crt_report_leaks()
+{
+ HANDLE hLogFile;
+
+ _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF // debug allocation on
+ | _CRTDBG_LEAK_CHECK_DF // leak checks on exit
+ | _CRTDBG_CHECK_ALWAYS_DF // memory check (slow)
+ );
+
+ return ((
+ NULL == (hLogFile= GetStdHandle(STD_ERROR_HANDLE)) ||
+ -1 == _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE) ||
+ _CRTDBG_HFILE_ERROR == _CrtSetReportFile(_CRT_WARN, hLogFile) ||
+ -1 == _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE) ||
+ _CRTDBG_HFILE_ERROR == _CrtSetReportFile(_CRT_ERROR, hLogFile) ||
+ -1 == _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_FILE) ||
+ _CRTDBG_HFILE_ERROR == _CrtSetReportFile(_CRT_ASSERT, hLogFile)) ? 1 : 0);
+}
+#endif
+
+
+/**
+ Initialize my_sys functions, resources and variables
+
+ @return Initialization result
+ @retval FALSE Success
+ @retval TRUE Error. Couldn't initialize environment
+*/
+my_bool my_init()
+{
+ char *str;
+
+ if (my_init_done)
+ return FALSE;
+
+ my_init_done= TRUE;
+
+#if defined(MY_MSCRT_DEBUG)
+ set_crt_report_leaks();
+#endif
+
+ my_umask= 0640; /* Default umask for new files */
+ my_umask_dir= 0750; /* Default umask for new directories */
+
+ /* Default creation of new files */
+ if ((str= getenv("UMASK")) != 0)
+ my_umask= (int) (atoi_octal(str) | 0640);
+ /* Default creation of new dir's */
+ if ((str= getenv("UMASK_DIR")) != 0)
+ my_umask_dir= (int) (atoi_octal(str) | 0750);
+
+ instrumented_stdin.m_file= stdin;
+ instrumented_stdin.m_psi= NULL; /* not yet instrumented */
+ mysql_stdin= & instrumented_stdin;
+
+ if (my_thread_global_init())
+ return TRUE;
+
+ if (my_thread_init())
+ return TRUE;
+
+ /* $HOME is needed early to parse configuration files located in ~/ */
+ if ((home_dir= getenv("HOME")) != 0)
+ home_dir= intern_filename(home_dir_buff, home_dir);
+
+ {
+ DBUG_ENTER("my_init");
+ DBUG_PROCESS((char*) (my_progname ? my_progname : "unknown"));
+#ifdef _WIN32
+ my_win_init();
+#endif
+ DBUG_PRINT("exit", ("home: '%s'", home_dir));
+ DBUG_RETURN(FALSE);
+ }
+} /* my_init */
+
+
+ /* End my_sys */
+
+void my_end(int infoflag)
+{
+ /*
+ We do not use DBUG_ENTER here, as after cleanup DBUG is no longer
+ operational, so we cannot use DBUG_RETURN.
+ */
+
+ FILE *info_file= (DBUG_FILE ? DBUG_FILE : stderr);
+
+ if (!my_init_done)
+ return;
+
+ if ((infoflag & MY_CHECK_ERROR) || (info_file != stderr))
+
+ { /* Test if some file is left open */
+ if (my_file_opened | my_stream_opened)
+ {
+ char ebuff[512];
+ my_snprintf(ebuff, sizeof(ebuff), EE(EE_OPEN_WARNING),
+ my_file_opened, my_stream_opened);
+ my_message_stderr(EE_OPEN_WARNING, ebuff, MYF(0));
+ DBUG_PRINT("error", ("%s", ebuff));
+ my_print_open_files();
+ }
+ }
+ free_charsets();
+ my_error_unregister_all();
+ my_once_free();
+
+ if ((infoflag & MY_GIVE_INFO) || (info_file != stderr))
+ {
+#ifdef HAVE_GETRUSAGE
+ struct rusage rus;
+ if (!getrusage(RUSAGE_SELF, &rus))
+ fprintf(info_file,"\n\
+User time %.2f, System time %.2f\n \
+Maximum resident set size %ld, Integral resident set size %ld\n\
+Non-physical pagefaults %ld, Physical pagefaults %ld, Swaps %ld\n\
+Blocks in %ld out %ld, Messages in %ld out %ld, Signals %ld\n\
+Voluntary context switches %ld, Involuntary context switches %ld\n",
+ (rus.ru_utime.tv_sec * SCALE_SEC +
+ rus.ru_utime.tv_usec / SCALE_USEC) / 100.0,
+ (rus.ru_stime.tv_sec * SCALE_SEC +
+ rus.ru_stime.tv_usec / SCALE_USEC) / 100.0,
+ rus.ru_maxrss, rus.ru_idrss,
+ rus.ru_minflt, rus.ru_majflt,
+ rus.ru_nswap, rus.ru_inblock, rus.ru_oublock,
+ rus.ru_msgsnd, rus.ru_msgrcv, rus.ru_nsignals,
+ rus.ru_nvcsw, rus.ru_nivcsw);
+#endif
+#if defined(_WIN32)
+ _CrtSetReportMode( _CRT_WARN, _CRTDBG_MODE_FILE );
+ _CrtSetReportFile( _CRT_WARN, _CRTDBG_FILE_STDERR );
+ _CrtSetReportMode( _CRT_ERROR, _CRTDBG_MODE_FILE );
+ _CrtSetReportFile( _CRT_ERROR, _CRTDBG_FILE_STDERR );
+ _CrtSetReportMode( _CRT_ASSERT, _CRTDBG_MODE_FILE );
+ _CrtSetReportFile( _CRT_ASSERT, _CRTDBG_FILE_STDERR );
+ _CrtCheckMemory();
+ _CrtDumpMemoryLeaks();
+#endif
+ }
+
+ if (!(infoflag & MY_DONT_FREE_DBUG))
+ {
+ DBUG_END(); /* Must be done before my_thread_end */
+ }
+
+ my_thread_end();
+ my_thread_global_end();
+
+#ifdef _WIN32
+ if (have_tcpip)
+ WSACleanup();
+#endif /* _WIN32 */
+
+ my_init_done= FALSE;
+} /* my_end */
+
+
+#ifdef _WIN32
+/*
+ my_parameter_handler
+
+ Invalid parameter handler we will use instead of the one "baked"
+ into the CRT for MSC v8. This one just prints out what invalid
+ parameter was encountered. By providing this routine, routines like
+ lseek will return -1 when we expect them to instead of crash.
+*/
+
+void my_parameter_handler(const wchar_t * expression, const wchar_t * function,
+ const wchar_t * file, unsigned int line,
+ uintptr_t pReserved)
+{
+ DBUG_PRINT("my",("Expression: %s function: %s file: %s, line: %d",
+ expression, function, file, line));
+}
+
+
+#ifdef __MSVC_RUNTIME_CHECKS
+#include <rtcapi.h>
+
+/* Turn off runtime checks for 'handle_rtc_failure' */
+#pragma runtime_checks("", off)
+
+/*
+ handle_rtc_failure
+ Windows: run-time error checks are reported to ...
+*/
+
+int handle_rtc_failure(int err_type, const char *file, int line,
+ const char* module, const char *format, ...)
+{
+ va_list args;
+ char buff[2048];
+ size_t len;
+
+ len= my_snprintf(buff, sizeof(buff), "At %s:%d: ", file, line);
+
+ va_start(args, format);
+ vsnprintf(buff + len, sizeof(buff) - len, format, args);
+ va_end(args);
+
+ my_message_local(ERROR_LEVEL, buff);
+
+ return 0; /* Error is handled */
+}
+#pragma runtime_checks("", restore)
+#endif
+
+#define OFFSET_TO_EPOC ((__int64) 134774 * 24 * 60 * 60 * 1000 * 1000 * 10)
+#define MS 10000000
+
+static void win_init_time()
+{
+ /* The following is used by time functions */
+ FILETIME ft;
+ LARGE_INTEGER li, t_cnt;
+
+ DBUG_ASSERT(sizeof(LARGE_INTEGER) == sizeof(query_performance_frequency));
+
+ if (QueryPerformanceFrequency((LARGE_INTEGER *)&query_performance_frequency) == 0)
+ query_performance_frequency= 0;
+ else
+ {
+ GetSystemTimeAsFileTime(&ft);
+ li.LowPart= ft.dwLowDateTime;
+ li.HighPart= ft.dwHighDateTime;
+ query_performance_offset= li.QuadPart-OFFSET_TO_EPOC;
+ QueryPerformanceCounter(&t_cnt);
+ query_performance_offset-= (t_cnt.QuadPart /
+ query_performance_frequency * MS +
+ t_cnt.QuadPart %
+ query_performance_frequency * MS /
+ query_performance_frequency);
+ }
+}
+
+
+/*
+ Open HKEY_LOCAL_MACHINE\SOFTWARE\MySQL and set any strings found
+ there as environment variables
+*/
+static void win_init_registry()
+{
+ HKEY key_handle;
+
+ if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, (LPCTSTR)"SOFTWARE\\MySQL",
+ 0, KEY_READ, &key_handle) == ERROR_SUCCESS)
+ {
+ LONG ret;
+ DWORD index= 0;
+ DWORD type;
+ char key_name[256], key_data[1024];
+ DWORD key_name_len= sizeof(key_name) - 1;
+ DWORD key_data_len= sizeof(key_data) - 1;
+
+ while ((ret= RegEnumValue(key_handle, index++,
+ key_name, &key_name_len,
+ NULL, &type, (LPBYTE)&key_data,
+ &key_data_len)) != ERROR_NO_MORE_ITEMS)
+ {
+ char env_string[sizeof(key_name) + sizeof(key_data) + 2];
+
+ if (ret == ERROR_MORE_DATA)
+ {
+ /* Registry value larger than 'key_data', skip it */
+ DBUG_PRINT("error", ("Skipped registry value that was too large"));
+ }
+ else if (ret == ERROR_SUCCESS)
+ {
+ if (type == REG_SZ)
+ {
+ strxmov(env_string, key_name, "=", key_data, NullS);
+
+ /* variable for putenv must be allocated ! */
+ putenv(strdup(env_string)) ;
+ }
+ }
+ else
+ {
+ /* Unhandled error, break out of loop */
+ break;
+ }
+
+ key_name_len= sizeof(key_name) - 1;
+ key_data_len= sizeof(key_data) - 1;
+ }
+
+ RegCloseKey(key_handle);
+ }
+}
+
+
+/*------------------------------------------------------------------
+ Name: CheckForTcpip| Desc: checks if tcpip has been installed on system
+ According to Microsoft Developers documentation the first registry
+ entry should be enough to check if TCP/IP is installed, but as expected
+ this doesn't work on all Win32 machines :(
+------------------------------------------------------------------*/
+
+#define TCPIPKEY "SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters"
+#define WINSOCK2KEY "SYSTEM\\CurrentControlSet\\Services\\Winsock2\\Parameters"
+#define WINSOCKKEY "SYSTEM\\CurrentControlSet\\Services\\Winsock\\Parameters"
+
+static my_bool win32_have_tcpip()
+{
+ HKEY hTcpipRegKey;
+ if (RegOpenKeyEx ( HKEY_LOCAL_MACHINE, TCPIPKEY, 0, KEY_READ,
+ &hTcpipRegKey) != ERROR_SUCCESS)
+ {
+ if (RegOpenKeyEx ( HKEY_LOCAL_MACHINE, WINSOCK2KEY, 0, KEY_READ,
+ &hTcpipRegKey) != ERROR_SUCCESS)
+ {
+ if (RegOpenKeyEx ( HKEY_LOCAL_MACHINE, WINSOCKKEY, 0, KEY_READ,
+ &hTcpipRegKey) != ERROR_SUCCESS)
+ if (!getenv("HAVE_TCPIP") || have_tcpip) /* Provide a workaround */
+ return (FALSE);
+ }
+ }
+ RegCloseKey ( hTcpipRegKey);
+ return (TRUE);
+}
+
+
+static my_bool win32_init_tcp_ip()
+{
+ if (win32_have_tcpip())
+ {
+ WORD wVersionRequested = MAKEWORD( 2, 2 );
+ WSADATA wsaData;
+ /* Be a good citizen: maybe another lib has already initialised
+ sockets, so dont clobber them unless necessary */
+ if (WSAStartup( wVersionRequested, &wsaData ))
+ {
+ /* Load failed, maybe because of previously loaded
+ incompatible version; try again */
+ WSACleanup( );
+ if (!WSAStartup( wVersionRequested, &wsaData ))
+ have_tcpip=1;
+ }
+ else
+ {
+ if (wsaData.wVersion != wVersionRequested)
+ {
+ /* Version is no good, try again */
+ WSACleanup( );
+ if (!WSAStartup( wVersionRequested, &wsaData ))
+ have_tcpip=1;
+ }
+ else
+ have_tcpip=1;
+ }
+ }
+ return(0);
+}
+
+
+static void my_win_init()
+{
+ DBUG_ENTER("my_win_init");
+
+ /* this is required to make crt functions return -1 appropriately */
+ _set_invalid_parameter_handler(my_parameter_handler);
+
+#ifdef __MSVC_RUNTIME_CHECKS
+ /*
+ Install handler to send RTC (Runtime Error Check) warnings
+ to log file
+ */
+ _RTC_SetErrorFunc(handle_rtc_failure);
+#endif
+
+ _tzset();
+
+ win_init_time();
+ win_init_registry();
+ win32_init_tcp_ip();
+
+ DBUG_VOID_RETURN;
+}
+#endif /* _WIN32 */
+
+PSI_stage_info stage_waiting_for_table_level_lock=
+{0, "Waiting for table level lock", 0};
+
+#ifdef HAVE_PSI_INTERFACE
+
+PSI_mutex_key key_BITMAP_mutex, key_IO_CACHE_append_buffer_lock,
+ key_IO_CACHE_SHARE_mutex, key_KEY_CACHE_cache_lock,
+ key_THR_LOCK_charset, key_THR_LOCK_heap,
+ key_THR_LOCK_lock, key_THR_LOCK_malloc,
+ key_THR_LOCK_mutex, key_THR_LOCK_myisam, key_THR_LOCK_net,
+ key_THR_LOCK_open, key_THR_LOCK_threads,
+ key_TMPDIR_mutex, key_THR_LOCK_myisam_mmap;
+
+static PSI_mutex_info all_mysys_mutexes[]=
+{
+ { &key_BITMAP_mutex, "BITMAP::mutex", 0},
+ { &key_IO_CACHE_append_buffer_lock, "IO_CACHE::append_buffer_lock", 0},
+ { &key_IO_CACHE_SHARE_mutex, "IO_CACHE::SHARE_mutex", 0},
+ { &key_KEY_CACHE_cache_lock, "KEY_CACHE::cache_lock", 0},
+ { &key_THR_LOCK_charset, "THR_LOCK_charset", PSI_FLAG_GLOBAL},
+ { &key_THR_LOCK_heap, "THR_LOCK_heap", PSI_FLAG_GLOBAL},
+ { &key_THR_LOCK_lock, "THR_LOCK_lock", PSI_FLAG_GLOBAL},
+ { &key_THR_LOCK_malloc, "THR_LOCK_malloc", PSI_FLAG_GLOBAL},
+ { &key_THR_LOCK_mutex, "THR_LOCK::mutex", 0},
+ { &key_THR_LOCK_myisam, "THR_LOCK_myisam", PSI_FLAG_GLOBAL},
+ { &key_THR_LOCK_net, "THR_LOCK_net", PSI_FLAG_GLOBAL},
+ { &key_THR_LOCK_open, "THR_LOCK_open", PSI_FLAG_GLOBAL},
+ { &key_THR_LOCK_threads, "THR_LOCK_threads", PSI_FLAG_GLOBAL},
+ { &key_TMPDIR_mutex, "TMPDIR_mutex", PSI_FLAG_GLOBAL},
+ { &key_THR_LOCK_myisam_mmap, "THR_LOCK_myisam_mmap", PSI_FLAG_GLOBAL}
+};
+
+PSI_rwlock_key key_SAFE_HASH_lock;
+
+static PSI_rwlock_info all_mysys_rwlocks[]=
+{
+ { &key_SAFE_HASH_lock, "SAFE_HASH::lock", 0}
+};
+
+PSI_cond_key key_IO_CACHE_SHARE_cond,
+ key_IO_CACHE_SHARE_cond_writer,
+ key_THR_COND_threads;
+
+static PSI_cond_info all_mysys_conds[]=
+{
+ { &key_IO_CACHE_SHARE_cond, "IO_CACHE_SHARE::cond", 0},
+ { &key_IO_CACHE_SHARE_cond_writer, "IO_CACHE_SHARE::cond_writer", 0},
+ { &key_THR_COND_threads, "THR_COND_threads", 0}
+};
+
+#ifdef HAVE_LINUX_LARGE_PAGES
+PSI_file_key key_file_proc_meminfo;
+#endif /* HAVE_LINUX_LARGE_PAGES */
+PSI_file_key key_file_charset, key_file_cnf;
+
+static PSI_file_info all_mysys_files[]=
+{
+#ifdef HAVE_LINUX_LARGE_PAGES
+ { &key_file_proc_meminfo, "proc_meminfo", 0},
+#endif /* HAVE_LINUX_LARGE_PAGES */
+ { &key_file_charset, "charset", 0},
+ { &key_file_cnf, "cnf", 0}
+};
+
+PSI_stage_info *all_mysys_stages[]=
+{
+ & stage_waiting_for_table_level_lock
+};
+
+static PSI_memory_info all_mysys_memory[]=
+{
+#ifdef _WIN32
+ { &key_memory_win_SECURITY_ATTRIBUTES, "win_SECURITY_ATTRIBUTES", 0},
+ { &key_memory_win_PACL, "win_PACL", 0},
+ { &key_memory_win_IP_ADAPTER_ADDRESSES, "win_IP_ADAPTER_ADDRESSES", 0},
+#endif
+
+ { &key_memory_max_alloca, "max_alloca", 0},
+ { &key_memory_charset_file, "charset_file", 0},
+ { &key_memory_charset_loader, "charset_loader", 0},
+ { &key_memory_lf_node, "lf_node", 0},
+ { &key_memory_lf_dynarray, "lf_dynarray", 0},
+ { &key_memory_lf_slist, "lf_slist", 0},
+ { &key_memory_LIST, "LIST", 0},
+ { &key_memory_IO_CACHE, "IO_CACHE", 0},
+ { &key_memory_KEY_CACHE, "KEY_CACHE", 0},
+ { &key_memory_SAFE_HASH_ENTRY, "SAFE_HASH_ENTRY", 0},
+ { &key_memory_MY_TMPDIR_full_list, "MY_TMPDIR::full_list", 0},
+ { &key_memory_MY_BITMAP_bitmap, "MY_BITMAP::bitmap", 0},
+ { &key_memory_my_compress_alloc, "my_compress_alloc", 0},
+ { &key_memory_pack_frm, "pack_frm", 0},
+ { &key_memory_my_err_head, "my_err_head", 0},
+ { &key_memory_my_file_info, "my_file_info", 0},
+ { &key_memory_MY_DIR, "MY_DIR", 0},
+ { &key_memory_MY_STAT, "MY_STAT", 0},
+ { &key_memory_QUEUE, "QUEUE", 0},
+ { &key_memory_DYNAMIC_STRING, "DYNAMIC_STRING", 0},
+ { &key_memory_TREE, "TREE", 0}
+};
+
+void my_init_mysys_psi_keys()
+{
+ const char* category= "mysys";
+ int count;
+
+ count= sizeof(all_mysys_mutexes)/sizeof(all_mysys_mutexes[0]);
+ mysql_mutex_register(category, all_mysys_mutexes, count);
+
+ count= sizeof(all_mysys_rwlocks)/sizeof(all_mysys_rwlocks[0]);
+ mysql_rwlock_register(category, all_mysys_rwlocks, count);
+
+ count= sizeof(all_mysys_conds)/sizeof(all_mysys_conds[0]);
+ mysql_cond_register(category, all_mysys_conds, count);
+
+ count= sizeof(all_mysys_files)/sizeof(all_mysys_files[0]);
+ mysql_file_register(category, all_mysys_files, count);
+
+ count= array_elements(all_mysys_stages);
+ mysql_stage_register(category, all_mysys_stages, count);
+
+ count= array_elements(all_mysys_memory);
+ mysql_memory_register(category, all_mysys_memory, count);
+}
+#endif /* HAVE_PSI_INTERFACE */
+
diff --git a/mysql/mysys/my_lib.c b/mysql/mysys/my_lib.c
new file mode 100644
index 0000000..f2c2940
--- /dev/null
+++ b/mysql/mysys/my_lib.c
@@ -0,0 +1,378 @@
+/* Copyright (c) 2000, 2016, Oracle and/or its affiliates. All rights reserved.
+
+ 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; version 2 of the License.
+
+ 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+/* TODO: check for overun of memory for names. */
+
+#include "mysys_priv.h"
+#include "my_sys.h"
+#include "m_string.h"
+#include "my_dir.h" /* Structs used by my_dir,includes sys/types */
+#include "mysys_err.h"
+#include "my_thread_local.h"
+#if !defined(_WIN32)
+# include <dirent.h>
+#endif
+
+
+/*
+ We are assuming that directory we are reading is either has less than
+ 100 files and so can be read in one initial chunk or has more than 1000
+ files and so big increment are suitable.
+*/
+#define ENTRIES_START_SIZE (8192/sizeof(FILEINFO))
+#define ENTRIES_INCREMENT (65536/sizeof(FILEINFO))
+#define NAMES_START_SIZE 32768
+
+static int comp_names(struct fileinfo *a,struct fileinfo *b);
+
+
+ /* We need this because program don't know with malloc we used */
+
+void my_dirend(MY_DIR *buffer)
+{
+ DBUG_ENTER("my_dirend");
+ if (buffer)
+ {
+ delete_dynamic((DYNAMIC_ARRAY*)((char*)buffer +
+ ALIGN_SIZE(sizeof(MY_DIR))));
+ free_root((MEM_ROOT*)((char*)buffer + ALIGN_SIZE(sizeof(MY_DIR)) +
+ ALIGN_SIZE(sizeof(DYNAMIC_ARRAY))), MYF(0));
+ my_free(buffer);
+ }
+ DBUG_VOID_RETURN;
+} /* my_dirend */
+
+
+ /* Compare in sort of filenames */
+
+static int comp_names(struct fileinfo *a, struct fileinfo *b)
+{
+ return (strcmp(a->name,b->name));
+} /* comp_names */
+
+
+#if !defined(_WIN32)
+
+static char* directory_file_name(char *dst, const char *src);
+
+MY_DIR *my_dir(const char *path, myf MyFlags)
+{
+ char *buffer;
+ MY_DIR *result= 0;
+ FILEINFO finfo;
+ DYNAMIC_ARRAY *dir_entries_storage;
+ MEM_ROOT *names_storage;
+ DIR *dirp;
+ char tmp_path[FN_REFLEN + 2], *tmp_file;
+ const struct dirent *dp;
+
+ DBUG_ENTER("my_dir");
+ DBUG_PRINT("my",("path: '%s' MyFlags: %d",path,MyFlags));
+
+ dirp = opendir(directory_file_name(tmp_path,(char *) path));
+ if (dirp == NULL ||
+ ! (buffer= my_malloc(key_memory_MY_DIR,
+ ALIGN_SIZE(sizeof(MY_DIR)) +
+ ALIGN_SIZE(sizeof(DYNAMIC_ARRAY)) +
+ sizeof(MEM_ROOT), MyFlags)))
+ goto error;
+
+ dir_entries_storage= (DYNAMIC_ARRAY*)(buffer + ALIGN_SIZE(sizeof(MY_DIR)));
+ names_storage= (MEM_ROOT*)(buffer + ALIGN_SIZE(sizeof(MY_DIR)) +
+ ALIGN_SIZE(sizeof(DYNAMIC_ARRAY)));
+
+ if (my_init_dynamic_array(dir_entries_storage,
+ key_memory_MY_DIR,
+ sizeof(FILEINFO),
+ NULL, /* init_buffer */
+ ENTRIES_START_SIZE, ENTRIES_INCREMENT))
+ {
+ my_free(buffer);
+ goto error;
+ }
+ init_alloc_root(key_memory_MY_DIR, names_storage, NAMES_START_SIZE, NAMES_START_SIZE);
+
+ /* MY_DIR structure is allocated and completly initialized at this point */
+ result= (MY_DIR*)buffer;
+
+ tmp_file=strend(tmp_path);
+
+ for (dp= readdir(dirp) ; dp; dp= readdir(dirp))
+ {
+ if (!(finfo.name= strdup_root(names_storage, dp->d_name)))
+ goto error;
+
+ if (MyFlags & MY_WANT_STAT)
+ {
+ if (!(finfo.mystat= (MY_STAT*)alloc_root(names_storage,
+ sizeof(MY_STAT))))
+ goto error;
+
+ memset(finfo.mystat, 0, sizeof(MY_STAT));
+ (void) my_stpcpy(tmp_file,dp->d_name);
+ (void) my_stat(tmp_path, finfo.mystat, MyFlags);
+ if (!(finfo.mystat->st_mode & MY_S_IREAD))
+ continue;
+ }
+ else
+ finfo.mystat= NULL;
+
+ if (insert_dynamic(dir_entries_storage, (uchar*)&finfo))
+ goto error;
+ }
+
+ (void) closedir(dirp);
+
+ result->dir_entry= (FILEINFO *)dir_entries_storage->buffer;
+ result->number_off_files= dir_entries_storage->elements;
+
+ if (!(MyFlags & MY_DONT_SORT))
+ my_qsort((void *) result->dir_entry, result->number_off_files,
+ sizeof(FILEINFO), (qsort_cmp) comp_names);
+ DBUG_RETURN(result);
+
+ error:
+ set_my_errno(errno);
+ if (dirp)
+ (void) closedir(dirp);
+ my_dirend(result);
+ if (MyFlags & (MY_FAE | MY_WME))
+ {
+ char errbuf[MYSYS_STRERROR_SIZE];
+ my_error(EE_DIR, MYF(0), path,
+ my_errno(), my_strerror(errbuf, sizeof(errbuf), my_errno()));
+ }
+ DBUG_RETURN((MY_DIR *) NULL);
+} /* my_dir */
+
+
+/*
+ * Convert from directory name to filename.
+ * On UNIX, it's simple: just make sure there is a terminating /
+
+ * Returns pointer to dst;
+ */
+
+static char* directory_file_name(char *dst, const char *src)
+{
+ /* Process as Unix format: just remove test the final slash. */
+ char *end;
+ DBUG_ASSERT(strlen(src) < (FN_REFLEN + 1));
+
+ if (src[0] == 0)
+ src= (char*) "."; /* Use empty as current */
+ end= my_stpnmov(dst, src, FN_REFLEN + 1);
+ if (end[-1] != FN_LIBCHAR)
+ {
+ end[0]=FN_LIBCHAR; /* Add last '/' */
+ end[1]='\0';
+ }
+ return dst;
+}
+
+#else
+
+/*
+*****************************************************************************
+** Read long filename using windows rutines
+*****************************************************************************
+*/
+
+MY_DIR *my_dir(const char *path, myf MyFlags)
+{
+ char *buffer;
+ MY_DIR *result= 0;
+ FILEINFO finfo;
+ DYNAMIC_ARRAY *dir_entries_storage;
+ MEM_ROOT *names_storage;
+ struct _finddata_t find;
+ ushort mode;
+ char tmp_path[FN_REFLEN],*tmp_file,attrib;
+#ifdef _WIN64
+ __int64 handle;
+#else
+ long handle;
+#endif
+ DBUG_ENTER("my_dir");
+ DBUG_PRINT("my",("path: '%s' stat: %d MyFlags: %d",path,MyFlags));
+
+ /* Put LIB-CHAR as last path-character if not there */
+ tmp_file=tmp_path;
+ if (!*path)
+ *tmp_file++ ='.'; /* From current dir */
+ tmp_file= my_stpnmov(tmp_file, path, FN_REFLEN-5);
+ if (tmp_file[-1] == FN_DEVCHAR)
+ *tmp_file++= '.'; /* From current dev-dir */
+ if (tmp_file[-1] != FN_LIBCHAR)
+ *tmp_file++ =FN_LIBCHAR;
+ tmp_file[0]='*'; /* Windows needs this !??? */
+ tmp_file[1]='.';
+ tmp_file[2]='*';
+ tmp_file[3]='\0';
+
+ if (!(buffer= my_malloc(key_memory_MY_DIR,
+ ALIGN_SIZE(sizeof(MY_DIR)) +
+ ALIGN_SIZE(sizeof(DYNAMIC_ARRAY)) +
+ sizeof(MEM_ROOT), MyFlags)))
+ goto error;
+
+ dir_entries_storage= (DYNAMIC_ARRAY*)(buffer + ALIGN_SIZE(sizeof(MY_DIR)));
+ names_storage= (MEM_ROOT*)(buffer + ALIGN_SIZE(sizeof(MY_DIR)) +
+ ALIGN_SIZE(sizeof(DYNAMIC_ARRAY)));
+
+ if (my_init_dynamic_array(dir_entries_storage,
+ key_memory_MY_DIR,
+ sizeof(FILEINFO),
+ NULL, /* init_buffer */
+ ENTRIES_START_SIZE, ENTRIES_INCREMENT))
+ {
+ my_free(buffer);
+ goto error;
+ }
+ init_alloc_root(key_memory_MY_DIR, names_storage, NAMES_START_SIZE, NAMES_START_SIZE);
+
+ /* MY_DIR structure is allocated and completly initialized at this point */
+ result= (MY_DIR*)buffer;
+
+ if ((handle=_findfirst(tmp_path,&find)) == -1L)
+ {
+ DBUG_PRINT("info", ("findfirst returned error, errno: %d", errno));
+ if (errno != EINVAL)
+ goto error;
+ /*
+ Could not read the directory, no read access.
+ Probably because by "chmod -r".
+ continue and return zero files in dir
+ */
+ }
+ else
+ {
+
+ do
+ {
+ attrib= find.attrib;
+ /*
+ Do not show hidden and system files which Windows sometimes create.
+ Note. Because Borland's findfirst() is called with the third
+ argument = 0 hidden/system files are excluded from the search.
+ */
+ if (attrib & (_A_HIDDEN | _A_SYSTEM))
+ continue;
+ if (!(finfo.name= strdup_root(names_storage, find.name)))
+ goto error;
+ if (MyFlags & MY_WANT_STAT)
+ {
+ if (!(finfo.mystat= (MY_STAT*)alloc_root(names_storage,
+ sizeof(MY_STAT))))
+ goto error;
+
+ memset(finfo.mystat, 0, sizeof(MY_STAT));
+ finfo.mystat->st_size=find.size;
+ mode= MY_S_IREAD;
+ if (!(attrib & _A_RDONLY))
+ mode|= MY_S_IWRITE;
+ if (attrib & _A_SUBDIR)
+ mode|= MY_S_IFDIR;
+ finfo.mystat->st_mode= mode;
+ finfo.mystat->st_mtime= ((uint32) find.time_write);
+ }
+ else
+ finfo.mystat= NULL;
+
+ if (insert_dynamic(dir_entries_storage, (uchar*)&finfo))
+ goto error;
+ }
+ while (_findnext(handle,&find) == 0);
+
+ _findclose(handle);
+ }
+
+ result->dir_entry= (FILEINFO *)dir_entries_storage->buffer;
+ result->number_off_files= dir_entries_storage->elements;
+
+ if (!(MyFlags & MY_DONT_SORT))
+ my_qsort((void *) result->dir_entry, result->number_off_files,
+ sizeof(FILEINFO), (qsort_cmp) comp_names);
+ DBUG_PRINT("exit", ("found %d files", result->number_off_files));
+ DBUG_RETURN(result);
+error:
+ set_my_errno(errno);
+ if (handle != -1)
+ _findclose(handle);
+ my_dirend(result);
+ if (MyFlags & MY_FAE+MY_WME)
+ {
+ char errbuf[MYSYS_STRERROR_SIZE];
+ my_error(EE_DIR, MYF(0), path,
+ errno, my_strerror(errbuf, sizeof(errbuf), errno));
+ }
+ DBUG_RETURN((MY_DIR *) NULL);
+} /* my_dir */
+
+#endif /* _WIN32 */
+
+/****************************************************************************
+** File status
+** Note that MY_STAT is assumed to be same as struct stat
+****************************************************************************/
+
+
+int my_fstat(File Filedes, MY_STAT *stat_area,
+ myf MyFlags MY_ATTRIBUTE((unused)))
+{
+ DBUG_ENTER("my_fstat");
+ DBUG_PRINT("my",("fd: %d MyFlags: %d", Filedes, MyFlags));
+#ifdef _WIN32
+ DBUG_RETURN(my_win_fstat(Filedes, stat_area));
+#else
+ DBUG_RETURN(fstat(Filedes, (struct stat *) stat_area));
+#endif
+}
+
+
+MY_STAT *my_stat(const char *path, MY_STAT *stat_area, myf my_flags)
+{
+ const int m_used= (stat_area == NULL);
+ DBUG_ENTER("my_stat");
+ DBUG_PRINT("my", ("path: '%s' stat_area: 0x%lx MyFlags: %d", path,
+ (long) stat_area, my_flags));
+
+ if (m_used)
+ if (!(stat_area= (MY_STAT *) my_malloc(key_memory_MY_STAT,
+ sizeof(MY_STAT), my_flags)))
+ goto error;
+#ifndef _WIN32
+ if (! stat((char *) path, (struct stat *) stat_area) )
+ DBUG_RETURN(stat_area);
+#else
+ if (! my_win_stat(path, stat_area) )
+ DBUG_RETURN(stat_area);
+#endif
+ DBUG_PRINT("error",("Got errno: %d from stat", errno));
+ set_my_errno(errno);
+ if (m_used) /* Free if new area */
+ my_free(stat_area);
+
+error:
+ if (my_flags & (MY_FAE+MY_WME))
+ {
+ char errbuf[MYSYS_STRERROR_SIZE];
+ my_error(EE_STAT, MYF(0), path,
+ my_errno(), my_strerror(errbuf, sizeof(errbuf), my_errno()));
+ DBUG_RETURN((MY_STAT *) NULL);
+ }
+ DBUG_RETURN((MY_STAT *) NULL);
+} /* my_stat */
diff --git a/mysql/mysys/my_lock.c b/mysql/mysys/my_lock.c
new file mode 100644
index 0000000..1b4336c
--- /dev/null
+++ b/mysql/mysys/my_lock.c
@@ -0,0 +1,221 @@
+/* Copyright (c) 2000, 2016, Oracle and/or its affiliates. All rights reserved.
+
+ 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; version 2 of the License.
+
+ 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include "mysys_priv.h"
+#include "my_sys.h"
+#include "mysys_err.h"
+#include <errno.h>
+#include "my_thread_local.h"
+
+
+#ifndef _WIN32
+#include <signal.h>
+
+static int volatile my_have_got_alarm= 0;
+static uint my_time_to_wait_for_lock= 2; /* In seconds */
+
+void my_set_alarm_variable(int signo MY_ATTRIBUTE((unused)))
+{
+ my_have_got_alarm= 1; /* Tell program that time expired */
+}
+#endif /* !_WIN32 */
+
+#ifdef _WIN32
+#define WIN_LOCK_INFINITE -1
+#define WIN_LOCK_SLEEP_MILLIS 100
+
+static int win_lock(File fd, int locktype, my_off_t start, my_off_t length,
+ int timeout_sec)
+{
+ LARGE_INTEGER liOffset,liLength;
+ DWORD dwFlags;
+ OVERLAPPED ov= {0};
+ HANDLE hFile= (HANDLE)my_get_osfhandle(fd);
+ DWORD lastError= 0;
+ int i;
+ int timeout_millis= timeout_sec * 1000;
+
+ DBUG_ENTER("win_lock");
+
+ liOffset.QuadPart= start;
+ liLength.QuadPart= length;
+
+ ov.Offset= liOffset.LowPart;
+ ov.OffsetHigh= liOffset.HighPart;
+
+ if (locktype == F_UNLCK)
+ {
+ if (UnlockFileEx(hFile, 0, liLength.LowPart, liLength.HighPart, &ov))
+ DBUG_RETURN(0);
+ /*
+ For compatibility with fcntl implementation, ignore error,
+ if region was not locked
+ */
+ if (GetLastError() == ERROR_NOT_LOCKED)
+ {
+ SetLastError(0);
+ DBUG_RETURN(0);
+ }
+ goto error;
+ }
+ else if (locktype == F_RDLCK)
+ /* read lock is mapped to a shared lock. */
+ dwFlags= 0;
+ else
+ /* write lock is mapped to an exclusive lock. */
+ dwFlags= LOCKFILE_EXCLUSIVE_LOCK;
+
+ /*
+ Drop old lock first to avoid double locking.
+ During analyze of Bug#38133 (Myisamlog test fails on Windows)
+ I met the situation that the program myisamlog locked the file
+ exclusively, then additionally shared, then did one unlock, and
+ then blocked on an attempt to lock it exclusively again.
+ Unlocking before every lock fixed the problem.
+ Note that this introduces a race condition. When the application
+ wants to convert an exclusive lock into a shared one, it will now
+ first unlock the file and then lock it shared. A waiting exclusive
+ lock could step in here. For reasons described in Bug#38133 and
+ Bug#41124 (Server hangs on Windows with --external-locking after
+ INSERT...SELECT) and in the review thread at
+ http://lists.mysql.com/commits/60721 it seems to be the better
+ option than not to unlock here.
+ If one day someone notices a way how to do file lock type changes
+ on Windows without unlocking before taking the new lock, please
+ change this code accordingly to fix the race condition.
+ */
+ if (!UnlockFileEx(hFile, 0, liLength.LowPart, liLength.HighPart, &ov) &&
+ (GetLastError() != ERROR_NOT_LOCKED))
+ goto error;
+
+ if (timeout_sec == WIN_LOCK_INFINITE)
+ {
+ if (LockFileEx(hFile, dwFlags, 0, liLength.LowPart, liLength.HighPart, &ov))
+ DBUG_RETURN(0);
+ goto error;
+ }
+
+ dwFlags|= LOCKFILE_FAIL_IMMEDIATELY;
+ timeout_millis= timeout_sec * 1000;
+ /* Try lock in a loop, until the lock is acquired or timeout happens */
+ for(i= 0; ;i+= WIN_LOCK_SLEEP_MILLIS)
+ {
+ if (LockFileEx(hFile, dwFlags, 0, liLength.LowPart, liLength.HighPart, &ov))
+ DBUG_RETURN(0);
+
+ if (GetLastError() != ERROR_LOCK_VIOLATION)
+ goto error;
+
+ if (i >= timeout_millis)
+ break;
+ Sleep(WIN_LOCK_SLEEP_MILLIS);
+ }
+
+ /* timeout */
+ errno= EAGAIN;
+ DBUG_RETURN(-1);
+
+error:
+ my_osmaperr(GetLastError());
+ DBUG_RETURN(-1);
+}
+#endif
+
+
+
+/*
+ Lock a part of a file
+
+ RETURN VALUE
+ 0 Success
+ -1 An error has occured and 'my_errno' is set
+ to indicate the actual error code.
+*/
+
+int my_lock(File fd, int locktype, my_off_t start, my_off_t length,
+ myf MyFlags)
+{
+ DBUG_ENTER("my_lock");
+ DBUG_PRINT("my",("fd: %d Op: %d start: %ld Length: %ld MyFlags: %d",
+ fd,locktype,(long) start,(long) length,MyFlags));
+ if (my_disable_locking)
+ DBUG_RETURN(0);
+
+#if defined(_WIN32)
+ {
+ int timeout_sec;
+ if (MyFlags & MY_DONT_WAIT)
+ timeout_sec= 0;
+ else
+ timeout_sec= WIN_LOCK_INFINITE;
+
+ if (win_lock(fd, locktype, start, length, timeout_sec) == 0)
+ DBUG_RETURN(0);
+ }
+#else
+ {
+ struct flock lock;
+
+ lock.l_type= (short) locktype;
+ lock.l_whence= SEEK_SET;
+ lock.l_start= (off_t) start;
+ lock.l_len= (off_t) length;
+
+ if (MyFlags & MY_DONT_WAIT)
+ {
+ int value;
+ uint alarm_old;
+ sig_return alarm_signal;
+
+ if (fcntl(fd,F_SETLK,&lock) != -1) /* Check if we can lock */
+ DBUG_RETURN(0); /* Ok, file locked */
+ DBUG_PRINT("info",("Was locked, trying with alarm"));
+ my_have_got_alarm= 0;
+ alarm_old= alarm(my_time_to_wait_for_lock);
+ alarm_signal= signal(SIGALRM, my_set_alarm_variable);
+ while ((value= fcntl(fd, F_SETLKW, &lock)) && !my_have_got_alarm &&
+ errno == EINTR)
+ { /* Setup again so we don`t miss it */
+ (void) alarm(my_time_to_wait_for_lock);
+ my_have_got_alarm= 0;
+ }
+ (void) signal(SIGALRM, alarm_signal);
+ (void) alarm(alarm_old);
+ if (value != -1)
+ DBUG_RETURN(0);
+ if (errno == EINTR)
+ errno=EAGAIN;
+ }
+ else if (fcntl(fd,F_SETLKW,&lock) != -1) /* Wait until a lock */
+ DBUG_RETURN(0);
+ }
+#endif /* _WIN32 */
+
+ /* We got an error. We don't want EACCES errors */
+ set_my_errno((errno == EACCES) ? EAGAIN : errno ? errno : -1);
+
+ if (MyFlags & MY_WME)
+ {
+ char errbuf[MYSYS_STRERROR_SIZE];
+ if (locktype == F_UNLCK)
+ my_error(EE_CANTUNLOCK, MYF(0),
+ my_errno(), my_strerror(errbuf, sizeof(errbuf), my_errno()));
+ else
+ my_error(EE_CANTLOCK, MYF(0),
+ my_errno(), my_strerror(errbuf, sizeof(errbuf), my_errno()));
+ }
+ DBUG_PRINT("error",("my_errno: %d (%d)",my_errno(),errno));
+ DBUG_RETURN(-1);
+} /* my_lock */
diff --git a/mysql/mysys/my_malloc.c b/mysql/mysys/my_malloc.c
new file mode 100644
index 0000000..230ee5b
--- /dev/null
+++ b/mysql/mysys/my_malloc.c
@@ -0,0 +1,325 @@
+/* Copyright (c) 2000, 2016, Oracle and/or its affiliates. All rights reserved.
+
+ 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; version 2 of the License.
+
+ 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include "mysys_priv.h"
+#include "my_sys.h"
+#include "mysys_err.h"
+#include <m_string.h>
+#include "my_thread_local.h"
+
+#ifdef HAVE_PSI_MEMORY_INTERFACE
+#define USE_MALLOC_WRAPPER
+#endif
+
+static void *my_raw_malloc(size_t size, myf my_flags);
+static void my_raw_free(void *ptr);
+
+#ifdef USE_MALLOC_WRAPPER
+struct my_memory_header
+{
+ PSI_memory_key m_key;
+ uint m_magic;
+ size_t m_size;
+ PSI_thread *m_owner;
+};
+typedef struct my_memory_header my_memory_header;
+#define HEADER_SIZE 32
+
+#define MAGIC 1234
+
+#define USER_TO_HEADER(P) \
+ ( (my_memory_header*) (((char *) P) - HEADER_SIZE ))
+#define HEADER_TO_USER(P) \
+ ( ((char*) P) + HEADER_SIZE )
+
+void * my_malloc(PSI_memory_key key, size_t size, myf flags)
+{
+ my_memory_header *mh;
+ size_t raw_size;
+ compile_time_assert(sizeof(my_memory_header) <= HEADER_SIZE);
+
+ raw_size= HEADER_SIZE + size;
+ mh= (my_memory_header*) my_raw_malloc(raw_size, flags);
+ if (likely(mh != NULL))
+ {
+ void *user_ptr;
+ mh->m_magic= MAGIC;
+ mh->m_size= size;
+ mh->m_key= PSI_MEMORY_CALL(memory_alloc)(key, size, & mh->m_owner);
+ user_ptr= HEADER_TO_USER(mh);
+ MEM_MALLOCLIKE_BLOCK(user_ptr, size, 0, (flags & MY_ZEROFILL));
+ return user_ptr;
+ }
+ return NULL;
+}
+
+void *
+my_realloc(PSI_memory_key key, void *ptr, size_t size, myf flags)
+{
+ my_memory_header *old_mh;
+ size_t old_size;
+ size_t min_size;
+ void *new_ptr;
+
+ if (ptr == NULL)
+ return my_malloc(key, size, flags);
+
+ old_mh= USER_TO_HEADER(ptr);
+ DBUG_ASSERT((old_mh->m_key == key) || (old_mh->m_key == PSI_NOT_INSTRUMENTED));
+ DBUG_ASSERT(old_mh->m_magic == MAGIC);
+
+ old_size= old_mh->m_size;
+
+ if (old_size == size)
+ return ptr;
+
+ new_ptr= my_malloc(key, size, flags);
+ if (likely(new_ptr != NULL))
+ {
+#ifndef DBUG_OFF
+ my_memory_header *new_mh= USER_TO_HEADER(new_ptr);
+#endif
+
+ DBUG_ASSERT((new_mh->m_key == key) || (new_mh->m_key == PSI_NOT_INSTRUMENTED));
+ DBUG_ASSERT(new_mh->m_magic == MAGIC);
+ DBUG_ASSERT(new_mh->m_size == size);
+
+ min_size= (old_size < size) ? old_size : size;
+ memcpy(new_ptr, ptr, min_size);
+ my_free(ptr);
+
+ return new_ptr;
+ }
+ return NULL;
+}
+
+void my_claim(void *ptr)
+{
+ my_memory_header *mh;
+
+ if (ptr == NULL)
+ return;
+
+ mh= USER_TO_HEADER(ptr);
+ DBUG_ASSERT(mh->m_magic == MAGIC);
+ mh->m_key= PSI_MEMORY_CALL(memory_claim)(mh->m_key, mh->m_size, & mh->m_owner);
+}
+
+void my_free(void *ptr)
+{
+ my_memory_header *mh;
+
+ if (ptr == NULL)
+ return;
+
+ mh= USER_TO_HEADER(ptr);
+ DBUG_ASSERT(mh->m_magic == MAGIC);
+ PSI_MEMORY_CALL(memory_free)(mh->m_key, mh->m_size, mh->m_owner);
+ /* Catch double free */
+ mh->m_magic= 0xDEAD;
+ MEM_FREELIKE_BLOCK(ptr, 0);
+ my_raw_free(mh);
+}
+
+#else
+
+void *my_malloc(PSI_memory_key key MY_ATTRIBUTE((unused)),
+ size_t size, myf my_flags)
+{
+ return my_raw_malloc(size, my_flags);
+}
+
+static void *my_raw_realloc(void *oldpoint, size_t size, myf my_flags);
+
+void *my_realloc(PSI_memory_key key MY_ATTRIBUTE((unused)),
+ void *ptr, size_t size, myf flags)
+{
+ return my_raw_realloc(ptr, size, flags);
+}
+
+void my_claim(void *ptr MY_ATTRIBUTE((unused)))
+{
+ /* Empty */
+}
+
+void my_free(void *ptr)
+{
+ my_raw_free(ptr);
+}
+#endif
+
+
+/**
+ Allocate a sized block of memory.
+
+ @param size The size of the memory block in bytes.
+ @param flags Failure action modifiers (bitmasks).
+
+ @return A pointer to the allocated memory block, or NULL on failure.
+*/
+static void *my_raw_malloc(size_t size, myf my_flags)
+{
+ void* point;
+ DBUG_ENTER("my_raw_malloc");
+ DBUG_PRINT("my",("size: %lu my_flags: %d", (ulong) size, my_flags));
+
+ /* Safety */
+ if (!size)
+ size=1;
+
+#if defined(MY_MSCRT_DEBUG)
+ if (my_flags & MY_ZEROFILL)
+ point= _calloc_dbg(size, 1, _CLIENT_BLOCK, __FILE__, __LINE__);
+ else
+ point= _malloc_dbg(size, _CLIENT_BLOCK, __FILE__, __LINE__);
+#else
+ if (my_flags & MY_ZEROFILL)
+ point= calloc(size, 1);
+ else
+ point= malloc(size);
+#endif
+
+ DBUG_EXECUTE_IF("simulate_out_of_memory",
+ {
+ free(point);
+ point= NULL;
+ });
+ DBUG_EXECUTE_IF("simulate_persistent_out_of_memory",
+ {
+ free(point);
+ point= NULL;
+ });
+
+ if (point == NULL)
+ {
+ set_my_errno(errno);
+ if (my_flags & MY_FAE)
+ error_handler_hook=fatal_error_handler_hook;
+ if (my_flags & (MY_FAE+MY_WME))
+ my_error(EE_OUTOFMEMORY, MYF(ME_ERRORLOG + ME_FATALERROR),size);
+ DBUG_EXECUTE_IF("simulate_out_of_memory",
+ DBUG_SET("-d,simulate_out_of_memory"););
+ if (my_flags & MY_FAE)
+ exit(1);
+ }
+
+ DBUG_PRINT("exit",("ptr: %p", point));
+ DBUG_RETURN(point);
+}
+
+
+#ifndef USE_MALLOC_WRAPPER
+/**
+ @brief wrapper around realloc()
+
+ @param oldpoint pointer to currently allocated area
+ @param size new size requested, must be >0
+ @param my_flags flags
+
+ @note if size==0 realloc() may return NULL; my_realloc() treats this as an
+ error which is not the intention of realloc()
+*/
+static void *my_raw_realloc(void *oldpoint, size_t size, myf my_flags)
+{
+ void *point;
+ DBUG_ENTER("my_realloc");
+ DBUG_PRINT("my",("ptr: %p size: %lu my_flags: %d", oldpoint,
+ (ulong) size, my_flags));
+
+ DBUG_ASSERT(size > 0);
+ /* These flags are mutually exclusive. */
+ DBUG_ASSERT(!((my_flags & MY_FREE_ON_ERROR) &&
+ (my_flags & MY_HOLD_ON_ERROR)));
+ DBUG_EXECUTE_IF("simulate_out_of_memory",
+ point= NULL;
+ goto end;);
+ if (!oldpoint && (my_flags & MY_ALLOW_ZERO_PTR))
+ DBUG_RETURN(my_raw_malloc(size, my_flags));
+#if defined(MY_MSCRT_DEBUG)
+ point= _realloc_dbg(oldpoint, size, _CLIENT_BLOCK, __FILE__, __LINE__);
+#else
+ point= realloc(oldpoint, size);
+#endif
+#ifndef DBUG_OFF
+end:
+#endif
+ if (point == NULL)
+ {
+ if (my_flags & MY_HOLD_ON_ERROR)
+ DBUG_RETURN(oldpoint);
+ if (my_flags & MY_FREE_ON_ERROR)
+ my_free(oldpoint);
+ set_my_errno(errno);
+ if (my_flags & (MY_FAE+MY_WME))
+ my_error(EE_OUTOFMEMORY, MYF(ME_FATALERROR),
+ size);
+ DBUG_EXECUTE_IF("simulate_out_of_memory",
+ DBUG_SET("-d,simulate_out_of_memory"););
+ }
+ DBUG_PRINT("exit",("ptr: %p", point));
+ DBUG_RETURN(point);
+}
+#endif
+
+/**
+ Free memory allocated with my_raw_malloc.
+
+ @remark Relies on free being able to handle a NULL argument.
+
+ @param ptr Pointer to the memory allocated by my_raw_malloc.
+*/
+static void my_raw_free(void *ptr)
+{
+ DBUG_ENTER("my_free");
+ DBUG_PRINT("my",("ptr: %p", ptr));
+#if defined(MY_MSCRT_DEBUG)
+ _free_dbg(ptr, _CLIENT_BLOCK);
+#else
+ free(ptr);
+#endif
+ DBUG_VOID_RETURN;
+}
+
+
+void *my_memdup(PSI_memory_key key, const void *from, size_t length, myf my_flags)
+{
+ void *ptr;
+ if ((ptr= my_malloc(key, length, my_flags)) != 0)
+ memcpy(ptr, from, length);
+ return ptr;
+}
+
+
+char *my_strdup(PSI_memory_key key, const char *from, myf my_flags)
+{
+ char *ptr;
+ size_t length= strlen(from)+1;
+ if ((ptr= (char*) my_malloc(key, length, my_flags)))
+ memcpy(ptr, from, length);
+ return ptr;
+}
+
+
+char *my_strndup(PSI_memory_key key, const char *from, size_t length, myf my_flags)
+{
+ char *ptr;
+ if ((ptr= (char*) my_malloc(key, length+1, my_flags)))
+ {
+ memcpy(ptr, from, length);
+ ptr[length]= 0;
+ }
+ return ptr;
+}
+
diff --git a/mysql/mysys/my_memmem.c b/mysql/mysys/my_memmem.c
new file mode 100644
index 0000000..e15dedf
--- /dev/null
+++ b/mysql/mysys/my_memmem.c
@@ -0,0 +1,84 @@
+/* Copyright (c) 2000, 2006, 2007 MySQL AB
+ Use is subject to license terms
+
+ 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; version 2 of the License.
+
+ 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */
+
+#include <my_global.h>
+#include <m_string.h>
+
+/*
+ my_memmem, port of a GNU extension.
+
+ Returns a pointer to the beginning of the substring, needle, or NULL if the
+ substring is not found in haystack.
+*/
+
+void *my_memmem(const void *haystack, size_t haystacklen,
+ const void *needle, size_t needlelen)
+{
+ const unsigned char *cursor;
+ const unsigned char *last_possible_needle_location =
+ (unsigned char *)haystack + haystacklen - needlelen;
+
+ /* Easy answers */
+ if (needlelen > haystacklen) return(NULL);
+ if (needle == NULL) return(NULL);
+ if (haystack == NULL) return(NULL);
+ if (needlelen == 0) return(NULL);
+ if (haystacklen == 0) return(NULL);
+
+ for (cursor = haystack; cursor <= last_possible_needle_location; cursor++) {
+ if (memcmp(needle, cursor, needlelen) == 0) {
+ return((void *) cursor);
+ }
+ }
+ return(NULL);
+}
+
+
+
+#ifdef MAIN
+#include <assert.h>
+
+int main(int argc, char *argv[]) {
+ char haystack[10], needle[3];
+
+ memmove(haystack, "0123456789", 10);
+
+ memmove(needle, "no", 2);
+ assert(my_memmem(haystack, 10, needle, 2) == NULL);
+
+ memmove(needle, "345", 3);
+ assert(my_memmem(haystack, 10, needle, 3) != NULL);
+
+ memmove(needle, "789", 3);
+ assert(my_memmem(haystack, 10, needle, 3) != NULL);
+ assert(my_memmem(haystack, 9, needle, 3) == NULL);
+
+ memmove(needle, "012", 3);
+ assert(my_memmem(haystack, 10, needle, 3) != NULL);
+ assert(my_memmem(NULL, 10, needle, 3) == NULL);
+
+ assert(my_memmem(NULL, 10, needle, 3) == NULL);
+ assert(my_memmem(haystack, 0, needle, 3) == NULL);
+ assert(my_memmem(haystack, 10, NULL, 3) == NULL);
+ assert(my_memmem(haystack, 10, needle, 0) == NULL);
+
+ assert(my_memmem(haystack, 1, needle, 3) == NULL);
+
+ printf("success\n");
+ return(0);
+}
+
+#endif
diff --git a/mysql/mysys/my_mess.c b/mysql/mysys/my_mess.c
new file mode 100644
index 0000000..0c6d24d
--- /dev/null
+++ b/mysql/mysys/my_mess.c
@@ -0,0 +1,65 @@
+/* Copyright (c) 2000, 2016, Oracle and/or its affiliates. All rights reserved.
+
+ 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; version 2 of the License.
+
+ 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include "mysys_priv.h"
+#include "my_sys.h"
+
+/**
+ Print an error message on stderr.
+ Prefixed with the binary's name (sans .exe, where applicable,
+ and without path, both to keep our test cases sane).
+ The name is intended to aid debugging by clarifying which
+ binary reported an error, especially in cases like mysql_upgrade
+ which calls several other tools whose messages should be
+ distinguishable from each other's, and from mysql_upgrade's.
+
+ This is low-level, in most cases, you should use my_message_local()
+ instead (which by default goes through my_message_local_stderr(),
+ which is a wrapper around this function that adds a severity level).
+
+ @param error The error number. Currently unused.
+ @param str The message to print. Not trailing \n needed.
+ @param MyFlags ME_BELL to beep, or 0.
+*/
+void my_message_stderr(uint error MY_ATTRIBUTE((unused)),
+ const char *str, myf MyFlags)
+{
+ DBUG_ENTER("my_message_stderr");
+ DBUG_PRINT("enter",("message: %s",str));
+ (void) fflush(stdout);
+ if (MyFlags & ME_BELL)
+ (void) fputc('\007', stderr);
+ if (my_progname)
+ {
+ size_t l;
+ const char *r;
+
+ if ((r= strrchr(my_progname, FN_LIBCHAR)))
+ r++;
+ else
+ r= my_progname;
+
+ l= strlen(r);
+ #ifdef _WIN32
+ if ((l > 4) && !strcmp(&r[l - 4], ".exe"))
+ l-= 4; /* purecov: inspected */ /* Windows-only */
+ #endif
+ fprintf(stderr, "%.*s: ", (int) l, r);
+ }
+ (void)fputs(str,stderr);
+ (void)fputc('\n',stderr);
+ (void)fflush(stderr);
+ DBUG_VOID_RETURN;
+}
diff --git a/mysql/mysys/my_mkdir.c b/mysql/mysys/my_mkdir.c
new file mode 100644
index 0000000..00f0a48
--- /dev/null
+++ b/mysql/mysys/my_mkdir.c
@@ -0,0 +1,48 @@
+/* Copyright (c) 2000, 2015, Oracle and/or its affiliates. All rights reserved.
+
+ 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; version 2 of the License.
+
+ 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */
+
+#include "mysys_priv.h"
+#include "my_sys.h"
+#include "mysys_err.h"
+#include "my_thread_local.h"
+#include <sys/types.h>
+#include <sys/stat.h>
+#ifdef _WIN32
+#include <direct.h>
+#endif
+
+int my_mkdir(const char *dir, int Flags, myf MyFlags)
+{
+ DBUG_ENTER("my_dir");
+ DBUG_PRINT("enter",("dir: %s",dir));
+
+#if defined(_WIN32)
+ if (mkdir((char*) dir))
+#else
+ if (mkdir((char*) dir, Flags & my_umask_dir))
+#endif
+ {
+ set_my_errno(errno);
+ DBUG_PRINT("error",("error %d when creating direcory %s",my_errno(),dir));
+ if (MyFlags & (MY_FFNF | MY_FAE | MY_WME))
+ {
+ char errbuf[MYSYS_STRERROR_SIZE];
+ my_error(EE_CANT_MKDIR, MYF(0), dir,
+ my_errno(), my_strerror(errbuf, sizeof(errbuf), my_errno()));
+ }
+ DBUG_RETURN(-1);
+ }
+ DBUG_RETURN(0);
+}
diff --git a/mysql/mysys/my_mmap.c b/mysql/mysys/my_mmap.c
new file mode 100644
index 0000000..b58c657
--- /dev/null
+++ b/mysql/mysys/my_mmap.c
@@ -0,0 +1,89 @@
+/* Copyright (c) 2000, 2015, Oracle and/or its affiliates. All rights reserved.
+
+ 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; version 2 of the License.
+
+ 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include "mysys_priv.h"
+#include "my_sys.h"
+
+#ifdef HAVE_SYS_MMAN_H
+
+/*
+ system msync() only syncs mmap'ed area to fs cache.
+ fsync() is required to really sync to disc
+*/
+int my_msync(int fd, void *addr, size_t len, int flags)
+{
+ msync(addr, len, flags);
+ return my_sync(fd, MYF(0));
+}
+
+#elif defined(_WIN32)
+
+static SECURITY_ATTRIBUTES mmap_security_attributes=
+ {sizeof(SECURITY_ATTRIBUTES), 0, TRUE};
+
+void *my_mmap(void *addr, size_t len, int prot,
+ int flags, File fd, my_off_t offset)
+{
+ HANDLE hFileMap;
+ LPVOID ptr;
+ HANDLE hFile= (HANDLE)my_get_osfhandle(fd);
+ DBUG_ENTER("my_mmap");
+ DBUG_PRINT("mysys", ("map fd: %d", fd));
+
+ if (hFile == INVALID_HANDLE_VALUE)
+ DBUG_RETURN(MAP_FAILED);
+
+ hFileMap=CreateFileMapping(hFile, &mmap_security_attributes,
+ PAGE_READWRITE, 0, (DWORD) len, NULL);
+ if (hFileMap == 0)
+ DBUG_RETURN(MAP_FAILED);
+
+ ptr=MapViewOfFile(hFileMap,
+ prot & PROT_WRITE ? FILE_MAP_WRITE : FILE_MAP_READ,
+ (DWORD)(offset >> 32), (DWORD)offset, len);
+
+ /*
+ MSDN explicitly states that it's possible to close File Mapping Object
+ even when a view is not unmapped - then the object will be held open
+ implicitly until unmap, as every view stores internally a handler of
+ a corresponding File Mapping Object
+ */
+ CloseHandle(hFileMap);
+
+ if (ptr)
+ {
+ DBUG_PRINT("mysys", ("mapped addr: %p", ptr));
+ DBUG_RETURN(ptr);
+ }
+
+ DBUG_RETURN(MAP_FAILED);
+}
+
+int my_munmap(void *addr, size_t len)
+{
+ DBUG_ENTER("my_munmap");
+ DBUG_PRINT("mysys", ("unmap addr: %p", addr));
+ DBUG_RETURN(UnmapViewOfFile(addr) ? 0 : -1);
+}
+
+int my_msync(int fd, void *addr, size_t len, int flags)
+{
+ return FlushViewOfFile(addr, len) ? 0 : -1;
+}
+
+#else
+#error "no mmap!"
+#endif
+
diff --git a/mysql/mysys/my_once.c b/mysql/mysys/my_once.c
new file mode 100644
index 0000000..b0938f0
--- /dev/null
+++ b/mysql/mysys/my_once.c
@@ -0,0 +1,120 @@
+/* Copyright (c) 2000, 2015, Oracle and/or its affiliates. All rights reserved.
+
+ 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; version 2 of the License.
+
+ 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+/* Not MT-SAFE */
+
+#include "mysys_priv.h"
+#include "my_static.h"
+#include "mysys_err.h"
+#include <m_string.h>
+#include "my_thread_local.h"
+
+/*
+ Alloc for things we don't nend to free run-time (that only
+ should be free'd on exit)
+
+ SYNOPSIS
+ my_once_alloc()
+ Size
+ MyFlags
+
+ NOTES
+ No DBUG_ENTER... here to get smaller dbug-startup
+*/
+
+void* my_once_alloc(size_t Size, myf MyFlags)
+{
+ size_t get_size, max_left;
+ uchar* point;
+ USED_MEM *next;
+ USED_MEM **prev;
+
+ Size= ALIGN_SIZE(Size);
+ prev= &my_once_root_block;
+ max_left=0;
+ for (next=my_once_root_block ; next && next->left < Size ; next= next->next)
+ {
+ if (next->left > max_left)
+ max_left=next->left;
+ prev= &next->next;
+ }
+ if (! next)
+ { /* Time to alloc new block */
+ get_size= Size+ALIGN_SIZE(sizeof(USED_MEM));
+ if (max_left*4 < my_once_extra && get_size < my_once_extra)
+ get_size=my_once_extra; /* Normal alloc */
+
+ if ((next = (USED_MEM*) malloc(get_size)) == 0)
+ {
+ set_my_errno(errno);
+ if (MyFlags & (MY_FAE+MY_WME))
+ my_error(EE_OUTOFMEMORY, MYF(ME_FATALERROR), get_size);
+ return((uchar*) 0);
+ }
+ DBUG_PRINT("test",("my_once_malloc %lu byte malloced", (ulong) get_size));
+ next->next= 0;
+ next->size= (uint)get_size;
+ next->left= (uint)(get_size-ALIGN_SIZE(sizeof(USED_MEM)));
+ *prev=next;
+ }
+ point= (uchar*) ((char*) next+ (next->size-next->left));
+ next->left-= (uint)Size;
+
+ if (MyFlags & MY_ZEROFILL)
+ memset(point, 0, Size);
+ return((void*) point);
+} /* my_once_alloc */
+
+
+char *my_once_strdup(const char *src,myf myflags)
+{
+ size_t len= strlen(src)+1;
+ uchar *dst= my_once_alloc(len, myflags);
+ if (dst)
+ memcpy(dst, src, len);
+ return (char*) dst;
+}
+
+
+void *my_once_memdup(const void *src, size_t len, myf myflags)
+{
+ uchar *dst= my_once_alloc(len, myflags);
+ if (dst)
+ memcpy(dst, src, len);
+ return dst;
+}
+
+
+/*
+ Deallocate everything that was allocated with my_once_alloc
+
+ SYNOPSIS
+ my_once_free()
+*/
+
+void my_once_free(void)
+{
+ USED_MEM *next,*old;
+ DBUG_ENTER("my_once_free");
+
+ for (next=my_once_root_block ; next ; )
+ {
+ old=next; next= next->next ;
+ free((uchar*) old);
+ }
+ my_once_root_block=0;
+
+ DBUG_VOID_RETURN;
+} /* my_once_free */
diff --git a/mysql/mysys/my_open.c b/mysql/mysys/my_open.c
new file mode 100644
index 0000000..b67ac6f
--- /dev/null
+++ b/mysql/mysys/my_open.c
@@ -0,0 +1,194 @@
+/* Copyright (c) 2000, 2015, Oracle and/or its affiliates. All rights reserved.
+
+ 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; version 2 of the License.
+
+ 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include "mysys_priv.h"
+#include "my_sys.h"
+#include "mysys_err.h"
+#include <my_dir.h>
+#include <errno.h>
+#include "my_thread_local.h"
+
+
+/*
+ Open a file
+
+ SYNOPSIS
+ my_open()
+ FileName Fully qualified file name
+ Flags Read | write
+ MyFlags Special flags
+
+ RETURN VALUE
+ File descriptor
+*/
+
+File my_open(const char *FileName, int Flags, myf MyFlags)
+ /* Path-name of file */
+ /* Read | write .. */
+ /* Special flags */
+{
+ File fd;
+ DBUG_ENTER("my_open");
+ DBUG_PRINT("my",("Name: '%s' Flags: %d MyFlags: %d",
+ FileName, Flags, MyFlags));
+#if defined(_WIN32)
+ fd= my_win_open(FileName, Flags);
+#else
+ fd = open(FileName, Flags, my_umask); /* Normal unix */
+#endif
+
+ fd= my_register_filename(fd, FileName, FILE_BY_OPEN, EE_FILENOTFOUND, MyFlags);
+ DBUG_RETURN(fd);
+} /* my_open */
+
+
+/*
+ Close a file
+
+ SYNOPSIS
+ my_close()
+ fd File sescriptor
+ myf Special Flags
+
+*/
+
+int my_close(File fd, myf MyFlags)
+{
+ int err;
+ DBUG_ENTER("my_close");
+ DBUG_PRINT("my",("fd: %d MyFlags: %d",fd, MyFlags));
+
+ mysql_mutex_lock(&THR_LOCK_open);
+#ifndef _WIN32
+ do
+ {
+ err= close(fd);
+ } while (err == -1 && errno == EINTR);
+#else
+ err= my_win_close(fd);
+#endif
+ if (err)
+ {
+ DBUG_PRINT("error",("Got error %d on close",err));
+ set_my_errno(errno);
+ if (MyFlags & (MY_FAE | MY_WME))
+ {
+ char errbuf[MYSYS_STRERROR_SIZE];
+ my_error(EE_BADCLOSE, MYF(0), my_filename(fd),
+ my_errno(), my_strerror(errbuf, sizeof(errbuf), my_errno()));
+ }
+ }
+ if ((uint) fd < my_file_limit && my_file_info[fd].type != UNOPEN)
+ {
+ my_free(my_file_info[fd].name);
+ my_file_info[fd].type = UNOPEN;
+ }
+ my_file_opened--;
+ mysql_mutex_unlock(&THR_LOCK_open);
+ DBUG_RETURN(err);
+} /* my_close */
+
+
+/*
+ Register file in my_file_info[]
+
+ SYNOPSIS
+ my_register_filename()
+ fd File number opened, -1 if error on open
+ FileName File name
+ type_file_type How file was created
+ error_message_number Error message number if caller got error (fd == -1)
+ MyFlags Flags for my_close()
+
+ RETURN
+ -1 error
+ # Filenumber
+
+*/
+
+File my_register_filename(File fd, const char *FileName, enum file_type
+ type_of_file, uint error_message_number, myf MyFlags)
+{
+ char *dup_filename= NULL;
+ DBUG_ENTER("my_register_filename");
+ if ((int) fd >= MY_FILE_MIN)
+ {
+ if ((uint) fd >= my_file_limit)
+ {
+#if defined(_WIN32)
+ set_my_errno(EMFILE);
+#else
+ mysql_mutex_lock(&THR_LOCK_open);
+ my_file_opened++;
+ mysql_mutex_unlock(&THR_LOCK_open);
+ DBUG_RETURN(fd); /* safeguard */
+#endif
+ }
+ else
+ {
+ dup_filename= my_strdup(key_memory_my_file_info, FileName, MyFlags);
+ if (dup_filename != NULL)
+ {
+ mysql_mutex_lock(&THR_LOCK_open);
+ my_file_info[fd].name= dup_filename;
+ my_file_opened++;
+ my_file_total_opened++;
+ my_file_info[fd].type = type_of_file;
+ mysql_mutex_unlock(&THR_LOCK_open);
+ DBUG_PRINT("exit",("fd: %d",fd));
+ DBUG_RETURN(fd);
+ }
+ set_my_errno(ENOMEM);
+ }
+ (void) my_close(fd, MyFlags);
+ }
+ else
+ set_my_errno(errno);
+
+ DBUG_PRINT("error",("Got error %d on open", my_errno()));
+ if (MyFlags & (MY_FFNF | MY_FAE | MY_WME))
+ {
+ char errbuf[MYSYS_STRERROR_SIZE];
+ if (my_errno() == EMFILE)
+ error_message_number= EE_OUT_OF_FILERESOURCES;
+ DBUG_PRINT("error",("print err: %d",error_message_number));
+ my_error(error_message_number, MYF(0), FileName,
+ my_errno(), my_strerror(errbuf, sizeof(errbuf), my_errno()));
+ }
+ DBUG_RETURN(-1);
+}
+
+
+
+
+#ifdef EXTRA_DEBUG
+
+void my_print_open_files(void)
+{
+ if (my_file_opened | my_stream_opened)
+ {
+ uint i;
+ for (i= 0 ; i < my_file_limit ; i++)
+ {
+ if (my_file_info[i].type != UNOPEN)
+ {
+ my_message_local(INFORMATION_LEVEL,
+ EE(EE_FILE_NOT_CLOSED), my_file_info[i].name, i);
+ }
+ }
+ }
+}
+
+#endif
diff --git a/mysql/mysys/my_pread.c b/mysql/mysys/my_pread.c
new file mode 100644
index 0000000..1511fd7
--- /dev/null
+++ b/mysql/mysys/my_pread.c
@@ -0,0 +1,207 @@
+/* Copyright (c) 2000, 2015, Oracle and/or its affiliates. All rights reserved.
+
+ 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; version 2 of the License.
+
+ 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include "mysys_priv.h"
+#include "my_sys.h"
+#include "mysys_err.h"
+#include "my_base.h"
+#include <m_string.h>
+#include <errno.h>
+#include "my_thread_local.h"
+
+
+/*
+ Read a chunk of bytes from a file from a given position
+
+ SYNOPSIOS
+ my_pread()
+ Filedes File decsriptor
+ Buffer Buffer to read data into
+ Count Number of bytes to read
+ offset Position to read from
+ MyFlags Flags
+
+ NOTES
+ This differs from the normal pread() call in that we don't care
+ to set the position in the file back to the original position
+ if the system doesn't support pread().
+
+ RETURN
+ (size_t) -1 Error
+ # Number of bytes read
+*/
+
+size_t my_pread(File Filedes, uchar *Buffer, size_t Count, my_off_t offset,
+ myf MyFlags)
+{
+ size_t readbytes;
+ int error= 0;
+ DBUG_ENTER("my_pread");
+ DBUG_PRINT("my",("fd: %d Seek: %llu Buffer: %p Count: %lu MyFlags: %d",
+ Filedes, (ulonglong)offset, Buffer, (ulong)Count, MyFlags));
+ for (;;)
+ {
+ errno= 0; /* Linux, Windows don't reset this on EOF/success */
+#if defined(_WIN32)
+ readbytes= my_win_pread(Filedes, Buffer, Count, offset);
+#else
+ readbytes= pread(Filedes, Buffer, Count, offset);
+#endif
+ error= (readbytes != Count);
+ if(error)
+ {
+ set_my_errno(errno ? errno : -1);
+ if (errno == 0 || (readbytes != (size_t) -1 &&
+ (MyFlags & (MY_NABP | MY_FNABP))))
+ set_my_errno(HA_ERR_FILE_TOO_SHORT);
+
+ DBUG_PRINT("warning",("Read only %d bytes off %u from %d, errno: %d",
+ (int) readbytes, (uint) Count,Filedes,my_errno()));
+
+ if ((readbytes == 0 || readbytes == (size_t) -1) && errno == EINTR)
+ {
+ DBUG_PRINT("debug", ("my_pread() was interrupted and returned %d",
+ (int) readbytes));
+ continue; /* Interrupted */
+ }
+
+ if (MyFlags & (MY_WME | MY_FAE | MY_FNABP))
+ {
+ char errbuf[MYSYS_STRERROR_SIZE];
+ if (readbytes == (size_t) -1)
+ my_error(EE_READ, MYF(0), my_filename(Filedes),
+ my_errno(), my_strerror(errbuf, sizeof(errbuf), my_errno()));
+ else if (MyFlags & (MY_NABP | MY_FNABP))
+ my_error(EE_EOFERR, MYF(0), my_filename(Filedes),
+ my_errno(), my_strerror(errbuf, sizeof(errbuf), my_errno()));
+ }
+ if (readbytes == (size_t) -1 || (MyFlags & (MY_FNABP | MY_NABP)))
+ DBUG_RETURN(MY_FILE_ERROR); /* Return with error */
+ }
+ if (MyFlags & (MY_NABP | MY_FNABP))
+ DBUG_RETURN(0); /* Read went ok; Return 0 */
+ DBUG_RETURN(readbytes); /* purecov: inspected */
+ }
+} /* my_pread */
+
+
+/**
+ Write a chunk of bytes to a file at a given position
+
+ SYNOPSIOS
+ my_pwrite()
+ Filedes File decsriptor
+ Buffer Buffer to write data from
+ Count Number of bytes to write
+ offset Position to write to
+ MyFlags Flags
+
+ NOTES
+ This differs from the normal pwrite() call in that we don't care
+ to set the position in the file back to the original position
+ if the system doesn't support pwrite()
+
+ if (MyFlags & (MY_NABP | MY_FNABP))
+ @returns
+ 0 if Count == 0
+ On succes, 0
+ On failure, (size_t)-1 == MY_FILE_ERROR
+
+ otherwise
+ @returns
+ 0 if Count == 0
+ On success, the number of bytes written.
+ On partial success (if less than Count bytes could be written),
+ the actual number of bytes written.
+ On failure, (size_t)-1 == MY_FILE_ERROR
+*/
+
+size_t my_pwrite(File Filedes, const uchar *Buffer, size_t Count,
+ my_off_t offset, myf MyFlags)
+{
+ size_t writtenbytes;
+ size_t sum_written= 0;
+ uint errors= 0;
+ const size_t initial_count= Count;
+
+ DBUG_ENTER("my_pwrite");
+ DBUG_PRINT("my",("fd: %d Seek: %llu Buffer: %p Count: %lu MyFlags: %d",
+ Filedes, offset, Buffer, (ulong)Count, MyFlags));
+
+ for (;;)
+ {
+ errno= 0;
+#if defined (_WIN32)
+ writtenbytes= my_win_pwrite(Filedes, Buffer, Count, offset);
+#else
+ writtenbytes= pwrite(Filedes, Buffer, Count, offset);
+#endif
+ if(writtenbytes == Count)
+ {
+ sum_written+= writtenbytes;
+ break;
+ }
+ set_my_errno(errno);
+ if (writtenbytes != (size_t) -1)
+ {
+ sum_written+= writtenbytes;
+ Buffer+= writtenbytes;
+ Count-= writtenbytes;
+ offset+= writtenbytes;
+ }
+ DBUG_PRINT("error",("Write only %u bytes", (uint) writtenbytes));
+
+ if (is_killed_hook(NULL))
+ MyFlags&= ~ MY_WAIT_IF_FULL; /* End if aborted by user */
+
+ if ((my_errno() == ENOSPC || my_errno() == EDQUOT) &&
+ (MyFlags & MY_WAIT_IF_FULL))
+ {
+ wait_for_free_space(my_filename(Filedes), errors);
+ errors++;
+ continue;
+ }
+ if (writtenbytes != 0 && writtenbytes != (size_t) -1)
+ continue;
+ else if (my_errno() == EINTR)
+ {
+ continue; /* Retry */
+ }
+ else if (writtenbytes == 0 && !errors++) /* Retry once */
+ {
+ /* We may come here if the file quota is exeeded */
+ continue;
+ }
+ break; /* Return bytes written */
+ }
+ if (MyFlags & (MY_NABP | MY_FNABP))
+ {
+ if (sum_written == initial_count)
+ DBUG_RETURN(0); /* Want only errors, not bytes written */
+ if (MyFlags & (MY_WME | MY_FAE | MY_FNABP))
+ {
+ char errbuf[MYSYS_STRERROR_SIZE];
+ my_error(EE_WRITE, MYF(0), my_filename(Filedes),
+ my_errno(), my_strerror(errbuf, sizeof(errbuf), my_errno()));
+ }
+ DBUG_RETURN(MY_FILE_ERROR);
+ }
+ DBUG_EXECUTE_IF("check", my_seek(Filedes, -1, SEEK_SET, MYF(0)););
+
+ if (sum_written == 0)
+ DBUG_RETURN(MY_FILE_ERROR);
+
+ DBUG_RETURN(sum_written);
+} /* my_pwrite */
diff --git a/mysql/mysys/my_rdtsc.c b/mysql/mysys/my_rdtsc.c
new file mode 100644
index 0000000..2be781f
--- /dev/null
+++ b/mysql/mysys/my_rdtsc.c
@@ -0,0 +1,902 @@
+/* Copyright (c) 2008, 2015, Oracle and/or its affiliates. All rights reserved.
+
+ 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; version 2 of the License.
+
+ 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+/*
+ rdtsc3 -- multi-platform timer code
+ pgulutzan@mysql.com, 2005-08-29
+ modified 2008-11-02
+
+ Functions:
+
+ my_timer_cycles ulonglong cycles
+ my_timer_nanoseconds ulonglong nanoseconds
+ my_timer_microseconds ulonglong "microseconds"
+ my_timer_milliseconds ulonglong milliseconds
+ my_timer_ticks ulonglong ticks
+ my_timer_init initialization / test
+
+ We'll call the first 5 functions (the ones that return
+ a ulonglong) "my_timer_xxx" functions.
+ Each my_timer_xxx function returns a 64-bit timing value
+ since an arbitrary 'epoch' start. Since the only purpose
+ is to determine elapsed times, wall-clock time-of-day
+ is not known and not relevant.
+
+ The my_timer_init function is necessary for initializing.
+ It returns information (underlying routine name,
+ frequency, resolution, overhead) about all my_timer_xxx
+ functions. A program should call my_timer_init once,
+ use the information to decide what my_timer_xxx function
+ to use, and subsequently call that function by function
+ pointer.
+
+ A typical use would be:
+ my_timer_init() ... once, at program start
+ ...
+ time1= my_timer_xxx() ... time before start
+ [code that's timed]
+ time2= my_timer_xxx() ... time after end
+ elapsed_time= (time2 - time1) - overhead
+*/
+
+#include "my_global.h"
+#include "my_rdtsc.h"
+
+#include <stdio.h>
+#if defined(_WIN32)
+#include "windows.h"
+#endif
+
+#if defined(TIME_WITH_SYS_TIME)
+#include <sys/time.h>
+#include <time.h> /* for clock_gettime */
+#endif
+
+#if defined(HAVE_SYS_TIMES_H) && defined(HAVE_TIMES)
+#include <sys/times.h> /* for times */
+#endif
+
+#if defined(__APPLE__) && defined(__MACH__)
+#include <mach/mach_time.h>
+#endif
+
+#if defined(__SUNPRO_CC) && defined(__sparcv9) && defined(_LP64) && !defined(__SunOS_5_7)
+extern "C" ulonglong my_timer_cycles_il_sparc64();
+#elif defined(__SUNPRO_CC) && defined(_ILP32) && !defined(__SunOS_5_7)
+extern "C" ulonglong my_timer_cycles_il_sparc32();
+#elif defined(__SUNPRO_CC) && defined(__i386) && defined(_ILP32)
+extern "C" ulonglong my_timer_cycles_il_i386();
+#elif defined(__SUNPRO_CC) && defined(__x86_64) && defined(_LP64)
+extern "C" ulonglong my_timer_cycles_il_x86_64();
+#elif defined(__SUNPRO_C) && defined(__sparcv9) && defined(_LP64) && !defined(__SunOS_5_7)
+ulonglong my_timer_cycles_il_sparc64();
+#elif defined(__SUNPRO_C) && defined(_ILP32) && !defined(__SunOS_5_7)
+ulonglong my_timer_cycles_il_sparc32();
+#elif defined(__SUNPRO_C) && defined(__i386) && defined(_ILP32)
+ulonglong my_timer_cycles_il_i386();
+#elif defined(__SUNPRO_C) && defined(__x86_64) && defined(_LP64)
+ulonglong my_timer_cycles_il_x86_64();
+#endif
+
+/*
+ For cycles, we depend on RDTSC for x86 platforms,
+ or on time buffer (which is not really a cycle count
+ but a separate counter with less than nanosecond
+ resolution) for most PowerPC platforms, or on
+ gethrtime which is okay for solaris.
+*/
+
+ulonglong my_timer_cycles(void)
+{
+#if defined(__GNUC__) && defined(__i386__)
+ /* This works much better if compiled with "gcc -O3". */
+ ulonglong result;
+ __asm__ __volatile__ ("rdtsc" : "=A" (result));
+ return result;
+#elif defined(__SUNPRO_C) && defined(__i386)
+ __asm("rdtsc");
+#elif defined(__GNUC__) && defined(__x86_64__)
+ ulonglong result;
+ __asm__ __volatile__ ("rdtsc\n\t" \
+ "shlq $32,%%rdx\n\t" \
+ "orq %%rdx,%%rax"
+ : "=a" (result) :: "%edx");
+ return result;
+#elif defined(_WIN32) && defined(_M_IX86)
+ __asm {rdtsc};
+#elif defined(_WIN64) && defined(_M_X64)
+ /* For 64-bit Windows: unsigned __int64 __rdtsc(); */
+ return __rdtsc();
+#elif defined(__GNUC__) && defined(__ia64__)
+ {
+ ulonglong result;
+ __asm __volatile__ ("mov %0=ar.itc" : "=r" (result));
+ return result;
+ }
+#elif defined(__GNUC__) && (defined(__powerpc__) || defined(__POWERPC__)) && (defined(__64BIT__) || defined(_ARCH_PPC64))
+ {
+ ulonglong result;
+ __asm __volatile__ ("mftb %0" : "=r" (result));
+ return result;
+ }
+#elif defined(__GNUC__) && (defined(__powerpc__) || defined(__POWERPC__)) && (!defined(__64BIT__) && !defined(_ARCH_PPC64))
+ {
+ /*
+ mftbu means "move from time-buffer-upper to result".
+ The loop is saying: x1=upper, x2=lower, x3=upper,
+ if x1!=x3 there was an overflow so repeat.
+ */
+ unsigned int x1, x2, x3;
+ ulonglong result;
+ for (;;)
+ {
+ __asm __volatile__ ( "mftbu %0" : "=r"(x1) );
+ __asm __volatile__ ( "mftb %0" : "=r"(x2) );
+ __asm __volatile__ ( "mftbu %0" : "=r"(x3) );
+ if (x1 == x3) break;
+ }
+ result = x1;
+ return ( result << 32 ) | x2;
+ }
+#elif (defined(__SUNPRO_CC) || defined(__SUNPRO_C)) && defined(__sparcv9) && defined(_LP64) && !defined(__SunOS_5_7)
+ return (my_timer_cycles_il_sparc64());
+#elif (defined(__SUNPRO_CC) || defined(__SUNPRO_C)) && defined(_ILP32) && !defined(__SunOS_5_7)
+ return (my_timer_cycles_il_sparc32());
+#elif (defined(__SUNPRO_CC) || defined(__SUNPRO_C)) && defined(__i386) && defined(_ILP32)
+ /* This is probably redundant for __SUNPRO_C. */
+ return (my_timer_cycles_il_i386());
+#elif (defined(__SUNPRO_CC) || defined(__SUNPRO_C)) && defined(__x86_64) && defined(_LP64)
+ return (my_timer_cycles_il_x86_64());
+#elif defined(__GNUC__) && defined(__sparcv9) && defined(_LP64)
+ {
+ ulonglong result;
+ __asm __volatile__ ("rd %%tick,%0" : "=r" (result));
+ return result;
+ }
+#elif defined(__GNUC__) && defined(__sparc__) && !defined(_LP64)
+ {
+ union {
+ ulonglong wholeresult;
+ struct {
+ ulong high;
+ ulong low;
+ } splitresult;
+ } result;
+ __asm __volatile__ ("rd %%tick,%1; srlx %1,32,%0" : "=r" (result.splitresult.high), "=r" (result.splitresult.low));
+ return result.wholeresult;
+ }
+#elif defined(__GNUC__) && defined(__aarch64__)
+ {
+ ulonglong result;
+ __asm __volatile__ ("mrs %[rt],cntvct_el0" : [rt] "=r" (result));
+ return result;
+ }
+#elif defined(HAVE_SYS_TIMES_H) && defined(HAVE_GETHRTIME)
+ /* gethrtime may appear as either cycle or nanosecond counter */
+ return (ulonglong) gethrtime();
+#else
+ return 0;
+#endif
+}
+
+/*
+ For nanoseconds, most platforms have nothing available that
+ (a) doesn't require bringing in a 40-kb librt.so library
+ (b) really has nanosecond resolution.
+*/
+
+ulonglong my_timer_nanoseconds(void)
+{
+#if defined(HAVE_SYS_TIMES_H) && defined(HAVE_GETHRTIME)
+ /* SunOS 5.10+, Solaris, HP-UX: hrtime_t gethrtime(void) */
+ return (ulonglong) gethrtime();
+#elif defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_REALTIME)
+ {
+ struct timespec tp;
+ clock_gettime(CLOCK_REALTIME, &tp);
+ return (ulonglong) tp.tv_sec * 1000000000 + (ulonglong) tp.tv_nsec;
+ }
+#elif defined(__APPLE__) && defined(__MACH__)
+ {
+ ulonglong tm;
+ static mach_timebase_info_data_t timebase_info= {0,0};
+ if (timebase_info.denom == 0)
+ (void) mach_timebase_info(&timebase_info);
+ tm= mach_absolute_time();
+ return (tm * timebase_info.numer) / timebase_info.denom;
+ }
+#else
+ return 0;
+#endif
+}
+
+/*
+ For microseconds, gettimeofday() is available on
+ almost all platforms. On Windows we use
+ QueryPerformanceCounter which will usually tick over
+ 3.5 million times per second, and we don't throw
+ away the extra precision. (On Windows Server 2003
+ the frequency is same as the cycle frequency.)
+*/
+
+ulonglong my_timer_microseconds(void)
+{
+#if defined(HAVE_GETTIMEOFDAY)
+ {
+ static ulonglong last_value= 0;
+ struct timeval tv;
+ if (gettimeofday(&tv, NULL) == 0)
+ last_value= (ulonglong) tv.tv_sec * 1000000 + (ulonglong) tv.tv_usec;
+ else
+ {
+ /*
+ There are reports that gettimeofday(2) can have intermittent failures
+ on some platform, see for example Bug#36819.
+ We are not trying again or looping, just returning the best value possible
+ under the circumstances ...
+ */
+ last_value++;
+ }
+ return last_value;
+ }
+#elif defined(_WIN32)
+ {
+ /* QueryPerformanceCounter usually works with about 1/3 microsecond. */
+ LARGE_INTEGER t_cnt;
+
+ QueryPerformanceCounter(&t_cnt);
+ return (ulonglong) t_cnt.QuadPart;
+ }
+#else
+ return 0;
+#endif
+}
+
+/*
+ For milliseconds, gettimeofday() is available on
+ almost all platforms. On Windows we use
+ GetSystemTimeAsFileTime.
+*/
+
+ulonglong my_timer_milliseconds(void)
+{
+#if defined(HAVE_GETTIMEOFDAY)
+ {
+ static ulonglong last_ms_value= 0;
+ struct timeval tv;
+ if (gettimeofday(&tv, NULL) == 0)
+ last_ms_value= (ulonglong) tv.tv_sec * 1000 +
+ (ulonglong) tv.tv_usec / 1000;
+ else
+ {
+ /*
+ There are reports that gettimeofday(2) can have intermittent failures
+ on some platform, see for example Bug#36819.
+ We are not trying again or looping, just returning the best value possible
+ under the circumstances ...
+ */
+ last_ms_value++;
+ }
+ return last_ms_value;
+ }
+#elif defined(_WIN32)
+ FILETIME ft;
+ GetSystemTimeAsFileTime( &ft );
+ return ((ulonglong)ft.dwLowDateTime +
+ (((ulonglong)ft.dwHighDateTime) << 32))/10000;
+#else
+ return 0;
+#endif
+}
+
+/*
+ For ticks, which we handle with times(), the frequency
+ is usually 100/second and the overhead is surprisingly
+ bad, sometimes even worse than gettimeofday's overhead.
+*/
+
+ulonglong my_timer_ticks(void)
+{
+#if defined(HAVE_SYS_TIMES_H) && defined(HAVE_TIMES)
+ {
+ struct tms times_buf;
+ return (ulonglong) times(&times_buf);
+ }
+#elif defined(_WIN32)
+ return (ulonglong) GetTickCount();
+#else
+ return 0;
+#endif
+}
+
+/*
+ The my_timer_init() function and its sub-functions
+ have several loops which call timers. If there's
+ something wrong with a timer -- which has never
+ happened in tests -- we want the loop to end after
+ an arbitrary number of iterations, and my_timer_info
+ will show a discouraging result. The arbitrary
+ number is 1,000,000.
+*/
+#define MY_TIMER_ITERATIONS 1000000
+
+/*
+ Calculate overhead. Called from my_timer_init().
+ Usually best_timer_overhead = cycles.overhead or
+ nanoseconds.overhead, so returned amount is in
+ cycles or nanoseconds. We repeat the calculation
+ ten times, so that we can disregard effects of
+ caching or interrupts. Result is quite consistent
+ for cycles, at least. But remember it's a minimum.
+*/
+
+static void my_timer_init_overhead(ulonglong *overhead,
+ ulonglong (*cycle_timer)(void),
+ ulonglong (*this_timer)(void),
+ ulonglong best_timer_overhead)
+{
+ ulonglong time1, time2;
+ int i;
+
+ /* *overhead, least of 20 calculations - cycles.overhead */
+ for (i= 0, *overhead= 1000000000; i < 20; ++i)
+ {
+ time1= cycle_timer();
+ this_timer(); /* rather than 'time_tmp= timer();' */
+ time2= cycle_timer() - time1;
+ if (*overhead > time2)
+ *overhead= time2;
+ }
+ *overhead-= best_timer_overhead;
+}
+
+/*
+ Calculate Resolution. Called from my_timer_init().
+ If a timer goes up by jumps, e.g. 1050, 1075, 1100, ...
+ then the best resolution is the minimum jump, e.g. 25.
+ If it's always divisible by 1000 then it's just a
+ result of multiplication of a lower-precision timer
+ result, e.g. nanoseconds are often microseconds * 1000.
+ If the minimum jump is less than an arbitrary passed
+ figure (a guess based on maximum overhead * 2), ignore.
+ Usually we end up with nanoseconds = 1 because it's too
+ hard to detect anything <= 100 nanoseconds.
+ Often GetTickCount() has resolution = 15.
+ We don't check with ticks because they take too long.
+*/
+static ulonglong my_timer_init_resolution(ulonglong (*this_timer)(void),
+ ulonglong overhead_times_2)
+{
+ ulonglong time1, time2;
+ ulonglong best_jump;
+ int i, jumps, divisible_by_1000, divisible_by_1000000;
+
+ divisible_by_1000= divisible_by_1000000= 0;
+ best_jump= 1000000;
+ for (i= jumps= 0; jumps < 3 && i < MY_TIMER_ITERATIONS * 10; ++i)
+ {
+ time1= this_timer();
+ time2= this_timer();
+ time2-= time1;
+ if (time2)
+ {
+ ++jumps;
+ if (!(time2 % 1000))
+ {
+ ++divisible_by_1000;
+ if (!(time2 % 1000000))
+ ++divisible_by_1000000;
+ }
+ if (best_jump > time2)
+ best_jump= time2;
+ /* For milliseconds, one jump is enough. */
+ if (overhead_times_2 == 0)
+ break;
+ }
+ }
+ if (jumps == 3)
+ {
+ if (jumps == divisible_by_1000000)
+ return 1000000;
+ if (jumps == divisible_by_1000)
+ return 1000;
+ }
+ if (best_jump > overhead_times_2)
+ return best_jump;
+ return 1;
+}
+
+/*
+ Calculate cycle frequency by seeing how many cycles pass
+ in a 200-microsecond period. I tried with 10-microsecond
+ periods originally, and the result was often very wrong.
+*/
+
+static ulonglong my_timer_init_frequency(MY_TIMER_INFO *mti)
+{
+ int i;
+ ulonglong time1, time2, time3, time4;
+ time1= my_timer_cycles();
+ time2= my_timer_microseconds();
+ time3= time2; /* Avoids a Microsoft/IBM compiler warning */
+ for (i= 0; i < MY_TIMER_ITERATIONS; ++i)
+ {
+ time3= my_timer_microseconds();
+ if (time3 - time2 > 200) break;
+ }
+ time4= my_timer_cycles() - mti->cycles.overhead;
+ time4-= mti->microseconds.overhead;
+ return (mti->microseconds.frequency * (time4 - time1)) / (time3 - time2);
+}
+
+/*
+ Call my_timer_init before the first call to my_timer_xxx().
+ If something must be initialized, it happens here.
+ Set: what routine is being used e.g. "asm_x86"
+ Set: function, overhead, actual frequency, resolution.
+*/
+
+void my_timer_init(MY_TIMER_INFO *mti)
+{
+ ulonglong (*best_timer)(void);
+ ulonglong best_timer_overhead;
+ ulonglong time1, time2;
+ int i;
+
+ /* cycles */
+ mti->cycles.frequency= 1000000000;
+#if defined(__GNUC__) && defined(__i386__)
+ mti->cycles.routine= MY_TIMER_ROUTINE_ASM_X86;
+#elif defined(__SUNPRO_C) && defined(__i386)
+ mti->cycles.routine= MY_TIMER_ROUTINE_ASM_X86;
+#elif defined(__GNUC__) && defined(__x86_64__)
+ mti->cycles.routine= MY_TIMER_ROUTINE_ASM_X86_64;
+#elif defined(_WIN32) && defined(_M_IX86)
+ mti->cycles.routine= MY_TIMER_ROUTINE_ASM_X86_WIN;
+#elif defined(_WIN64) && defined(_M_X64)
+ mti->cycles.routine= MY_TIMER_ROUTINE_RDTSC;
+#elif defined(__GNUC__) && defined(__ia64__)
+ mti->cycles.routine= MY_TIMER_ROUTINE_ASM_IA64;
+#elif defined(__GNUC__) && (defined(__powerpc__) || defined(__POWERPC__)) && (defined(__64BIT__) || defined(_ARCH_PPC64))
+ mti->cycles.routine= MY_TIMER_ROUTINE_ASM_PPC64;
+#elif defined(__GNUC__) && (defined(__powerpc__) || defined(__POWERPC__)) && (!defined(__64BIT__) && !defined(_ARCH_PPC64))
+ mti->cycles.routine= MY_TIMER_ROUTINE_ASM_PPC;
+#elif (defined(__SUNPRO_CC) || defined(__SUNPRO_C)) && defined(__sparcv9) && defined(_LP64) && !defined(__SunOS_5_7)
+ mti->cycles.routine= MY_TIMER_ROUTINE_ASM_SUNPRO_SPARC64;
+#elif (defined(__SUNPRO_CC) || defined(__SUNPRO_C)) && defined(_ILP32) && !defined(__SunOS_5_7)
+ mti->cycles.routine= MY_TIMER_ROUTINE_ASM_SUNPRO_SPARC32;
+#elif (defined(__SUNPRO_CC) || defined(__SUNPRO_C)) && defined(__i386) && defined(_ILP32)
+ mti->cycles.routine= MY_TIMER_ROUTINE_ASM_SUNPRO_I386;
+#elif (defined(__SUNPRO_CC) || defined(__SUNPRO_C)) && defined(__x86_64) && defined(_LP64)
+ mti->cycles.routine= MY_TIMER_ROUTINE_ASM_SUNPRO_X86_64;
+#elif defined(__GNUC__) && defined(__sparcv9) && defined(_LP64)
+ mti->cycles.routine= MY_TIMER_ROUTINE_ASM_GCC_SPARC64;
+#elif defined(__GNUC__) && defined(__sparc__) && !defined(_LP64)
+ mti->cycles.routine= MY_TIMER_ROUTINE_ASM_GCC_SPARC32;
+#elif defined(__GNUC__) && defined(__aarch64__)
+ mti->cycles.routine= MY_TIMER_ROUTINE_ASM_AARCH64;
+#elif defined(HAVE_SYS_TIMES_H) && defined(HAVE_GETHRTIME)
+ mti->cycles.routine= MY_TIMER_ROUTINE_GETHRTIME;
+#else
+ mti->cycles.routine= 0;
+#endif
+
+ if (!mti->cycles.routine || !my_timer_cycles())
+ {
+ mti->cycles.routine= 0;
+ mti->cycles.resolution= 0;
+ mti->cycles.frequency= 0;
+ mti->cycles.overhead= 0;
+ }
+
+ /* nanoseconds */
+ mti->nanoseconds.frequency= 1000000000; /* initial assumption */
+#if defined(HAVE_SYS_TIMES_H) && defined(HAVE_GETHRTIME)
+ mti->nanoseconds.routine= MY_TIMER_ROUTINE_GETHRTIME;
+#elif defined(HAVE_CLOCK_GETTIME)
+ mti->nanoseconds.routine= MY_TIMER_ROUTINE_CLOCK_GETTIME;
+#elif defined(__APPLE__) && defined(__MACH__)
+ mti->nanoseconds.routine= MY_TIMER_ROUTINE_MACH_ABSOLUTE_TIME;
+#else
+ mti->nanoseconds.routine= 0;
+#endif
+ if (!mti->nanoseconds.routine || !my_timer_nanoseconds())
+ {
+ mti->nanoseconds.routine= 0;
+ mti->nanoseconds.resolution= 0;
+ mti->nanoseconds.frequency= 0;
+ mti->nanoseconds.overhead= 0;
+ }
+
+ /* microseconds */
+ mti->microseconds.frequency= 1000000; /* initial assumption */
+#if defined(HAVE_GETTIMEOFDAY)
+ mti->microseconds.routine= MY_TIMER_ROUTINE_GETTIMEOFDAY;
+#elif defined(_WIN32)
+ {
+ LARGE_INTEGER li;
+ /* Windows: typical frequency = 3579545, actually 1/3 microsecond. */
+ if (!QueryPerformanceFrequency(&li))
+ mti->microseconds.routine= 0;
+ else
+ {
+ mti->microseconds.frequency= li.QuadPart;
+ mti->microseconds.routine= MY_TIMER_ROUTINE_QUERYPERFORMANCECOUNTER;
+ }
+ }
+#else
+ mti->microseconds.routine= 0;
+#endif
+ if (!mti->microseconds.routine || !my_timer_microseconds())
+ {
+ mti->microseconds.routine= 0;
+ mti->microseconds.resolution= 0;
+ mti->microseconds.frequency= 0;
+ mti->microseconds.overhead= 0;
+ }
+
+ /* milliseconds */
+ mti->milliseconds.frequency= 1000; /* initial assumption */
+#if defined(HAVE_GETTIMEOFDAY)
+ mti->milliseconds.routine= MY_TIMER_ROUTINE_GETTIMEOFDAY;
+#elif defined(_WIN32)
+ mti->milliseconds.routine= MY_TIMER_ROUTINE_GETSYSTEMTIMEASFILETIME;
+#else
+ mti->milliseconds.routine= 0;
+#endif
+ if (!mti->milliseconds.routine || !my_timer_milliseconds())
+ {
+ mti->milliseconds.routine= 0;
+ mti->milliseconds.resolution= 0;
+ mti->milliseconds.frequency= 0;
+ mti->milliseconds.overhead= 0;
+ }
+
+ /* ticks */
+ mti->ticks.frequency= 100; /* permanent assumption */
+#if defined(HAVE_SYS_TIMES_H) && defined(HAVE_TIMES)
+ mti->ticks.routine= MY_TIMER_ROUTINE_TIMES;
+#elif defined(_WIN32)
+ mti->ticks.routine= MY_TIMER_ROUTINE_GETTICKCOUNT;
+#else
+ mti->ticks.routine= 0;
+#endif
+ if (!mti->ticks.routine || !my_timer_ticks())
+ {
+ mti->ticks.routine= 0;
+ mti->ticks.resolution= 0;
+ mti->ticks.frequency= 0;
+ mti->ticks.overhead= 0;
+ }
+
+ /*
+ Calculate overhead in terms of the timer that
+ gives the best resolution: cycles or nanoseconds.
+ I doubt it ever will be as bad as microseconds.
+ */
+ if (mti->cycles.routine)
+ best_timer= &my_timer_cycles;
+ else
+ {
+ if (mti->nanoseconds.routine)
+ {
+ best_timer= &my_timer_nanoseconds;
+ }
+ else
+ best_timer= &my_timer_microseconds;
+ }
+
+ /* best_timer_overhead = least of 20 calculations */
+ for (i= 0, best_timer_overhead= 1000000000; i < 20; ++i)
+ {
+ time1= best_timer();
+ time2= best_timer() - time1;
+ if (best_timer_overhead > time2)
+ best_timer_overhead= time2;
+ }
+ if (mti->cycles.routine)
+ my_timer_init_overhead(&mti->cycles.overhead,
+ best_timer,
+ &my_timer_cycles,
+ best_timer_overhead);
+ if (mti->nanoseconds.routine)
+ my_timer_init_overhead(&mti->nanoseconds.overhead,
+ best_timer,
+ &my_timer_nanoseconds,
+ best_timer_overhead);
+ if (mti->microseconds.routine)
+ my_timer_init_overhead(&mti->microseconds.overhead,
+ best_timer,
+ &my_timer_microseconds,
+ best_timer_overhead);
+ if (mti->milliseconds.routine)
+ my_timer_init_overhead(&mti->milliseconds.overhead,
+ best_timer,
+ &my_timer_milliseconds,
+ best_timer_overhead);
+ if (mti->ticks.routine)
+ my_timer_init_overhead(&mti->ticks.overhead,
+ best_timer,
+ &my_timer_ticks,
+ best_timer_overhead);
+
+/*
+ Calculate resolution for nanoseconds or microseconds
+ or milliseconds, by seeing if it's always divisible
+ by 1000, and by noticing how much jumping occurs.
+ For ticks, just assume the resolution is 1.
+*/
+ if (mti->cycles.routine)
+ mti->cycles.resolution= 1;
+ if (mti->nanoseconds.routine)
+ mti->nanoseconds.resolution=
+ my_timer_init_resolution(&my_timer_nanoseconds, 20000);
+ if (mti->microseconds.routine)
+ mti->microseconds.resolution=
+ my_timer_init_resolution(&my_timer_microseconds, 20);
+ if (mti->milliseconds.routine)
+ mti->milliseconds.resolution=
+ my_timer_init_resolution(&my_timer_milliseconds, 0);
+ if (mti->ticks.routine)
+ mti->ticks.resolution= 1;
+
+/*
+ Calculate cycles frequency,
+ if we have both a cycles routine and a microseconds routine.
+ In tests, this usually results in a figure within 2% of
+ what "cat /proc/cpuinfo" says.
+ If the microseconds routine is QueryPerformanceCounter
+ (i.e. it's Windows), and the microseconds frequency is >
+ 500,000,000 (i.e. it's Windows Server so it uses RDTSC)
+ and the microseconds resolution is > 100 (i.e. dreadful),
+ then calculate cycles frequency = microseconds frequency.
+*/
+ if (mti->cycles.routine
+ && mti->microseconds.routine)
+ {
+ if (mti->microseconds.routine ==
+ MY_TIMER_ROUTINE_QUERYPERFORMANCECOUNTER
+ && mti->microseconds.frequency > 500000000
+ && mti->microseconds.resolution > 100)
+ mti->cycles.frequency= mti->microseconds.frequency;
+ else
+ {
+ ulonglong time1, time2;
+ time1= my_timer_init_frequency(mti);
+ /* Repeat once in case there was an interruption. */
+ time2= my_timer_init_frequency(mti);
+ if (time1 < time2) mti->cycles.frequency= time1;
+ else mti->cycles.frequency= time2;
+ }
+ }
+
+/*
+ Calculate milliseconds frequency =
+ (cycles-frequency/#-of-cycles) * #-of-milliseconds,
+ if we have both a milliseconds routine and a cycles
+ routine.
+ This will be inaccurate if milliseconds resolution > 1.
+ This is probably only useful when testing new platforms.
+*/
+ if (mti->milliseconds.routine
+ && mti->milliseconds.resolution < 1000
+ && mti->microseconds.routine
+ && mti->cycles.routine)
+ {
+ int i;
+ ulonglong time1, time2, time3, time4;
+ time1= my_timer_cycles();
+ time2= my_timer_milliseconds();
+ time3= time2; /* Avoids a Microsoft/IBM compiler warning */
+ for (i= 0; i < MY_TIMER_ITERATIONS * 1000; ++i)
+ {
+ time3= my_timer_milliseconds();
+ if (time3 - time2 > 10) break;
+ }
+ time4= my_timer_cycles();
+ mti->milliseconds.frequency=
+ (mti->cycles.frequency * (time3 - time2)) / (time4 - time1);
+ }
+
+/*
+ Calculate ticks.frequency =
+ (cycles-frequency/#-of-cycles * #-of-ticks,
+ if we have both a ticks routine and a cycles
+ routine,
+ This is probably only useful when testing new platforms.
+*/
+ if (mti->ticks.routine
+ && mti->microseconds.routine
+ && mti->cycles.routine)
+ {
+ int i;
+ ulonglong time1, time2, time3, time4;
+ time1= my_timer_cycles();
+ time2= my_timer_ticks();
+ time3= time2; /* Avoids a Microsoft/IBM compiler warning */
+ for (i= 0; i < MY_TIMER_ITERATIONS * 1000; ++i)
+ {
+ time3= my_timer_ticks();
+ if (time3 - time2 > 10) break;
+ }
+ time4= my_timer_cycles();
+ mti->ticks.frequency=
+ (mti->cycles.frequency * (time3 - time2)) / (time4 - time1);
+ }
+}
+
+/*
+ Additional Comments
+ -------------------
+
+ This is for timing, i.e. finding out how long a piece of code
+ takes. If you want time of day matching a wall clock, the
+ my_timer_xxx functions won't help you.
+
+ The best timer is the one with highest frequency, lowest
+ overhead, and resolution=1. The my_timer_info() routine will tell
+ you at runtime which timer that is. Usually it will be
+ my_timer_cycles() but be aware that, although it's best,
+ it has possible flaws and dangers. Depending on platform:
+ - The frequency might change. We don't test for this. It
+ happens on laptops for power saving, and on blade servers
+ for avoiding overheating.
+ - The overhead that my_timer_init() returns is the minimum.
+ In fact it could be slightly greater because of caching or
+ because you call the routine by address, as recommended.
+ It could be hugely greater if there's an interrupt.
+ - The x86 cycle counter, RDTSC doesn't "serialize". That is,
+ if there is out-of-order execution, rdtsc might be processed
+ after an instruction that logically follows it.
+ (We could force serialization, but that would be slower.)
+ - It is possible to set a flag which renders RDTSC
+ inoperative. Somebody responsible for the kernel
+ of the operating system would have to make this
+ decision. For the platforms we've tested with, there's
+ no such problem.
+ - With a multi-processor arrangement, it's possible
+ to get the cycle count from one processor in
+ thread X, and the cycle count from another processor
+ in thread Y. They may not always be in synch.
+ - You can't depend on a cycle counter being available for
+ all platforms. On Alphas, the
+ cycle counter is only 32-bit, so it would overflow quickly,
+ so we don't bother with it. On platforms that we haven't
+ tested, there might be some if/endif combination that we
+ didn't expect, or some assembler routine that we didn't
+ supply.
+
+ The recommended way to use the timer routines is:
+ 1. Somewhere near the beginning of the program, call
+ my_timer_init(). This should only be necessary once,
+ although you can call it again if you think that the
+ frequency has changed.
+ 2. Determine the best timer based on frequency, resolution,
+ overhead -- all things that my_timer_init() returns.
+ Preserve the address of the timer and the my_timer_into
+ results in an easily-accessible place.
+ 3. Instrument the code section that you're monitoring, thus:
+ time1= my_timer_xxx();
+ Instrumented code;
+ time2= my_timer_xxx();
+ elapsed_time= (time2 - time1) - overhead;
+ If the timer is always on, then overhead is always there,
+ so don't subtract it.
+ 4. Save the elapsed time, or add it to a totaller.
+ 5. When all timing processes are complete, transfer the
+ saved / totalled elapsed time to permanent storage.
+ Optionally you can convert cycles to microseconds at
+ this point. (Don't do so every time you calculate
+ elapsed_time! That would waste time and lose precision!)
+ For converting cycles to microseconds, use the frequency
+ that my_timer_init() returns. You'll also need to convert
+ if the my_timer_microseconds() function is the Windows
+ function QueryPerformanceCounter(), since that's sometimes
+ a counter with precision slightly better than microseconds.
+
+ Since we recommend calls by function pointer, we supply
+ no inline functions.
+
+ Some comments on the many candidate routines for timing ...
+
+ clock() -- We don't use because it would overflow frequently.
+
+ clock_gettime() -- In tests, clock_gettime often had
+ resolution = 1000.
+
+ gettimeofday() -- available on most platforms, though not
+ on Windows. There is a hardware timer (sometimes a Programmable
+ Interrupt Timer or "PIT") (sometimes a "HPET") used for
+ interrupt generation. When it interrupts (a "tick" or "jiffy",
+ typically 1 centisecond) it sets xtime. For gettimeofday, a
+ Linux kernel routine usually gets xtime and then gets rdtsc
+ to get elapsed nanoseconds since the last tick. On Red Hat
+ Enterprise Linux 3, there was once a bug which caused the
+ resolution to be 1000, i.e. one centisecond. We never check
+ for time-zone change.
+
+ getnstimeofday() -- something to watch for in future Linux
+
+ do_gettimeofday() -- exists on Linux but not for "userland"
+
+ get_cycles() -- a multi-platform function, worth watching
+ in future Linux versions. But we found platform-specific
+ functions which were better documented in operating-system
+ manuals. And get_cycles() can fail or return a useless
+ 32-bit number. It might be available on some platforms,
+ such as arm, which we didn't test. Using
+ "include <linux/timex.h>" or "include <asm/timex.h>"
+ can lead to autoconf or compile errors, depending on system.
+
+ rdtsc, __rdtsc, rdtscll: available for x86 with Linux BSD,
+ Solaris, Windows. See "possible flaws and dangers" comments.
+
+ times(): what we use for ticks. Should just read the last
+ (xtime) tick count, therefore should be fast, but usually
+ isn't.
+
+ GetTickCount(): we use this for my_timer_ticks() on
+ Windows. Actually it really is a tick counter, so resolution
+ >= 10 milliseconds unless you have a very old Windows version.
+ With Windows 95 or 98 or ME, timeGetTime() has better resolution than
+ GetTickCount (1ms rather than 55ms). But with Windows NT or XP or 2000,
+ they're both getting from a variable in the Process Environment Block
+ (PEB), and the variable is set by the programmable interrupt timer, so
+ the resolution is the same (usually 10-15 milliseconds). Also timeGetTime
+ is slower on old machines:
+ http://www.doumo.jp/aon-java/jsp/postgretips/tips.jsp?tips=74.
+ Also timeGetTime requires linking winmm.lib,
+ Therefore we use GetTickCount.
+ It will overflow every 49 days because the return is 32-bit.
+ There is also a GetTickCount64 but it requires Vista or Windows Server 2008.
+ (As for GetSystemTimeAsFileTime, its precision is spurious, it
+ just reads the tick variable like the other functions do.
+ However, we don't expect it to overflow every 49 days, so we
+ will prefer it for my_timer_milliseconds().)
+
+ QueryPerformanceCounter() we use this for my_timer_microseconds()
+ on Windows. 1-PIT-tick (often 1/3-microsecond). Usually reads
+ the PIT so it's slow. On some Windows variants, uses RDTSC.
+
+ GetLocalTime() this is available on Windows but we don't use it.
+
+ getclock(): documented for Alpha, but not found during tests.
+
+ mach_absolute_time() and UpTime() are recommended for Apple.
+ Inititally they weren't tried, because asm_ppc seems to do the job.
+ But now we use mach_absolute_time for nanoseconds.
+
+ Any clock-based timer can be affected by NPT (ntpd program),
+ which means:
+ - full-second correction can occur for leap second
+ - tiny corrections can occcur approimately every 11 minutes
+ (but I think they only affect the RTC which isn't the PIT).
+
+ We define "precision" as "frequency" and "high precision" is
+ "frequency better than 1 microsecond". We define "resolution"
+ as a synonym for "granularity". We define "accuracy" as
+ "closeness to the truth" as established by some authoritative
+ clock, but we can't measure accuracy.
+
+ Do not expect any of our timers to be monotonic; we
+ won't guarantee that they return constantly-increasing
+ unique numbers.
+
+ We tested with AIX, Solaris (x86 + Sparc), Linux (x86 +
+ Itanium), Windows, 64-bit Windows, QNX, FreeBSD, HPUX,
+ Irix, Mac. We didn't test with SCO.
+
+*/
+
diff --git a/mysql/mysys/my_read.c b/mysql/mysys/my_read.c
new file mode 100644
index 0000000..c80ad1c
--- /dev/null
+++ b/mysql/mysys/my_read.c
@@ -0,0 +1,107 @@
+/* Copyright (c) 2000, 2015, Oracle and/or its affiliates. All rights reserved.
+
+ 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; version 2 of the License.
+
+ 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include "mysys_priv.h"
+#include "my_sys.h"
+#include "mysys_err.h"
+#include <my_base.h>
+#include <errno.h>
+#include "my_thread_local.h"
+
+/*
+ Read a chunk of bytes from a file with retry's if needed
+
+ The parameters are:
+ File descriptor
+ Buffer to hold at least Count bytes
+ Bytes to read
+ Flags on what to do on error
+
+ Return:
+ -1 on error
+ 0 if flag has bits MY_NABP or MY_FNABP set
+ N number of bytes read.
+*/
+
+size_t my_read(File Filedes, uchar *Buffer, size_t Count, myf MyFlags)
+{
+ size_t readbytes, save_count;
+ DBUG_ENTER("my_read");
+ DBUG_PRINT("my",("fd: %d Buffer: %p Count: %lu MyFlags: %d",
+ Filedes, Buffer, (ulong) Count, MyFlags));
+ save_count= Count;
+
+ for (;;)
+ {
+ errno= 0; /* Linux, Windows don't reset this on EOF/success */
+#ifdef _WIN32
+ readbytes= my_win_read(Filedes, Buffer, Count);
+#else
+ readbytes= read(Filedes, Buffer, Count);
+#endif
+ DBUG_EXECUTE_IF ("simulate_file_read_error",
+ {
+ errno= ENOSPC;
+ readbytes= (size_t) -1;
+ DBUG_SET("-d,simulate_file_read_error");
+ DBUG_SET("-d,simulate_my_b_fill_error");
+ });
+
+ if (readbytes != Count)
+ {
+ set_my_errno(errno);
+ if (errno == 0 || (readbytes != (size_t) -1 &&
+ (MyFlags & (MY_NABP | MY_FNABP))))
+ set_my_errno(HA_ERR_FILE_TOO_SHORT);
+ DBUG_PRINT("warning",("Read only %d bytes off %lu from %d, errno: %d",
+ (int) readbytes, (ulong) Count, Filedes,
+ my_errno()));
+
+ if ((readbytes == 0 || (int) readbytes == -1) && errno == EINTR)
+ {
+ DBUG_PRINT("debug", ("my_read() was interrupted and returned %ld",
+ (long) readbytes));
+ continue; /* Interrupted */
+ }
+
+ if (MyFlags & (MY_WME | MY_FAE | MY_FNABP))
+ {
+ char errbuf[MYSYS_STRERROR_SIZE];
+ if (readbytes == (size_t) -1)
+ my_error(EE_READ, MYF(0), my_filename(Filedes),
+ my_errno(), my_strerror(errbuf, sizeof(errbuf), my_errno()));
+ else if (MyFlags & (MY_NABP | MY_FNABP))
+ my_error(EE_EOFERR, MYF(0), my_filename(Filedes),
+ my_errno(), my_strerror(errbuf, sizeof(errbuf), my_errno()));
+ }
+ if (readbytes == (size_t) -1 ||
+ ((MyFlags & (MY_FNABP | MY_NABP)) && !(MyFlags & MY_FULL_IO)))
+ DBUG_RETURN(MY_FILE_ERROR); /* Return with error */
+ if (readbytes != (size_t) -1 && (MyFlags & MY_FULL_IO))
+ {
+ Buffer+= readbytes;
+ Count-= readbytes;
+ continue;
+ }
+ }
+
+ if (MyFlags & (MY_NABP | MY_FNABP))
+ readbytes= 0; /* Ok on read */
+ else if (MyFlags & MY_FULL_IO)
+ readbytes= save_count;
+ break;
+ }
+ DBUG_RETURN(readbytes);
+} /* my_read */
diff --git a/mysql/mysys/my_redel.c b/mysql/mysys/my_redel.c
new file mode 100644
index 0000000..43e7e73
--- /dev/null
+++ b/mysql/mysys/my_redel.c
@@ -0,0 +1,132 @@
+/* Copyright (c) 2000, 2016, Oracle and/or its affiliates. All rights reserved.
+
+ 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; version 2 of the License.
+
+ 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include "mysys_priv.h"
+#include "my_sys.h"
+#include <my_dir.h>
+#include <m_string.h>
+#include "mysys_err.h"
+#include "my_thread_local.h"
+
+#ifndef _WIN32
+#include <utime.h>
+#else
+#include <sys/utime.h>
+#endif
+
+ /*
+ Rename with copy stat form old file
+ Copy stats from old file to new file, deletes orginal and
+ changes new file name to old file name
+
+ if MY_REDEL_MAKE_COPY is given, then the orginal file
+ is renamed to org_name-'current_time'.BAK
+
+ if MY_REDEL_NO_COPY_STAT is given, stats are not copied
+ from org_name to tmp_name.
+ */
+
+#define REDEL_EXT ".BAK"
+
+int my_redel(const char *org_name, const char *tmp_name, myf MyFlags)
+{
+ int error=1;
+ DBUG_ENTER("my_redel");
+ DBUG_PRINT("my",("org_name: '%s' tmp_name: '%s' MyFlags: %d",
+ org_name,tmp_name,MyFlags));
+
+ if (!(MyFlags & MY_REDEL_NO_COPY_STAT))
+ {
+ if (my_copystat(org_name,tmp_name,MyFlags) < 0)
+ goto end;
+ }
+ if (MyFlags & MY_REDEL_MAKE_BACKUP)
+ {
+ char name_buff[FN_REFLEN+20];
+ char ext[20];
+ ext[0]='-';
+ get_date(ext+1,2+4,(time_t) 0);
+ my_stpcpy(strend(ext),REDEL_EXT);
+ if (my_rename(org_name, fn_format(name_buff, org_name, "", ext, 2),
+ MyFlags))
+ goto end;
+ }
+ else if (my_delete_allow_opened(org_name, MyFlags))
+ goto end;
+ if (my_rename(tmp_name,org_name,MyFlags))
+ goto end;
+
+ error=0;
+end:
+ DBUG_RETURN(error);
+} /* my_redel */
+
+
+ /* Copy stat from one file to another */
+ /* Return -1 if can't get stat, 1 if wrong type of file */
+
+int my_copystat(const char *from, const char *to, int MyFlags)
+{
+ MY_STAT statbuf;
+
+ if (my_stat(from, &statbuf, MyFlags) == NULL)
+ return -1; /* Can't get stat on input file */
+
+ if ((statbuf.st_mode & S_IFMT) != S_IFREG)
+ return 1;
+
+ /* Copy modes */
+ if (chmod(to, statbuf.st_mode & 07777))
+ {
+ set_my_errno(errno);
+ if (MyFlags & (MY_FAE+MY_WME))
+ {
+ char errbuf[MYSYS_STRERROR_SIZE];
+ my_error(EE_CHANGE_PERMISSIONS, MYF(0), from,
+ errno, my_strerror(errbuf, sizeof(errbuf), errno));
+ }
+ return -1;
+ }
+
+#if !defined(_WIN32)
+ if (statbuf.st_nlink > 1 && MyFlags & MY_LINK_WARNING)
+ {
+ if (MyFlags & MY_LINK_WARNING)
+ my_error(EE_LINK_WARNING,MYF(0),from,statbuf.st_nlink);
+ }
+ /* Copy ownership */
+ if (chown(to, statbuf.st_uid, statbuf.st_gid))
+ {
+ set_my_errno(errno);
+ if (MyFlags & (MY_FAE+MY_WME))
+ {
+ char errbuf[MYSYS_STRERROR_SIZE];
+ my_error(EE_CHANGE_OWNERSHIP, MYF(0), from,
+ errno, my_strerror(errbuf, sizeof(errbuf), errno));
+ }
+ return -1;
+ }
+#endif /* !_WIN32 */
+
+ if (MyFlags & MY_COPYTIME)
+ {
+ struct utimbuf timep;
+ timep.actime = statbuf.st_atime;
+ timep.modtime = statbuf.st_mtime;
+ (void) utime((char*) to, &timep);/* Update last accessed and modified times */
+ }
+
+ return 0;
+} /* my_copystat */
diff --git a/mysql/mysys/my_rename.c b/mysql/mysys/my_rename.c
new file mode 100644
index 0000000..c8ad130
--- /dev/null
+++ b/mysql/mysys/my_rename.c
@@ -0,0 +1,59 @@
+/* Copyright (c) 2000, 2015, Oracle and/or its affiliates. All rights reserved.
+
+ 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; version 2 of the License.
+
+ 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include "mysys_priv.h"
+#include "my_sys.h"
+#include <my_dir.h>
+#include "mysys_err.h"
+#include "m_string.h"
+#include "my_thread_local.h"
+#undef my_rename
+
+ /* On unix rename deletes to file if it exists */
+
+int my_rename(const char *from, const char *to, myf MyFlags)
+{
+ int error = 0;
+ DBUG_ENTER("my_rename");
+ DBUG_PRINT("my",("from %s to %s MyFlags %d", from, to, MyFlags));
+
+#if defined(_WIN32)
+ if(!MoveFileEx(from, to, MOVEFILE_COPY_ALLOWED|
+ MOVEFILE_REPLACE_EXISTING))
+ {
+ my_osmaperr(GetLastError());
+#else
+ if (rename(from,to))
+ {
+#endif
+ set_my_errno(errno);
+ error = -1;
+ if (MyFlags & (MY_FAE+MY_WME))
+ {
+ char errbuf[MYSYS_STRERROR_SIZE];
+ my_error(EE_LINK, MYF(0), from, to,
+ my_errno(), my_strerror(errbuf, sizeof(errbuf), my_errno()));
+ }
+ }
+ else if (MyFlags & MY_SYNC_DIR)
+ {
+ /* do only the needed amount of syncs: */
+ if (my_sync_dir_by_file(from, MyFlags) ||
+ (strcmp(from, to) &&
+ my_sync_dir_by_file(to, MyFlags)))
+ error= -1;
+ }
+ DBUG_RETURN(error);
+} /* my_rename */
diff --git a/mysql/mysys/my_seek.c b/mysql/mysys/my_seek.c
new file mode 100644
index 0000000..3a73e06
--- /dev/null
+++ b/mysql/mysys/my_seek.c
@@ -0,0 +1,111 @@
+/* Copyright (c) 2000, 2015, Oracle and/or its affiliates. All rights reserved.
+
+ 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; version 2 of the License.
+
+ 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include "mysys_priv.h"
+#include "my_sys.h"
+#include "mysys_err.h"
+#include "my_thread_local.h"
+
+/*
+ Seek to a position in a file.
+
+ ARGUMENTS
+ File fd The file descriptor
+ my_off_t pos The expected position (absolute or relative)
+ int whence A direction parameter and one of
+ {SEEK_SET, SEEK_CUR, SEEK_END}
+ myf MyFlags MY_THREADSAFE must be set in case my_seek may be mixed
+ with my_pread/my_pwrite calls and fd is shared among
+ threads.
+
+ DESCRIPTION
+ The my_seek function is a wrapper around the system call lseek and
+ repositions the offset of the file descriptor fd to the argument
+ offset according to the directive whence as follows:
+ SEEK_SET The offset is set to offset bytes.
+ SEEK_CUR The offset is set to its current location plus offset bytes
+ SEEK_END The offset is set to the size of the file plus offset bytes
+
+ RETURN VALUE
+ my_off_t newpos The new position in the file.
+ MY_FILEPOS_ERROR An error was encountered while performing
+ the seek. my_errno is set to indicate the
+ actual error.
+*/
+
+my_off_t my_seek(File fd, my_off_t pos, int whence, myf MyFlags)
+{
+ os_off_t newpos= -1;
+ DBUG_ENTER("my_seek");
+ DBUG_PRINT("my",("fd: %d Pos: %llu Whence: %d MyFlags: %d",
+ fd, (ulonglong) pos, whence, MyFlags));
+
+ /*
+ Make sure we are using a valid file descriptor!
+ */
+ DBUG_ASSERT(fd != -1);
+#if defined (_WIN32)
+ newpos= my_win_lseek(fd, pos, whence);
+#else
+ newpos= lseek(fd, pos, whence);
+#endif
+ if (newpos == (os_off_t) -1)
+ {
+ set_my_errno(errno);
+ if (MyFlags & MY_WME)
+ {
+ char errbuf[MYSYS_STRERROR_SIZE];
+ my_error(EE_CANT_SEEK, MYF(0), my_filename(fd),
+ my_errno(), my_strerror(errbuf, sizeof(errbuf), my_errno()));
+ }
+ DBUG_PRINT("error", ("lseek: %llu errno: %d", (ulonglong) newpos, errno));
+ DBUG_RETURN(MY_FILEPOS_ERROR);
+ }
+ if ((my_off_t) newpos != pos)
+ {
+ DBUG_PRINT("exit",("pos: %llu", (ulonglong) newpos));
+ }
+ DBUG_RETURN((my_off_t) newpos);
+} /* my_seek */
+
+
+ /* Tell current position of file */
+ /* ARGSUSED */
+
+my_off_t my_tell(File fd, myf MyFlags)
+{
+ os_off_t pos;
+ DBUG_ENTER("my_tell");
+ DBUG_PRINT("my",("fd: %d MyFlags: %d",fd, MyFlags));
+ DBUG_ASSERT(fd >= 0);
+#if defined (HAVE_TELL) && !defined (_WIN32)
+ pos= tell(fd);
+#else
+ pos= my_seek(fd, 0L, MY_SEEK_CUR,0);
+#endif
+ if (pos == (os_off_t) -1)
+ {
+ set_my_errno(errno);
+ if (MyFlags & MY_WME)
+ {
+ char errbuf[MYSYS_STRERROR_SIZE];
+ my_error(EE_CANT_SEEK, MYF(0), my_filename(fd),
+ my_errno(), my_strerror(errbuf, sizeof(errbuf), my_errno()));
+ }
+ DBUG_PRINT("error", ("tell: %llu errno: %d", (ulonglong) pos, my_errno()));
+ }
+ DBUG_PRINT("exit",("pos: %llu", (ulonglong) pos));
+ DBUG_RETURN((my_off_t) pos);
+} /* my_tell */
diff --git a/mysql/mysys/my_static.c b/mysql/mysys/my_static.c
new file mode 100644
index 0000000..60db888
--- /dev/null
+++ b/mysql/mysys/my_static.c
@@ -0,0 +1,143 @@
+/* Copyright (c) 2000, 2016, Oracle and/or its affiliates. All rights reserved.
+
+ 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; version 2 of the License.
+
+ 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+/*
+ Static variables for mysys library. All definied here for easy making of
+ a shared library
+*/
+
+#include "mysys_priv.h"
+#include "my_static.h"
+
+PSI_memory_key key_memory_charset_file;
+PSI_memory_key key_memory_charset_loader;
+PSI_memory_key key_memory_lf_node;
+PSI_memory_key key_memory_lf_dynarray;
+PSI_memory_key key_memory_lf_slist;
+PSI_memory_key key_memory_LIST;
+PSI_memory_key key_memory_IO_CACHE;
+PSI_memory_key key_memory_KEY_CACHE;
+PSI_memory_key key_memory_SAFE_HASH_ENTRY;
+PSI_memory_key key_memory_MY_BITMAP_bitmap;
+PSI_memory_key key_memory_my_compress_alloc;
+PSI_memory_key key_memory_pack_frm;
+PSI_memory_key key_memory_my_err_head;
+PSI_memory_key key_memory_my_file_info;
+PSI_memory_key key_memory_max_alloca;
+PSI_memory_key key_memory_MY_DIR;
+PSI_memory_key key_memory_MY_STAT;
+PSI_memory_key key_memory_MY_TMPDIR_full_list;
+PSI_memory_key key_memory_QUEUE;
+PSI_memory_key key_memory_DYNAMIC_STRING;
+PSI_memory_key key_memory_TREE;
+
+#ifdef _WIN32
+PSI_memory_key key_memory_win_SECURITY_ATTRIBUTES;
+PSI_memory_key key_memory_win_PACL;
+PSI_memory_key key_memory_win_IP_ADAPTER_ADDRESSES;
+#endif /* _WIN32 */
+
+ /* from my_init */
+char * home_dir=0;
+const char *my_progname=0;
+char curr_dir[FN_REFLEN]= {0},
+ home_dir_buff[FN_REFLEN]= {0};
+ulong my_stream_opened=0,my_file_opened=0, my_tmp_file_created=0;
+ulong my_file_total_opened= 0;
+int my_umask=0664, my_umask_dir=0777;
+
+struct st_my_file_info my_file_info_default[MY_NFILE];
+uint my_file_limit= MY_NFILE;
+struct st_my_file_info *my_file_info= my_file_info_default;
+
+ /* from mf_reccache.c */
+ulong my_default_record_cache_size=RECORD_CACHE_SIZE;
+
+ /* from soundex.c */
+ /* ABCDEFGHIJKLMNOPQRSTUVWXYZ */
+ /* :::::::::::::::::::::::::: */
+const char *soundex_map= "01230120022455012623010202";
+
+ /* from my_malloc */
+USED_MEM* my_once_root_block=0; /* pointer to first block */
+uint my_once_extra=ONCE_ALLOC_INIT; /* Memory to alloc / block */
+
+ /* from my_largepage.c */
+#ifdef HAVE_LINUX_LARGE_PAGES
+my_bool my_use_large_pages= 0;
+uint my_large_page_size= 0;
+#endif
+
+ /* from errors.c */
+void (*error_handler_hook)(uint error, const char *str, myf MyFlags)=
+ my_message_stderr;
+void (*fatal_error_handler_hook)(uint error, const char *str, myf MyFlags)=
+ my_message_stderr;
+void (*local_message_hook)(enum loglevel ll, const char *format, va_list args)=
+ my_message_local_stderr;
+
+static void enter_cond_dummy(void *a MY_ATTRIBUTE((unused)),
+ mysql_cond_t *b MY_ATTRIBUTE((unused)),
+ mysql_mutex_t *c MY_ATTRIBUTE((unused)),
+ const PSI_stage_info *d MY_ATTRIBUTE((unused)),
+ PSI_stage_info *e MY_ATTRIBUTE((unused)),
+ const char *f MY_ATTRIBUTE((unused)),
+ const char *g MY_ATTRIBUTE((unused)),
+ int h MY_ATTRIBUTE((unused)))
+{ };
+
+static void exit_cond_dummy(void *a MY_ATTRIBUTE((unused)),
+ const PSI_stage_info *b MY_ATTRIBUTE((unused)),
+ const char *c MY_ATTRIBUTE((unused)),
+ const char *d MY_ATTRIBUTE((unused)),
+ int e MY_ATTRIBUTE((unused)))
+{ };
+
+static int is_killed_dummy(const void *a MY_ATTRIBUTE((unused)))
+{
+ return 0;
+}
+
+/*
+ Initialize these hooks to dummy implementations. The real server
+ implementations will be set during server startup by
+ init_server_components().
+*/
+void (*enter_cond_hook)(void *, mysql_cond_t *, mysql_mutex_t *,
+ const PSI_stage_info *, PSI_stage_info *,
+ const char *, const char *, int)= enter_cond_dummy;
+
+void (*exit_cond_hook)(void *, const PSI_stage_info *,
+ const char *, const char *, int)= exit_cond_dummy;
+
+int (*is_killed_hook)(const void *)= is_killed_dummy;
+
+#if defined(ENABLED_DEBUG_SYNC)
+/**
+ Global pointer to be set if callback function is defined
+ (e.g. in mysqld). See sql/debug_sync.cc.
+*/
+void (*debug_sync_C_callback_ptr)(const char *, size_t);
+#endif /* defined(ENABLED_DEBUG_SYNC) */
+
+#ifdef _WIN32
+/* from my_getsystime.c */
+ulonglong query_performance_frequency, query_performance_offset;
+#endif
+
+ /* How to disable options */
+my_bool my_disable_locking=0;
+my_bool my_enable_symlinks= 1;
+
diff --git a/mysql/mysys/my_static.h b/mysql/mysys/my_static.h
new file mode 100644
index 0000000..a036613
--- /dev/null
+++ b/mysql/mysys/my_static.h
@@ -0,0 +1,41 @@
+#ifndef MYSYS_MY_STATIC_INCLUDED
+#define MYSYS_MY_STATIC_INCLUDED
+
+/* Copyright (c) 2000, 2015, Oracle and/or its affiliates. All rights reserved.
+
+ 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; version 2 of the License.
+
+ 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+/*
+ Static variables for mysys library. All definied here for easy making of
+ a shared library
+*/
+
+#include "my_global.h"
+#include "my_sys.h"
+
+C_MODE_START
+extern char curr_dir[FN_REFLEN], home_dir_buff[FN_REFLEN];
+
+extern const char *soundex_map;
+
+extern USED_MEM* my_once_root_block;
+extern uint my_once_extra;
+
+extern struct st_my_file_info my_file_info_default[MY_NFILE];
+
+extern ulonglong query_performance_frequency, query_performance_offset;
+
+C_MODE_END
+
+#endif /* MYSYS_MY_STATIC_INCLUDED */
diff --git a/mysql/mysys/my_symlink.c b/mysql/mysys/my_symlink.c
new file mode 100644
index 0000000..4777928
--- /dev/null
+++ b/mysql/mysys/my_symlink.c
@@ -0,0 +1,208 @@
+/* Copyright (c) 2001, 2017, Oracle and/or its affiliates. All rights reserved.
+
+ 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; version 2 of the License.
+
+ 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include "mysys_priv.h"
+#include "my_sys.h"
+#include "mysys_err.h"
+#include <m_string.h>
+#include <errno.h>
+#include "my_thread_local.h"
+#include "my_dir.h"
+#ifdef HAVE_REALPATH
+#include <sys/param.h>
+#include <sys/stat.h>
+#endif
+
+/*
+ Reads the content of a symbolic link
+ If the file is not a symbolic link, return the original file name in to.
+
+ RETURN
+ 0 If filename was a symlink, (to will be set to value of symlink)
+ 1 If filename was a normal file (to will be set to filename)
+ -1 on error.
+*/
+
+int my_readlink(char *to, const char *filename, myf MyFlags)
+{
+#ifndef HAVE_READLINK
+ my_stpcpy(to,filename);
+ return 1;
+#else
+ int result=0;
+ int length;
+ DBUG_ENTER("my_readlink");
+
+ if ((length=readlink(filename, to, FN_REFLEN-1)) < 0)
+ {
+ /* Don't give an error if this wasn't a symlink */
+ set_my_errno(errno);
+ if (my_errno() == EINVAL)
+ {
+ result= 1;
+ my_stpcpy(to,filename);
+ }
+ else
+ {
+ if (MyFlags & MY_WME)
+ {
+ char errbuf[MYSYS_STRERROR_SIZE];
+ my_error(EE_CANT_READLINK, MYF(0), filename,
+ errno, my_strerror(errbuf, sizeof(errbuf), errno));
+ }
+ result= -1;
+ }
+ }
+ else
+ to[length]=0;
+ DBUG_PRINT("exit" ,("result: %d", result));
+ DBUG_RETURN(result);
+#endif /* HAVE_READLINK */
+}
+
+
+/* Create a symbolic link */
+
+int my_symlink(const char *content, const char *linkname, myf MyFlags)
+{
+#ifndef HAVE_READLINK
+ return 0;
+#else
+ int result;
+ DBUG_ENTER("my_symlink");
+ DBUG_PRINT("enter",("content: %s linkname: %s", content, linkname));
+
+ result= 0;
+ if (symlink(content, linkname))
+ {
+ result= -1;
+ set_my_errno(errno);
+ if (MyFlags & MY_WME)
+ {
+ char errbuf[MYSYS_STRERROR_SIZE];
+ my_error(EE_CANT_SYMLINK, MYF(0), linkname, content,
+ errno, my_strerror(errbuf, sizeof(errbuf), errno));
+ }
+ }
+ else if ((MyFlags & MY_SYNC_DIR) && my_sync_dir_by_file(linkname, MyFlags))
+ result= -1;
+ DBUG_RETURN(result);
+#endif /* HAVE_READLINK */
+}
+
+#if defined(MAXPATHLEN)
+#define BUFF_LEN MAXPATHLEN
+#else
+#define BUFF_LEN FN_LEN
+#endif
+
+
+int my_is_symlink(const char *filename MY_ATTRIBUTE((unused)),
+ ST_FILE_ID *file_id)
+{
+#if defined (HAVE_LSTAT) && defined (S_ISLNK)
+ struct stat stat_buff;
+ int result= !lstat(filename, &stat_buff) && S_ISLNK(stat_buff.st_mode);
+ if (file_id && !result)
+ {
+ file_id->st_dev= stat_buff.st_dev;
+ file_id->st_ino= stat_buff.st_ino;
+ }
+ return result;
+#elif defined (_WIN32)
+ DWORD dwAttr = GetFileAttributes(filename);
+ return (dwAttr != INVALID_FILE_ATTRIBUTES) &&
+ (dwAttr & FILE_ATTRIBUTE_REPARSE_POINT);
+#else /* No symlinks */
+ return 0;
+#endif
+}
+
+/*
+ Resolve all symbolic links in path
+ 'to' may be equal to 'filename'
+*/
+
+int my_realpath(char *to, const char *filename, myf MyFlags)
+{
+#if defined(HAVE_REALPATH)
+ int result=0;
+ char buff[BUFF_LEN];
+ char *ptr;
+ DBUG_ENTER("my_realpath");
+
+ DBUG_PRINT("info",("executing realpath"));
+ if ((ptr=realpath(filename,buff)))
+ strmake(to,ptr,FN_REFLEN-1);
+ else
+ {
+ /*
+ Realpath didn't work; Use my_load_path() which is a poor substitute
+ original name but will at least be able to resolve paths that starts
+ with '.'.
+ */
+ DBUG_PRINT("error",("realpath failed with errno: %d", errno));
+ set_my_errno(errno);
+ if (MyFlags & MY_WME)
+ {
+ char errbuf[MYSYS_STRERROR_SIZE];
+ my_error(EE_REALPATH, MYF(0), filename,
+ my_errno(), my_strerror(errbuf, sizeof(errbuf), my_errno()));
+ }
+ my_load_path(to, filename, NullS);
+ result= -1;
+ }
+ DBUG_RETURN(result);
+#elif defined(_WIN32)
+ int ret= GetFullPathName(filename,FN_REFLEN, to, NULL);
+ if (ret == 0 || ret > FN_REFLEN)
+ {
+ set_my_errno((ret > FN_REFLEN) ? ENAMETOOLONG : GetLastError());
+ if (MyFlags & MY_WME)
+ {
+ char errbuf[MYSYS_STRERROR_SIZE];
+ my_error(EE_REALPATH, MYF(0), filename,
+ my_errno(), my_strerror(errbuf, sizeof(errbuf), my_errno()));
+ }
+ /*
+ GetFullPathName didn't work : use my_load_path() which is a poor
+ substitute original name but will at least be able to resolve
+ paths that starts with '.'.
+ */
+ my_load_path(to, filename, NullS);
+ return -1;
+ }
+#else
+ my_load_path(to, filename, NullS);
+#endif
+ return 0;
+}
+
+
+/**
+ Return non-zero if the file descriptor and a previously lstat-ed file
+ identified by file_id point to the same file
+*/
+int my_is_same_file(File file, const ST_FILE_ID *file_id)
+{
+ MY_STAT stat_buf;
+ if (my_fstat(file, &stat_buf, MYF(0)) == -1)
+ {
+ set_my_errno(errno);
+ return 0;
+ }
+ return (stat_buf.st_dev == file_id->st_dev)
+ && (stat_buf.st_ino == file_id->st_ino);
+}
diff --git a/mysql/mysys/my_symlink2.c b/mysql/mysys/my_symlink2.c
new file mode 100644
index 0000000..0540b47
--- /dev/null
+++ b/mysql/mysys/my_symlink2.c
@@ -0,0 +1,195 @@
+/* Copyright (c) 2000, 2015, Oracle and/or its affiliates. All rights reserved.
+
+ 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; version 2 of the License.
+
+ 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+/*
+ Advanced symlink handling.
+ This is used in MyISAM to let users symlinks tables to different disk.
+ The main idea with these functions is to automaticly create, delete and
+ rename files and symlinks like they would be one unit.
+*/
+
+#include "mysys_priv.h"
+#include "my_sys.h"
+#include "mysys_err.h"
+#include <m_string.h>
+#include "my_thread_local.h"
+
+File my_create_with_symlink(const char *linkname, const char *filename,
+ int createflags, int access_flags, myf MyFlags)
+{
+ File file;
+ int tmp_errno;
+ /* Test if we should create a link */
+ int create_link;
+ char abs_linkname[FN_REFLEN];
+ DBUG_ENTER("my_create_with_symlink");
+ DBUG_PRINT("enter", ("linkname: %s filename: %s",
+ linkname ? linkname : "(null)",
+ filename ? filename : "(null)"));
+
+ if (!my_enable_symlinks)
+ {
+ DBUG_PRINT("info", ("Symlinks disabled"));
+ /* Create only the file, not the link and file */
+ create_link= 0;
+ if (linkname)
+ filename= linkname;
+ }
+ else
+ {
+ if (linkname)
+ my_realpath(abs_linkname, linkname, MYF(0));
+ create_link= (linkname && strcmp(abs_linkname,filename));
+ }
+
+ if (!(MyFlags & MY_DELETE_OLD))
+ {
+ if (!access(filename,F_OK))
+ {
+ char errbuf[MYSYS_STRERROR_SIZE];
+ errno= EEXIST;
+ set_my_errno(EEXIST);
+ my_error(EE_CANTCREATEFILE, MYF(0), filename,
+ EEXIST, my_strerror(errbuf, sizeof(errbuf), EEXIST));
+ DBUG_RETURN(-1);
+ }
+ if (create_link && !access(linkname,F_OK))
+ {
+ char errbuf[MYSYS_STRERROR_SIZE];
+ errno= EEXIST;
+ set_my_errno(EEXIST);
+ my_error(EE_CANTCREATEFILE, MYF(0), linkname,
+ EEXIST, my_strerror(errbuf, sizeof(errbuf), EEXIST));
+ DBUG_RETURN(-1);
+ }
+ }
+
+ if ((file=my_create(filename, createflags, access_flags, MyFlags)) >= 0)
+ {
+ if (create_link)
+ {
+ /* Delete old link/file */
+ if (MyFlags & MY_DELETE_OLD)
+ my_delete(linkname, MYF(0));
+ /* Create link */
+ if (my_symlink(filename, linkname, MyFlags))
+ {
+ /* Fail, remove everything we have done */
+ tmp_errno=my_errno();
+ my_close(file,MYF(0));
+ my_delete(filename, MYF(0));
+ file= -1;
+ set_my_errno(tmp_errno);
+ }
+ }
+ }
+ DBUG_RETURN(file);
+}
+
+/*
+ If the file was a symlink, delete both symlink and the file which the
+ symlink pointed to.
+*/
+
+int my_delete_with_symlink(const char *name, myf MyFlags)
+{
+ char link_name[FN_REFLEN];
+ int was_symlink= (my_enable_symlinks &&
+ !my_readlink(link_name, name, MYF(0)));
+ int result;
+ DBUG_ENTER("my_delete_with_symlink");
+
+ if (!(result=my_delete(name, MyFlags)))
+ {
+ if (was_symlink)
+ result=my_delete(link_name, MyFlags);
+ }
+ DBUG_RETURN(result);
+}
+
+/*
+ If the file is a normal file, just rename it.
+ If the file is a symlink:
+ - Create a new file with the name 'to' that points at
+ symlink_dir/basename(to)
+ - Rename the symlinked file to symlink_dir/basename(to)
+ - Delete 'from'
+ If something goes wrong, restore everything.
+*/
+
+int my_rename_with_symlink(const char *from, const char *to, myf MyFlags)
+{
+#ifndef HAVE_READLINK
+ return my_rename(from, to, MyFlags);
+#else
+ char link_name[FN_REFLEN], tmp_name[FN_REFLEN];
+ int was_symlink= (my_enable_symlinks &&
+ !my_readlink(link_name, from, MYF(0)));
+ int result=0;
+ int name_is_different;
+ DBUG_ENTER("my_rename_with_symlink");
+
+ if (!was_symlink)
+ DBUG_RETURN(my_rename(from, to, MyFlags));
+
+ /* Change filename that symlink pointed to */
+ my_stpcpy(tmp_name, to);
+ fn_same(tmp_name,link_name,1); /* Copy dir */
+ name_is_different= strcmp(link_name, tmp_name);
+ if (name_is_different && !access(tmp_name, F_OK))
+ {
+ set_my_errno(EEXIST);
+ if (MyFlags & MY_WME)
+ {
+ char errbuf[MYSYS_STRERROR_SIZE];
+ my_error(EE_CANTCREATEFILE, MYF(0), tmp_name,
+ EEXIST, my_strerror(errbuf, sizeof(errbuf), EEXIST));
+ }
+ DBUG_RETURN(1);
+ }
+
+ /* Create new symlink */
+ if (my_symlink(tmp_name, to, MyFlags))
+ DBUG_RETURN(1);
+
+ /*
+ Rename symlinked file if the base name didn't change.
+ This can happen if you use this function where 'from' and 'to' has
+ the same basename and different directories.
+ */
+
+ if (name_is_different && my_rename(link_name, tmp_name, MyFlags))
+ {
+ int save_errno=my_errno();
+ my_delete(to, MyFlags); /* Remove created symlink */
+ set_my_errno(save_errno);
+ DBUG_RETURN(1);
+ }
+
+ /* Remove original symlink */
+ if (my_delete(from, MyFlags))
+ {
+ int save_errno=my_errno();
+ /* Remove created link */
+ my_delete(to, MyFlags);
+ /* Rename file back */
+ if (strcmp(link_name, tmp_name))
+ (void) my_rename(tmp_name, link_name, MyFlags);
+ set_my_errno(save_errno);
+ result= 1;
+ }
+ DBUG_RETURN(result);
+#endif /* HAVE_READLINK */
+}
diff --git a/mysql/mysys/my_sync.c b/mysql/mysys/my_sync.c
new file mode 100644
index 0000000..2ddfc7d
--- /dev/null
+++ b/mysql/mysys/my_sync.c
@@ -0,0 +1,195 @@
+/* Copyright (c) 2003, 2016, Oracle and/or its affiliates. All rights reserved.
+
+ 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; version 2 of the License.
+
+ 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include "mysys_priv.h"
+#include "my_sys.h"
+#include "mysys_err.h"
+#include <errno.h>
+#include "my_thread_local.h"
+
+static void (*before_sync_wait)(void)= 0;
+static void (*after_sync_wait)(void)= 0;
+
+void thr_set_sync_wait_callback(void (*before_wait)(void),
+ void (*after_wait)(void))
+{
+ before_sync_wait= before_wait;
+ after_sync_wait= after_wait;
+}
+
+/*
+ Sync data in file to disk
+
+ SYNOPSIS
+ my_sync()
+ fd File descritor to sync
+ my_flags Flags (now only MY_WME is supported)
+
+ NOTE
+ If file system supports its, only file data is synced, not inode data.
+
+ MY_IGNORE_BADFD is useful when fd is "volatile" - not protected by a
+ mutex. In this case by the time of fsync(), fd may be already closed by
+ another thread, or even reassigned to a different file. With this flag -
+ MY_IGNORE_BADFD - such a situation will not be considered an error.
+ (which is correct behaviour, if we know that the other thread synced the
+ file before closing)
+
+ RETURN
+ 0 ok
+ -1 error
+*/
+
+int my_sync(File fd, myf my_flags)
+{
+ int res;
+ DBUG_ENTER("my_sync");
+ DBUG_PRINT("my",("Fd: %d my_flags: %d", fd, my_flags));
+
+ if (before_sync_wait)
+ (*before_sync_wait)();
+ do
+ {
+#if defined(F_FULLFSYNC)
+ /*
+ In Mac OS X >= 10.3 this call is safer than fsync() (it forces the
+ disk's cache and guarantees ordered writes).
+ */
+ if (!(res= fcntl(fd, F_FULLFSYNC, 0)))
+ break; /* ok */
+ /* Some file systems don't support F_FULLFSYNC and fail above: */
+ DBUG_PRINT("info",("fcntl(F_FULLFSYNC) failed, falling back"));
+#endif
+#if defined(HAVE_FDATASYNC) && HAVE_DECL_FDATASYNC
+ res= fdatasync(fd);
+#elif defined(HAVE_FSYNC)
+ res= fsync(fd);
+#elif defined(_WIN32)
+ res= my_win_fsync(fd);
+#else
+#error Cannot find a way to sync a file, durability in danger
+ res= 0; /* No sync (strange OS) */
+#endif
+ } while (res == -1 && errno == EINTR);
+
+ if (res)
+ {
+ int er= errno;
+ set_my_errno(er);
+ if (!er)
+ set_my_errno(-1); /* Unknown error */
+ if (after_sync_wait)
+ (*after_sync_wait)();
+ if ((my_flags & MY_IGNORE_BADFD) &&
+ (er == EBADF || er == EINVAL || er == EROFS
+#ifdef __APPLE__
+ || er == ENOTSUP
+#endif
+ ))
+ {
+ DBUG_PRINT("info", ("ignoring errno %d", er));
+ res= 0;
+ }
+ else if (my_flags & MY_WME)
+ {
+ char errbuf[MYSYS_STRERROR_SIZE];
+ my_error(EE_SYNC, MYF(0), my_filename(fd),
+ my_errno(), my_strerror(errbuf, sizeof(errbuf), my_errno()));
+ }
+ }
+ else
+ {
+ if (after_sync_wait)
+ (*after_sync_wait)();
+ }
+ DBUG_RETURN(res);
+} /* my_sync */
+
+
+/*
+ Force directory information to disk.
+
+ SYNOPSIS
+ my_sync_dir()
+ dir_name the name of the directory
+ my_flags flags (MY_WME etc)
+
+ RETURN
+ 0 if ok, !=0 if error
+*/
+
+#ifdef __linux__
+static const char cur_dir_name[]= {FN_CURLIB, 0};
+#endif
+
+int my_sync_dir(const char *dir_name MY_ATTRIBUTE((unused)),
+ myf my_flags MY_ATTRIBUTE((unused)))
+{
+/*
+ Only Linux is known to need an explicit sync of the directory to make sure a
+ file creation/deletion/renaming in(from,to) this directory durable.
+*/
+#ifdef __linux__
+ File dir_fd;
+ int res= 0;
+ const char *correct_dir_name;
+ DBUG_ENTER("my_sync_dir");
+ DBUG_PRINT("my",("Dir: '%s' my_flags: %d", dir_name, my_flags));
+ /* Sometimes the path does not contain an explicit directory */
+ correct_dir_name= (dir_name[0] == 0) ? cur_dir_name : dir_name;
+ /*
+ Syncing a dir may give EINVAL on tmpfs on Linux, which is ok.
+ EIO on the other hand is very important. Hence MY_IGNORE_BADFD.
+ */
+ if ((dir_fd= my_open(correct_dir_name, O_RDONLY, MYF(my_flags))) >= 0)
+ {
+ if (my_sync(dir_fd, MYF(my_flags | MY_IGNORE_BADFD)))
+ res= 2;
+ if (my_close(dir_fd, MYF(my_flags)))
+ res= 3;
+ }
+ else
+ res= 1;
+ DBUG_RETURN(res);
+#else
+ return 0;
+#endif
+}
+
+
+/*
+ Force directory information to disk.
+
+ SYNOPSIS
+ my_sync_dir_by_file()
+ file_name the name of a file in the directory
+ my_flags flags (MY_WME etc)
+
+ RETURN
+ 0 if ok, !=0 if error
+*/
+
+int my_sync_dir_by_file(const char *file_name MY_ATTRIBUTE((unused)),
+ myf my_flags MY_ATTRIBUTE((unused)))
+{
+#ifdef __linux__
+ char dir_name[FN_REFLEN];
+ size_t dir_name_length;
+ dirname_part(dir_name, file_name, &dir_name_length);
+ return my_sync_dir(dir_name, my_flags);
+#else
+ return 0;
+#endif
+}
diff --git a/mysql/mysys/my_syslog.c b/mysql/mysys/my_syslog.c
new file mode 100644
index 0000000..e45410b
--- /dev/null
+++ b/mysql/mysys/my_syslog.c
@@ -0,0 +1,287 @@
+/*
+ Copyright (c) 2000, 2016, Oracle and/or its affiliates. All rights reserved.
+
+ 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; version 2 of the License.
+
+ 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#include "mysys_priv.h"
+#include "my_sys.h"
+#include <m_string.h>
+#include <stdarg.h>
+
+#ifndef _WIN32
+#include <syslog.h>
+
+/*
+ Some C libraries offer a variant of this, but we roll our own so we
+ won't have to worry about portability.
+*/
+SYSLOG_FACILITY syslog_facility[] = {
+ { LOG_DAEMON, "daemon" }, /* default for mysqld */
+ { LOG_USER, "user" }, /* default for mysql command-line client */
+
+ { LOG_LOCAL0, "local0" }, { LOG_LOCAL1, "local1" }, { LOG_LOCAL2, "local2" },
+ { LOG_LOCAL3, "local3" }, { LOG_LOCAL4, "local4" }, { LOG_LOCAL5, "local5" },
+ { LOG_LOCAL6, "local6" }, { LOG_LOCAL7, "local7" },
+
+ /* "just in case" */
+ { LOG_AUTH, "auth" }, { LOG_CRON, "cron" }, { LOG_KERN, "kern" },
+ { LOG_LPR, "lpr" }, { LOG_MAIL, "mail" }, { LOG_NEWS, "news" },
+ { LOG_SYSLOG, "syslog" }, { LOG_UUCP, "uucp" },
+
+#if defined(LOG_FTP)
+ { LOG_FTP, "ftp" },
+#endif
+#if defined(LOG_AUTHPRIV)
+ { LOG_AUTHPRIV, "authpriv" },
+#endif
+
+ { -1, NULL }};
+#endif
+
+
+#ifdef _WIN32
+#define MSG_DEFAULT 0xC0000064L
+static HANDLE hEventLog= NULL; // global
+#endif
+
+/**
+ Sends message to the system logger. On Windows, the specified message is
+ internally converted to UCS-2 encoding, while on other platforms, no
+ conversion takes place and the string is passed to the syslog API as it is.
+
+ @param cs [in] Character set info of the message string
+ @param level [in] Log level
+ @param msg [in] Message to be logged
+
+ @return
+ 0 Success
+ -1 Error
+*/
+int my_syslog(const CHARSET_INFO *cs MY_ATTRIBUTE((unused)),
+ enum loglevel level,
+ const char *msg)
+{
+ int _level;
+#ifdef _WIN32
+ wchar_t buff[MAX_SYSLOG_MESSAGE_SIZE];
+ wchar_t *u16buf= NULL;
+ size_t nchars;
+ uint dummy_errors;
+
+ DBUG_ENTER("my_syslog");
+
+ _level= (level == INFORMATION_LEVEL) ? EVENTLOG_INFORMATION_TYPE :
+ (level == WARNING_LEVEL) ? EVENTLOG_WARNING_TYPE : EVENTLOG_ERROR_TYPE;
+
+ if (hEventLog)
+ {
+ nchars= my_convert((char *) buff, sizeof(buff) - sizeof(buff[0]),
+ &my_charset_utf16le_bin, msg,
+ MAX_SYSLOG_MESSAGE_SIZE, cs, &dummy_errors);
+
+ // terminate it with NULL
+ buff[nchars / sizeof(wchar_t)]= L'\0';
+ u16buf= buff;
+
+ if (!ReportEventW(hEventLog, _level, 0, MSG_DEFAULT, NULL, 1, 0,
+ (LPCWSTR*) &u16buf, NULL))
+ goto err;
+ }
+
+ // Message successfully written to the event log.
+ DBUG_RETURN(0);
+
+err:
+ // map error appropriately
+ my_osmaperr(GetLastError());
+ DBUG_RETURN(-1);
+
+#else
+ DBUG_ENTER("my_syslog");
+
+ _level= (level == INFORMATION_LEVEL) ? LOG_INFO :
+ (level == WARNING_LEVEL) ? LOG_WARNING : LOG_ERR;
+
+ syslog(_level, "%s", msg);
+ DBUG_RETURN(0);
+
+#endif /* _WIN32 */
+}
+
+
+#ifdef _WIN32
+
+/**
+ Create a key in the Windows registry.
+ We'll setup a "MySQL" key in the EventLog branch (RegCreateKey),
+ set our executable name (GetModuleFileName) as file-name
+ ("EventMessageFile"), then set the message types we expect to
+ be logging ("TypesSupported").
+ If the key does not exist, sufficient privileges will be required
+ to create and configure it. If the key does exist, opening it
+ should be unprivileged; modifying will fail on insufficient
+ privileges, but that is non-fatal.
+
+ @param key [in] Name of the event generator.
+ (Only last part of the key, e.g. "MySQL")
+
+ @return
+ 0 Success
+ -1 Error
+*/
+
+const char registry_prefix[]=
+ "SYSTEM\\CurrentControlSet\\services\\eventlog\\Application\\";
+
+static int windows_eventlog_create_registry_entry(const char *key)
+{
+ HKEY hRegKey= NULL;
+ DWORD dwError= 0;
+ TCHAR szPath[MAX_PATH];
+ DWORD dwTypes;
+
+ size_t l= sizeof(registry_prefix) + strlen(key) + 1;
+ char *buff;
+
+ int ret= 0;
+
+ DBUG_ENTER("my_syslog");
+
+ if ((buff= (char *) my_malloc(PSI_NOT_INSTRUMENTED, l, MYF(0))) == NULL)
+ DBUG_RETURN(-1);
+
+ my_snprintf(buff, l, "%s%s", registry_prefix, key);
+
+ // Opens the event source registry key; creates it first if required.
+ dwError= RegCreateKey(HKEY_LOCAL_MACHINE, buff, &hRegKey);
+
+ my_free(buff);
+
+ if (dwError != ERROR_SUCCESS)
+ {
+ if (dwError == ERROR_ACCESS_DENIED)
+ {
+ my_message_stderr(0, "Could not create or access the registry key needed for the MySQL application\n"
+ "to log to the Windows EventLog. Run the application with sufficient\n"
+ "privileges once to create the key, add the key manually, or turn off\n"
+ "logging for that application.", MYF(0));
+ }
+ DBUG_RETURN(-1);
+ }
+
+ /* Name of the PE module that contains the message resource */
+ GetModuleFileName(NULL, szPath, MAX_PATH);
+
+ /* Register EventMessageFile (DLL/exec containing event identifiers) */
+ dwError = RegSetValueEx(hRegKey, "EventMessageFile", 0, REG_EXPAND_SZ,
+ (PBYTE) szPath, (DWORD) (strlen(szPath) + 1));
+ if ((dwError != ERROR_SUCCESS) && (dwError != ERROR_ACCESS_DENIED))
+ ret = -1;
+
+ /* Register supported event types */
+ dwTypes= (EVENTLOG_ERROR_TYPE | EVENTLOG_WARNING_TYPE |
+ EVENTLOG_INFORMATION_TYPE);
+ dwError= RegSetValueEx(hRegKey, "TypesSupported", 0, REG_DWORD,
+ (LPBYTE) &dwTypes, sizeof dwTypes);
+ if ((dwError != ERROR_SUCCESS) && (dwError != ERROR_ACCESS_DENIED))
+ ret= -1;
+
+ RegCloseKey(hRegKey);
+
+ DBUG_RETURN(ret);
+}
+#endif
+
+
+/**
+ Opens/Registers a new handle for system logging.
+ Note: It's a thread-unsafe function. It should either
+ be invoked from the main thread or some extra thread
+ safety measures need to be taken.
+
+ @param name [in] Name of the event source / syslog ident
+
+ @return
+ 0 Success
+ -1 Error, log not opened
+ -2 Error, not updated, using previous values
+*/
+int my_openlog(const char *name, int option, int facility)
+{
+#ifndef _WIN32
+ int opts= (option & MY_SYSLOG_PIDS) ? LOG_PID : 0;
+
+ DBUG_ENTER("my_openlog");
+ openlog(name, opts | LOG_NDELAY, facility);
+
+#else
+
+ HANDLE hEL_new;
+
+ DBUG_ENTER("my_openlog");
+
+ // OOM failsafe. Not needed for syslog.
+ if (name == NULL)
+ DBUG_RETURN(-1);
+
+ if ((windows_eventlog_create_registry_entry(name) != 0) ||
+ !(hEL_new= RegisterEventSource(NULL, name)))
+ {
+ // map error appropriately
+ my_osmaperr(GetLastError());
+ DBUG_RETURN((hEventLog == NULL) ? -1 : -2);
+ }
+ else
+ {
+ if (hEventLog != NULL)
+ DeregisterEventSource(hEventLog);
+ hEventLog= hEL_new;
+ }
+#endif
+
+ DBUG_RETURN(0);
+}
+
+
+/**
+ Closes/de-registers the system logging handle.
+ Note: Its a thread-unsafe function. It should
+ either be invoked from the main thread or some
+ extra thread safety measures need to be taken.
+
+ @return
+ 0 Success
+ -1 Error
+*/
+int my_closelog(void)
+{
+ DBUG_ENTER("my_closelog");
+#ifndef _WIN32
+ closelog();
+ DBUG_RETURN(0);
+#else
+ if ((hEventLog != NULL) && (! DeregisterEventSource(hEventLog)))
+ goto err;
+
+ hEventLog= NULL;
+ DBUG_RETURN(0);
+
+err:
+ hEventLog= NULL;
+ // map error appropriately
+ my_osmaperr(GetLastError());
+ DBUG_RETURN(-1);
+#endif
+}
diff --git a/mysql/mysys/my_thr_init.c b/mysql/mysys/my_thr_init.c
new file mode 100644
index 0000000..db0881c
--- /dev/null
+++ b/mysql/mysys/my_thr_init.c
@@ -0,0 +1,459 @@
+/* Copyright (c) 2000, 2015, Oracle and/or its affiliates. All rights reserved.
+
+ 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; version 2 of the License.
+
+ 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+/*
+ Functions to handle initializating and allocationg of all mysys & debug
+ thread variables.
+*/
+
+#include "mysys_priv.h"
+#include "my_sys.h"
+#include <m_string.h>
+#include <signal.h>
+#include "my_thread_local.h"
+
+static my_bool THR_KEY_mysys_initialized= FALSE;
+static my_bool my_thread_global_init_done= FALSE;
+#ifndef DBUG_OFF
+static uint THR_thread_count= 0;
+static uint my_thread_end_wait_time= 5;
+static my_thread_id thread_id= 0;
+static thread_local_key_t THR_KEY_mysys;
+#endif
+static thread_local_key_t THR_KEY_myerrno;
+#ifdef _WIN32
+static thread_local_key_t THR_KEY_winerrno;
+#endif
+
+mysql_mutex_t THR_LOCK_malloc, THR_LOCK_open,
+ THR_LOCK_lock, THR_LOCK_myisam, THR_LOCK_heap,
+ THR_LOCK_net, THR_LOCK_charset,
+ THR_LOCK_myisam_mmap;
+#ifndef DBUG_OFF
+mysql_mutex_t THR_LOCK_threads;
+mysql_cond_t THR_COND_threads;
+#endif
+
+#ifdef PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP
+native_mutexattr_t my_fast_mutexattr;
+#endif
+#ifdef PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP
+native_mutexattr_t my_errorcheck_mutexattr;
+#endif
+#ifdef _WIN32
+static void install_sigabrt_handler();
+#endif
+
+#ifndef DBUG_OFF
+struct st_my_thread_var
+{
+ my_thread_id id;
+ struct _db_code_state_ *dbug;
+};
+
+static struct st_my_thread_var *mysys_thread_var()
+{
+ DBUG_ASSERT(THR_KEY_mysys_initialized);
+ return (struct st_my_thread_var*)my_get_thread_local(THR_KEY_mysys);
+}
+
+
+static int set_mysys_thread_var(struct st_my_thread_var *mysys_var)
+{
+ DBUG_ASSERT(THR_KEY_mysys_initialized);
+ return my_set_thread_local(THR_KEY_mysys, mysys_var);
+}
+#endif
+
+
+/**
+ Re-initialize components initialized early with @c my_thread_global_init.
+ Some mutexes were initialized before the instrumentation.
+ Destroy + create them again, now that the instrumentation
+ is in place.
+ This is safe, since this function() is called before creating new threads,
+ so the mutexes are not in use.
+*/
+
+void my_thread_global_reinit()
+{
+ DBUG_ASSERT(my_thread_global_init_done);
+
+#ifdef HAVE_PSI_INTERFACE
+ my_init_mysys_psi_keys();
+#endif
+
+ mysql_mutex_destroy(&THR_LOCK_heap);
+ mysql_mutex_init(key_THR_LOCK_heap, &THR_LOCK_heap, MY_MUTEX_INIT_FAST);
+
+ mysql_mutex_destroy(&THR_LOCK_net);
+ mysql_mutex_init(key_THR_LOCK_net, &THR_LOCK_net, MY_MUTEX_INIT_FAST);
+
+ mysql_mutex_destroy(&THR_LOCK_myisam);
+ mysql_mutex_init(key_THR_LOCK_myisam, &THR_LOCK_myisam, MY_MUTEX_INIT_SLOW);
+
+ mysql_mutex_destroy(&THR_LOCK_malloc);
+ mysql_mutex_init(key_THR_LOCK_malloc, &THR_LOCK_malloc, MY_MUTEX_INIT_FAST);
+
+ mysql_mutex_destroy(&THR_LOCK_open);
+ mysql_mutex_init(key_THR_LOCK_open, &THR_LOCK_open, MY_MUTEX_INIT_FAST);
+
+ mysql_mutex_destroy(&THR_LOCK_charset);
+ mysql_mutex_init(key_THR_LOCK_charset, &THR_LOCK_charset, MY_MUTEX_INIT_FAST);
+
+#ifndef DBUG_OFF
+ mysql_mutex_destroy(&THR_LOCK_threads);
+ mysql_mutex_init(key_THR_LOCK_threads, &THR_LOCK_threads, MY_MUTEX_INIT_FAST);
+
+ mysql_cond_destroy(&THR_COND_threads);
+ mysql_cond_init(key_THR_COND_threads, &THR_COND_threads);
+#endif
+}
+
+
+/**
+ initialize thread environment
+
+ @retval FALSE ok
+ @retval TRUE error (Couldn't create THR_KEY_mysys)
+*/
+
+my_bool my_thread_global_init()
+{
+ int pth_ret;
+
+ if (my_thread_global_init_done)
+ return FALSE;
+ my_thread_global_init_done= TRUE;
+
+#if defined(SAFE_MUTEX)
+ safe_mutex_global_init(); /* Must be called early */
+#endif
+
+#ifdef PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP
+ /*
+ Set mutex type to "fast" a.k.a "adaptive"
+
+ In this case the thread may steal the mutex from some other thread
+ that is waiting for the same mutex. This will save us some
+ context switches but may cause a thread to 'starve forever' while
+ waiting for the mutex (not likely if the code within the mutex is
+ short).
+ */
+ pthread_mutexattr_init(&my_fast_mutexattr);
+ pthread_mutexattr_settype(&my_fast_mutexattr,
+ PTHREAD_MUTEX_ADAPTIVE_NP);
+#endif
+
+#ifdef PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP
+ /*
+ Set mutex type to "errorcheck"
+ */
+ pthread_mutexattr_init(&my_errorcheck_mutexattr);
+ pthread_mutexattr_settype(&my_errorcheck_mutexattr,
+ PTHREAD_MUTEX_ERRORCHECK);
+#endif
+
+ DBUG_ASSERT(! THR_KEY_mysys_initialized);
+#ifndef DBUG_OFF
+ if ((pth_ret= my_create_thread_local_key(&THR_KEY_mysys, NULL)) != 0)
+ { /* purecov: begin inspected */
+ my_message_local(ERROR_LEVEL, "Can't initialize threads: error %d",
+ pth_ret);
+ /* purecov: end */
+ return TRUE;
+ }
+#endif
+ if ((pth_ret= my_create_thread_local_key(&THR_KEY_myerrno, NULL)) != 0)
+ { /* purecov: begin inspected */
+ my_message_local(ERROR_LEVEL, "Can't initialize threads: error %d",
+ pth_ret);
+ /* purecov: end */
+ return TRUE;
+ }
+#ifdef _WIN32
+ if ((pth_ret= my_create_thread_local_key(&THR_KEY_winerrno, NULL)) != 0)
+ { /* purecov: begin inspected */
+ my_message_local(ERROR_LEVEL, "Can't initialize threads: error %d",
+ pth_ret);
+ /* purecov: end */
+ return TRUE;
+ }
+#endif
+ THR_KEY_mysys_initialized= TRUE;
+
+ mysql_mutex_init(key_THR_LOCK_malloc, &THR_LOCK_malloc, MY_MUTEX_INIT_FAST);
+ mysql_mutex_init(key_THR_LOCK_open, &THR_LOCK_open, MY_MUTEX_INIT_FAST);
+ mysql_mutex_init(key_THR_LOCK_charset, &THR_LOCK_charset, MY_MUTEX_INIT_FAST);
+ mysql_mutex_init(key_THR_LOCK_lock, &THR_LOCK_lock, MY_MUTEX_INIT_FAST);
+ mysql_mutex_init(key_THR_LOCK_myisam, &THR_LOCK_myisam, MY_MUTEX_INIT_SLOW);
+ mysql_mutex_init(key_THR_LOCK_myisam_mmap, &THR_LOCK_myisam_mmap, MY_MUTEX_INIT_FAST);
+ mysql_mutex_init(key_THR_LOCK_heap, &THR_LOCK_heap, MY_MUTEX_INIT_FAST);
+ mysql_mutex_init(key_THR_LOCK_net, &THR_LOCK_net, MY_MUTEX_INIT_FAST);
+#ifndef DBUG_OFF
+ mysql_mutex_init(key_THR_LOCK_threads, &THR_LOCK_threads, MY_MUTEX_INIT_FAST);
+ mysql_cond_init(key_THR_COND_threads, &THR_COND_threads);
+#endif
+
+ return FALSE;
+}
+
+
+void my_thread_global_end()
+{
+#ifndef DBUG_OFF
+ struct timespec abstime;
+ my_bool all_threads_killed= TRUE;
+
+ set_timespec(&abstime, my_thread_end_wait_time);
+ mysql_mutex_lock(&THR_LOCK_threads);
+ while (THR_thread_count > 0)
+ {
+ int error= mysql_cond_timedwait(&THR_COND_threads, &THR_LOCK_threads,
+ &abstime);
+ if (error == ETIMEDOUT || error == ETIME)
+ {
+#ifndef _WIN32
+ /*
+ We shouldn't give an error here, because if we don't have
+ pthread_kill(), programs like mysqld can't ensure that all threads
+ are killed when we enter here.
+ */
+ if (THR_thread_count)
+ /* purecov: begin inspected */
+ my_message_local(ERROR_LEVEL, "Error in my_thread_global_end(): "
+ "%d threads didn't exit", THR_thread_count);
+ /* purecov: end */
+#endif
+ all_threads_killed= FALSE;
+ break;
+ }
+ }
+ mysql_mutex_unlock(&THR_LOCK_threads);
+#endif
+
+ DBUG_ASSERT(THR_KEY_mysys_initialized);
+#ifndef DBUG_OFF
+ my_delete_thread_local_key(THR_KEY_mysys);
+#endif
+ my_delete_thread_local_key(THR_KEY_myerrno);
+#ifdef _WIN32
+ my_delete_thread_local_key(THR_KEY_winerrno);
+#endif
+ THR_KEY_mysys_initialized= FALSE;
+#ifdef PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP
+ pthread_mutexattr_destroy(&my_fast_mutexattr);
+#endif
+#ifdef PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP
+ pthread_mutexattr_destroy(&my_errorcheck_mutexattr);
+#endif
+ mysql_mutex_destroy(&THR_LOCK_malloc);
+ mysql_mutex_destroy(&THR_LOCK_open);
+ mysql_mutex_destroy(&THR_LOCK_lock);
+ mysql_mutex_destroy(&THR_LOCK_myisam);
+ mysql_mutex_destroy(&THR_LOCK_myisam_mmap);
+ mysql_mutex_destroy(&THR_LOCK_heap);
+ mysql_mutex_destroy(&THR_LOCK_net);
+ mysql_mutex_destroy(&THR_LOCK_charset);
+#ifndef DBUG_OFF
+ if (all_threads_killed)
+ {
+ mysql_mutex_destroy(&THR_LOCK_threads);
+ mysql_cond_destroy(&THR_COND_threads);
+ }
+#endif
+
+ my_thread_global_init_done= FALSE;
+}
+
+
+/**
+ Allocate thread specific memory for the thread, used by mysys and dbug
+
+ @note This function may called multiple times for a thread, for example
+ if one uses my_init() followed by mysql_server_init().
+
+ @retval FALSE ok
+ @retval TRUE Fatal error; mysys/dbug functions can't be used
+*/
+
+my_bool my_thread_init()
+{
+#ifndef DBUG_OFF
+ struct st_my_thread_var *tmp;
+#endif
+
+ if (!my_thread_global_init_done)
+ return TRUE; /* cannot proceed with unintialized library */
+
+#ifdef _WIN32
+ install_sigabrt_handler();
+#endif
+
+#ifndef DBUG_OFF
+ if (mysys_thread_var())
+ return FALSE;
+
+ if (!(tmp= (struct st_my_thread_var *) calloc(1, sizeof(*tmp))))
+ return TRUE;
+
+ mysql_mutex_lock(&THR_LOCK_threads);
+ tmp->id= ++thread_id;
+ ++THR_thread_count;
+ mysql_mutex_unlock(&THR_LOCK_threads);
+ set_mysys_thread_var(tmp);
+#endif
+
+ return FALSE;
+}
+
+
+/**
+ Deallocate memory used by the thread for book-keeping
+
+ @note This may be called multiple times for a thread.
+ This happens for example when one calls 'mysql_server_init()'
+ mysql_server_end() and then ends with a mysql_end().
+*/
+
+void my_thread_end()
+{
+#ifndef DBUG_OFF
+ struct st_my_thread_var *tmp= mysys_thread_var();
+#endif
+
+#ifdef HAVE_PSI_INTERFACE
+ /*
+ Remove the instrumentation for this thread.
+ This must be done before trashing st_my_thread_var,
+ because the LF_HASH depends on it.
+ */
+ PSI_THREAD_CALL(delete_current_thread)();
+#endif
+
+#if !defined(DBUG_OFF)
+ if (tmp)
+ {
+ /* tmp->dbug is allocated inside DBUG library */
+ if (tmp->dbug)
+ {
+ DBUG_POP();
+ free(tmp->dbug);
+ tmp->dbug= NULL;
+ }
+ free(tmp);
+
+ /*
+ Decrement counter for number of running threads. We are using this
+ in my_thread_global_end() to wait until all threads have called
+ my_thread_end and thus freed all memory they have allocated in
+ my_thread_init() and DBUG_xxxx
+ */
+ mysql_mutex_lock(&THR_LOCK_threads);
+ DBUG_ASSERT(THR_thread_count != 0);
+ if (--THR_thread_count == 0)
+ mysql_cond_signal(&THR_COND_threads);
+ mysql_mutex_unlock(&THR_LOCK_threads);
+ }
+ set_mysys_thread_var(NULL);
+#endif
+}
+
+
+int my_errno()
+{
+ if (THR_KEY_mysys_initialized)
+ return (int)(intptr)my_get_thread_local(THR_KEY_myerrno);
+ return 0;
+}
+
+
+void set_my_errno(int my_errno)
+{
+ if (THR_KEY_mysys_initialized)
+ (void) my_set_thread_local(THR_KEY_myerrno, (void*)(intptr)my_errno);
+}
+
+
+#ifdef _WIN32
+int thr_winerr()
+{
+ if (THR_KEY_mysys_initialized)
+ return (int)(intptr)my_get_thread_local(THR_KEY_winerrno);
+ return 0;
+}
+
+
+void set_thr_winerr(int winerr)
+{
+ if (THR_KEY_mysys_initialized)
+ (void) my_set_thread_local(THR_KEY_winerrno, (void*)(intptr)winerr);
+}
+#endif
+
+
+#ifndef DBUG_OFF
+my_thread_id my_thread_var_id()
+{
+ return mysys_thread_var()->id;
+}
+
+
+void set_my_thread_var_id(my_thread_id id)
+{
+ mysys_thread_var()->id= id;
+}
+
+
+struct _db_code_state_ **my_thread_var_dbug()
+{
+ struct st_my_thread_var *tmp;
+ /*
+ Instead of enforcing DBUG_ASSERT(THR_KEY_mysys_initialized) here,
+ which causes any DBUG_ENTER and related traces to fail when
+ used in init / cleanup code, we are more tolerant:
+ using DBUG_ENTER / DBUG_PRINT / DBUG_RETURN
+ when the dbug instrumentation is not in place will do nothing.
+ */
+ if (! THR_KEY_mysys_initialized)
+ return NULL;
+ tmp= mysys_thread_var();
+ return tmp ? &tmp->dbug : NULL;
+}
+#endif /* DBUG_OFF */
+
+
+#ifdef _WIN32
+/*
+ In Visual Studio 2005 and later, default SIGABRT handler will overwrite
+ any unhandled exception filter set by the application and will try to
+ call JIT debugger. This is not what we want, this we calling __debugbreak
+ to stop in debugger, if process is being debugged or to generate
+ EXCEPTION_BREAKPOINT and then handle_segfault will do its magic.
+*/
+
+static void my_sigabrt_handler(int sig)
+{
+ __debugbreak();
+}
+
+static void install_sigabrt_handler()
+{
+ /*abort() should not override our exception filter*/
+ _set_abort_behavior(0,_CALL_REPORTFAULT);
+ signal(SIGABRT,my_sigabrt_handler);
+}
+#endif
+
diff --git a/mysql/mysys/my_thread.c b/mysql/mysys/my_thread.c
new file mode 100644
index 0000000..5db580e
--- /dev/null
+++ b/mysql/mysys/my_thread.c
@@ -0,0 +1,185 @@
+/* Copyright (c) 2000, 2016, Oracle and/or its affiliates. All rights reserved.
+
+ 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; version 2 of the License.
+
+ 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include "my_thread.h"
+
+#ifdef _WIN32
+#include "my_sys.h" /* my_osmaperr */
+#include <process.h>
+#include <signal.h>
+
+struct thread_start_parameter
+{
+ my_start_routine func;
+ void *arg;
+};
+
+
+static unsigned int __stdcall win_thread_start(void *p)
+{
+ struct thread_start_parameter *par= (struct thread_start_parameter *)p;
+ my_start_routine func= par->func;
+ void *arg= par->arg;
+ free(p);
+ (*func)(arg);
+ return 0;
+}
+#endif
+
+
+/*
+ One time initialization. For simplicity, we assume initializer thread
+ does not exit within init_routine().
+*/
+int my_thread_once(my_thread_once_t *once_control, void (*init_routine)(void))
+{
+#ifndef _WIN32
+ return pthread_once(once_control, init_routine);
+#else
+ LONG state;
+
+ /*
+ Do "dirty" read to find out if initialization is already done, to
+ save an interlocked operation in common case. Memory barriers are ensured by
+ Visual C++ volatile implementation.
+ */
+ if (*once_control == MY_THREAD_ONCE_DONE)
+ return 0;
+
+ state= InterlockedCompareExchange(once_control, MY_THREAD_ONCE_INPROGRESS,
+ MY_THREAD_ONCE_INIT);
+
+ switch(state)
+ {
+ case MY_THREAD_ONCE_INIT:
+ /* This is initializer thread */
+ (*init_routine)();
+ *once_control= MY_THREAD_ONCE_DONE;
+ break;
+
+ case MY_THREAD_ONCE_INPROGRESS:
+ /* init_routine in progress. Wait for its completion */
+ while(*once_control == MY_THREAD_ONCE_INPROGRESS)
+ {
+ Sleep(1);
+ }
+ break;
+ case MY_THREAD_ONCE_DONE:
+ /* Nothing to do */
+ break;
+ }
+ return 0;
+#endif /* _WIN32 */
+}
+
+
+int my_thread_create(my_thread_handle *thread, const my_thread_attr_t *attr,
+ my_start_routine func, void *arg)
+{
+#ifndef _WIN32
+ return pthread_create(&thread->thread, attr, func, arg);
+#else
+ struct thread_start_parameter *par;
+ unsigned int stack_size;
+
+ par= (struct thread_start_parameter *)malloc(sizeof(*par));
+ if (!par)
+ goto error_return;
+
+ par->func= func;
+ par->arg= arg;
+ stack_size= attr ? attr->dwStackSize : 0;
+
+ thread->handle= (HANDLE)_beginthreadex(NULL, stack_size, win_thread_start,
+ par, 0, &thread->thread);
+
+ if (thread->handle)
+ {
+ /* Note that JOINABLE is default, so attr == NULL => JOINABLE. */
+ if (attr && attr->detachstate == MY_THREAD_CREATE_DETACHED)
+ {
+ /*
+ Close handles for detached threads right away to avoid leaking
+ handles. For joinable threads we need the handle during
+ my_thread_join. It will be closed there.
+ */
+ CloseHandle(thread->handle);
+ thread->handle= NULL;
+ }
+ return 0;
+ }
+
+ my_osmaperr(GetLastError());
+ free(par);
+
+error_return:
+ thread->thread= 0;
+ thread->handle= NULL;
+ return 1;
+#endif
+}
+
+
+int my_thread_join(my_thread_handle *thread, void **value_ptr)
+{
+#ifndef _WIN32
+ return pthread_join(thread->thread, value_ptr);
+#else
+ DWORD ret;
+ int result= 0;
+ ret= WaitForSingleObject(thread->handle, INFINITE);
+ if (ret != WAIT_OBJECT_0)
+ {
+ my_osmaperr(GetLastError());
+ result= 1;
+ }
+ if (thread->handle)
+ CloseHandle(thread->handle);
+ thread->thread= 0;
+ thread->handle= NULL;
+ return result;
+#endif
+}
+
+
+int my_thread_cancel(my_thread_handle *thread)
+{
+#ifndef _WIN32
+ return pthread_cancel(thread->thread);
+#else
+ BOOL ok= FALSE;
+
+ if (thread->handle)
+ {
+ ok= TerminateThread(thread->handle, 0);
+ CloseHandle(thread->handle);
+ }
+ if (ok)
+ return 0;
+
+ errno= EINVAL;
+ return -1;
+#endif
+}
+
+
+void my_thread_exit(void *value_ptr)
+{
+#ifndef _WIN32
+ pthread_exit(value_ptr);
+#else
+ _endthreadex(0);
+#endif
+}
diff --git a/mysql/mysys/my_winerr.c b/mysql/mysys/my_winerr.c
new file mode 100644
index 0000000..5910010
--- /dev/null
+++ b/mysql/mysys/my_winerr.c
@@ -0,0 +1,130 @@
+/* Copyright (c) 2008, 2015, Oracle and/or its affiliates. All rights reserved.
+
+ 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; version 2 of the License.
+
+ 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+/*
+ Convert Windows API error (GetLastError() to Posix equivalent (errno)
+ The exported function my_osmaperr() is modelled after and borrows
+ heavily from undocumented _dosmaperr()(found of the static Microsoft C runtime).
+*/
+
+#include <my_global.h>
+#include <my_sys.h>
+#include "my_thread_local.h"
+
+
+struct errentry
+{
+ unsigned long oscode; /* OS return value */
+ int sysv_errno; /* System V error code */
+};
+
+static struct errentry errtable[]= {
+ { ERROR_INVALID_FUNCTION, EINVAL }, /* 1 */
+ { ERROR_FILE_NOT_FOUND, ENOENT }, /* 2 */
+ { ERROR_PATH_NOT_FOUND, ENOENT }, /* 3 */
+ { ERROR_TOO_MANY_OPEN_FILES, EMFILE }, /* 4 */
+ { ERROR_ACCESS_DENIED, EACCES }, /* 5 */
+ { ERROR_INVALID_HANDLE, EBADF }, /* 6 */
+ { ERROR_ARENA_TRASHED, ENOMEM }, /* 7 */
+ { ERROR_NOT_ENOUGH_MEMORY, ENOMEM }, /* 8 */
+ { ERROR_INVALID_BLOCK, ENOMEM }, /* 9 */
+ { ERROR_BAD_ENVIRONMENT, E2BIG }, /* 10 */
+ { ERROR_BAD_FORMAT, ENOEXEC }, /* 11 */
+ { ERROR_INVALID_ACCESS, EINVAL }, /* 12 */
+ { ERROR_INVALID_DATA, EINVAL }, /* 13 */
+ { ERROR_INVALID_DRIVE, ENOENT }, /* 15 */
+ { ERROR_CURRENT_DIRECTORY, EACCES }, /* 16 */
+ { ERROR_NOT_SAME_DEVICE, EXDEV }, /* 17 */
+ { ERROR_NO_MORE_FILES, ENOENT }, /* 18 */
+ { ERROR_LOCK_VIOLATION, EACCES }, /* 33 */
+ { ERROR_BAD_NETPATH, ENOENT }, /* 53 */
+ { ERROR_NETWORK_ACCESS_DENIED, EACCES }, /* 65 */
+ { ERROR_BAD_NET_NAME, ENOENT }, /* 67 */
+ { ERROR_FILE_EXISTS, EEXIST }, /* 80 */
+ { ERROR_CANNOT_MAKE, EACCES }, /* 82 */
+ { ERROR_FAIL_I24, EACCES }, /* 83 */
+ { ERROR_INVALID_PARAMETER, EINVAL }, /* 87 */
+ { ERROR_NO_PROC_SLOTS, EAGAIN }, /* 89 */
+ { ERROR_DRIVE_LOCKED, EACCES }, /* 108 */
+ { ERROR_BROKEN_PIPE, EPIPE }, /* 109 */
+ { ERROR_DISK_FULL, ENOSPC }, /* 112 */
+ { ERROR_INVALID_TARGET_HANDLE, EBADF }, /* 114 */
+ { ERROR_INVALID_NAME, ENOENT }, /* 123 */
+ { ERROR_INVALID_HANDLE, EINVAL }, /* 124 */
+ { ERROR_WAIT_NO_CHILDREN, ECHILD }, /* 128 */
+ { ERROR_CHILD_NOT_COMPLETE, ECHILD }, /* 129 */
+ { ERROR_DIRECT_ACCESS_HANDLE, EBADF }, /* 130 */
+ { ERROR_NEGATIVE_SEEK, EINVAL }, /* 131 */
+ { ERROR_SEEK_ON_DEVICE, EACCES }, /* 132 */
+ { ERROR_DIR_NOT_EMPTY, ENOTEMPTY }, /* 145 */
+ { ERROR_NOT_LOCKED, EACCES }, /* 158 */
+ { ERROR_BAD_PATHNAME, ENOENT }, /* 161 */
+ { ERROR_MAX_THRDS_REACHED, EAGAIN }, /* 164 */
+ { ERROR_LOCK_FAILED, EACCES }, /* 167 */
+ { ERROR_ALREADY_EXISTS, EEXIST }, /* 183 */
+ { ERROR_FILENAME_EXCED_RANGE, ENOENT }, /* 206 */
+ { ERROR_NESTING_NOT_ALLOWED, EAGAIN }, /* 215 */
+ { ERROR_NOT_ENOUGH_QUOTA, ENOMEM } /* 1816 */
+};
+
+/* size of the table */
+#define ERRTABLESIZE (sizeof(errtable)/sizeof(errtable[0]))
+
+/* The following two constants must be the minimum and maximum
+values in the (contiguous) range of Exec Failure errors. */
+#define MIN_EXEC_ERROR ERROR_INVALID_STARTING_CODESEG
+#define MAX_EXEC_ERROR ERROR_INFLOOP_IN_RELOC_CHAIN
+
+/* These are the low and high value in the range of errors that are
+access violations */
+#define MIN_EACCES_RANGE ERROR_WRITE_PROTECT
+#define MAX_EACCES_RANGE ERROR_SHARING_BUFFER_EXCEEDED
+
+
+static int get_errno_from_oserr(unsigned long oserrno)
+{
+ int i;
+
+ /* check the table for the OS error code */
+ for (i= 0; i < ERRTABLESIZE; ++i)
+ {
+ if (oserrno == errtable[i].oscode)
+ {
+ return errtable[i].sysv_errno;
+ }
+ }
+
+ /* The error code wasn't in the table. We check for a range of */
+ /* EACCES errors or exec failure errors (ENOEXEC). Otherwise */
+ /* EINVAL is returned. */
+
+ if (oserrno >= MIN_EACCES_RANGE && oserrno <= MAX_EACCES_RANGE)
+ return EACCES;
+ else if (oserrno >= MIN_EXEC_ERROR && oserrno <= MAX_EXEC_ERROR)
+ return ENOEXEC;
+ else
+ return EINVAL;
+}
+
+/* Set errno corresponsing to GetLastError() value */
+void my_osmaperr( unsigned long oserrno)
+{
+ /*
+ set thr_winerr so that we could return the Windows Error Code
+ when it is EINVAL.
+ */
+ set_thr_winerr(oserrno);
+ errno= get_errno_from_oserr(oserrno);
+}
diff --git a/mysql/mysys/my_winfile.c b/mysql/mysys/my_winfile.c
new file mode 100644
index 0000000..a5c13a3
--- /dev/null
+++ b/mysql/mysys/my_winfile.c
@@ -0,0 +1,682 @@
+/* Copyright (c) 2008, 2015, Oracle and/or its affiliates. All rights reserved.
+
+ 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; version 2 of the License.
+
+ 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+/*
+ The purpose of this file is to provide implementation of file IO routines on
+ Windows that can be thought as drop-in replacement for corresponding C runtime
+ functionality.
+
+ Compared to Windows CRT, this one
+ - does not have the same file descriptor
+ limitation (default is 16384 and can be increased further, whereas CRT poses
+ a hard limit of 2048 file descriptors)
+ - the file operations are not serialized
+ - positional IO pread/pwrite is ported here.
+ - no text mode for files, all IO is "binary"
+
+ Naming convention:
+ All routines are prefixed with my_win_, e.g Posix open() is implemented with
+ my_win_open()
+
+ Implemented are
+ - POSIX routines(e.g open, read, lseek ...)
+ - Some ANSI C stream routines (fopen, fdopen, fileno, fclose)
+ - Windows CRT equvalients (my_get_osfhandle, open_osfhandle)
+
+ Worth to note:
+ - File descriptors used here are located in a range that is not compatible
+ with CRT on purpose. Attempt to use a file descriptor from Windows CRT library
+ range in my_win_* function will be punished with DBUG_ASSERT()
+
+ - File streams (FILE *) are actually from the C runtime. The routines provided
+ here are useful only in scernarios that use low-level IO with my_win_fileno()
+*/
+
+#ifdef _WIN32
+
+#include "mysys_priv.h"
+#include "my_thread_local.h"
+#include <share.h>
+#include <sys/stat.h>
+
+/* Associates a file descriptor with an existing operating-system file handle.*/
+File my_open_osfhandle(HANDLE handle, int oflag)
+{
+ int offset= -1;
+ uint i;
+ DBUG_ENTER("my_open_osfhandle");
+
+ mysql_mutex_lock(&THR_LOCK_open);
+ for(i= MY_FILE_MIN; i < my_file_limit;i++)
+ {
+ if(my_file_info[i].fhandle == 0)
+ {
+ struct st_my_file_info *finfo= &(my_file_info[i]);
+ finfo->type= FILE_BY_OPEN;
+ finfo->fhandle= handle;
+ finfo->oflag= oflag;
+ offset= i;
+ break;
+ }
+ }
+ mysql_mutex_unlock(&THR_LOCK_open);
+ if(offset == -1)
+ errno= EMFILE; /* to many file handles open */
+ DBUG_RETURN(offset);
+}
+
+
+static void invalidate_fd(File fd)
+{
+ DBUG_ENTER("invalidate_fd");
+ DBUG_ASSERT(fd >= MY_FILE_MIN && fd < (int)my_file_limit);
+ my_file_info[fd].fhandle= 0;
+ DBUG_VOID_RETURN;
+}
+
+
+/* Get Windows handle for a file descriptor */
+HANDLE my_get_osfhandle(File fd)
+{
+ DBUG_ENTER("my_get_osfhandle");
+ DBUG_ASSERT(fd >= MY_FILE_MIN && fd < (int)my_file_limit);
+ DBUG_RETURN(my_file_info[fd].fhandle);
+}
+
+
+static int my_get_open_flags(File fd)
+{
+ DBUG_ENTER("my_get_open_flags");
+ DBUG_ASSERT(fd >= MY_FILE_MIN && fd < (int)my_file_limit);
+ DBUG_RETURN(my_file_info[fd].oflag);
+}
+
+
+/*
+ Open a file with sharing. Similar to _sopen() from libc, but allows managing
+ share delete on win32
+
+ SYNOPSIS
+ my_win_sopen()
+ path file name
+ oflag operation flags
+ shflag share flag
+ pmode permission flags
+
+ RETURN VALUE
+ File descriptor of opened file if success
+ -1 and sets errno if fails.
+*/
+
+File my_win_sopen(const char *path, int oflag, int shflag, int pmode)
+{
+ int fh; /* handle of opened file */
+ int mask;
+ HANDLE osfh; /* OS handle of opened file */
+ DWORD fileaccess; /* OS file access (requested) */
+ DWORD fileshare; /* OS file sharing mode */
+ DWORD filecreate; /* OS method of opening/creating */
+ DWORD fileattrib; /* OS file attribute flags */
+ SECURITY_ATTRIBUTES SecurityAttributes;
+
+ DBUG_ENTER("my_win_sopen");
+
+ if (check_if_legal_filename(path))
+ {
+ errno= EACCES;
+ DBUG_RETURN(-1);
+ }
+ SecurityAttributes.nLength= sizeof(SecurityAttributes);
+ SecurityAttributes.lpSecurityDescriptor= NULL;
+ SecurityAttributes.bInheritHandle= !(oflag & _O_NOINHERIT);
+
+ /* decode the access flags */
+ switch (oflag & (_O_RDONLY | _O_WRONLY | _O_RDWR)) {
+ case _O_RDONLY: /* read access */
+ fileaccess= GENERIC_READ;
+ break;
+ case _O_WRONLY: /* write access */
+ fileaccess= GENERIC_WRITE;
+ break;
+ case _O_RDWR: /* read and write access */
+ fileaccess= GENERIC_READ | GENERIC_WRITE;
+ break;
+ default: /* error, bad oflag */
+ errno= EINVAL;
+ DBUG_RETURN(-1);
+ }
+
+ /* decode sharing flags */
+ switch (shflag) {
+ case _SH_DENYRW: /* exclusive access except delete */
+ fileshare= FILE_SHARE_DELETE;
+ break;
+ case _SH_DENYWR: /* share read and delete access */
+ fileshare= FILE_SHARE_READ | FILE_SHARE_DELETE;
+ break;
+ case _SH_DENYRD: /* share write and delete access */
+ fileshare= FILE_SHARE_WRITE | FILE_SHARE_DELETE;
+ break;
+ case _SH_DENYNO: /* share read, write and delete access */
+ fileshare= FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
+ break;
+ case _SH_DENYRWD: /* exclusive access */
+ fileshare= 0L;
+ break;
+ case _SH_DENYWRD: /* share read access */
+ fileshare= FILE_SHARE_READ;
+ break;
+ case _SH_DENYRDD: /* share write access */
+ fileshare= FILE_SHARE_WRITE;
+ break;
+ case _SH_DENYDEL: /* share read and write access */
+ fileshare= FILE_SHARE_READ | FILE_SHARE_WRITE;
+ break;
+ default: /* error, bad shflag */
+ errno= EINVAL;
+ DBUG_RETURN(-1);
+ }
+
+ /* decode open/create method flags */
+ switch (oflag & (_O_CREAT | _O_EXCL | _O_TRUNC)) {
+ case 0:
+ case _O_EXCL: /* ignore EXCL w/o CREAT */
+ filecreate= OPEN_EXISTING;
+ break;
+
+ case _O_CREAT:
+ filecreate= OPEN_ALWAYS;
+ break;
+
+ case _O_CREAT | _O_EXCL:
+ case _O_CREAT | _O_TRUNC | _O_EXCL:
+ filecreate= CREATE_NEW;
+ break;
+
+ case _O_TRUNC:
+ case _O_TRUNC | _O_EXCL: /* ignore EXCL w/o CREAT */
+ filecreate= TRUNCATE_EXISTING;
+ break;
+
+ case _O_CREAT | _O_TRUNC:
+ filecreate= CREATE_ALWAYS;
+ break;
+
+ default:
+ /* this can't happen ... all cases are covered */
+ errno= EINVAL;
+ DBUG_RETURN(-1);
+ }
+
+ /* decode file attribute flags if _O_CREAT was specified */
+ fileattrib= FILE_ATTRIBUTE_NORMAL; /* default */
+ if (oflag & _O_CREAT)
+ {
+ _umask((mask= _umask(0)));
+
+ if (!((pmode & ~mask) & _S_IWRITE))
+ fileattrib= FILE_ATTRIBUTE_READONLY;
+ }
+
+ /* Set temporary file (delete-on-close) attribute if requested. */
+ if (oflag & _O_TEMPORARY)
+ {
+ fileattrib|= FILE_FLAG_DELETE_ON_CLOSE;
+ fileaccess|= DELETE;
+ }
+
+ /* Set temporary file (delay-flush-to-disk) attribute if requested.*/
+ if (oflag & _O_SHORT_LIVED)
+ fileattrib|= FILE_ATTRIBUTE_TEMPORARY;
+
+ /* Set sequential or random access attribute if requested. */
+ if (oflag & _O_SEQUENTIAL)
+ fileattrib|= FILE_FLAG_SEQUENTIAL_SCAN;
+ else if (oflag & _O_RANDOM)
+ fileattrib|= FILE_FLAG_RANDOM_ACCESS;
+
+ /* try to open/create the file */
+ if ((osfh= CreateFile(path, fileaccess, fileshare, &SecurityAttributes,
+ filecreate, fileattrib, NULL)) == INVALID_HANDLE_VALUE)
+ {
+ /*
+ OS call to open/create file failed! map the error, release
+ the lock, and return -1. note that it's not necessary to
+ call _free_osfhnd (it hasn't been used yet).
+ */
+ my_osmaperr(GetLastError()); /* map error */
+ DBUG_RETURN(-1); /* return error to caller */
+ }
+
+ if ((fh= my_open_osfhandle(osfh,
+ oflag & (_O_APPEND | _O_RDONLY | _O_TEXT))) == -1)
+ {
+ CloseHandle(osfh);
+ }
+
+ DBUG_RETURN(fh); /* return handle */
+}
+
+
+File my_win_open(const char *path, int flags)
+{
+ DBUG_ENTER("my_win_open");
+ DBUG_RETURN(my_win_sopen((char *) path, flags | _O_BINARY, _SH_DENYNO,
+ _S_IREAD | S_IWRITE));
+}
+
+
+int my_win_close(File fd)
+{
+ DBUG_ENTER("my_win_close");
+ if(CloseHandle(my_get_osfhandle(fd)))
+ {
+ invalidate_fd(fd);
+ DBUG_RETURN(0);
+ }
+ my_osmaperr(GetLastError());
+ DBUG_RETURN(-1);
+}
+
+
+size_t my_win_pread(File Filedes, uchar *Buffer, size_t Count, my_off_t offset)
+{
+ DWORD nBytesRead;
+ HANDLE hFile;
+ OVERLAPPED ov= {0};
+ LARGE_INTEGER li;
+
+ DBUG_ENTER("my_win_pread");
+
+ if(!Count)
+ DBUG_RETURN(0);
+#ifdef _WIN64
+ if(Count > UINT_MAX)
+ Count= UINT_MAX;
+#endif
+
+ hFile= (HANDLE)my_get_osfhandle(Filedes);
+ li.QuadPart= offset;
+ ov.Offset= li.LowPart;
+ ov.OffsetHigh= li.HighPart;
+
+ if(!ReadFile(hFile, Buffer, (DWORD)Count, &nBytesRead, &ov))
+ {
+ DWORD lastError= GetLastError();
+ /*
+ ERROR_BROKEN_PIPE is returned when no more data coming
+ through e.g. a command pipe in windows : see MSDN on ReadFile.
+ */
+ if(lastError == ERROR_HANDLE_EOF || lastError == ERROR_BROKEN_PIPE)
+ DBUG_RETURN(0); /*return 0 at EOF*/
+ my_osmaperr(lastError);
+ DBUG_RETURN((size_t)-1);
+ }
+ DBUG_RETURN(nBytesRead);
+}
+
+
+size_t my_win_read(File Filedes, uchar *Buffer, size_t Count)
+{
+ DWORD nBytesRead;
+ HANDLE hFile;
+
+ DBUG_ENTER("my_win_read");
+ if(!Count)
+ DBUG_RETURN(0);
+#ifdef _WIN64
+ if(Count > UINT_MAX)
+ Count= UINT_MAX;
+#endif
+
+ hFile= (HANDLE)my_get_osfhandle(Filedes);
+
+ if(!ReadFile(hFile, Buffer, (DWORD)Count, &nBytesRead, NULL))
+ {
+ DWORD lastError= GetLastError();
+ /*
+ ERROR_BROKEN_PIPE is returned when no more data coming
+ through e.g. a command pipe in windows : see MSDN on ReadFile.
+ */
+ if(lastError == ERROR_HANDLE_EOF || lastError == ERROR_BROKEN_PIPE)
+ DBUG_RETURN(0); /*return 0 at EOF*/
+ my_osmaperr(lastError);
+ DBUG_RETURN((size_t)-1);
+ }
+ DBUG_RETURN(nBytesRead);
+}
+
+
+size_t my_win_pwrite(File Filedes, const uchar *Buffer, size_t Count,
+ my_off_t offset)
+{
+ DWORD nBytesWritten;
+ HANDLE hFile;
+ OVERLAPPED ov= {0};
+ LARGE_INTEGER li;
+
+ DBUG_ENTER("my_win_pwrite");
+ DBUG_PRINT("my",("Filedes: %d, Buffer: %p, Count: %llu, offset: %llu",
+ Filedes, Buffer, (ulonglong)Count, (ulonglong)offset));
+
+ if(!Count)
+ DBUG_RETURN(0);
+
+#ifdef _WIN64
+ if(Count > UINT_MAX)
+ Count= UINT_MAX;
+#endif
+
+ hFile= (HANDLE)my_get_osfhandle(Filedes);
+ li.QuadPart= offset;
+ ov.Offset= li.LowPart;
+ ov.OffsetHigh= li.HighPart;
+
+ if(!WriteFile(hFile, Buffer, (DWORD)Count, &nBytesWritten, &ov))
+ {
+ my_osmaperr(GetLastError());
+ DBUG_RETURN((size_t)-1);
+ }
+ else
+ DBUG_RETURN(nBytesWritten);
+}
+
+
+my_off_t my_win_lseek(File fd, my_off_t pos, int whence)
+{
+ LARGE_INTEGER offset;
+ LARGE_INTEGER newpos;
+
+ DBUG_ENTER("my_win_lseek");
+
+ /* Check compatibility of Windows and Posix seek constants */
+ compile_time_assert(FILE_BEGIN == SEEK_SET && FILE_CURRENT == SEEK_CUR
+ && FILE_END == SEEK_END);
+
+ offset.QuadPart= pos;
+ if(!SetFilePointerEx(my_get_osfhandle(fd), offset, &newpos, whence))
+ {
+ my_osmaperr(GetLastError());
+ newpos.QuadPart= -1;
+ }
+ DBUG_RETURN(newpos.QuadPart);
+}
+
+
+#ifndef FILE_WRITE_TO_END_OF_FILE
+#define FILE_WRITE_TO_END_OF_FILE 0xffffffff
+#endif
+size_t my_win_write(File fd, const uchar *Buffer, size_t Count)
+{
+ DWORD nWritten;
+ OVERLAPPED ov;
+ OVERLAPPED *pov= NULL;
+ HANDLE hFile;
+
+ DBUG_ENTER("my_win_write");
+ DBUG_PRINT("my",("Filedes: %d, Buffer: %p, Count %llu", fd, Buffer,
+ (ulonglong)Count));
+
+ if(!Count)
+ DBUG_RETURN(0);
+
+#ifdef _WIN64
+ if(Count > UINT_MAX)
+ Count= UINT_MAX;
+#endif
+
+ if(my_get_open_flags(fd) & _O_APPEND)
+ {
+ /*
+ Atomic append to the end of file is is done by special initialization of
+ the OVERLAPPED structure. See MSDN WriteFile documentation for more info.
+ */
+ memset(&ov, 0, sizeof(ov));
+ ov.Offset= FILE_WRITE_TO_END_OF_FILE;
+ ov.OffsetHigh= -1;
+ pov= &ov;
+ }
+
+ hFile= my_get_osfhandle(fd);
+ if(!WriteFile(hFile, Buffer, (DWORD)Count, &nWritten, pov))
+ {
+ my_osmaperr(GetLastError());
+ DBUG_RETURN((size_t)-1);
+ }
+ DBUG_RETURN(nWritten);
+}
+
+
+int my_win_chsize(File fd, my_off_t newlength)
+{
+ HANDLE hFile;
+ LARGE_INTEGER length;
+ DBUG_ENTER("my_win_chsize");
+
+ hFile= (HANDLE) my_get_osfhandle(fd);
+ length.QuadPart= newlength;
+ if (!SetFilePointerEx(hFile, length , NULL , FILE_BEGIN))
+ goto err;
+ if (!SetEndOfFile(hFile))
+ goto err;
+ DBUG_RETURN(0);
+err:
+ my_osmaperr(GetLastError());
+ set_my_errno(errno);
+ DBUG_RETURN(-1);
+}
+
+
+/* Get the file descriptor for stdin,stdout or stderr */
+static File my_get_stdfile_descriptor(FILE *stream)
+{
+ HANDLE hFile;
+ DWORD nStdHandle;
+ DBUG_ENTER("my_get_stdfile_descriptor");
+
+ if(stream == stdin)
+ nStdHandle= STD_INPUT_HANDLE;
+ else if(stream == stdout)
+ nStdHandle= STD_OUTPUT_HANDLE;
+ else if(stream == stderr)
+ nStdHandle= STD_ERROR_HANDLE;
+ else
+ DBUG_RETURN(-1);
+
+ hFile= GetStdHandle(nStdHandle);
+ if(hFile != INVALID_HANDLE_VALUE)
+ DBUG_RETURN(my_open_osfhandle(hFile, 0));
+ DBUG_RETURN(-1);
+}
+
+
+File my_win_fileno(FILE *file)
+{
+ HANDLE hFile= (HANDLE)_get_osfhandle(fileno(file));
+ int retval= -1;
+ uint i;
+
+ DBUG_ENTER("my_win_fileno");
+
+ for(i= MY_FILE_MIN; i < my_file_limit; i++)
+ {
+ if(my_file_info[i].fhandle == hFile)
+ {
+ retval= i;
+ break;
+ }
+ }
+ if(retval == -1)
+ /* try std stream */
+ DBUG_RETURN(my_get_stdfile_descriptor(file));
+ DBUG_RETURN(retval);
+}
+
+
+FILE *my_win_fopen(const char *filename, const char *type)
+{
+ FILE *file;
+ int flags= 0;
+ DBUG_ENTER("my_win_open");
+
+ /*
+ If we are not creating, then we need to use my_access to make sure
+ the file exists since Windows doesn't handle files like "com1.sym"
+ very well
+ */
+ if (check_if_legal_filename(filename))
+ {
+ errno= EACCES;
+ DBUG_RETURN(NULL);
+ }
+
+ file= fopen(filename, type);
+ if(!file)
+ DBUG_RETURN(NULL);
+
+ if(strchr(type,'a') != NULL)
+ flags= O_APPEND;
+
+ /*
+ Register file handle in my_table_info.
+ Necessary for my_fileno()
+ */
+ if(my_open_osfhandle((HANDLE)_get_osfhandle(fileno(file)), flags) < 0)
+ {
+ fclose(file);
+ DBUG_RETURN(NULL);
+ }
+ DBUG_RETURN(file);
+}
+
+
+FILE * my_win_fdopen(File fd, const char *type)
+{
+ FILE *file;
+ int crt_fd;
+ int flags= 0;
+
+ DBUG_ENTER("my_win_fdopen");
+
+ if(strchr(type,'a') != NULL)
+ flags= O_APPEND;
+ /* Convert OS file handle to CRT file descriptor and then call fdopen*/
+ crt_fd= _open_osfhandle((intptr_t)my_get_osfhandle(fd), flags);
+ if(crt_fd < 0)
+ file= NULL;
+ else
+ file= fdopen(crt_fd, type);
+ DBUG_RETURN(file);
+}
+
+
+int my_win_fclose(FILE *file)
+{
+ File fd;
+
+ DBUG_ENTER("my_win_close");
+ fd= my_fileno(file);
+ if(fd < 0)
+ DBUG_RETURN(-1);
+ if(fclose(file) < 0)
+ DBUG_RETURN(-1);
+ invalidate_fd(fd);
+ DBUG_RETURN(0);
+}
+
+
+
+/*
+ Quick and dirty my_fstat() implementation for Windows.
+ Use CRT fstat on temporarily allocated file descriptor.
+ Patch file size, because size that fstat returns is not
+ reliable (may be outdated)
+*/
+int my_win_fstat(File fd, struct _stati64 *buf)
+{
+ int crt_fd;
+ int retval;
+ HANDLE hFile, hDup;
+
+ DBUG_ENTER("my_win_fstat");
+
+ hFile= my_get_osfhandle(fd);
+ if(!DuplicateHandle( GetCurrentProcess(), hFile, GetCurrentProcess(),
+ &hDup ,0,FALSE,DUPLICATE_SAME_ACCESS))
+ {
+ my_osmaperr(GetLastError());
+ DBUG_RETURN(-1);
+ }
+ if ((crt_fd= _open_osfhandle((intptr_t)hDup,0)) < 0)
+ DBUG_RETURN(-1);
+
+ retval= _fstati64(crt_fd, buf);
+ if(retval == 0)
+ {
+ /* File size returned by stat is not accurate (may be outdated), fix it*/
+ GetFileSizeEx(hDup, (PLARGE_INTEGER) (&(buf->st_size)));
+ }
+ _close(crt_fd);
+ DBUG_RETURN(retval);
+}
+
+
+
+int my_win_stat( const char *path, struct _stati64 *buf)
+{
+ DBUG_ENTER("my_win_stat");
+ if(_stati64( path, buf) == 0)
+ {
+ /* File size returned by stat is not accurate (may be outdated), fix it*/
+ WIN32_FILE_ATTRIBUTE_DATA data;
+ if (GetFileAttributesEx(path, GetFileExInfoStandard, &data))
+ {
+ LARGE_INTEGER li;
+ li.LowPart= data.nFileSizeLow;
+ li.HighPart= data.nFileSizeHigh;
+ buf->st_size= li.QuadPart;
+ }
+ DBUG_RETURN(0);
+ }
+ DBUG_RETURN(-1);
+}
+
+
+
+int my_win_fsync(File fd)
+{
+ DBUG_ENTER("my_win_fsync");
+ if(FlushFileBuffers(my_get_osfhandle(fd)))
+ DBUG_RETURN(0);
+ my_osmaperr(GetLastError());
+ DBUG_RETURN(-1);
+}
+
+
+
+int my_win_dup(File fd)
+{
+ HANDLE hDup;
+ DBUG_ENTER("my_win_dup");
+ if (DuplicateHandle(GetCurrentProcess(), my_get_osfhandle(fd),
+ GetCurrentProcess(), &hDup, 0, FALSE, DUPLICATE_SAME_ACCESS))
+ {
+ DBUG_RETURN(my_open_osfhandle(hDup, my_get_open_flags(fd)));
+ }
+ my_osmaperr(GetLastError());
+ DBUG_RETURN(-1);
+}
+
+#endif /*_WIN32*/
diff --git a/mysql/mysys/my_write.c b/mysql/mysys/my_write.c
new file mode 100644
index 0000000..d849949
--- /dev/null
+++ b/mysql/mysys/my_write.c
@@ -0,0 +1,129 @@
+/* Copyright (c) 2000, 2016, Oracle and/or its affiliates. All rights reserved.
+
+ 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; version 2 of the License.
+
+ 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include "mysys_priv.h"
+#include "my_sys.h"
+#include "mysys_err.h"
+#include <errno.h>
+#include "my_thread_local.h"
+
+
+/**
+ Write a chunk of bytes to a file
+
+ if (MyFlags & (MY_NABP | MY_FNABP))
+ @returns
+ 0 if Count == 0
+ On succes, 0
+ On failure, (size_t)-1 == MY_FILE_ERROR
+
+ otherwise
+ @returns
+ 0 if Count == 0
+ On success, the number of bytes written.
+ On partial success (if less than Count bytes could be written),
+ the actual number of bytes written.
+ On failure, (size_t)-1 == MY_FILE_ERROR
+*/
+size_t my_write(File Filedes, const uchar *Buffer, size_t Count, myf MyFlags)
+{
+ size_t writtenbytes;
+ size_t sum_written= 0;
+ uint errors= 0;
+ const size_t initial_count= Count;
+
+ DBUG_ENTER("my_write");
+ DBUG_PRINT("my",("fd: %d Buffer: %p Count: %lu MyFlags: %d",
+ Filedes, Buffer, (ulong) Count, MyFlags));
+
+ /* The behavior of write(fd, buf, 0) is not portable */
+ if (unlikely(!Count))
+ DBUG_RETURN(0);
+
+ DBUG_EXECUTE_IF ("simulate_no_free_space_error",
+ { DBUG_SET("+d,simulate_file_write_error");});
+ for (;;)
+ {
+ errno= 0;
+#ifdef _WIN32
+ writtenbytes= my_win_write(Filedes, Buffer, Count);
+#else
+ writtenbytes= write(Filedes, Buffer, Count);
+#endif
+ DBUG_EXECUTE_IF("simulate_file_write_error",
+ {
+ errno= ENOSPC;
+ writtenbytes= (size_t) -1;
+ });
+ if (writtenbytes == Count)
+ {
+ sum_written+= writtenbytes;
+ break;
+ }
+ if (writtenbytes != (size_t) -1)
+ { /* Safeguard */
+ sum_written+= writtenbytes;
+ Buffer+= writtenbytes;
+ Count-= writtenbytes;
+ }
+ set_my_errno(errno);
+ DBUG_PRINT("error",("Write only %ld bytes, error: %d",
+ (long) writtenbytes, my_errno()));
+ if (is_killed_hook(NULL))
+ MyFlags&= ~ MY_WAIT_IF_FULL; /* End if aborted by user */
+
+ if ((my_errno() == ENOSPC || my_errno() == EDQUOT) &&
+ (MyFlags & MY_WAIT_IF_FULL))
+ {
+ wait_for_free_space(my_filename(Filedes), errors);
+ errors++;
+ DBUG_EXECUTE_IF("simulate_no_free_space_error",
+ { DBUG_SET("-d,simulate_file_write_error");});
+ continue;
+ }
+
+ if (writtenbytes != 0 && writtenbytes != (size_t) -1)
+ continue; /* Retry if something written */
+ else if (my_errno() == EINTR)
+ {
+ DBUG_PRINT("debug", ("my_write() was interrupted and returned %ld",
+ (long) writtenbytes));
+ continue; /* Interrupted, retry */
+ }
+ else if (writtenbytes == 0 && !errors++) /* Retry once */
+ {
+ /* We may come here if the file quota is exeeded */
+ continue;
+ }
+ break;
+ }
+ if (MyFlags & (MY_NABP | MY_FNABP))
+ {
+ if (sum_written == initial_count)
+ DBUG_RETURN(0); /* Want only errors, not bytes written */
+ if (MyFlags & (MY_WME | MY_FAE | MY_FNABP))
+ {
+ char errbuf[MYSYS_STRERROR_SIZE];
+ my_error(EE_WRITE, MYF(0), my_filename(Filedes),
+ my_errno(), my_strerror(errbuf, sizeof(errbuf), my_errno()));
+ }
+ DBUG_RETURN(MY_FILE_ERROR);
+ }
+
+ if (sum_written == 0)
+ DBUG_RETURN(MY_FILE_ERROR);
+
+ DBUG_RETURN(sum_written);
+} /* my_write */
diff --git a/mysql/mysys/mysys_priv.h b/mysql/mysys/mysys_priv.h
new file mode 100644
index 0000000..8b7a554
--- /dev/null
+++ b/mysql/mysys/mysys_priv.h
@@ -0,0 +1,130 @@
+/* Copyright (c) 2000, 2015, Oracle and/or its affiliates. All rights reserved.
+
+ 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; version 2 of the License.
+
+ 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#ifndef MYSYS_PRIV_INCLUDED
+#define MYSYS_PRIV_INCLUDED
+
+#include "my_global.h"
+#include "mysql/psi/mysql_thread.h"
+
+#ifdef HAVE_PSI_INTERFACE
+
+#include <mysql/psi/mysql_file.h>
+#include <mysql/psi/mysql_thread.h>
+
+C_MODE_START
+
+extern PSI_mutex_key key_BITMAP_mutex, key_IO_CACHE_append_buffer_lock,
+ key_IO_CACHE_SHARE_mutex, key_KEY_CACHE_cache_lock,
+ key_THR_LOCK_charset, key_THR_LOCK_heap,
+ key_THR_LOCK_lock, key_THR_LOCK_malloc,
+ key_THR_LOCK_mutex, key_THR_LOCK_myisam, key_THR_LOCK_net,
+ key_THR_LOCK_open, key_THR_LOCK_threads,
+ key_TMPDIR_mutex, key_THR_LOCK_myisam_mmap;
+
+extern PSI_rwlock_key key_SAFE_HASH_lock;
+
+extern PSI_cond_key key_IO_CACHE_SHARE_cond,
+ key_IO_CACHE_SHARE_cond_writer,
+ key_THR_COND_threads;
+
+#endif /* HAVE_PSI_INTERFACE */
+
+extern PSI_stage_info stage_waiting_for_table_level_lock;
+
+extern mysql_mutex_t THR_LOCK_malloc, THR_LOCK_open, THR_LOCK_keycache;
+extern mysql_mutex_t THR_LOCK_lock, THR_LOCK_net;
+extern mysql_mutex_t THR_LOCK_charset;
+
+#ifdef HAVE_PSI_INTERFACE
+#ifdef HAVE_LINUX_LARGE_PAGES
+extern PSI_file_key key_file_proc_meminfo;
+#endif /* HAVE_LINUX_LARGE_PAGES */
+extern PSI_file_key key_file_charset;
+
+C_MODE_END
+
+#endif /* HAVE_PSI_INTERFACE */
+
+/* These keys are always defined. */
+
+C_MODE_START
+
+extern PSI_memory_key key_memory_charset_file;
+extern PSI_memory_key key_memory_charset_loader;
+extern PSI_memory_key key_memory_lf_node;
+extern PSI_memory_key key_memory_lf_dynarray;
+extern PSI_memory_key key_memory_lf_slist;
+extern PSI_memory_key key_memory_LIST;
+extern PSI_memory_key key_memory_IO_CACHE;
+extern PSI_memory_key key_memory_KEY_CACHE;
+extern PSI_memory_key key_memory_SAFE_HASH_ENTRY;
+extern PSI_memory_key key_memory_MY_TMPDIR_full_list;
+extern PSI_memory_key key_memory_MY_BITMAP_bitmap;
+extern PSI_memory_key key_memory_my_compress_alloc;
+extern PSI_memory_key key_memory_pack_frm;
+extern PSI_memory_key key_memory_my_err_head;
+extern PSI_memory_key key_memory_my_file_info;
+extern PSI_memory_key key_memory_MY_DIR;
+extern PSI_memory_key key_memory_MY_STAT;
+extern PSI_memory_key key_memory_QUEUE;
+extern PSI_memory_key key_memory_DYNAMIC_STRING;
+extern PSI_memory_key key_memory_TREE;
+extern PSI_memory_key key_memory_defaults;
+
+#ifdef _WIN32
+extern PSI_memory_key key_memory_win_SECURITY_ATTRIBUTES;
+extern PSI_memory_key key_memory_win_PACL;
+extern PSI_memory_key key_memory_win_IP_ADAPTER_ADDRESSES;
+#endif
+
+C_MODE_END
+
+/*
+ EDQUOT is used only in 3 C files only in mysys/. If it does not exist on
+ system, we set it to some value which can never happen.
+*/
+#ifndef EDQUOT
+#define EDQUOT (-1)
+#endif
+
+void my_error_unregister_all(void);
+
+#ifdef _WIN32
+#include <sys/stat.h>
+/* my_winfile.c exports, should not be used outside mysys */
+extern File my_win_open(const char *path, int oflag);
+extern int my_win_close(File fd);
+extern size_t my_win_read(File fd, uchar *buffer, size_t count);
+extern size_t my_win_write(File fd, const uchar *buffer, size_t count);
+extern size_t my_win_pread(File fd, uchar *buffer, size_t count,
+ my_off_t offset);
+extern size_t my_win_pwrite(File fd, const uchar *buffer, size_t count,
+ my_off_t offset);
+extern my_off_t my_win_lseek(File fd, my_off_t pos, int whence);
+extern int my_win_chsize(File fd, my_off_t newlength);
+extern FILE* my_win_fopen(const char *filename, const char *type);
+extern File my_win_fclose(FILE *file);
+extern File my_win_fileno(FILE *file);
+extern FILE* my_win_fdopen(File Filedes, const char *type);
+extern int my_win_stat(const char *path, struct _stati64 *buf);
+extern int my_win_fstat(File fd, struct _stati64 *buf);
+extern int my_win_fsync(File fd);
+extern File my_win_dup(File fd);
+extern File my_win_sopen(const char *path, int oflag, int shflag, int perm);
+extern File my_open_osfhandle(HANDLE handle, int oflag);
+#endif
+
+#endif /* MYSYS_PRIV_INCLUDED */
diff --git a/mysql/mysys/posix_timers.c b/mysql/mysys/posix_timers.c
new file mode 100644
index 0000000..2215a66
--- /dev/null
+++ b/mysql/mysys/posix_timers.c
@@ -0,0 +1,395 @@
+/* Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved.
+
+ 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; version 2 of the License.
+
+ 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+
+#include "my_global.h"
+#include "my_thread.h" /* my_thread_init, my_thread_end */
+#include "my_sys.h" /* my_message_local */
+#include "my_timer.h" /* my_timer_t */
+
+#include <string.h> /* memset */
+#include <signal.h>
+
+#if defined(HAVE_SIGEV_THREAD_ID)
+#include <sys/syscall.h> /* SYS_gettid */
+
+#ifndef sigev_notify_thread_id
+#define sigev_notify_thread_id _sigev_un._tid
+#endif
+
+#define MY_TIMER_EVENT_SIGNO (SIGRTMIN)
+#define MY_TIMER_KILL_SIGNO (SIGRTMIN+1)
+
+/* Timer thread ID (TID). */
+static pid_t timer_notify_thread_id;
+
+#elif defined(HAVE_SIGEV_PORT)
+#include <port.h>
+
+int port_id= -1;
+
+#endif
+
+/* Timer thread object. */
+static my_thread_handle timer_notify_thread;
+
+#if defined(HAVE_SIGEV_THREAD_ID)
+/**
+ Timer expiration notification thread.
+
+ @param arg Barrier object.
+*/
+
+static void *
+timer_notify_thread_func(void *arg)
+{
+ sigset_t set;
+ siginfo_t info;
+ my_timer_t *timer;
+ pthread_barrier_t *barrier= arg;
+
+ my_thread_init();
+
+ sigemptyset(&set);
+ sigaddset(&set, MY_TIMER_EVENT_SIGNO);
+ sigaddset(&set, MY_TIMER_KILL_SIGNO);
+
+ /* Get the thread ID of the current thread. */
+ timer_notify_thread_id= (pid_t) syscall(SYS_gettid);
+
+ /* Wake up parent thread, timer_notify_thread_id is available. */
+ pthread_barrier_wait(barrier);
+
+ while (1)
+ {
+ if (sigwaitinfo(&set, &info) < 0)
+ continue;
+
+ if (info.si_signo == MY_TIMER_EVENT_SIGNO)
+ {
+ timer= (my_timer_t*)info.si_value.sival_ptr;
+ timer->notify_function(timer);
+ }
+ else if (info.si_signo == MY_TIMER_KILL_SIGNO)
+ break;
+ }
+
+ my_thread_end();
+
+ return NULL;
+}
+
+
+/**
+ Create a helper thread to dispatch timer expiration notifications.
+
+ @return On success, 0. On error, -1 is returned.
+*/
+
+static int
+start_helper_thread(void)
+{
+ pthread_barrier_t barrier;
+
+ if (pthread_barrier_init(&barrier, NULL, 2))
+ {
+ my_message_local(ERROR_LEVEL,
+ "Failed to initialize pthread barrier. errno=%d", errno);
+ return -1;
+ }
+
+ if (mysql_thread_create(key_thread_timer_notifier, &timer_notify_thread,
+ NULL, timer_notify_thread_func, &barrier))
+ {
+ my_message_local(ERROR_LEVEL,
+ "Failed to create timer notify thread (errno= %d).",
+ errno);
+ pthread_barrier_destroy(&barrier);
+ return -1;
+ }
+
+ pthread_barrier_wait(&barrier);
+ pthread_barrier_destroy(&barrier);
+
+ return 0;
+}
+
+
+/**
+ Initialize internal components.
+
+ @return On success, 0.
+ On error, -1 is returned, and errno is set to indicate the error.
+*/
+
+int
+my_timer_initialize(void)
+{
+ int rc;
+ sigset_t set, old_set;
+
+ if (sigfillset(&set))
+ {
+ my_message_local(ERROR_LEVEL,
+ "Failed to intialize signal set (errno=%d).", errno);
+ return -1;
+ }
+
+ /*
+ Temporarily block all signals. New thread will inherit signal
+ mask of the current thread.
+ */
+ if (pthread_sigmask(SIG_BLOCK, &set, &old_set))
+ return -1;
+
+ /* Create a helper thread. */
+ rc= start_helper_thread();
+
+ /* Restore the signal mask. */
+ pthread_sigmask(SIG_SETMASK, &old_set, NULL);
+
+ return rc;
+}
+
+
+/**
+ Release any resources that were allocated as part of initialization.
+*/
+
+void
+my_timer_deinitialize(void)
+{
+ /* Kill helper thread. */
+ pthread_kill(timer_notify_thread.thread, MY_TIMER_KILL_SIGNO);
+
+ /* Wait for helper thread termination. */
+ my_thread_join(&timer_notify_thread, NULL);
+}
+
+
+/**
+ Create a timer object.
+
+ @param timer Location where the timer ID is returned.
+
+ @return On success, 0.
+ On error, -1 is returned, and errno is set to indicate the error.
+*/
+
+int
+my_timer_create(my_timer_t *timer)
+{
+ struct sigevent sigev;
+
+ memset(&sigev, 0, sizeof(sigev));
+
+ sigev.sigev_value.sival_ptr= timer;
+ sigev.sigev_signo= MY_TIMER_EVENT_SIGNO;
+ sigev.sigev_notify= SIGEV_SIGNAL | SIGEV_THREAD_ID;
+ sigev.sigev_notify_thread_id= timer_notify_thread_id;
+
+ return timer_create(CLOCK_MONOTONIC, &sigev, &timer->id);
+}
+#elif defined(HAVE_SIGEV_PORT)
+/**
+ Timer expiration notification thread.
+
+ @param arg Barrier object.
+*/
+
+static void *
+timer_notify_thread_func(void *arg MY_ATTRIBUTE((unused)))
+{
+ port_event_t port_event;
+ my_timer_t *timer;
+
+ my_thread_init();
+
+ while (1)
+ {
+ if (port_get(port_id, &port_event, NULL))
+ break;
+
+ if (port_event.portev_source != PORT_SOURCE_TIMER)
+ continue;
+
+ timer= (my_timer_t*)port_event.portev_user;
+ timer->notify_function(timer);
+ }
+
+ my_thread_end();
+
+ return NULL;
+}
+
+
+/**
+ Create a helper thread to dispatch timer expiration notifications.
+
+ @return On success, 0. On error, -1 is returned.
+*/
+
+static int
+start_helper_thread(void)
+{
+ if (mysql_thread_create(key_thread_timer_notifier, &timer_notify_thread,
+ NULL, timer_notify_thread_func, NULL))
+ {
+ my_message_local(ERROR_LEVEL,
+ "Failed to create timer notify thread (errno= %d).",
+ errno);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+/**
+ Initialize internal components.
+
+ @return On success, 0.
+ On error, -1 is returned, and errno is set to indicate the error.
+*/
+
+int
+my_timer_initialize(void)
+{
+ int rc;
+
+ if ((port_id= port_create()) < 0)
+ {
+ my_message_local(ERROR_LEVEL, "Failed to create port (errno= %d).", errno);
+ return -1;
+ }
+
+ /* Create a helper thread. */
+ rc= start_helper_thread();
+
+ return rc;
+}
+
+
+/**
+ Release any resources that were allocated as part of initialization.
+*/
+
+void
+my_timer_deinitialize(void)
+{
+ DBUG_ASSERT(port_id >= 0);
+
+ // close port
+ close(port_id);
+
+ /* Wait for helper thread termination. */
+ my_thread_join(&timer_notify_thread, NULL);
+}
+
+
+/**
+ Create a timer object.
+
+ @param timer Location where the timer ID is returned.
+
+ @return On success, 0.
+ On error, -1 is returned, and errno is set to indicate the error.
+*/
+
+int
+my_timer_create(my_timer_t *timer)
+{
+ struct sigevent sigev;
+ port_notify_t port_notify;
+
+ port_notify.portnfy_port= port_id;
+ port_notify.portnfy_user= timer;
+
+ memset(&sigev, 0, sizeof(sigev));
+ sigev.sigev_value.sival_ptr= &port_notify;
+ sigev.sigev_notify= SIGEV_PORT;
+
+ return timer_create(CLOCK_REALTIME, &sigev, &timer->id);
+}
+#endif
+
+
+/**
+ Set the time until the next expiration of the timer.
+
+ @param timer Timer object.
+ @param time Amount of time (in milliseconds) before the timer expires.
+
+ @return On success, 0.
+ On error, -1 is returned, and errno is set to indicate the error.
+*/
+
+int
+my_timer_set(my_timer_t *timer, unsigned long time)
+{
+ const struct itimerspec spec= {
+ .it_interval= {.tv_sec= 0, .tv_nsec= 0},
+ .it_value= {.tv_sec= time / 1000,
+ .tv_nsec= (time % 1000) * 1000000}
+ };
+
+ return timer_settime(timer->id, 0, &spec, NULL);
+}
+
+
+/**
+ Cancel the timer.
+
+ @param timer Timer object.
+ @param state The state of the timer at the time of cancellation, either
+ signaled (false) or nonsignaled (true).
+
+ @return On success, 0.
+ On error, -1 is returned, and errno is set to indicate the error.
+*/
+
+int
+my_timer_cancel(my_timer_t *timer, int *state)
+{
+ int status;
+ struct itimerspec old_spec;
+
+ /* A zeroed initial expiration value disarms the timer. */
+ const struct timespec zero_time= { .tv_sec= 0, .tv_nsec= 0 };
+ const struct itimerspec zero_spec= { .it_value= zero_time };
+
+ /*
+ timer_settime returns the amount of time before the timer
+ would have expired or zero if the timer was disarmed.
+ */
+ if (! (status= timer_settime(timer->id, 0, &zero_spec, &old_spec)))
+ *state= (old_spec.it_value.tv_sec || old_spec.it_value.tv_nsec);
+
+ return status;
+}
+
+
+/**
+ Delete a timer object.
+
+ @param timer Timer object.
+*/
+
+void
+my_timer_delete(my_timer_t *timer)
+{
+ timer_delete(timer->id);
+}
+
diff --git a/mysql/mysys/psi_noop.c b/mysql/mysys/psi_noop.c
new file mode 100644
index 0000000..6835096
--- /dev/null
+++ b/mysql/mysys/psi_noop.c
@@ -0,0 +1,1040 @@
+/* Copyright (c) 2011, 2016, Oracle and/or its affiliates. All rights reserved.
+
+ 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; version 2 of the License.
+
+ 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,
+ 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA */
+
+/*
+ Always provide the noop performance interface, for plugins.
+*/
+
+#define USE_PSI_V1
+#define HAVE_PSI_INTERFACE
+
+#include "my_global.h"
+#include "my_thread.h"
+#include "my_sys.h"
+#include "mysql/psi/psi.h"
+
+C_MODE_START
+
+#define NNN MY_ATTRIBUTE((unused))
+
+static void register_mutex_noop(const char *category NNN,
+ PSI_mutex_info *info NNN,
+ int count NNN)
+{
+ return;
+}
+
+static void register_rwlock_noop(const char *category NNN,
+ PSI_rwlock_info *info NNN,
+ int count NNN)
+{
+ return;
+}
+
+static void register_cond_noop(const char *category NNN,
+ PSI_cond_info *info NNN,
+ int count NNN)
+{
+ return;
+}
+
+static void register_thread_noop(const char *category NNN,
+ PSI_thread_info *info NNN,
+ int count NNN)
+{
+ return;
+}
+
+static void register_file_noop(const char *category NNN,
+ PSI_file_info *info NNN,
+ int count NNN)
+{
+ return;
+}
+
+static void register_stage_noop(const char *category NNN,
+ PSI_stage_info **info_array NNN,
+ int count NNN)
+{
+ return;
+}
+
+static void register_statement_noop(const char *category NNN,
+ PSI_statement_info *info NNN,
+ int count NNN)
+{
+ return;
+}
+
+static void register_socket_noop(const char *category NNN,
+ PSI_socket_info *info NNN,
+ int count NNN)
+{
+ return;
+}
+
+static PSI_mutex*
+init_mutex_noop(PSI_mutex_key key NNN, const void *identity NNN)
+{
+ return NULL;
+}
+
+static void destroy_mutex_noop(PSI_mutex* mutex NNN)
+{
+ return;
+}
+
+static PSI_rwlock*
+init_rwlock_noop(PSI_rwlock_key key NNN, const void *identity NNN)
+{
+ return NULL;
+}
+
+static void destroy_rwlock_noop(PSI_rwlock* rwlock NNN)
+{
+ return;
+}
+
+static PSI_cond*
+init_cond_noop(PSI_cond_key key NNN, const void *identity NNN)
+{
+ return NULL;
+}
+
+static void destroy_cond_noop(PSI_cond* cond NNN)
+{
+ return;
+}
+
+static PSI_socket*
+init_socket_noop(PSI_socket_key key NNN, const my_socket *fd NNN,
+ const struct sockaddr *addr NNN, socklen_t addr_len NNN)
+{
+ return NULL;
+}
+
+static void destroy_socket_noop(PSI_socket* socket NNN)
+{
+ return;
+}
+
+static PSI_table_share*
+get_table_share_noop(my_bool temporary NNN, struct TABLE_SHARE *share NNN)
+{
+ return NULL;
+}
+
+static void release_table_share_noop(PSI_table_share* share NNN)
+{
+ return;
+}
+
+static void
+drop_table_share_noop(my_bool temporary NNN, const char *schema_name NNN,
+ int schema_name_length NNN, const char *table_name NNN,
+ int table_name_length NNN)
+{
+ return;
+}
+
+static PSI_table*
+open_table_noop(PSI_table_share *share NNN, const void *identity NNN)
+{
+ return NULL;
+}
+
+static void unbind_table_noop(PSI_table *table NNN)
+{
+ return;
+}
+
+static PSI_table*
+rebind_table_noop(PSI_table_share *share NNN,
+ const void *identity NNN,
+ PSI_table *table NNN)
+{
+ return NULL;
+}
+
+static void close_table_noop(struct TABLE_SHARE *share NNN,
+ PSI_table *table NNN)
+{
+ return;
+}
+
+static void create_file_noop(PSI_file_key key NNN,
+ const char *name NNN, File file NNN)
+{
+ return;
+}
+
+static int spawn_thread_noop(PSI_thread_key key NNN,
+ my_thread_handle *thread NNN,
+ const my_thread_attr_t *attr NNN,
+ my_start_routine start_routine, void *arg NNN)
+{
+ return my_thread_create(thread, attr, start_routine, arg);
+}
+
+static PSI_thread*
+new_thread_noop(PSI_thread_key key NNN,
+ const void *identity NNN, ulonglong thread_id NNN)
+{
+ return NULL;
+}
+
+static void set_thread_id_noop(PSI_thread *thread NNN, ulonglong id NNN)
+{
+ return;
+}
+
+static void set_thread_THD_noop(PSI_thread *thread NNN, THD *thd NNN)
+{
+ return;
+}
+
+static void set_thread_os_id_noop(PSI_thread *thread NNN)
+{
+ return;
+}
+
+static PSI_thread*
+get_thread_noop(void NNN)
+{
+ return NULL;
+}
+
+static void set_thread_user_noop(const char *user NNN, int user_len NNN)
+{
+ return;
+}
+
+static void set_thread_user_host_noop(const char *user NNN, int user_len NNN,
+ const char *host NNN, int host_len NNN)
+{
+ return;
+}
+
+static void set_thread_db_noop(const char* db NNN, int db_len NNN)
+{
+ return;
+}
+
+static void set_thread_command_noop(int command NNN)
+{
+ return;
+}
+
+static void set_connection_type_noop(opaque_vio_type conn_type NNN)
+{
+ return;
+}
+
+static void set_thread_start_time_noop(time_t start_time NNN)
+{
+ return;
+}
+
+static void set_thread_state_noop(const char* state NNN)
+{
+ return;
+}
+
+static void set_thread_info_noop(const char* info NNN, uint info_len NNN)
+{
+ return;
+}
+
+static void set_thread_noop(PSI_thread* thread NNN)
+{
+ return;
+}
+
+static void delete_current_thread_noop(void)
+{
+ return;
+}
+
+static void delete_thread_noop(PSI_thread *thread NNN)
+{
+ return;
+}
+
+static PSI_file_locker*
+get_thread_file_name_locker_noop(PSI_file_locker_state *state NNN,
+ PSI_file_key key NNN,
+ enum PSI_file_operation op NNN,
+ const char *name NNN, const void *identity NNN)
+{
+ return NULL;
+}
+
+static PSI_file_locker*
+get_thread_file_stream_locker_noop(PSI_file_locker_state *state NNN,
+ PSI_file *file NNN,
+ enum PSI_file_operation op NNN)
+{
+ return NULL;
+}
+
+
+static PSI_file_locker*
+get_thread_file_descriptor_locker_noop(PSI_file_locker_state *state NNN,
+ File file NNN,
+ enum PSI_file_operation op NNN)
+{
+ return NULL;
+}
+
+static void unlock_mutex_noop(PSI_mutex *mutex NNN)
+{
+ return;
+}
+
+static void unlock_rwlock_noop(PSI_rwlock *rwlock NNN)
+{
+ return;
+}
+
+static void signal_cond_noop(PSI_cond* cond NNN)
+{
+ return;
+}
+
+static void broadcast_cond_noop(PSI_cond* cond NNN)
+{
+ return;
+}
+
+static PSI_idle_locker*
+start_idle_wait_noop(PSI_idle_locker_state* state NNN,
+ const char *src_file NNN, uint src_line NNN)
+{
+ return NULL;
+}
+
+static void end_idle_wait_noop(PSI_idle_locker* locker NNN)
+{
+ return;
+}
+
+static PSI_mutex_locker*
+start_mutex_wait_noop(PSI_mutex_locker_state *state NNN,
+ PSI_mutex *mutex NNN,
+ PSI_mutex_operation op NNN,
+ const char *src_file NNN, uint src_line NNN)
+{
+ return NULL;
+}
+
+static void end_mutex_wait_noop(PSI_mutex_locker* locker NNN, int rc NNN)
+{
+ return;
+}
+
+
+static PSI_rwlock_locker*
+start_rwlock_rdwait_noop(struct PSI_rwlock_locker_state_v1 *state NNN,
+ struct PSI_rwlock *rwlock NNN,
+ enum PSI_rwlock_operation op NNN,
+ const char *src_file NNN, uint src_line NNN)
+{
+ return NULL;
+}
+
+static void end_rwlock_rdwait_noop(PSI_rwlock_locker* locker NNN, int rc NNN)
+{
+ return;
+}
+
+static struct PSI_rwlock_locker*
+start_rwlock_wrwait_noop(struct PSI_rwlock_locker_state_v1 *state NNN,
+ struct PSI_rwlock *rwlock NNN,
+ enum PSI_rwlock_operation op NNN,
+ const char *src_file NNN, uint src_line NNN)
+{
+ return NULL;
+}
+
+static void end_rwlock_wrwait_noop(PSI_rwlock_locker* locker NNN, int rc NNN)
+{
+ return;
+}
+
+static struct PSI_cond_locker*
+start_cond_wait_noop(struct PSI_cond_locker_state_v1 *state NNN,
+ struct PSI_cond *cond NNN,
+ struct PSI_mutex *mutex NNN,
+ enum PSI_cond_operation op NNN,
+ const char *src_file NNN, uint src_line NNN)
+{
+ return NULL;
+}
+
+static void end_cond_wait_noop(PSI_cond_locker* locker NNN, int rc NNN)
+{
+ return;
+}
+
+static struct PSI_table_locker*
+start_table_io_wait_noop(struct PSI_table_locker_state *state NNN,
+ struct PSI_table *table NNN,
+ enum PSI_table_io_operation op NNN,
+ uint index NNN,
+ const char *src_file NNN, uint src_line NNN)
+{
+ return NULL;
+}
+
+static void end_table_io_wait_noop(PSI_table_locker* locker NNN,
+ ulonglong numrows NNN)
+{
+ return;
+}
+
+static struct PSI_table_locker*
+start_table_lock_wait_noop(struct PSI_table_locker_state *state NNN,
+ struct PSI_table *table NNN,
+ enum PSI_table_lock_operation op NNN,
+ ulong flags NNN,
+ const char *src_file NNN, uint src_line NNN)
+{
+ return NULL;
+}
+
+static void end_table_lock_wait_noop(PSI_table_locker* locker NNN)
+{
+ return;
+}
+
+static void start_file_open_wait_noop(PSI_file_locker *locker NNN,
+ const char *src_file NNN,
+ uint src_line NNN)
+{
+ return;
+}
+
+static PSI_file* end_file_open_wait_noop(PSI_file_locker *locker NNN,
+ void *result NNN)
+{
+ return NULL;
+}
+
+static void end_file_open_wait_and_bind_to_descriptor_noop
+ (PSI_file_locker *locker NNN, File file NNN)
+{
+ return;
+}
+
+static void end_temp_file_open_wait_and_bind_to_descriptor_noop
+ (PSI_file_locker *locker NNN, File file NNN, const char *filaneme NNN)
+{
+ return;
+}
+
+static void start_file_wait_noop(PSI_file_locker *locker NNN,
+ size_t count NNN,
+ const char *src_file NNN,
+ uint src_line NNN)
+{
+ return;
+}
+
+static void end_file_wait_noop(PSI_file_locker *locker NNN,
+ size_t count NNN)
+{
+ return;
+}
+
+static void start_file_close_wait_noop(PSI_file_locker *locker NNN,
+ const char *src_file NNN,
+ uint src_line NNN)
+{
+ return;
+}
+
+static void end_file_close_wait_noop(PSI_file_locker *locker NNN,
+ int result NNN)
+{
+ return;
+}
+
+static PSI_stage_progress*
+start_stage_noop(PSI_stage_key key NNN,
+ const char *src_file NNN, int src_line NNN)
+{
+ return NULL;
+}
+
+static PSI_stage_progress*
+get_current_stage_progress_noop()
+{
+ return NULL;
+}
+
+static void end_stage_noop(void)
+{
+ return;
+}
+
+static PSI_statement_locker*
+get_thread_statement_locker_noop(PSI_statement_locker_state *state NNN,
+ PSI_statement_key key NNN,
+ const void *charset NNN,
+ PSI_sp_share *sp_share NNN)
+{
+ return NULL;
+}
+
+static PSI_statement_locker*
+refine_statement_noop(PSI_statement_locker *locker NNN,
+ PSI_statement_key key NNN)
+{
+ return NULL;
+}
+
+static void start_statement_noop(PSI_statement_locker *locker NNN,
+ const char *db NNN, uint db_len NNN,
+ const char *src_file NNN, uint src_line NNN)
+{
+ return;
+}
+
+static void set_statement_text_noop(PSI_statement_locker *locker NNN,
+ const char *text NNN, uint text_len NNN)
+{
+ return;
+}
+
+static void set_statement_lock_time_noop(PSI_statement_locker *locker NNN,
+ ulonglong count NNN)
+{
+ return;
+}
+
+static void set_statement_rows_sent_noop(PSI_statement_locker *locker NNN,
+ ulonglong count NNN)
+{
+ return;
+}
+
+static void set_statement_rows_examined_noop(PSI_statement_locker *locker NNN,
+ ulonglong count NNN)
+{
+ return;
+}
+
+static void inc_statement_created_tmp_disk_tables_noop(PSI_statement_locker *locker NNN,
+ ulong count NNN)
+{
+ return;
+}
+
+static void inc_statement_created_tmp_tables_noop(PSI_statement_locker *locker NNN,
+ ulong count NNN)
+{
+ return;
+}
+
+static void inc_statement_select_full_join_noop(PSI_statement_locker *locker NNN,
+ ulong count NNN)
+{
+ return;
+}
+
+static void inc_statement_select_full_range_join_noop(PSI_statement_locker *locker NNN,
+ ulong count NNN)
+{
+ return;
+}
+
+static void inc_statement_select_range_noop(PSI_statement_locker *locker NNN,
+ ulong count NNN)
+{
+ return;
+}
+
+static void inc_statement_select_range_check_noop(PSI_statement_locker *locker NNN,
+ ulong count NNN)
+{
+ return;
+}
+
+static void inc_statement_select_scan_noop(PSI_statement_locker *locker NNN,
+ ulong count NNN)
+{
+ return;
+}
+
+static void inc_statement_sort_merge_passes_noop(PSI_statement_locker *locker NNN,
+ ulong count NNN)
+{
+ return;
+}
+
+static void inc_statement_sort_range_noop(PSI_statement_locker *locker NNN,
+ ulong count NNN)
+{
+ return;
+}
+
+static void inc_statement_sort_rows_noop(PSI_statement_locker *locker NNN,
+ ulong count NNN)
+{
+ return;
+}
+
+static void inc_statement_sort_scan_noop(PSI_statement_locker *locker NNN,
+ ulong count NNN)
+{
+ return;
+}
+
+static void set_statement_no_index_used_noop(PSI_statement_locker *locker NNN)
+{
+ return;
+}
+
+static void set_statement_no_good_index_used_noop(PSI_statement_locker *locker NNN)
+{
+ return;
+}
+
+static void end_statement_noop(PSI_statement_locker *locker NNN,
+ void *stmt_da NNN)
+{
+ return;
+}
+
+static PSI_transaction_locker*
+get_thread_transaction_locker_noop(PSI_transaction_locker_state *state NNN,
+ const void *xid NNN,
+ const ulonglong *trxid NNN,
+ int isolation_level NNN,
+ my_bool read_only NNN,
+ my_bool autocommit NNN)
+{
+ return NULL;
+}
+
+static void start_transaction_noop(PSI_transaction_locker *locker NNN,
+ const char *src_file NNN, uint src_line NNN)
+{
+ return;
+}
+
+static void set_transaction_xid_noop(PSI_transaction_locker *locker NNN,
+ const void *xid NNN,
+ int xa_state NNN)
+{
+ return;
+}
+
+static void set_transaction_xa_state_noop(PSI_transaction_locker *locker NNN,
+ int xa_state NNN)
+{
+ return;
+}
+
+static void set_transaction_gtid_noop(PSI_transaction_locker *locker NNN,
+ const void *sid NNN,
+ const void *gtid_spec NNN)
+{
+ return;
+}
+
+static void set_transaction_trxid_noop(PSI_transaction_locker *locker NNN,
+ const ulonglong *trxid NNN)
+{
+ return;
+}
+
+static void inc_transaction_savepoints_noop(PSI_transaction_locker *locker NNN,
+ ulong count NNN)
+{
+ return;
+}
+
+static void inc_transaction_rollback_to_savepoint_noop(PSI_transaction_locker *locker NNN,
+ ulong count NNN)
+{
+ return;
+}
+
+static void inc_transaction_release_savepoint_noop(PSI_transaction_locker *locker NNN,
+ ulong count NNN)
+{
+ return;
+}
+
+static void end_transaction_noop(PSI_transaction_locker *locker NNN,
+ my_bool commit NNN)
+{
+ return;
+}
+
+static PSI_socket_locker*
+start_socket_wait_noop(PSI_socket_locker_state *state NNN,
+ PSI_socket *socket NNN,
+ PSI_socket_operation op NNN,
+ size_t count NNN,
+ const char *src_file NNN,
+ uint src_line NNN)
+{
+ return NULL;
+}
+
+static void end_socket_wait_noop(PSI_socket_locker *locker NNN,
+ size_t count NNN)
+{
+ return;
+}
+
+static void set_socket_state_noop(PSI_socket *socket NNN,
+ enum PSI_socket_state state NNN)
+{
+ return;
+}
+
+static void set_socket_info_noop(PSI_socket *socket NNN,
+ const my_socket *fd NNN,
+ const struct sockaddr *addr NNN,
+ socklen_t addr_len NNN)
+{
+ return;
+}
+
+static void set_socket_thread_owner_noop(PSI_socket *socket NNN)
+{
+ return;
+}
+
+static PSI_prepared_stmt*
+create_prepare_stmt_noop(void *identity NNN, uint stmt_id NNN,
+ PSI_statement_locker *locker NNN,
+ const char *stmt_name NNN, size_t stmt_name_length NNN,
+ const char *name NNN, size_t length NNN)
+{
+ return NULL;
+}
+
+static void
+execute_prepare_stmt_noop(PSI_statement_locker *locker NNN,
+ PSI_prepared_stmt *prepared_stmt NNN)
+{
+ return;
+}
+
+void
+destroy_prepared_stmt_noop(PSI_prepared_stmt *prepared_stmt NNN)
+{
+ return;
+}
+
+void
+reprepare_prepared_stmt_noop(PSI_prepared_stmt *prepared_stmt NNN)
+{
+ return;
+}
+
+static struct PSI_digest_locker*
+digest_start_noop(PSI_statement_locker *locker NNN)
+{
+ return NULL;
+}
+
+static void
+digest_end_noop(PSI_digest_locker *locker NNN,
+ const struct sql_digest_storage *digest NNN)
+{
+ return;
+}
+
+static int
+set_thread_connect_attrs_noop(const char *buffer MY_ATTRIBUTE((unused)),
+ uint length MY_ATTRIBUTE((unused)),
+ const void *from_cs MY_ATTRIBUTE((unused)))
+{
+ return 0;
+}
+
+static PSI_sp_locker*
+pfs_start_sp_noop(PSI_sp_locker_state *state NNN, PSI_sp_share *sp_share NNN)
+{
+ return NULL;
+}
+
+static void pfs_end_sp_noop(PSI_sp_locker *locker NNN)
+{
+ return;
+}
+
+static void
+pfs_drop_sp_noop(uint object_type NNN,
+ const char *schema_name NNN, uint schema_name_length NNN,
+ const char *object_name NNN, uint object_name_length NNN)
+{
+ return;
+}
+
+static PSI_sp_share*
+pfs_get_sp_share_noop(uint object_type NNN,
+ const char *schema_name NNN, uint schema_name_length NNN,
+ const char *object_name NNN, uint object_name_length NNN)
+{
+ return NULL;
+}
+
+static void
+pfs_release_sp_share_noop(PSI_sp_share *sp_share NNN)
+{
+ return;
+}
+
+static void register_memory_noop(const char *category NNN,
+ PSI_memory_info *info NNN,
+ int count NNN)
+{
+ return;
+}
+
+static PSI_memory_key memory_alloc_noop(PSI_memory_key key NNN, size_t size NNN, struct PSI_thread ** owner NNN)
+{
+ *owner= NULL;
+ return PSI_NOT_INSTRUMENTED;
+}
+
+static PSI_memory_key memory_realloc_noop(PSI_memory_key key NNN, size_t old_size NNN, size_t new_size NNN, struct PSI_thread ** owner NNN)
+{
+ *owner= NULL;
+ return PSI_NOT_INSTRUMENTED;
+}
+
+static PSI_memory_key memory_claim_noop(PSI_memory_key key NNN, size_t size NNN, struct PSI_thread ** owner)
+{
+ *owner= NULL;
+ return PSI_NOT_INSTRUMENTED;
+}
+
+static void memory_free_noop(PSI_memory_key key NNN, size_t size NNN, struct PSI_thread * owner NNN)
+{
+ return;
+}
+
+static void unlock_table_noop(PSI_table *table NNN)
+{
+ return;
+}
+
+static PSI_metadata_lock *
+create_metadata_lock_noop(void *identity NNN,
+ const MDL_key *mdl_key NNN,
+ opaque_mdl_type mdl_type NNN,
+ opaque_mdl_duration mdl_duration NNN,
+ opaque_mdl_status mdl_status NNN,
+ const char *src_file NNN,
+ uint src_line NNN)
+{
+ return NULL;
+}
+
+static void
+set_metadata_lock_status_noop(PSI_metadata_lock* lock NNN,
+ opaque_mdl_status mdl_status NNN)
+{
+}
+
+static void
+destroy_metadata_lock_noop(PSI_metadata_lock* lock NNN)
+{
+}
+
+static PSI_metadata_locker *
+start_metadata_wait_noop(PSI_metadata_locker_state *state NNN,
+ PSI_metadata_lock *mdl NNN,
+ const char *src_file NNN,
+ uint src_line NNN)
+{
+ return NULL;
+}
+
+static void
+end_metadata_wait_noop(PSI_metadata_locker *locker NNN,
+ int rc NNN)
+{
+}
+
+static PSI PSI_noop=
+{
+ register_mutex_noop,
+ register_rwlock_noop,
+ register_cond_noop,
+ register_thread_noop,
+ register_file_noop,
+ register_stage_noop,
+ register_statement_noop,
+ register_socket_noop,
+ init_mutex_noop,
+ destroy_mutex_noop,
+ init_rwlock_noop,
+ destroy_rwlock_noop,
+ init_cond_noop,
+ destroy_cond_noop,
+ init_socket_noop,
+ destroy_socket_noop,
+ get_table_share_noop,
+ release_table_share_noop,
+ drop_table_share_noop,
+ open_table_noop,
+ unbind_table_noop,
+ rebind_table_noop,
+ close_table_noop,
+ create_file_noop,
+ spawn_thread_noop,
+ new_thread_noop,
+ set_thread_id_noop,
+ set_thread_THD_noop,
+ set_thread_os_id_noop,
+ get_thread_noop,
+ set_thread_user_noop,
+ set_thread_user_host_noop,
+ set_thread_db_noop,
+ set_thread_command_noop,
+ set_connection_type_noop,
+ set_thread_start_time_noop,
+ set_thread_state_noop,
+ set_thread_info_noop,
+ set_thread_noop,
+ delete_current_thread_noop,
+ delete_thread_noop,
+ get_thread_file_name_locker_noop,
+ get_thread_file_stream_locker_noop,
+ get_thread_file_descriptor_locker_noop,
+ unlock_mutex_noop,
+ unlock_rwlock_noop,
+ signal_cond_noop,
+ broadcast_cond_noop,
+ start_idle_wait_noop,
+ end_idle_wait_noop,
+ start_mutex_wait_noop,
+ end_mutex_wait_noop,
+ start_rwlock_rdwait_noop,
+ end_rwlock_rdwait_noop,
+ start_rwlock_wrwait_noop,
+ end_rwlock_wrwait_noop,
+ start_cond_wait_noop,
+ end_cond_wait_noop,
+ start_table_io_wait_noop,
+ end_table_io_wait_noop,
+ start_table_lock_wait_noop,
+ end_table_lock_wait_noop,
+ start_file_open_wait_noop,
+ end_file_open_wait_noop,
+ end_file_open_wait_and_bind_to_descriptor_noop,
+ end_temp_file_open_wait_and_bind_to_descriptor_noop,
+ start_file_wait_noop,
+ end_file_wait_noop,
+ start_file_close_wait_noop,
+ end_file_close_wait_noop,
+ start_stage_noop,
+ get_current_stage_progress_noop,
+ end_stage_noop,
+ get_thread_statement_locker_noop,
+ refine_statement_noop,
+ start_statement_noop,
+ set_statement_text_noop,
+ set_statement_lock_time_noop,
+ set_statement_rows_sent_noop,
+ set_statement_rows_examined_noop,
+ inc_statement_created_tmp_disk_tables_noop,
+ inc_statement_created_tmp_tables_noop,
+ inc_statement_select_full_join_noop,
+ inc_statement_select_full_range_join_noop,
+ inc_statement_select_range_noop,
+ inc_statement_select_range_check_noop,
+ inc_statement_select_scan_noop,
+ inc_statement_sort_merge_passes_noop,
+ inc_statement_sort_range_noop,
+ inc_statement_sort_rows_noop,
+ inc_statement_sort_scan_noop,
+ set_statement_no_index_used_noop,
+ set_statement_no_good_index_used_noop,
+ end_statement_noop,
+ get_thread_transaction_locker_noop,
+ start_transaction_noop,
+ set_transaction_xid_noop,
+ set_transaction_xa_state_noop,
+ set_transaction_gtid_noop,
+ set_transaction_trxid_noop,
+ inc_transaction_savepoints_noop,
+ inc_transaction_rollback_to_savepoint_noop,
+ inc_transaction_release_savepoint_noop,
+ end_transaction_noop,
+ start_socket_wait_noop,
+ end_socket_wait_noop,
+ set_socket_state_noop,
+ set_socket_info_noop,
+ set_socket_thread_owner_noop,
+ create_prepare_stmt_noop,
+ destroy_prepared_stmt_noop,
+ reprepare_prepared_stmt_noop,
+ execute_prepare_stmt_noop,
+ digest_start_noop,
+ digest_end_noop,
+ set_thread_connect_attrs_noop,
+ pfs_start_sp_noop,
+ pfs_end_sp_noop,
+ pfs_drop_sp_noop,
+ pfs_get_sp_share_noop,
+ pfs_release_sp_share_noop,
+ register_memory_noop,
+ memory_alloc_noop,
+ memory_realloc_noop,
+ memory_claim_noop,
+ memory_free_noop,
+
+ unlock_table_noop,
+ create_metadata_lock_noop,
+ set_metadata_lock_status_noop,
+ destroy_metadata_lock_noop,
+ start_metadata_wait_noop,
+ end_metadata_wait_noop
+};
+
+/**
+ Hook for the instrumentation interface.
+ Code implementing the instrumentation interface should register here.
+*/
+struct PSI_bootstrap *PSI_hook= NULL;
+
+/**
+ Instance of the instrumentation interface for the MySQL server.
+ @todo This is currently a global variable, which is handy when
+ compiling instrumented code that is bundled with the server.
+ When dynamic plugin are truly supported, this variable will need
+ to be replaced by a macro, so that each XYZ plugin can have it's own
+ xyz_psi_server variable, obtained from PSI_bootstrap::get_interface()
+ with the version used at compile time for plugin XYZ.
+*/
+
+PSI *PSI_server= & PSI_noop;
+
+void set_psi_server(PSI *psi)
+{
+ PSI_server= psi;
+}
+
+C_MODE_END
+
diff --git a/mysql/mysys/ptr_cmp.c b/mysql/mysys/ptr_cmp.c
new file mode 100644
index 0000000..9a06d33
--- /dev/null
+++ b/mysql/mysys/ptr_cmp.c
@@ -0,0 +1,55 @@
+/* Copyright (c) 2000, 2015, Oracle and/or its affiliates. All rights reserved.
+
+ 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; version 2 of the License.
+
+ 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include "mysys_priv.h"
+#include <myisampack.h>
+
+void my_store_ptr(uchar *buff, size_t pack_length, my_off_t pos)
+{
+ switch (pack_length) {
+#if SIZEOF_OFF_T > 4
+ case 8: mi_int8store(buff,pos); break;
+ case 7: mi_int7store(buff,pos); break;
+ case 6: mi_int6store(buff,pos); break;
+ case 5: mi_int5store(buff,pos); break;
+#endif
+ case 4: mi_int4store(buff,pos); break;
+ case 3: mi_int3store(buff,pos); break;
+ case 2: mi_int2store(buff,pos); break;
+ case 1: buff[0]= (uchar) pos; break;
+ default: DBUG_ASSERT(0);
+ }
+ return;
+}
+
+my_off_t my_get_ptr(uchar *ptr, size_t pack_length)
+{
+ my_off_t pos;
+ switch (pack_length) {
+#if SIZEOF_OFF_T > 4
+ case 8: pos= (my_off_t) mi_uint8korr(ptr); break;
+ case 7: pos= (my_off_t) mi_uint7korr(ptr); break;
+ case 6: pos= (my_off_t) mi_uint6korr(ptr); break;
+ case 5: pos= (my_off_t) mi_uint5korr(ptr); break;
+#endif
+ case 4: pos= (my_off_t) mi_uint4korr(ptr); break;
+ case 3: pos= (my_off_t) mi_uint3korr(ptr); break;
+ case 2: pos= (my_off_t) mi_uint2korr(ptr); break;
+ case 1: pos= (my_off_t) *(uchar*) ptr; break;
+ default: DBUG_ASSERT(0); return 0;
+ }
+ return pos;
+}
+
diff --git a/mysql/mysys/queues.c b/mysql/mysys/queues.c
new file mode 100644
index 0000000..a15b348
--- /dev/null
+++ b/mysql/mysys/queues.c
@@ -0,0 +1,622 @@
+/* Copyright (c) 2000, 2015, Oracle and/or its affiliates. All rights reserved.
+
+ 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; version 2 of the License.
+
+ 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+/*
+ Code for handling of priority Queues.
+ Implemention of queues from "Algoritms in C" by Robert Sedgewick.
+ An optimisation of _downheap suggested in Exercise 7.51 in "Data
+ Structures & Algorithms in C++" by Mark Allen Weiss, Second Edition
+ was implemented by Mikael Ronstrom 2005. Also the O(N) algorithm
+ of queue_fix was implemented.
+*/
+
+#include "mysys_priv.h"
+#include "my_sys.h"
+#include "mysys_err.h"
+#include <queues.h>
+
+int resize_queue(QUEUE *queue, uint max_elements);
+
+/*
+ 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 *, uchar *, uchar *),
+ void *first_cmp_arg)
+{
+ DBUG_ENTER("init_queue");
+ if ((queue->root= (uchar **) my_malloc(key_memory_QUEUE,
+ (max_elements+1)*sizeof(void*),
+ MYF(MY_WME))) == 0)
+ DBUG_RETURN(1);
+ queue->elements=0;
+ queue->compare=compare;
+ queue->first_cmp_arg=first_cmp_arg;
+ queue->max_elements=max_elements;
+ queue->offset_to_key=offset_to_key;
+ queue_set_max_at_top(queue, max_at_top);
+ DBUG_RETURN(0);
+}
+
+
+
+/*
+ Init queue, uses init_queue internally for init work but also accepts
+ auto_extent as parameter
+
+ SYNOPSIS
+ init_queue_ex()
+ 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
+ auto_extent When the queue is full and there is insert operation
+ extend the queue.
+
+ NOTES
+ Will allocate max_element pointers for queue array
+
+ RETURN
+ 0 ok
+ 1 Could not allocate memory
+*/
+
+int init_queue_ex(QUEUE *queue, uint max_elements, uint offset_to_key,
+ pbool max_at_top, int (*compare) (void *, uchar *, uchar *),
+ void *first_cmp_arg, uint auto_extent)
+{
+ int ret;
+ DBUG_ENTER("init_queue_ex");
+
+ if ((ret= init_queue(queue, max_elements, offset_to_key, max_at_top, compare,
+ first_cmp_arg)))
+ DBUG_RETURN(ret);
+
+ queue->auto_extent= auto_extent;
+ DBUG_RETURN(0);
+}
+
+/*
+ Reinitialize queue for other usage
+
+ SYNOPSIS
+ reinit_queue()
+ queue Queue to initialise
+ max_elements Max elements that will be put in queue
+ offset_to_key Offset to key in element stored in queue
+ Used when sending pointers to compare function
+ max_at_top Set to 1 if you want biggest element on top.
+ compare Compare function for elements, takes 3 arguments.
+ first_cmp_arg First argument to compare function
+
+ NOTES
+ This will delete all elements from the queue. If you don't want this,
+ use resize_queue() instead.
+
+ RETURN
+ 0 ok
+ EE_OUTOFMEMORY Wrong max_elements
+*/
+
+int reinit_queue(QUEUE *queue, uint max_elements, uint offset_to_key,
+ pbool max_at_top, int (*compare) (void *, uchar *, uchar *),
+ void *first_cmp_arg)
+{
+ DBUG_ENTER("reinit_queue");
+ queue->elements=0;
+ queue->compare=compare;
+ queue->first_cmp_arg=first_cmp_arg;
+ queue->offset_to_key=offset_to_key;
+ queue_set_max_at_top(queue, max_at_top);
+ resize_queue(queue, max_elements);
+ DBUG_RETURN(0);
+}
+
+
+/*
+ Resize queue
+
+ SYNOPSIS
+ resize_queue()
+ queue Queue
+ max_elements New max size for queue
+
+ NOTES
+ If you resize queue to be less than the elements you have in it,
+ the extra elements will be deleted
+
+ RETURN
+ 0 ok
+ 1 Error. In this case the queue is unchanged
+*/
+
+int resize_queue(QUEUE *queue, uint max_elements)
+{
+ uchar **new_root;
+ DBUG_ENTER("resize_queue");
+ if (queue->max_elements == max_elements)
+ DBUG_RETURN(0);
+ if ((new_root= (uchar **) my_realloc(key_memory_QUEUE,
+ (void *)queue->root,
+ (max_elements+1)*sizeof(void*),
+ MYF(MY_WME))) == 0)
+ DBUG_RETURN(1);
+ set_if_smaller(queue->elements, max_elements);
+ queue->max_elements= max_elements;
+ queue->root= new_root;
+ 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");
+ my_free(queue->root);
+ queue->root= NULL;
+ DBUG_VOID_RETURN;
+}
+
+
+ /* Code for insert, search and delete of elements */
+
+void queue_insert(QUEUE *queue, uchar *element)
+{
+ uint idx, next;
+ DBUG_ASSERT(queue->elements < queue->max_elements);
+ queue->root[0]= element;
+ idx= ++queue->elements;
+ /* max_at_top swaps the comparison if we want to order by desc */
+ while ((queue->compare(queue->first_cmp_arg,
+ element + queue->offset_to_key,
+ queue->root[(next= idx >> 1)] +
+ queue->offset_to_key) * queue->max_at_top) < 0)
+ {
+ queue->root[idx]= queue->root[next];
+ idx= next;
+ }
+ queue->root[idx]= element;
+}
+
+ /* Remove item from queue */
+ /* Returns pointer to removed element */
+
+uchar *queue_remove(QUEUE *queue, uint idx)
+{
+ uchar *element;
+ DBUG_ASSERT(idx < queue->max_elements);
+ element= queue->root[++idx]; /* Intern index starts from 1 */
+ queue->root[idx]= queue->root[queue->elements--];
+ _downheap(queue, idx);
+ return element;
+}
+
+
+void _downheap(QUEUE *queue, uint idx)
+{
+ uchar *element;
+ uint elements,half_queue,offset_to_key, next_index;
+ my_bool first= TRUE;
+ uint start_idx= idx;
+
+ offset_to_key=queue->offset_to_key;
+ element=queue->root[idx];
+ half_queue=(elements=queue->elements) >> 1;
+
+ while (idx <= half_queue)
+ {
+ next_index=idx+idx;
+ if (next_index < elements &&
+ (queue->compare(queue->first_cmp_arg,
+ queue->root[next_index]+offset_to_key,
+ queue->root[next_index+1]+offset_to_key) *
+ queue->max_at_top) > 0)
+ next_index++;
+ if (first &&
+ (((queue->compare(queue->first_cmp_arg,
+ queue->root[next_index]+offset_to_key,
+ element+offset_to_key) * queue->max_at_top) >= 0)))
+ {
+ queue->root[idx]= element;
+ return;
+ }
+ queue->root[idx]=queue->root[next_index];
+ idx=next_index;
+ first= FALSE;
+ }
+
+ next_index= idx >> 1;
+ while (next_index > start_idx)
+ {
+ if ((queue->compare(queue->first_cmp_arg,
+ queue->root[next_index]+offset_to_key,
+ element+offset_to_key) *
+ queue->max_at_top) < 0)
+ break;
+ queue->root[idx]=queue->root[next_index];
+ idx=next_index;
+ next_index= idx >> 1;
+ }
+ queue->root[idx]=element;
+}
+
+/*
+ Fix heap when every element was changed.
+*/
+
+void queue_fix(QUEUE *queue)
+{
+ uint i;
+ for (i= queue->elements >> 1; i > 0; i--)
+ _downheap(queue, i);
+}
+
+#ifdef MAIN
+ /*
+ A test program for the priority queue implementation.
+ It can also be used to benchmark changes of the implementation
+ Build by doing the following in the directory mysys
+ make queues
+ ./queues
+
+ Written by Mikael Ronström, 2005
+ */
+
+static uint num_array[1025];
+static uint tot_no_parts= 0;
+static uint tot_no_loops= 0;
+static uint expected_part= 0;
+static uint expected_num= 0;
+static my_bool max_ind= 0;
+static my_bool fix_used= 0;
+static ulonglong start_time= 0;
+
+static my_bool is_divisible_by(uint num, uint divisor)
+{
+ uint quotient= num / divisor;
+ if (quotient * divisor == num)
+ return TRUE;
+ return FALSE;
+}
+
+void calculate_next()
+{
+ uint part= expected_part, num= expected_num;
+ uint no_parts= tot_no_parts;
+ if (max_ind)
+ {
+ do
+ {
+ while (++part <= no_parts)
+ {
+ if (is_divisible_by(num, part) &&
+ (num <= ((1 << 21) + part)))
+ {
+ expected_part= part;
+ expected_num= num;
+ return;
+ }
+ }
+ part= 0;
+ } while (--num);
+ }
+ else
+ {
+ do
+ {
+ while (--part > 0)
+ {
+ if (is_divisible_by(num, part))
+ {
+ expected_part= part;
+ expected_num= num;
+ return;
+ }
+ }
+ part= no_parts + 1;
+ } while (++num);
+ }
+}
+
+void calculate_end_next(uint part)
+{
+ uint no_parts= tot_no_parts, num;
+ num_array[part]= 0;
+ if (max_ind)
+ {
+ expected_num= 0;
+ for (part= no_parts; part > 0 ; part--)
+ {
+ if (num_array[part])
+ {
+ num= num_array[part] & 0x3FFFFF;
+ if (num >= expected_num)
+ {
+ expected_num= num;
+ expected_part= part;
+ }
+ }
+ }
+ if (expected_num == 0)
+ expected_part= 0;
+ }
+ else
+ {
+ expected_num= 0xFFFFFFFF;
+ for (part= 1; part <= no_parts; part++)
+ {
+ if (num_array[part])
+ {
+ num= num_array[part] & 0x3FFFFF;
+ if (num <= expected_num)
+ {
+ expected_num= num;
+ expected_part= part;
+ }
+ }
+ }
+ if (expected_num == 0xFFFFFFFF)
+ expected_part= 0;
+ }
+ return;
+}
+static int test_compare(void *null_arg, uchar *a, uchar *b)
+{
+ uint a_num= (*(uint*)a) & 0x3FFFFF;
+ uint b_num= (*(uint*)b) & 0x3FFFFF;
+ uint a_part, b_part;
+ (void) null_arg;
+ if (a_num > b_num)
+ return +1;
+ if (a_num < b_num)
+ return -1;
+ a_part= (*(uint*)a) >> 22;
+ b_part= (*(uint*)b) >> 22;
+ if (a_part < b_part)
+ return +1;
+ if (a_part > b_part)
+ return -1;
+ return 0;
+}
+
+my_bool check_num(uint num_part)
+{
+ uint part= num_part >> 22;
+ uint num= num_part & 0x3FFFFF;
+ if (part == expected_part)
+ if (num == expected_num)
+ return FALSE;
+ printf("Expect part %u Expect num 0x%x got part %u num 0x%x max_ind %u fix_used %u \n",
+ expected_part, expected_num, part, num, max_ind, fix_used);
+ return TRUE;
+}
+
+
+void perform_insert(QUEUE *queue)
+{
+ uint i= 1, no_parts= tot_no_parts;
+ uint backward_start= 0;
+
+ expected_part= 1;
+ expected_num= 1;
+
+ if (max_ind)
+ backward_start= 1 << 21;
+
+ do
+ {
+ uint num= (i + backward_start);
+ if (max_ind)
+ {
+ while (!is_divisible_by(num, i))
+ num--;
+ if (max_ind && (num > expected_num ||
+ (num == expected_num && i < expected_part)))
+ {
+ expected_num= num;
+ expected_part= i;
+ }
+ }
+ num_array[i]= num + (i << 22);
+ if (fix_used)
+ queue_element(queue, i-1)= (uchar*)&num_array[i];
+ else
+ queue_insert(queue, (uchar*)&num_array[i]);
+ } while (++i <= no_parts);
+ if (fix_used)
+ {
+ queue->elements= no_parts;
+ queue_fix(queue);
+ }
+}
+
+my_bool perform_ins_del(QUEUE *queue, my_bool max_ind)
+{
+ uint i= 0, no_loops= tot_no_loops, j= tot_no_parts;
+ do
+ {
+ uint num_part= *(uint*)queue_top(queue);
+ uint part= num_part >> 22;
+ if (check_num(num_part))
+ return TRUE;
+ if (j++ >= no_loops)
+ {
+ calculate_end_next(part);
+ queue_remove(queue, (uint) 0);
+ }
+ else
+ {
+ calculate_next();
+ if (max_ind)
+ num_array[part]-= part;
+ else
+ num_array[part]+= part;
+ queue_top(queue)= (uchar*)&num_array[part];
+ queue_replaced(queue);
+ }
+ } while (++i < no_loops);
+ return FALSE;
+}
+
+my_bool do_test(uint no_parts, uint l_max_ind, my_bool l_fix_used)
+{
+ QUEUE queue;
+ my_bool result;
+ max_ind= l_max_ind;
+ fix_used= l_fix_used;
+ init_queue(&queue, no_parts, 0, max_ind, test_compare, NULL);
+ tot_no_parts= no_parts;
+ tot_no_loops= 1024;
+ perform_insert(&queue);
+ result= perform_ins_del(&queue, max_ind);
+ if (result)
+ {
+ printf("Error\n");
+ delete_queue(&queue);
+ return TRUE;
+ }
+ delete_queue(&queue);
+ return FALSE;
+}
+
+static void start_measurement()
+{
+ start_time= my_getsystime();
+}
+
+static void stop_measurement()
+{
+ ulonglong stop_time= my_getsystime();
+ uint time_in_micros;
+ stop_time-= start_time;
+ stop_time/= 10; /* Convert to microseconds */
+ time_in_micros= (uint)stop_time;
+ printf("Time expired is %u microseconds \n", time_in_micros);
+}
+
+static void benchmark_test()
+{
+ QUEUE queue_real;
+ QUEUE *queue= &queue_real;
+ uint i, add;
+ fix_used= TRUE;
+ max_ind= FALSE;
+ tot_no_parts= 1024;
+ init_queue(queue, tot_no_parts, 0, max_ind, test_compare, NULL);
+ /*
+ First benchmark whether queue_fix is faster than using queue_insert
+ for sizes of 16 partitions.
+ */
+ for (tot_no_parts= 2, add=2; tot_no_parts < 128;
+ tot_no_parts+= add, add++)
+ {
+ printf("Start benchmark queue_fix, tot_no_parts= %u \n", tot_no_parts);
+ start_measurement();
+ for (i= 0; i < 128; i++)
+ {
+ perform_insert(queue);
+ queue_remove_all(queue);
+ }
+ stop_measurement();
+
+ fix_used= FALSE;
+ printf("Start benchmark queue_insert\n");
+ start_measurement();
+ for (i= 0; i < 128; i++)
+ {
+ perform_insert(queue);
+ queue_remove_all(queue);
+ }
+ stop_measurement();
+ }
+ /*
+ Now benchmark insertion and deletion of 16400 elements.
+ Used in consecutive runs this shows whether the optimised _downheap
+ is faster than the standard implementation.
+ */
+ printf("Start benchmarking _downheap \n");
+ start_measurement();
+ perform_insert(queue);
+ for (i= 0; i < 65536; i++)
+ {
+ uint num, part;
+ num= *(uint*)queue_top(queue);
+ num+= 16;
+ part= num >> 22;
+ num_array[part]= num;
+ queue_top(queue)= (uchar*)&num_array[part];
+ queue_replaced(queue);
+ }
+ for (i= 0; i < 16; i++)
+ queue_remove(queue, (uint) 0);
+ queue_remove_all(queue);
+ stop_measurement();
+ delete_queue(queue);
+}
+
+int main()
+{
+ int i, add= 1;
+ for (i= 1; i < 1024; i+=add, add++)
+ {
+ printf("Start test for priority queue of size %u\n", i);
+ if (do_test(i, 0, 1))
+ return -1;
+ if (do_test(i, 1, 1))
+ return -1;
+ if (do_test(i, 0, 0))
+ return -1;
+ if (do_test(i, 1, 0))
+ return -1;
+ }
+ benchmark_test();
+ printf("OK\n");
+ return 0;
+}
+#endif
diff --git a/mysql/mysys/sql_chars.c b/mysql/mysys/sql_chars.c
new file mode 100644
index 0000000..071b0dd
--- /dev/null
+++ b/mysql/mysys/sql_chars.c
@@ -0,0 +1,120 @@
+/*
+ Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+
+ 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; version 2 of the License.
+
+ 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include "sql_chars.h"
+#include "m_ctype.h"
+#include "my_sys.h"
+
+static
+void hint_lex_init_maps(charset_info_st *cs,
+ enum hint_lex_char_classes *hint_map)
+{
+ size_t i;
+ for (i= 0; i < 256 ; i++)
+ {
+ if (my_ismb1st(cs, i))
+ hint_map[i]= HINT_CHR_MB;
+ else if (my_isalpha(cs, i))
+ hint_map[i]= HINT_CHR_IDENT;
+ else if (my_isdigit(cs, i))
+ hint_map[i]= HINT_CHR_DIGIT;
+ else if (my_isspace(cs, i))
+ {
+ DBUG_ASSERT(!my_ismb1st(cs, i));
+ hint_map[i]= HINT_CHR_SPACE;
+ }
+ else
+ hint_map[i]= HINT_CHR_CHAR;
+ }
+ hint_map[(uchar) '*']= HINT_CHR_ASTERISK;
+ hint_map[(uchar) '@']= HINT_CHR_AT;
+ hint_map[(uchar) '`']= HINT_CHR_BACKQUOTE;
+ hint_map[(uchar) '"']= HINT_CHR_DOUBLEQUOTE;
+ hint_map[(uchar) '_']= HINT_CHR_IDENT;
+ hint_map[(uchar) '$']= HINT_CHR_IDENT;
+ hint_map[(uchar) '/']= HINT_CHR_SLASH;
+ hint_map[(uchar) '\n']= HINT_CHR_NL;
+}
+
+
+my_bool init_state_maps(charset_info_st *cs)
+{
+ uint i;
+ uchar *ident_map;
+ enum my_lex_states *state_map= NULL;
+
+ lex_state_maps_st *lex_state_maps=
+ (lex_state_maps_st *) my_once_alloc(sizeof(lex_state_maps_st), MYF(MY_WME));
+
+ if (lex_state_maps == NULL)
+ return TRUE; // OOM
+
+ cs->state_maps= lex_state_maps;
+ state_map= lex_state_maps->main_map;
+
+ if (!(cs->ident_map= ident_map= (uchar*) my_once_alloc(256, MYF(MY_WME))))
+ return TRUE; // OOM
+
+ hint_lex_init_maps(cs, lex_state_maps->hint_map);
+
+ /* Fill state_map with states to get a faster parser */
+ for (i=0; i < 256 ; i++)
+ {
+ if (my_isalpha(cs,i))
+ state_map[i]= MY_LEX_IDENT;
+ else if (my_isdigit(cs,i))
+ state_map[i]= MY_LEX_NUMBER_IDENT;
+ else if (my_ismb1st(cs, i))
+ /* To get whether it's a possible leading byte for a charset. */
+ state_map[i]= MY_LEX_IDENT;
+ else if (my_isspace(cs,i))
+ state_map[i]= MY_LEX_SKIP;
+ else
+ state_map[i]= MY_LEX_CHAR;
+ }
+ state_map[(uchar)'_']=state_map[(uchar)'$']= MY_LEX_IDENT;
+ state_map[(uchar)'\'']= MY_LEX_STRING;
+ state_map[(uchar)'.']= MY_LEX_REAL_OR_POINT;
+ state_map[(uchar)'>']=state_map[(uchar)'=']=state_map[(uchar)'!']= MY_LEX_CMP_OP;
+ state_map[(uchar)'<']= MY_LEX_LONG_CMP_OP;
+ state_map[(uchar)'&']=state_map[(uchar)'|']= MY_LEX_BOOL;
+ state_map[(uchar)'#']= MY_LEX_COMMENT;
+ state_map[(uchar)';']= MY_LEX_SEMICOLON;
+ state_map[(uchar)':']= MY_LEX_SET_VAR;
+ state_map[0]= MY_LEX_EOL;
+ state_map[(uchar)'\\']= MY_LEX_ESCAPE;
+ state_map[(uchar)'/']= MY_LEX_LONG_COMMENT;
+ state_map[(uchar)'*']= MY_LEX_END_LONG_COMMENT;
+ state_map[(uchar)'@']= MY_LEX_USER_END;
+ state_map[(uchar) '`']= MY_LEX_USER_VARIABLE_DELIMITER;
+ state_map[(uchar)'"']= MY_LEX_STRING_OR_DELIMITER;
+
+ /*
+ Create a second map to make it faster to find identifiers
+ */
+ for (i=0; i < 256 ; i++)
+ {
+ ident_map[i]= (uchar) (state_map[i] == MY_LEX_IDENT ||
+ state_map[i] == MY_LEX_NUMBER_IDENT);
+ }
+
+ /* Special handling of hex and binary strings */
+ state_map[(uchar)'x']= state_map[(uchar)'X']= MY_LEX_IDENT_OR_HEX;
+ state_map[(uchar)'b']= state_map[(uchar)'B']= MY_LEX_IDENT_OR_BIN;
+ state_map[(uchar)'n']= state_map[(uchar)'N']= MY_LEX_IDENT_OR_NCHAR;
+
+ return FALSE;
+}
diff --git a/mysql/mysys/stacktrace.c b/mysql/mysys/stacktrace.c
new file mode 100644
index 0000000..f4fe6c8
--- /dev/null
+++ b/mysql/mysys/stacktrace.c
@@ -0,0 +1,802 @@
+/* Copyright (c) 2001, 2016, Oracle and/or its affiliates. All rights reserved.
+
+ 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; version 2 of the License.
+
+ 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include "my_stacktrace.h"
+
+#ifndef _WIN32
+#include "my_thread.h"
+#include "m_string.h"
+#include <signal.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef HAVE_STACKTRACE
+
+#ifdef __linux__
+#include <ctype.h> /* isprint */
+#include <sys/syscall.h> /* SYS_gettid */
+#endif
+
+#if HAVE_EXECINFO_H
+#include <execinfo.h>
+#endif
+
+#ifdef __linux__
+/* __bss_start doesn't seem to work on FreeBSD and doesn't exist on OSX/Solaris. */
+#define PTR_SANE(p) ((p) && (char*)(p) >= heap_start && (char*)(p) <= heap_end)
+static char *heap_start;
+extern char *__bss_start;
+#else
+#define PTR_SANE(p) (p)
+#endif /* __linux */
+
+void my_init_stacktrace()
+{
+#ifdef __linux__
+ heap_start = (char*) &__bss_start;
+#endif /* __linux__ */
+}
+
+#ifdef __linux__
+
+static void print_buffer(char *buffer, size_t count)
+{
+ const char s[]= " ";
+ for (; count && *buffer; --count)
+ {
+ my_write_stderr(isprint(*buffer) ? buffer : s, 1);
+ ++buffer;
+ }
+}
+
+/**
+ Access the pages of this process through /proc/self/task/<tid>/mem
+ in order to safely print the contents of a memory address range.
+
+ @param addr The address at the start of the memory region.
+ @param max_len The length of the memory region.
+
+ @return Zero on success.
+*/
+static int safe_print_str(const char *addr, int max_len)
+{
+ int fd;
+ pid_t tid;
+ off_t offset;
+ ssize_t nbytes= 0;
+ size_t total, count;
+ char buf[256];
+
+ tid= (pid_t) syscall(SYS_gettid);
+
+ sprintf(buf, "/proc/self/task/%d/mem", tid);
+
+ if ((fd= open(buf, O_RDONLY)) < 0)
+ return -1;
+
+ /* Ensure that off_t can hold a pointer. */
+ compile_time_assert(sizeof(off_t) >= sizeof(intptr));
+
+ total= max_len;
+ offset= (intptr) addr;
+
+ /* Read up to the maximum number of bytes. */
+ while (total)
+ {
+ count= MY_MIN(sizeof(buf), total);
+
+ if ((nbytes= pread(fd, buf, count, offset)) < 0)
+ {
+ /* Just in case... */
+ if (errno == EINTR)
+ continue;
+ else
+ break;
+ }
+
+ /* Advance offset into memory. */
+ total-= nbytes;
+ offset+= nbytes;
+ addr+= nbytes;
+
+ /* Output the printable characters. */
+ print_buffer(buf, nbytes);
+
+ /* Break if less than requested... */
+ if ((count - nbytes))
+ break;
+ }
+
+ /* Output a new line if something was printed. */
+ if (total != (size_t) max_len)
+ my_safe_printf_stderr("%s", "\n");
+
+ if (nbytes == -1)
+ my_safe_printf_stderr("Can't read from address %p\n", addr);
+
+ close(fd);
+
+ return 0;
+}
+
+#endif /* __linux __ */
+
+void my_safe_puts_stderr(const char* val, size_t max_len)
+{
+#ifdef __linux__
+/* Only needed by the linux version of PTR_SANE */
+ char *heap_end;
+
+ if (!safe_print_str(val, max_len))
+ return;
+
+ heap_end= (char*) sbrk(0);
+#endif
+
+ if (!PTR_SANE(val))
+ {
+ my_safe_printf_stderr("%s", "is an invalid pointer\n");
+ return;
+ }
+
+ for (; max_len && PTR_SANE(val) && *val; --max_len)
+ my_write_stderr((val++), 1);
+ my_safe_printf_stderr("%s", "\n");
+}
+
+#if defined(HAVE_PRINTSTACK)
+
+/* Use Solaris' symbolic stack trace routine. */
+#include <ucontext.h>
+
+void my_print_stacktrace(uchar* stack_bottom MY_ATTRIBUTE((unused)),
+ ulong thread_stack MY_ATTRIBUTE((unused)))
+{
+ if (printstack(fileno(stderr)) == -1)
+ my_safe_printf_stderr("%s",
+ "Error when traversing the stack, stack appears corrupt.\n");
+ else
+ my_safe_printf_stderr("Please read "
+ "http://dev.mysql.com/doc/refman/%u.%u/en/resolve-stack-dump.html\n"
+ "and follow instructions on how to resolve the stack trace.\n"
+ "Resolved stack trace is much more helpful in diagnosing the\n"
+ "problem, so please do resolve it\n",
+ MYSQL_VERSION_MAJOR, MYSQL_VERSION_MINOR);
+}
+
+#elif HAVE_BACKTRACE
+
+#if HAVE_ABI_CXA_DEMANGLE
+
+char MY_ATTRIBUTE ((weak)) *
+my_demangle(const char *mangled_name MY_ATTRIBUTE((unused)),
+ int *status MY_ATTRIBUTE((unused)))
+{
+ return NULL;
+}
+
+static void my_demangle_symbols(char **addrs, int n)
+{
+ int status, i;
+ char *begin, *end, *demangled;
+
+ for (i= 0; i < n; i++)
+ {
+ demangled= NULL;
+ begin= strchr(addrs[i], '(');
+ end= begin ? strchr(begin, '+') : NULL;
+
+ if (begin && end)
+ {
+ *begin++= *end++= '\0';
+ demangled= my_demangle(begin, &status);
+ if (!demangled || status)
+ {
+ demangled= NULL;
+ begin[-1]= '(';
+ end[-1]= '+';
+ }
+ }
+
+ if (demangled)
+ my_safe_printf_stderr("%s(%s+%s\n", addrs[i], demangled, end);
+ else
+ my_safe_printf_stderr("%s\n", addrs[i]);
+ }
+}
+
+#endif /* HAVE_ABI_CXA_DEMANGLE */
+
+void my_print_stacktrace(uchar* stack_bottom, ulong thread_stack)
+{
+ void *addrs[128];
+ char **strings= NULL;
+ int n = backtrace(addrs, array_elements(addrs));
+ my_safe_printf_stderr("stack_bottom = %p thread_stack 0x%lx\n",
+ stack_bottom, thread_stack);
+#if HAVE_ABI_CXA_DEMANGLE
+ if ((strings= backtrace_symbols(addrs, n)))
+ {
+ my_demangle_symbols(strings, n);
+ free(strings);
+ }
+#endif
+ if (!strings)
+ {
+ backtrace_symbols_fd(addrs, n, fileno(stderr));
+ }
+}
+
+#endif /* HAVE_PRINTSTACK || HAVE_BACKTRACE */
+#endif /* HAVE_STACKTRACE */
+
+/* Produce a core for the thread */
+void my_write_core(int sig)
+{
+ signal(sig, SIG_DFL);
+ pthread_kill(my_thread_self(), sig);
+#if defined(P_MYID)
+ /* On Solaris, the above kill is not enough */
+ sigsend(P_PID,P_MYID,sig);
+#endif
+}
+
+#else /* _WIN32*/
+
+#include <dbghelp.h>
+#include <tlhelp32.h>
+#if _MSC_VER
+#pragma comment(lib, "dbghelp")
+#endif
+
+static EXCEPTION_POINTERS *exception_ptrs;
+
+#define MODULE64_SIZE_WINXP 576
+#define STACKWALK_MAX_FRAMES 64
+
+void my_init_stacktrace()
+{
+}
+
+
+void my_set_exception_pointers(EXCEPTION_POINTERS *ep)
+{
+ exception_ptrs = ep;
+}
+
+/*
+ Appends directory to symbol path.
+*/
+static void add_to_symbol_path(char *path, size_t path_buffer_size,
+ char *dir, size_t dir_buffer_size)
+{
+ strcat_s(dir, dir_buffer_size, ";");
+ if (!strstr(path, dir))
+ {
+ strcat_s(path, path_buffer_size, dir);
+ }
+}
+
+/*
+ Get symbol path - semicolon-separated list of directories to search for debug
+ symbols. We expect PDB in the same directory as corresponding exe or dll,
+ so the path is build from directories of the loaded modules. If environment
+ variable _NT_SYMBOL_PATH is set, it's value appended to the symbol search path
+*/
+static void get_symbol_path(char *path, size_t size)
+{
+ HANDLE hSnap;
+ char *envvar;
+ char *p;
+#ifndef DBUG_OFF
+ static char pdb_debug_dir[MAX_PATH + 7];
+#endif
+
+ path[0]= '\0';
+
+#ifndef DBUG_OFF
+ /*
+ Add "debug" subdirectory of the application directory, sometimes PDB will
+ placed here by installation.
+ */
+ GetModuleFileName(NULL, pdb_debug_dir, MAX_PATH);
+ p= strrchr(pdb_debug_dir, '\\');
+ if(p)
+ {
+ *p= 0;
+ strcat_s(pdb_debug_dir, sizeof(pdb_debug_dir), "\\debug;");
+ add_to_symbol_path(path, size, pdb_debug_dir, sizeof(pdb_debug_dir));
+ }
+#endif
+
+ /*
+ Enumerate all modules, and add their directories to the path.
+ Avoid duplicate entries.
+ */
+ hSnap= CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, GetCurrentProcessId());
+ if (hSnap != INVALID_HANDLE_VALUE)
+ {
+ BOOL ret;
+ MODULEENTRY32 mod;
+ mod.dwSize= sizeof(MODULEENTRY32);
+ for (ret= Module32First(hSnap, &mod); ret; ret= Module32Next(hSnap, &mod))
+ {
+ char *module_dir= mod.szExePath;
+ p= strrchr(module_dir,'\\');
+ if (!p)
+ {
+ /*
+ Path separator was not found. Not known to happen, if ever happens,
+ will indicate current directory.
+ */
+ module_dir[0]= '.';
+ module_dir[1]= '\0';
+ }
+ else
+ {
+ *p= '\0';
+ }
+ add_to_symbol_path(path, size, module_dir,sizeof(mod.szExePath));
+ }
+ CloseHandle(hSnap);
+ }
+
+
+ /* Add _NT_SYMBOL_PATH, if present. */
+ envvar= getenv("_NT_SYMBOL_PATH");
+ if(envvar)
+ {
+ strcat_s(path, size, envvar);
+ }
+}
+
+#define MAX_SYMBOL_PATH 32768
+
+/* Platform SDK in VS2003 does not have definition for SYMOPT_NO_PROMPTS*/
+#ifndef SYMOPT_NO_PROMPTS
+#define SYMOPT_NO_PROMPTS 0
+#endif
+
+void my_print_stacktrace(uchar* unused1, ulong unused2)
+{
+ HANDLE hProcess= GetCurrentProcess();
+ HANDLE hThread= GetCurrentThread();
+ static IMAGEHLP_MODULE64 module= {sizeof(module)};
+ static IMAGEHLP_SYMBOL64_PACKAGE package;
+ DWORD64 addr;
+ DWORD machine;
+ int i;
+ CONTEXT context;
+ STACKFRAME64 frame={0};
+ static char symbol_path[MAX_SYMBOL_PATH];
+
+ if(!exception_ptrs)
+ return;
+
+ /* Copy context, as stackwalking on original will unwind the stack */
+ context = *(exception_ptrs->ContextRecord);
+ /*Initialize symbols.*/
+ SymSetOptions(SYMOPT_LOAD_LINES|SYMOPT_NO_PROMPTS|SYMOPT_DEFERRED_LOADS|SYMOPT_DEBUG);
+ get_symbol_path(symbol_path, sizeof(symbol_path));
+ SymInitialize(hProcess, symbol_path, TRUE);
+
+ /*Prepare stackframe for the first StackWalk64 call*/
+ frame.AddrFrame.Mode= frame.AddrPC.Mode= frame.AddrStack.Mode= AddrModeFlat;
+#if (defined _M_IX86)
+ machine= IMAGE_FILE_MACHINE_I386;
+ frame.AddrFrame.Offset= context.Ebp;
+ frame.AddrPC.Offset= context.Eip;
+ frame.AddrStack.Offset= context.Esp;
+#elif (defined _M_X64)
+ machine = IMAGE_FILE_MACHINE_AMD64;
+ frame.AddrFrame.Offset= context.Rbp;
+ frame.AddrPC.Offset= context.Rip;
+ frame.AddrStack.Offset= context.Rsp;
+#else
+ /*There is currently no need to support IA64*/
+#pragma error ("unsupported architecture")
+#endif
+
+ package.sym.SizeOfStruct= sizeof(package.sym);
+ package.sym.MaxNameLength= sizeof(package.name);
+
+ /*Walk the stack, output useful information*/
+ for(i= 0; i< STACKWALK_MAX_FRAMES;i++)
+ {
+ DWORD64 function_offset= 0;
+ DWORD line_offset= 0;
+ IMAGEHLP_LINE64 line= {sizeof(line)};
+ BOOL have_module= FALSE;
+ BOOL have_symbol= FALSE;
+ BOOL have_source= FALSE;
+
+ if(!StackWalk64(machine, hProcess, hThread, &frame, &context, 0, 0, 0 ,0))
+ break;
+ addr= frame.AddrPC.Offset;
+
+ have_module= SymGetModuleInfo64(hProcess,addr,&module);
+#ifdef _M_IX86
+ if(!have_module)
+ {
+ /*
+ ModuleInfo structure has been "compatibly" extended in releases after XP,
+ and its size was increased. To make XP dbghelp.dll function
+ happy, pretend passing the old structure.
+ */
+ module.SizeOfStruct= MODULE64_SIZE_WINXP;
+ have_module= SymGetModuleInfo64(hProcess, addr, &module);
+ }
+#endif
+
+ have_symbol= SymGetSymFromAddr64(hProcess, addr, &function_offset,
+ &(package.sym));
+ have_source= SymGetLineFromAddr64(hProcess, addr, &line_offset, &line);
+
+ my_safe_printf_stderr("%p ", addr);
+ if(have_module)
+ {
+ char *base_image_name= strrchr(module.ImageName, '\\');
+ if(base_image_name)
+ base_image_name++;
+ else
+ base_image_name= module.ImageName;
+ my_safe_printf_stderr("%s!", base_image_name);
+ }
+ if(have_symbol)
+ my_safe_printf_stderr("%s()", package.sym.Name);
+
+ else if(have_module)
+ my_safe_printf_stderr("%s", "???");
+
+ if(have_source)
+ {
+ char *base_file_name= strrchr(line.FileName, '\\');
+ if(base_file_name)
+ base_file_name++;
+ else
+ base_file_name= line.FileName;
+ my_safe_printf_stderr("[%s:%u]",
+ base_file_name, line.LineNumber);
+ }
+ my_safe_printf_stderr("%s", "\n");
+ }
+}
+
+
+/*
+ Write dump. The dump is created in current directory,
+ file name is constructed from executable name plus
+ ".dmp" extension
+*/
+void my_write_core(int unused)
+{
+ char path[MAX_PATH];
+ char dump_fname[MAX_PATH]= "core.dmp";
+
+ if(!exception_ptrs)
+ return;
+
+ if(GetModuleFileName(NULL, path, sizeof(path)))
+ {
+ _splitpath(path, NULL, NULL,dump_fname,NULL);
+ strncat(dump_fname, ".dmp", sizeof(dump_fname));
+ }
+ my_create_minidump(dump_fname, 0, 0);
+}
+
+
+/** Create a minidump.
+ @param name path of minidump file.
+ @param process HANDLE to process. (0 for own process).
+ @param pid Process id.
+*/
+
+void my_create_minidump(const char *name, HANDLE process, DWORD pid)
+{
+ char path[MAX_PATH];
+ MINIDUMP_EXCEPTION_INFORMATION info;
+ HANDLE hFile;
+
+ if (process == 0)
+ {
+ /* Does not need to CloseHandle() for the below. */
+ process= GetCurrentProcess();
+ pid= GetCurrentProcessId();
+ info.ExceptionPointers= exception_ptrs;
+ info.ClientPointers= FALSE;
+ info.ThreadId= GetCurrentThreadId();
+ }
+
+ hFile= CreateFile(name, GENERIC_WRITE, 0, 0, CREATE_ALWAYS,
+ FILE_ATTRIBUTE_NORMAL, 0);
+ if(hFile)
+ {
+ MINIDUMP_TYPE mdt= (MINIDUMP_TYPE) (MiniDumpNormal |
+ MiniDumpWithThreadInfo |
+ MiniDumpWithProcessThreadData);
+ /* Create minidump, use info only if same process. */
+ if(MiniDumpWriteDump(process, pid, hFile, mdt,
+ process ? NULL : &info, 0, 0))
+ {
+ my_safe_printf_stderr("Minidump written to %s\n",
+ _fullpath(path, name, sizeof(path)) ?
+ path : name);
+ }
+ else
+ {
+ my_safe_printf_stderr("MiniDumpWriteDump() failed, last error %d\n",
+ GetLastError());
+ }
+ CloseHandle(hFile);
+ }
+ else
+ {
+ my_safe_printf_stderr("CreateFile(%s) failed, last error %d\n",
+ name, GetLastError());
+ }
+}
+
+
+void my_safe_puts_stderr(const char *val, size_t len)
+{
+ __try
+ {
+ my_write_stderr(val, len);
+ my_safe_printf_stderr("%s", "\n");
+ }
+ __except(EXCEPTION_EXECUTE_HANDLER)
+ {
+ my_safe_printf_stderr("%s", "is an invalid string pointer\n");
+ }
+}
+#endif /* _WIN32 */
+
+
+#ifdef _WIN32
+size_t my_write_stderr(const void *buf, size_t count)
+{
+ DWORD bytes_written;
+ SetFilePointer(GetStdHandle(STD_ERROR_HANDLE), 0, NULL, FILE_END);
+ WriteFile(GetStdHandle(STD_ERROR_HANDLE), buf, (DWORD)count, &bytes_written, NULL);
+ return bytes_written;
+}
+#else
+size_t my_write_stderr(const void *buf, size_t count)
+{
+ return (size_t) write(STDERR_FILENO, buf, count);
+}
+#endif
+
+
+static const char digits[]= "0123456789abcdef";
+
+char *my_safe_utoa(int base, ulonglong val, char *buf)
+{
+ *buf--= 0;
+ do {
+ *buf--= digits[val % base];
+ } while ((val /= base) != 0);
+ return buf + 1;
+}
+
+
+char *my_safe_itoa(int base, longlong val, char *buf)
+{
+ char *orig_buf= buf;
+ const my_bool is_neg= (val < 0);
+ *buf--= 0;
+
+ if (is_neg)
+ val= -val;
+ if (is_neg && base == 16)
+ {
+ int ix;
+ val-= 1;
+ for (ix= 0; ix < 16; ++ix)
+ buf[-ix]= '0';
+ }
+
+ do {
+ *buf--= digits[val % base];
+ } while ((val /= base) != 0);
+
+ if (is_neg && base == 10)
+ *buf--= '-';
+
+ if (is_neg && base == 16)
+ {
+ int ix;
+ buf= orig_buf - 1;
+ for (ix= 0; ix < 16; ++ix, --buf)
+ {
+ switch (*buf)
+ {
+ case '0': *buf= 'f'; break;
+ case '1': *buf= 'e'; break;
+ case '2': *buf= 'd'; break;
+ case '3': *buf= 'c'; break;
+ case '4': *buf= 'b'; break;
+ case '5': *buf= 'a'; break;
+ case '6': *buf= '9'; break;
+ case '7': *buf= '8'; break;
+ case '8': *buf= '7'; break;
+ case '9': *buf= '6'; break;
+ case 'a': *buf= '5'; break;
+ case 'b': *buf= '4'; break;
+ case 'c': *buf= '3'; break;
+ case 'd': *buf= '2'; break;
+ case 'e': *buf= '1'; break;
+ case 'f': *buf= '0'; break;
+ }
+ }
+ }
+ return buf+1;
+}
+
+
+static const char *check_longlong(const char *fmt, my_bool *have_longlong)
+{
+ *have_longlong= FALSE;
+ if (*fmt == 'l')
+ {
+ fmt++;
+ if (*fmt != 'l')
+ *have_longlong= (sizeof(long) == sizeof(longlong));
+ else
+ {
+ fmt++;
+ *have_longlong= TRUE;
+ }
+ }
+ return fmt;
+}
+
+static size_t my_safe_vsnprintf(char *to, size_t size,
+ const char* format, va_list ap)
+{
+ char *start= to;
+ char *end= start + size - 1;
+ for (; *format; ++format)
+ {
+ my_bool have_longlong = FALSE;
+ if (*format != '%')
+ {
+ if (to == end) /* end of buffer */
+ break;
+ *to++= *format; /* copy ordinary char */
+ continue;
+ }
+ ++format; /* skip '%' */
+
+ format= check_longlong(format, &have_longlong);
+
+ switch (*format)
+ {
+ case 'd':
+ case 'i':
+ case 'u':
+ case 'x':
+ case 'p':
+ {
+ longlong ival= 0;
+ ulonglong uval = 0;
+ if (*format == 'p')
+ have_longlong= (sizeof(void *) == sizeof(longlong));
+ if (have_longlong)
+ {
+ if (*format == 'u')
+ uval= va_arg(ap, ulonglong);
+ else
+ ival= va_arg(ap, longlong);
+ }
+ else
+ {
+ if (*format == 'u')
+ uval= va_arg(ap, unsigned int);
+ else
+ ival= va_arg(ap, int);
+ }
+
+ {
+ char buff[22];
+ const int base= (*format == 'x' || *format == 'p') ? 16 : 10;
+ char *val_as_str= (*format == 'u') ?
+ my_safe_utoa(base, uval, &buff[sizeof(buff)-1]) :
+ my_safe_itoa(base, ival, &buff[sizeof(buff)-1]);
+
+ /*
+ Strip off "ffffffff" if we have 'x' format without 'll'
+ Similarly for 'p' format on 32bit systems.
+ */
+ if (base == 16 && !have_longlong && ival < 0)
+ val_as_str+= 8;
+
+ while (*val_as_str && to < end)
+ *to++= *val_as_str++;
+ continue;
+ }
+ }
+ case 's':
+ {
+ const char *val= va_arg(ap, char*);
+ if (!val)
+ val= "(null)";
+ while (*val && to < end)
+ *to++= *val++;
+ continue;
+ }
+ }
+ }
+ *to= 0;
+ return to - start;
+}
+
+
+size_t my_safe_snprintf(char* to, size_t n, const char* fmt, ...)
+{
+ size_t result;
+ va_list args;
+ va_start(args,fmt);
+ result= my_safe_vsnprintf(to, n, fmt, args);
+ va_end(args);
+ return result;
+}
+
+
+size_t my_safe_printf_stderr(const char* fmt, ...)
+{
+ char to[512];
+ size_t result;
+ va_list args;
+ va_start(args,fmt);
+ result= my_safe_vsnprintf(to, sizeof(to), fmt, args);
+ va_end(args);
+ my_write_stderr(to, result);
+ return result;
+}
+
+
+void my_safe_print_system_time()
+{
+ char hrs_buf[3]= "00";
+ char mins_buf[3]= "00";
+ char secs_buf[3]= "00";
+ int base= 10;
+#ifdef _WIN32
+ SYSTEMTIME utc_time;
+ long hrs, mins, secs;
+ GetSystemTime(&utc_time);
+ hrs= utc_time.wHour;
+ mins= utc_time.wMinute;
+ secs= utc_time.wSecond;
+#else
+ /* Using time() instead of my_time() to avoid looping */
+ const time_t curr_time= time(NULL);
+ /* Calculate time of day */
+ const long tmins = curr_time / 60;
+ const long thrs = tmins / 60;
+ const long hrs = thrs % 24;
+ const long mins = tmins % 60;
+ const long secs = curr_time % 60;
+#endif
+
+ my_safe_itoa(base, hrs, &hrs_buf[2]);
+ my_safe_itoa(base, mins, &mins_buf[2]);
+ my_safe_itoa(base, secs, &secs_buf[2]);
+
+ my_safe_printf_stderr("---------- %s:%s:%s UTC - ",
+ hrs_buf, mins_buf, secs_buf);
+}
+
diff --git a/mysql/mysys/string.c b/mysql/mysys/string.c
new file mode 100644
index 0000000..693c28c
--- /dev/null
+++ b/mysql/mysys/string.c
@@ -0,0 +1,187 @@
+/* Copyright (c) 2000, 2015, Oracle and/or its affiliates. All rights reserved.
+
+ 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; version 2 of the License.
+
+ 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+/*
+ Code for handling strings with can grow dynamicly.
+ Copyright Monty Program KB.
+ By monty.
+*/
+
+#include "mysys_priv.h"
+#include "my_sys.h"
+#include <m_string.h>
+
+my_bool init_dynamic_string(DYNAMIC_STRING *str, const char *init_str,
+ size_t init_alloc, size_t alloc_increment)
+{
+ size_t length;
+ DBUG_ENTER("init_dynamic_string");
+
+ if (!alloc_increment)
+ alloc_increment=128;
+ length=1;
+ if (init_str && (length= strlen(init_str)+1) < init_alloc)
+ init_alloc=((length+alloc_increment-1)/alloc_increment)*alloc_increment;
+ if (!init_alloc)
+ init_alloc=alloc_increment;
+
+ if (!(str->str=(char*) my_malloc(key_memory_DYNAMIC_STRING,
+ init_alloc,MYF(MY_WME))))
+ DBUG_RETURN(TRUE);
+ str->length=length-1;
+ if (init_str)
+ memcpy(str->str,init_str,length);
+ str->max_length=init_alloc;
+ str->alloc_increment=alloc_increment;
+ DBUG_RETURN(FALSE);
+}
+
+
+my_bool dynstr_set(DYNAMIC_STRING *str, const char *init_str)
+{
+ uint length=0;
+ DBUG_ENTER("dynstr_set");
+
+ if (init_str && (length= (uint) strlen(init_str)+1) > str->max_length)
+ {
+ str->max_length=((length+str->alloc_increment-1)/str->alloc_increment)*
+ str->alloc_increment;
+ if (!str->max_length)
+ str->max_length=str->alloc_increment;
+ if (!(str->str=(char*) my_realloc(key_memory_DYNAMIC_STRING,
+ str->str,str->max_length,MYF(MY_WME))))
+ DBUG_RETURN(TRUE);
+ }
+ if (init_str)
+ {
+ str->length=length-1;
+ memcpy(str->str,init_str,length);
+ }
+ else
+ str->length=0;
+ DBUG_RETURN(FALSE);
+}
+
+
+my_bool dynstr_realloc(DYNAMIC_STRING *str, size_t additional_size)
+{
+ DBUG_ENTER("dynstr_realloc");
+
+ if (!additional_size) DBUG_RETURN(FALSE);
+ if (str->length + additional_size > str->max_length)
+ {
+ str->max_length=((str->length + additional_size+str->alloc_increment-1)/
+ str->alloc_increment)*str->alloc_increment;
+ if (!(str->str=(char*) my_realloc(key_memory_DYNAMIC_STRING,
+ str->str,str->max_length,MYF(MY_WME))))
+ DBUG_RETURN(TRUE);
+ }
+ DBUG_RETURN(FALSE);
+}
+
+
+my_bool dynstr_append(DYNAMIC_STRING *str, const char *append)
+{
+ return dynstr_append_mem(str,append,(uint) strlen(append));
+}
+
+
+my_bool dynstr_append_mem(DYNAMIC_STRING *str, const char *append,
+ size_t length)
+{
+ char *new_ptr;
+ if (str->length+length >= str->max_length)
+ {
+ size_t new_length=(str->length+length+str->alloc_increment)/
+ str->alloc_increment;
+ new_length*=str->alloc_increment;
+ if (!(new_ptr=(char*) my_realloc(key_memory_DYNAMIC_STRING,
+ str->str,new_length,MYF(MY_WME))))
+ return TRUE;
+ str->str=new_ptr;
+ str->max_length=new_length;
+ }
+ memcpy(str->str + str->length,append,length);
+ str->length+=length;
+ str->str[str->length]=0; /* Safety for C programs */
+ return FALSE;
+}
+
+
+my_bool dynstr_trunc(DYNAMIC_STRING *str, size_t n)
+{
+ str->length-=n;
+ str->str[str->length]= '\0';
+ return FALSE;
+}
+
+/*
+ Concatenates any number of strings, escapes any OS quote in the result then
+ surround the whole affair in another set of quotes which is finally appended
+ to specified DYNAMIC_STRING. This function is especially useful when
+ building strings to be executed with the system() function.
+
+ @param str Dynamic String which will have addtional strings appended.
+ @param append String to be appended.
+ @param ... Optional. Additional string(s) to be appended.
+
+ @note The final argument in the list must be NullS even if no additional
+ options are passed.
+
+ @return True = Success.
+*/
+
+my_bool dynstr_append_os_quoted(DYNAMIC_STRING *str, const char *append, ...)
+{
+#ifdef _WIN32
+ const char *quote_str= "\"";
+ const uint quote_len= 1;
+#else
+ const char *quote_str= "\'";
+ const uint quote_len= 1;
+#endif /* _WIN32 */
+ my_bool ret= TRUE;
+ va_list dirty_text;
+
+ ret&= dynstr_append_mem(str, quote_str, quote_len); /* Leading quote */
+ va_start(dirty_text, append);
+ while (append != NullS)
+ {
+ const char *cur_pos= append;
+ const char *next_pos= cur_pos;
+
+ /* Search for quote in each string and replace with escaped quote */
+ while(*(next_pos= strcend(cur_pos, quote_str[0])) != '\0')
+ {
+ ret&= dynstr_append_mem(str, cur_pos, (uint) (next_pos - cur_pos));
+ ret&= dynstr_append_mem(str ,"\\", 1);
+ ret&= dynstr_append_mem(str, quote_str, quote_len);
+ cur_pos= next_pos + 1;
+ }
+ ret&= dynstr_append_mem(str, cur_pos, (uint) (next_pos - cur_pos));
+ append= va_arg(dirty_text, char *);
+ }
+ va_end(dirty_text);
+ ret&= dynstr_append_mem(str, quote_str, quote_len); /* Trailing quote */
+
+ return ret;
+}
+
+
+void dynstr_free(DYNAMIC_STRING *str)
+{
+ my_free(str->str);
+ str->str= NULL;
+}
diff --git a/mysql/mysys/thr_cond.c b/mysql/mysys/thr_cond.c
new file mode 100644
index 0000000..25decb4
--- /dev/null
+++ b/mysql/mysys/thr_cond.c
@@ -0,0 +1,113 @@
+/* Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved.
+
+ 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; version 2 of the License.
+
+ 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#ifdef SAFE_MUTEX
+
+#include "thr_cond.h"
+#include "my_thread_local.h"
+
+int safe_cond_wait(native_cond_t *cond, my_mutex_t *mp,
+ const char *file, uint line)
+{
+ int error;
+ native_mutex_lock(&mp->global);
+ if (mp->count == 0)
+ {
+ fprintf(stderr,"safe_mutex: Trying to cond_wait on a unlocked mutex at %s, line %d\n",file,line);
+ fflush(stderr);
+ abort();
+ }
+ if (!my_thread_equal(my_thread_self(),mp->thread))
+ {
+ fprintf(stderr,"safe_mutex: Trying to cond_wait on a mutex at %s, line %d that was locked by another thread at: %s, line: %d\n",
+ file,line,mp->file,mp->line);
+ fflush(stderr);
+ abort();
+ }
+
+ if (mp->count-- != 1)
+ {
+ fprintf(stderr,"safe_mutex: Count was %d on locked mutex at %s, line %d\n",
+ mp->count+1, file, line);
+ fflush(stderr);
+ abort();
+ }
+ native_mutex_unlock(&mp->global);
+ error= native_cond_wait(cond,&mp->mutex);
+ native_mutex_lock(&mp->global);
+ if (error)
+ {
+ fprintf(stderr,"safe_mutex: Got error: %d (%d) when doing a safe_mutex_wait at %s, line %d\n", error, errno, file, line);
+ fflush(stderr);
+ abort();
+ }
+ mp->thread= my_thread_self();
+ if (mp->count++)
+ {
+#ifndef DBUG_OFF
+ fprintf(stderr,
+ "safe_mutex: Count was %d in thread 0x%x when locking mutex at %s, line %d\n",
+ mp->count-1, my_thread_var_id(), file, line);
+ fflush(stderr);
+#endif
+ abort();
+ }
+ mp->file= file;
+ mp->line=line;
+ native_mutex_unlock(&mp->global);
+ return error;
+}
+
+
+int safe_cond_timedwait(native_cond_t *cond, my_mutex_t *mp,
+ const struct timespec *abstime,
+ const char *file, uint line)
+{
+ int error;
+ native_mutex_lock(&mp->global);
+ if (mp->count != 1 || !my_thread_equal(my_thread_self(),mp->thread))
+ {
+ fprintf(stderr,"safe_mutex: Trying to cond_wait at %s, line %d on a not hold mutex\n",file,line);
+ fflush(stderr);
+ abort();
+ }
+ mp->count--; /* Mutex will be released */
+ native_mutex_unlock(&mp->global);
+ error= native_cond_timedwait(cond,&mp->mutex,abstime);
+#ifdef EXTRA_DEBUG
+ if (error && (error != EINTR && error != ETIMEDOUT && error != ETIME))
+ {
+ fprintf(stderr,"safe_mutex: Got error: %d (%d) when doing a safe_cond_timedwait at %s, line %d\n", error, errno, file, line);
+ }
+#endif
+ native_mutex_lock(&mp->global);
+ mp->thread= my_thread_self();
+ if (mp->count++)
+ {
+#ifndef DBUG_OFF
+ fprintf(stderr,
+ "safe_mutex: Count was %d in thread 0x%x when locking mutex at %s, line %d (error: %d (%d))\n",
+ mp->count-1, my_thread_var_id(), file, line, error, error);
+ fflush(stderr);
+#endif
+ abort();
+ }
+ mp->file= file;
+ mp->line=line;
+ native_mutex_unlock(&mp->global);
+ return error;
+}
+
+#endif
diff --git a/mysql/mysys/thr_lock.c b/mysql/mysys/thr_lock.c
new file mode 100644
index 0000000..0fc54cb
--- /dev/null
+++ b/mysql/mysys/thr_lock.c
@@ -0,0 +1,1522 @@
+/* Copyright (c) 2000, 2016, Oracle and/or its affiliates. All rights reserved.
+
+ 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; version 2 of the License.
+
+ 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+/*
+Read and write locks for Posix threads. All tread must acquire
+all locks it needs through thr_multi_lock() to avoid dead-locks.
+A lock consists of a master lock (THR_LOCK), and lock instances
+(THR_LOCK_DATA).
+Any thread can have any number of lock instances (read and write:s) on
+any lock. All lock instances must be freed.
+Locks are prioritized according to:
+
+The current lock types are:
+
+TL_READ # Low priority read
+TL_READ_WITH_SHARED_LOCKS
+TL_READ_HIGH_PRIORITY # High priority read
+TL_READ_NO_INSERT # Read without concurrent inserts
+TL_WRITE_ALLOW_WRITE # Write lock that allows other writers
+TL_WRITE_CONCURRENT_INSERT
+ # Insert that can be mixed when selects
+ # Allows lower locks to take over
+TL_WRITE_LOW_PRIORITY # Low priority write
+TL_WRITE # High priority write
+TL_WRITE_ONLY # High priority write
+ # Abort all new lock request with an error
+
+Locks are prioritized according to:
+
+WRITE_ALLOW_WRITE, WRITE_CONCURRENT_INSERT, WRITE_LOW_PRIORITY, READ,
+WRITE, READ_HIGH_PRIORITY and WRITE_ONLY
+
+Locks in the same privilege level are scheduled in first-in-first-out order.
+
+To allow concurrent read/writes locks, with 'WRITE_CONCURRENT_INSERT' one
+should put a pointer to the following functions in the lock structure:
+(If the pointer is zero (default), the function is not called)
+
+check_status:
+ Before giving a lock of type TL_WRITE_CONCURRENT_INSERT,
+ we check if this function exists and returns 0.
+ If not, then the lock is upgraded to TL_WRITE_LOCK
+ In MyISAM this is a simple check if the insert can be done
+ at the end of the datafile.
+update_status:
+ Before a write lock is released, this function is called.
+ In MyISAM this functions updates the count and length of the datafile
+get_status:
+ When one gets a lock this functions is called.
+ In MyISAM this stores the number of rows and size of the datafile
+ for concurrent reads.
+
+The lock algorithm allows one to have one TL_WRITE_CONCURRENT_INSERT
+lock at the same time as multiple read locks.
+
+*/
+
+#include "mysys_priv.h"
+#include "my_sys.h"
+#include "thr_lock.h"
+#include "mysql/psi/mysql_table.h"
+#include <m_string.h>
+#include <errno.h>
+
+ulong locks_immediate = 0L, locks_waited = 0L;
+enum thr_lock_type thr_upgraded_concurrent_insert_lock = TL_WRITE;
+
+/* The following constants are only for debug output */
+#define MAX_THREADS 100
+#define MAX_LOCKS 100
+
+
+LIST *thr_lock_thread_list; /* List of threads in use */
+ulong max_write_lock_count= ~(ulong) 0L;
+
+static void (*before_lock_wait)(void)= 0;
+static void (*after_lock_wait)(void)= 0;
+
+void thr_set_lock_wait_callback(void (*before_wait)(void),
+ void (*after_wait)(void))
+{
+ before_lock_wait= before_wait;
+ after_lock_wait= after_wait;
+}
+
+
+static inline my_bool
+thr_lock_owner_equal(THR_LOCK_INFO *rhs, THR_LOCK_INFO *lhs)
+{
+ return rhs == lhs;
+}
+
+
+#ifdef EXTRA_DEBUG
+#define MAX_FOUND_ERRORS 10 /* Report 10 first errors */
+static uint found_errors=0;
+
+static int check_lock(struct st_lock_list *list, const char* lock_type,
+ const char *where, my_bool same_owner, my_bool no_cond)
+{
+ THR_LOCK_DATA *data,**prev;
+ uint count=0;
+ THR_LOCK_INFO *first_owner= NULL;
+
+ prev= &list->data;
+ if (list->data)
+ {
+ enum thr_lock_type last_lock_type=list->data->type;
+
+ if (same_owner && list->data)
+ first_owner= list->data->owner;
+ for (data=list->data; data && count++ < MAX_LOCKS ; data=data->next)
+ {
+ if (data->type != last_lock_type)
+ last_lock_type=TL_IGNORE;
+ if (data->prev != prev)
+ {
+ my_message_stderr(0, "prev link %d didn't point at "
+ "previous lock at %s: %s", count, lock_type, where);
+ return 1;
+ }
+ if (same_owner &&
+ !thr_lock_owner_equal(data->owner, first_owner) &&
+ last_lock_type != TL_WRITE_ALLOW_WRITE)
+ {
+ my_message_stderr(0, "Found locks from different threads "
+ "in %s: %s", lock_type, where);
+ return 1;
+ }
+ if (no_cond && data->cond)
+ {
+ my_message_stderr(0, "Found active lock with not reset "
+ "cond %s: %s", lock_type, where);
+ return 1;
+ }
+ prev= &data->next;
+ }
+ if (data)
+ {
+ my_message_stderr(0, "found too many locks at %s: %s",
+ lock_type, where);
+ return 1;
+ }
+ }
+ if (prev != list->last)
+ {
+ my_message_stderr(0, "last didn't point at last lock at %s: %s",
+ lock_type, where);
+ return 1;
+ }
+ return 0;
+}
+
+
+static void check_locks(THR_LOCK *lock, const char *where,
+ my_bool allow_no_locks)
+{
+ uint old_found_errors=found_errors;
+ DBUG_ENTER("check_locks");
+
+ if (found_errors < MAX_FOUND_ERRORS)
+ {
+ if (check_lock(&lock->write,"write",where,1,1) |
+ check_lock(&lock->write_wait,"write_wait",where,0,0) |
+ check_lock(&lock->read,"read",where,0,1) |
+ check_lock(&lock->read_wait,"read_wait",where,0,0))
+ found_errors++;
+
+ if (found_errors < MAX_FOUND_ERRORS)
+ {
+ uint count=0;
+ THR_LOCK_DATA *data;
+ for (data=lock->read.data ; data ; data=data->next)
+ {
+ if ((int) data->type == (int) TL_READ_NO_INSERT)
+ count++;
+ /* Protect against infinite loop. */
+ DBUG_ASSERT(count <= lock->read_no_write_count);
+ }
+ if (count != lock->read_no_write_count)
+ {
+ found_errors++;
+ my_message_stderr(0, "at '%s': Locks read_no_write_count "
+ "was %u when it should have been %u",
+ where, lock->read_no_write_count,count);
+ }
+
+ if (!lock->write.data)
+ {
+ if (!allow_no_locks && !lock->read.data &&
+ (lock->write_wait.data || lock->read_wait.data))
+ {
+ found_errors++;
+ my_message_stderr(0, "at '%s': No locks in use but locks "
+ "are in wait queue", where);
+ }
+ if (!lock->write_wait.data)
+ {
+ if (!allow_no_locks && lock->read_wait.data)
+ {
+ found_errors++;
+ my_message_stderr(0, "at '%s': No write locks and "
+ "waiting read locks", where);
+ }
+ }
+ else
+ {
+ if (!allow_no_locks &&
+ (((lock->write_wait.data->type == TL_WRITE_CONCURRENT_INSERT ||
+ lock->write_wait.data->type == TL_WRITE_ALLOW_WRITE) &&
+ !lock->read_no_write_count)))
+ {
+ found_errors++;
+ my_message_stderr(0, "at '%s': Write lock %d waiting "
+ "while no exclusive read locks",
+ where, (int) lock->write_wait.data->type);
+ }
+ }
+ }
+ else
+ { /* Have write lock */
+ if (lock->write_wait.data)
+ {
+ if (!allow_no_locks &&
+ lock->write.data->type == TL_WRITE_ALLOW_WRITE &&
+ lock->write_wait.data->type == TL_WRITE_ALLOW_WRITE)
+ {
+ found_errors++;
+ my_message_stderr(0, "at '%s': Found WRITE_ALLOW_WRITE "
+ "lock waiting for WRITE_ALLOW_WRITE lock", where);
+ }
+ }
+ if (lock->read.data)
+ {
+ if (!thr_lock_owner_equal(lock->write.data->owner,
+ lock->read.data->owner) &&
+ ((lock->write.data->type > TL_WRITE_CONCURRENT_INSERT &&
+ lock->write.data->type != TL_WRITE_ONLY) ||
+ ((lock->write.data->type == TL_WRITE_CONCURRENT_INSERT ||
+ lock->write.data->type == TL_WRITE_ALLOW_WRITE) &&
+ lock->read_no_write_count)))
+ {
+ found_errors++;
+ my_message_stderr(0, "at '%s': Found lock of type %d "
+ "that is write and read locked",
+ where, lock->write.data->type);
+ DBUG_PRINT("warning",("At '%s': Found lock of type %d that is write and read locked\n",
+ where, lock->write.data->type));
+
+ }
+ }
+ if (lock->read_wait.data)
+ {
+ if (!allow_no_locks && lock->write.data->type <= TL_WRITE_CONCURRENT_INSERT &&
+ lock->read_wait.data->type <= TL_READ_HIGH_PRIORITY)
+ {
+ found_errors++;
+ my_message_stderr(0, "at '%s': Found read lock of "
+ "type %d waiting for write lock of type %d",
+ where, (int) lock->read_wait.data->type,
+ (int) lock->write.data->type);
+ }
+ }
+ }
+ }
+ if (found_errors != old_found_errors)
+ {
+ DBUG_PRINT("error",("Found wrong lock"));
+ }
+ }
+ DBUG_VOID_RETURN;
+}
+
+#else /* EXTRA_DEBUG */
+#define check_locks(A,B,C)
+#endif
+
+
+ /* Initialize a lock */
+
+void thr_lock_init(THR_LOCK *lock)
+{
+ DBUG_ENTER("thr_lock_init");
+ memset(lock, 0, sizeof(*lock));
+
+ mysql_mutex_init(key_THR_LOCK_mutex, &lock->mutex, MY_MUTEX_INIT_FAST);
+ lock->read.last= &lock->read.data;
+ lock->read_wait.last= &lock->read_wait.data;
+ lock->write_wait.last= &lock->write_wait.data;
+ lock->write.last= &lock->write.data;
+
+ mysql_mutex_lock(&THR_LOCK_lock); /* Add to locks in use */
+ lock->list.data=(void*) lock;
+ thr_lock_thread_list=list_add(thr_lock_thread_list,&lock->list);
+ mysql_mutex_unlock(&THR_LOCK_lock);
+ DBUG_VOID_RETURN;
+}
+
+
+void thr_lock_delete(THR_LOCK *lock)
+{
+ DBUG_ENTER("thr_lock_delete");
+ mysql_mutex_lock(&THR_LOCK_lock);
+ thr_lock_thread_list=list_delete(thr_lock_thread_list,&lock->list);
+ mysql_mutex_unlock(&THR_LOCK_lock);
+ mysql_mutex_destroy(&lock->mutex);
+ DBUG_VOID_RETURN;
+}
+
+
+void thr_lock_info_init(THR_LOCK_INFO *info, my_thread_id thread_id,
+ mysql_cond_t *suspend)
+{
+ info->thread_id= thread_id;
+ info->suspend= suspend;
+}
+
+ /* Initialize a lock instance */
+
+void thr_lock_data_init(THR_LOCK *lock,THR_LOCK_DATA *data, void *param)
+{
+ data->lock=lock;
+ data->type=TL_UNLOCK;
+ data->owner= 0; /* no owner yet */
+ data->status_param=param;
+ data->cond=0;
+}
+
+
+static inline my_bool
+has_old_lock(THR_LOCK_DATA *data, THR_LOCK_INFO *owner)
+{
+ for ( ; data ; data=data->next)
+ {
+ if (thr_lock_owner_equal(data->owner, owner))
+ return 1; /* Already locked by thread */
+ }
+ return 0;
+}
+
+static void wake_up_waiters(THR_LOCK *lock);
+
+
+static enum enum_thr_lock_result
+wait_for_lock(struct st_lock_list *wait, THR_LOCK_DATA *data,
+ THR_LOCK_INFO *owner,
+ my_bool in_wait_list, ulong lock_wait_timeout)
+{
+ struct timespec wait_timeout;
+ enum enum_thr_lock_result result= THR_LOCK_ABORTED;
+ PSI_stage_info old_stage;
+ DBUG_ENTER("wait_for_lock");
+
+ /*
+ One can use this to signal when a thread is going to wait for a lock.
+ See debug_sync.cc.
+
+ Beware of waiting for a signal here. The lock has aquired its mutex.
+ While waiting on a signal here, the locking thread could not aquire
+ the mutex to release the lock. One could lock up the table
+ completely.
+
+ In detail it works so: When thr_lock() tries to acquire a table
+ lock, it locks the lock->mutex, checks if it can have the lock, and
+ if not, it calls wait_for_lock(). Here it unlocks the table lock
+ while waiting on a condition. The sync point is located before this
+ wait for condition. If we have a waiting action here, we hold the
+ the table locks mutex all the time. Any attempt to look at the table
+ lock by another thread blocks it immediately on lock->mutex. This
+ can easily become an unexpected and unobvious blockage. So be
+ warned: Do not request a WAIT_FOR action for the 'wait_for_lock'
+ sync point unless you really know what you do.
+ */
+ DEBUG_SYNC_C("wait_for_lock");
+
+ if (!in_wait_list)
+ {
+ (*wait->last)=data; /* Wait for lock */
+ data->prev= wait->last;
+ wait->last= &data->next;
+ }
+
+ locks_waited++;
+
+ /* Set up control struct to allow others to abort locks */
+ data->cond= owner->suspend;
+
+ enter_cond_hook(NULL, data->cond, &data->lock->mutex,
+ &stage_waiting_for_table_level_lock, &old_stage,
+ __func__, __FILE__, __LINE__);
+
+ /*
+ Since before_lock_wait potentially can create more threads to
+ scheduler work for, we don't want to call the before_lock_wait
+ callback unless it will really start to wait.
+
+ For similar reasons, we do not want to call before_lock_wait and
+ after_lock_wait for each lap around the loop, so we restrict
+ ourselves to call it before_lock_wait once before starting to wait
+ and once after the thread has exited the wait loop.
+ */
+ if ((!is_killed_hook(NULL) || in_wait_list) && before_lock_wait)
+ (*before_lock_wait)();
+
+ set_timespec(&wait_timeout, lock_wait_timeout);
+ while (!is_killed_hook(NULL) || in_wait_list)
+ {
+ int rc= mysql_cond_timedwait(data->cond, &data->lock->mutex, &wait_timeout);
+ /*
+ We must break the wait if one of the following occurs:
+ - the connection has been aborted (!is_killed_hook()),
+ - the lock has been granted (data->cond is set to NULL by the granter),
+ or the waiting has been aborted (additionally data->type is set to
+ TL_UNLOCK).
+ - the wait has timed out (rc == ETIMEDOUT)
+ Order of checks below is important to not report about timeout
+ if the predicate is true.
+ */
+ if (data->cond == 0)
+ {
+ DBUG_PRINT("thr_lock", ("lock granted/aborted"));
+ break;
+ }
+ if (rc == ETIMEDOUT || rc == ETIME)
+ {
+ /* purecov: begin inspected */
+ DBUG_PRINT("thr_lock", ("lock timed out"));
+ result= THR_LOCK_WAIT_TIMEOUT;
+ break;
+ /* purecov: end */
+ }
+ }
+
+ /*
+ We call the after_lock_wait callback once the wait loop has
+ finished.
+ */
+ if (after_lock_wait)
+ (*after_lock_wait)();
+
+ if (data->cond || data->type == TL_UNLOCK)
+ {
+ if (data->cond) /* aborted or timed out */
+ {
+ if (((*data->prev)=data->next)) /* remove from wait-list */
+ data->next->prev= data->prev;
+ else
+ wait->last=data->prev;
+ data->type= TL_UNLOCK; /* No lock */
+ check_locks(data->lock, "killed or timed out wait_for_lock", 1);
+ wake_up_waiters(data->lock);
+ }
+ else
+ {
+ DBUG_PRINT("thr_lock", ("lock aborted"));
+ check_locks(data->lock, "aborted wait_for_lock", 0);
+ }
+ }
+ else
+ {
+ result= THR_LOCK_SUCCESS;
+ if (data->lock->get_status)
+ (*data->lock->get_status)(data->status_param, 0);
+ check_locks(data->lock,"got wait_for_lock",0);
+ }
+ mysql_mutex_unlock(&data->lock->mutex);
+
+ exit_cond_hook(NULL, &old_stage, __func__, __FILE__, __LINE__);
+
+ DBUG_RETURN(result);
+}
+
+
+enum enum_thr_lock_result
+thr_lock(THR_LOCK_DATA *data, THR_LOCK_INFO *owner,
+ enum thr_lock_type lock_type, ulong lock_wait_timeout)
+{
+ THR_LOCK *lock=data->lock;
+ enum enum_thr_lock_result result= THR_LOCK_SUCCESS;
+ struct st_lock_list *wait_queue;
+ MYSQL_TABLE_WAIT_VARIABLES(locker, state) /* no ';' */
+ DBUG_ENTER("thr_lock");
+
+ data->next=0;
+ data->cond=0; /* safety */
+ data->type=lock_type;
+ data->owner= owner; /* Must be reset ! */
+
+ MYSQL_START_TABLE_LOCK_WAIT(locker, &state, data->m_psi,
+ PSI_TABLE_LOCK, lock_type);
+
+ mysql_mutex_lock(&lock->mutex);
+ DBUG_PRINT("lock",("data: 0x%lx thread: 0x%x lock: 0x%lx type: %d",
+ (long) data, data->owner->thread_id,
+ (long) lock, (int) lock_type));
+ check_locks(lock,(uint) lock_type <= (uint) TL_READ_NO_INSERT ?
+ "enter read_lock" : "enter write_lock",0);
+ if ((int) lock_type <= (int) TL_READ_NO_INSERT)
+ {
+ /* Request for READ lock */
+ if (lock->write.data)
+ {
+ /*
+ We can allow a read lock even if there is already a
+ write lock on the table if they are owned by the same
+ thread or if they satisfy the following lock
+ compatibility matrix:
+
+ Request
+ /-------
+ H|++++ WRITE_ALLOW_WRITE
+ e|+++- WRITE_CONCURRENT_INSERT
+ l ||||
+ d ||||
+ |||\= READ_NO_INSERT
+ ||\ = READ_HIGH_PRIORITY
+ |\ = READ_WITH_SHARED_LOCKS
+ \ = READ
+
+ + = Request can be satisified.
+ - = Request cannot be satisified.
+
+ READ_NO_INSERT and WRITE_ALLOW_WRITE should in principle
+ be incompatible. Before this could have caused starvation of
+ LOCK TABLE READ in InnoDB under high write load. However
+ now READ_NO_INSERT is only used for LOCK TABLES READ and this
+ statement is handled by the MDL subsystem.
+ See Bug#42147 for more information.
+ */
+
+ DBUG_PRINT("lock",("write locked 1 by thread: 0x%x",
+ lock->write.data->owner->thread_id));
+ if (thr_lock_owner_equal(data->owner, lock->write.data->owner) ||
+ (lock->write.data->type < TL_WRITE_CONCURRENT_INSERT) ||
+ ((lock->write.data->type == TL_WRITE_CONCURRENT_INSERT) &&
+ ((int) lock_type <= (int) TL_READ_HIGH_PRIORITY)))
+ { /* Already got a write lock */
+ (*lock->read.last)=data; /* Add to running FIFO */
+ data->prev=lock->read.last;
+ lock->read.last= &data->next;
+ if (lock_type == TL_READ_NO_INSERT)
+ lock->read_no_write_count++;
+ check_locks(lock,"read lock with old write lock",0);
+ if (lock->get_status)
+ (*lock->get_status)(data->status_param, 0);
+ locks_immediate++;
+ goto end;
+ }
+ if (lock->write.data->type == TL_WRITE_ONLY)
+ {
+ /* We are not allowed to get a READ lock in this case */
+ data->type=TL_UNLOCK;
+ result= THR_LOCK_ABORTED; /* Can't wait for this one */
+ goto end;
+ }
+ }
+ else if (!lock->write_wait.data ||
+ lock->write_wait.data->type <= TL_WRITE_LOW_PRIORITY ||
+ lock_type == TL_READ_HIGH_PRIORITY ||
+ has_old_lock(lock->read.data, data->owner)) /* Has old read lock */
+ { /* No important write-locks */
+ (*lock->read.last)=data; /* Add to running FIFO */
+ data->prev=lock->read.last;
+ lock->read.last= &data->next;
+ if (lock->get_status)
+ (*lock->get_status)(data->status_param, 0);
+ if (lock_type == TL_READ_NO_INSERT)
+ lock->read_no_write_count++;
+ check_locks(lock,"read lock with no write locks",0);
+ locks_immediate++;
+ goto end;
+ }
+ /*
+ We're here if there is an active write lock or no write
+ lock but a high priority write waiting in the write_wait queue.
+ In the latter case we should yield the lock to the writer.
+ */
+ wait_queue= &lock->read_wait;
+ }
+ else /* Request for WRITE lock */
+ {
+ if (lock_type == TL_WRITE_CONCURRENT_INSERT && ! lock->check_status)
+ data->type=lock_type= thr_upgraded_concurrent_insert_lock;
+
+ if (lock->write.data) /* If there is a write lock */
+ {
+ if (lock->write.data->type == TL_WRITE_ONLY)
+ {
+ /* purecov: begin tested */
+ /* Allow lock owner to bypass TL_WRITE_ONLY. */
+ if (!thr_lock_owner_equal(data->owner, lock->write.data->owner))
+ {
+ /* We are not allowed to get a lock in this case */
+ data->type=TL_UNLOCK;
+ result= THR_LOCK_ABORTED; /* Can't wait for this one */
+ goto end;
+ }
+ /* purecov: end */
+ }
+
+ /*
+ The idea is to allow us to get a lock at once if we already have
+ a write lock or if there is no pending write locks and if all
+ write locks are of TL_WRITE_ALLOW_WRITE type.
+
+ Note that, since lock requests for the same table are sorted in
+ such way that requests with higher thr_lock_type value come first
+ (with one exception (*)), lock being requested usually has
+ equal or "weaker" type than one which thread might have already
+ acquired.
+ *) The only exception to this rule is case when type of old lock
+ is TL_WRITE_LOW_PRIORITY and type of new lock is changed inside
+ of thr_lock() from TL_WRITE_CONCURRENT_INSERT to TL_WRITE since
+ engine turns out to be not supporting concurrent inserts.
+ Note that since TL_WRITE has the same compatibility rules as
+ TL_WRITE_LOW_PRIORITY (their only difference is priority),
+ it is OK to grant new lock without additional checks in such
+ situation.
+
+ Therefore it is OK to allow acquiring write lock on the table if
+ this thread already holds some write lock on it.
+
+ (INSERT INTO t1 VALUES (f1()), where f1() is stored function which
+ tries to update t1, is an example of statement which requests two
+ different types of write lock on the same table).
+ */
+ DBUG_ASSERT(! has_old_lock(lock->write.data, data->owner) ||
+ ((lock_type <= lock->write.data->type ||
+ (lock_type == TL_WRITE &&
+ lock->write.data->type == TL_WRITE_LOW_PRIORITY))));
+
+ if ((lock_type == TL_WRITE_ALLOW_WRITE &&
+ ! lock->write_wait.data &&
+ lock->write.data->type == TL_WRITE_ALLOW_WRITE) ||
+ has_old_lock(lock->write.data, data->owner))
+ {
+ /*
+ We have already got a write lock or all locks are
+ TL_WRITE_ALLOW_WRITE
+ */
+ DBUG_PRINT("info", ("write_wait.data: 0x%lx old_type: %d",
+ (ulong) lock->write_wait.data,
+ lock->write.data->type));
+
+ (*lock->write.last)=data; /* Add to running fifo */
+ data->prev=lock->write.last;
+ lock->write.last= &data->next;
+ check_locks(lock,"second write lock",0);
+ if (data->lock->get_status)
+ (*data->lock->get_status)(data->status_param, 0);
+ locks_immediate++;
+ goto end;
+ }
+ DBUG_PRINT("lock",("write locked 2 by thread: 0x%x",
+ lock->write.data->owner->thread_id));
+ }
+ else
+ {
+ DBUG_PRINT("info", ("write_wait.data: 0x%lx",
+ (ulong) lock->write_wait.data));
+ if (!lock->write_wait.data)
+ { /* no scheduled write locks */
+ my_bool concurrent_insert= 0;
+ if (lock_type == TL_WRITE_CONCURRENT_INSERT)
+ {
+ concurrent_insert= 1;
+ if ((*lock->check_status)(data->status_param))
+ {
+ concurrent_insert= 0;
+ data->type=lock_type= thr_upgraded_concurrent_insert_lock;
+ }
+ }
+
+ if (!lock->read.data ||
+ (lock_type <= TL_WRITE_CONCURRENT_INSERT &&
+ ((lock_type != TL_WRITE_CONCURRENT_INSERT &&
+ lock_type != TL_WRITE_ALLOW_WRITE) ||
+ !lock->read_no_write_count)))
+ {
+ (*lock->write.last)=data; /* Add as current write lock */
+ data->prev=lock->write.last;
+ lock->write.last= &data->next;
+ if (data->lock->get_status)
+ (*data->lock->get_status)(data->status_param, concurrent_insert);
+ check_locks(lock,"only write lock",0);
+ locks_immediate++;
+ goto end;
+ }
+ }
+ DBUG_PRINT("lock",("write locked 3 by thread: 0x%x type: %d",
+ lock->read.data->owner->thread_id, data->type));
+ }
+ wait_queue= &lock->write_wait;
+ }
+ /* Can't get lock yet; Wait for it */
+ result= wait_for_lock(wait_queue, data, owner, 0,
+ lock_wait_timeout);
+ MYSQL_END_TABLE_LOCK_WAIT(locker);
+ DBUG_RETURN(result);
+end:
+ mysql_mutex_unlock(&lock->mutex);
+ MYSQL_END_TABLE_LOCK_WAIT(locker);
+ DBUG_RETURN(result);
+}
+
+
+static inline void free_all_read_locks(THR_LOCK *lock,
+ my_bool using_concurrent_insert)
+{
+ THR_LOCK_DATA *data=lock->read_wait.data;
+
+ check_locks(lock,"before freeing read locks",1);
+
+ /* move all locks from read_wait list to read list */
+ (*lock->read.last)=data;
+ data->prev=lock->read.last;
+ lock->read.last=lock->read_wait.last;
+
+ /* Clear read_wait list */
+ lock->read_wait.last= &lock->read_wait.data;
+
+ do
+ {
+ mysql_cond_t *cond= data->cond;
+ if ((int) data->type == (int) TL_READ_NO_INSERT)
+ {
+ if (using_concurrent_insert)
+ {
+ /*
+ We can't free this lock;
+ Link lock away from read chain back into read_wait chain
+ */
+ if (((*data->prev)=data->next))
+ data->next->prev=data->prev;
+ else
+ lock->read.last=data->prev;
+ *lock->read_wait.last= data;
+ data->prev= lock->read_wait.last;
+ lock->read_wait.last= &data->next;
+ continue;
+ }
+ lock->read_no_write_count++;
+ }
+ /* purecov: begin inspected */
+ DBUG_PRINT("lock",("giving read lock to thread: 0x%x",
+ data->owner->thread_id));
+ /* purecov: end */
+ data->cond=0; /* Mark thread free */
+ mysql_cond_signal(cond);
+ } while ((data=data->next));
+ *lock->read_wait.last=0;
+ if (!lock->read_wait.data)
+ lock->write_lock_count=0;
+ check_locks(lock,"after giving read locks",0);
+}
+
+ /* Unlock lock and free next thread on same lock */
+
+void thr_unlock(THR_LOCK_DATA *data)
+{
+ THR_LOCK *lock=data->lock;
+ enum thr_lock_type lock_type=data->type;
+ DBUG_ENTER("thr_unlock");
+ DBUG_PRINT("lock",("data: 0x%lx thread: 0x%x lock: 0x%lx",
+ (long) data, data->owner->thread_id, (long) lock));
+ mysql_mutex_lock(&lock->mutex);
+ check_locks(lock,"start of release lock",0);
+
+ if (((*data->prev)=data->next)) /* remove from lock-list */
+ data->next->prev= data->prev;
+ else if (lock_type <= TL_READ_NO_INSERT)
+ lock->read.last=data->prev;
+ else
+ lock->write.last=data->prev;
+ if (lock_type >= TL_WRITE_CONCURRENT_INSERT)
+ {
+ if (lock->update_status)
+ (*lock->update_status)(data->status_param);
+ }
+ else
+ {
+ if (lock->restore_status)
+ (*lock->restore_status)(data->status_param);
+ }
+ if (lock_type == TL_READ_NO_INSERT)
+ lock->read_no_write_count--;
+ data->type=TL_UNLOCK; /* Mark unlocked */
+ MYSQL_UNLOCK_TABLE(data->m_psi);
+ check_locks(lock,"after releasing lock",1);
+ wake_up_waiters(lock);
+ mysql_mutex_unlock(&lock->mutex);
+ DBUG_VOID_RETURN;
+}
+
+
+/**
+ @brief Wake up all threads which pending requests for the lock
+ can be satisfied.
+
+ @param lock Lock for which threads should be woken up
+
+*/
+
+static void wake_up_waiters(THR_LOCK *lock)
+{
+ THR_LOCK_DATA *data;
+ enum thr_lock_type lock_type;
+
+ DBUG_ENTER("wake_up_waiters");
+
+ if (!lock->write.data) /* If no active write locks */
+ {
+ data=lock->write_wait.data;
+ if (!lock->read.data) /* If no more locks in use */
+ {
+ /* Release write-locks with TL_WRITE or TL_WRITE_ONLY priority first */
+ if (data &&
+ (data->type != TL_WRITE_LOW_PRIORITY || !lock->read_wait.data ||
+ lock->read_wait.data->type < TL_READ_HIGH_PRIORITY))
+ {
+ if (lock->write_lock_count++ > max_write_lock_count)
+ {
+ /* Too many write locks in a row; Release all waiting read locks */
+ lock->write_lock_count=0;
+ if (lock->read_wait.data)
+ {
+ DBUG_PRINT("info",("Freeing all read_locks because of max_write_lock_count"));
+ free_all_read_locks(lock,0);
+ goto end;
+ }
+ }
+ for (;;)
+ {
+ if (((*data->prev)=data->next)) /* remove from wait-list */
+ data->next->prev= data->prev;
+ else
+ lock->write_wait.last=data->prev;
+ (*lock->write.last)=data; /* Put in execute list */
+ data->prev=lock->write.last;
+ data->next=0;
+ lock->write.last= &data->next;
+ if (data->type == TL_WRITE_CONCURRENT_INSERT &&
+ (*lock->check_status)(data->status_param))
+ data->type=TL_WRITE; /* Upgrade lock */
+ /* purecov: begin inspected */
+ DBUG_PRINT("lock",("giving write lock of type %d to thread: 0x%x",
+ data->type, data->owner->thread_id));
+ /* purecov: end */
+ {
+ mysql_cond_t *cond= data->cond;
+ data->cond=0; /* Mark thread free */
+ mysql_cond_signal(cond); /* Start waiting thread */
+ }
+ if (data->type != TL_WRITE_ALLOW_WRITE ||
+ !lock->write_wait.data ||
+ lock->write_wait.data->type != TL_WRITE_ALLOW_WRITE)
+ break;
+ data=lock->write_wait.data; /* Free this too */
+ }
+ if (data->type >= TL_WRITE_LOW_PRIORITY)
+ goto end;
+ /* Release possible read locks together with the write lock */
+ }
+ if (lock->read_wait.data)
+ free_all_read_locks(lock,
+ data &&
+ (data->type == TL_WRITE_CONCURRENT_INSERT ||
+ data->type == TL_WRITE_ALLOW_WRITE));
+ else
+ {
+ DBUG_PRINT("lock",("No waiting read locks to free"));
+ }
+ }
+ else if (data &&
+ (lock_type= data->type) <= TL_WRITE_CONCURRENT_INSERT &&
+ ((lock_type != TL_WRITE_CONCURRENT_INSERT &&
+ lock_type != TL_WRITE_ALLOW_WRITE) ||
+ !lock->read_no_write_count))
+ {
+ /*
+ For ALLOW_READ, WRITE_ALLOW_WRITE or CONCURRENT_INSERT locks
+ start WRITE locks together with the READ locks
+ */
+ if (lock_type == TL_WRITE_CONCURRENT_INSERT &&
+ (*lock->check_status)(data->status_param))
+ {
+ data->type=TL_WRITE; /* Upgrade lock */
+ if (lock->read_wait.data)
+ free_all_read_locks(lock,0);
+ goto end;
+ }
+ do {
+ mysql_cond_t *cond= data->cond;
+ if (((*data->prev)=data->next)) /* remove from wait-list */
+ data->next->prev= data->prev;
+ else
+ lock->write_wait.last=data->prev;
+ (*lock->write.last)=data; /* Put in execute list */
+ data->prev=lock->write.last;
+ lock->write.last= &data->next;
+ data->next=0; /* Only one write lock */
+ data->cond=0; /* Mark thread free */
+ mysql_cond_signal(cond); /* Start waiting thread */
+ } while (lock_type == TL_WRITE_ALLOW_WRITE &&
+ (data=lock->write_wait.data) &&
+ data->type == TL_WRITE_ALLOW_WRITE);
+ if (lock->read_wait.data)
+ free_all_read_locks(lock,
+ (lock_type == TL_WRITE_CONCURRENT_INSERT ||
+ lock_type == TL_WRITE_ALLOW_WRITE));
+ }
+ else if (!data && lock->read_wait.data)
+ free_all_read_locks(lock,0);
+ }
+end:
+ check_locks(lock, "after waking up waiters", 0);
+ DBUG_VOID_RETURN;
+}
+
+
+/*
+** Get all locks in a specific order to avoid dead-locks
+** Sort acording to lock position and put write_locks before read_locks if
+** lock on same lock.
+*/
+
+
+#define LOCK_CMP(A,B) ((uchar*) (A->lock) - (uint) ((A)->type) < (uchar*) (B->lock)- (uint) ((B)->type))
+
+static void sort_locks(THR_LOCK_DATA **data,uint count)
+{
+ THR_LOCK_DATA **pos,**end,**prev,*tmp;
+
+ /* Sort locks with insertion sort (fast because almost always few locks) */
+
+ for (pos=data+1,end=data+count; pos < end ; pos++)
+ {
+ tmp= *pos;
+ if (LOCK_CMP(tmp,pos[-1]))
+ {
+ prev=pos;
+ do {
+ prev[0]=prev[-1];
+ } while (--prev != data && LOCK_CMP(tmp,prev[-1]));
+ prev[0]=tmp;
+ }
+ }
+}
+
+
+enum enum_thr_lock_result
+thr_multi_lock(THR_LOCK_DATA **data, uint count, THR_LOCK_INFO *owner,
+ ulong lock_wait_timeout)
+{
+ THR_LOCK_DATA **pos,**end;
+ DBUG_ENTER("thr_multi_lock");
+ DBUG_PRINT("lock",("data: 0x%lx count: %d", (long) data, count));
+ if (count > 1)
+ sort_locks(data,count);
+ /* lock everything */
+ for (pos=data,end=data+count; pos < end ; pos++)
+ {
+ enum enum_thr_lock_result result= thr_lock(*pos, owner, (*pos)->type,
+ lock_wait_timeout);
+ if (result != THR_LOCK_SUCCESS)
+ { /* Aborted */
+ thr_multi_unlock(data,(uint) (pos-data));
+ DBUG_RETURN(result);
+ }
+ DEBUG_SYNC_C("thr_multi_lock_after_thr_lock");
+#ifdef MAIN
+ printf("Thread: T@%u Got lock: 0x%lx type: %d\n",
+ pos[0]->owner->thread_id, (long) pos[0]->lock, pos[0]->type);
+ fflush(stdout);
+#endif
+ }
+ thr_lock_merge_status(data, count);
+ DBUG_RETURN(THR_LOCK_SUCCESS);
+}
+
+
+/**
+ Ensure that all locks for a given table have the same
+ status_param.
+
+ This is a MyISAM and possibly Maria specific crutch. MyISAM
+ engine stores data file length, record count and other table
+ properties in status_param member of handler. When a table is
+ locked, connection-local copy is made from a global copy
+ (myisam_share) by mi_get_status(). When a table is unlocked,
+ the changed status is transferred back to the global share by
+ mi_update_status().
+
+ One thing MyISAM doesn't do is to ensure that when the same
+ table is opened twice in a connection all instances share the
+ same status_param. This is necessary, however: for one, to keep
+ all instances of a connection "on the same page" with regard to
+ the current state of the table. For other, unless this is done,
+ myisam_share will always get updated from the last unlocked
+ instance (in mi_update_status()), and when this instance was not
+ the one that was used to update data, records may be lost.
+
+ For each table, this function looks up the last lock_data in the
+ list of acquired locks, and makes sure that all other instances
+ share status_param with it.
+*/
+
+void
+thr_lock_merge_status(THR_LOCK_DATA **data, uint count)
+{
+ THR_LOCK_DATA **pos= data;
+ THR_LOCK_DATA **end= data + count;
+ if (count > 1)
+ {
+ THR_LOCK_DATA *last_lock= end[-1];
+ pos=end-1;
+ do
+ {
+ pos--;
+ if (last_lock->lock == (*pos)->lock &&
+ last_lock->lock->copy_status)
+ {
+ if (last_lock->type <= TL_READ_NO_INSERT)
+ {
+ THR_LOCK_DATA **read_lock;
+ /*
+ If we are locking the same table with read locks we must ensure
+ that all tables share the status of the last write lock or
+ the same read lock.
+ */
+ for (;
+ (*pos)->type <= TL_READ_NO_INSERT &&
+ pos != data &&
+ pos[-1]->lock == (*pos)->lock ;
+ pos--) ;
+
+ read_lock = pos+1;
+ do
+ {
+ (last_lock->lock->copy_status)((*read_lock)->status_param,
+ (*pos)->status_param);
+ } while (*(read_lock++) != last_lock);
+ last_lock= (*pos); /* Point at last write lock */
+ }
+ else
+ (*last_lock->lock->copy_status)((*pos)->status_param,
+ last_lock->status_param);
+ }
+ else
+ last_lock=(*pos);
+ } while (pos != data);
+ }
+}
+
+ /* free all locks */
+
+void thr_multi_unlock(THR_LOCK_DATA **data,uint count)
+{
+ THR_LOCK_DATA **pos,**end;
+ DBUG_ENTER("thr_multi_unlock");
+ DBUG_PRINT("lock",("data: 0x%lx count: %d", (long) data, count));
+
+ for (pos=data,end=data+count; pos < end ; pos++)
+ {
+#ifdef MAIN
+ printf("Thread: T@%u Rel lock: 0x%lx type: %d\n",
+ pos[0]->owner->thread_id, (long) pos[0]->lock, pos[0]->type);
+ fflush(stdout);
+#endif
+ if ((*pos)->type != TL_UNLOCK)
+ thr_unlock(*pos);
+ else
+ {
+ DBUG_PRINT("lock",("Free lock: data: 0x%lx thread: 0x%x lock: 0x%lx",
+ (long) *pos, (*pos)->owner->thread_id,
+ (long) (*pos)->lock));
+ }
+ }
+ DBUG_VOID_RETURN;
+}
+
+/*
+ Abort all threads waiting for a lock. The lock will be upgraded to
+ TL_WRITE_ONLY to abort any new accesses to the lock
+*/
+
+void thr_abort_locks(THR_LOCK *lock, my_bool upgrade_lock)
+{
+ THR_LOCK_DATA *data;
+ DBUG_ENTER("thr_abort_locks");
+ mysql_mutex_lock(&lock->mutex);
+
+ for (data=lock->read_wait.data; data ; data=data->next)
+ {
+ data->type=TL_UNLOCK; /* Mark killed */
+ /* It's safe to signal the cond first: we're still holding the mutex. */
+ mysql_cond_signal(data->cond);
+ data->cond=0; /* Removed from list */
+ }
+ for (data=lock->write_wait.data; data ; data=data->next)
+ {
+ data->type=TL_UNLOCK;
+ mysql_cond_signal(data->cond);
+ data->cond=0;
+ }
+ lock->read_wait.last= &lock->read_wait.data;
+ lock->write_wait.last= &lock->write_wait.data;
+ lock->read_wait.data=lock->write_wait.data=0;
+ if (upgrade_lock && lock->write.data)
+ lock->write.data->type=TL_WRITE_ONLY;
+ mysql_mutex_unlock(&lock->mutex);
+ DBUG_VOID_RETURN;
+}
+
+
+/*
+ Abort all locks for specific table/thread combination
+
+ This is used to abort all locks for a specific thread
+*/
+
+void thr_abort_locks_for_thread(THR_LOCK *lock, my_thread_id thread_id)
+{
+ THR_LOCK_DATA *data;
+ DBUG_ENTER("thr_abort_locks_for_thread");
+
+ mysql_mutex_lock(&lock->mutex);
+ for (data= lock->read_wait.data; data ; data= data->next)
+ {
+ if (data->owner->thread_id == thread_id) /* purecov: tested */
+ {
+ DBUG_PRINT("info",("Aborting read-wait lock"));
+ data->type= TL_UNLOCK; /* Mark killed */
+ /* It's safe to signal the cond first: we're still holding the mutex. */
+ mysql_cond_signal(data->cond);
+ data->cond= 0; /* Removed from list */
+
+ if (((*data->prev)= data->next))
+ data->next->prev= data->prev;
+ else
+ lock->read_wait.last= data->prev;
+ }
+ }
+ for (data= lock->write_wait.data; data ; data= data->next)
+ {
+ if (data->owner->thread_id == thread_id) /* purecov: tested */
+ {
+ DBUG_PRINT("info",("Aborting write-wait lock"));
+ data->type= TL_UNLOCK;
+ mysql_cond_signal(data->cond);
+ data->cond= 0;
+
+ if (((*data->prev)= data->next))
+ data->next->prev= data->prev;
+ else
+ lock->write_wait.last= data->prev;
+ }
+ }
+ wake_up_waiters(lock);
+ mysql_mutex_unlock(&lock->mutex);
+ DBUG_VOID_RETURN;
+}
+
+
+/*
+ Downgrade a WRITE_* to a lower WRITE level
+ SYNOPSIS
+ thr_downgrade_write_lock()
+ in_data Lock data of thread downgrading its lock
+ new_lock_type New write lock type
+ RETURN VALUE
+ NONE
+ DESCRIPTION
+ This can be used to downgrade a lock already owned. When the downgrade
+ occurs also other waiters, both readers and writers can be allowed to
+ start.
+ The previous lock is often TL_WRITE_ONLY but can also be
+ TL_WRITE. The normal downgrade variants are:
+ TL_WRITE_ONLY => TL_WRITE after a short exclusive lock while holding a
+ write table lock
+ TL_WRITE_ONLY => TL_WRITE_ALLOW_WRITE After a short exclusive lock after
+ already earlier having dongraded lock to TL_WRITE_ALLOW_WRITE
+ The implementation is conservative and rather don't start rather than
+ go on unknown paths to start, the common cases are handled.
+
+ NOTE:
+ In its current implementation it is only allowed to downgrade from
+ TL_WRITE_ONLY. In this case there are no waiters. Thus no wake up
+ logic is required.
+*/
+
+void thr_downgrade_write_lock(THR_LOCK_DATA *in_data,
+ enum thr_lock_type new_lock_type)
+{
+ THR_LOCK *lock=in_data->lock;
+#ifndef DBUG_OFF
+ enum thr_lock_type old_lock_type= in_data->type;
+#endif
+ DBUG_ENTER("thr_downgrade_write_only_lock");
+
+ mysql_mutex_lock(&lock->mutex);
+ DBUG_ASSERT(old_lock_type == TL_WRITE_ONLY);
+ DBUG_ASSERT(old_lock_type > new_lock_type);
+ in_data->type= new_lock_type;
+ check_locks(lock,"after downgrading lock",0);
+
+ mysql_mutex_unlock(&lock->mutex);
+ DBUG_VOID_RETURN;
+}
+
+
+#include <my_sys.h>
+
+static void thr_print_lock(const char* name,struct st_lock_list *list)
+{
+ THR_LOCK_DATA *data,**prev;
+ uint count=0;
+
+ if (list->data)
+ {
+ printf("%-10s: ",name);
+ prev= &list->data;
+ for (data=list->data; data && count++ < MAX_LOCKS ; data=data->next)
+ {
+ printf("0x%lx (%u:%d); ", (ulong) data, data->owner->thread_id,
+ (int) data->type);
+ if (data->prev != prev)
+ printf("\nWarning: prev didn't point at previous lock\n");
+ prev= &data->next;
+ }
+ puts("");
+ if (prev != list->last)
+ printf("Warning: last didn't point at last lock\n");
+ }
+}
+
+void thr_print_locks(void)
+{
+ LIST *list;
+ uint count=0;
+
+ mysql_mutex_lock(&THR_LOCK_lock);
+ puts("Current locks:");
+ for (list= thr_lock_thread_list; list && count++ < MAX_THREADS;
+ list= list_rest(list))
+ {
+ THR_LOCK *lock=(THR_LOCK*) list->data;
+ mysql_mutex_lock(&lock->mutex);
+ printf("lock: 0x%lx:",(ulong) lock);
+ if ((lock->write_wait.data || lock->read_wait.data) &&
+ (! lock->read.data && ! lock->write.data))
+ printf(" WARNING: ");
+ if (lock->write.data)
+ printf(" write");
+ if (lock->write_wait.data)
+ printf(" write_wait");
+ if (lock->read.data)
+ printf(" read");
+ if (lock->read_wait.data)
+ printf(" read_wait");
+ puts("");
+ thr_print_lock("write",&lock->write);
+ thr_print_lock("write_wait",&lock->write_wait);
+ thr_print_lock("read",&lock->read);
+ thr_print_lock("read_wait",&lock->read_wait);
+ mysql_mutex_unlock(&lock->mutex);
+ puts("");
+ }
+ fflush(stdout);
+ mysql_mutex_unlock(&THR_LOCK_lock);
+}
+
+
+/*****************************************************************************
+** Test of thread locks
+****************************************************************************/
+
+#ifdef MAIN
+
+struct st_test {
+ uint lock_nr;
+ enum thr_lock_type lock_type;
+};
+
+THR_LOCK locks[5]; /* 4 locks */
+
+struct st_test test_0[] = {{0,TL_READ}}; /* One lock */
+struct st_test test_1[] = {{0,TL_READ},{0,TL_WRITE}}; /* Read and write lock of lock 0 */
+struct st_test test_2[] = {{1,TL_WRITE},{0,TL_READ},{2,TL_READ}};
+struct st_test test_3[] = {{2,TL_WRITE},{1,TL_READ},{0,TL_READ}}; /* Deadlock with test_2 ? */
+struct st_test test_4[] = {{0,TL_WRITE},{0,TL_READ},{0,TL_WRITE},{0,TL_READ}};
+struct st_test test_5[] = {{0,TL_READ},{1,TL_READ},{2,TL_READ},{3,TL_READ}}; /* Many reads */
+struct st_test test_6[] = {{0,TL_WRITE},{1,TL_WRITE},{2,TL_WRITE},{3,TL_WRITE}}; /* Many writes */
+struct st_test test_7[] = {{3,TL_READ}};
+struct st_test test_8[] = {{1,TL_READ_NO_INSERT},{2,TL_READ_NO_INSERT},{3,TL_READ_NO_INSERT}}; /* Should be quick */
+struct st_test test_9[] = {{4,TL_READ_HIGH_PRIORITY}};
+struct st_test test_10[] ={{4,TL_WRITE}};
+struct st_test test_11[] = {{0,TL_WRITE_LOW_PRIORITY},{1,TL_WRITE_LOW_PRIORITY},{2,TL_WRITE_LOW_PRIORITY},{3,TL_WRITE_LOW_PRIORITY}}; /* Many writes */
+struct st_test test_12[] = {{0,TL_WRITE_CONCURRENT_INSERT},{1,TL_WRITE_CONCURRENT_INSERT},{2,TL_WRITE_CONCURRENT_INSERT},{3,TL_WRITE_CONCURRENT_INSERT}};
+struct st_test test_13[] = {{0,TL_WRITE_CONCURRENT_INSERT},{1,TL_READ}};
+struct st_test test_14[] = {{0,TL_WRITE_ALLOW_WRITE},{1,TL_READ}};
+struct st_test test_15[] = {{0,TL_WRITE_ALLOW_WRITE},{1,TL_WRITE_ALLOW_WRITE}};
+
+struct st_test *tests[] = {test_0,test_1,test_2,test_3,test_4,test_5,test_6,
+ test_7,test_8,test_9,test_10,test_11,test_12,
+ test_13,test_14,test_15};
+int lock_counts[]= {sizeof(test_0)/sizeof(struct st_test),
+ sizeof(test_1)/sizeof(struct st_test),
+ sizeof(test_2)/sizeof(struct st_test),
+ sizeof(test_3)/sizeof(struct st_test),
+ sizeof(test_4)/sizeof(struct st_test),
+ sizeof(test_5)/sizeof(struct st_test),
+ sizeof(test_6)/sizeof(struct st_test),
+ sizeof(test_7)/sizeof(struct st_test),
+ sizeof(test_8)/sizeof(struct st_test),
+ sizeof(test_9)/sizeof(struct st_test),
+ sizeof(test_10)/sizeof(struct st_test),
+ sizeof(test_11)/sizeof(struct st_test),
+ sizeof(test_12)/sizeof(struct st_test),
+ sizeof(test_13)/sizeof(struct st_test),
+ sizeof(test_14)/sizeof(struct st_test),
+ sizeof(test_15)/sizeof(struct st_test)
+};
+
+
+static mysql_cond_t COND_thread_count;
+static mysql_mutex_t LOCK_thread_count;
+static uint thread_count;
+static ulong sum=0;
+
+#define MAX_LOCK_COUNT 8
+#define TEST_TIMEOUT 100000
+
+/* The following functions is for WRITE_CONCURRENT_INSERT */
+
+static void test_get_status(void* param MY_ATTRIBUTE((unused)),
+ int concurrent_insert MY_ATTRIBUTE((unused)))
+{
+}
+
+static void test_update_status(void* param MY_ATTRIBUTE((unused)))
+{
+}
+
+static void test_copy_status(void* to MY_ATTRIBUTE((unused)) ,
+ void *from MY_ATTRIBUTE((unused)))
+{
+}
+
+static my_bool test_check_status(void* param MY_ATTRIBUTE((unused)))
+{
+ return 0;
+}
+
+
+static void *test_thread(void *arg)
+{
+ int i,j,param=*((int*) arg);
+ THR_LOCK_DATA data[MAX_LOCK_COUNT];
+ THR_LOCK_INFO lock_info;
+ THR_LOCK_DATA *multi_locks[MAX_LOCK_COUNT];
+ my_thread_id id;
+ mysql_cond_t COND_thr_lock;
+
+ id= param + 1; /* Main thread uses value 0. */
+ mysql_cond_init(0, &COND_thr_lock);
+
+ printf("Thread T@%d started\n", id);
+ fflush(stdout);
+
+ thr_lock_info_init(&lock_info, id, &COND_thr_lock);
+ for (i=0; i < lock_counts[param] ; i++)
+ {
+ thr_lock_data_init(locks+tests[param][i].lock_nr,data+i,NULL);
+ data[i].m_psi= NULL;
+ }
+ for (j=1 ; j < 10 ; j++) /* try locking 10 times */
+ {
+ for (i=0; i < lock_counts[param] ; i++)
+ { /* Init multi locks */
+ multi_locks[i]= &data[i];
+ data[i].type= tests[param][i].lock_type;
+ }
+ thr_multi_lock(multi_locks, lock_counts[param], &lock_info, TEST_TIMEOUT);
+ mysql_mutex_lock(&LOCK_thread_count);
+ {
+ int tmp=rand() & 7; /* Do something from 0-2 sec */
+ if (tmp == 0)
+ sleep(1);
+ else if (tmp == 1)
+ sleep(2);
+ else
+ {
+ ulong k;
+ for (k=0 ; k < (ulong) (tmp-2)*100000L ; k++)
+ sum+=k;
+ }
+ }
+ mysql_mutex_unlock(&LOCK_thread_count);
+ thr_multi_unlock(multi_locks,lock_counts[param]);
+ }
+
+ printf("Thread T@%d ended\n", id);
+ fflush(stdout);
+ thr_print_locks();
+ mysql_mutex_lock(&LOCK_thread_count);
+ thread_count--;
+ mysql_cond_signal(&COND_thread_count); /* Tell main we are ready */
+ mysql_mutex_unlock(&LOCK_thread_count);
+ mysql_cond_destroy(&COND_thr_lock);
+ free((uchar*) arg);
+ return 0;
+}
+
+
+int main(int argc MY_ATTRIBUTE((unused)),char **argv MY_ATTRIBUTE((unused)))
+{
+ my_thread_handle tid;
+ my_thread_attr_t thr_attr;
+ int i,*param,error;
+ MY_INIT(argv[0]);
+ if (argc > 1 && argv[1][0] == '-' && argv[1][1] == '#')
+ DBUG_PUSH(argv[1]+2);
+
+ printf("Main thread: T@%u\n", 0); /* 0 for main thread, 1+ for test_thread */
+
+ if ((error= mysql_cond_init(0, &COND_thread_count)))
+ {
+ my_message_stderr(0, "Got error %d from mysql_cond_init", errno);
+ exit(1);
+ }
+ if ((error= mysql_mutex_init(0, &LOCK_thread_count, MY_MUTEX_INIT_FAST)))
+ {
+ my_message_stderr(0, "Got error %d from mysql_cond_init", errno);
+ exit(1);
+ }
+
+ for (i=0 ; i < (int) array_elements(locks) ; i++)
+ {
+ thr_lock_init(locks+i);
+ locks[i].check_status= test_check_status;
+ locks[i].update_status=test_update_status;
+ locks[i].copy_status= test_copy_status;
+ locks[i].get_status= test_get_status;
+ }
+ if ((error=my_thread_attr_init(&thr_attr)))
+ {
+ my_message_stderr(0, "Got error %d from pthread_attr_init",errno);
+ exit(1);
+ }
+ if ((error= my_thread_attr_setdetachstate(&thr_attr, MY_THREAD_CREATE_DETACHED)))
+ {
+ my_message_stderr(0, "Got error %d from "
+ "my_thread_attr_setdetachstate", errno);
+ exit(1);
+ }
+ if ((error= my_thread_attr_setstacksize(&thr_attr,65536L)))
+ {
+ my_message_stderr(0, "Got error %d from "
+ "my_thread_attr_setstacksize", error);
+ exit(1);
+ }
+ for (i=0 ; i < (int) array_elements(lock_counts) ; i++)
+ {
+ param=(int*) malloc(sizeof(int));
+ *param=i;
+
+ if ((error= mysql_mutex_lock(&LOCK_thread_count)))
+ {
+ my_message_stderr(0, "Got error %d from mysql_mutex_lock",
+ errno);
+ exit(1);
+ }
+ if ((error= mysql_thread_create(0,
+ &tid, &thr_attr, test_thread,
+ (void*) param)))
+ {
+ my_message_stderr(0, "Got error %d from mysql_thread_create",
+ errno);
+ mysql_mutex_unlock(&LOCK_thread_count);
+ exit(1);
+ }
+ thread_count++;
+ mysql_mutex_unlock(&LOCK_thread_count);
+ }
+
+ my_thread_attr_destroy(&thr_attr);
+ if ((error= mysql_mutex_lock(&LOCK_thread_count)))
+ my_message_stderr(0, "Got error %d from mysql_mutex_lock", error);
+ while (thread_count)
+ {
+ if ((error= mysql_cond_wait(&COND_thread_count, &LOCK_thread_count)))
+ my_message_stderr(0, "Got error %d from mysql_cond_wait",
+ error);
+ }
+ if ((error= mysql_mutex_unlock(&LOCK_thread_count)))
+ my_message_stderr(0, "Got error %d from mysql_mutex_unlock",
+ error);
+ for (i=0 ; i < (int) array_elements(locks) ; i++)
+ thr_lock_delete(locks+i);
+#ifdef EXTRA_DEBUG
+ if (found_errors)
+ printf("Got %d warnings\n", found_errors);
+ else
+#endif
+ printf("Test succeeded\n");
+ return 0;
+}
+
+#endif /* MAIN */
diff --git a/mysql/mysys/thr_mutex.c b/mysql/mysys/thr_mutex.c
new file mode 100644
index 0000000..f0396b7
--- /dev/null
+++ b/mysql/mysys/thr_mutex.c
@@ -0,0 +1,195 @@
+/* Copyright (c) 2000, 2015, Oracle and/or its affiliates. All rights reserved.
+
+ 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; version 2 of the License.
+
+ 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include "thr_mutex.h"
+#include "my_thread_local.h"
+
+#if defined(SAFE_MUTEX)
+/* This makes a wrapper for mutex handling to make it easier to debug mutex */
+
+static my_bool safe_mutex_inited= FALSE;
+
+/**
+ While it looks like this function is pointless, it makes it possible to
+ catch usage of global static mutexes. Since the order of construction of
+ global objects in different compilation units is undefined, this is
+ quite useful.
+*/
+void safe_mutex_global_init(void)
+{
+ safe_mutex_inited= TRUE;
+}
+
+
+int safe_mutex_init(my_mutex_t *mp, const native_mutexattr_t *attr,
+ const char *file, uint line)
+{
+ DBUG_ASSERT(safe_mutex_inited);
+ memset(mp, 0, sizeof(*mp));
+ native_mutex_init(&mp->global,MY_MUTEX_INIT_ERRCHK);
+ native_mutex_init(&mp->mutex,attr);
+ /* Mark that mutex is initialized */
+ mp->file= file;
+ mp->line= line;
+ return 0;
+}
+
+
+int safe_mutex_lock(my_mutex_t *mp, my_bool try_lock,
+ const char *file, uint line)
+{
+ int error;
+ native_mutex_lock(&mp->global);
+ if (!mp->file)
+ {
+ native_mutex_unlock(&mp->global);
+ fprintf(stderr,
+ "safe_mutex: Trying to lock uninitialized mutex at %s, line %d\n",
+ file, line);
+ fflush(stderr);
+ abort();
+ }
+
+ if (mp->count > 0)
+ {
+ if (try_lock)
+ {
+ native_mutex_unlock(&mp->global);
+ return EBUSY;
+ }
+ else if (my_thread_equal(my_thread_self(),mp->thread))
+ {
+#ifndef DBUG_OFF
+ fprintf(stderr,
+ "safe_mutex: Trying to lock mutex at %s, line %d, when the"
+ " mutex was already locked at %s, line %d in thread T@%u\n",
+ file, line, mp->file, mp->line, my_thread_var_id());
+ fflush(stderr);
+#endif
+ abort();
+ }
+ }
+ native_mutex_unlock(&mp->global);
+
+ /*
+ If we are imitating trylock(), we need to take special
+ precautions.
+
+ - We cannot use pthread_mutex_lock() only since another thread can
+ overtake this thread and take the lock before this thread
+ causing pthread_mutex_trylock() to hang. In this case, we should
+ just return EBUSY. Hence, we use pthread_mutex_trylock() to be
+ able to return immediately.
+
+ - We cannot just use trylock() and continue execution below, since
+ this would generate an error and abort execution if the thread
+ was overtaken and trylock() returned EBUSY . In this case, we
+ instead just return EBUSY, since this is the expected behaviour
+ of trylock().
+ */
+ if (try_lock)
+ {
+ error= native_mutex_trylock(&mp->mutex);
+ if (error == EBUSY)
+ return error;
+ }
+ else
+ error= native_mutex_lock(&mp->mutex);
+
+ if (error || (error=native_mutex_lock(&mp->global)))
+ {
+ fprintf(stderr,"Got error %d when trying to lock mutex at %s, line %d\n",
+ error, file, line);
+ fflush(stderr);
+ abort();
+ }
+ mp->thread= my_thread_self();
+ if (mp->count++)
+ {
+ fprintf(stderr,"safe_mutex: Error in thread libray: Got mutex at %s, \
+line %d more than 1 time\n", file,line);
+ fflush(stderr);
+ abort();
+ }
+ mp->file= file;
+ mp->line=line;
+ native_mutex_unlock(&mp->global);
+ return error;
+}
+
+
+int safe_mutex_unlock(my_mutex_t *mp, const char *file, uint line)
+{
+ int error;
+ native_mutex_lock(&mp->global);
+ if (mp->count == 0)
+ {
+ fprintf(stderr,"safe_mutex: Trying to unlock mutex that wasn't locked at %s, line %d\n Last used at %s, line: %d\n",
+ file,line,mp->file ? mp->file : "",mp->line);
+ fflush(stderr);
+ abort();
+ }
+ if (!my_thread_equal(my_thread_self(),mp->thread))
+ {
+ fprintf(stderr,"safe_mutex: Trying to unlock mutex at %s, line %d that was locked by another thread at: %s, line: %d\n",
+ file,line,mp->file,mp->line);
+ fflush(stderr);
+ abort();
+ }
+ mp->thread= 0;
+ mp->count--;
+ error=native_mutex_unlock(&mp->mutex);
+ if (error)
+ {
+ fprintf(stderr,"safe_mutex: Got error: %d (%d) when trying to unlock mutex at %s, line %d\n", error, errno, file, line);
+ fflush(stderr);
+ abort();
+ }
+ native_mutex_unlock(&mp->global);
+ return error;
+}
+
+
+int safe_mutex_destroy(my_mutex_t *mp, const char *file, uint line)
+{
+ int error=0;
+ native_mutex_lock(&mp->global);
+ if (!mp->file)
+ {
+ native_mutex_unlock(&mp->global);
+ fprintf(stderr,
+ "safe_mutex: Trying to destroy uninitialized mutex at %s, line %d\n",
+ file, line);
+ fflush(stderr);
+ abort();
+ }
+ if (mp->count != 0)
+ {
+ native_mutex_unlock(&mp->global);
+ fprintf(stderr,"safe_mutex: Trying to destroy a mutex that was locked at %s, line %d at %s, line %d\n",
+ mp->file,mp->line, file, line);
+ fflush(stderr);
+ abort();
+ }
+ native_mutex_unlock(&mp->global);
+ if (native_mutex_destroy(&mp->global))
+ error=1;
+ if (native_mutex_destroy(&mp->mutex))
+ error=1;
+ mp->file= 0; /* Mark destroyed */
+ return error;
+}
+
+#endif /* SAFE_MUTEX */
diff --git a/mysql/mysys/thr_rwlock.c b/mysql/mysys/thr_rwlock.c
new file mode 100644
index 0000000..cadedda
--- /dev/null
+++ b/mysql/mysys/thr_rwlock.c
@@ -0,0 +1,139 @@
+/* Copyright (c) 2000, 2015, Oracle and/or its affiliates. All rights reserved.
+
+ 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; version 2 of the License.
+
+ 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+/* Synchronization - readers / writer thread locks */
+
+#include "thr_rwlock.h"
+
+int rw_pr_init(rw_pr_lock_t *rwlock)
+{
+ native_mutex_init(&rwlock->lock, NULL);
+ native_cond_init(&rwlock->no_active_readers);
+ rwlock->active_readers= 0;
+ rwlock->writers_waiting_readers= 0;
+ rwlock->active_writer= FALSE;
+#ifdef SAFE_MUTEX
+ rwlock->writer_thread= 0;
+#endif
+ return 0;
+}
+
+
+int rw_pr_destroy(rw_pr_lock_t *rwlock)
+{
+ native_cond_destroy(&rwlock->no_active_readers);
+ native_mutex_destroy(&rwlock->lock);
+ return 0;
+}
+
+
+int rw_pr_rdlock(rw_pr_lock_t *rwlock)
+{
+ native_mutex_lock(&rwlock->lock);
+ /*
+ The fact that we were able to acquire 'lock' mutex means
+ that there are no active writers and we can acquire rd-lock.
+ Increment active readers counter to prevent requests for
+ wr-lock from succeeding and unlock mutex.
+ */
+ rwlock->active_readers++;
+ native_mutex_unlock(&rwlock->lock);
+ return 0;
+}
+
+
+int rw_pr_wrlock(rw_pr_lock_t *rwlock)
+{
+ native_mutex_lock(&rwlock->lock);
+
+ if (rwlock->active_readers != 0)
+ {
+ /* There are active readers. We have to wait until they are gone. */
+ rwlock->writers_waiting_readers++;
+
+ while (rwlock->active_readers != 0)
+ native_cond_wait(&rwlock->no_active_readers, &rwlock->lock);
+
+ rwlock->writers_waiting_readers--;
+ }
+
+ /*
+ We own 'lock' mutex so there is no active writers.
+ Also there are no active readers.
+ This means that we can grant wr-lock.
+ Not releasing 'lock' mutex until unlock will block
+ both requests for rd and wr-locks.
+ Set 'active_writer' flag to simplify unlock.
+
+ Thanks to the fact wr-lock/unlock in the absence of
+ contention from readers is essentially mutex lock/unlock
+ with a few simple checks make this rwlock implementation
+ wr-lock optimized.
+ */
+ rwlock->active_writer= TRUE;
+#ifdef SAFE_MUTEX
+ rwlock->writer_thread= my_thread_self();
+#endif
+ return 0;
+}
+
+
+int rw_pr_unlock(rw_pr_lock_t *rwlock)
+{
+ if (rwlock->active_writer)
+ {
+ /* We are unlocking wr-lock. */
+#ifdef SAFE_MUTEX
+ rwlock->writer_thread= 0;
+#endif
+ rwlock->active_writer= FALSE;
+ if (rwlock->writers_waiting_readers)
+ {
+ /*
+ Avoid expensive cond signal in case when there is no contention
+ or it is wr-only.
+
+ Note that from view point of performance it would be better to
+ signal on the condition variable after unlocking mutex (as it
+ reduces number of contex switches).
+
+ Unfortunately this would mean that such rwlock can't be safely
+ used by MDL subsystem, which relies on the fact that it is OK
+ to destroy rwlock once it is in unlocked state.
+ */
+ native_cond_signal(&rwlock->no_active_readers);
+ }
+ native_mutex_unlock(&rwlock->lock);
+ }
+ else
+ {
+ /* We are unlocking rd-lock. */
+ native_mutex_lock(&rwlock->lock);
+ rwlock->active_readers--;
+ if (rwlock->active_readers == 0 &&
+ rwlock->writers_waiting_readers)
+ {
+ /*
+ If we are last reader and there are waiting
+ writers wake them up.
+ */
+ native_cond_signal(&rwlock->no_active_readers);
+ }
+ native_mutex_unlock(&rwlock->lock);
+ }
+ return 0;
+}
+
+
diff --git a/mysql/mysys/tree.c b/mysql/mysys/tree.c
new file mode 100644
index 0000000..1f21b67
--- /dev/null
+++ b/mysql/mysys/tree.c
@@ -0,0 +1,760 @@
+/* Copyright (c) 2000, 2016, Oracle and/or its affiliates. All rights reserved.
+
+ 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; version 2 of the License.
+
+ 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+/*
+ Code for handling red-black (balanced) binary trees.
+ key in tree is allocated accrding to following:
+
+ 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.
+
+ 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.
+
+ - 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.
+
+ Implemented by monty.
+*/
+
+/*
+ NOTE:
+ tree->compare function should be ALWAYS called as
+ (*tree->compare)(custom_arg, ELEMENT_KEY(tree,element), key)
+ and not other way around, as
+ (*tree->compare)(custom_arg, key, ELEMENT_KEY(tree,element))
+
+ ft_boolean_search.c (at least) relies on that.
+*/
+
+#include "mysys_priv.h"
+#include "my_sys.h"
+#include <m_string.h>
+#include <my_tree.h>
+#include "my_base.h"
+
+#define BLACK 1
+#define RED 0
+#define DEFAULT_ALLOC_SIZE 8192
+#define DEFAULT_ALIGN_SIZE 8192
+
+static void delete_tree_element(TREE *,TREE_ELEMENT *);
+static int tree_walk_left_root_right(TREE *,TREE_ELEMENT *,
+ tree_walk_action,void *);
+static int tree_walk_right_root_left(TREE *,TREE_ELEMENT *,
+ tree_walk_action,void *);
+static void left_rotate(TREE_ELEMENT **parent,TREE_ELEMENT *leaf);
+static void right_rotate(TREE_ELEMENT **parent, TREE_ELEMENT *leaf);
+static void rb_insert(TREE *tree,TREE_ELEMENT ***parent,
+ TREE_ELEMENT *leaf);
+static void rb_delete_fixup(TREE *tree,TREE_ELEMENT ***parent);
+
+
+ /* The actuall code for handling binary trees */
+
+#ifndef DBUG_OFF
+static int test_rb_tree(TREE_ELEMENT *element);
+#endif
+
+void init_tree(TREE *tree, size_t default_alloc_size, ulong memory_limit,
+ int size, qsort_cmp2 compare, my_bool with_delete,
+ tree_element_free free_element, const void *custom_arg)
+{
+ DBUG_ENTER("init_tree");
+ DBUG_PRINT("enter",("tree: 0x%lx size: %d", (long) tree, size));
+
+ if (default_alloc_size < DEFAULT_ALLOC_SIZE)
+ default_alloc_size= DEFAULT_ALLOC_SIZE;
+ default_alloc_size= MY_ALIGN(default_alloc_size, DEFAULT_ALIGN_SIZE);
+ memset(&tree->null_element, 0, sizeof(tree->null_element));
+ tree->root= &tree->null_element;
+ tree->compare=compare;
+ tree->size_of_element=size > 0 ? (uint) size : 0;
+ tree->memory_limit=memory_limit;
+ tree->free=free_element;
+ tree->allocated=0;
+ tree->elements_in_tree=0;
+ 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))))
+ {
+ /*
+ We know that the data doesn't have to be aligned (like if the key
+ contains a double), so we can store the data combined with the
+ TREE_ELEMENT.
+ */
+ tree->offset_to_key=sizeof(TREE_ELEMENT); /* Put key after element */
+ /* Fix allocation size so that we don't lose any memory */
+ default_alloc_size/=(sizeof(TREE_ELEMENT)+size);
+ if (!default_alloc_size)
+ default_alloc_size=1;
+ default_alloc_size*=(sizeof(TREE_ELEMENT)+size);
+ }
+ else
+ {
+ tree->offset_to_key=0; /* use key through pointer */
+ tree->size_of_element+=sizeof(void*);
+ }
+ if (!(tree->with_delete=with_delete))
+ {
+ init_alloc_root(key_memory_TREE,
+ &tree->mem_root, default_alloc_size, 0);
+ tree->mem_root.min_malloc=(sizeof(TREE_ELEMENT)+tree->size_of_element);
+ }
+ DBUG_VOID_RETURN;
+}
+
+static void free_tree(TREE *tree, myf free_flags)
+{
+ DBUG_ENTER("free_tree");
+ DBUG_PRINT("enter",("tree: 0x%lx", (long) tree));
+
+ if (tree->root) /* If initialized */
+ {
+ if (tree->with_delete)
+ delete_tree_element(tree,tree->root);
+ else
+ {
+ if (tree->free)
+ {
+ if (tree->memory_limit)
+ (*tree->free)(NULL, free_init, tree->custom_arg);
+ delete_tree_element(tree,tree->root);
+ if (tree->memory_limit)
+ (*tree->free)(NULL, free_end, tree->custom_arg);
+ }
+ free_root(&tree->mem_root, free_flags);
+ }
+ }
+ tree->root= &tree->null_element;
+ tree->elements_in_tree=0;
+ tree->allocated=0;
+
+ DBUG_VOID_RETURN;
+}
+
+void delete_tree(TREE* tree)
+{
+ free_tree(tree, MYF(0)); /* my_free() mem_root if applicable */
+}
+
+void reset_tree(TREE* tree)
+{
+ /* do not free mem_root, just mark blocks as free */
+ free_tree(tree, MYF(MY_MARK_BLOCKS_FREE));
+}
+
+
+static void delete_tree_element(TREE *tree, TREE_ELEMENT *element)
+{
+ if (element != &tree->null_element)
+ {
+ delete_tree_element(tree,element->left);
+ if (tree->free)
+ (*tree->free)(ELEMENT_KEY(tree,element), free_free, tree->custom_arg);
+ delete_tree_element(tree,element->right);
+ if (tree->with_delete)
+ my_free(element);
+ }
+}
+
+
+/*
+ insert, search and delete of elements
+
+ The following should be true:
+ parent[0] = & parent[-1][0]->left ||
+ parent[0] = & parent[-1][0]->right
+*/
+
+TREE_ELEMENT *tree_insert(TREE *tree, void *key, uint key_size,
+ const void* custom_arg)
+{
+ int cmp;
+ TREE_ELEMENT *element,***parent;
+
+ parent= tree->parents;
+ *parent = &tree->root; element= tree->root;
+ for (;;)
+ {
+ if (element == &tree->null_element ||
+ (cmp = (*tree->compare)(custom_arg, ELEMENT_KEY(tree,element),
+ key)) == 0)
+ break;
+ if (cmp < 0)
+ {
+ *++parent= &element->right; element= element->right;
+ }
+ else
+ {
+ *++parent = &element->left; element= element->left;
+ }
+ }
+ if (element == &tree->null_element)
+ {
+ uint alloc_size=sizeof(TREE_ELEMENT)+key_size+tree->size_of_element;
+ tree->allocated+=alloc_size;
+
+ if (tree->memory_limit && tree->elements_in_tree
+ && tree->allocated > tree->memory_limit)
+ {
+ reset_tree(tree);
+ return tree_insert(tree, key, key_size, custom_arg);
+ }
+
+ key_size+=tree->size_of_element;
+ if (tree->with_delete)
+ element=(TREE_ELEMENT *) my_malloc(key_memory_TREE,
+ alloc_size, MYF(MY_WME));
+ else
+ element=(TREE_ELEMENT *) alloc_root(&tree->mem_root,alloc_size);
+ if (!element)
+ return(NULL);
+ **parent=element;
+ element->left=element->right= &tree->null_element;
+ if (!tree->offset_to_key)
+ {
+ if (key_size == sizeof(void*)) /* no length, save pointer */
+ *((void**) (element+1))=key;
+ else
+ {
+ *((void**) (element+1))= (void*) ((void **) (element+1)+1);
+ memcpy((uchar*) *((void **) (element+1)),key,
+ (size_t) (key_size-sizeof(void*)));
+ }
+ }
+ else
+ memcpy((uchar*) element+tree->offset_to_key,key,(size_t) key_size);
+ element->count=1;
+ tree->elements_in_tree++;
+ rb_insert(tree,parent,element); /* rebalance tree */
+ }
+ else
+ {
+ if (tree->flag & TREE_NO_DUPS)
+ return(NULL);
+ element->count++;
+ /* Avoid a wrap over of the count. */
+ if (! element->count)
+ element->count--;
+ }
+ DBUG_EXECUTE("check_tree", test_rb_tree(tree->root););
+ return element;
+}
+
+int tree_delete(TREE *tree, void *key, uint key_size, const void *custom_arg)
+{
+ int cmp,remove_colour;
+ TREE_ELEMENT *element,***parent, ***org_parent, *nod;
+ if (!tree->with_delete)
+ return 1; /* not allowed */
+
+ parent= tree->parents;
+ *parent= &tree->root; element= tree->root;
+ for (;;)
+ {
+ if (element == &tree->null_element)
+ return 1; /* Was not in tree */
+ if ((cmp = (*tree->compare)(custom_arg, ELEMENT_KEY(tree,element),
+ key)) == 0)
+ break;
+ if (cmp < 0)
+ {
+ *++parent= &element->right; element= element->right;
+ }
+ else
+ {
+ *++parent = &element->left; element= element->left;
+ }
+ }
+ if (element->left == &tree->null_element)
+ {
+ (**parent)=element->right;
+ remove_colour= element->colour;
+ }
+ else if (element->right == &tree->null_element)
+ {
+ (**parent)=element->left;
+ remove_colour= element->colour;
+ }
+ else
+ {
+ org_parent= parent;
+ *++parent= &element->right; nod= element->right;
+ while (nod->left != &tree->null_element)
+ {
+ *++parent= &nod->left; nod= nod->left;
+ }
+ (**parent)=nod->right; /* unlink nod from tree */
+ remove_colour= nod->colour;
+ org_parent[0][0]=nod; /* put y in place of element */
+ org_parent[1]= &nod->right;
+ nod->left=element->left;
+ nod->right=element->right;
+ nod->colour=element->colour;
+ }
+ if (remove_colour == BLACK)
+ rb_delete_fixup(tree,parent);
+ if (tree->free)
+ (*tree->free)(ELEMENT_KEY(tree,element), free_free, tree->custom_arg);
+ tree->allocated-= sizeof(TREE_ELEMENT) + tree->size_of_element + key_size;
+ my_free(element);
+ tree->elements_in_tree--;
+ return 0;
+}
+
+
+void *tree_search(TREE *tree, void *key, const void *custom_arg)
+{
+ int cmp;
+ TREE_ELEMENT *element=tree->root;
+
+ for (;;)
+ {
+ if (element == &tree->null_element)
+ return (void*) 0;
+ if ((cmp = (*tree->compare)(custom_arg, ELEMENT_KEY(tree,element),
+ key)) == 0)
+ return ELEMENT_KEY(tree,element);
+ if (cmp < 0)
+ element=element->right;
+ else
+ element=element->left;
+ }
+}
+
+void *tree_search_key(TREE *tree, const void *key,
+ TREE_ELEMENT **parents, TREE_ELEMENT ***last_pos,
+ enum ha_rkey_function flag, const 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, const void *custom_arg)
+{
+ int cmp;
+ TREE_ELEMENT *element= tree->root;
+ double left= 1;
+ double right= tree->elements_in_tree;
+
+ while (element != &tree->null_element)
+ {
+ if ((cmp= (*tree->compare)(custom_arg, ELEMENT_KEY(tree, element),
+ key)) == 0)
+ {
+ switch (flag) {
+ case HA_READ_KEY_EXACT:
+ case HA_READ_BEFORE_KEY:
+ cmp= 1;
+ break;
+ case HA_READ_AFTER_KEY:
+ cmp= -1;
+ break;
+ default:
+ return HA_POS_ERROR;
+ }
+ }
+ if (cmp < 0) /* element < key */
+ {
+ element= element->right;
+ left= (left + right) / 2;
+ }
+ else
+ {
+ element= element->left;
+ right= (left + right) / 2;
+ }
+ }
+ switch (flag) {
+ case HA_READ_KEY_EXACT:
+ case HA_READ_BEFORE_KEY:
+ return (ha_rows) right;
+ case HA_READ_AFTER_KEY:
+ return (ha_rows) left;
+ default:
+ return HA_POS_ERROR;
+ }
+}
+
+int tree_walk(TREE *tree, tree_walk_action action, void *argument, TREE_WALK visit)
+{
+ switch (visit) {
+ case left_root_right:
+ return tree_walk_left_root_right(tree,tree->root,action,argument);
+ case right_root_left:
+ return tree_walk_right_root_left(tree,tree->root,action,argument);
+ }
+ return 0; /* Keep gcc happy */
+}
+
+static int tree_walk_left_root_right(TREE *tree, TREE_ELEMENT *element, tree_walk_action action, void *argument)
+{
+ int error;
+ if (element->left) /* Not null_element */
+ {
+ if ((error=tree_walk_left_root_right(tree,element->left,action,
+ argument)) == 0 &&
+ (error=(*action)(ELEMENT_KEY(tree,element),
+ (element_count) element->count,
+ argument)) == 0)
+ error=tree_walk_left_root_right(tree,element->right,action,argument);
+ return error;
+ }
+ return 0;
+}
+
+static int tree_walk_right_root_left(TREE *tree, TREE_ELEMENT *element, tree_walk_action action, void *argument)
+{
+ int error;
+ if (element->right) /* Not null_element */
+ {
+ if ((error=tree_walk_right_root_left(tree,element->right,action,
+ argument)) == 0 &&
+ (error=(*action)(ELEMENT_KEY(tree,element),
+ (element_count) element->count,
+ argument)) == 0)
+ error=tree_walk_right_root_left(tree,element->left,action,argument);
+ return error;
+ }
+ return 0;
+}
+
+
+ /* Functions to fix up the tree after insert and delete */
+
+static void left_rotate(TREE_ELEMENT **parent, TREE_ELEMENT *leaf)
+{
+ TREE_ELEMENT *y;
+
+ y=leaf->right;
+ leaf->right=y->left;
+ parent[0]=y;
+ y->left=leaf;
+}
+
+static void right_rotate(TREE_ELEMENT **parent, TREE_ELEMENT *leaf)
+{
+ TREE_ELEMENT *x;
+
+ x=leaf->left;
+ leaf->left=x->right;
+ parent[0]=x;
+ x->right=leaf;
+}
+
+static void rb_insert(TREE *tree, TREE_ELEMENT ***parent, TREE_ELEMENT *leaf)
+{
+ TREE_ELEMENT *y,*par,*par2;
+
+ leaf->colour=RED;
+ while (leaf != tree->root && (par=parent[-1][0])->colour == RED)
+ {
+ if (par == (par2=parent[-2][0])->left)
+ {
+ y= par2->right;
+ if (y->colour == RED)
+ {
+ par->colour=BLACK;
+ y->colour=BLACK;
+ leaf=par2;
+ parent-=2;
+ leaf->colour=RED; /* And the loop continues */
+ }
+ else
+ {
+ if (leaf == par->right)
+ {
+ left_rotate(parent[-1],par);
+ par=leaf; /* leaf is now parent to old leaf */
+ }
+ par->colour=BLACK;
+ par2->colour=RED;
+ right_rotate(parent[-2],par2);
+ break;
+ }
+ }
+ else
+ {
+ y= par2->left;
+ if (y->colour == RED)
+ {
+ par->colour=BLACK;
+ y->colour=BLACK;
+ leaf=par2;
+ parent-=2;
+ leaf->colour=RED; /* And the loop continues */
+ }
+ else
+ {
+ if (leaf == par->left)
+ {
+ right_rotate(parent[-1],par);
+ par=leaf;
+ }
+ par->colour=BLACK;
+ par2->colour=RED;
+ left_rotate(parent[-2],par2);
+ break;
+ }
+ }
+ }
+ tree->root->colour=BLACK;
+}
+
+static void rb_delete_fixup(TREE *tree, TREE_ELEMENT ***parent)
+{
+ TREE_ELEMENT *x,*w,*par;
+
+ x= **parent;
+ while (x != tree->root && x->colour == BLACK)
+ {
+ if (x == (par=parent[-1][0])->left)
+ {
+ w=par->right;
+ if (w->colour == RED)
+ {
+ w->colour=BLACK;
+ par->colour=RED;
+ left_rotate(parent[-1],par);
+ parent[0]= &w->left;
+ *++parent= &par->left;
+ w=par->right;
+ }
+ if (w->left->colour == BLACK && w->right->colour == BLACK)
+ {
+ w->colour=RED;
+ x=par;
+ parent--;
+ }
+ else
+ {
+ if (w->right->colour == BLACK)
+ {
+ w->left->colour=BLACK;
+ w->colour=RED;
+ right_rotate(&par->right,w);
+ w=par->right;
+ }
+ w->colour=par->colour;
+ par->colour=BLACK;
+ w->right->colour=BLACK;
+ left_rotate(parent[-1],par);
+ x=tree->root;
+ break;
+ }
+ }
+ else
+ {
+ w=par->left;
+ if (w->colour == RED)
+ {
+ w->colour=BLACK;
+ par->colour=RED;
+ right_rotate(parent[-1],par);
+ parent[0]= &w->right;
+ *++parent= &par->right;
+ w=par->left;
+ }
+ if (w->right->colour == BLACK && w->left->colour == BLACK)
+ {
+ w->colour=RED;
+ x=par;
+ parent--;
+ }
+ else
+ {
+ if (w->left->colour == BLACK)
+ {
+ w->right->colour=BLACK;
+ w->colour=RED;
+ left_rotate(&par->left,w);
+ w=par->left;
+ }
+ w->colour=par->colour;
+ par->colour=BLACK;
+ w->left->colour=BLACK;
+ right_rotate(parent[-1],par);
+ x=tree->root;
+ break;
+ }
+ }
+ }
+ x->colour=BLACK;
+}
+
+#ifndef DBUG_OFF
+
+ /* Test that the proporties for a red-black tree holds */
+
+static int test_rb_tree(TREE_ELEMENT *element)
+{
+ int count_l,count_r;
+
+ if (!element->left)
+ return 0; /* Found end of tree */
+ if (element->colour == RED &&
+ (element->left->colour == RED || element->right->colour == RED))
+ {
+ printf("Wrong tree: Found two red in a row\n");
+ return -1;
+ }
+ count_l=test_rb_tree(element->left);
+ count_r=test_rb_tree(element->right);
+ if (count_l >= 0 && count_r >= 0)
+ {
+ if (count_l == count_r)
+ return count_l+(element->colour == BLACK);
+ printf("Wrong tree: Incorrect black-count: %d - %d\n",count_l,count_r);
+ }
+ return -1;
+}
+#endif
diff --git a/mysql/mysys/typelib.c b/mysql/mysys/typelib.c
new file mode 100644
index 0000000..247c4d9
--- /dev/null
+++ b/mysql/mysys/typelib.c
@@ -0,0 +1,388 @@
+/* Copyright (c) 2000, 2015, Oracle and/or its affiliates. All rights reserved.
+
+ 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; version 2 of the License.
+
+ 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */
+
+/* Functions to handle typelib */
+
+#include "mysys_priv.h"
+#include "my_sys.h"
+#include <m_string.h>
+#include <m_ctype.h>
+
+
+#define is_field_separator(X) ((X) == ',' || (X) == '=')
+
+int find_type_or_exit(const char *x, TYPELIB *typelib, const char *option)
+{
+ int res;
+ const char **ptr;
+
+ if ((res= find_type((char *) x, typelib, FIND_TYPE_BASIC)) <= 0)
+ {
+ ptr= typelib->type_names;
+ if (!*x)
+ fprintf(stderr, "No option given to %s\n", option);
+ else
+ fprintf(stderr, "Unknown option to %s: %s\n", option, x);
+ fprintf(stderr, "Alternatives are: '%s'", *ptr);
+ while (*++ptr)
+ fprintf(stderr, ",'%s'", *ptr);
+ fprintf(stderr, "\n");
+ exit(1);
+ }
+ return res;
+}
+
+
+/**
+ Search after a string in a list of strings. Endspace in x is not compared.
+
+ @param x String to find
+ @param typelib TYPELIB (struct of pointer to values + count)
+ @param flags flags to tune behaviour: a combination of
+ FIND_TYPE_NO_PREFIX
+ FIND_TYPE_ALLOW_NUMBER
+ FIND_TYPE_COMMA_TERM.
+ FIND_TYPE_NO_OVERWRITE can be passed but is
+ superfluous (is always implicitely on).
+
+ @retval
+ -1 Too many matching values
+ @retval
+ 0 No matching value
+ @retval
+ >0 Offset+1 in typelib for matched string
+*/
+
+
+int find_type(const char *x, const TYPELIB *typelib, uint flags)
+{
+ int find,pos;
+ int findpos= 0; /* guarded by find */
+ const char *i;
+ const char *j;
+ DBUG_ENTER("find_type");
+ DBUG_PRINT("enter",("x: '%s' lib: 0x%lx", x, (long) typelib));
+
+ DBUG_ASSERT(!(flags & ~(FIND_TYPE_NO_PREFIX | FIND_TYPE_ALLOW_NUMBER |
+ FIND_TYPE_NO_OVERWRITE | FIND_TYPE_COMMA_TERM)));
+ if (!typelib->count)
+ {
+ DBUG_PRINT("exit",("no count"));
+ DBUG_RETURN(0);
+ }
+ find=0;
+ for (pos=0 ; (j=typelib->type_names[pos]) ; pos++)
+ {
+ for (i=x ;
+ *i && (!(flags & FIND_TYPE_COMMA_TERM) || !is_field_separator(*i)) &&
+ my_toupper(&my_charset_latin1,*i) ==
+ my_toupper(&my_charset_latin1,*j) ; i++, j++) ;
+ if (! *j)
+ {
+ while (*i == ' ')
+ i++; /* skip_end_space */
+ if (! *i || ((flags & FIND_TYPE_COMMA_TERM) && is_field_separator(*i)))
+ DBUG_RETURN(pos+1);
+ }
+ if ((!*i &&
+ (!(flags & FIND_TYPE_COMMA_TERM) || !is_field_separator(*i))) &&
+ (!*j || !(flags & FIND_TYPE_NO_PREFIX)))
+ {
+ find++;
+ findpos=pos;
+ }
+ }
+ if (find == 0 && (flags & FIND_TYPE_ALLOW_NUMBER) && x[0] == '#' &&
+ strend(x)[-1] == '#' &&
+ (findpos=atoi(x+1)-1) >= 0 && (uint) findpos < typelib->count)
+ find=1;
+ else if (find == 0 || ! x[0])
+ {
+ DBUG_PRINT("exit",("Couldn't find type"));
+ DBUG_RETURN(0);
+ }
+ else if (find != 1 || (flags & FIND_TYPE_NO_PREFIX))
+ {
+ DBUG_PRINT("exit",("Too many possybilities"));
+ DBUG_RETURN(-1);
+ }
+ DBUG_RETURN(findpos+1);
+} /* find_type */
+
+
+/**
+ Get name of type nr
+
+ @note
+ first type is 1, 0 = empty field
+*/
+
+void make_type(char * to, uint nr,
+ TYPELIB *typelib)
+{
+ DBUG_ENTER("make_type");
+ if (!nr)
+ to[0]=0;
+ else
+ (void) my_stpcpy(to,get_type(typelib,nr-1));
+ DBUG_VOID_RETURN;
+} /* make_type */
+
+
+/**
+ Get type
+
+ @note
+ first type is 0
+*/
+
+const char *get_type(TYPELIB *typelib, uint nr)
+{
+ if (nr < (uint) typelib->count && typelib->type_names)
+ return(typelib->type_names[nr]);
+ return "?";
+}
+
+
+/**
+ Create an integer value to represent the supplied comma-seperated
+ string where each string in the TYPELIB denotes a bit position.
+
+ @param x string to decompose
+ @param lib TYPELIB (struct of pointer to values + count)
+ @param err index (not char position) of string element which was not
+ found or 0 if there was no error
+
+ @retval
+ a integer representation of the supplied string
+*/
+
+my_ulonglong find_typeset(char *x, TYPELIB *lib, int *err)
+{
+ my_ulonglong result;
+ int find;
+ char *i;
+ DBUG_ENTER("find_set");
+ DBUG_PRINT("enter",("x: '%s' lib: 0x%lx", x, (long) lib));
+
+ if (!lib->count)
+ {
+ DBUG_PRINT("exit",("no count"));
+ DBUG_RETURN(0);
+ }
+ result= 0;
+ *err= 0;
+ while (*x)
+ {
+ (*err)++;
+ i= x;
+ while (*x && !is_field_separator(*x))
+ x++;
+ if (x[0] && x[1]) /* skip separator if found */
+ x++;
+ if ((find= find_type(i, lib, FIND_TYPE_COMMA_TERM) - 1) < 0)
+ DBUG_RETURN(0);
+ result|= (1ULL << find);
+ }
+ *err= 0;
+ DBUG_RETURN(result);
+} /* find_set */
+
+
+/**
+ Create a copy of a specified TYPELIB structure.
+
+ @param root pointer to a MEM_ROOT object for allocations
+ @param from pointer to a source TYPELIB structure
+
+ @retval
+ pointer to the new TYPELIB structure on successful copy
+ @retval
+ NULL otherwise
+*/
+
+TYPELIB *copy_typelib(MEM_ROOT *root, TYPELIB *from)
+{
+ TYPELIB *to;
+ uint i;
+
+ if (!from)
+ return NULL;
+
+ if (!(to= (TYPELIB*) alloc_root(root, sizeof(TYPELIB))))
+ return NULL;
+
+ if (!(to->type_names= (const char **)
+ alloc_root(root, (sizeof(char *) + sizeof(int)) * (from->count + 1))))
+ return NULL;
+ to->type_lengths= (unsigned int *)(to->type_names + from->count + 1);
+ to->count= from->count;
+ if (from->name)
+ {
+ if (!(to->name= strdup_root(root, from->name)))
+ return NULL;
+ }
+ else
+ to->name= NULL;
+
+ for (i= 0; i < from->count; i++)
+ {
+ if (!(to->type_names[i]= strmake_root(root, from->type_names[i],
+ from->type_lengths[i])))
+ return NULL;
+ to->type_lengths[i]= from->type_lengths[i];
+ }
+ to->type_names[to->count]= NULL;
+ to->type_lengths[to->count]= 0;
+
+ return to;
+}
+
+
+static const char *on_off_default_names[]= { "off","on","default", 0};
+static TYPELIB on_off_default_typelib= {array_elements(on_off_default_names)-1,
+ "", on_off_default_names, 0};
+
+/**
+ Parse a TYPELIB name from the buffer
+
+ @param lib Set of names to scan for.
+ @param strpos INOUT Start of the buffer (updated to point to the next
+ character after the name)
+ @param end End of the buffer
+
+ @note
+ The buffer is assumed to contain one of the names specified in the TYPELIB,
+ followed by comma, '=', or end of the buffer.
+
+ @retval
+ 0 No matching name
+ @retval
+ >0 Offset+1 in typelib for matched name
+*/
+
+static uint parse_name(const TYPELIB *lib, const char **strpos, const char *end)
+{
+ const char *pos= *strpos;
+ uint find= find_type(pos, lib, FIND_TYPE_COMMA_TERM);
+ for (; pos != end && *pos != '=' && *pos !=',' ; pos++);
+ *strpos= pos;
+ return find;
+}
+
+/**
+ Parse and apply a set of flag assingments
+
+ @param lib Flag names
+ @param default_name Number of "default" in the typelib
+ @param cur_set Current set of flags (start from this state)
+ @param default_set Default set of flags (use this for assign-default
+ keyword and flag=default assignments)
+ @param str String to be parsed
+ @param length Length of the string
+ @param err_pos OUT If error, set to point to start of wrong set string
+ NULL on success
+ @param err_len OUT If error, set to the length of wrong set string
+
+ @details
+ Parse a set of flag assignments, that is, parse a string in form:
+
+ param_name1=value1,param_name2=value2,...
+
+ where the names are specified in the TYPELIB, and each value can be
+ either 'on','off', or 'default'. Setting the same name twice is not
+ allowed.
+
+ Besides param=val assignments, we support the "default" keyword (keyword
+ #default_name in the typelib). It can be used one time, if specified it
+ causes us to build the new set over the default_set rather than cur_set
+ value.
+
+ @note
+ it's not charset aware
+
+ @retval
+ Parsed set value if (*errpos == NULL), otherwise undefined
+*/
+
+my_ulonglong find_set_from_flags(const TYPELIB *lib, uint default_name,
+ my_ulonglong cur_set, my_ulonglong default_set,
+ const char *str, uint length,
+ char **err_pos, uint *err_len)
+{
+ const char *end= str + length;
+ my_ulonglong flags_to_set= 0, flags_to_clear= 0, res;
+ my_bool set_defaults= 0;
+
+ *err_pos= 0; /* No error yet */
+ if (str != end)
+ {
+ const char *start= str;
+ for (;;)
+ {
+ const char *pos= start;
+ uint flag_no, value;
+
+ if (!(flag_no= parse_name(lib, &pos, end)))
+ goto err;
+
+ if (flag_no == default_name)
+ {
+ /* Using 'default' twice isn't allowed. */
+ if (set_defaults)
+ goto err;
+ set_defaults= TRUE;
+ }
+ else
+ {
+ my_ulonglong bit= (1ULL << (flag_no - 1));
+ /* parse the '=on|off|default' */
+ if ((flags_to_clear | flags_to_set) & bit ||
+ pos >= end || *pos++ != '=' ||
+ !(value= parse_name(&on_off_default_typelib, &pos, end)))
+ goto err;
+
+ if (value == 1) /* this is '=off' */
+ flags_to_clear|= bit;
+ else if (value == 2) /* this is '=on' */
+ flags_to_set|= bit;
+ else /* this is '=default' */
+ {
+ if (default_set & bit)
+ flags_to_set|= bit;
+ else
+ flags_to_clear|= bit;
+ }
+ }
+ if (pos >= end)
+ break;
+
+ if (*pos++ != ',')
+ goto err;
+
+ start=pos;
+ continue;
+ err:
+ *err_pos= (char*)start;
+ *err_len= (uint)(end - start);
+ break;
+ }
+ }
+ res= set_defaults? default_set : cur_set;
+ res|= flags_to_set;
+ res&= ~flags_to_clear;
+ return res;
+}
+