diff options
author | Xanadu <none@none> | 2010-07-20 02:49:28 +0200 |
---|---|---|
committer | Xanadu <none@none> | 2010-07-20 02:49:28 +0200 |
commit | 79622802f397258ee0f34327ba3ae6977ca3e7ff (patch) | |
tree | 1868946c234ab9ee256a6b7766a15713eae94235 /externals/mysql/mysys | |
parent | 7dd2dc91816ab8b3bc3b99a1b1c99c7ea314d5a8 (diff) | |
parent | f906976837502fa5aa81b982b901d1509f5aa0c4 (diff) |
Merge. Revision history for source files should be all back now.
--HG--
branch : trunk
rename : sql/CMakeLists.txt => sql/tools/CMakeLists.txt
rename : src/server/game/Pools/PoolHandler.cpp => src/server/game/Pools/PoolMgr.cpp
rename : src/server/game/Pools/PoolHandler.h => src/server/game/Pools/PoolMgr.h
rename : src/server/game/PrecompiledHeaders/NixCorePCH.cpp => src/server/game/PrecompiledHeaders/gamePCH.cpp
rename : src/server/game/PrecompiledHeaders/NixCorePCH.h => src/server/game/PrecompiledHeaders/gamePCH.h
Diffstat (limited to 'externals/mysql/mysys')
136 files changed, 40190 insertions, 0 deletions
diff --git a/externals/mysql/mysys/array.c b/externals/mysql/mysys/array.c new file mode 100644 index 00000000000..b31260344a9 --- /dev/null +++ b/externals/mysql/mysys/array.c @@ -0,0 +1,379 @@ +/* Copyright (C) 2000 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +/* Handling of arrays that can grow dynamicly. */ + +#include "mysys_priv.h" +#include "m_string.h" + +/* + Initiate dynamic array + + SYNOPSIS + init_dynamic_array2() + 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. + Static buffers must begin immediately after the array structure. + + RETURN VALUE + TRUE my_malloc_ci() failed + FALSE Ok +*/ + +my_bool init_dynamic_array2(DYNAMIC_ARRAY *array, uint element_size, + void *init_buffer, uint init_alloc, + uint alloc_increment CALLER_INFO_PROTO) +{ + DBUG_ENTER("init_dynamic_array"); + if (!alloc_increment) + { + alloc_increment=max((8192-MALLOC_OVERHEAD)/element_size,16); + if (init_alloc > 8 && alloc_increment > init_alloc * 2) + alloc_increment=init_alloc*2; + } + array->elements=0; + array->max_element=init_alloc; + array->alloc_increment=alloc_increment; + array->size_of_element=element_size; + if ((array->buffer= init_buffer)) + DBUG_RETURN(FALSE); + if (init_alloc && + !(array->buffer=(uchar*) my_malloc_ci(element_size*init_alloc, + MYF(MY_WME)))) + { + array->max_element=0; + DBUG_RETURN(TRUE); + } + DBUG_RETURN(FALSE); +} + +my_bool init_dynamic_array(DYNAMIC_ARRAY *array, uint element_size, + uint init_alloc, + uint alloc_increment CALLER_INFO_PROTO) +{ + /* placeholder to preserve ABI */ + return my_init_dynamic_array_ci(array, element_size, 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 uchar* 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 +*/ + +uchar *alloc_dynamic(DYNAMIC_ARRAY *array) +{ + DBUG_ENTER("alloc_dynamic"); + 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->max_element+ + array->alloc_increment) * + array->size_of_element, + MYF(MY_WME)))) + DBUG_RETURN(0); + memcpy(new_ptr, array->buffer, + array->elements * array->size_of_element); + } + else if (!(new_ptr=(char*) + my_realloc(array->buffer,(array->max_element+ + array->alloc_increment)* + array->size_of_element, + MYF(MY_WME | MY_ALLOW_ZERO_PTR)))) + DBUG_RETURN(0); + array->buffer= (uchar*) new_ptr; + array->max_element+=array->alloc_increment; + } + DBUG_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 +*/ + +uchar *pop_dynamic(DYNAMIC_ARRAY *array) +{ + if (array->elements) + return array->buffer+(--array->elements * array->size_of_element); + return 0; +} + +/* + Replace element in array with given element and index + + SYNOPSIS + set_dynamic() + array + element Element to be inserted + idx Index where element is to be inserted + + DESCRIPTION + set_dynamic() replaces element in array. + If idx > max_element insert new element. Allocate memory if needed. + + RETURN VALUE + TRUE Idx was out of range and allocation of new memory failed + FALSE Ok +*/ + +my_bool set_dynamic(DYNAMIC_ARRAY *array, uchar* element, uint idx) +{ + if (idx >= array->elements) + { + if (idx >= array->max_element && allocate_dynamic(array, idx)) + return TRUE; + bzero((uchar*) (array->buffer+array->elements*array->size_of_element), + (idx - array->elements)*array->size_of_element); + array->elements=idx+1; + } + memcpy(array->buffer+(idx * array->size_of_element),element, + (size_t) array->size_of_element); + return FALSE; +} + + +/* + Ensure that dynamic array has enough elements + + SYNOPSIS + allocate_dynamic() + array + max_elements Numbers of elements that is needed + + NOTES + Any new allocated element are NOT initialized + + RETURN VALUE + FALSE Ok + TRUE Allocation of new memory failed +*/ + +my_bool allocate_dynamic(DYNAMIC_ARRAY *array, uint max_elements) +{ + DBUG_ENTER("allocate_dynamic"); + + if (max_elements >= array->max_element) + { + uint size; + uchar *new_ptr; + size= (max_elements + array->alloc_increment)/array->alloc_increment; + size*= array->alloc_increment; + 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= (uchar *) my_malloc(size * + array->size_of_element, + MYF(MY_WME)))) + DBUG_RETURN(0); + memcpy(new_ptr, array->buffer, + array->elements * array->size_of_element); + } + else if (!(new_ptr= (uchar*) my_realloc(array->buffer,size* + array->size_of_element, + MYF(MY_WME | MY_ALLOW_ZERO_PTR)))) + DBUG_RETURN(TRUE); + array->buffer= new_ptr; + array->max_element= size; + } + DBUG_RETURN(FALSE); +} + + +/* + 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, uchar* element, uint idx) +{ + if (idx >= array->elements) + { + DBUG_PRINT("warning",("To big array idx: %d, array size is %d", + idx,array->elements)); + bzero(element,array->size_of_element); + return; + } + memcpy(element,array->buffer+idx*array->size_of_element, + (size_t) array->size_of_element); +} + + +/* + 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,MYF(MY_WME)); + array->buffer=0; + array->elements=array->max_element=0; + } +} + +/* + Delete element by given index + + SYNOPSIS + delete_dynamic_element() + array + idx Index of element to be deleted +*/ + +void delete_dynamic_element(DYNAMIC_ARRAY *array, uint idx) +{ + char *ptr= (char*) array->buffer+array->size_of_element*idx; + array->elements--; + memmove(ptr,ptr+array->size_of_element, + (array->elements-idx)*array->size_of_element); +} + + +/* + Free unused memory + + SYNOPSIS + freeze_size() + array Array to be freed + +*/ + +void freeze_size(DYNAMIC_ARRAY *array) +{ + uint elements=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->buffer, + elements*array->size_of_element, + MYF(MY_WME)); + array->max_element=elements; + } +} + + +/* + Get the index of a dynamic element + + SYNOPSIS + get_index_dynamic() + array Array + element Whose element index + +*/ + +int get_index_dynamic(DYNAMIC_ARRAY *array, uchar* element) +{ + uint ret; + if (array->buffer > element) + return -1; + + ret= (element - array->buffer) / array->size_of_element; + if (ret > array->elements) + return -1; + + return ret; + +} diff --git a/externals/mysql/mysys/base64.c b/externals/mysql/mysys/base64.c new file mode 100644 index 00000000000..7e69ab54500 --- /dev/null +++ b/externals/mysql/mysys/base64.c @@ -0,0 +1,311 @@ +/* Copyright (C) 2003 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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+/"; + + +int +my_base64_needed_encoded_length(int length_of_data) +{ + int nb_base64_chars; + nb_base64_chars= (length_of_data + 2) / 3 * 4; + + return + nb_base64_chars + /* base64 char incl padding */ + (nb_base64_chars - 1)/ 76 + /* newlines */ + 1; /* NUL termination of string */ +} + + +int +my_base64_needed_decoded_length(int length_of_encoded_data) +{ + return (int) ceil(length_of_encoded_data * 3 / 4); +} + + +/* + Encode a data as base64. + + Note: We require that dst is pre-allocated to correct size. + See my_base64_needed_encoded_length(). +*/ + +int +my_base64_encode(const void *src, size_t src_len, char *dst) +{ + const unsigned char *s= (const unsigned char*)src; + size_t i= 0; + size_t len= 0; + + for (; i < src_len; len += 4) + { + unsigned c; + + if (len == 76) + { + len= 0; + *dst++= '\n'; + } + + c= s[i++]; + c <<= 8; + + if (i < src_len) + c += s[i]; + c <<= 8; + i++; + + if (i < src_len) + c += s[i]; + i++; + + *dst++= base64_table[(c >> 18) & 0x3f]; + *dst++= base64_table[(c >> 12) & 0x3f]; + + if (i > (src_len + 1)) + *dst++= '='; + else + *dst++= base64_table[(c >> 6) & 0x3f]; + + if (i > src_len) + *dst++= '='; + else + *dst++= base64_table[(c >> 0) & 0x3f]; + } + *dst= '\0'; + + return 0; +} + + +static inline uint +pos(unsigned char c) +{ + return (uint) (strchr(base64_table, c) - base64_table); +} + + +#define SKIP_SPACE(src, i, size) \ +{ \ + while (i < size && my_isspace(&my_charset_latin1, * src)) \ + { \ + i++; \ + src++; \ + } \ + if (i == size) \ + { \ + break; \ + } \ +} + + +/* + Decode a base64 string + + SYNOPSIS + base64_decode() + src Pointer to base64-encoded string + len Length of string at 'src' + dst Pointer to location where decoded data will be stored + end_ptr Pointer to variable that will refer to the character + after the end of the encoded data that were decoded. Can + be NULL. + + DESCRIPTION + + 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. + + SEE ALSO + my_base64_needed_decoded_length(). + + RETURN VALUE + Number of bytes written at 'dst' or -1 in case of failure +*/ +int +my_base64_decode(const char *src_base, size_t len, + void *dst, const char **end_ptr) +{ + char b[3]; + size_t i= 0; + char *dst_base= (char *)dst; + char const *src= src_base; + char *d= dst_base; + size_t j; + + while (i < len) + { + unsigned c= 0; + size_t mark= 0; + + SKIP_SPACE(src, i, len); + + c += pos(*src++); + c <<= 6; + i++; + + SKIP_SPACE(src, i, len); + + c += pos(*src++); + c <<= 6; + i++; + + SKIP_SPACE(src, i, len); + + if (*src != '=') + c += pos(*src++); + else + { + src += 2; /* There should be two bytes padding */ + i= len; + mark= 2; + c <<= 6; + goto end; + } + c <<= 6; + i++; + + SKIP_SPACE(src, i, len); + + if (*src != '=') + c += pos(*src++); + else + { + src += 1; /* There should be one byte padding */ + i= len; + mark= 1; + goto end; + } + i++; + + end: + b[0]= (c >> 16) & 0xff; + b[1]= (c >> 8) & 0xff; + b[2]= (c >> 0) & 0xff; + + for (j=0; j<3-mark; j++) + *d++= b[j]; + } + + if (end_ptr != NULL) + *end_ptr= src; + + /* + The variable 'i' is set to 'len' when padding has been read, so it + does not actually reflect the number of bytes read from 'src'. + */ + return i != len ? -1 : (int) (d - dst_base); +} + + +#else /* MAIN */ + +#define require(b) { \ + if (!(b)) { \ + printf("Require failed at %s:%d\n", __FILE__, __LINE__); \ + abort(); \ + } \ +} + + +int +main(void) +{ + int i; + size_t j; + size_t k, l; + size_t dst_len; + size_t needed_length; + + for (i= 0; i < 500; i++) + { + /* Create source data */ + const size_t src_len= rand() % 1000 + 1; + + char * src= (char *) malloc(src_len); + char * s= src; + char * str; + char * dst; + + require(src); + for (j= 0; j<src_len; j++) + { + char c= rand(); + *s++= c; + } + + /* Encode */ + needed_length= my_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(my_base64_encode(src, src_len, str) == 0); + require(needed_length == strlen(str) + 1); + + /* Decode */ + dst= (char *) malloc(my_base64_needed_decoded_length(strlen(str))); + require(dst); + dst_len= my_base64_decode(str, strlen(str), dst, NULL); + require(dst_len == src_len); + + if (memcmp(src, dst, src_len) != 0) + { + printf(" --------- src --------- --------- dst ---------\n"); + for (k= 0; k<src_len; k+=8) + { + printf("%.4x ", (uint) k); + for (l=0; l<8 && k+l<src_len; l++) + { + unsigned char c= src[k+l]; + printf("%.2x ", (unsigned)c); + } + + printf(" "); + + for (l=0; l<8 && k+l<dst_len; l++) + { + unsigned char c= dst[k+l]; + printf("%.2x ", (unsigned)c); + } + printf("\n"); + } + printf("src length: %.8x, dst length: %.8x\n", + (uint) src_len, (uint) dst_len); + require(0); + } + } + printf("Test succeeded.\n"); + return 0; +} + +#endif diff --git a/externals/mysql/mysys/charset-def.c b/externals/mysql/mysys/charset-def.c new file mode 100644 index 00000000000..6d3cda5279a --- /dev/null +++ b/externals/mysql/mysys/charset-def.c @@ -0,0 +1,356 @@ +/* Copyright (C) 2000 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#include "mysys_priv.h" + +/* + Include all compiled character sets into the client + If a client don't want to use all of them, he can define his own + init_compiled_charsets() that only adds those that he wants +*/ + +#ifdef HAVE_UCA_COLLATIONS + +#ifdef HAVE_CHARSET_ucs2 +extern CHARSET_INFO my_charset_ucs2_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_sinhala_uca_ci; +#endif /* HAVE_CHARSET_ucs2 */ + + +#ifdef HAVE_CHARSET_utf32 +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_sinhala_uca_ci; +#endif /* HAVE_CHARSET_utf32 */ + + +#ifdef HAVE_CHARSET_utf16 +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_sinhala_uca_ci; +#endif /* HAVE_CHARSET_utf16 */ + + +#ifdef HAVE_CHARSET_utf8mb3 +extern CHARSET_INFO my_charset_utf8mb3_icelandic_uca_ci; +extern CHARSET_INFO my_charset_utf8mb3_latvian_uca_ci; +extern CHARSET_INFO my_charset_utf8mb3_romanian_uca_ci; +extern CHARSET_INFO my_charset_utf8mb3_slovenian_uca_ci; +extern CHARSET_INFO my_charset_utf8mb3_polish_uca_ci; +extern CHARSET_INFO my_charset_utf8mb3_estonian_uca_ci; +extern CHARSET_INFO my_charset_utf8mb3_spanish_uca_ci; +extern CHARSET_INFO my_charset_utf8mb3_swedish_uca_ci; +extern CHARSET_INFO my_charset_utf8mb3_turkish_uca_ci; +extern CHARSET_INFO my_charset_utf8mb3_czech_uca_ci; +extern CHARSET_INFO my_charset_utf8mb3_danish_uca_ci; +extern CHARSET_INFO my_charset_utf8mb3_lithuanian_uca_ci; +extern CHARSET_INFO my_charset_utf8mb3_slovak_uca_ci; +extern CHARSET_INFO my_charset_utf8mb3_spanish2_uca_ci; +extern CHARSET_INFO my_charset_utf8mb3_roman_uca_ci; +extern CHARSET_INFO my_charset_utf8mb3_persian_uca_ci; +extern CHARSET_INFO my_charset_utf8mb3_esperanto_uca_ci; +extern CHARSET_INFO my_charset_utf8mb3_hungarian_uca_ci; +extern CHARSET_INFO my_charset_utf8mb3_sinhala_uca_ci; +#ifdef HAVE_UTF8_GENERAL_CS +extern CHARSET_INFO my_charset_utf8mb3_general_cs; +#endif +#endif /* HAVE_CHARSET_utf8mb3 */ + +#ifdef HAVE_CHARSET_utf8mb4 +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_sinhala_uca_ci; +#endif /* HAVE_CHARSET_utf8mb4 */ + +#endif /* HAVE_UCA_COLLATIONS */ + + +my_bool init_compiled_charsets(myf flags __attribute__((unused))) +{ + CHARSET_INFO *cs; + + add_compiled_collation(&my_charset_bin); + add_compiled_collation(&my_charset_filename); + + 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_sjis + add_compiled_collation(&my_charset_sjis_japanese_ci); + add_compiled_collation(&my_charset_sjis_bin); +#endif + +#ifdef HAVE_CHARSET_tis620 + add_compiled_collation(&my_charset_tis620_thai_ci); + add_compiled_collation(&my_charset_tis620_bin); +#endif + +#ifdef HAVE_CHARSET_ucs2 + add_compiled_collation(&my_charset_ucs2_general_ci); + add_compiled_collation(&my_charset_ucs2_bin); +#ifdef HAVE_UCA_COLLATIONS + add_compiled_collation(&my_charset_ucs2_unicode_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_sinhala_uca_ci); +#endif +#endif + +#ifdef HAVE_CHARSET_ujis + add_compiled_collation(&my_charset_ujis_japanese_ci); + add_compiled_collation(&my_charset_ujis_bin); +#endif + +#ifdef HAVE_CHARSET_utf8mb3 + add_compiled_collation(&my_charset_utf8mb3_general_ci); + add_compiled_collation(&my_charset_utf8mb3_bin); +#ifdef HAVE_UTF8_GENERAL_CS + add_compiled_collation(&my_charset_utf8mb3_general_cs); +#endif +#ifdef HAVE_UCA_COLLATIONS + add_compiled_collation(&my_charset_utf8mb3_unicode_ci); + add_compiled_collation(&my_charset_utf8mb3_icelandic_uca_ci); + add_compiled_collation(&my_charset_utf8mb3_latvian_uca_ci); + add_compiled_collation(&my_charset_utf8mb3_romanian_uca_ci); + add_compiled_collation(&my_charset_utf8mb3_slovenian_uca_ci); + add_compiled_collation(&my_charset_utf8mb3_polish_uca_ci); + add_compiled_collation(&my_charset_utf8mb3_estonian_uca_ci); + add_compiled_collation(&my_charset_utf8mb3_spanish_uca_ci); + add_compiled_collation(&my_charset_utf8mb3_swedish_uca_ci); + add_compiled_collation(&my_charset_utf8mb3_turkish_uca_ci); + add_compiled_collation(&my_charset_utf8mb3_czech_uca_ci); + add_compiled_collation(&my_charset_utf8mb3_danish_uca_ci); + add_compiled_collation(&my_charset_utf8mb3_lithuanian_uca_ci); + add_compiled_collation(&my_charset_utf8mb3_slovak_uca_ci); + add_compiled_collation(&my_charset_utf8mb3_spanish2_uca_ci); + add_compiled_collation(&my_charset_utf8mb3_roman_uca_ci); + add_compiled_collation(&my_charset_utf8mb3_persian_uca_ci); + add_compiled_collation(&my_charset_utf8mb3_esperanto_uca_ci); + add_compiled_collation(&my_charset_utf8mb3_hungarian_uca_ci); + add_compiled_collation(&my_charset_utf8mb3_sinhala_uca_ci); +#endif /* HAVE_UCA_COLLATIONS */ +#endif /* HAVE_CHARSET_utf8mb3 */ + + +#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_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_sinhala_uca_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); +#ifdef HAVE_UCA_COLLATIONS + add_compiled_collation(&my_charset_utf16_unicode_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_sinhala_uca_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_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_sinhala_uca_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/externals/mysql/mysys/charset.c b/externals/mysql/mysys/charset.c new file mode 100644 index 00000000000..a327b243e57 --- /dev/null +++ b/externals/mysql/mysys/charset.c @@ -0,0 +1,915 @@ +/* Copyright (C) 2000 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#include "mysys_priv.h" +#include "mysys_err.h" +#include <m_ctype.h> +#include <m_string.h> +#include <my_dir.h> +#include <my_xml.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(CHARSET_INFO *cs1, 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 my_bool init_state_maps(CHARSET_INFO *cs) +{ + uint i; + uchar *state_map; + uchar *ident_map; + + if (!(cs->state_map= (uchar*) my_once_alloc(256, MYF(MY_WME)))) + return 1; + + if (!(cs->ident_map= (uchar*) my_once_alloc(256, MYF(MY_WME)))) + return 1; + + state_map= cs->state_map; + ident_map= cs->ident_map; + + /* Fill state_map with states to get a faster parser */ + for (i=0; i < 256 ; i++) + { + if (my_isalpha(cs,i)) + state_map[i]=(uchar) MY_LEX_IDENT; + else if (my_isdigit(cs,i)) + state_map[i]=(uchar) MY_LEX_NUMBER_IDENT; +#if defined(USE_MB) && defined(USE_MB_IDENT) + else if (my_mbcharlen(cs, i)>1) + state_map[i]=(uchar) MY_LEX_IDENT; +#endif + else if (my_isspace(cs,i)) + state_map[i]=(uchar) MY_LEX_SKIP; + else + state_map[i]=(uchar) MY_LEX_CHAR; + } + state_map[(uchar)'_']=state_map[(uchar)'$']=(uchar) MY_LEX_IDENT; + state_map[(uchar)'\'']=(uchar) MY_LEX_STRING; + state_map[(uchar)'.']=(uchar) MY_LEX_REAL_OR_POINT; + state_map[(uchar)'>']=state_map[(uchar)'=']=state_map[(uchar)'!']= (uchar) MY_LEX_CMP_OP; + state_map[(uchar)'<']= (uchar) MY_LEX_LONG_CMP_OP; + state_map[(uchar)'&']=state_map[(uchar)'|']=(uchar) MY_LEX_BOOL; + state_map[(uchar)'#']=(uchar) MY_LEX_COMMENT; + state_map[(uchar)';']=(uchar) MY_LEX_SEMICOLON; + state_map[(uchar)':']=(uchar) MY_LEX_SET_VAR; + state_map[0]=(uchar) MY_LEX_EOL; + state_map[(uchar)'\\']= (uchar) MY_LEX_ESCAPE; + state_map[(uchar)'/']= (uchar) MY_LEX_LONG_COMMENT; + state_map[(uchar)'*']= (uchar) MY_LEX_END_LONG_COMMENT; + state_map[(uchar)'@']= (uchar) MY_LEX_USER_END; + state_map[(uchar) '`']= (uchar) MY_LEX_USER_VARIABLE_DELIMITER; + state_map[(uchar)'"']= (uchar) MY_LEX_STRING_OR_DELIMITER; + + /* + Create a second map to make it faster to find identifiers + */ + for (i=0; i < 256 ; i++) + { + ident_map[i]= (uchar) (state_map[i] == MY_LEX_IDENT || + state_map[i] == MY_LEX_NUMBER_IDENT); + } + + /* Special handling of hex and binary strings */ + state_map[(uchar)'x']= state_map[(uchar)'X']= (uchar) MY_LEX_IDENT_OR_HEX; + state_map[(uchar)'b']= state_map[(uchar)'B']= (uchar) MY_LEX_IDENT_OR_BIN; + state_map[(uchar)'n']= state_map[(uchar)'N']= (uchar) MY_LEX_IDENT_OR_NCHAR; + return 0; +} + + +static 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->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)))) + { + if (!all_charsets[cs->number]) + { + if (!(all_charsets[cs->number]= + (CHARSET_INFO*) my_once_alloc(sizeof(CHARSET_INFO),MYF(0)))) + return MY_XML_ERROR; + bzero((void*)all_charsets[cs->number],sizeof(CHARSET_INFO)); + } + + if (cs->primary_number == cs->number) + cs->state |= MY_CS_PRIMARY; + + if (cs->binary_number == cs->number) + cs->state |= MY_CS_BINSORT; + + all_charsets[cs->number]->state|= cs->state; + + if (!(all_charsets[cs->number]->state & MY_CS_COMPILED)) + { + CHARSET_INFO *newcs= all_charsets[cs->number]; + if (cs_copy_data(all_charsets[cs->number],cs)) + return MY_XML_ERROR; + + 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")) + { +#if defined (HAVE_CHARSET_utf8mb3) && defined(HAVE_UCA_COLLATIONS) + copy_uca_collation(newcs, &my_charset_utf8mb4_unicode_ci); + newcs->state|= MY_CS_AVAILABLE | MY_CS_LOADED; +#endif + } + else if (!strcmp(cs->csname, "utf8mb3")) + { +#if defined (HAVE_CHARSET_utf8mb3) && defined(HAVE_UCA_COLLATIONS) + copy_uca_collation(newcs, &my_charset_utf8mb3_unicode_ci); + 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 + { + 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 && !dst->csname) + if (!(dst->csname= my_once_strdup(cs->csname,MYF(MY_WME)))) + return MY_XML_ERROR; + if (cs->name && !dst->name) + if (!(dst->name= my_once_strdup(cs->name,MYF(MY_WME)))) + return MY_XML_ERROR; + } + cs->number= 0; + cs->primary_number= 0; + cs->binary_number= 0; + cs->name= NULL; + cs->state= 0; + cs->sort_order= NULL; + cs->state= 0; + } + return MY_XML_OK; +} + + +#define MY_MAX_ALLOWED_BUF 1024*1024 +#define MY_CHARSET_INDEX "Index.xml" + +const char *charsets_dir= NULL; +static int charset_initialized=0; + + +static my_bool my_read_charset_file(const char *filename, myf myflags) +{ + uchar *buf; + int fd; + uint len, tmp_len; + MY_STAT stat_info; + + if (!my_stat(filename, &stat_info, MYF(myflags)) || + ((len= (uint)stat_info.st_size) > MY_MAX_ALLOWED_BUF) || + !(buf= (uchar*) my_malloc(len,myflags))) + return TRUE; + + if ((fd=my_open(filename,O_RDONLY,myflags)) < 0) + goto error; + tmp_len=my_read(fd, buf, len, myflags); + my_close(fd,myflags); + if (tmp_len != len) + goto error; + + if (my_parse_charset_xml((char*) buf,len,add_collation)) + { +#ifdef NOT_YET + printf("ERROR at line %d pos %d '%s'\n", + my_xml_error_lineno(&p)+1, + my_xml_error_pos(&p), + my_xml_error_string(&p)); +#endif + } + + my_free(buf, myflags); + return FALSE; + +error: + my_free(buf, myflags); + return TRUE; +} + + +char *get_charsets_dir(char *buf) +{ + const char *sharedir= SHAREDIR; + 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]; +CHARSET_INFO *default_charset_info = &my_charset_latin1; + +void add_compiled_collation(CHARSET_INFO *cs) +{ + all_charsets[cs->number]= cs; + cs->state|= MY_CS_AVAILABLE; +} + +static void *cs_alloc(size_t size) +{ + return my_once_alloc(size, MYF(MY_WME)); +} + + +#ifdef __NETWARE__ +my_bool STDCALL init_available_charsets(myf myflags) +#else +static my_bool init_available_charsets(myf myflags) +#endif +{ + char fname[FN_REFLEN + sizeof(MY_CHARSET_INDEX)]; + my_bool error=FALSE; + /* + We have to use charset_initialized to not lock on THR_LOCK_charset + inside get_internal_charset... + */ + if (!charset_initialized) + { + CHARSET_INFO **cs; + /* + To make things thread safe we are not allowing other threads to interfere + while we may changing the cs_info_table + */ + pthread_mutex_lock(&THR_LOCK_charset); + if (!charset_initialized) + { + bzero(&all_charsets,sizeof(all_charsets)); + init_compiled_charsets(myflags); + + /* Copy compiled charsets */ + for (cs=all_charsets; + cs < all_charsets + array_elements(all_charsets); + cs++) + { + if (*cs) + { + if (cs[0]->ctype) + if (init_state_maps(*cs)) + *cs= NULL; + } + } + + strmov(get_charsets_dir(fname), MY_CHARSET_INDEX); + error= my_read_charset_file(fname,myflags); + charset_initialized=1; + } + pthread_mutex_unlock(&THR_LOCK_charset); + } + return error; +} + + +void free_charsets(void) +{ + charset_initialized=0; +} + + +uint get_collation_number(const char *name) +{ + init_available_charsets(MYF(0)); + return get_collation_number_internal(name); +} + + +uint get_charset_number(const char *charset_name, uint cs_flags) +{ + CHARSET_INFO **cs; + init_available_charsets(MYF(0)); + + 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; +} + + +const char *get_charset_name(uint charset_number) +{ + CHARSET_INFO *cs; + init_available_charsets(MYF(0)); + + cs=all_charsets[charset_number]; + if (cs && (cs->number == charset_number) && cs->name ) + return (char*) cs->name; + + return (char*) "?"; /* this mimics find_type() */ +} + + +static CHARSET_INFO *get_internal_charset(uint cs_number, myf flags) +{ + char buf[FN_REFLEN]; + CHARSET_INFO *cs; + + 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 + */ + pthread_mutex_lock(&THR_LOCK_charset); + + if (!(cs->state & (MY_CS_COMPILED|MY_CS_LOADED))) /* if CS is not in memory */ + { + strxmov(get_charsets_dir(buf), cs->csname, ".xml", NullS); + my_read_charset_file(buf,flags); + } + + if (cs->state & MY_CS_AVAILABLE) + { + if (!(cs->state & MY_CS_READY)) + { + if ((cs->cset->init && cs->cset->init(cs, cs_alloc)) || + (cs->coll->init && cs->coll->init(cs, cs_alloc))) + cs= NULL; + else + cs->state|= MY_CS_READY; + } + } + else + cs= NULL; + + pthread_mutex_unlock(&THR_LOCK_charset); + } + return cs; +} + + +CHARSET_INFO *get_charset(uint cs_number, myf flags) +{ + CHARSET_INFO *cs; + if (cs_number == default_charset_info->number) + return default_charset_info; + + (void) init_available_charsets(MYF(0)); /* If it isn't initialized */ + + if (!cs_number || cs_number > array_elements(all_charsets)) + return NULL; + + cs=get_internal_charset(cs_number, flags); + + if (!cs && (flags & MY_WME)) + { + char index_file[FN_REFLEN + sizeof(MY_CHARSET_INDEX)], cs_string[23]; + strmov(get_charsets_dir(index_file),MY_CHARSET_INDEX); + cs_string[0]='#'; + int10_to_str(cs_number, cs_string+1, 10); + my_error(EE_UNKNOWN_CHARSET, MYF(ME_BELL), cs_string, index_file); + } + return cs; +} + +CHARSET_INFO *get_charset_by_name(const char *cs_name, myf flags) +{ + uint cs_number; + CHARSET_INFO *cs; + (void) init_available_charsets(MYF(0)); /* If it isn't initialized */ + + cs_number=get_collation_number(cs_name); + cs= cs_number ? get_internal_charset(cs_number,flags) : NULL; + + if (!cs && (flags & MY_WME)) + { + char index_file[FN_REFLEN + sizeof(MY_CHARSET_INDEX)]; + strmov(get_charsets_dir(index_file),MY_CHARSET_INDEX); + my_error(EE_UNKNOWN_COLLATION, MYF(ME_BELL), cs_name, index_file); + } + + return cs; +} + + +CHARSET_INFO *get_charset_by_csname(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)); + + (void) init_available_charsets(MYF(0)); /* If it isn't initialized */ + + cs_number= get_charset_number(cs_name, cs_flags); + cs= cs_number ? get_internal_charset(cs_number, flags) : NULL; + + if (!cs && (flags & MY_WME)) + { + char index_file[FN_REFLEN + sizeof(MY_CHARSET_INDEX)]; + strmov(get_charsets_dir(index_file),MY_CHARSET_INDEX); + my_error(EE_UNKNOWN_CHARSET, MYF(ME_BELL), cs_name, index_file); + } + + DBUG_RETURN(cs); +} + + +/** + 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, + CHARSET_INFO *default_cs, + 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, + CHARSET_INFO *default_cl, + 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(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; +#ifdef USE_MB + my_bool use_mb_flag= use_mb(charset_info); +#endif + for (end= from + length; from < end; from++) + { + char escape= 0; +#ifdef USE_MB + int tmp_length; + if (use_mb_flag && (tmp_length= my_ismbchar(charset_info, from, end))) + { + if (to + tmp_length > to_end) + { + overflow= TRUE; + break; + } + while (tmp_length--) + *to++= *from++; + from--; + continue; + } + /* + If the next character appears to begin a multi-byte character, we + escape that first byte of that apparent multi-byte character. (The + character just looks like a multi-byte character -- if it were actually + a multi-byte character, it would have been passed through in the test + above.) + + Without this check, we can create a problem by converting an invalid + multi-byte character into a valid one. For example, 0xbf27 is not + a valid GBK character, but 0xbf5c is. (0x27 = ', 0x5c = \) + */ + if (use_mb_flag && (tmp_length= my_mbcharlen(charset_info, *from)) > 1) + escape= *from; + else +#endif + 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 BACKSLASH_MBTAIL +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= !strcmp(buf, "cp932") ? + &my_charset_cp932_japanese_ci : &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 + + DESCRIPTION + This escapes the contents of a string by doubling up any apostrophes that + it contains. This is used when the NO_BACKSLASH_ESCAPES SQL_MODE is in + effect on the server. + + NOTE + To be consistent with escape_string_for_mysql(), to_length may be 0 to + mean "big enough" + + RETURN VALUES + ~0 The escaped string did not fit in the to buffer + >=0 The length of the escaped string +*/ + +size_t escape_quotes_for_mysql(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; +#ifdef USE_MB + my_bool use_mb_flag= use_mb(charset_info); +#endif + for (end= from + length; from < end; from++) + { +#ifdef USE_MB + int tmp_length; + if (use_mb_flag && (tmp_length= my_ismbchar(charset_info, from, end))) + { + if (to + tmp_length > to_end) + { + overflow= TRUE; + break; + } + while (tmp_length--) + *to++= *from++; + from--; + continue; + } + /* + We don't have the same issue here with a non-multi-byte character being + turned into a multi-byte character by the addition of an escaping + character, because we are only escaping the ' character with itself. + */ +#endif + if (*from == '\'') + { + if (to + 2 > to_end) + { + overflow= TRUE; + break; + } + *to++= '\''; + *to++= '\''; + } + else + { + if (to + 1 > to_end) + { + overflow= TRUE; + break; + } + *to++= *from; + } + } + *to= 0; + return overflow ? (ulong)~0 : (ulong) (to - to_start); +} diff --git a/externals/mysql/mysys/checksum.c b/externals/mysql/mysys/checksum.c new file mode 100644 index 00000000000..1d264b54321 --- /dev/null +++ b/externals/mysql/mysys/checksum.c @@ -0,0 +1,40 @@ +/* Copyright (C) 2000 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + + +#include <my_global.h> +#include <my_sys.h> +#include <zlib.h> + +ha_checksum my_crc_dbug_check= 1; /* Unlikely number */ + +/* + 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) +{ + crc= (ha_checksum) crc32((uint)crc, pos, length); + DBUG_PRINT("info", ("crc: %lu", (ulong) crc)); + if (crc == my_crc_dbug_check) + my_debug_put_break_here(); + return crc; +} diff --git a/externals/mysql/mysys/default.c b/externals/mysql/mysys/default.c new file mode 100644 index 00000000000..5e440b43fdd --- /dev/null +++ b/externals/mysql/mysys/default.c @@ -0,0 +1,1172 @@ +/* Copyright (C) 2000-2003 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +/**************************************************************************** + Add all options from files named "group".cnf from the default_directories + before the command line arguments. + On Windows defaults will also search in the Windows directory for a file + called 'group'.ini + As long as the program uses the last argument for conflicting + options one only have to add a call to "load_defaults" to enable + use of default values. + pre- and end 'blank space' are removed from options and values. The + following escape sequences are recognized in values: \b \t \n \r \\ + + The following arguments are handled automaticly; If used, they must be + first argument on the command line! + --no-defaults ; no options are read. + --defaults-file=full-path-to-default-file ; Only this file will be read. + --defaults-extra-file=full-path-to-default-file ; Read this file before ~/ + --defaults-group-suffix ; Also read groups with concat(group, suffix) + --print-defaults ; Print the modified command line and exit +****************************************************************************/ + +#include "mysys_priv.h" +#include "m_string.h" +#include "m_ctype.h" +#include <my_dir.h> +#ifdef __WIN__ +#include <winbase.h> +#endif + +/** + arguments separator + + load_defaults() loads arguments from config file and put them + before the arguments from command line, this separator is used to + separate the arguments loaded from config file and arguments user + provided on command line. + + Options with value loaded from config file are always in the form + '--option=value', while for command line options, the value can be + given as the next argument. Thus we used a separator so that + handle_options() can distinguish them. + + Note: any other places that does not need to distinguish them + should skip the separator. + + The content of arguments separator does not matter, one should only + check the pointer, use "----args-separator----" here to ease debug + if someone misused it. + + See BUG#25192 +*/ +const char *args_separator= "----args-separator----"; +const char *my_defaults_file=0; +const char *my_defaults_group_suffix=0; +char *my_defaults_extra_file=0; + +/* Which directories are searched for options (and in which order) */ + +#define MAX_DEFAULT_DIRS 6 +#define DEFAULT_DIRS_SIZE (MAX_DEFAULT_DIRS + 1) /* Terminate with NULL */ +static const char **default_directories = NULL; + +#ifdef __WIN__ +static const char *f_extensions[]= { ".ini", ".cnf", 0 }; +#define NEWLINE "\r\n" +#else +static const char *f_extensions[]= { ".cnf", 0 }; +#define NEWLINE "\n" +#endif + +static int handle_default_option(void *in_ctx, const char *group_name, + const char *option); + +/* + This structure defines the context that we pass to callback + function 'handle_default_option' used in search_default_file + to process each option. This context is used if search_default_file + was called from load_defaults. +*/ + +struct handle_option_ctx +{ + MEM_ROOT *alloc; + DYNAMIC_ARRAY *args; + TYPELIB *group; +}; + +static int search_default_file(Process_option_func func, void *func_ctx, + const char *dir, const char *config_file); +static int search_default_file_with_ext(Process_option_func func, + void *func_ctx, + const char *dir, const char *ext, + const char *config_file, int recursion_level); + + +/** + Create the list of default directories. + + @param alloc MEM_ROOT where the list of directories is stored + + @details + The directories searched, in order, are: + - Windows: GetSystemWindowsDirectory() + - Windows: GetWindowsDirectory() + - Windows: C:/ + - Windows: Directory above where the executable is located + - Netware: sys:/etc/ + - Unix: /etc/ + - Unix: /etc/mysql/ + - Unix: --sysconfdir=<path> (compile-time option) + - ALL: getenv(DEFAULT_HOME_ENV) + - ALL: --defaults-extra-file=<path> (run-time option) + - Unix: ~/ + + On all systems, if a directory is already in the list, it will be moved + to the end of the list. This avoids reading defaults files multiple times, + while ensuring the correct precedence. + + @retval NULL Failure (out of memory, probably) + @retval other Pointer to NULL-terminated array of default directories +*/ + +static const char **init_default_directories(MEM_ROOT *alloc); + + +static char *remove_end_comment(char *ptr); + + +/* + Process config files in default directories. + + SYNOPSIS + my_search_option_files() + conf_file Basename for configuration file to search for. + If this is a path, then only this file is read. + argc Pointer to argc of original program + argv Pointer to argv of original program + args_used Pointer to variable for storing the number of + arguments used. + func Pointer to the function to process options + func_ctx It's context. Usually it is the structure to + store additional options. + DESCRIPTION + Process the default options from argc & argv + Read through each found config file looks and calls 'func' to process + each option. + + NOTES + --defaults-group-suffix is only processed if we are called from + load_defaults(). + + + RETURN + 0 ok + 1 given cinf_file doesn't exist + 2 out of memory + + The global variable 'my_defaults_group_suffix' is updated with value for + --defaults_group_suffix +*/ + +int my_search_option_files(const char *conf_file, int *argc, char ***argv, + uint *args_used, Process_option_func func, + void *func_ctx, const char **default_directories) +{ + const char **dirs, *forced_default_file, *forced_extra_defaults; + int error= 0; + DBUG_ENTER("my_search_option_files"); + + /* Check if we want to force the use a specific default file */ + *args_used+= get_defaults_options(*argc - *args_used, *argv + *args_used, + (char **) &forced_default_file, + (char **) &forced_extra_defaults, + (char **) &my_defaults_group_suffix); + + if (! my_defaults_group_suffix) + my_defaults_group_suffix= getenv(STRINGIFY_ARG(DEFAULT_GROUP_SUFFIX_ENV)); + + if (forced_extra_defaults) + my_defaults_extra_file= (char *) forced_extra_defaults; + + if (forced_default_file) + my_defaults_file= forced_default_file; + + /* + We can only handle 'defaults-group-suffix' if we are called from + load_defaults() as otherwise we can't know the type of 'func_ctx' + */ + + if (my_defaults_group_suffix && func == handle_default_option) + { + /* Handle --defaults-group-suffix= */ + uint i; + const char **extra_groups; + const size_t instance_len= strlen(my_defaults_group_suffix); + struct handle_option_ctx *ctx= (struct handle_option_ctx*) func_ctx; + char *ptr; + TYPELIB *group= ctx->group; + + if (!(extra_groups= + (const char**)alloc_root(ctx->alloc, + (2*group->count+1)*sizeof(char*)))) + DBUG_RETURN(2); + + for (i= 0; i < group->count; i++) + { + size_t len; + extra_groups[i]= group->type_names[i]; /** copy group */ + + len= strlen(extra_groups[i]); + if (!(ptr= alloc_root(ctx->alloc, (uint) (len+instance_len+1)))) + DBUG_RETURN(2); + + extra_groups[i+group->count]= ptr; + + /** Construct new group */ + memcpy(ptr, extra_groups[i], len); + memcpy(ptr+len, my_defaults_group_suffix, instance_len+1); + } + + group->count*= 2; + group->type_names= extra_groups; + group->type_names[group->count]= 0; + } + + if (forced_default_file) + { + if ((error= search_default_file_with_ext(func, func_ctx, "", "", + forced_default_file, 0)) < 0) + goto err; + if (error > 0) + { + fprintf(stderr, "Could not open required defaults file: %s\n", + forced_default_file); + goto err; + } + } + else if (dirname_length(conf_file)) + { + if ((error= search_default_file(func, func_ctx, NullS, conf_file)) < 0) + goto err; + } + else + { + for (dirs= default_directories ; *dirs; dirs++) + { + if (**dirs) + { + if (search_default_file(func, func_ctx, *dirs, conf_file) < 0) + goto err; + } + else if (my_defaults_extra_file) + { + if ((error= search_default_file_with_ext(func, func_ctx, "", "", + my_defaults_extra_file, 0)) < 0) + goto err; /* Fatal error */ + if (error > 0) + { + fprintf(stderr, "Could not open required defaults file: %s\n", + my_defaults_extra_file); + goto err; + } + } + } + } + + DBUG_RETURN(0); + +err: + fprintf(stderr,"Fatal error in defaults handling. Program aborted\n"); + DBUG_RETURN(1); +} + + +/* + The option handler for load_defaults. + + SYNOPSIS + handle_deault_option() + in_ctx Handler context. In this case it is a + handle_option_ctx structure. + group_name The name of the group the option belongs to. + option The very option to be processed. It is already + prepared to be used in argv (has -- prefix). If it + is NULL, we are handling a new group (section). + + DESCRIPTION + This handler checks whether a group is one of the listed and adds an option + to the array if yes. Some other handler can record, for instance, all + groups and their options, not knowing in advance the names and amount of + groups. + + RETURN + 0 - ok + 1 - error occured +*/ + +static int handle_default_option(void *in_ctx, const char *group_name, + const char *option) +{ + char *tmp; + struct handle_option_ctx *ctx= (struct handle_option_ctx *) in_ctx; + + if (!option) + return 0; + + if (find_type((char *)group_name, ctx->group, 3)) + { + if (!(tmp= alloc_root(ctx->alloc, strlen(option) + 1))) + return 1; + if (insert_dynamic(ctx->args, (uchar*) &tmp)) + return 1; + strmov(tmp, option); + } + + return 0; +} + + +/* + Gets options from the command line + + SYNOPSIS + get_defaults_options() + argc Pointer to argc of original program + argv Pointer to argv of original program + defaults --defaults-file option + extra_defaults --defaults-extra-file option + + RETURN + # Number of arguments used from *argv + defaults and extra_defaults will be set to option of the appropriate + items of argv array, or to NULL if there are no such options +*/ + +int get_defaults_options(int argc, char **argv, + char **defaults, + char **extra_defaults, + char **group_suffix) +{ + int org_argc= argc, prev_argc= 0; + *defaults= *extra_defaults= *group_suffix= 0; + + while (argc >= 2 && argc != prev_argc) + { + /* Skip program name or previously handled argument */ + argv++; + prev_argc= argc; /* To check if we found */ + if (!*defaults && is_prefix(*argv,"--defaults-file=")) + { + *defaults= *argv + sizeof("--defaults-file=")-1; + argc--; + continue; + } + if (!*extra_defaults && is_prefix(*argv,"--defaults-extra-file=")) + { + *extra_defaults= *argv + sizeof("--defaults-extra-file=")-1; + argc--; + continue; + } + if (!*group_suffix && is_prefix(*argv, "--defaults-group-suffix=")) + { + *group_suffix= *argv + sizeof("--defaults-group-suffix=")-1; + argc--; + continue; + } + } + return org_argc - argc; +} + +/* + Wrapper around my_load_defaults() for interface compatibility. + + SYNOPSIS + load_defaults() + conf_file Basename for configuration file to search for. + If this is a path, then only this file is read. + groups Which [group] entrys to read. + Points to an null terminated array of pointers + argc Pointer to argc of original program + argv Pointer to argv of original program + + NOTES + + This function is NOT thread-safe as it uses a global pointer internally. + See also notes for my_load_defaults(). + + RETURN + 0 ok + 1 The given conf_file didn't exists +*/ +int load_defaults(const char *conf_file, const char **groups, + int *argc, char ***argv) +{ + return my_load_defaults(conf_file, groups, argc, argv, &default_directories); +} + +/* + Read options from configurations files + + SYNOPSIS + my_load_defaults() + conf_file Basename for configuration file to search for. + If this is a path, then only this file is read. + groups Which [group] entrys to read. + Points to an null terminated array of pointers + argc Pointer to argc of original program + argv Pointer to argv of original program + default_directories Pointer to a location where a pointer to the list + of default directories will be stored + + IMPLEMENTATION + + Read options from configuration files and put them BEFORE the arguments + that are already in argc and argv. This way the calling program can + easily command line options override options in configuration files + + NOTES + In case of fatal error, the function will print a warning and do + exit(1) + + To free used memory one should call free_defaults() with the argument + that was put in *argv + + RETURN + - If successful, 0 is returned. If 'default_directories' is not NULL, + a pointer to the array of default directory paths is stored to a location + it points to. That stored value must be passed to my_search_option_files() + later. + + - 1 is returned if the given conf_file didn't exist. In this case, the + value pointed to by default_directories is undefined. +*/ + + +int my_load_defaults(const char *conf_file, const char **groups, + int *argc, char ***argv, const char ***default_directories) +{ + DYNAMIC_ARRAY args; + TYPELIB group; + my_bool found_print_defaults= 0; + uint args_used= 0; + int error= 0; + MEM_ROOT alloc; + char *ptr,**res; + struct handle_option_ctx ctx; + const char **dirs; + DBUG_ENTER("load_defaults"); + + init_alloc_root(&alloc,512,0); + if ((dirs= init_default_directories(&alloc)) == NULL) + goto err; + /* + Check if the user doesn't want any default option processing + --no-defaults is always the first option + */ + if (*argc >= 2 && !strcmp(argv[0][1],"--no-defaults")) + { + /* remove the --no-defaults argument and return only the other arguments */ + uint i; + if (!(ptr=(char*) alloc_root(&alloc,sizeof(alloc)+ + (*argc + 1)*sizeof(char*)))) + goto err; + res= (char**) (ptr+sizeof(alloc)); + res[0]= **argv; /* Copy program name */ + /* set arguments separator */ + res[1]= (char *)args_separator; + for (i=2 ; i < (uint) *argc ; i++) + res[i]=argv[0][i]; + res[i]=0; /* End pointer */ + *argv=res; + *(MEM_ROOT*) ptr= alloc; /* Save alloc root for free */ + if (default_directories) + *default_directories= dirs; + DBUG_RETURN(0); + } + + group.count=0; + group.name= "defaults"; + group.type_names= groups; + + for (; *groups ; groups++) + group.count++; + + if (my_init_dynamic_array(&args, sizeof(char*),*argc, 32)) + goto err; + + ctx.alloc= &alloc; + ctx.args= &args; + ctx.group= &group; + + error= my_search_option_files(conf_file, argc, argv, &args_used, + handle_default_option, (void *) &ctx, + dirs); + /* + Here error contains <> 0 only if we have a fully specified conf_file + or a forced default file + */ + if (!(ptr=(char*) alloc_root(&alloc,sizeof(alloc)+ + (args.elements + *argc + 1 + 1) *sizeof(char*)))) + goto err; + res= (char**) (ptr+sizeof(alloc)); + + /* copy name + found arguments + command line arguments to new array */ + res[0]= argv[0][0]; /* Name MUST be set, even by embedded library */ + memcpy((uchar*) (res+1), args.buffer, args.elements*sizeof(char*)); + /* Skip --defaults-xxx options */ + (*argc)-= args_used; + (*argv)+= args_used; + + /* + Check if we wan't to see the new argument list + This options must always be the last of the default options + */ + if (*argc >= 2 && !strcmp(argv[0][1],"--print-defaults")) + { + found_print_defaults=1; + --*argc; ++*argv; /* skip argument */ + } + + /* set arguments separator for arguments from config file and + command line */ + res[args.elements+1]= (char *)args_separator; + + if (*argc) + memcpy((uchar*) (res+1+args.elements+1), (char*) ((*argv)+1), + (*argc-1)*sizeof(char*)); + res[args.elements+ *argc+1]=0; /* last null */ + + (*argc)+=args.elements+1; + *argv= (char**) res; + *(MEM_ROOT*) ptr= alloc; /* Save alloc root for free */ + delete_dynamic(&args); + if (found_print_defaults) + { + int i; + printf("%s would have been started with the following arguments:\n", + **argv); + for (i=1 ; i < *argc ; i++) + if ((*argv)[i] != args_separator) /* skip arguments separator */ + printf("%s ", (*argv)[i]); + puts(""); + exit(0); + } + + if (error == 0 && default_directories) + *default_directories= dirs; + + DBUG_RETURN(error); + + err: + fprintf(stderr,"Fatal error in defaults handling. Program aborted\n"); + exit(1); + return 0; /* Keep compiler happy */ +} + + +void free_defaults(char **argv) +{ + MEM_ROOT ptr; + memcpy_fixed((char*) &ptr,(char *) argv - sizeof(ptr), sizeof(ptr)); + free_root(&ptr,MYF(0)); +} + + +static int search_default_file(Process_option_func opt_handler, + void *handler_ctx, + const char *dir, + const char *config_file) +{ + char **ext; + const char *empty_list[]= { "", 0 }; + my_bool have_ext= fn_ext(config_file)[0] != 0; + const char **exts_to_use= have_ext ? empty_list : f_extensions; + + for (ext= (char**) exts_to_use; *ext; ext++) + { + int error; + if ((error= search_default_file_with_ext(opt_handler, handler_ctx, + dir, *ext, + config_file, 0)) < 0) + return error; + } + return 0; +} + + +/* + Skip over keyword and get argument after keyword + + SYNOPSIS + get_argument() + keyword Include directive keyword + kwlen Length of keyword + ptr Pointer to the keword in the line under process + line line number + + RETURN + 0 error + # Returns pointer to the argument after the keyword. +*/ + +static char *get_argument(const char *keyword, size_t kwlen, + char *ptr, char *name, uint line) +{ + char *end; + + /* Skip over "include / includedir keyword" and following whitespace */ + + for (ptr+= kwlen - 1; + my_isspace(&my_charset_latin1, ptr[0]); + ptr++) + {} + + /* + Trim trailing whitespace from directory name + The -1 below is for the newline added by fgets() + Note that my_isspace() is true for \r and \n + */ + for (end= ptr + strlen(ptr) - 1; + my_isspace(&my_charset_latin1, *(end - 1)); + end--) + {} + end[0]= 0; /* Cut off end space */ + + /* Print error msg if there is nothing after !include* directive */ + if (end <= ptr) + { + fprintf(stderr, + "error: Wrong '!%s' directive in config file: %s at line %d\n", + keyword, name, line); + return 0; + } + return ptr; +} + + +/* + Open a configuration file (if exists) and read given options from it + + SYNOPSIS + search_default_file_with_ext() + opt_handler Option handler function. It is used to process + every separate option. + handler_ctx Pointer to the structure to store actual + parameters of the function. + dir directory to read + ext Extension for configuration file + config_file Name of configuration file + group groups to read + recursion_level the level of recursion, got while processing + "!include" or "!includedir" + + RETURN + 0 Success + -1 Fatal error, abort + 1 File not found (Warning) +*/ + +static int search_default_file_with_ext(Process_option_func opt_handler, + void *handler_ctx, + const char *dir, + const char *ext, + const char *config_file, + int recursion_level) +{ + char name[FN_REFLEN + 10], buff[4096], curr_gr[4096], *ptr, *end, **tmp_ext; + char *value, option[4096], tmp[FN_REFLEN]; + static const char includedir_keyword[]= "includedir"; + static const char include_keyword[]= "include"; + const int max_recursion_level= 10; + FILE *fp; + uint line=0; + my_bool found_group=0; + uint i; + MY_DIR *search_dir; + FILEINFO *search_file; + + if ((dir ? strlen(dir) : 0 )+strlen(config_file) >= FN_REFLEN-3) + return 0; /* Ignore wrong paths */ + if (dir) + { + end=convert_dirname(name, dir, NullS); + if (dir[0] == FN_HOMELIB) /* Add . to filenames in home */ + *end++='.'; + strxmov(end,config_file,ext,NullS); + } + else + { + strmov(name,config_file); + } + fn_format(name,name,"","",4); +#if !defined(__WIN__) && !defined(__NETWARE__) + { + MY_STAT stat_info; + if (!my_stat(name,&stat_info,MYF(0))) + return 1; + /* + Ignore world-writable regular files. + This is mainly done to protect us to not read a file created by + the mysqld server, but the check is still valid in most context. + */ + if ((stat_info.st_mode & S_IWOTH) && + (stat_info.st_mode & S_IFMT) == S_IFREG) + { + fprintf(stderr, "Warning: World-writable config file '%s' is ignored\n", + name); + return 0; + } + } +#endif + if (!(fp= my_fopen(name, O_RDONLY, MYF(0)))) + return 1; /* Ignore wrong files */ + + while (fgets(buff, sizeof(buff) - 1, fp)) + { + line++; + /* Ignore comment and empty lines */ + for (ptr= buff; my_isspace(&my_charset_latin1, *ptr); ptr++) + {} + + if (*ptr == '#' || *ptr == ';' || !*ptr) + continue; + + /* Configuration File Directives */ + if ((*ptr == '!')) + { + if (recursion_level >= max_recursion_level) + { + for (end= ptr + strlen(ptr) - 1; + my_isspace(&my_charset_latin1, *(end - 1)); + end--) + {} + end[0]= 0; + fprintf(stderr, + "Warning: skipping '%s' directive as maximum include" + "recursion level was reached in file %s at line %d\n", + ptr, name, line); + continue; + } + + /* skip over `!' and following whitespace */ + for (++ptr; my_isspace(&my_charset_latin1, ptr[0]); ptr++) + {} + + if ((!strncmp(ptr, includedir_keyword, + sizeof(includedir_keyword) - 1)) && + my_isspace(&my_charset_latin1, ptr[sizeof(includedir_keyword) - 1])) + { + if (!(ptr= get_argument(includedir_keyword, + sizeof(includedir_keyword), + ptr, name, line))) + goto err; + + if (!(search_dir= my_dir(ptr, MYF(MY_WME)))) + goto err; + + for (i= 0; i < (uint) search_dir->number_off_files; i++) + { + search_file= search_dir->dir_entry + i; + ext= fn_ext(search_file->name); + + /* check extension */ + for (tmp_ext= (char**) f_extensions; *tmp_ext; tmp_ext++) + { + if (!strcmp(ext, *tmp_ext)) + break; + } + + if (*tmp_ext) + { + fn_format(tmp, search_file->name, ptr, "", + MY_UNPACK_FILENAME | MY_SAFE_PATH); + + search_default_file_with_ext(opt_handler, handler_ctx, "", "", tmp, + recursion_level + 1); + } + } + + my_dirend(search_dir); + } + else if ((!strncmp(ptr, include_keyword, sizeof(include_keyword) - 1)) && + my_isspace(&my_charset_latin1, ptr[sizeof(include_keyword)-1])) + { + if (!(ptr= get_argument(include_keyword, + sizeof(include_keyword), ptr, + name, line))) + goto err; + + search_default_file_with_ext(opt_handler, handler_ctx, "", "", ptr, + recursion_level + 1); + } + + continue; + } + + if (*ptr == '[') /* Group name */ + { + found_group=1; + if (!(end=(char *) strchr(++ptr,']'))) + { + fprintf(stderr, + "error: Wrong group definition in config file: %s at line %d\n", + name,line); + goto err; + } + /* Remove end space */ + for ( ; my_isspace(&my_charset_latin1,end[-1]) ; end--) ; + end[0]=0; + + strmake(curr_gr, ptr, min((size_t) (end-ptr)+1, sizeof(curr_gr)-1)); + + /* signal that a new group is found */ + opt_handler(handler_ctx, curr_gr, NULL); + + continue; + } + if (!found_group) + { + fprintf(stderr, + "error: Found option without preceding group in config file: %s at line: %d\n", + name,line); + goto err; + } + + + end= remove_end_comment(ptr); + if ((value= strchr(ptr, '='))) + end= value; /* Option without argument */ + for ( ; my_isspace(&my_charset_latin1,end[-1]) ; end--) ; + if (!value) + { + strmake(strmov(option,"--"),ptr, (size_t) (end-ptr)); + if (opt_handler(handler_ctx, curr_gr, option)) + goto err; + } + else + { + /* Remove pre- and end space */ + char *value_end; + for (value++ ; my_isspace(&my_charset_latin1,*value); value++) ; + value_end=strend(value); + /* + We don't have to test for value_end >= value as we know there is + an '=' before + */ + for ( ; my_isspace(&my_charset_latin1,value_end[-1]) ; value_end--) ; + if (value_end < value) /* Empty string */ + value_end=value; + + /* remove quotes around argument */ + if ((*value == '\"' || *value == '\'') && /* First char is quote */ + (value + 1 < value_end ) && /* String is longer than 1 */ + *value == value_end[-1] ) /* First char is equal to last char */ + { + value++; + value_end--; + } + ptr=strnmov(strmov(option,"--"),ptr,(size_t) (end-ptr)); + *ptr++= '='; + + for ( ; value != value_end; value++) + { + if (*value == '\\' && value != value_end-1) + { + switch(*++value) { + case 'n': + *ptr++='\n'; + break; + case 't': + *ptr++= '\t'; + break; + case 'r': + *ptr++ = '\r'; + break; + case 'b': + *ptr++ = '\b'; + break; + case 's': + *ptr++= ' '; /* space */ + break; + case '\"': + *ptr++= '\"'; + break; + case '\'': + *ptr++= '\''; + break; + case '\\': + *ptr++= '\\'; + break; + default: /* Unknown; Keep '\' */ + *ptr++= '\\'; + *ptr++= *value; + break; + } + } + else + *ptr++= *value; + } + *ptr=0; + if (opt_handler(handler_ctx, curr_gr, option)) + goto err; + } + } + my_fclose(fp,MYF(0)); + return(0); + + err: + my_fclose(fp,MYF(0)); + return -1; /* Fatal error */ +} + + +static char *remove_end_comment(char *ptr) +{ + char quote= 0; /* we are inside quote marks */ + char escape= 0; /* symbol is protected by escape chagacter */ + + for (; *ptr; ptr++) + { + if ((*ptr == '\'' || *ptr == '\"') && !escape) + { + if (!quote) + quote= *ptr; + else if (quote == *ptr) + quote= 0; + } + /* We are not inside a string */ + if (!quote && *ptr == '#') + { + *ptr= 0; + return ptr; + } + escape= (quote && *ptr == '\\' && !escape); + } + return ptr; +} + +void my_print_default_files(const char *conf_file) +{ + const char *empty_list[]= { "", 0 }; + my_bool have_ext= fn_ext(conf_file)[0] != 0; + const char **exts_to_use= have_ext ? empty_list : f_extensions; + char name[FN_REFLEN], **ext; + + puts("\nDefault options are read from the following files in the given order:"); + + if (dirname_length(conf_file)) + fputs(conf_file,stdout); + else + { + const char **dirs; + MEM_ROOT alloc; + init_alloc_root(&alloc,512,0); + + if ((dirs= init_default_directories(&alloc)) == NULL) + { + fputs("Internal error initializing default directories list", stdout); + } + else + { + for ( ; *dirs; dirs++) + { + for (ext= (char**) exts_to_use; *ext; ext++) + { + const char *pos; + char *end; + if (**dirs) + pos= *dirs; + else if (my_defaults_extra_file) + pos= my_defaults_extra_file; + else + continue; + end= convert_dirname(name, pos, NullS); + if (name[0] == FN_HOMELIB) /* Add . to filenames in home */ + *end++= '.'; + strxmov(end, conf_file, *ext, " ", NullS); + fputs(name, stdout); + } + } + } + + free_root(&alloc, MYF(0)); + } + puts(""); +} + +void print_defaults(const char *conf_file, const char **groups) +{ + const char **groups_save= groups; + my_print_default_files(conf_file); + + fputs("The following groups are read:",stdout); + for ( ; *groups ; groups++) + { + fputc(' ',stdout); + fputs(*groups,stdout); + } + + if (my_defaults_group_suffix) + { + groups= groups_save; + for ( ; *groups ; groups++) + { + fputc(' ',stdout); + fputs(*groups,stdout); + fputs(my_defaults_group_suffix,stdout); + } + } + puts("\nThe following options may be given as the first argument:\n\ +--print-defaults Print the program argument list and exit\n\ +--no-defaults Don't read default options from any options file\n\ +--defaults-file=# Only read default options from the given file #\n\ +--defaults-extra-file=# Read this file after the global files are read"); +} + + +static int add_directory(MEM_ROOT *alloc, const char *dir, const char **dirs) +{ + char buf[FN_REFLEN]; + size_t len; + char *p; + my_bool err __attribute__((unused)); + + len= normalize_dirname(buf, dir); + if (!(p= strmake_root(alloc, buf, len))) + return 1; /* Failure */ + /* Should never fail if DEFAULT_DIRS_SIZE is correct size */ + err= array_append_string_unique(p, dirs, DEFAULT_DIRS_SIZE); + DBUG_ASSERT(err == FALSE); + + return 0; +} + + +#ifdef __WIN__ +/* + This wrapper for GetSystemWindowsDirectory() will dynamically bind to the + function if it is available, emulate it on NT4 Terminal Server by stripping + the \SYSTEM32 from the end of the results of GetSystemDirectory(), or just + return GetSystemDirectory(). + */ + +typedef UINT (WINAPI *GET_SYSTEM_WINDOWS_DIRECTORY)(LPSTR, UINT); + +static size_t my_get_system_windows_directory(char *buffer, size_t size) +{ + size_t count; + GET_SYSTEM_WINDOWS_DIRECTORY + func_ptr= (GET_SYSTEM_WINDOWS_DIRECTORY) + GetProcAddress(GetModuleHandle("kernel32.dll"), + "GetSystemWindowsDirectoryA"); + + if (func_ptr) + return func_ptr(buffer, (uint) size); + + /* + Windows NT 4.0 Terminal Server Edition: + To retrieve the shared Windows directory, call GetSystemDirectory and + trim the "System32" element from the end of the returned path. + */ + count= GetSystemDirectory(buffer, (uint) size); + if (count > 8 && stricmp(buffer+(count-8), "\\System32") == 0) + { + count-= 8; + buffer[count] = '\0'; + } + return count; +} + + +static const char *my_get_module_parent(char *buf, size_t size) +{ + char *last= NULL; + char *end; + if (!GetModuleFileName(NULL, buf, (DWORD) size)) + return NULL; + end= strend(buf); + + /* + Look for the second-to-last \ in the filename, but hang on + to a pointer after the last \ in case we're in the root of + a drive. + */ + for ( ; end > buf; end--) + { + if (*end == FN_LIBCHAR) + { + if (last) + { + /* Keep the last '\' as this works both with D:\ and a directory */ + end[1]= 0; + break; + } + last= end; + } + } + + return buf; +} +#endif /* __WIN__ */ + + +static const char **init_default_directories(MEM_ROOT *alloc) +{ + const char **dirs; + char *env; + int errors= 0; + + dirs= (const char **)alloc_root(alloc, DEFAULT_DIRS_SIZE * sizeof(char *)); + if (dirs == NULL) + return NULL; + bzero((char *) dirs, DEFAULT_DIRS_SIZE * sizeof(char *)); + +#ifdef __WIN__ + + { + char fname_buffer[FN_REFLEN]; + if (my_get_system_windows_directory(fname_buffer, sizeof(fname_buffer))) + errors += add_directory(alloc, fname_buffer, dirs); + + if (GetWindowsDirectory(fname_buffer, sizeof(fname_buffer))) + errors += add_directory(alloc, fname_buffer, dirs); + + errors += add_directory(alloc, "C:/", dirs); + + if (my_get_module_parent(fname_buffer, sizeof(fname_buffer)) != NULL) + errors += add_directory(alloc, fname_buffer, dirs); + } + +#elif defined(__NETWARE__) + + errors += add_directory(alloc, "sys:/etc/", dirs); + +#else + + errors += add_directory(alloc, "/etc/", dirs); + errors += add_directory(alloc, "/etc/mysql/", dirs); + +#if defined(DEFAULT_SYSCONFDIR) + if (DEFAULT_SYSCONFDIR[0]) + errors += add_directory(alloc, DEFAULT_SYSCONFDIR, dirs); +#endif /* DEFAULT_SYSCONFDIR */ + +#endif + + if ((env= getenv(STRINGIFY_ARG(DEFAULT_HOME_ENV)))) + errors += add_directory(alloc, env, dirs); + + /* Placeholder for --defaults-extra-file=<path> */ + errors += add_directory(alloc, "", dirs); + +#if !defined(__WIN__) && !defined(__NETWARE__) + errors += add_directory(alloc, "~/", dirs); +#endif + + return (errors > 0 ? NULL : dirs); +} diff --git a/externals/mysql/mysys/default_modify.c b/externals/mysql/mysys/default_modify.c new file mode 100644 index 00000000000..35555f5766a --- /dev/null +++ b/externals/mysql/mysys/default_modify.c @@ -0,0 +1,254 @@ +/* Copyright (C) 2005 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#include "my_global.h" +#include "mysys_priv.h" +#include "m_string.h" +#include <my_dir.h> + +#define BUFF_SIZE 1024 +#define RESERVE 1024 /* Extend buffer with this extent */ + +#ifdef _WIN32 +#define NEWLINE "\r\n" +#define NEWLINE_LEN 2 +#else +#define NEWLINE "\n" +#define NEWLINE_LEN 1 +#endif + +static char *add_option(char *dst, const char *option_value, + const char *option, int remove_option); + + +/* + Add/remove option to the option file section. + + SYNOPSYS + modify_defaults_file() + file_location The location of configuration file to edit + option The name of the option to look for (can be NULL) + option value The value of the option we would like to set (can be NULL) + section_name The name of the section (must be NOT NULL) + remove_option This defines what we want to remove: + - MY_REMOVE_NONE -- nothing to remove; + - MY_REMOVE_OPTION -- remove the specified option; + - MY_REMOVE_SECTION -- remove the specified section; + IMPLEMENTATION + We open the option file first, then read the file line-by-line, + looking for the section we need. At the same time we put these lines + into a buffer. Then we look for the option within this section and + change/remove it. In the end we get a buffer with modified version of the + file. Then we write it to the file, truncate it if needed and close it. + Note that there is a small time gap, when the file is incomplete, + and this theoretically might introduce a problem. + + RETURN + 0 - ok + 1 - some error has occured. Probably due to the lack of resourses + 2 - cannot open the file +*/ + +int modify_defaults_file(const char *file_location, const char *option, + const char *option_value, + const char *section_name, int remove_option) +{ + FILE *cnf_file; + MY_STAT file_stat; + char linebuff[BUFF_SIZE], *src_ptr, *dst_ptr, *file_buffer; + size_t opt_len= 0, optval_len= 0, sect_len; + uint nr_newlines= 0, buffer_size; + my_bool in_section= FALSE, opt_applied= 0; + uint reserve_extended; + uint new_opt_len; + int reserve_occupied= 0; + DBUG_ENTER("modify_defaults_file"); + + if (!(cnf_file= my_fopen(file_location, O_RDWR | O_BINARY, MYF(0)))) + DBUG_RETURN(2); + + /* my_fstat doesn't use the flag parameter */ + if (my_fstat(my_fileno(cnf_file), &file_stat, MYF(0))) + goto malloc_err; + + if (option && option_value) + { + opt_len= strlen(option); + optval_len= strlen(option_value); + } + + new_opt_len= opt_len + 1 + optval_len + NEWLINE_LEN; + + /* calculate the size of the buffer we need */ + reserve_extended= (opt_len + + 1 + /* For '=' char */ + optval_len + /* Option value len */ + NEWLINE_LEN + /* Space for newline */ + RESERVE); /* Some additional space */ + + buffer_size= (uint)(file_stat.st_size + + 1); /* The ending zero */ + + /* + Reserve space to read the contents of the file and some more + for the option we want to add. + */ + if (!(file_buffer= (char*) my_malloc(buffer_size + reserve_extended, + MYF(MY_WME)))) + goto malloc_err; + + sect_len= strlen(section_name); + + for (dst_ptr= file_buffer; fgets(linebuff, BUFF_SIZE, cnf_file); ) + { + /* Skip over whitespaces */ + for (src_ptr= linebuff; my_isspace(&my_charset_latin1, *src_ptr); + src_ptr++) + {} + + if (!*src_ptr) /* Empty line */ + { + nr_newlines++; + continue; + } + + /* correct the option (if requested) */ + if (option && in_section && !strncmp(src_ptr, option, opt_len) && + (*(src_ptr + opt_len) == '=' || + my_isspace(&my_charset_latin1, *(src_ptr + opt_len)) || + *(src_ptr + opt_len) == '\0')) + { + char *old_src_ptr= src_ptr; + src_ptr= strend(src_ptr+ opt_len); /* Find the end of the line */ + + /* could be negative */ + reserve_occupied+= (int) new_opt_len - (int) (src_ptr - old_src_ptr); + if (reserve_occupied >= (int) reserve_extended) + { + reserve_extended= (uint) reserve_occupied + RESERVE; + if (!(file_buffer= (char*) my_realloc(file_buffer, buffer_size + + reserve_extended, + MYF(MY_WME|MY_FREE_ON_ERROR)))) + goto malloc_err; + } + opt_applied= 1; + dst_ptr= add_option(dst_ptr, option_value, option, remove_option); + } + else + { + /* + If we are going to the new group and have an option to apply, do + it now. If we are removing a single option or the whole section + this will only trigger opt_applied flag. + */ + + if (in_section && !opt_applied && *src_ptr == '[') + { + dst_ptr= add_option(dst_ptr, option_value, option, remove_option); + opt_applied= 1; /* set the flag to do write() later */ + reserve_occupied= new_opt_len+ opt_len + 1 + NEWLINE_LEN; + } + + for (; nr_newlines; nr_newlines--) + dst_ptr= strmov(dst_ptr, NEWLINE); + + /* Skip the section if MY_REMOVE_SECTION was given */ + if (!in_section || remove_option != MY_REMOVE_SECTION) + dst_ptr= strmov(dst_ptr, linebuff); + } + /* Look for a section */ + if (*src_ptr == '[') + { + /* Copy the line to the buffer */ + if (!strncmp(++src_ptr, section_name, sect_len)) + { + src_ptr+= sect_len; + /* Skip over whitespaces. They are allowed after section name */ + for (; my_isspace(&my_charset_latin1, *src_ptr); src_ptr++) + {} + + if (*src_ptr != ']') + { + in_section= FALSE; + continue; /* Missing closing parenthesis. Assume this was no group */ + } + + if (remove_option == MY_REMOVE_SECTION) + dst_ptr= dst_ptr - strlen(linebuff); + + in_section= TRUE; + } + else + in_section= FALSE; /* mark that this section is of no interest to us */ + } + } + + /* + File ended. Apply an option or set opt_applied flag (in case of + MY_REMOVE_SECTION) so that the changes are saved. Do not do anything + if we are removing non-existent option. + */ + + if (!opt_applied && in_section && (remove_option != MY_REMOVE_OPTION)) + { + /* New option still remains to apply at the end */ + if (!remove_option && *(dst_ptr - 1) != '\n') + dst_ptr= strmov(dst_ptr, NEWLINE); + dst_ptr= add_option(dst_ptr, option_value, option, remove_option); + opt_applied= 1; + } + for (; nr_newlines; nr_newlines--) + dst_ptr= strmov(dst_ptr, NEWLINE); + + if (opt_applied) + { + /* Don't write the file if there are no changes to be made */ + if (my_chsize(my_fileno(cnf_file), (my_off_t) (dst_ptr - file_buffer), 0, + MYF(MY_WME)) || + my_fseek(cnf_file, 0, MY_SEEK_SET, MYF(0)) || + my_fwrite(cnf_file, (uchar*) file_buffer, (size_t) (dst_ptr - file_buffer), + MYF(MY_NABP))) + goto err; + } + if (my_fclose(cnf_file, MYF(MY_WME))) + DBUG_RETURN(1); + + my_free(file_buffer, MYF(0)); + DBUG_RETURN(0); + +err: + my_free(file_buffer, MYF(0)); +malloc_err: + my_fclose(cnf_file, MYF(0)); + DBUG_RETURN(1); /* out of resources */ +} + + +static char *add_option(char *dst, const char *option_value, + const char *option, int remove_option) +{ + if (!remove_option) + { + dst= strmov(dst, option); + if (*option_value) + { + *dst++= '='; + dst= strmov(dst, option_value); + } + /* add a newline */ + dst= strmov(dst, NEWLINE); + } + return dst; +} diff --git a/externals/mysql/mysys/errors.c b/externals/mysql/mysys/errors.c new file mode 100644 index 00000000000..0c1d8c1e0b6 --- /dev/null +++ b/externals/mysql/mysys/errors.c @@ -0,0 +1,110 @@ +/* Copyright (C) 2000 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#include "mysys_priv.h" +#include "mysys_err.h" + +#ifndef SHARED_LIBRARY + +const char * NEAR globerrs[GLOBERRS]= +{ + "Can't create/write to file '%s' (Errcode: %d)", + "Error reading file '%s' (Errcode: %d)", + "Error writing file '%s' (Errcode: %d)", + "Error on close of '%s' (Errcode: %d)", + "Out of memory (Needed %u bytes)", + "Error on delete of '%s' (Errcode: %d)", + "Error on rename of '%s' to '%s' (Errcode: %d)", + "", + "Unexpected eof found when reading file '%s' (Errcode: %d)", + "Can't lock file (Errcode: %d)", + "Can't unlock file (Errcode: %d)", + "Can't read dir of '%s' (Errcode: %d)", + "Can't get stat of '%s' (Errcode: %d)", + "Can't change size of file (Errcode: %d)", + "Can't open stream from handle (Errcode: %d)", + "Can't get working dirctory (Errcode: %d)", + "Can't change dir to '%s' (Errcode: %d)", + "Warning: '%s' had %d links", + "Warning: %d files and %d streams is left open\n", + "Disk is full writing '%s' (Errcode: %d). Waiting for someone to free space... (Expect up to %d secs delay for server to continue after freeing disk space)", + "Can't create directory '%s' (Errcode: %d)", + "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)", + "Can't read value for symlink '%s' (Error %d)", + "Can't create symlink '%s' pointing at '%s' (Error %d)", + "Error on realpath() on '%s' (Error %d)", + "Can't sync file '%s' to disk (Errcode: %d)", + "Collation '%s' is not a compiled collation and is not specified in the '%s' file", + "File '%s' not found (Errcode: %d)", + "File '%s' (fileno: %d) was not closed", + "Can't change mode for file '%s' to 0x%lx (Error: %d)" +}; + +void init_glob_errs(void) +{ + /* This is now done statically. */ +} + +#else + +void init_glob_errs() +{ + EE(EE_CANTCREATEFILE) = "Can't create/write to file '%s' (Errcode: %d)"; + EE(EE_READ) = "Error reading file '%s' (Errcode: %d)"; + EE(EE_WRITE) = "Error writing file '%s' (Errcode: %d)"; + EE(EE_BADCLOSE) = "Error on close of '%'s (Errcode: %d)"; + EE(EE_OUTOFMEMORY) = "Out of memory (Needed %u bytes)"; + EE(EE_DELETE) = "Error on delete of '%s' (Errcode: %d)"; + EE(EE_LINK) = "Error on rename of '%s' to '%s' (Errcode: %d)"; + EE(EE_EOFERR) = "Unexpected eof found when reading file '%s' (Errcode: %d)"; + EE(EE_CANTLOCK) = "Can't lock file (Errcode: %d)"; + EE(EE_CANTUNLOCK) = "Can't unlock file (Errcode: %d)"; + EE(EE_DIR) = "Can't read dir of '%s' (Errcode: %d)"; + EE(EE_STAT) = "Can't get stat of '%s' (Errcode: %d)"; + EE(EE_CANT_CHSIZE) = "Can't change size of file (Errcode: %d)"; + EE(EE_CANT_OPEN_STREAM)= "Can't open stream from handle (Errcode: %d)"; + EE(EE_GETWD) = "Can't get working directory (Errcode: %d)"; + EE(EE_SETWD) = "Can't change dir to '%s' (Errcode: %d)"; + EE(EE_LINK_WARNING) = "Warning: '%s' had %d links"; + EE(EE_OPEN_WARNING) = "Warning: %d files and %d streams is left open\n"; + EE(EE_DISK_FULL) = "Disk is full writing '%s'. Waiting for someone to free space..."; + EE(EE_CANT_MKDIR) ="Can't create directory '%s' (Errcode: %d)"; + EE(EE_UNKNOWN_CHARSET)= "Character set '%s' is not a compiled character set and is not specified in the %s file"; + EE(EE_OUT_OF_FILERESOURCES)="Out of resources when opening file '%s' (Errcode: %d)"; + EE(EE_CANT_READLINK)= "Can't read value for symlink '%s' (Error %d)"; + EE(EE_CANT_SYMLINK)= "Can't create symlink '%s' pointing at '%s' (Error %d)"; + EE(EE_REALPATH)= "Error on realpath() on '%s' (Error %d)"; + EE(EE_SYNC)= "Can't sync file '%s' to disk (Errcode: %d)"; + EE(EE_UNKNOWN_COLLATION)= "Collation '%s' is not a compiled collation and is not specified in the %s file"; + EE(EE_FILENOTFOUND) = "File '%s' not found (Errcode: %d)"; + EE(EE_FILE_NOT_CLOSED) = "File '%s' (fileno: %d) was not closed"; + EE(EE_CANT_CHMOD) = "Can't change mode for file '%s' to 0x%lx (Error: %d)"; +} +#endif + +void wait_for_free_space(const char *filename, int errors) +{ + if (errors == 0) + my_error(EE_DISK_FULL,MYF(ME_BELL | ME_NOREFRESH), + filename,my_errno,MY_WAIT_FOR_USER_TO_FIX_PANIC); + if (!(errors % MY_WAIT_GIVE_USER_A_MESSAGE)) + my_printf_error(EE_DISK_FULL, + "Retry in %d secs. Message reprinted in %d secs", + MYF(ME_BELL | ME_NOREFRESH), + MY_WAIT_FOR_USER_TO_FIX_PANIC, + MY_WAIT_GIVE_USER_A_MESSAGE * MY_WAIT_FOR_USER_TO_FIX_PANIC ); + (void) sleep(MY_WAIT_FOR_USER_TO_FIX_PANIC); +} diff --git a/externals/mysql/mysys/hash.c b/externals/mysql/mysys/hash.c new file mode 100644 index 00000000000..a521cd84e91 --- /dev/null +++ b/externals/mysql/mysys/hash.c @@ -0,0 +1,763 @@ +/* Copyright 2000-2008 MySQL AB, 2008 Sun Microsystems, Inc. + + 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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(size_t 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 uint calc_hash(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 nr1; +} + +my_bool +_my_hash_init(HASH *hash, uint growth_size, CHARSET_INFO *charset, + ulong size, size_t key_offset, size_t key_length, + my_hash_get_key get_key, + void (*free_element)(void*), uint flags CALLER_INFO_PROTO) +{ + DBUG_ENTER("my_hash_init"); + DBUG_PRINT("enter",("hash: %p size: %u", hash, (uint) size)); + + hash->records=0; + if (my_init_dynamic_array_ci(&hash->array, sizeof(HASH_LINK), size, + growth_size)) + { + hash->free=0; /* Allow call to my_hash_free */ + DBUG_RETURN(1); + } + 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; + DBUG_RETURN(0); +} + + +/* + 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; +} + + +/* + 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: %p", hash)); + + my_hash_free_elements(hash); + hash->free= 0; + delete_dynamic(&hash->array); + DBUG_VOID_RETURN; +} + + +/* + Delete all elements from the hash (the hash itself is to be reused). + + SYNOPSIS + my_hash_reset() + hash the hash to delete elements of +*/ + +void my_hash_reset(HASH *hash) +{ + DBUG_ENTER("my_hash_reset"); + DBUG_PRINT("enter",("hash: %pd", 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(size_t 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); +} + + + +/* for compilers which can not handle inline */ +static +#if !defined(__USLC__) && !defined(__sgi) +inline +#endif +unsigned int 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); +} + +/* + 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) +{ + HASH_LINK *pos; + uint flag,idx; + DBUG_ENTER("my_hash_first"); + + flag=1; + if (hash->records) + { + idx= my_hash_mask(calc_hash(hash, key, length ? length : hash->key_length), + 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 + + @return + @retval 0 ok + @retval 1 Duplicate key or out of memory +*/ + +my_bool my_hash_insert(HASH *info, const uchar *record) +{ + int flag; + size_t idx,halfbuff,hash_nr,first_index; + uchar *ptr_to_rec,*ptr_to_rec2; + HASH_LINK *data,*empty,*gpos,*gpos2,*pos; + + LINT_INIT(gpos); + LINT_INIT(gpos2); + LINT_INIT(ptr_to_rec); + LINT_INIT(ptr_to_rec2); + + if (info->flags & HASH_UNIQUE) + { + 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. + + @fn hash_delete() + @param hash Hash tree + @param record Row to be deleted + + @notes + The record with the same record ptr is removed. + If there is a free-function it's called if record was found. + + @return + @retval 0 ok + @retval 1 Record not found +*/ + +my_bool my_hash_delete(HASH *hash, uchar *record) +{ + uint blength,pos2,pos_hashnr,lastpos_hashnr,idx,empty_index; + 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,blength,records; + size_t 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,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=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; +} + + +/** + Iterate over all elements in hash and call function with the element + + @param hash hash array + @param action function to call for each argument + @param argument second argument for call to action + + @notes + If one of functions calls returns 1 then the iteration aborts + + @retval 0 ok + @retval 1 iteration aborted becasue action returned 1 +*/ + +my_bool my_hash_iterate(HASH *hash, my_hash_walk_action action, void *argument) +{ + uint records, i; + HASH_LINK *data; + + records= hash->records; + data= dynamic_element(&hash->array,0,HASH_LINK*); + + for (i= 0 ; i < records ; i++) + { + if ((*action)(data[i].data, argument)) + return 1; + } + return 0; +} + + +#ifndef DBUG_OFF + +my_bool my_hash_check(HASH *hash) +{ + int error; + uint i,rec_link,found,max_links,seek,links,idx; + uint records,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/externals/mysql/mysys/lf_alloc-pin.c b/externals/mysql/mysys/lf_alloc-pin.c new file mode 100644 index 00000000000..fda9b97791d --- /dev/null +++ b/externals/mysql/mysys/lf_alloc-pin.c @@ -0,0 +1,534 @@ +/* QQ: TODO multi-pinbox */ +/* Copyright (C) 2006-2008 MySQL AB, 2008-2009 Sun Microsystems, Inc. + + 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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. + + It is assumed that pins belong to a THD and are not transferable + between THD's (LF_PINS::stack_ends_here being a primary reason + for this limitation). +*/ +#include <my_global.h> +#include <my_sys.h> +#include <lf.h> + +#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. Usually called via lf_alloc_get_pins() or + lf_hash_get_pins(). + + SYNOPSYS + pinbox - + + DESCRIPTION + get a new LF_PINS structure from a stack of unused pins, + or allocate a new one out of dynarray. + + NOTE + It is assumed that pins belong to a thread and are not transferable + between threads. +*/ +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). Versioniong 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; + el->stack_ends_here= & my_thread_var->stack_ends_here; + return el; +} + +/* + Put pins back to a pinbox. Usually called via lf_alloc_put_pins() or + lf_hash_put_pins(). + + 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; +#ifdef MY_LF_EXTRA_DEBUG + { + int i; + for (i= 0; i < LF_PINBOX_PINS; i++) + DBUG_ASSERT(pins->pin[i] == 0); + } +#endif + /* + 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_atomic_rwlock_wrunlock(&pins->pinbox->pinarray.lock); + pthread_yield(); + my_atomic_rwlock_wrlock(&pins->pinbox->pinarray.lock); + } + } + 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)); + return; +} + +static int ptr_cmp(void **a, void **b) +{ + return *a < *b ? -1 : *a == *b ? 0 : 1; +} + +#define add_to_purgatory(PINS, ADDR) \ + do \ + { \ + *(void **)((char *)(ADDR)+(PINS)->pinbox->free_ptr_offset)= \ + (PINS)->purgatory; \ + (PINS)->purgatory= (ADDR); \ + (PINS)->purgatory_count++; \ + } while (0) + +/* + 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) + _lf_pinbox_real_free(pins); +} + +struct st_harvester { + void **granary; + int npins; +}; + +/* + callback for _lf_dynarray_iterate: + scan all pins of all threads and accumulate all pins +*/ +static int harvest_pins(LF_PINS *el, struct st_harvester *hv) +{ + int i; + LF_PINS *el_end= el+min(hv->npins, LF_DYNARRAY_LEVEL_LENGTH); + for (; el < el_end; el++) + { + for (i= 0; i < LF_PINBOX_PINS; i++) + { + void *p= el->pin[i]; + if (p) + *hv->granary++= p; + } + } + /* + hv->npins may become negative below, but it means that + we're on the last dynarray page and harvest_pins() won't be + called again. We don't bother to make hv->npins() correct + (that is 0) in this case. + */ + hv->npins-= LF_DYNARRAY_LEVEL_LENGTH; + return 0; +} + +/* + callback for _lf_dynarray_iterate: + scan all pins of all threads and see if addr is present there +*/ +static int match_pins(LF_PINS *el, void *addr) +{ + int i; + LF_PINS *el_end= el+LF_DYNARRAY_LEVEL_LENGTH; + for (; el < el_end; el++) + for (i= 0; i < LF_PINBOX_PINS; i++) + if (el->pin[i] == addr) + return 1; + return 0; +} + +#if STACK_DIRECTION < 0 +#define available_stack_size(CUR,END) (long) ((char*)(CUR) - (char*)(END)) +#else +#define available_stack_size(CUR,END) (long) ((char*)(END) - (char*)(CUR)) +#endif + +#define next_node(P, X) (*((uchar * volatile *)(((uchar *)(X)) + (P)->free_ptr_offset))) +#define anext_node(X) next_node(&allocator->pinbox, (X)) + +/* + Scan the purgatory and free everything that can be freed +*/ +static void _lf_pinbox_real_free(LF_PINS *pins) +{ + int npins, alloca_size; + void *list, **addr; + void *first, *last= NULL; + LF_PINBOX *pinbox= pins->pinbox; + + LINT_INIT(first); + npins= pinbox->pins_in_array+1; + +#ifdef HAVE_ALLOCA + alloca_size= sizeof(void *)*LF_PINBOX_PINS*npins; + /* create a sorted list of pinned addresses, to speed up searches */ + if (available_stack_size(&pinbox, *pins->stack_ends_here) > alloca_size) + { + struct st_harvester hv; + addr= (void **) alloca(alloca_size); + hv.granary= addr; + hv.npins= npins; + /* scan the dynarray and accumulate all pinned addresses */ + _lf_dynarray_iterate(&pinbox->pinarray, + (lf_dynarray_func)harvest_pins, &hv); + + npins= hv.granary-addr; + /* and sort them */ + if (npins) + qsort(addr, npins, sizeof(void *), (qsort_cmp)ptr_cmp); + } + else +#endif + addr= 0; + + list= pins->purgatory; + pins->purgatory= 0; + pins->purgatory_count= 0; + while (list) + { + void *cur= list; + list= *(void **)((char *)cur+pinbox->free_ptr_offset); + if (npins) + { + if (addr) /* use binary search */ + { + void **a, **b, **c; + for (a= addr, b= addr+npins-1, c= a+(b-a)/2; (b-a) > 1; c= a+(b-a)/2) + if (cur == *c) + a= b= c; + else if (cur > *c) + a= c; + else + b= c; + if (cur == *a || cur == *b) + goto found; + } + else /* no alloca - no cookie. linear search here */ + { + if (_lf_dynarray_iterate(&pinbox->pinarray, + (lf_dynarray_func)match_pins, cur)) + goto found; + } + } + /* not pinned - freeing */ + if (last) + last= next_node(pinbox, last)= (uchar *)cur; + else + first= last= (uchar *)cur; + continue; +found: + /* pinned - keeping */ + add_to_purgatory(pins, cur); + } + if (last) + pinbox->free_func(first, last, pinbox->free_func_arg); +} + +/* 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 + + SYNOPSYS + allocator - + size a size of an object to allocate + 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. +*/ +void lf_alloc_init(LF_ALLOCATOR *allocator, uint size, uint free_ptr_offset) +{ + 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= 0; + allocator->destructor= 0; + 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((void *)node, MYF(0)); + 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(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/externals/mysql/mysys/lf_dynarray.c b/externals/mysql/mysys/lf_dynarray.c new file mode 100644 index 00000000000..b1cdce698a9 --- /dev/null +++ b/externals/mysql/mysys/lf_dynarray.c @@ -0,0 +1,207 @@ +/* Copyright (C) 2006 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 <lf.h> + +void lf_dynarray_init(LF_DYNARRAY *array, uint element_size) +{ + bzero(array, sizeof(*array)); + array->size_of_element= element_size; + my_atomic_rwlock_init(&array->lock); +} + +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((void *)alloc, MYF(0)); + } + else + my_free(alloc[-1], MYF(0)); +} + +void lf_dynarray_destroy(LF_DYNARRAY *array) +{ + int i; + for (i= 0; i < LF_DYNARRAY_LEVELS; i++) + recursive_free(array->level[i], i); + my_atomic_rwlock_destroy(&array->lock); +} + +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(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, MYF(0)); + } + 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(LF_DYNARRAY_LEVEL_LENGTH * array->size_of_element + + 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, MYF(0)); + } + 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/externals/mysql/mysys/lf_hash.c b/externals/mysql/mysys/lf_hash.c new file mode 100644 index 00000000000..f478196c7c8 --- /dev/null +++ b/externals/mysql/mysys/lf_hash.c @@ -0,0 +1,505 @@ +/* Copyright (C) 2006-2008 MySQL AB, 2008-2009 Sun Microsystems, Inc. + + 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 <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 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 lfind(LF_SLIST * volatile *head, CHARSET_INFO *cs, uint32 hashnr, + const uchar *key, uint keylen, CURSOR *cursor, LF_PINS *pins) +{ + uint32 cur_hashnr; + const uchar *cur_key; + uint 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_alloc_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 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 (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 (!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_alloc_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) + */ + 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 *lsearch(LF_SLIST * volatile *head, CHARSET_INFO *cs, + uint32 hashnr, const uchar *key, uint keylen, + LF_PINS *pins) +{ + CURSOR cursor; + int res= 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, uint keylen) +{ + ulong nr1= 1, nr2= 4; + hash->charset->coll->hash_sort(hash->charset, (uchar*) key, keylen, + &nr1, &nr2); + return nr1 & 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 *); + +/* + 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. +*/ +void lf_hash_init(LF_HASH *hash, uint element_size, uint flags, + uint key_offset, uint key_length, my_hash_get_key get_key, + CHARSET_INFO *charset) +{ + lf_alloc_init(&hash->alloc, sizeof(LF_SLIST)+element_size, + offsetof(LF_SLIST, key)); + 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; + 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((void *)el, MYF(0)); /* 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; + + lf_rwlock_by_pins(pins); + node= (LF_SLIST *)_lf_alloc_new(pins); + if (unlikely(!node)) + return -1; + 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_alloc_free(pins, node); + lf_rwunlock_by_pins(pins); + 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); + lf_rwunlock_by_pins(pins); + 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; + lf_rwlock_by_pins(pins); + 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)) + { + lf_rwunlock_by_pins(pins); + return 1; + } + my_atomic_add32(&hash->count, -1); + lf_rwunlock_by_pins(pins); + return 0; +} + +/* + RETURN + 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) + NULL if nothing is found + MY_ERRPTR if OOM + + NOTE + see lsearch() for pin usage notes +*/ +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; + lf_rwlock_by_pins(pins); + 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= lsearch(el, hash->charset, my_reverse_bits(hashnr) | 1, + (uchar *)key, keylen, pins); + lf_rwunlock_by_pins(pins); + return found ? found+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(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((void *)dummy, MYF(0)); + 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/externals/mysql/mysys/list.c b/externals/mysql/mysys/list.c new file mode 100644 index 00000000000..75678af7a66 --- /dev/null +++ b/externals/mysql/mysys/list.c @@ -0,0 +1,114 @@ +/* Copyright (C) 2000 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +/* + Code for handling dubble-linked lists in C +*/ + +#include "mysys_priv.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: %p element: %p", root, 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((uchar*) root->data,MYF(0)); + my_free((uchar*) root,MYF(0)); + root=next; + } +} + + +LIST *list_cons(void *data, LIST *list) +{ + LIST *new_charset=(LIST*) my_malloc(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/externals/mysql/mysys/make-conf.c b/externals/mysql/mysys/make-conf.c new file mode 100644 index 00000000000..0dacde4dee0 --- /dev/null +++ b/externals/mysql/mysys/make-conf.c @@ -0,0 +1,71 @@ +/* Copyright (C) 2000 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +/* make-conf.c + * make a charset .conf file out of a ctype-charset.c file. + */ + +#ifndef CHARSET +#error You must define the charset, e.g.: -DCHARSET=latin1 +#endif + +/* some pre-processor tricks to get us going */ +#define _STRINGIZE_HELPER(x) #x +#define STRINGIZE(x) _STRINGIZE_HELPER(x) + +#define _JOIN_WORDS_HELPER(a, b) a ## b +#define JOIN_WORDS(a, b) _JOIN_WORDS_HELPER(a, b) + +#define CH_SRC ctype- ## CHARSET ## .c +#define CH_INCLUDE STRINGIZE(CH_SRC) + +/* aaaah, that's better */ +#include <my_my_global.h> +#include CH_INCLUDE + +#include <stdio.h> +#include <stdlib.h> + +#define ROW_LEN 16 + +void print_array(const char *name, const uchar *array, uint size); + +int main(void) +{ + printf("# Configuration file for the " + STRINGIZE(CHARSET) + " character set.\n"); + + print_array("ctype", JOIN_WORDS(ctype_, CHARSET), 257); + print_array("to_lower", JOIN_WORDS(to_lower_, CHARSET), 256); + print_array("to_upper", JOIN_WORDS(to_upper_, CHARSET), 256); + print_array("sort_order", JOIN_WORDS(sort_order_, CHARSET), 256); + + exit(EXIT_SUCCESS); +} + +void print_array(const char *name, const uchar *array, uint size) +{ + uint i; + + printf("\n# The %s array must have %d elements.\n", name, size); + + for (i = 0; i < size; ++i) { + printf(" %02X", array[i]); + + if ((i+1) % ROW_LEN == size % ROW_LEN) + printf("\n"); + } +} diff --git a/externals/mysql/mysys/md5.c b/externals/mysql/mysys/md5.c new file mode 100644 index 00000000000..2388cebedc4 --- /dev/null +++ b/externals/mysql/mysys/md5.c @@ -0,0 +1,325 @@ +/* Copyright (C) 2000 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +/* + * This code implements the MD5 message-digest algorithm. + * The algorithm is due to Ron Rivest. This code was + * written by Colin Plumb in 1993, no copyright is claimed. + * This code is in the public domain; do with it what you wish. + * + * Equivalent code is available from RSA Data Security, Inc. + * This code has been tested against that, and is equivalent, + * except that you don't need to include two pages of legalese + * with every copy. + * + * To compute the message digest of a chunk of bytes, declare an + * MD5Context structure, pass it to MD5Init, call MD5Update as + * needed on buffers full of bytes, and then call MD5Final, which + * will fill a supplied 16-byte array with the digest. + */ + +/* This code was modified in 1997 by Jim Kingdon of Cyclic Software to + not require an integer type which is exactly 32 bits. This work + draws on the changes for the same purpose by Tatu Ylonen + <ylo@cs.hut.fi> as part of SSH, but since I didn't actually use + that code, there is no copyright issue. I hereby disclaim + copyright in any changes I have made; this code remains in the + public domain. */ + +#include <my_global.h> +#include <m_string.h> +#include "my_md5.h" + +#include <string.h> /* for memcpy() and memset() */ + + +static void +my_MD5Transform (cvs_uint32 buf[4], const unsigned char in[64]); + +/* Little-endian byte-swapping routines. Note that these do not + depend on the size of datatypes such as uint32, nor do they require + us to detect the endianness of the machine we are running on. It + is possible they should be macros for speed, but I would be + surprised if they were a performance bottleneck for MD5. */ + +static uint32 getu32 (const unsigned char *addr) +{ + return (((((unsigned long)addr[3] << 8) | addr[2]) << 8) + | addr[1]) << 8 | addr[0]; +} + +static void +putu32 (uint32 data, unsigned char *addr) +{ + addr[0] = (unsigned char)data; + addr[1] = (unsigned char)(data >> 8); + addr[2] = (unsigned char)(data >> 16); + addr[3] = (unsigned char)(data >> 24); +} + +/* + Start MD5 accumulation. Set bit count to 0 and buffer to mysterious + initialization constants. +*/ +void +my_MD5Init (my_MD5Context *ctx) +{ + ctx->buf[0] = 0x67452301; + ctx->buf[1] = 0xefcdab89; + ctx->buf[2] = 0x98badcfe; + ctx->buf[3] = 0x10325476; + + ctx->bits[0] = 0; + ctx->bits[1] = 0; +} + +/* + Update context to reflect the concatenation of another buffer full + of bytes. +*/ +void +my_MD5Update (my_MD5Context *ctx, unsigned char const *buf, unsigned len) +{ + uint32 t; + + /* Update bitcount */ + + t = ctx->bits[0]; + if ((ctx->bits[0] = (t + ((uint32)len << 3)) & 0xffffffff) < t) + ctx->bits[1]++; /* Carry from low to high */ + ctx->bits[1] += len >> 29; + + t = (t >> 3) & 0x3f; /* Bytes already in shsInfo->data */ + + /* Handle any leading odd-sized chunks */ + + if ( t ) { + unsigned char *p = ctx->in + t; + + t = 64-t; + if (len < t) { + memcpy(p, buf, len); + return; + } + memcpy(p, buf, t); + my_MD5Transform (ctx->buf, ctx->in); + buf += t; + len -= t; + } + + /* Process data in 64-byte chunks */ + + while (len >= 64) { + memcpy(ctx->in, buf, 64); + my_MD5Transform (ctx->buf, ctx->in); + buf += 64; + len -= 64; + } + + /* Handle any remaining bytes of data. */ + + memcpy(ctx->in, buf, len); +} + +/* + Final wrapup - pad to 64-byte boundary with the bit pattern + 1 0* (64-bit count of bits processed, MSB-first) +*/ +void +my_MD5Final (unsigned char digest[16], my_MD5Context *ctx) +{ + unsigned count; + unsigned char *p; + + /* Compute number of bytes mod 64 */ + count = (ctx->bits[0] >> 3) & 0x3F; + + /* Set the first char of padding to 0x80. This is safe since there is + always at least one byte free */ + p = ctx->in + count; + *p++ = 0x80; + + /* Bytes of padding needed to make 64 bytes */ + count = 64 - 1 - count; + + /* Pad out to 56 mod 64 */ + if (count < 8) { + /* Two lots of padding: Pad the first block to 64 bytes */ + memset(p, 0, count); + my_MD5Transform (ctx->buf, ctx->in); + + /* Now fill the next block with 56 bytes */ + memset(ctx->in, 0, 56); + } else { + /* Pad block to 56 bytes */ + memset(p, 0, count-8); + } + + /* Append length in bits and transform */ + putu32(ctx->bits[0], ctx->in + 56); + putu32(ctx->bits[1], ctx->in + 60); + + my_MD5Transform (ctx->buf, ctx->in); + putu32(ctx->buf[0], digest); + putu32(ctx->buf[1], digest + 4); + putu32(ctx->buf[2], digest + 8); + putu32(ctx->buf[3], digest + 12); + memset(ctx, 0, sizeof(ctx)); /* In case it's sensitive */ +} + +#ifndef ASM_MD5 + +/* The four core functions - F1 is optimized somewhat */ + +/* #define F1(x, y, z) (x & y | ~x & z) */ +#define F1(x, y, z) (z ^ (x & (y ^ z))) +#define F2(x, y, z) F1(z, x, y) +#define F3(x, y, z) (x ^ y ^ z) +#define F4(x, y, z) (y ^ (x | ~z)) + +/* This is the central step in the MD5 algorithm. */ +#define MD5STEP(f, w, x, y, z, data, s) \ + ( w += f(x, y, z) + data, w &= 0xffffffff, w = w<<s | w>>(32-s), w += x ) + +/* + * The core of the MD5 algorithm, this alters an existing MD5 hash to + * reflect the addition of 16 longwords of new data. MD5Update blocks + * the data and converts bytes into longwords for this routine. + */ +static void +my_MD5Transform (uint32 buf[4], const unsigned char inraw[64]) +{ + register uint32 a, b, c, d; + uint32 in[16]; + int i; + + for (i = 0; i < 16; ++i) + in[i] = getu32 (inraw + 4 * i); + + a = buf[0]; + b = buf[1]; + c = buf[2]; + d = buf[3]; + + MD5STEP(F1, a, b, c, d, in[ 0]+0xd76aa478, 7); + MD5STEP(F1, d, a, b, c, in[ 1]+0xe8c7b756, 12); + MD5STEP(F1, c, d, a, b, in[ 2]+0x242070db, 17); + MD5STEP(F1, b, c, d, a, in[ 3]+0xc1bdceee, 22); + MD5STEP(F1, a, b, c, d, in[ 4]+0xf57c0faf, 7); + MD5STEP(F1, d, a, b, c, in[ 5]+0x4787c62a, 12); + MD5STEP(F1, c, d, a, b, in[ 6]+0xa8304613, 17); + MD5STEP(F1, b, c, d, a, in[ 7]+0xfd469501, 22); + MD5STEP(F1, a, b, c, d, in[ 8]+0x698098d8, 7); + MD5STEP(F1, d, a, b, c, in[ 9]+0x8b44f7af, 12); + MD5STEP(F1, c, d, a, b, in[10]+0xffff5bb1, 17); + MD5STEP(F1, b, c, d, a, in[11]+0x895cd7be, 22); + MD5STEP(F1, a, b, c, d, in[12]+0x6b901122, 7); + MD5STEP(F1, d, a, b, c, in[13]+0xfd987193, 12); + MD5STEP(F1, c, d, a, b, in[14]+0xa679438e, 17); + MD5STEP(F1, b, c, d, a, in[15]+0x49b40821, 22); + + MD5STEP(F2, a, b, c, d, in[ 1]+0xf61e2562, 5); + MD5STEP(F2, d, a, b, c, in[ 6]+0xc040b340, 9); + MD5STEP(F2, c, d, a, b, in[11]+0x265e5a51, 14); + MD5STEP(F2, b, c, d, a, in[ 0]+0xe9b6c7aa, 20); + MD5STEP(F2, a, b, c, d, in[ 5]+0xd62f105d, 5); + MD5STEP(F2, d, a, b, c, in[10]+0x02441453, 9); + MD5STEP(F2, c, d, a, b, in[15]+0xd8a1e681, 14); + MD5STEP(F2, b, c, d, a, in[ 4]+0xe7d3fbc8, 20); + MD5STEP(F2, a, b, c, d, in[ 9]+0x21e1cde6, 5); + MD5STEP(F2, d, a, b, c, in[14]+0xc33707d6, 9); + MD5STEP(F2, c, d, a, b, in[ 3]+0xf4d50d87, 14); + MD5STEP(F2, b, c, d, a, in[ 8]+0x455a14ed, 20); + MD5STEP(F2, a, b, c, d, in[13]+0xa9e3e905, 5); + MD5STEP(F2, d, a, b, c, in[ 2]+0xfcefa3f8, 9); + MD5STEP(F2, c, d, a, b, in[ 7]+0x676f02d9, 14); + MD5STEP(F2, b, c, d, a, in[12]+0x8d2a4c8a, 20); + + MD5STEP(F3, a, b, c, d, in[ 5]+0xfffa3942, 4); + MD5STEP(F3, d, a, b, c, in[ 8]+0x8771f681, 11); + MD5STEP(F3, c, d, a, b, in[11]+0x6d9d6122, 16); + MD5STEP(F3, b, c, d, a, in[14]+0xfde5380c, 23); + MD5STEP(F3, a, b, c, d, in[ 1]+0xa4beea44, 4); + MD5STEP(F3, d, a, b, c, in[ 4]+0x4bdecfa9, 11); + MD5STEP(F3, c, d, a, b, in[ 7]+0xf6bb4b60, 16); + MD5STEP(F3, b, c, d, a, in[10]+0xbebfbc70, 23); + MD5STEP(F3, a, b, c, d, in[13]+0x289b7ec6, 4); + MD5STEP(F3, d, a, b, c, in[ 0]+0xeaa127fa, 11); + MD5STEP(F3, c, d, a, b, in[ 3]+0xd4ef3085, 16); + MD5STEP(F3, b, c, d, a, in[ 6]+0x04881d05, 23); + MD5STEP(F3, a, b, c, d, in[ 9]+0xd9d4d039, 4); + MD5STEP(F3, d, a, b, c, in[12]+0xe6db99e5, 11); + MD5STEP(F3, c, d, a, b, in[15]+0x1fa27cf8, 16); + MD5STEP(F3, b, c, d, a, in[ 2]+0xc4ac5665, 23); + + MD5STEP(F4, a, b, c, d, in[ 0]+0xf4292244, 6); + MD5STEP(F4, d, a, b, c, in[ 7]+0x432aff97, 10); + MD5STEP(F4, c, d, a, b, in[14]+0xab9423a7, 15); + MD5STEP(F4, b, c, d, a, in[ 5]+0xfc93a039, 21); + MD5STEP(F4, a, b, c, d, in[12]+0x655b59c3, 6); + MD5STEP(F4, d, a, b, c, in[ 3]+0x8f0ccc92, 10); + MD5STEP(F4, c, d, a, b, in[10]+0xffeff47d, 15); + MD5STEP(F4, b, c, d, a, in[ 1]+0x85845dd1, 21); + MD5STEP(F4, a, b, c, d, in[ 8]+0x6fa87e4f, 6); + MD5STEP(F4, d, a, b, c, in[15]+0xfe2ce6e0, 10); + MD5STEP(F4, c, d, a, b, in[ 6]+0xa3014314, 15); + MD5STEP(F4, b, c, d, a, in[13]+0x4e0811a1, 21); + MD5STEP(F4, a, b, c, d, in[ 4]+0xf7537e82, 6); + MD5STEP(F4, d, a, b, c, in[11]+0xbd3af235, 10); + MD5STEP(F4, c, d, a, b, in[ 2]+0x2ad7d2bb, 15); + MD5STEP(F4, b, c, d, a, in[ 9]+0xeb86d391, 21); + + buf[0] += a; + buf[1] += b; + buf[2] += c; + buf[3] += d; +} +#endif + +#ifdef TEST +/* + Simple test program. Can use it to manually run the tests from + RFC1321 for example. +*/ +#include <stdio.h> + +int +main (int argc, char **argv) +{ + my_MD5Context context; + unsigned char checksum[16]; + int i; + int j; + + if (argc < 2) + { + fprintf (stderr, "usage: %s string-to-hash\n", argv[0]); + exit (1); + } + for (j = 1; j < argc; ++j) + { + printf ("MD5 (\"%s\") = ", argv[j]); + my_MD5Init (&context); + my_MD5Update (&context, argv[j], strlen (argv[j])); + my_MD5Final (checksum, &context); + for (i = 0; i < 16; i++) + { + printf ("%02x", (unsigned int) checksum[i]); + } + printf ("\n"); + } + return 0; +} +#endif /* TEST */ diff --git a/externals/mysql/mysys/mf_arr_appstr.c b/externals/mysql/mysys/mf_arr_appstr.c new file mode 100644 index 00000000000..1edbea9df4a --- /dev/null +++ b/externals/mysql/mysys/mf_arr_appstr.c @@ -0,0 +1,61 @@ +/* Copyright (C) 2007 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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/externals/mysql/mysys/mf_brkhant.c b/externals/mysql/mysys/mf_brkhant.c new file mode 100644 index 00000000000..3573b9973b2 --- /dev/null +++ b/externals/mysql/mysys/mf_brkhant.c @@ -0,0 +1,72 @@ +/* Copyright (C) 2000 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +/* Dont let the user break when you are doing something important */ +/* Remembers if it got 'SIGINT' and executes it on allow_break */ +/* A static buffer is used; don't call dont_break() twice in a row */ + +#include "mysys_priv.h" +#include "my_static.h" + + /* Set variable that we can't break */ + +#if !defined(THREAD) +void dont_break(void) +{ + my_dont_interrupt=1; + return; +} /* dont_break */ + +void allow_break(void) +{ + { + reg1 int index; + + my_dont_interrupt=0; + if (_my_signals) + { + if (_my_signals > MAX_SIGNALS) + _my_signals=MAX_SIGNALS; + for (index=0 ; index < _my_signals ; index++) + { + if (_my_sig_remember[index].func) /* Safequard */ + { + (*_my_sig_remember[index].func)(_my_sig_remember[index].number); + _my_sig_remember[index].func=0; + } + } + _my_signals=0; + } + } +} /* dont_break */ +#endif + + /* Set old status */ + +#if !defined(THREAD) +void my_remember_signal(int signal_number, sig_handler (*func) (int)) +{ +#ifndef __WIN__ + reg1 int index; + + index=_my_signals++; /* Nobody can break a ++ ? */ + if (index < MAX_SIGNALS) + { + _my_sig_remember[index].number=signal_number; + _my_sig_remember[index].func=func; + } +#endif /* __WIN__ */ +} /* my_remember_signal */ +#endif /* THREAD */ diff --git a/externals/mysql/mysys/mf_cache.c b/externals/mysql/mysys/mf_cache.c new file mode 100644 index 00000000000..f0df0f3fa77 --- /dev/null +++ b/externals/mysql/mysys/mf_cache.c @@ -0,0 +1,121 @@ +/* Copyright (C) 2000 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +/* Open a temporary file and cache it with io_cache. Delete it on close */ + +#include "mysys_priv.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; If the operating system doesn't support + this, just remember the file name for later removal + */ + +static my_bool cache_remove_open_tmp(IO_CACHE *cache __attribute__((unused)), + const char *name) +{ +#if O_TEMPORARY == 0 +#if !defined(CANT_DELETE_OPEN_FILES) + /* The following should always succeed */ + (void) my_delete(name,MYF(MY_WME | ME_NOINPUT)); +#else + int length; + if (!(cache->file_name= + (char*) my_malloc((length=strlen(name)+1),MYF(MY_WME)))) + { + my_close(cache->file,MYF(0)); + cache->file = -1; + errno=my_errno=ENOMEM; + return 1; + } + memcpy(cache->file_name,name,length); +#endif +#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(dir,MYF(cache_myflags & MY_WME)) : (char*) 0; + cache->prefix= (prefix ? my_strdup(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, MYF(MY_ALLOW_ZERO_PTR)); + my_free(cache->prefix,MYF(MY_ALLOW_ZERO_PTR)); + 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=create_temp_file(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) my_close(file,MYF(0)); +#ifdef CANT_DELETE_OPEN_FILES + if (cache->file_name) + { + (void) my_delete(cache->file_name,MYF(MY_WME | ME_NOINPUT)); + my_free(cache->file_name,MYF(0)); + } +#endif + } + my_free(cache->dir,MYF(MY_ALLOW_ZERO_PTR)); + my_free(cache->prefix,MYF(MY_ALLOW_ZERO_PTR)); + } + DBUG_VOID_RETURN; +} diff --git a/externals/mysql/mysys/mf_dirname.c b/externals/mysql/mysys/mf_dirname.c new file mode 100644 index 00000000000..1b428ded751 --- /dev/null +++ b/externals/mysql/mysys/mf_dirname.c @@ -0,0 +1,165 @@ +/* Copyright (C) 2000 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#include "mysys_priv.h" +#include <m_string.h> + + /* Functions definied in this file */ + +size_t dirname_length(const char *name) +{ + register char *pos, *gpos; +#ifdef BASKSLASH_MBTAIL + 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 BASKSLASH_MBTAIL + uint l; + if (use_mb(fs) && (l= my_ismbchar(fs, pos, pos + 3))) + { + pos+= l - 1; + continue; + } +#endif + if (*pos == FN_LIBCHAR || *pos == '/' +#ifdef FN_C_AFTER_DIR + || *pos == FN_C_AFTER_DIR || *pos == FN_C_AFTER_DIR_2 +#endif + ) + gpos=pos; + } + return (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 MSDOS converts '/' to '\' + If VMS converts '<' to '[' and '>' 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 BACKSLASH_MBTAIL + 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 != '/' || defined(FN_C_BEFORE_DIR_2) + { + for (; from != from_end && *from ; from++) + { + if (*from == '/') + *to++= FN_LIBCHAR; +#ifdef FN_C_BEFORE_DIR_2 + else if (*from == FN_C_BEFORE_DIR_2) + *to++= FN_C_BEFORE_DIR; + else if (*from == FN_C_AFTER_DIR_2) + *to++= FN_C_AFTER_DIR; +#endif + else + { +#ifdef BACKSLASH_MBTAIL + uint l; + if (use_mb(fs) && (l= my_ismbchar(fs, from, from + 3))) + { + memmove(to, from, l); + to+= l; + from+= l - 1; + to_org= to; /* Don't look inside mbchar */ + } + else +#endif + { + *to++= *from; + } + } + } + *to=0; + } +#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/externals/mysql/mysys/mf_fn_ext.c b/externals/mysql/mysys/mf_fn_ext.c new file mode 100644 index 00000000000..da7fac3de73 --- /dev/null +++ b/externals/mysql/mysys/mf_fn_ext.c @@ -0,0 +1,54 @@ +/* Copyright (C) 2000 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 first 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) +{ + register const char *pos, *gpos; + DBUG_ENTER("fn_ext"); + DBUG_PRINT("mfunkt",("name: '%s'",name)); + +#if defined(FN_DEVCHAR) || defined(FN_C_AFTER_DIR) || defined(BASKSLASH_MBTAIL) + { + 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=strchr(gpos,FN_EXTCHAR); + DBUG_RETURN((char*) (pos ? pos : strend(gpos))); +} /* fn_ext */ diff --git a/externals/mysql/mysys/mf_format.c b/externals/mysql/mysys/mf_format.c new file mode 100644 index 00000000000..f199132626b --- /dev/null +++ b/externals/mysql/mysys/mf_format.c @@ -0,0 +1,142 @@ +/* Copyright (C) 2000 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#include "mysys_priv.h" +#include <m_string.h> + +/* + Formats a filename with possible replace of directory of extension + Function can handle the case where 'to' == 'name' + For a description of the flag values, consult my_sys.h + The arguments should be in unix format. +*/ + +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; + reg1 size_t length; + size_t dev_length; + DBUG_ENTER("fn_format"); + 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)) + { + /* Use given directory */ + convert_dirname(dev,dir,NullS); /* Fix to this OS */ + } + else if ((flag & MY_RELATIVE_PATH) && !test_if_hard_path(dev)) + { + /* 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) + return NullS; + tmp_length= strlength(startpos); + DBUG_PRINT("error",("dev: '%s' ext: '%s' length: %u",dev,ext, + (uint) length)); + (void) strmake(to,startpos,min(tmp_length,FN_REFLEN-1)); + } + else + { + if (to == startpos) + { + bmove(buff,(uchar*) name,length); /* Save name for last copy */ + name=buff; + } + pos=strmake(strmov(to,dev),name,length); + (void) strmov(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) + { + strmov(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) +{ + reg1 const char * pos; + reg2 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/externals/mysql/mysys/mf_getdate.c b/externals/mysql/mysys/mf_getdate.c new file mode 100644 index 00000000000..3a8e1be6a0b --- /dev/null +++ b/externals/mysql/mysys/mf_getdate.c @@ -0,0 +1,81 @@ +/* Copyright (C) 2000 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +/* Get date in a printable form: yyyy-mm-dd hh:mm:ss */ + +#include "mysys_priv.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(register char * to, int flag, time_t date) +{ + reg2 struct tm *start_time; + time_t skr; +#if defined(HAVE_LOCALTIME_R) && defined(_REENTRANT) + struct tm tm_tmp; +#endif + + skr=date ? (time_t) date : my_time(0); +#if defined(HAVE_LOCALTIME_R) && defined(_REENTRANT) + if (flag & GETDATE_GMT) + localtime_r(&skr,&tm_tmp); + else + gmtime_r(&skr,&tm_tmp); + start_time= &tm_tmp; +#else + if (flag & GETDATE_GMT) + start_time= localtime(&skr); + else + start_time= gmtime(&skr); +#endif + 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/externals/mysql/mysys/mf_iocache.c b/externals/mysql/mysys/mf_iocache.c new file mode 100644 index 00000000000..120d886ce06 --- /dev/null +++ b/externals/mysql/mysys/mf_iocache.c @@ -0,0 +1,1967 @@ +/* Copyright (C) 2000 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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. +*/ + +#define MAP_TO_USE_RAID +#include "mysys_priv.h" +#include <m_string.h> +#ifdef HAVE_AIOWAIT +#include "mysys_err.h" +static void my_aiowait(my_aio_result *result); +#endif +#include <errno.h> + +#ifdef THREAD +#define lock_append_buffer(info) \ + pthread_mutex_lock(&(info)->append_buffer_lock) +#define unlock_append_buffer(info) \ + pthread_mutex_unlock(&(info)->append_buffer_lock) +#else +#define lock_append_buffer(info) +#define unlock_append_buffer(info) +#endif + +#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 = +#ifdef THREAD + info->share ? _my_b_read_r : +#endif + _my_b_read; + info->write_function = _my_b_write; + } + + setup_io_cache(info); +} + + +/* FUNCTIONS TO SET UP OR RESET A CACHE */ + + +/* + Initialize an IO_CACHE object + + SYNOPSOS + init_io_cache() + info cache handler to initialize + file File that should be associated to to the handler + If == -1 then real_open_cached_file() + will be called when it's time to open file. + cachesize Size of buffer to allocate for read/write + If == 0 then use my_default_record_cache_size + type Type of cache + seek_offset Where cache should start reading/writing + use_async_io Set to 1 of we should use async_io (if avaiable) + cache_myflags Bitmap of differnt flags + MY_WME | MY_FAE | MY_NABP | MY_FNABP | + MY_DONT_CHECK_FILESIZE + + RETURN + 0 ok + # error +*/ + +int init_io_cache(IO_CACHE *info, File file, size_t cachesize, + enum cache_type type, my_off_t seek_offset, + pbool use_async_io, myf cache_myflags) +{ + size_t min_cache; + my_off_t pos; + my_off_t end_of_file= ~(my_off_t) 0; + DBUG_ENTER("init_io_cache"); + DBUG_PRINT("enter",("cache: %p type: %d pos: %ld", + info, (int) type, (ulong) seek_offset)); + + info->file= file; + 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= info->post_write= NULL; + info->arg = 0; + info->alloced_buffer = 0; + info->buffer=0; + info->seek_not_done= 0; + + if (file >= 0) + { + pos= my_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= test(seek_offset != pos); + } + + info->disk_writes= 0; +#ifdef THREAD + info->share=0; +#endif + + 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=my_seek(file,0L,MY_SEEK_END,MYF(0)); + /* Need to reset seek_not_done now that we just did a seek. */ + info->seek_not_done= end_of_file == seek_offset ? 0 : 1; + if (end_of_file < seek_offset) + end_of_file=seek_offset; + /* Trim cache size if the file is very small */ + 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; + if (cachesize < min_cache) + cachesize = min_cache; + buffer_block= cachesize; + if (type == SEQ_READ_APPEND) + buffer_block *= 2; + if ((info->buffer= + (uchar*) my_malloc(buffer_block, + MYF((cache_myflags & ~ MY_WME) | + (cachesize == min_cache ? MY_WME : 0)))) != 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; +#ifdef THREAD + pthread_mutex_init(&info->append_buffer_lock,MY_MUTEX_INIT_FAST); +#endif + } +#if defined(SAFE_MUTEX) && defined(THREAD) + else + { + /* Clear mutex so that safe_mutex will notice that it's not initialized */ + bzero((char*) &info->append_buffer_lock, sizeof(info)); + } +#endif + + if (type == WRITE_CACHE) + info->write_end= + 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= info->hard_write_error_in_the_past= 0; + info->type= type; + init_functions(info); +#ifdef HAVE_AIOWAIT + if (use_async_io && ! my_disable_async_io) + { + DBUG_PRINT("info",("Using async io")); + info->read_length/=2; + info->read_function=_my_b_async_read; + } + info->inited=info->aio_result.pending=0; +#endif + DBUG_RETURN(0); +} /* init_io_cache */ + + /* Wait until current request is ready */ + +#ifdef HAVE_AIOWAIT +static void my_aiowait(my_aio_result *result) +{ + if (result->pending) + { + struct aio_result_t *tmp; + for (;;) + { + if ((int) (tmp=aiowait((struct timeval *) 0)) == -1) + { + if (errno == EINTR) + continue; + DBUG_PRINT("error",("No aio request, error: %d",errno)); + result->pending=0; /* Assume everythings is ok */ + break; + } + ((my_aio_result*) tmp)->pending=0; + if ((my_aio_result*) tmp == result) + break; + } + } + return; +} +#endif + + +/* + 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 __attribute__((unused)), + pbool clear_cache) +{ + DBUG_ENTER("reinit_io_cache"); + DBUG_PRINT("enter",("cache: %p type: %d seek_offset: %lu clear_cache: %d", + 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; +#ifdef HAVE_AIOWAIT + my_aiowait(&info->aio_result); /* Wait for outstanding req */ +#endif + } + 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= info->hard_write_error_in_the_past= 0; + init_functions(info); + +#ifdef HAVE_AIOWAIT + if (use_async_io && ! my_disable_async_io && + ((ulong) info->buffer_length < + (ulong) (info->end_of_file - seek_offset))) + { + info->read_length=info->buffer_length/2; + info->read_function=_my_b_async_read; + } + info->inited=0; +#endif + DBUG_RETURN(0); +} /* reinit_io_cache */ + + +/* FUNCTIONS TO DO READS FROM THE 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: can't read requested characters +*/ + +int _my_b_read(register 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 ((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 ((my_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); + } + } + + diff_length= (size_t) (pos_in_file & (IO_SIZE-1)); + 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 */ + info->error= (int) left_length; + DBUG_RETURN(1); + } + length=(Count & (size_t) ~(IO_SIZE-1))-diff_length; + if ((read_length= my_read(info->file,Buffer, length, info->myflags)) + != length) + { + 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; + } + + max_length= info->read_length-diff_length; + 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 (!max_length) + { + if (Count) + { + info->error= (int) left_length; /* We only got this many char */ + DBUG_RETURN(1); + } + length=0; /* Didn't read any chars */ + } + else if ((length= my_read(info->file,info->buffer, max_length, + info->myflags)) < Count || + length == (size_t) -1) + { + if (length != (size_t) -1) + memcpy(Buffer, info->buffer, length); + info->pos_in_file= pos_in_file; + info->error= length == (size_t) -1 ? -1 : (int) (length+left_length); + info->read_pos=info->read_end=info->buffer; + DBUG_RETURN(1); + } + 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); +} + + +#ifdef THREAD +/* + 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: %p share: %p " + "write_cache: %p threads: %u", + read_cache, cshare, + write_cache, num_threads)); + + DBUG_ASSERT(num_threads > 1); + DBUG_ASSERT(read_cache->type == READ_CACHE); + DBUG_ASSERT(!write_cache || (write_cache->type == WRITE_CACHE)); + + pthread_mutex_init(&cshare->mutex, MY_MUTEX_INIT_FAST); + pthread_cond_init(&cshare->cond, 0); + pthread_cond_init(&cshare->cond_writer, 0); + + cshare->running_threads= num_threads; + cshare->total_threads= num_threads; + cshare->error= 0; /* Initialize. */ + cshare->buffer= read_cache->buffer; + cshare->read_end= NULL; /* See function comment of lock_io_cache(). */ + cshare->pos_in_file= 0; /* See function comment of lock_io_cache(). */ + cshare->source_cache= write_cache; /* Can be NULL. */ + + read_cache->share= cshare; + read_cache->read_function= _my_b_read_r; + read_cache->current_pos= NULL; + read_cache->current_end= NULL; + + if (write_cache) + write_cache->share= cshare; + + DBUG_VOID_RETURN; +} + + +/* + Remove a thread from shared access to IO_CACHE. + + 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); + + pthread_mutex_lock(&cshare->mutex); + DBUG_PRINT("io_cache_share", ("%s: %p", + (cache == cshare->source_cache) ? + "writer" : "reader", cache)); + + /* Remove from share. */ + total= --cshare->total_threads; + DBUG_PRINT("io_cache_share", ("remaining threads: %u", total)); + + /* Detach from share. */ + cache->share= NULL; + + /* If the writer goes, let the readers know. */ + if (cache == cshare->source_cache) + { + DBUG_PRINT("io_cache_share", ("writer leaves")); + cshare->source_cache= NULL; + } + + /* If all threads are waiting for me to join the lock, wake them. */ + if (!--cshare->running_threads) + { + DBUG_PRINT("io_cache_share", ("the last running thread leaves, wake all")); + pthread_cond_signal(&cshare->cond_writer); + pthread_cond_broadcast(&cshare->cond); + } + + pthread_mutex_unlock(&cshare->mutex); + + if (!total) + { + DBUG_PRINT("io_cache_share", ("last thread removed, destroy share")); + pthread_cond_destroy (&cshare->cond_writer); + pthread_cond_destroy (&cshare->cond); + pthread_mutex_destroy(&cshare->mutex); + } + + DBUG_VOID_RETURN; +} + + +/* + Lock IO cache and wait for all other threads to join. + + SYNOPSIS + lock_io_cache() + cache The cache of the thread entering the lock. + pos File position of the block to read. + Unused for the write thread. + + DESCRIPTION + + Wait for all threads to finish with the current buffer. We want + all threads to proceed in concert. The last thread to join + lock_io_cache() will read the block from file and all threads start + to use it. Then they will join again for reading the next block. + + The waiting threads detect a fresh buffer by comparing + cshare->pos_in_file with the position they want to process next. + Since the first block may start at position 0, we take + cshare->read_end as an additional condition. This variable is + initialized to NULL and will be set after a block of data is written + to the buffer. + + RETURN + 1 OK, lock in place, go ahead and read. + 0 OK, unlocked, another thread did the read. +*/ + +static int lock_io_cache(IO_CACHE *cache, my_off_t pos) +{ + IO_CACHE_SHARE *cshare= cache->share; + DBUG_ENTER("lock_io_cache"); + + /* Enter the lock. */ + pthread_mutex_lock(&cshare->mutex); + cshare->running_threads--; + DBUG_PRINT("io_cache_share", ("%s: %p pos: %lu running: %u", + (cache == cshare->source_cache) ? + "writer" : "reader", 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")); + pthread_cond_wait(&cshare->cond_writer, &cshare->mutex); + } + DBUG_PRINT("io_cache_share", ("writer awoke, going to copy")); + + /* Stay locked. Leave the lock later by unlock_io_cache(). */ + DBUG_RETURN(1); + } + + /* The last thread wakes the writer. */ + if (!cshare->running_threads) + { + DBUG_PRINT("io_cache_share", ("waking writer")); + pthread_cond_signal(&cshare->cond_writer); + } + + /* + Readers wait until the data is copied from the writer. Another + reason to stop waiting is the removal of the write thread. If this + happens, we leave the lock with old data in the buffer. + */ + while ((!cshare->read_end || (cshare->pos_in_file < pos)) && + cshare->source_cache) + { + DBUG_PRINT("io_cache_share", ("reader waits in lock")); + pthread_cond_wait(&cshare->cond, &cshare->mutex); + } + + /* + If the writer was removed from the share while this thread was + asleep, we need to simulate an EOF condition. The writer cannot + reset the share variables as they might still be in use by readers + of the last block. When we awake here then because the last + joining thread signalled us. If the writer is not the last, it + will not signal. So it is safe to clear the buffer here. + */ + if (!cshare->read_end || (cshare->pos_in_file < pos)) + { + DBUG_PRINT("io_cache_share", ("reader found writer removed. EOF")); + cshare->read_end= cshare->buffer; /* Empty buffer. */ + cshare->error= 0; /* EOF is not an error. */ + } + } + else + { + /* + There are read caches only. The last thread arriving in + lock_io_cache() continues with a locked cache and reads the block. + */ + if (!cshare->running_threads) + { + DBUG_PRINT("io_cache_share", ("last thread joined, going to read")); + /* Stay locked. Leave the lock later by unlock_io_cache(). */ + DBUG_RETURN(1); + } + + /* + All other threads wait until the requested block is read by the + last thread arriving. Another reason to stop waiting is the + removal of a thread. If this leads to all threads being in the + lock, we have to continue also. The first of the awaken threads + will then do the read. + */ + while ((!cshare->read_end || (cshare->pos_in_file < pos)) && + cshare->running_threads) + { + DBUG_PRINT("io_cache_share", ("reader waits in lock")); + pthread_cond_wait(&cshare->cond, &cshare->mutex); + } + + /* If 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. + */ + pthread_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: %p pos: %lu running: %u", + (cache == cshare->source_cache) ? + "writer" : "reader", + cache, (ulong) cshare->pos_in_file, + cshare->total_threads)); + + cshare->running_threads= cshare->total_threads; + pthread_cond_broadcast(&cshare->cond); + pthread_mutex_unlock(&cshare->mutex); + DBUG_VOID_RETURN; +} + + +/* + Read from IO_CACHE when it is shared between several threads. + + SYNOPSIS + _my_b_read_r() + cache IO_CACHE pointer + Buffer Buffer to retrieve count bytes from file + Count Number of bytes to read into Buffer + + NOTE + This function is only called from the my_b_read() macro when there + isn't enough characters in the buffer to satisfy the request. + + IMPLEMENTATION + + It works as follows: when a thread tries to read from a file (that + is, after using all the data from the (shared) buffer), it just + hangs on lock_io_cache(), waiting for other threads. When the very + last thread attempts a read, lock_io_cache() returns 1, the thread + does actual IO and unlock_io_cache(), which signals all the waiting + threads that data is in the buffer. + + WARNING + + When changing this function, be careful with handling file offsets + (end-of_file, pos_in_file). Do not cast them to possibly smaller + types than my_off_t unless you can be sure that their value fits. + Same applies to differences of file offsets. (Bug #11527) + + When changing this function, check _my_b_read(). It might need the + same change. + + RETURN + 0 we succeeded in reading all data + 1 Error: can't read requested characters +*/ + +int _my_b_read_r(register IO_CACHE *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 (my_seek(cache->file, pos_in_file, MY_SEEK_SET, MYF(0)) + == MY_FILEPOS_ERROR) + { + cache->error= -1; + unlock_io_cache(cache); + DBUG_RETURN(1); + } + } + len= my_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= min(write_length, write_cache->buffer_length); + int __attribute__((unused)) rc; + + rc= lock_io_cache(write_cache, write_cache->pos_in_file); + /* The writing thread does always have the lock when it awakes. */ + DBUG_ASSERT(rc); + + memcpy(cshare->buffer, write_buffer, copy_length); + + cshare->error= 0; + cshare->read_end= cshare->buffer + copy_length; + cshare->pos_in_file= write_cache->pos_in_file; + + /* Mark all threads as running and wake them. */ + unlock_io_cache(write_cache); + + write_buffer+= copy_length; + write_length-= copy_length; + } +} +#endif /*THREAD*/ + + +/* + Do sequential read from the SEQ_READ_APPEND cache. + + We do this in three stages: + - first read from info->buffer + - then if there are still data to read, try the file descriptor + - afterwards, if there are still data to read, try append buffer + + RETURNS + 0 Success + 1 Failed to read +*/ + +int _my_b_seq_read(register IO_CACHE *info, 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 (my_seek(info->file,pos_in_file,MY_SEEK_SET,MYF(0)) == MY_FILEPOS_ERROR) + { + info->error= -1; + unlock_append_buffer(info); + return (1); + } + info->seek_not_done=0; + + diff_length= (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= my_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= my_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=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; +} + + +#ifdef HAVE_AIOWAIT + +/* + Read from the IO_CACHE into a buffer and feed asynchronously + from disk when needed. + + SYNOPSIS + _my_b_async_read() + info IO_CACHE pointer + Buffer Buffer to retrieve count bytes from file + Count Number of bytes to read into Buffer + + RETURN VALUE + -1 An error has occurred; my_errno is set. + 0 Success + 1 An error has occurred; IO_CACHE to error state. +*/ + +int _my_b_async_read(register IO_CACHE *info, uchar *Buffer, size_t Count) +{ + size_t length,read_length,diff_length,left_length,use_length,org_Count; + size_t max_length; + my_off_t next_pos_in_file; + uchar *read_buffer; + + memcpy(Buffer,info->read_pos, + (left_length= (size_t) (info->read_end-info->read_pos))); + Buffer+=left_length; + org_Count=Count; + Count-=left_length; + + if (info->inited) + { /* wait for read block */ + info->inited=0; /* No more block to read */ + my_aiowait(&info->aio_result); /* Wait for outstanding req */ + if (info->aio_result.result.aio_errno) + { + if (info->myflags & MY_WME) + my_error(EE_READ, MYF(ME_BELL+ME_WAITTANG), + my_filename(info->file), + info->aio_result.result.aio_errno); + my_errno=info->aio_result.result.aio_errno; + info->error= -1; + return(1); + } + if (! (read_length= (size_t) info->aio_result.result.aio_return) || + read_length == (size_t) -1) + { + my_errno=0; /* For testing */ + info->error= (read_length == (size_t) -1 ? -1 : + (int) (read_length+left_length)); + return(1); + } + info->pos_in_file+= (size_t) (info->read_end - info->request_pos); + + if (info->request_pos != info->buffer) + info->request_pos=info->buffer; + else + info->request_pos=info->buffer+info->read_length; + info->read_pos=info->request_pos; + next_pos_in_file=info->aio_read_pos+read_length; + + /* Check if pos_in_file is changed + (_ni_read_cache may have skipped some bytes) */ + + if (info->aio_read_pos < info->pos_in_file) + { /* Fix if skipped bytes */ + if (info->aio_read_pos + read_length < info->pos_in_file) + { + read_length=0; /* Skip block */ + next_pos_in_file=info->pos_in_file; + } + else + { + my_off_t offset= (info->pos_in_file - info->aio_read_pos); + info->pos_in_file=info->aio_read_pos; /* Whe are here */ + info->read_pos=info->request_pos+offset; + read_length-=offset; /* Bytes left from read_pos */ + } + } +#ifndef DBUG_OFF + if (info->aio_read_pos > info->pos_in_file) + { + my_errno=EINVAL; + return(info->read_length= (size_t) -1); + } +#endif + /* Copy found bytes to buffer */ + length=min(Count,read_length); + memcpy(Buffer,info->read_pos,(size_t) length); + Buffer+=length; + Count-=length; + left_length+=length; + info->read_end=info->rc_pos+read_length; + info->read_pos+=length; + } + else + next_pos_in_file=(info->pos_in_file+ (size_t) + (info->read_end - info->request_pos)); + + /* If reading large blocks, or first read or read with skip */ + if (Count) + { + if (next_pos_in_file == info->end_of_file) + { + info->error=(int) (read_length+left_length); + return 1; + } + + if (my_seek(info->file,next_pos_in_file,MY_SEEK_SET,MYF(0)) + == MY_FILEPOS_ERROR) + { + info->error= -1; + return (1); + } + + read_length=IO_SIZE*2- (size_t) (next_pos_in_file & (IO_SIZE-1)); + if (Count < read_length) + { /* Small block, read to cache */ + if ((read_length=my_read(info->file,info->request_pos, + read_length, info->myflags)) == (size_t) -1) + return info->error= -1; + use_length=min(Count,read_length); + memcpy(Buffer,info->request_pos,(size_t) use_length); + info->read_pos=info->request_pos+Count; + info->read_end=info->request_pos+read_length; + info->pos_in_file=next_pos_in_file; /* Start of block in cache */ + next_pos_in_file+=read_length; + + if (Count != use_length) + { /* Didn't find hole block */ + if (info->myflags & (MY_WME | MY_FAE | MY_FNABP) && Count != org_Count) + my_error(EE_EOFERR, MYF(ME_BELL+ME_WAITTANG), + my_filename(info->file),my_errno); + info->error=(int) (read_length+left_length); + return 1; + } + } + else + { /* Big block, don't cache it */ + if ((read_length= my_read(info->file,Buffer, Count,info->myflags)) + != Count) + { + info->error= read_length == (size_t) -1 ? -1 : read_length+left_length; + return 1; + } + info->read_pos=info->read_end=info->request_pos; + info->pos_in_file=(next_pos_in_file+=Count); + } + } + + /* Read next block with asyncronic io */ + diff_length=(next_pos_in_file & (IO_SIZE-1)); + max_length= info->read_length - diff_length; + if (max_length > info->end_of_file - next_pos_in_file) + max_length= (size_t) (info->end_of_file - next_pos_in_file); + + if (info->request_pos != info->buffer) + read_buffer=info->buffer; + else + read_buffer=info->buffer+info->read_length; + info->aio_read_pos=next_pos_in_file; + if (max_length) + { + info->aio_result.result.aio_errno=AIO_INPROGRESS; /* Marker for test */ + DBUG_PRINT("aioread",("filepos: %ld length: %lu", + (ulong) next_pos_in_file, (ulong) max_length)); + if (aioread(info->file,read_buffer, max_length, + (my_off_t) next_pos_in_file,MY_SEEK_SET, + &info->aio_result.result)) + { /* Skip async io */ + my_errno=errno; + DBUG_PRINT("error",("got error: %d, aio_result: %d from aioread, async skipped", + errno, info->aio_result.result.aio_errno)); + if (info->request_pos != info->buffer) + { + bmove(info->buffer,info->request_pos, + (size_t) (info->read_end - info->read_pos)); + info->request_pos=info->buffer; + info->read_pos-=info->read_length; + info->read_end-=info->read_length; + } + info->read_length=info->buffer_length; /* Use hole buffer */ + info->read_function=_my_b_read; /* Use normal IO_READ next */ + } + else + info->inited=info->aio_result.pending=1; + } + return 0; /* Block read, async in use */ +} /* _my_b_async_read */ +#endif + + +/* 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, NULL, 0, 0); + if ((*(info)->read_function)(info,&buff,1)) + return my_b_EOF; + if ((post_read = info->post_read)) + (*post_read)(info, NULL, 0, 0); + return (int) (uchar) buff; +} + +/* FUNCTIONS TO DO WRITES TO THE CACHE */ + +#define set_hard_write_error(CACHE) \ + ((CACHE)->error= (CACHE)->hard_write_error_in_the_past= -1) + +/* + Write a byte buffer to IO_CACHE and flush to disk + if IO_CACHE is full. + + RETURN VALUE + 1 On error on write + 0 On success + -1 On error; my_errno contains error code. +*/ + +int _my_b_write(register IO_CACHE *info, const uchar *Buffer, size_t Count) +{ + size_t rest_length,length; + + if (info->pos_in_file+info->buffer_length > info->end_of_file) + { + my_errno=errno=EFBIG; + return set_hard_write_error(info); + } + + 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 (my_seek(info->file,info->pos_in_file,MY_SEEK_SET,MYF(0))) + { + set_hard_write_error(info); + return (1); + } + info->seek_not_done=0; + } + if (my_write(info->file, Buffer, length, info->myflags | MY_NABP)) + return set_hard_write_error(info); + if (info->post_write) + (*(info->post_write))(info, Buffer, length, info->pos_in_file); + +#ifdef THREAD + /* + In case of a shared I/O cache with a writer we normally do direct + write cache to read cache copy. Simulate this here by direct + caller buffer to read cache copy. Do it after the write so that + the cache readers actions on the flushed part can go in parallel + with the write of the extra stuff. copy_to_read_buffer() + synchronizes writer and readers so that after this call the + readers can act on the extra stuff while the writer can go ahead + and prepare the next output. copy_to_read_buffer() relies on + info->pos_in_file. + */ + if (info->share) + copy_to_read_buffer(info, Buffer, length); +#endif + + Count-=length; + Buffer+=length; + info->pos_in_file+=length; + } + 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(register IO_CACHE *info, const uchar *Buffer, size_t Count) +{ + size_t rest_length,length; + +#ifdef THREAD + /* + Assert that we cannot come here with a shared cache. If we do one + day, we might need to add a call to copy_to_read_buffer(). + */ + DBUG_ASSERT(!info->share); +#endif + DBUG_ASSERT(info->post_write == NULL); /* unsupported */ + 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 (my_write(info->file,Buffer, length, info->myflags | MY_NABP)) + { + unlock_append_buffer(info); + return set_hard_write_error(info); + } + 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(register IO_CACHE *info, const uchar *Buffer, size_t Count, + my_off_t pos) +{ + size_t length; + int error=0; + +#ifdef THREAD + /* + Assert that we cannot come here with a shared cache. If we do one + day, we might need to add a call to copy_to_read_buffer(). + */ + DBUG_ASSERT(!info->share); +#endif + + if (pos < info->pos_in_file) + { + /* Of no overlap, write everything without buffering */ + if (pos + Count <= info->pos_in_file) + { + int ret= my_pwrite(info->file, Buffer, Count, pos, + info->myflags | MY_NABP); + if (unlikely(ret)) + set_hard_write_error(info); + if (info->post_write) + (*(info->post_write))(info, Buffer, Count, pos); + return ret; + } + /* Write the part of the block that is before buffer */ + length= (uint) (info->pos_in_file - pos); + if (my_pwrite(info->file, Buffer, length, pos, info->myflags | MY_NABP)) + error= set_hard_write_error(info); + if (info->post_write) + (*(info->post_write))(info, Buffer, length, pos); + Buffer+=length; + pos+= length; + Count-= length; +#ifndef HAVE_PREAD + 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 */ + +#ifdef THREAD +#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); +#else +#define LOCK_APPEND_BUFFER +#define UNLOCK_APPEND_BUFFER +#endif + + +int my_b_flush_io_cache(IO_CACHE *info, int need_append_buffer_lock) +{ + size_t length; + my_bool append_cache; + my_off_t pos_in_file; + DBUG_ENTER("my_b_flush_io_cache"); + DBUG_PRINT("enter", ("cache: 0x%lx", (long) info)); + + if (!(append_cache = (info->type == SEQ_READ_APPEND))) + need_append_buffer_lock=0; + + if (info->type == WRITE_CACHE || append_cache) + { + if (info->file == -1) + { + if (real_open_cached_file(info)) + DBUG_RETURN(set_hard_write_error(info)); + } + LOCK_APPEND_BUFFER; + + if ((length=(size_t) (info->write_pos - info->write_buffer))) + { +#ifdef THREAD + /* + In case of a shared I/O cache with a writer we do direct write + cache to read cache copy. Do it before the write here so that + the readers can work in parallel with the write. + copy_to_read_buffer() relies on info->pos_in_file. + */ + if (info->share) + copy_to_read_buffer(info, info->write_buffer, length); +#endif + + pos_in_file=info->pos_in_file; + /* + If we have append cache, we always open the file with + O_APPEND which moves the pos to EOF automatically on every write + */ + if (!append_cache && info->seek_not_done) + { /* File touched, do seek */ + if (my_seek(info->file,pos_in_file,MY_SEEK_SET,MYF(0)) == + MY_FILEPOS_ERROR) + { + UNLOCK_APPEND_BUFFER; + DBUG_RETURN(set_hard_write_error(info)); + } + if (!append_cache) + info->seek_not_done=0; + } + info->write_end= (info->write_buffer+info->buffer_length- + ((pos_in_file+length) & (IO_SIZE-1))); + + if (my_write(info->file,info->write_buffer,length, + info->myflags | MY_NABP)) + set_hard_write_error(info); + else + info->error= 0; + if (!append_cache) + { + /* + This post_write is really POST-write; callers depend on this! So + always call it after writing to the file, not before. + */ + if (info->post_write) + (*(info->post_write))(info, info->write_buffer, + length, info->pos_in_file); + /* + The addition below will make the info->pos_in_file be the end of + written block; whereas the value we needed in post_write is the + value before the addition. That's why we called post_write before + this. + */ + info->pos_in_file+=length; + 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 == my_tell(info->file,MYF(0))); + DBUG_ASSERT(info->post_write == NULL); /* unsupported */ + } + + info->append_read_pos=info->write_pos=info->write_buffer; + ++info->disk_writes; + UNLOCK_APPEND_BUFFER; + DBUG_RETURN(info->error); + } + } +#ifdef HAVE_AIOWAIT + else if (info->type != READ_NET) + { + my_aiowait(&info->aio_result); /* Wait for outstanding req */ + info->inited=0; + } +#endif + 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: %p", info)); + +#ifdef THREAD + /* + Every thread must call remove_io_thread(). The last one destroys + the share elements. + */ + DBUG_ASSERT(!info->share || !info->share->total_threads); +#endif + + if ((pre_close=info->pre_close)) + { + (*pre_close)(info, NULL, 0, 0); + 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((uchar*) info->buffer,MYF(MY_WME)); + info->buffer=info->read_pos=(uchar*) 0; + } + if (info->type == SEQ_READ_APPEND) + { + /* Destroy allocated mutex */ + info->type= TYPE_NOT_SET; +#ifdef THREAD + pthread_mutex_destroy(&info->append_buffer_lock); +#endif + } +#ifdef THREAD + info->share= 0; +#endif + 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(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,MYF(MY_WME)); + 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/externals/mysql/mysys/mf_iocache2.c b/externals/mysql/mysys/mf_iocache2.c new file mode 100644 index 00000000000..38415f3909c --- /dev/null +++ b/externals/mysql/mysys/mf_iocache2.c @@ -0,0 +1,466 @@ +/* Copyright (C) 2000 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +/* + More functions to be used with IO_CACHE files +*/ + +#define MAP_TO_USE_RAID +#include "mysys_priv.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))); + DBUG_RETURN(0); +} + + +my_off_t my_b_append_tell(IO_CACHE* info) +{ + /* + 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. + */ +#ifdef THREAD + pthread_mutex_lock(&info->append_buffer_lock); +#endif +#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); +#ifdef THREAD + pthread_mutex_unlock(&info->append_buffer_lock); +#endif + 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 */ + } + 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. Supports '%s', '%d', '%u', "%ld" and "%lu" + Used for logging in MySQL + returns number of written character, or (size_t) -1 on error +*/ + +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; +} + + +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 == '*') + { + precision= (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 */ + { + reg2 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 == '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 */ + { + register int iarg; + size_t length2; + char buff[17]; + + 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) + { + uchar *buffz; + + buffz= (uchar*) my_alloca(minimum_width - length2); + if (is_zero_padded) + memset(buffz, '0', minimum_width - length2); + else + memset(buffz, ' ', minimum_width - length2); + my_b_write(info, buffz, minimum_width - length2); + my_afree(buffz); + } + + 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 */ + { + register long iarg; + size_t length2; + char buff[17]; + + 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 + { + /* %% 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/externals/mysql/mysys/mf_keycache.c b/externals/mysql/mysys/mf_keycache.c new file mode 100644 index 00000000000..aca47f564cf --- /dev/null +++ b/externals/mysql/mysys/mf_keycache.c @@ -0,0 +1,4580 @@ +/* Copyright (C) 2000 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 + pthread_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 + pthread_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> + +/* + Some compilation flags have been added specifically for this module + to control the following: + - not to let a thread to yield the control when reading directly + from key cache, which might improve performance in many cases; + to enable this add: + #define SERIALIZED_READ_FROM_CACHE + - to set an upper bound for number of threads simultaneously + using the key cache; this setting helps to determine an optimal + size for hash table and improve performance when the number of + blocks in the key cache much less than the number of threads + accessing it; + to set this number equal to <N> add + #define MAX_THREADS <N> + - to substitute calls of pthread_cond_wait for calls of + pthread_cond_timedwait (wait with timeout set up); + this setting should be used only when you want to trap a deadlock + situation, which theoretically should not happen; + to set timeout equal to <T> seconds add + #define KEYCACHE_TIMEOUT <T> + - to enable the module traps and to send debug information from + key cache module to a special debug log add: + #define KEYCACHE_DEBUG + the name of this debug log file <LOG NAME> can be set through: + #define KEYCACHE_DEBUG_LOG <LOG NAME> + if the name is not defined, it's set by default; + if the KEYCACHE_DEBUG flag is not set up and we are in a debug + mode, i.e. when ! defined(DBUG_OFF), the debug information from the + module is sent to the regular debug log. + + Example of the settings: + #define SERIALIZED_READ_FROM_CACHE + #define MAX_THREADS 100 + #define KEYCACHE_TIMEOUT 1 + #define KEYCACHE_DEBUG + #define KEYCACHE_DEBUG_LOG "my_key_cache_debug.log" +*/ + +#define STRUCT_PTR(TYPE, MEMBER, a) \ + (TYPE *) ((char *) (a) - offsetof(TYPE, MEMBER)) + +/* types of condition variables */ +#define COND_FOR_REQUESTED 0 +#define COND_FOR_SAVED 1 +#define COND_FOR_READERS 2 + +typedef pthread_cond_t KEYCACHE_CONDVAR; + +/* descriptor of the page in the key cache block buffer */ +struct st_keycache_page +{ + int file; /* file to which the page belongs to */ + my_off_t filepos; /* position of the page in the file */ +}; + +/* element in the chain of a hash table bucket */ +struct st_hash_link +{ + struct st_hash_link *next, **prev; /* to connect links in the same bucket */ + struct st_block_link *block; /* reference to the block for the page: */ + File file; /* from such a file */ + my_off_t diskpos; /* with such an offset */ + uint requests; /* number of requests for the page */ +}; + +/* simple states of a block */ +#define BLOCK_ERROR 1 /* an error occured when performing 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 */ + void *post_write_arg; /**< post_write's argument*/ +}; + +KEY_CACHE dflt_key_cache_var; +KEY_CACHE *dflt_key_cache= &dflt_key_cache_var; + +#define FLUSH_CACHE 2000 /* sort this many blocks at once */ + +static int flush_all_key_blocks(KEY_CACHE *keycache); +#ifdef THREAD +static void wait_on_queue(KEYCACHE_WQUEUE *wqueue, + pthread_mutex_t *mutex); +static void release_whole_queue(KEYCACHE_WQUEUE *wqueue); +#else +#define wait_on_queue(wqueue, mutex) do {} while (0) +#define release_whole_queue(wqueue) do {} while (0) +#endif +static void free_block(KEY_CACHE *keycache, BLOCK_LINK *block); +#if !defined(DBUG_OFF) +static void test_key_cache(KEY_CACHE *keycache, + const char *where, my_bool lock); +#endif + +#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 DEFAULT_KEYCACHE_DEBUG_LOG "keycache_debug.log" + +#if defined(KEYCACHE_DEBUG) && ! defined(KEYCACHE_DEBUG_LOG) +#define KEYCACHE_DEBUG_LOG DEFAULT_KEYCACHE_DEBUG_LOG +#endif + +#if defined(KEYCACHE_DEBUG_LOG) +static FILE *keycache_debug_log=NULL; +static void keycache_debug_print _VARARGS((const char *fmt,...)); +#define KEYCACHE_DEBUG_OPEN \ + if (!keycache_debug_log) \ + { \ + keycache_debug_log= fopen(KEYCACHE_DEBUG_LOG, "w"); \ + (void) setvbuf(keycache_debug_log, NULL, _IOLBF, BUFSIZ); \ + } + +#define KEYCACHE_DEBUG_CLOSE \ + if (keycache_debug_log) \ + { \ + fclose(keycache_debug_log); \ + keycache_debug_log= 0; \ + } +#else +#define KEYCACHE_DEBUG_OPEN +#define KEYCACHE_DEBUG_CLOSE +#endif /* defined(KEYCACHE_DEBUG_LOG) */ + +#if defined(KEYCACHE_DEBUG_LOG) && defined(KEYCACHE_DEBUG) +#define KEYCACHE_DBUG_PRINT(l, m) \ + { if (keycache_debug_log) fprintf(keycache_debug_log, "%s: ", l); \ + keycache_debug_print m; } + +#define KEYCACHE_DBUG_ASSERT(a) \ + { if (! (a) && keycache_debug_log) fclose(keycache_debug_log); \ + assert(a); } +#else +#define KEYCACHE_DBUG_PRINT(l, m) DBUG_PRINT(l, m) +#define KEYCACHE_DBUG_ASSERT(a) DBUG_ASSERT(a) +#endif /* defined(KEYCACHE_DEBUG_LOG) && defined(KEYCACHE_DEBUG) */ + +#if defined(KEYCACHE_DEBUG) || !defined(DBUG_OFF) +#ifdef THREAD +static long keycache_thread_id; +#define KEYCACHE_THREAD_TRACE(l) \ + KEYCACHE_DBUG_PRINT(l,("|thread %ld",keycache_thread_id)) + +#define KEYCACHE_THREAD_TRACE_BEGIN(l) \ + { struct st_my_thread_var *thread_var= my_thread_var; \ + keycache_thread_id= thread_var->id; \ + KEYCACHE_DBUG_PRINT(l,("[thread %ld",keycache_thread_id)) } + +#define KEYCACHE_THREAD_TRACE_END(l) \ + KEYCACHE_DBUG_PRINT(l,("]thread %ld",keycache_thread_id)) +#else /* THREAD */ +#define KEYCACHE_THREAD_TRACE(l) KEYCACHE_DBUG_PRINT(l,("")) +#define KEYCACHE_THREAD_TRACE_BEGIN(l) KEYCACHE_DBUG_PRINT(l,("")) +#define KEYCACHE_THREAD_TRACE_END(l) KEYCACHE_DBUG_PRINT(l,("")) +#endif /* THREAD */ +#else +#define KEYCACHE_THREAD_TRACE_BEGIN(l) +#define KEYCACHE_THREAD_TRACE_END(l) +#define KEYCACHE_THREAD_TRACE(l) +#endif /* defined(KEYCACHE_DEBUG) || !defined(DBUG_OFF) */ + +#define BLOCK_NUMBER(b) \ + ((uint) (((char*)(b)-(char *) keycache->block_root)/sizeof(BLOCK_LINK))) +#define HASH_LINK_NUMBER(h) \ + ((uint) (((char*)(h)-(char *) keycache->hash_link_root)/sizeof(HASH_LINK))) + +#if (defined(KEYCACHE_TIMEOUT) && !defined(__WIN__)) || defined(KEYCACHE_DEBUG) +static int keycache_pthread_cond_wait(pthread_cond_t *cond, + pthread_mutex_t *mutex); +#else +#define keycache_pthread_cond_wait pthread_cond_wait +#endif + +#if defined(KEYCACHE_DEBUG) +static int keycache_pthread_mutex_lock(pthread_mutex_t *mutex); +static void keycache_pthread_mutex_unlock(pthread_mutex_t *mutex); +static int keycache_pthread_cond_signal(pthread_cond_t *cond); +#else +#define keycache_pthread_mutex_lock pthread_mutex_lock +#define keycache_pthread_mutex_unlock pthread_mutex_unlock +#define keycache_pthread_cond_signal pthread_cond_signal +#endif /* defined(KEYCACHE_DEBUG) */ + +#if !defined(DBUG_OFF) +#if defined(inline) +#undef inline +#endif +#define inline /* disabled inline for easier debugging */ +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, uint key_cache_block_size, + size_t use_mem, uint division_limit, + uint age_threshold) +{ + ulong blocks, hash_links; + size_t length; + int error; + DBUG_ENTER("init_key_cache"); + DBUG_ASSERT(key_cache_block_size >= 512); + + KEYCACHE_DEBUG_OPEN; + 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; + pthread_mutex_init(&keycache->cache_lock, MY_MUTEX_INIT_FAST); + keycache->resize_queue.last_thread= NULL; + keycache->post_write= NULL; + } + + keycache->key_cache_mem_size= use_mem; + keycache->key_cache_block_size= key_cache_block_size; + DBUG_PRINT("info", ("key_cache_block_size: %u", + 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; +#if defined(MAX_THREADS) + if (hash_links < MAX_THREADS + blocks - 1) + hash_links= MAX_THREADS + blocks - 1; +#endif + while ((length= (ALIGN_SIZE(blocks * sizeof(BLOCK_LINK)) + + ALIGN_SIZE(hash_links * sizeof(HASH_LINK)) + + ALIGN_SIZE(sizeof(HASH_LINK*) * + keycache->hash_entries))) + + ((size_t) blocks * keycache->key_cache_block_size) > use_mem) + blocks--; + /* Allocate memory for cache page buffers */ + if ((keycache->block_mem= + my_large_malloc((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(length, + MYF(0)))) + break; + my_large_free(keycache->block_mem, MYF(0)); + keycache->block_mem= 0; + } + if (blocks < 8) + { + my_errno= ENOMEM; + my_error(EE_OUTOFMEMORY, MYF(0), 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))); + bzero((uchar*) keycache->block_root, + keycache->disk_blocks * sizeof(BLOCK_LINK)); + bzero((uchar*) keycache->hash_root, + keycache->hash_entries * sizeof(HASH_LINK*)); + bzero((uchar*) keycache->hash_link_root, + keycache->hash_links * sizeof(HASH_LINK)); + keycache->hash_links_used= 0; + keycache->free_hash_list= NULL; + keycache->blocks_used= keycache->blocks_changed= 0; + + keycache->global_blocks_changed= 0; + keycache->blocks_available=0; /* For debugging */ + + /* The LRU chain is empty after initialization */ + keycache->used_last= NULL; + keycache->used_ins= NULL; + keycache->free_block_list= NULL; + keycache->keycache_time= 0; + keycache->warm_blocks= 0; + keycache->min_warm_blocks= (division_limit ? + blocks * division_limit / 100 + 1 : + blocks); + keycache->age_threshold= (age_threshold ? + blocks * age_threshold / 100 : + blocks); + + keycache->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: %p hash_entries: %d\ + hash_root: %p hash_links: %d hash_link_root: %p", + keycache->disk_blocks, keycache->block_root, + keycache->hash_entries, keycache->hash_root, + keycache->hash_links, keycache->hash_link_root)); + bzero((uchar*) keycache->changed_blocks, + sizeof(keycache->changed_blocks[0]) * CHANGED_BLOCKS_HASH); + bzero((uchar*) keycache->file_blocks, + 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, MYF(0)); + keycache->block_mem= NULL; + } + if (keycache->block_root) + { + my_free((uchar*) keycache->block_root, MYF(0)); + keycache->block_root= NULL; + } + 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 + key_cache_block_size size of blocks to keep cached data + use_mem total memory to use for the new key cache + division_limit new division limit (if not zero) + age_threshold new age threshold (if not zero) + + RETURN VALUE + number of blocks in the key cache, if successful, + 0 - otherwise. + + NOTES. + The function first compares the memory size and the block size parameters + with the key cache values. + + If they differ the function free the the memory allocated for the + old key cache blocks by calling the end_key_cache function and + then rebuilds the key cache with new blocks by calling + init_key_cache. + + The function starts the operation only when all other threads + performing operations with the key cache let her to proceed + (when cnt_for_resize=0). +*/ + +int resize_key_cache(KEY_CACHE *keycache, uint key_cache_block_size, + size_t use_mem, uint division_limit, + uint 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); + } + + keycache_pthread_mutex_lock(&keycache->cache_lock); + +#ifdef THREAD + /* + 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); + /* purecov: end */ + } +#endif + + /* + 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)) + { + /* 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; + } + +#ifdef THREAD + /* + 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); +#else + KEYCACHE_DBUG_ASSERT(keycache->cnt_for_resize_op == 0); +#endif + + /* + 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); + + keycache_pthread_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. +*/ + +void change_key_cache_param(KEY_CACHE *keycache, uint division_limit, + uint age_threshold) +{ + DBUG_ENTER("change_key_cache_param"); + + keycache_pthread_mutex_lock(&keycache->cache_lock); + if (division_limit) + keycache->min_warm_blocks= (keycache->disk_blocks * + division_limit / 100 + 1); + if (age_threshold) + keycache->age_threshold= (keycache->disk_blocks * + age_threshold / 100); + keycache_pthread_mutex_unlock(&keycache->cache_lock); + DBUG_VOID_RETURN; +} + + +/* + Remove key_cache from memory + + SYNOPSIS + end_key_cache() + keycache key cache handle + cleanup Complete free (Free also mutex for key cache) + + RETURN VALUE + none +*/ + +void end_key_cache(KEY_CACHE *keycache, my_bool cleanup) +{ + DBUG_ENTER("end_key_cache"); + DBUG_PRINT("enter", ("key_cache: %p", 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, MYF(0)); + keycache->block_mem= NULL; + my_free((uchar*) keycache->block_root, MYF(0)); + keycache->block_root= NULL; + } + keycache->disk_blocks= -1; + /* Reset blocks_changed to be safe if flush_all_key_blocks is called */ + keycache->blocks_changed= 0; + } + + DBUG_PRINT("status", ("used: %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) + { + pthread_mutex_destroy(&keycache->cache_lock); + keycache->key_cache_inited= keycache->can_be_used= 0; + keycache->post_write= NULL; + KEYCACHE_DEBUG_CLOSE; + } + DBUG_VOID_RETURN; +} /* end_key_cache */ + + +/** + Does a my_pwrite() to the file and then calls callback. Arguments are those + of my_pwrite() plus the callback and its argument. + + @note The callback is really POST-write; callers depend on this! So always + call it after writing to the file, not before. + + @return Operation status + @retval 0 ok + @retval !=0 write or callback failed +*/ + +static inline int key_cache_pwrite(int Filedes, const uchar *Buffer, + uint Count, my_off_t offset, myf MyFlags, + KEYCACHE_POST_WRITE_CALLBACK callback, + void *callback_arg) +{ + int ret= my_pwrite(Filedes, Buffer, Count, offset, MyFlags); + if (callback) + ret|= (*callback)(callback_arg, Buffer, Count, offset); + return ret; +} + + +#ifdef THREAD + +/* + Link a thread into double-linked queue of waiting threads. + + SYNOPSIS + link_into_queue() + wqueue pointer to the queue structure + thread pointer to the thread to be added to the queue + + RETURN VALUE + none + + NOTES. + Queue is represented by a circular list of the thread structures + The list is double-linked of the type (**prev,*next), accessed by + a pointer to the last element. +*/ + +static void link_into_queue(KEYCACHE_WQUEUE *wqueue, + struct st_my_thread_var *thread) +{ + struct st_my_thread_var *last; + + 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 + + SYNOPSIS + unlink_from_queue() + wqueue pointer to the queue structure + thread pointer to the thread to be removed from the queue + + RETURN VALUE + none + + NOTES. + See NOTES for link_into_queue +*/ + +static void unlink_from_queue(KEYCACHE_WQUEUE *wqueue, + struct st_my_thread_var *thread) +{ + KEYCACHE_DBUG_PRINT("unlink_from_queue", ("thread %ld", thread->id)); + 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(struct st_my_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. + + 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, + pthread_mutex_t *mutex) +{ + struct st_my_thread_var *last; + struct st_my_thread_var *thread= my_thread_var; + + /* 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 + { + KEYCACHE_DBUG_PRINT("wait", ("suspend thread %ld", thread->id)); + keycache_pthread_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) +{ + struct st_my_thread_var *last; + struct st_my_thread_var *next; + struct st_my_thread_var *thread; + + /* Queue may be empty. */ + if (!(last= wqueue->last_thread)) + return; + + next= last->next; + do + { + thread=next; + KEYCACHE_DBUG_PRINT("release_whole_queue: signal", + ("thread %ld", thread->id)); + /* Signal the thread. */ + keycache_pthread_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; +} + +#endif /* THREAD */ + + +/* + 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); +#ifdef THREAD + if (!hot && keycache->waiting_for_block.last_thread) + { + /* Signal that in the LRU warm sub-chain an available block has appeared */ + struct st_my_thread_var *last_thread= + keycache->waiting_for_block.last_thread; + struct st_my_thread_var *first_thread= last_thread->next; + struct st_my_thread_var *next_thread= first_thread; + HASH_LINK *hash_link= (HASH_LINK *) first_thread->opt_info; + struct st_my_thread_var *thread; + do + { + thread= next_thread; + next_thread= thread->next; + /* + We notify about the event all threads that ask + for the same page as the first thread in the queue + */ + if ((HASH_LINK *) thread->opt_info == hash_link) + { + KEYCACHE_DBUG_PRINT("link_block: signal", ("thread %ld", thread->id)); + keycache_pthread_cond_signal(&thread->suspend); + unlink_from_queue(&keycache->waiting_for_block, thread); + block->requests++; + } + } + while (thread != last_thread); + hash_link->block= block; + /* + 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; + KEYCACHE_THREAD_TRACE("link_block: after signaling"); +#if defined(KEYCACHE_DEBUG) + KEYCACHE_DBUG_PRINT("link_block", + ("linked,unlinked block %u status=%x #requests=%u #available=%u", + BLOCK_NUMBER(block), block->status, + block->requests, keycache->blocks_available)); +#endif + return; + } +#else /* THREAD */ + KEYCACHE_DBUG_ASSERT(! (!hot && keycache->waiting_for_block.last_thread)); + /* Condition not transformed using DeMorgan, to keep the text identical */ +#endif /* THREAD */ + pins= hot ? &keycache->used_ins : &keycache->used_last; + ins= *pins; + if (ins) + { + 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; + } + KEYCACHE_THREAD_TRACE("link_block"); +#if defined(KEYCACHE_DEBUG) + keycache->blocks_available++; + KEYCACHE_DBUG_PRINT("link_block", + ("linked block %u:%1u status=%x #requests=%u #available=%u", + BLOCK_NUMBER(block), at_end, block->status, + block->requests, keycache->blocks_available)); + KEYCACHE_DBUG_ASSERT((ulong) keycache->blocks_available <= + keycache->blocks_used); +#endif +} + + +/* + 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 + + KEYCACHE_THREAD_TRACE("unlink_block"); +#if defined(KEYCACHE_DEBUG) + KEYCACHE_DBUG_ASSERT(keycache->blocks_available != 0); + keycache->blocks_available--; + KEYCACHE_DBUG_PRINT("unlink_block", + ("unlinked block %u status=%x #requests=%u #available=%u", + BLOCK_NUMBER(block), block->status, + block->requests, keycache->blocks_available)); +#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; + KEYCACHE_DBUG_PRINT("unreg_request", ("#warm_blocks: %lu", + keycache->warm_blocks)); + } + 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; + } + KEYCACHE_DBUG_PRINT("unreg_request", ("#warm_blocks: %lu", + keycache->warm_blocks)); + } + } +} + +/* + 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); +#ifdef THREAD + if (! --block->hash_link->requests && block->condvar) + keycache_pthread_cond_signal(block->condvar); +#else + --block->hash_link->requests; +#endif +} + + +/* + 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) +{ +#ifdef THREAD + struct st_my_thread_var *thread= my_thread_var; + 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) + { + KEYCACHE_DBUG_PRINT("wait_for_readers: wait", + ("suspend thread %ld block %u", + thread->id, BLOCK_NUMBER(block))); + /* There must be no other waiter. We have no queue here. */ + DBUG_ASSERT(!block->condvar); + block->condvar= &thread->suspend; + keycache_pthread_cond_wait(&thread->suspend, &keycache->cache_lock); + block->condvar= NULL; + } +#else + KEYCACHE_DBUG_ASSERT(block->hash_link->requests == 0); +#endif +} + + +/* + Add a hash link to a bucket in the hash_table +*/ + +static inline void link_hash(HASH_LINK **start, HASH_LINK *hash_link) +{ + if (*start) + (*start)->prev= &hash_link->next; + hash_link->next= *start; + hash_link->prev= start; + *start= hash_link; +} + + +/* + Remove a hash link from the hash table +*/ + +static void unlink_hash(KEY_CACHE *keycache, HASH_LINK *hash_link) +{ + KEYCACHE_DBUG_PRINT("unlink_hash", ("fd: %u pos_ %lu #requests=%u", + (uint) hash_link->file,(ulong) hash_link->diskpos, hash_link->requests)); + KEYCACHE_DBUG_ASSERT(hash_link->requests == 0); + if ((*hash_link->prev= hash_link->next)) + hash_link->next->prev= hash_link->prev; + hash_link->block= NULL; +#ifdef THREAD + if (keycache->waiting_for_hash_link.last_thread) + { + /* Signal that a free hash link has appeared */ + struct st_my_thread_var *last_thread= + keycache->waiting_for_hash_link.last_thread; + struct st_my_thread_var *first_thread= last_thread->next; + struct st_my_thread_var *next_thread= first_thread; + KEYCACHE_PAGE *first_page= (KEYCACHE_PAGE *) (first_thread->opt_info); + struct st_my_thread_var *thread; + + hash_link->file= first_page->file; + hash_link->diskpos= first_page->filepos; + do + { + KEYCACHE_PAGE *page; + thread= next_thread; + page= (KEYCACHE_PAGE *) thread->opt_info; + next_thread= thread->next; + /* + We notify about the event all threads that ask + for the same page as the first thread in the queue + */ + if (page->file == hash_link->file && page->filepos == hash_link->diskpos) + { + KEYCACHE_DBUG_PRINT("unlink_hash: signal", ("thread %ld", thread->id)); + keycache_pthread_cond_signal(&thread->suspend); + unlink_from_queue(&keycache->waiting_for_hash_link, thread); + } + } + while (thread != last_thread); + link_hash(&keycache->hash_root[KEYCACHE_HASH(hash_link->file, + hash_link->diskpos)], + hash_link); + return; + } +#else /* THREAD */ + KEYCACHE_DBUG_ASSERT(! (keycache->waiting_for_hash_link.last_thread)); +#endif /* THREAD */ + hash_link->next= keycache->free_hash_list; + keycache->free_hash_list= hash_link; +} + + +/* + Get the hash link for a page +*/ + +static HASH_LINK *get_hash_link(KEY_CACHE *keycache, + int file, my_off_t filepos) +{ + reg1 HASH_LINK *hash_link, **start; +#if defined(KEYCACHE_DEBUG) + int cnt; +#endif + + KEYCACHE_DBUG_PRINT("get_hash_link", ("fd: %u pos: %lu", + (uint) file,(ulong) filepos)); + +restart: + /* + Find the bucket in the hash table for the pair (file, filepos); + start contains the head of the bucket list, + hash_link points to the first member of the list + */ + hash_link= *(start= &keycache->hash_root[KEYCACHE_HASH(file, filepos)]); +#if defined(KEYCACHE_DEBUG) + cnt= 0; +#endif + /* Look for an element for the pair (file, filepos) in the bucket chain */ + while (hash_link && + (hash_link->diskpos != filepos || hash_link->file != file)) + { + hash_link= hash_link->next; +#if defined(KEYCACHE_DEBUG) + cnt++; + if (! (cnt <= keycache->hash_links_used)) + { + int i; + for (i=0, hash_link= *start ; + i < cnt ; i++, hash_link= hash_link->next) + { + KEYCACHE_DBUG_PRINT("get_hash_link", ("fd: %u pos: %lu", + (uint) hash_link->file,(ulong) hash_link->diskpos)); + } + } + KEYCACHE_DBUG_ASSERT(cnt <= keycache->hash_links_used); +#endif + } + if (! hash_link) + { + /* There is no hash link in the hash table for the pair (file, filepos) */ + if (keycache->free_hash_list) + { + hash_link= keycache->free_hash_list; + keycache->free_hash_list= hash_link->next; + } + else if (keycache->hash_links_used < keycache->hash_links) + { + hash_link= &keycache->hash_link_root[keycache->hash_links_used++]; + } + else + { +#ifdef THREAD + /* Wait for a free hash link */ + struct st_my_thread_var *thread= my_thread_var; + KEYCACHE_PAGE page; + KEYCACHE_DBUG_PRINT("get_hash_link", ("waiting")); + page.file= file; + page.filepos= filepos; + thread->opt_info= (void *) &page; + link_into_queue(&keycache->waiting_for_hash_link, thread); + KEYCACHE_DBUG_PRINT("get_hash_link: wait", + ("suspend thread %ld", thread->id)); + keycache_pthread_cond_wait(&thread->suspend, + &keycache->cache_lock); + thread->opt_info= NULL; +#else + KEYCACHE_DBUG_ASSERT(0); +#endif + goto restart; + } + hash_link->file= file; + hash_link->diskpos= filepos; + link_hash(start, hash_link); + } + /* Register the request for the page */ + hash_link->requests++; + + return hash_link; +} + + +/* + Get a block for the file page requested by a keycache read/write operation; + If the page is not in the cache return a free block, if there is none + return the lru block after saving its buffer if the page is dirty. + + SYNOPSIS + + find_key_block() + keycache pointer to a key cache data structure + file handler for the file to read page from + filepos position of the page in the file + init_hits_left how initialize the block counter for the page + wrmode <-> get for writing + page_st out {PAGE_READ,PAGE_TO_BE_READ,PAGE_WAIT_TO_BE_READ} + + RETURN VALUE + Pointer to the found block if successful, 0 - otherwise + + NOTES. + For the page from file positioned at filepos the function checks whether + the page is in the key cache specified by the first parameter. + If this is the case it immediately returns the block. + If not, the function first chooses a block for this page. If there is + no not used blocks in the key cache yet, the function takes the block + at the very beginning of the warm sub-chain. It saves the page in that + block if it's dirty before returning the pointer to it. + The function returns in the page_st parameter the following values: + PAGE_READ - if page already in the block, + PAGE_TO_BE_READ - if it is to be read yet by the current thread + WAIT_TO_BE_READ - if it is to be read by another thread + If an error occurs THE BLOCK_ERROR bit is set in the block status. + It might happen that there are no blocks in LRU chain (in warm part) - + all blocks are unlinked for some read/write operations. Then the function + waits until first of this operations links any block back. +*/ + +static BLOCK_LINK *find_key_block(KEY_CACHE *keycache, + File file, my_off_t filepos, + int init_hits_left, + int wrmode, int *page_st) +{ + HASH_LINK *hash_link; + BLOCK_LINK *block; + int error= 0; + int page_status; + + DBUG_ENTER("find_key_block"); + KEYCACHE_THREAD_TRACE("find_key_block:begin"); + DBUG_PRINT("enter", ("fd: %d pos: %lu wrmode: %d", + file, (ulong) filepos, wrmode)); + KEYCACHE_DBUG_PRINT("find_key_block", ("fd: %d pos: %lu wrmode: %d", + file, (ulong) filepos, + wrmode)); +#if !defined(DBUG_OFF) && defined(EXTRA_DEBUG) + DBUG_EXECUTE("check_keycache2", + test_key_cache(keycache, "start of find_key_block", 0);); +#endif + +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); + 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) + { + struct st_my_thread_var *thread; + + /* + 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= my_thread_var; + thread->opt_info= (void *) hash_link; + link_into_queue(&keycache->waiting_for_block, thread); + do + { + KEYCACHE_DBUG_PRINT("find_key_block: wait", + ("suspend thread %ld", thread->id)); + keycache_pthread_cond_wait(&thread->suspend, + &keycache->cache_lock); + } while (thread->next); + thread->opt_info= NULL; + /* + 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); + /* + 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); + /* + 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, 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); + /* + 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). + */ + + KEYCACHE_DBUG_PRINT("find_key_block", + ("request for old page in block %u " + "wrmode: %d block->status: %d", + BLOCK_NUMBER(block), wrmode, block->status)); + /* + Only reading requests can proceed until the old dirty page is flushed, + all others are to be suspended, then resubmitted + */ + if (!wrmode && !(block->status & BLOCK_REASSIGNED)) + { + /* + 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--; + KEYCACHE_DBUG_PRINT("find_key_block", + ("request waiting for old page to be saved")); + wait_on_queue(&block->wqueue[COND_FOR_SAVED], &keycache->cache_lock); + KEYCACHE_DBUG_PRINT("find_key_block", + ("request for old page resubmitted")); + /* + 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; + KEYCACHE_DBUG_PRINT("find_key_block", + ("got free or never used block %u", + BLOCK_NUMBER(block))); + } + else + { + /* + There are no free blocks and no never used blocks, use a block + from the LRU ring. + */ + +#ifdef THREAD + 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. + */ + + struct st_my_thread_var *thread= my_thread_var; + thread->opt_info= (void *) hash_link; + link_into_queue(&keycache->waiting_for_block, thread); + do + { + KEYCACHE_DBUG_PRINT("find_key_block: wait", + ("suspend thread %ld", thread->id)); + keycache_pthread_cond_wait(&thread->suspend, + &keycache->cache_lock); + } + while (thread->next); + thread->opt_info= NULL; + /* 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); + } +#else + KEYCACHE_DBUG_ASSERT(keycache->used_last); +#endif + /* + 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; + + KEYCACHE_DBUG_PRINT("find_key_block", + ("got block %u for new page", BLOCK_NUMBER(block))); + + if (block->status & BLOCK_CHANGED) + { + /* The block contains a dirty page - push it out of the cache */ + + KEYCACHE_DBUG_PRINT("find_key_block", ("block is dirty")); + 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); + /* + 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); + + keycache_pthread_mutex_unlock(&keycache->cache_lock); + /* + The call is thread safe because only the current + thread might change the block->hash_link value + */ + error= key_cache_pwrite(block->hash_link->file, + block->buffer + block->offset, + block->length - block->offset, + block->hash_link->diskpos + + block->offset, + MYF(MY_NABP | MY_WAIT_IF_FULL), + keycache->post_write, + block->post_write_arg); + keycache_pthread_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); + 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; + + KEYCACHE_DBUG_ASSERT(block->hash_link->block == block); + KEYCACHE_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. + */ + KEYCACHE_DBUG_PRINT("find_key_block", + ("block->hash_link: %p hash_link: %p " + "block->status: %u", block->hash_link, + hash_link, block->status )); + page_status= (((block->hash_link == hash_link) && + (block->status & BLOCK_READ)) ? + PAGE_READ : PAGE_WAIT_TO_BE_READ); + } + } + } + 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); + KEYCACHE_DBUG_PRINT("find_key_block", + ("block->hash_link: %p hash_link: %p " + "block->status: %u", block->hash_link, + hash_link, block->status )); + page_status= (((block->hash_link == hash_link) && + (block->status & BLOCK_READ)) ? + PAGE_READ : PAGE_WAIT_TO_BE_READ); + } + } + + KEYCACHE_DBUG_ASSERT(page_status != -1); + /* Same assert basically, but be very sure. */ + KEYCACHE_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; + KEYCACHE_DBUG_PRINT("find_key_block", + ("fd: %d pos: %lu block->status: %u page_status: %d", + file, (ulong) filepos, block->status, + page_status)); + +#if !defined(DBUG_OFF) && defined(EXTRA_DEBUG) + DBUG_EXECUTE("check_keycache2", + test_key_cache(keycache, "end of find_key_block",0);); +#endif + KEYCACHE_THREAD_TRACE("find_key_block:end"); + DBUG_RETURN(block); +} + + +/* + Read into a key cache block buffer from disk. + + SYNOPSIS + + read_block() + keycache pointer to a key cache data structure + block block to which buffer the data is to be read + read_length size of data to be read + min_length at least so much data must be read + primary <-> the current thread will read the data + + RETURN VALUE + None + + NOTES. + The function either reads a page data from file to the block buffer, + or waits until another thread reads it. What page to read is determined + by a block parameter - reference to a hash link for this page. + If an error occurs THE BLOCK_ERROR bit is set in the block status. + We do not report error when the size of successfully read + portion is less than read_length, but not less than min_length. +*/ + +static void read_block(KEY_CACHE *keycache, + BLOCK_LINK *block, uint read_length, + uint min_length, my_bool primary) +{ + uint got_length; + + /* On entry cache_lock is locked */ + + KEYCACHE_THREAD_TRACE("read_block"); + 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_DBUG_PRINT("read_block", + ("page to be read by primary request")); + + keycache->global_cache_read++; + /* Page is not in buffer yet, is to be read from disk */ + keycache_pthread_mutex_unlock(&keycache->cache_lock); + /* + Here other threads may step in and register as secondary readers. + They will register in block->wqueue[COND_FOR_REQUESTED]. + */ + got_length= my_pread(block->hash_link->file, block->buffer, + read_length, block->hash_link->diskpos, MYF(0)); + keycache_pthread_mutex_lock(&keycache->cache_lock); + /* + 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= 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. + */ + } + KEYCACHE_DBUG_PRINT("read_block", + ("primary request: new page in cache")); + /* 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. + */ + KEYCACHE_DBUG_PRINT("read_block", + ("secondary request waiting for new page to be read")); + wait_on_queue(&block->wqueue[COND_FOR_REQUESTED], &keycache->cache_lock); + KEYCACHE_DBUG_PRINT("read_block", + ("secondary request: new page in cache")); + } +} + + +/* + Read a block of data from a cached file into a buffer; + + SYNOPSIS + + key_cache_read() + keycache pointer to a key cache data structure + file handler for the file for the block of data to be read + filepos position of the block of data in the file + level determines the weight of the data + buff buffer to where the data must be placed + length length of the buffer + block_length length of the block in the key cache buffer + return_buffer return pointer to the key cache buffer with the data + + RETURN VALUE + Returns address from where the data is placed if sucessful, 0 - otherwise. + + NOTES. + The function ensures that a block of data of size length from file + positioned at filepos is in the buffers for some key cache blocks. + Then the function either copies the data into the buffer buff, or, + if return_buffer is TRUE, it just returns the pointer to the key cache + buffer with the data. + Filepos must be a multiple of 'block_length', but it doesn't + have to be a multiple of key_cache_block_size; +*/ + +uchar *key_cache_read(KEY_CACHE *keycache, + File file, my_off_t filepos, int level, + uchar *buff, uint length, + uint block_length __attribute__((unused)), + int return_buffer __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 */ + reg1 BLOCK_LINK *block; + uint read_length; + uint offset; + int page_st; + + /* + 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. + */ + keycache_pthread_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); + /* 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) + { + KEYCACHE_DBUG_PRINT("key_cache_read", ("keycache cannot 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); + KEYCACHE_DBUG_ASSERT(read_length > 0); + +#ifndef THREAD + if (block_length > keycache->key_cache_block_size || offset) + return_buffer=0; +#endif + + /* Request the cache block that matches file/pos. */ + keycache->global_cache_r_requests++; + block=find_key_block(keycache, 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++; + keycache_pthread_mutex_unlock(&keycache->cache_lock); + error= (my_pread(file, (uchar*) buff, read_length, + filepos + offset, MYF(MY_NABP)) != 0); + keycache_pthread_mutex_lock(&keycache->cache_lock); + goto next_block; + } + if (!(block->status & BLOCK_ERROR)) + { + if (page_st != PAGE_READ) + { + /* The requested page is to be read into the block buffer */ + read_block(keycache, block, + keycache->key_cache_block_size, read_length+offset, + (my_bool)(page_st == PAGE_TO_BE_READ)); + /* + 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 + */ + my_errno= -1; + block->status|= BLOCK_ERROR; + } + } + + /* block status may have added BLOCK_ERROR in the above 'if'. */ + if (!(block->status & BLOCK_ERROR)) + { +#ifndef THREAD + if (! return_buffer) +#endif + { + DBUG_ASSERT(block->status & (BLOCK_READ | BLOCK_IN_USE)); +#if !defined(SERIALIZED_READ_FROM_CACHE) + keycache_pthread_mutex_unlock(&keycache->cache_lock); +#endif + + /* Copy data from the cache buffer */ + if (!(read_length & 511)) + bmove512(buff, block->buffer+offset, read_length); + else + memcpy(buff, block->buffer+offset, (size_t) read_length); + +#if !defined(SERIALIZED_READ_FROM_CACHE) + keycache_pthread_mutex_lock(&keycache->cache_lock); + DBUG_ASSERT(block->status & (BLOCK_READ | BLOCK_IN_USE)); +#endif + } + } + + 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, block); + error= 1; + break; + } + +#ifndef THREAD + /* This is only true if we where able to read everything in one block */ + if (return_buffer) + DBUG_RETURN(block->buffer); +#endif + next_block: + buff+= read_length; + filepos+= read_length+offset; + offset= 0; + + } while ((length-= read_length)); + goto end; + } + KEYCACHE_DBUG_PRINT("key_cache_read", ("keycache not initialized")); + +no_key_cache: + /* Key cache is not used */ + + keycache->global_cache_r_requests++; + keycache->global_cache_read++; + + if (locked_and_incremented) + keycache_pthread_mutex_unlock(&keycache->cache_lock); + if (my_pread(file, (uchar*) buff, length, filepos, MYF(MY_NABP))) + error= 1; + if (locked_and_incremented) + keycache_pthread_mutex_lock(&keycache->cache_lock); + +end: + if (locked_and_incremented) + { + dec_counter_for_resize_op(keycache); + keycache_pthread_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 + 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, + 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 */ + reg1 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. + */ + keycache_pthread_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); + KEYCACHE_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, 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, 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)); + +#if !defined(SERIALIZED_READ_FROM_CACHE) + keycache_pthread_mutex_unlock(&keycache->cache_lock); + /* + Here other threads may step in and register as secondary readers. + They will register in block->wqueue[COND_FOR_REQUESTED]. + */ +#endif + + /* Copy data from buff */ + if (!(read_length & 511)) + bmove512(block->buffer+offset, buff, read_length); + else + memcpy(block->buffer+offset, buff, (size_t) read_length); + +#if !defined(SERIALIZED_READ_FROM_CACHE) + keycache_pthread_mutex_lock(&keycache->cache_lock); + DBUG_ASSERT(block->status & BLOCK_IN_USE); + DBUG_ASSERT((page_st == PAGE_TO_BE_READ) || + (block->status & BLOCK_READ)); +#endif + /* + 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. + */ + KEYCACHE_DBUG_PRINT("key_cache_insert", + ("primary request: new page in cache")); + /* 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, 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); + keycache_pthread_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 + 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 + post_write_arg argument which will be passed to key cache's + post_write callback + + 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, + File file, my_off_t filepos, int level, + uchar *buff, uint length, + uint block_length __attribute__((unused)), + int dont_write, + void *post_write_arg) +{ + 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 (key_cache_pwrite(file, buff, length, filepos, + MYF(MY_NABP | MY_WAIT_IF_FULL), + keycache->post_write, post_write_arg)) + DBUG_RETURN(1); + /* purecov: end */ + } + +#if !defined(DBUG_OFF) && defined(EXTRA_DEBUG) + DBUG_EXECUTE("check_keycache", + test_key_cache(keycache, "start of key_cache_write", 1);); +#endif + + if (keycache->key_cache_inited) + { + /* Key cache is used */ + reg1 BLOCK_LINK *block; + uint read_length; + uint offset; + int page_st; + + /* + 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. + */ + keycache_pthread_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); + /* 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; + /* 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); + KEYCACHE_DBUG_ASSERT(read_length > 0); + + /* Request the cache block that matches file/pos. */ + keycache->global_cache_w_requests++; + block= find_key_block(keycache, 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++; + keycache_pthread_mutex_unlock(&keycache->cache_lock); + if (key_cache_pwrite(file, (uchar*) buff, read_length, + filepos + offset, + MYF(MY_NABP | MY_WAIT_IF_FULL), + keycache->post_write, post_write_arg)) + error=1; + keycache_pthread_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, 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); + 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)) + { + block->post_write_arg= post_write_arg; +#if !defined(SERIALIZED_READ_FROM_CACHE) + keycache_pthread_mutex_unlock(&keycache->cache_lock); +#endif + if (!(read_length & 511)) + bmove512(block->buffer+offset, buff, read_length); + else + memcpy(block->buffer+offset, buff, (size_t) read_length); + +#if !defined(SERIALIZED_READ_FROM_CACHE) + keycache_pthread_mutex_lock(&keycache->cache_lock); +#endif + } + + 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, 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) + keycache_pthread_mutex_unlock(&keycache->cache_lock); + if (key_cache_pwrite(file, (uchar*) buff, length, filepos, + MYF(MY_NABP | MY_WAIT_IF_FULL), + keycache->post_write, post_write_arg)) + error=1; + if (locked_and_incremented) + keycache_pthread_mutex_lock(&keycache->cache_lock); + } + +end: + if (locked_and_incremented) + { + dec_counter_for_resize_op(keycache); + keycache_pthread_mutex_unlock(&keycache->cache_lock); + } +#if !defined(DBUG_OFF) && defined(EXTRA_DEBUG) + DBUG_EXECUTE("exec", + test_key_cache(keycache, "end of key_cache_write", 1);); +#endif + DBUG_RETURN(error); +} + + +/* + Free block. + + SYNOPSIS + free_block() + keycache Pointer to a key cache data structure + 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, BLOCK_LINK *block) +{ + KEYCACHE_THREAD_TRACE("free block"); + KEYCACHE_DBUG_PRINT("free_block", + ("block %u to be freed, hash_link %p status: %u", + BLOCK_NUMBER(block), block->hash_link, + block->status)); + /* + 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); + /* + 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; + KEYCACHE_THREAD_TRACE("free block"); + KEYCACHE_DBUG_PRINT("free_block", ("block is freed")); + + /* 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, + 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 */ + keycache_pthread_mutex_unlock(&keycache->cache_lock); + /* + As all blocks referred in 'cache' are marked by BLOCK_IN_FLUSH + we are guarunteed no thread will change them + */ + my_qsort((uchar*) cache, count, sizeof(*cache), (qsort_cmp) cmp_sec_link); + + keycache_pthread_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; + + KEYCACHE_DBUG_PRINT("flush_cached_blocks", + ("block %u to be flushed", BLOCK_NUMBER(block))); + /* + 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; + keycache_pthread_mutex_unlock(&keycache->cache_lock); + error= key_cache_pwrite(file, + block->buffer+block->offset, + block->length - block->offset, + block->hash_link->diskpos+ block->offset, + MYF(MY_NABP | MY_WAIT_IF_FULL), + keycache->post_write, block->post_write_arg); + keycache_pthread_mutex_lock(&keycache->cache_lock); + keycache->global_cache_write++; + if (error) + { + block->status|= BLOCK_ERROR; + if (!last_errno) + last_errno= errno ? errno : -1; + } + 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, 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 + 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, + 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)); + +#if !defined(DBUG_OFF) && defined(EXTRA_DEBUG) + DBUG_EXECUTE("check_keycache", + test_key_cache(keycache, "start of flush_key_blocks", 0);); +#endif + + DBUG_ASSERT(type != FLUSH_KEEP_LAZY); + cache= cache_buff; + if (keycache->disk_blocks > 0 && + (!my_disable_flush_key_blocks || type != FLUSH_KEEP)) + { + /* 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; +#if defined(KEYCACHE_DEBUG) + uint cnt=0; +#endif + + if (type != FLUSH_IGNORE_CHANGED) + { + /* + Count how many key blocks we have to cache to be able + to 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++; + KEYCACHE_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(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) + { +#if defined(KEYCACHE_DEBUG) + cnt++; + KEYCACHE_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, 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, 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, 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); + /* 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); + /* 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) + { +#if defined(KEYCACHE_DEBUG) + cnt= 0; +#endif + wait_on_queue(&first_in_switch->wqueue[COND_FOR_SAVED], + &keycache->cache_lock); +#if defined(KEYCACHE_DEBUG) + cnt++; + KEYCACHE_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; + my_off_t next_diskpos; + File next_file; + uint next_status; + uint hash_requests; + + total_found++; + found++; + KEYCACHE_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, 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); + 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); + goto restart; + } + + } /* if (! (type == FLUSH_KEEP || type == FLUSH_FORCE_WRITE)) */ + + } /* if (keycache->disk_blocks > 0 */ + +#ifndef DBUG_OFF + DBUG_EXECUTE("check_keycache", + test_key_cache(keycache, "end of flush_key_blocks", 0);); +#endif +err: + if (cache != cache_buff) + my_free((uchar*) cache, MYF(0)); + 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 + 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, + File file, enum flush_type type) +{ + int res= 0; + DBUG_ENTER("flush_key_blocks"); + DBUG_PRINT("enter", ("keycache: %p", keycache)); + + if (!keycache->key_cache_inited) + DBUG_RETURN(0); + + keycache_pthread_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, file, type); + dec_counter_for_resize_op(keycache); + } + keycache_pthread_mutex_unlock(&keycache->cache_lock); + DBUG_RETURN(res); +} + + +/* + Flush all blocks in the key cache to disk. + + SYNOPSIS + flush_all_key_blocks() + keycache pointer to key cache root structure + + 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) +{ + BLOCK_LINK *block; + uint total_found; + uint found; + uint idx; + DBUG_ENTER("flush_all_key_blocks"); + + do + { + safe_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, 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, 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 __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); +} + + +#ifndef DBUG_OFF +/* + Test if disk-cache is ok +*/ +static void test_key_cache(KEY_CACHE *keycache __attribute__((unused)), + const char *where __attribute__((unused)), + my_bool lock __attribute__((unused))) +{ + /* TODO */ +} +#endif + +#if defined(KEYCACHE_TIMEOUT) + +#define KEYCACHE_DUMP_FILE "keycache_dump.txt" +#define MAX_QUEUE_LEN 100 + + +static void keycache_dump(KEY_CACHE *keycache) +{ + FILE *keycache_dump_file=fopen(KEYCACHE_DUMP_FILE, "w"); + struct st_my_thread_var *last; + struct st_my_thread_var *thread; + BLOCK_LINK *block; + HASH_LINK *hash_link; + KEYCACHE_PAGE *page; + uint i; + + fprintf(keycache_dump_file, "thread:%u\n", thread->id); + + i=0; + thread=last=waiting_for_hash_link.last_thread; + fprintf(keycache_dump_file, "queue of threads waiting for hash link\n"); + if (thread) + do + { + thread=thread->next; + page= (KEYCACHE_PAGE *) thread->opt_info; + fprintf(keycache_dump_file, + "thread:%u, (file,filepos)=(%u,%lu)\n", + thread->id,(uint) page->file,(ulong) page->filepos); + if (++i == MAX_QUEUE_LEN) + break; + } + while (thread != last); + + i=0; + thread=last=waiting_for_block.last_thread; + fprintf(keycache_dump_file, "queue of threads waiting for block\n"); + if (thread) + do + { + thread=thread->next; + hash_link= (HASH_LINK *) thread->opt_info; + fprintf(keycache_dump_file, + "thread:%u hash_link:%u (file,filepos)=(%u,%lu)\n", + thread->id, (uint) HASH_LINK_NUMBER(hash_link), + (uint) hash_link->file,(ulong) hash_link->diskpos); + if (++i == MAX_QUEUE_LEN) + break; + } + while (thread != last); + + for (i=0 ; i< keycache->blocks_used ; i++) + { + int j; + block= &keycache->block_root[i]; + hash_link= block->hash_link; + fprintf(keycache_dump_file, + "block:%u hash_link:%d status:%x #requests=%u waiting_for_readers:%d\n", + i, (int) (hash_link ? HASH_LINK_NUMBER(hash_link) : -1), + block->status, block->requests, block->condvar ? 1 : 0); + for (j=0 ; j < 2; j++) + { + KEYCACHE_WQUEUE *wqueue=&block->wqueue[j]; + thread= last= wqueue->last_thread; + fprintf(keycache_dump_file, "queue #%d\n", j); + if (thread) + { + do + { + thread=thread->next; + fprintf(keycache_dump_file, + "thread:%u\n", thread->id); + if (++i == MAX_QUEUE_LEN) + break; + } + while (thread != last); + } + } + } + fprintf(keycache_dump_file, "LRU chain:"); + block= keycache= used_last; + if (block) + { + do + { + block= block->next_used; + fprintf(keycache_dump_file, + "block:%u, ", BLOCK_NUMBER(block)); + } + while (block != keycache->used_last); + } + fprintf(keycache_dump_file, "\n"); + + fclose(keycache_dump_file); +} + +#endif /* defined(KEYCACHE_TIMEOUT) */ + +#if defined(KEYCACHE_TIMEOUT) && !defined(__WIN__) + + +static int keycache_pthread_cond_wait(pthread_cond_t *cond, + pthread_mutex_t *mutex) +{ + int rc; + struct timeval now; /* time when we started waiting */ + struct timespec timeout; /* timeout value for the wait function */ + struct timezone tz; +#if defined(KEYCACHE_DEBUG) + int cnt=0; +#endif + + /* Get current time */ + gettimeofday(&now, &tz); + /* Prepare timeout value */ + timeout.tv_sec= now.tv_sec + KEYCACHE_TIMEOUT; + /* + timeval uses microseconds. + timespec uses nanoseconds. + 1 nanosecond = 1000 micro seconds + */ + timeout.tv_nsec= now.tv_usec * 1000; + KEYCACHE_THREAD_TRACE_END("started waiting"); +#if defined(KEYCACHE_DEBUG) + cnt++; + if (cnt % 100 == 0) + fprintf(keycache_debug_log, "waiting...\n"); + fflush(keycache_debug_log); +#endif + rc= pthread_cond_timedwait(cond, mutex, &timeout); + KEYCACHE_THREAD_TRACE_BEGIN("finished waiting"); + if (rc == ETIMEDOUT || rc == ETIME) + { +#if defined(KEYCACHE_DEBUG) + fprintf(keycache_debug_log,"aborted by keycache timeout\n"); + fclose(keycache_debug_log); + abort(); +#endif + keycache_dump(); + } + +#if defined(KEYCACHE_DEBUG) + KEYCACHE_DBUG_ASSERT(rc != ETIMEDOUT); +#else + assert(rc != ETIMEDOUT); +#endif + return rc; +} +#else +#if defined(KEYCACHE_DEBUG) +static int keycache_pthread_cond_wait(pthread_cond_t *cond, + pthread_mutex_t *mutex) +{ + int rc; + KEYCACHE_THREAD_TRACE_END("started waiting"); + rc= pthread_cond_wait(cond, mutex); + KEYCACHE_THREAD_TRACE_BEGIN("finished waiting"); + return rc; +} +#endif +#endif /* defined(KEYCACHE_TIMEOUT) && !defined(__WIN__) */ + +#if defined(KEYCACHE_DEBUG) + + +static int keycache_pthread_mutex_lock(pthread_mutex_t *mutex) +{ + int rc; + rc= pthread_mutex_lock(mutex); + KEYCACHE_THREAD_TRACE_BEGIN(""); + return rc; +} + + +static void keycache_pthread_mutex_unlock(pthread_mutex_t *mutex) +{ + KEYCACHE_THREAD_TRACE_END(""); + pthread_mutex_unlock(mutex); +} + + +static int keycache_pthread_cond_signal(pthread_cond_t *cond) +{ + int rc; + KEYCACHE_THREAD_TRACE("signal"); + rc= pthread_cond_signal(cond); + return rc; +} + + +#if defined(KEYCACHE_DEBUG_LOG) + + +static void keycache_debug_print(const char * fmt,...) +{ + va_list args; + va_start(args,fmt); + if (keycache_debug_log) + { + (void) vfprintf(keycache_debug_log, fmt, args); + (void) fputc('\n',keycache_debug_log); + } + va_end(args); +} +#endif /* defined(KEYCACHE_DEBUG_LOG) */ + +#if defined(KEYCACHE_DEBUG_LOG) + + +void keycache_debug_log_close(void) +{ + if (keycache_debug_log) + fclose(keycache_debug_log); +} +#endif /* defined(KEYCACHE_DEBUG_LOG) */ + +#endif /* defined(KEYCACHE_DEBUG) */ + +#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) + { + fprintf(stderr, "block index: %u\n", 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) + { + fprintf(stderr, "hash_link index: %u\n", idx); + fail_hlink(hash_link); + errcnt++; + } + } + if (errcnt) + { + fprintf(stderr, "blocks: %d used: %lu\n", + keycache->disk_blocks, keycache->blocks_used); + fprintf(stderr, "hash_links: %d used: %d\n", + keycache->hash_links, keycache->hash_links_used); + fprintf(stderr, "\n"); + } + return !errcnt; +} +#endif + diff --git a/externals/mysql/mysys/mf_keycaches.c b/externals/mysql/mysys/mf_keycaches.c new file mode 100644 index 00000000000..9ea5678da9a --- /dev/null +++ b/externals/mysql/mysys/mf_keycaches.c @@ -0,0 +1,106 @@ +/* Copyright (C) 2003-2007 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +/* + Handling of multiple key caches + + The idea is to have a thread safe hash on the table name, + with a default key cache value that is returned if the table name is not in + the cache. +*/ + +#include "mysys_priv.h" +#include <keycache.h> +#include <hash.h> +#include <m_string.h> +#include "my_safehash.h" + +/***************************************************************************** + 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. + def Default value if no key cache + + 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, + KEY_CACHE *def) +{ + if (!key_cache_hash.hash.records) + return def; + return (KEY_CACHE*) safe_hash_search(&key_cache_hash, key, length, + (void*) def); +} + + +/* + 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/externals/mysql/mysys/mf_loadpath.c b/externals/mysql/mysys/mf_loadpath.c new file mode 100644 index 00000000000..fbf6f7f5d57 --- /dev/null +++ b/externals/mysql/mysys/mf_loadpath.c @@ -0,0 +1,54 @@ +/* Copyright (C) 2000 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#include "mysys_priv.h" +#include <m_string.h> + + /* Returns full load-path for a file. to may be = path */ + /* if path is a hard-path return path */ + /* if path starts with home-dir return path */ + /* if path starts with current dir or parent-dir unpack path */ + /* if there is no path, prepend with own_path_prefix if given */ + /* else unpack path according to current dir */ + +char * my_load_path(char * to, const char *path, + const char *own_path_prefix) +{ + char buff[FN_REFLEN]; + int is_cur; + DBUG_ENTER("my_load_path"); + DBUG_PRINT("enter",("path: %s prefix: %s",path, + own_path_prefix ? own_path_prefix : "")); + + if ((path[0] == FN_HOMELIB && path[1] == FN_LIBCHAR) || + test_if_hard_path(path)) + (void) strmov(buff,path); + else if ((is_cur=(path[0] == FN_CURLIB && path[1] == FN_LIBCHAR)) || + (is_prefix(path,FN_PARENTDIR)) || + ! own_path_prefix) + { + if (is_cur) + is_cur=2; /* Remove current dir */ + if (! my_getwd(buff,(uint) (FN_REFLEN-strlen(path)+is_cur),MYF(0))) + (void) strcat(buff,path+is_cur); + else + (void) strmov(buff,path); /* Return org file name */ + } + else + (void) strxmov(buff,own_path_prefix,path,NullS); + strmov(to,buff); + DBUG_PRINT("exit",("to: %s",to)); + DBUG_RETURN(to); +} /* my_load_path */ diff --git a/externals/mysql/mysys/mf_pack.c b/externals/mysql/mysys/mf_pack.c new file mode 100644 index 00000000000..ab4cd352710 --- /dev/null +++ b/externals/mysql/mysys/mf_pack.c @@ -0,0 +1,526 @@ +/* Copyright (C) 2000 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#include "mysys_priv.h" +#include <m_string.h> +#ifdef HAVE_PWD_H +#include <pwd.h> +#endif +#ifdef VMS +#include <rms.h> +#include <iodef.h> +#include <descrip.h> +#endif /* VMS */ + +static char * NEAR_F 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; + char * start; + char buff[FN_REFLEN]; + DBUG_ENTER("pack_dirname"); + + LINT_INIT(buff_length); + (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 && !bcmp(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 (bcmp(to,home_dir,length) == 0 && to[length] == FN_LIBCHAR) + { + to[0]=FN_HOMELIB; /* Filename begins with ~ */ + (void) strmov_overlapp(to+1,to+length); + } + } + if (! cwd_err) + { /* Test if cwd is ~/... */ + if (length > 1 && length < buff_length) + { + if (bcmp(buff,home_dir,length) == 0 && buff[length] == FN_LIBCHAR) + { + buff[0]=FN_HOMELIB; + (void) strmov_overlapp(buff+1,buff+length); + } + } + if (is_prefix(to,buff)) + { + length= strlen(buff); + if (to[length]) + (void) strmov_overlapp(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(register char *to, const char *from) +{ + reg5 size_t length; + reg2 char * pos; + reg3 char * from_ptr; + reg4 char * start; + char parent[5], /* for "FN_PARENTDIR" */ + buff[FN_REFLEN+1],*end_parentdir; +#ifdef BACKSLASH_MBTAIL + CHARSET_INFO *fs= fs_character_set(); +#endif + DBUG_ENTER("cleanup_dirname"); + DBUG_PRINT("enter",("from: '%s'",from)); + + 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=strnmov(buff,from_ptr,length); from_ptr+=length; + } +#endif + + parent[0]=FN_LIBCHAR; + length=(size_t) (strmov(parent+1,FN_PARENTDIR)-parent); + for (pos=start ; (*pos= *from_ptr++) != 0 ; pos++) + { +#ifdef BACKSLASH_MBTAIL + uint l; + if (use_mb(fs) && (l= my_ismbchar(fs, from_ptr - 1, from_ptr + 2))) + { + for (l-- ; l ; *++pos= *from_ptr++, l--); + start= pos + 1; /* Don't look inside multi-byte char */ + continue; + } +#endif + if (*pos == '/') + *pos = FN_LIBCHAR; + if (*pos == FN_LIBCHAR) + { + if ((size_t) (pos-start) > length && bcmp(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=strmov(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=strmov(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 || bcmp(pos,parent,length) == 0) + { /* Don't remove ~user/ */ + pos=strmov(end_parentdir+1,parent); + *pos=FN_LIBCHAR; + continue; + } + } + } + else if ((size_t) (pos-start) == length-1 && + !bcmp(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) strmov(to,buff); + DBUG_PRINT("exit",("to: '%s'",to)); + DBUG_RETURN((size_t) (pos-buff)); +} /* cleanup_dirname */ + + +/* + On system where you don't have symbolic links, the following + code will allow you to create a file: + directory-name.sym that should contain the real path + to the directory. This will be used if the directory name + doesn't exists +*/ + + +my_bool my_use_symdir=0; /* Set this if you want to use symdirs */ + +#ifdef USE_SYMDIR +void symdirget(char *dir) +{ + char buff[FN_REFLEN]; + char *pos=strend(dir); + if (dir[0] && pos[-1] != FN_DEVCHAR && my_access(dir, F_OK)) + { + File file; + size_t length; + char temp= *(--pos); /* May be "/" or "\" */ + strmov(pos,".sym"); + file= my_open(dir, O_RDONLY, MYF(0)); + *pos++=temp; *pos=0; /* Restore old filename */ + if (file >= 0) + { + if ((length= my_read(file, buff, sizeof(buff), MYF(0))) > 0) + { + for (pos= buff + length ; + pos > buff && (iscntrl(pos[-1]) || isspace(pos[-1])) ; + pos --); + + /* Ensure that the symlink ends with the directory symbol */ + if (pos == buff || pos[-1] != FN_LIBCHAR) + *pos++=FN_LIBCHAR; + + strmake(dir,buff, (size_t) (pos-buff)); + } + my_close(file, MYF(0)); + } + } +} +#endif /* USE_SYMDIR */ + + +/** + 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: rip out the non-working VMS stuff and 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] != '/') + { + 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/... + - Resolves MySQL's fake "foo.sym" symbolic directory names (if USE_SYMDIR) + - 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--; + if (buff+h_length < suffix) + bmove(buff+h_length,suffix,length); + else + bmove_upp((uchar*) buff+h_length+length, (uchar*) suffix+length, length); + bmove(buff,tilde_expansion,h_length); + } + } + } +#ifdef USE_SYMDIR + if (my_use_symdir) + symdirget(buff); +#endif + 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 * NEAR_F 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) strmov(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) +{ +#ifndef FN_C_BEFORE_DIR + return (size_t) (strmake(to,from,FN_REFLEN-1)-to); +#else /* VMS */ + + /* change 'dev:lib/xxx' to 'dev:[lib]xxx' */ + /* change 'dev:xxx' to 'dev:xxx' */ + /* change './xxx' to 'xxx' */ + /* change './lib/' or lib/ to '[.lib]' */ + /* change '/x/y/z to '[x.y]x' */ + /* change 'dev:/x' to 'dev:[000000]x' */ + + int libchar_found; + size_t length; + char * to_pos,from_pos,pos; + char buff[FN_REFLEN]; + DBUG_ENTER("system_filename"); + + libchar_found=0; + (void) strmov(buff,from); /* If to == from */ + from_pos= buff; + if ((pos=strrchr(from_pos,FN_DEVCHAR))) /* Skip device part */ + { + pos++; + to_pos=strnmov(to,from_pos,(size_t) (pos-from_pos)); + from_pos=pos; + } + else + to_pos=to; + + if (from_pos[0] == FN_CURLIB && from_pos[1] == FN_LIBCHAR) + from_pos+=2; /* Skip './' */ + if (strchr(from_pos,FN_LIBCHAR)) + { + *(to_pos++) = FN_C_BEFORE_DIR; + if (strinstr(from_pos,FN_ROOTDIR) == 1) + { + from_pos+=strlen(FN_ROOTDIR); /* Actually +1 but... */ + if (! strchr(from_pos,FN_LIBCHAR)) + { /* No dir, use [000000] */ + to_pos=strmov(to_pos,FN_C_ROOT_DIR); + libchar_found++; + } + } + else + *(to_pos++)=FN_C_DIR_SEP; /* '.' gives current dir */ + + while ((pos=strchr(from_pos,FN_LIBCHAR))) + { + if (libchar_found++) + *(to_pos++)=FN_C_DIR_SEP; /* Add '.' between dirs */ + if (strinstr(from_pos,FN_PARENTDIR) == 1 && + from_pos+strlen(FN_PARENTDIR) == pos) + to_pos=strmov(to_pos,FN_C_PARENT_DIR); /* Found '../' */ + else + to_pos=strnmov(to_pos,from_pos,(size_t) (pos-from_pos)); + from_pos=pos+1; + } + *(to_pos++)=FN_C_AFTER_DIR; + } + length= (size_t) (strmov(to_pos,from_pos)-to); + DBUG_PRINT("exit",("name: '%s'",to)); + DBUG_RETURN(length); +#endif +} /* system_filename */ + + + /* 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 */ + strmov(buff,from); + from=buff; + } + length= dirname_part(to, from, &to_length); /* Copy dirname & fix chars */ + (void) strmov(to + to_length,from+length); + return (to); +} /* intern_filename */ diff --git a/externals/mysql/mysys/mf_path.c b/externals/mysql/mysys/mf_path.c new file mode 100644 index 00000000000..d51cac732f5 --- /dev/null +++ b/externals/mysql/mysys/mf_path.c @@ -0,0 +1,123 @@ +/* Copyright (C) 2000 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#include "mysys_priv.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) strmov(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(__WIN__) +#define F_OK 0 +#define PATH_SEP ';' +#define PROGRAM_EXTENSION ".exe" +#elif defined(__NETWARE__) +#define PATH_SEP ';' +#define PROGRAM_EXTENSION ".nlm" +#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(strnmov(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 __WIN__ + 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/externals/mysql/mysys/mf_qsort.c b/externals/mysql/mysys/mf_qsort.c new file mode 100644 index 00000000000..4b3ecb603a6 --- /dev/null +++ b/externals/mysql/mysys/mf_qsort.c @@ -0,0 +1,216 @@ +/* Copyright (C) 2000 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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" +#ifndef SCO +#include <m_string.h> +#endif + +/* 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) \ + { \ + reg1 char **a = (char**) (A), **b = (char**) (B); \ + char *tmp = *a; *a++ = *b; *b++ = tmp; \ + } \ + else \ + { \ + reg1 char *a = (A), *b = (B); \ + reg3 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 +#if defined(QSORT_TYPE_IS_VOID) +#define SORT_RETURN return +#else +#define SORT_RETURN return 0 +#endif + +/**************************************************************************** +** '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 +qsort_t my_qsort2(void *base_ptr, size_t count, size_t size, qsort2_cmp cmp, + void *cmp_argument) +#else +qsort_t 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) + SORT_RETURN; + + low = (char*) base_ptr; + high = low+ size * (count - 1); + stack_ptr = stack + 1; +#ifdef HAVE_purify + /* The first element in the stack will be accessed for the last POP */ + stack[0].low=stack[0].high=0; +#endif + 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); + my_afree(pivot); + SORT_RETURN; +} diff --git a/externals/mysql/mysys/mf_qsort2.c b/externals/mysql/mysys/mf_qsort2.c new file mode 100644 index 00000000000..ca2bd1a4952 --- /dev/null +++ b/externals/mysql/mysys/mf_qsort2.c @@ -0,0 +1,19 @@ +/* Copyright (C) 2000 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +/* qsort that sends one extra argument to the compare subrutine */ + +#define QSORT_EXTRA_CMP_ARGUMENT +#include "mf_qsort.c" diff --git a/externals/mysql/mysys/mf_radix.c b/externals/mysql/mysys/mf_radix.c new file mode 100644 index 00000000000..582ca76b8f8 --- /dev/null +++ b/externals/mysql/mysys/mf_radix.c @@ -0,0 +1,54 @@ +/* Copyright (C) 2000 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 */ + +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--) + { + bzero((uchar*) count,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/externals/mysql/mysys/mf_same.c b/externals/mysql/mysys/mf_same.c new file mode 100644 index 00000000000..6738dc8051e --- /dev/null +++ b/externals/mysql/mysys/mf_same.c @@ -0,0 +1,40 @@ +/* Copyright (C) 2000 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +/* Kopierar biblioteksstrukturen och extensionen fr}n ett filnamn */ + +#include "mysys_priv.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/externals/mysql/mysys/mf_sort.c b/externals/mysql/mysys/mf_sort.c new file mode 100644 index 00000000000..686ebbc1d14 --- /dev/null +++ b/externals/mysql/mysys/mf_sort.c @@ -0,0 +1,41 @@ +/* Copyright (C) 2000 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +/* Sort of string pointers in string-order with radix or qsort */ + +#include "mysys_priv.h" +#include <m_string.h> + +void my_string_ptr_sort(uchar *base, uint items, size_t size) +{ +#if INT_MAX > 65536L + uchar **ptr=0; + + if (size <= 20 && items >= 1000 && items < 100000 && + (ptr= (uchar**) my_malloc(items*sizeof(char*),MYF(0)))) + { + radixsort_for_str_ptr((uchar**) base,items,size,ptr); + my_free((uchar*) ptr,MYF(0)); + } + else +#endif + { + if (size && items) + { + my_qsort2(base,items, sizeof(uchar*), get_ptr_compare(size), + (void*) &size); + } + } +} diff --git a/externals/mysql/mysys/mf_soundex.c b/externals/mysql/mysys/mf_soundex.c new file mode 100644 index 00000000000..fe30d8c81af --- /dev/null +++ b/externals/mysql/mysys/mf_soundex.c @@ -0,0 +1,105 @@ +/* Copyright (C) 2000 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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,register char * out_pntr, char * in_pntr, + pbool remove_garbage) +{ + char ch,last_ch; + reg3 char * end; + register 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/externals/mysql/mysys/mf_strip.c b/externals/mysql/mysys/mf_strip.c new file mode 100644 index 00000000000..b33620b1b2d --- /dev/null +++ b/externals/mysql/mysys/mf_strip.c @@ -0,0 +1,45 @@ +/* Copyright (C) 2000 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +/* T|mmer en str{ng p{ slut_space */ + +#include "mysys_priv.h" + +/* + strip_sp(char * str) + Strips end-space from string and returns new length. +*/ + +size_t strip_sp(register char * str) +{ + reg2 char * found; + reg3 char * start; + + start=found=str; + + while (*str) + { + if (*str != ' ') + { + while (*++str && *str != ' ') {}; + if (!*str) + return (size_t) (str-start); /* Return stringlength */ + } + found=str; + while (*++str == ' ') {}; + } + *found= '\0'; /* Stripp at first space */ + return (size_t) (found-start); +} /* strip_sp */ diff --git a/externals/mysql/mysys/mf_tempdir.c b/externals/mysql/mysys/mf_tempdir.c new file mode 100644 index 00000000000..d6492c90965 --- /dev/null +++ b/externals/mysql/mysys/mf_tempdir.c @@ -0,0 +1,95 @@ +/* Copyright (C) 2000 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#include "mysys_priv.h" +#include <m_string.h> + +#if defined( __WIN__) || defined(__NETWARE__) +#define DELIM ';' +#else +#define DELIM ':' +#endif + +my_bool init_tmpdir(MY_TMPDIR *tmpdir, const char *pathlist) +{ + char *end, *copy; + char buff[FN_REFLEN]; + DBUG_ENTER("init_tmpdir"); + DBUG_PRINT("enter", ("pathlist: %s", pathlist ? pathlist : "NULL")); + + pthread_mutex_init(&tmpdir->mutex, MY_MUTEX_INIT_FAST); + if (my_init_dynamic_array(&tmpdir->full_list, sizeof(char*), 1, 5)) + goto err; + if (!pathlist || !pathlist[0]) + { + /* Get default temporary directory */ + pathlist=getenv("TMPDIR"); /* Use this if possible */ +#if defined( __WIN__) || defined(__NETWARE__) + if (!pathlist) + pathlist=getenv("TEMP"); + if (!pathlist) + pathlist=getenv("TMP"); +#endif + if (!pathlist || !pathlist[0]) + pathlist=(char*) P_tmpdir; + } + do + { + uint length; + end=strcend(pathlist, DELIM); + strmake(buff, pathlist, (uint) (end-pathlist)); + length= cleanup_dirname(buff, buff); + if (!(copy= my_strndup(buff, length, MYF(MY_WME))) || + insert_dynamic(&tmpdir->full_list, (uchar*) ©)) + DBUG_RETURN(TRUE); + pathlist=end+1; + } + while (*end); + freeze_size(&tmpdir->full_list); + tmpdir->list=(char **)tmpdir->full_list.buffer; + tmpdir->max=tmpdir->full_list.elements-1; + tmpdir->cur=0; + DBUG_RETURN(FALSE); + +err: + delete_dynamic(&tmpdir->full_list); /* Safe to free */ + pthread_mutex_destroy(&tmpdir->mutex); + DBUG_RETURN(TRUE); +} + + +char *my_tmpdir(MY_TMPDIR *tmpdir) +{ + char *dir; + if (!tmpdir->max) + return tmpdir->list[0]; + pthread_mutex_lock(&tmpdir->mutex); + dir=tmpdir->list[tmpdir->cur]; + tmpdir->cur= (tmpdir->cur == tmpdir->max) ? 0 : tmpdir->cur+1; + pthread_mutex_unlock(&tmpdir->mutex); + return dir; +} + +void free_tmpdir(MY_TMPDIR *tmpdir) +{ + uint i; + if (!tmpdir->full_list.elements) + return; + for (i=0; i<=tmpdir->max; i++) + my_free(tmpdir->list[i], MYF(0)); + delete_dynamic(&tmpdir->full_list); + pthread_mutex_destroy(&tmpdir->mutex); +} + diff --git a/externals/mysql/mysys/mf_tempfile.c b/externals/mysql/mysys/mf_tempfile.c new file mode 100644 index 00000000000..40016210de4 --- /dev/null +++ b/externals/mysql/mysys/mf_tempfile.c @@ -0,0 +1,189 @@ +/* Copyright (C) 2000 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#include "mysys_priv.h" +#include <m_string.h> +#include "my_static.h" +#include "mysys_err.h" +#include <errno.h> +#ifdef HAVE_PATHS_H +#include <paths.h> +#endif + + + +/* + @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 __attribute__((unused)), + myf MyFlags __attribute__((unused))) +{ + File file= -1; +#ifdef __WIN__ + TCHAR path_buf[MAX_PATH-14]; +#endif + + DBUG_ENTER("create_temp_file"); + DBUG_PRINT("enter", ("dir: %s, prefix: %s", dir, prefix)); +#if defined (__WIN__) + + /* + 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)); + my_errno= tmp; + } + +#elif defined(_ZTC__) + if (!dir) + dir=getenv("TMPDIR"); + if ((res=tempnam((char*) dir,(char *) prefix))) + { + strmake(to,res,FN_REFLEN-1); + (*free)(res); + file=my_create(to, 0, mode | O_EXCL | O_NOFOLLOW, MyFlags); + } +#elif defined(HAVE_MKSTEMP) && !defined(__NETWARE__) + { + char prefix_buff[30]; + uint pfx_len; + File org_file; + + pfx_len= (uint) (strmov(strnmov(prefix_buff, + prefix ? prefix : "tmp.", + sizeof(prefix_buff)-7),"XXXXXX") - + prefix_buff); + if (!dir && ! (dir =getenv("TMPDIR"))) + dir=P_tmpdir; + if (strlen(dir)+ pfx_len > FN_REFLEN-2) + { + errno=my_errno= ENAMETOOLONG; + DBUG_RETURN(file); + } + strmov(convert_dirname(to,dir,NullS),prefix_buff); + org_file=mkstemp(to); + if (mode & O_TEMPORARY) + (void) my_delete(to, MYF(MY_WME | ME_NOINPUT)); + 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 | ME_NOINPUT)); + my_errno=tmp; + } + } +#elif defined(HAVE_TEMPNAM) + { +#if !defined(__NETWARE__) + extern char **environ; +#endif + + char *res,**old_env,*temp_env[1]; + if (dir && !dir[0]) + { /* Change empty string to current dir */ + to[0]= FN_CURLIB; + to[1]= 0; + dir=to; + } +#if !defined(__NETWARE__) + old_env= (char**) environ; + if (dir) + { /* Don't use TMPDIR if dir is given */ + environ=(const char**) temp_env; + temp_env[0]=0; + } +#endif + if ((res=tempnam((char*) dir, (char*) prefix))) + { + strmake(to,res,FN_REFLEN-1); + (*free)(res); + file=my_create(to,0, + (int) (O_RDWR | O_BINARY | O_TRUNC | O_EXCL | O_NOFOLLOW | + O_TEMPORARY | O_SHORT_LIVED), + MYF(MY_WME)); + + } + else + { + DBUG_PRINT("error",("Got error: %d from tempnam",errno)); + } +#if !defined(__NETWARE__) + environ=(const char**) old_env; +#endif + } +#else +#error No implementation found for create_temp_file +#endif + if (file >= 0) + thread_safe_increment(my_tmp_file_created,&THR_LOCK_open); + DBUG_RETURN(file); +} diff --git a/externals/mysql/mysys/mf_unixpath.c b/externals/mysql/mysys/mf_unixpath.c new file mode 100644 index 00000000000..75f8de14879 --- /dev/null +++ b/externals/mysql/mysys/mf_unixpath.c @@ -0,0 +1,31 @@ +/* Copyright (C) 2000 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#include "mysys_priv.h" +#include <m_string.h> + + /* convert filename to unix style filename */ + /* If MSDOS converts '\' to '/' */ + +void to_unix_path(char * to __attribute__((unused))) +{ +#if FN_LIBCHAR != '/' + { + to--; + while ((to=strchr(to+1,FN_LIBCHAR)) != 0) + *to='/'; + } +#endif +} diff --git a/externals/mysql/mysys/mf_util.c b/externals/mysql/mysys/mf_util.c new file mode 100644 index 00000000000..248b72b8748 --- /dev/null +++ b/externals/mysql/mysys/mf_util.c @@ -0,0 +1,47 @@ +/* Copyright (C) 2000 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +/* Utilities with are missing on some systems */ + +#include "mysys_priv.h" +#ifdef __ZTC__ +#include <dos.h> +#endif + +#ifdef __ZTC__ + + /* On ZORTECH C we don't have a getpid() call */ + +int getpid(void) +{ + return (int) _psp; +} + +#ifndef M_IC80386 + + /* Define halloc and hfree in as in MSC */ + +void * __CDECL halloc(long count,size_t length) +{ + return (void*) MK_FP(dos_alloc((uint) ((count*length+15) >> 4)),0); +} + +void __CDECL hfree(void *ptr) +{ + dos_free(FP_SEG(ptr)); +} + +#endif /* M_IC80386 */ +#endif /* __ZTC__ */ diff --git a/externals/mysql/mysys/mf_wcomp.c b/externals/mysql/mysys/mf_wcomp.c new file mode 100644 index 00000000000..4786537d1a5 --- /dev/null +++ b/externals/mysql/mysys/mf_wcomp.c @@ -0,0 +1,89 @@ +/* Copyright (C) 2000 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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(register const char *str, register 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/externals/mysql/mysys/mf_wfile.c b/externals/mysql/mysys/mf_wfile.c new file mode 100644 index 00000000000..f98d348994e --- /dev/null +++ b/externals/mysql/mysys/mf_wfile.c @@ -0,0 +1,124 @@ +/* Copyright (C) 2000 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +/* Functions for finding files with wildcards */ + +/* + The following file-name-test is supported: + - "name [[,] name...] ; Matches any of used filenames. + Each name can have "*" and/or "?" + wild-cards. + - [wildspec [,]] !wildspec2 ; File that matches wildspec and not + wildspec2. +*/ + +#include "mysys_priv.h" +#include <m_string.h> + + /* Store wildcard-string in a easyer format */ + +WF_PACK *wf_comp(char * str) +{ + uint ant; + int not_pos; + register char * pos; + char * buffer; + WF_PACK *ret; + DBUG_ENTER("wf_comp"); + + not_pos= -1; /* Skip space and '!' in front */ + while (*str == ' ') + str++; + if (*str == '!') + { + not_pos=0; + while (*++str == ' ') {}; + } + if (*str == 0) /* Empty == everything */ + DBUG_RETURN((WF_PACK *) NULL); + + ant=1; /* Count filespecs */ + for (pos=str ; *pos ; pos++) + ant+= test(*pos == ' ' || *pos == ','); + + if ((ret= (WF_PACK*) my_malloc((uint) ant*(sizeof(char **)+2)+ + sizeof(WF_PACK)+ (uint) strlen(str)+1, + MYF(MY_WME))) + == 0) + DBUG_RETURN((WF_PACK *) NULL); + ret->wild= (char **) (ret+1); + buffer= (char *) (ret->wild+ant); + + ant=0; + for (pos=str ; *pos ; str= pos) + { + ret->wild[ant++]=buffer; + while (*pos != ' ' && *pos != ',' && *pos != '!' && *pos) + *buffer++ = *pos++; + + *buffer++ = '\0'; + while (*pos == ' ' || *pos == ',' || *pos == '!' ) + if (*pos++ == '!' && not_pos <0) + not_pos=(int) ant; + } + + ret->wilds=ant; + if (not_pos <0) + ret->not_pos=ant; + else + ret->not_pos=(uint) not_pos; + + DBUG_PRINT("exit",("antal: %d not_pos: %d",ret->wilds,ret->not_pos)); + DBUG_RETURN(ret); +} /* wf_comp */ + + + /* Test if a given filename is matched */ + +int wf_test(register WF_PACK *wf_pack, register const char *name) +{ + reg2 uint i; + reg3 uint not_pos; + DBUG_ENTER("wf_test"); + + if (! wf_pack || wf_pack->wilds == 0) + DBUG_RETURN(0); /* Everything goes */ + + not_pos=wf_pack->not_pos; + for (i=0 ; i < not_pos; i++) + if (wild_compare(name,wf_pack->wild[i],0) == 0) + goto found; + if (i) + DBUG_RETURN(1); /* No-match */ + +found: +/* Test that it isn't in not-list */ + + for (i=not_pos ; i < wf_pack->wilds; i++) + if (wild_compare(name,wf_pack->wild[i],0) == 0) + DBUG_RETURN(1); + DBUG_RETURN(0); +} /* wf_test */ + + + /* We need this because program don't know with malloc we used */ + +void wf_end(WF_PACK *buffer) +{ + DBUG_ENTER("wf_end"); + if (buffer) + my_free((uchar*) buffer,MYF(0)); + DBUG_VOID_RETURN; +} /* wf_end */ diff --git a/externals/mysql/mysys/mulalloc.c b/externals/mysql/mysys/mulalloc.c new file mode 100644 index 00000000000..f4ca3d9f9ab --- /dev/null +++ b/externals/mysql/mysys/mulalloc.c @@ -0,0 +1,63 @@ +/* Copyright (C) 2000 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#include "mysys_priv.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(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(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/externals/mysql/mysys/my_access.c b/externals/mysql/mysys/my_access.c new file mode 100644 index 00000000000..210946d50a8 --- /dev/null +++ b/externals/mysql/mysys/my_access.c @@ -0,0 +1,201 @@ +/* Copyright (C) 2000 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#include "mysys_priv.h" +#include <m_string.h> + +#ifdef __WIN__ + +/* + Check a file or path for accessability. + + SYNOPSIS + file_access() + path Path to file + amode Access method + + DESCRIPTION + This function wraps the normal access method because the access + available in MSVCRT> +reports that filenames such as LPT1 and + COM1 are valid (they are but should not be so for us). + + RETURN VALUES + 0 ok + -1 error (We use -1 as my_access is mapped to access on other platforms) +*/ + +int my_access(const char *path, int amode) +{ + WIN32_FILE_ATTRIBUTE_DATA fileinfo; + BOOL result; + + result= GetFileAttributesEx(path, GetFileExInfoStandard, &fileinfo); + if (! result || + (fileinfo.dwFileAttributes & FILE_ATTRIBUTE_READONLY) && (amode & W_OK)) + { + my_errno= errno= EACCES; + return -1; + } + return 0; +} + +#endif /* __WIN__ */ + + +/* + 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((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)); +} + + +#if defined(__WIN__) || defined(__EMX__) + + +/* + Check if a path will access a reserverd file name that may cause problems + + SYNOPSIS + check_if_legal_filename + path Path to file + + RETURN + 0 ok + 1 reserved file name +*/ + +int check_if_legal_filename(const char *path) +{ + const char *end; + const char **reserved_name; + DBUG_ENTER("check_if_legal_filename"); + + path+= dirname_length(path); /* To start of filename */ + if (!(end= strchr(path, FN_EXTCHAR))) + end= strend(path); + if (path == end || (uint) (end - path) > MAX_RESERVED_NAME_LENGTH) + DBUG_RETURN(0); /* Simplify inner loop */ + + for (reserved_name= reserved_names; *reserved_name; reserved_name++) + { + const char *reserved= *reserved_name; /* never empty */ + const char *name= path; + + do + { + if (*reserved != my_toupper(&my_charset_latin1, *name)) + break; + if (++name == end && !reserved[1]) + DBUG_RETURN(1); /* Found wrong path */ + } while (*++reserved); + } + DBUG_RETURN(0); +} + +#endif /* defined(__WIN__) || defined(__EMX__) */ diff --git a/externals/mysql/mysys/my_aes.c b/externals/mysql/mysys/my_aes.c new file mode 100644 index 00000000000..575d4702dee --- /dev/null +++ b/externals/mysql/mysys/my_aes.c @@ -0,0 +1,227 @@ +/* Copyright (C) 2002 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + + +/* + Implementation of AES Encryption for MySQL + Initial version by Peter Zaitsev June 2002 +*/ + + +#include <my_global.h> +#include <m_string.h> +#include "my_aes.h" + +enum encrypt_dir { AES_ENCRYPT, AES_DECRYPT }; + +#define AES_BLOCK_SIZE 16 /* Block size in bytes */ + +#define AES_BAD_DATA -1 /* If bad data discovered during decoding */ + + +/* The structure for key information */ +typedef struct { + int nr; /* Number of rounds */ + uint32 rk[4*(AES_MAXNR + 1)]; /* key schedule */ +} KEYINSTANCE; + + +/* + This is internal function just keeps joint code of Key generation + + SYNOPSIS + my_aes_create_key() + aes_key Address of Key Instance to be created + direction Direction (are we encoding or decoding) + key Key to use for real key creation + key_length Length of the key + + DESCRIPTION + + RESULT + 0 ok + -1 Error Note: The current impementation never returns this +*/ + +static int my_aes_create_key(KEYINSTANCE *aes_key, + enum encrypt_dir direction, const char *key, + int key_length) +{ + uint8 rkey[AES_KEY_LENGTH/8]; /* The real key to be used for encryption */ + uint8 *rkey_end=rkey+AES_KEY_LENGTH/8; /* Real key boundary */ + uint8 *ptr; /* Start of the real key*/ + const char *sptr; /* Start of the working key */ + const char *key_end=key+key_length; /* Working key boundary*/ + + bzero((char*) rkey,AES_KEY_LENGTH/8); /* Set initial key */ + + for (ptr= rkey, sptr= key; sptr < key_end; ptr++,sptr++) + { + if (ptr == rkey_end) + ptr= rkey; /* Just loop over tmp_key until we used all key */ + *ptr^= (uint8) *sptr; + } +#ifdef AES_USE_KEY_BITS + /* + This block is intended to allow more weak encryption if application + build with libmysqld needs to correspond to export regulations + It should be never used in normal distribution as does not give + any speed improvement. + To get worse security define AES_USE_KEY_BITS to number of bits + you want key to be. It should be divisible by 8 + + WARNING: Changing this value results in changing of enryption for + all key lengths so altering this value will result in impossibility + to decrypt data encrypted with previous value + */ +#define AES_USE_KEY_BYTES (AES_USE_KEY_BITS/8) + /* + To get weaker key we use first AES_USE_KEY_BYTES bytes of created key + and cyclically copy them until we created all required key length + */ + for (ptr= rkey+AES_USE_KEY_BYTES, sptr=rkey ; ptr < rkey_end; + ptr++,sptr++) + { + if (sptr == rkey+AES_USE_KEY_BYTES) + sptr=rkey; + *ptr=*sptr; + } +#endif + if (direction == AES_DECRYPT) + aes_key->nr = rijndaelKeySetupDec(aes_key->rk, rkey, AES_KEY_LENGTH); + else + aes_key->nr = rijndaelKeySetupEnc(aes_key->rk, rkey, AES_KEY_LENGTH); + return 0; +} + + +/* + Crypt buffer with AES encryption algorithm. + + SYNOPSIS + my_aes_encrypt() + source Pointer to data for encryption + source_length Size of encryption data + dest Buffer to place encrypted data (must be large enough) + key Key to be used for encryption + key_length Length of the key. Will handle keys of any length + + RETURN + >= 0 Size of encrypted data + < 0 Error +*/ + +int my_aes_encrypt(const char* source, int source_length, char* dest, + const char* key, int key_length) +{ + KEYINSTANCE aes_key; + uint8 block[AES_BLOCK_SIZE]; /* 128 bit block used for padding */ + int rc; /* result codes */ + int num_blocks; /* number of complete blocks */ + char pad_len; /* pad size for the last block */ + int i; + + if ((rc= my_aes_create_key(&aes_key,AES_ENCRYPT,key,key_length))) + return rc; + + num_blocks = source_length/AES_BLOCK_SIZE; + + for (i = num_blocks; i > 0; i--) /* Encode complete blocks */ + { + rijndaelEncrypt(aes_key.rk, aes_key.nr, (const uint8*) source, + (uint8*) dest); + source+= AES_BLOCK_SIZE; + dest+= AES_BLOCK_SIZE; + } + + /* Encode the rest. We always have incomplete block */ + pad_len = AES_BLOCK_SIZE - (source_length - AES_BLOCK_SIZE*num_blocks); + memcpy(block, source, 16 - pad_len); + bfill(block + AES_BLOCK_SIZE - pad_len, pad_len, pad_len); + rijndaelEncrypt(aes_key.rk, aes_key.nr, block, (uint8*) dest); + return AES_BLOCK_SIZE*(num_blocks + 1); +} + + +/* + DeCrypt buffer with AES encryption algorithm. + + SYNOPSIS + my_aes_decrypt() + source Pointer to data for decryption + source_length Size of encrypted data + dest Buffer to place decrypted data (must be large enough) + key Key to be used for decryption + key_length Length of the key. Will handle keys of any length + + RETURN + >= 0 Size of encrypted data + < 0 Error +*/ + +int my_aes_decrypt(const char *source, int source_length, char *dest, + const char *key, int key_length) +{ + KEYINSTANCE aes_key; + uint8 block[AES_BLOCK_SIZE]; /* 128 bit block used for padding */ + int rc; /* Result codes */ + int num_blocks; /* Number of complete blocks */ + uint pad_len; /* Pad size for the last block */ + int i; + + if ((rc=my_aes_create_key(&aes_key,AES_DECRYPT,key,key_length))) + return rc; + + num_blocks = source_length/AES_BLOCK_SIZE; + + if ((source_length != num_blocks*AES_BLOCK_SIZE) || num_blocks ==0 ) + return AES_BAD_DATA; /* Input size has to be even and at least one block */ + + for (i = num_blocks-1; i > 0; i--) /* Decode all but last blocks */ + { + rijndaelDecrypt(aes_key.rk, aes_key.nr, (const uint8*) source, + (uint8*) dest); + source+= AES_BLOCK_SIZE; + dest+= AES_BLOCK_SIZE; + } + + rijndaelDecrypt(aes_key.rk, aes_key.nr, (const uint8*) source, block); + /* Use last char in the block as size */ + pad_len = (uint) (uchar) block[AES_BLOCK_SIZE-1]; + + if (pad_len > AES_BLOCK_SIZE) + return AES_BAD_DATA; + /* We could also check whole padding but we do not really need this */ + + memcpy(dest, block, AES_BLOCK_SIZE - pad_len); + return AES_BLOCK_SIZE*num_blocks - pad_len; +} + + +/* + Get size of buffer which will be large enough for encrypted data + + SYNOPSIS + my_aes_get_size() + source_length Length of data to be encrypted + + RETURN + Size of buffer required to store encrypted data +*/ + +int my_aes_get_size(int source_length) +{ + return AES_BLOCK_SIZE*(source_length/AES_BLOCK_SIZE)+AES_BLOCK_SIZE; +} diff --git a/externals/mysql/mysys/my_alarm.c b/externals/mysql/mysys/my_alarm.c new file mode 100644 index 00000000000..d6a0da1bd13 --- /dev/null +++ b/externals/mysql/mysys/my_alarm.c @@ -0,0 +1,32 @@ +/* Copyright (C) 2000 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +/* Function to set a varible when we got a alarm */ +/* Used by my_lock samt functions in m_alarm.h */ + + +#include "mysys_priv.h" +#include "my_alarm.h" + +#ifdef HAVE_ALARM + + /* ARGSUSED */ +sig_handler my_set_alarm_variable(int signo __attribute__((unused))) +{ + my_have_got_alarm=1; /* Tell program that time expired */ + return; +} + +#endif /* HAVE_ALARM */ diff --git a/externals/mysql/mysys/my_alloc.c b/externals/mysql/mysys/my_alloc.c new file mode 100644 index 00000000000..80add2f4936 --- /dev/null +++ b/externals/mysql/mysys/my_alloc.c @@ -0,0 +1,420 @@ +/* Copyright (C) 2000 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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> +#undef EXTRA_DEBUG +#define EXTRA_DEBUG + + +/* + Initialize memory root + + SYNOPSIS + init_alloc_root() + mem_root - memory root to initialize + block_size - size of chunks (blocks) used for memory allocation + (It is external size of chunk i.e. it should include + memory required for internal structures, thus it + should be no less than ALLOC_ROOT_MIN_BLOCK_SIZE) + pre_alloc_size - if non-0, then size of block that should be + pre-allocated during memory root initialization. + + DESCRIPTION + This function prepares memory root for further use, sets initial size of + chunk for memory allocation and pre-allocates first block if specified. + Altough error can happen during execution of this function if + pre_alloc_size is non-0 it won't be reported. Instead it will be + reported as error in first alloc_root() on this memory root. +*/ + +void init_alloc_root(MEM_ROOT *mem_root, size_t block_size, + size_t pre_alloc_size __attribute__((unused))) +{ + DBUG_ENTER("init_alloc_root"); + DBUG_PRINT("enter",("root: %p", 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; + +#if !(defined(HAVE_purify) && defined(EXTRA_DEBUG)) + if (pre_alloc_size) + { + if ((mem_root->free= mem_root->pre_alloc= + (USED_MEM*) my_malloc(pre_alloc_size+ ALIGN_SIZE(sizeof(USED_MEM)), + MYF(0)))) + { + mem_root->free->size= pre_alloc_size+ALIGN_SIZE(sizeof(USED_MEM)); + mem_root->free->left= pre_alloc_size; + mem_root->free->next= 0; + } + } +#endif + DBUG_VOID_RETURN; +} + + +/* + SYNOPSIS + reset_root_defaults() + mem_root memory root to change defaults of + block_size new value of block size. Must be greater 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 __attribute__((unused))) +{ + DBUG_ASSERT(alloc_root_inited(mem_root)); + + mem_root->block_size= block_size - ALLOC_ROOT_MIN_BLOCK_SIZE; +#if !(defined(HAVE_purify) && defined(EXTRA_DEBUG)) + if (pre_alloc_size) + { + 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 == 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; + my_free(mem, MYF(0)); + } + else + prev= &mem->next; + } + /* Allocate new prealloc block and add it to the end of free list */ + if ((mem= (USED_MEM *) my_malloc(size, MYF(0)))) + { + mem->size= size; + mem->left= pre_alloc_size; + mem->next= *prev; + *prev= mem_root->pre_alloc= mem; + } + else + { + mem_root->pre_alloc= 0; + } + } + } + else +#endif + mem_root->pre_alloc= 0; +} + + +void *alloc_root(MEM_ROOT *mem_root, size_t length) +{ +#if defined(HAVE_purify) && defined(EXTRA_DEBUG) + reg1 USED_MEM *next; + DBUG_ENTER("alloc_root"); + DBUG_PRINT("enter",("root: %p", mem_root)); + + DBUG_ASSERT(alloc_root_inited(mem_root)); + + length+=ALIGN_SIZE(sizeof(USED_MEM)); + if (!(next = (USED_MEM*) my_malloc(length,MYF(MY_WME | ME_FATALERROR)))) + { + if (mem_root->error_handler) + (*mem_root->error_handler)(); + DBUG_RETURN((uchar*) 0); /* purecov: inspected */ + } + next->next= mem_root->used; + next->size= length; + mem_root->used= next; + DBUG_PRINT("exit",("ptr: %p", (((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; + reg1 USED_MEM *next= 0; + reg2 USED_MEM **prev; + DBUG_ENTER("alloc_root"); + DBUG_PRINT("enter",("root: %p", mem_root)); + DBUG_ASSERT(alloc_root_inited(mem_root)); + + 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= max(get_size, block_size); + + if (!(next = (USED_MEM*) my_malloc(get_size,MYF(MY_WME | ME_FATALERROR)))) + { + if (mem_root->error_handler) + (*mem_root->error_handler)(); + DBUG_RETURN((void*) 0); /* purecov: inspected */ + } + mem_root->block_num++; + next->next= *prev; + next->size= get_size; + next->left= 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-= 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: %p", 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); +} + +#define TRASH_MEM(X) TRASH(((char*)(X) + ((X)->size-(X)->left)), (X)->left) + +/* Mark all data in blocks free for reusage */ + +static inline void mark_blocks_free(MEM_ROOT* root) +{ + reg1 USED_MEM *next; + reg2 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 - 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 - 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; +} + + +/* + Deallocate everything used by alloc_root or just move + used blocks to free list if called with MY_USED_TO_FREE + + SYNOPSIS + free_root() + root Memory root + MyFlags Flags for what should be freed: + + MY_MARK_BLOCKS_FREED Don't free blocks, just mark them free + MY_KEEP_PREALLOC If this is not set, then free also the + preallocated block + + NOTES + One can call this function either with root block initialised with + init_alloc_root() or with a bzero()-ed block. + It's also safe to call this multiple times with the same mem_root. +*/ + +void free_root(MEM_ROOT *root, myf MyFlags) +{ + reg1 USED_MEM *next,*old; + DBUG_ENTER("free_root"); + DBUG_PRINT("enter",("root: %p flags: %u", 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) + my_free(old,MYF(0)); + } + for (next=root->free ; next ;) + { + old=next; next= next->next; + if (old != root->pre_alloc) + my_free(old,MYF(0)); + } + root->used=root->free=0; + if (root->pre_alloc) + { + root->free=root->pre_alloc; + root->free->left=root->pre_alloc->size-ALIGN_SIZE(sizeof(USED_MEM)); + TRASH_MEM(root->pre_alloc); + root->free->next=0; + } + root->block_num= 4; + root->first_block_usage= 0; + DBUG_VOID_RETURN; +} + +/* + Find block that contains an object and set the pre_alloc to it +*/ + +void set_prealloc_root(MEM_ROOT *root, char *ptr) +{ + USED_MEM *next; + for (next=root->used; next ; next=next->next) + { + if ((char*) next <= ptr && (char*) next + next->size > ptr) + { + root->pre_alloc=next; + return; + } + } + for (next=root->free ; next ; next=next->next) + { + if ((char*) next <= ptr && (char*) next + next->size > ptr) + { + root->pre_alloc=next; + 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; +} diff --git a/externals/mysql/mysys/my_append.c b/externals/mysql/mysys/my_append.c new file mode 100644 index 00000000000..d410df1296f --- /dev/null +++ b/externals/mysql/mysys/my_append.c @@ -0,0 +1,64 @@ +/* Copyright (C) 2000 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#include "mysys_priv.h" +#include <sys/stat.h> +#include <m_string.h> +#if defined(HAVE_UTIME_H) +#include <utime.h> +#elif defined(HAVE_SYS_UTIME_H) +#include <sys/utime.h> +#elif !defined(HPUX10) +struct utimbuf { + time_t actime; + time_t modtime; +}; +#endif + +/* + Append a file to another + + NOTES + Don't set MY_FNABP or MY_NABP bits on when calling this function +*/ + +int my_append(const char *from, const char *to, myf MyFlags) +{ + uint Count; + File from_file,to_file; + uchar buff[IO_SIZE]; + DBUG_ENTER("my_append"); + DBUG_PRINT("my",("from %s to %s MyFlags %d", from, to, MyFlags)); + + from_file= to_file= -1; + + if ((from_file=my_open(from,O_RDONLY,MyFlags)) >= 0) + { + if ((to_file=my_open(to,O_APPEND | O_WRONLY,MyFlags)) >= 0) + { + while ((Count=my_read(from_file,buff,IO_SIZE,MyFlags)) != 0) + if (Count == (uint) -1 || + my_write(to_file,buff,Count,MYF(MyFlags | MY_NABP))) + goto err; + if (my_close(from_file,MyFlags) | my_close(to_file,MyFlags)) + DBUG_RETURN(-1); /* Error on close */ + DBUG_RETURN(0); + } + } +err: + if (from_file >= 0) (void) my_close(from_file,MyFlags); + if (to_file >= 0) (void) my_close(to_file,MyFlags); + DBUG_RETURN(-1); +} diff --git a/externals/mysql/mysys/my_atomic.c b/externals/mysql/mysys/my_atomic.c new file mode 100644 index 00000000000..df6490b5770 --- /dev/null +++ b/externals/mysql/mysys/my_atomic.c @@ -0,0 +1,45 @@ +/* Copyright (C) 2006 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#include <my_global.h> +#include <my_sys.h> + +#ifndef HAVE_INLINE +/* the following will cause all inline functions to be instantiated */ +#define HAVE_INLINE +#undef STATIC_INLINE +#define STATIC_INLINE extern +#endif + +#include <my_atomic.h> + +/* + checks that the current build of atomic ops + can run on this machine + + RETURN + ATOMIC_xxx values, see my_atomic.h +*/ +int my_atomic_initialize() +{ + compile_time_assert(sizeof(intptr) == sizeof(void *)); + /* currently the only thing worth checking is SMP/UP issue */ +#ifdef MY_ATOMIC_MODE_DUMMY + return my_getncpus() == 1 ? MY_ATOMIC_OK : MY_ATOMIC_NOT_1CPU; +#else + return MY_ATOMIC_OK; +#endif +} + diff --git a/externals/mysql/mysys/my_bit.c b/externals/mysql/mysys/my_bit.c new file mode 100644 index 00000000000..620d1b8ba50 --- /dev/null +++ b/externals/mysql/mysys/my_bit.c @@ -0,0 +1,70 @@ +/* Copyright (C) 2000 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#include <my_global.h> + +#ifndef HAVE_INLINE +/* the following will cause all inline functions to be instantiated */ +#define HAVE_INLINE +#undef STATIC_INLINE +#define STATIC_INLINE extern +#endif + +#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/externals/mysql/mysys/my_bitmap.c b/externals/mysql/mysys/my_bitmap.c new file mode 100644 index 00000000000..d6cf5d4c517 --- /dev/null +++ b/externals/mysql/mysys/my_bitmap.c @@ -0,0 +1,1126 @@ +/* Copyright (C) 2000 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 + * there are THREAD safe versions of most calls called bitmap_lock_* + many of those are not used and not compiled normally but the code + already exist for them in an #ifdef:ed part. These can only be used + if THREAD was specified in bitmap_init + + 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_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; + + map->last_word_ptr= 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 __attribute__((unused))) +{ +#ifdef THREAD + if (map->mutex) + pthread_mutex_lock(map->mutex); +#endif +} + +static inline void bitmap_unlock(MY_BITMAP *map __attribute__((unused))) +{ +#ifdef THREAD + if (map->mutex) + pthread_mutex_unlock(map->mutex); +#endif +} + + +my_bool bitmap_init(MY_BITMAP *map, my_bitmap_map *buf, uint n_bits, + my_bool thread_safe __attribute__((unused))) +{ + DBUG_ENTER("bitmap_init"); + if (!buf) + { + uint size_in_bytes= bitmap_buffer_size(n_bits); + uint extra= 0; +#ifdef THREAD + if (thread_safe) + { + size_in_bytes= ALIGN_SIZE(size_in_bytes); + extra= sizeof(pthread_mutex_t); + } + map->mutex= 0; +#endif + if (!(buf= (my_bitmap_map*) my_malloc(size_in_bytes+extra, MYF(MY_WME)))) + DBUG_RETURN(1); +#ifdef THREAD + if (thread_safe) + { + map->mutex= (pthread_mutex_t *) ((char*) buf + size_in_bytes); + pthread_mutex_init(map->mutex, MY_MUTEX_INIT_FAST); + } +#endif + } +#ifdef THREAD + else + { + DBUG_ASSERT(thread_safe == 0); + } +#endif + + 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) + { +#ifdef THREAD + if (map->mutex) + pthread_mutex_destroy(map->mutex); +#endif + my_free((char*) map->bitmap, MYF(0)); + 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; +} + + +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; + if ((d= no_bytes_in_map(map)-prefix_bytes)) + bzero(m, d); +} + + +my_bool bitmap_is_prefix(const MY_BITMAP *map, uint prefix_size) +{ + uint prefix_bits= prefix_size & 0x7, res; + uchar *m= (uchar*)map->bitmap; + uchar *end_prefix= m+prefix_size/8; + uchar *end; + DBUG_ASSERT(m && prefix_size <= map->n_bits); + end= m+no_bytes_in_map(map); + + while (m < end_prefix) + if (*m++ != 0xff) + return 0; + + *map->last_word_ptr&= ~map->last_word_mask; /*Clear bits*/ + res= 0; + if (prefix_bits && *m++ != (1 << prefix_bits)-1) + goto ret; + + while (m < end) + if (*m++ != 0) + goto ret; + res= 1; +ret: + return res; +} + + +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; + *map->last_word_ptr |= map->last_word_mask; + for (; data_ptr <= end; data_ptr++) + if (*data_ptr != 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; + if (*map->last_word_ptr & ~map->last_word_mask) + return FALSE; + end= map->last_word_ptr; + for (; data_ptr < end; data_ptr++) + if (*data_ptr) + 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; + *map1->last_word_ptr &= ~map1->last_word_mask; + *map2->last_word_ptr &= ~map2->last_word_mask; + while (m1 <= end) + { + if ((*m1++) & ~(*m2++)) + return 0; + } + return 1; +} + +/* 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); + + end= map1->last_word_ptr; + *map1->last_word_ptr &= ~map1->last_word_mask; + *map2->last_word_ptr &= ~map2->last_word_mask; + while (m1 <= end) + { + if ((*m1++) & (*m2++)) + return 1; + } + return 0; +} + + +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+min(len,len2); + *map2->last_word_ptr&= ~map2->last_word_mask; /*Clear last bits in map2*/ + while (to < end) + *to++ &= *from++; + + if (len2 < len) + { + end+=len-len2; + while (to < end) + *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; + + while (to < end) + *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); + + end= map->last_word_ptr; + + while (to <= end) + *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); + end= map->last_word_ptr; + + while (to <= end) + *to++ |= *from++; +} + + +void bitmap_xor(MY_BITMAP *map, const MY_BITMAP *map2) +{ + my_bitmap_map *to= map->bitmap, *from= map2->bitmap, *end= map->last_word_ptr; + DBUG_ASSERT(map->bitmap && map2->bitmap && + map->n_bits==map2->n_bits); + while (to <= end) + *to++ ^= *from++; +} + + +void bitmap_invert(MY_BITMAP *map) +{ + my_bitmap_map *to= map->bitmap, *end; + + DBUG_ASSERT(map->bitmap); + end= map->last_word_ptr; + + while (to <= end) + *to++ ^= 0xFFFFFFFF; +} + + +uint bitmap_bits_set(const MY_BITMAP *map) +{ + uchar *m= (uchar*)map->bitmap; + uchar *end= m + no_bytes_in_map(map); + uint res= 0; + + DBUG_ASSERT(map->bitmap); + *map->last_word_ptr&= ~map->last_word_mask; /*Reset last bits to zero*/ + while (m < end) + res+= my_count_bits_ushort(*m++); + 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); + end= map->last_word_ptr; + while (to <= end) + *to++ = *from++; +} + + +uint bitmap_get_first_set(const MY_BITMAP *map) +{ + uchar *byte_ptr; + uint i,j,k; + my_bitmap_map *data_ptr, *end= map->last_word_ptr; + + DBUG_ASSERT(map->bitmap); + data_ptr= map->bitmap; + *map->last_word_ptr &= ~map->last_word_mask; + + for (i=0; data_ptr <= end; data_ptr++, i++) + { + if (*data_ptr) + { + byte_ptr= (uchar*)data_ptr; + for (j=0; ; j++, byte_ptr++) + { + if (*byte_ptr) + { + for (k=0; ; k++) + { + if (*byte_ptr & (1 << k)) + return (i*32) + (j*8) + k; + } + } + } + } + } + return MY_BIT_NONE; +} + + +uint bitmap_get_first(const MY_BITMAP *map) +{ + uchar *byte_ptr; + uint i,j,k; + my_bitmap_map *data_ptr, *end= map->last_word_ptr; + + DBUG_ASSERT(map->bitmap); + data_ptr= map->bitmap; + *map->last_word_ptr|= map->last_word_mask; + + for (i=0; data_ptr <= end; data_ptr++, i++) + { + if (*data_ptr != 0xFFFFFFFF) + { + byte_ptr= (uchar*)data_ptr; + for (j=0; ; j++, byte_ptr++) + { + if (*byte_ptr != 0xFF) + { + for (k=0; ; k++) + { + if (!(*byte_ptr & (1 << k))) + return (i*32) + (j*8) + k; + } + } + } + } + } + return MY_BIT_NONE; +} + + +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); +} + + +#ifdef NOT_USED +my_bool bitmap_lock_is_prefix(const MY_BITMAP *map, uint prefix_size) +{ + my_bool res; + bitmap_lock((MY_BITMAP *)map); + res= bitmap_is_prefix(map, prefix_size); + bitmap_unlock((MY_BITMAP *)map); + return res; +} + + +void bitmap_lock_set_all(MY_BITMAP *map) +{ + bitmap_lock(map); + bitmap_set_all(map); + bitmap_unlock(map); +} + + +void bitmap_lock_clear_all(MY_BITMAP *map) +{ + bitmap_lock(map); + bitmap_clear_all(map); + bitmap_unlock(map); +} + + +void bitmap_lock_set_prefix(MY_BITMAP *map, uint prefix_size) +{ + bitmap_lock(map); + bitmap_set_prefix(map, prefix_size); + bitmap_unlock(map); +} + + +my_bool bitmap_lock_is_clear_all(const MY_BITMAP *map) +{ + uint res; + bitmap_lock((MY_BITMAP *)map); + res= bitmap_is_clear_all(map); + bitmap_unlock((MY_BITMAP *)map); + return res; +} + + +my_bool bitmap_lock_is_set_all(const MY_BITMAP *map) +{ + uint res; + bitmap_lock((MY_BITMAP *)map); + res= bitmap_is_set_all(map); + bitmap_unlock((MY_BITMAP *)map); + return res; +} + + +my_bool bitmap_lock_is_set(const MY_BITMAP *map, uint bitmap_bit) +{ + my_bool res; + DBUG_ASSERT(map->bitmap && bitmap_bit < map->n_bits); + bitmap_lock((MY_BITMAP *)map); + res= bitmap_is_set(map, bitmap_bit); + bitmap_unlock((MY_BITMAP *)map); + return res; +} + + +my_bool bitmap_lock_is_subset(const MY_BITMAP *map1, const MY_BITMAP *map2) +{ + uint res; + bitmap_lock((MY_BITMAP *)map1); + bitmap_lock((MY_BITMAP *)map2); + res= bitmap_is_subset(map1, map2); + bitmap_unlock((MY_BITMAP *)map2); + bitmap_unlock((MY_BITMAP *)map1); + return res; +} + + +my_bool bitmap_lock_cmp(const MY_BITMAP *map1, const MY_BITMAP *map2) +{ + uint res; + + DBUG_ASSERT(map1->bitmap && map2->bitmap && + map1->n_bits==map2->n_bits); + bitmap_lock((MY_BITMAP *)map1); + bitmap_lock((MY_BITMAP *)map2); + res= bitmap_cmp(map1, map2); + bitmap_unlock((MY_BITMAP *)map2); + bitmap_unlock((MY_BITMAP *)map1); + return res; +} + + +void bitmap_lock_intersect(MY_BITMAP *map, const MY_BITMAP *map2) +{ + bitmap_lock(map); + bitmap_lock((MY_BITMAP *)map2); + bitmap_intersect(map, map2); + bitmap_unlock((MY_BITMAP *)map2); + bitmap_unlock(map); +} + + +void bitmap_lock_subtract(MY_BITMAP *map, const MY_BITMAP *map2) +{ + bitmap_lock(map); + bitmap_lock((MY_BITMAP *)map2); + bitmap_subtract(map, map2); + bitmap_unlock((MY_BITMAP *)map2); + bitmap_unlock(map); +} + + +void bitmap_lock_union(MY_BITMAP *map, const MY_BITMAP *map2) +{ + bitmap_lock(map); + bitmap_lock((MY_BITMAP *)map2); + bitmap_union(map, map2); + bitmap_unlock((MY_BITMAP *)map2); + bitmap_unlock(map); +} + + +/* + SYNOPSIS + bitmap_bits_set() + map + RETURN + Number of set bits in the bitmap. +*/ +uint bitmap_lock_bits_set(const MY_BITMAP *map) +{ + uint res; + bitmap_lock((MY_BITMAP *)map); + DBUG_ASSERT(map->bitmap); + res= bitmap_bits_set(map); + bitmap_unlock((MY_BITMAP *)map); + return res; +} + + +/* + SYNOPSIS + bitmap_get_first() + map + RETURN + Number of first unset bit in the bitmap or MY_BIT_NONE if all bits are set. +*/ +uint bitmap_lock_get_first(const MY_BITMAP *map) +{ + uint res; + bitmap_lock((MY_BITMAP*)map); + res= bitmap_get_first(map); + bitmap_unlock((MY_BITMAP*)map); + return res; +} + + +uint bitmap_lock_get_first_set(const MY_BITMAP *map) +{ + uint res; + bitmap_lock((MY_BITMAP*)map); + res= bitmap_get_first_set(map); + bitmap_unlock((MY_BITMAP*)map); + return res; +} + + +void bitmap_lock_set_bit(MY_BITMAP *map, uint bitmap_bit) +{ + DBUG_ASSERT(map->bitmap && bitmap_bit < map->n_bits); + bitmap_lock(map); + bitmap_set_bit(map, bitmap_bit); + bitmap_unlock(map); +} + + +void bitmap_lock_flip_bit(MY_BITMAP *map, uint bitmap_bit) +{ + DBUG_ASSERT(map->bitmap && bitmap_bit < map->n_bits); + bitmap_lock(map); + bitmap_flip_bit(map, bitmap_bit); + bitmap_unlock(map); +} +#endif +#ifdef MAIN + +uint get_rand_bit(uint bitsize) +{ + return (rand() % bitsize); +} + +my_bool test_set_get_clear_bit(MY_BITMAP *map, uint bitsize) +{ + uint i, test_bit; + uint no_loops= bitsize > 128 ? 128 : bitsize; + for (i=0; i < no_loops; i++) + { + test_bit= get_rand_bit(bitsize); + bitmap_set_bit(map, test_bit); + if (!bitmap_is_set(map, test_bit)) + goto error1; + bitmap_clear_bit(map, test_bit); + if (bitmap_is_set(map, test_bit)) + goto error2; + } + return FALSE; +error1: + printf("Error in set bit, bit %u, bitsize = %u", test_bit, bitsize); + return TRUE; +error2: + printf("Error in clear bit, bit %u, bitsize = %u", test_bit, bitsize); + return TRUE; +} + +my_bool test_flip_bit(MY_BITMAP *map, uint bitsize) +{ + uint i, test_bit; + uint no_loops= bitsize > 128 ? 128 : bitsize; + for (i=0; i < no_loops; i++) + { + test_bit= get_rand_bit(bitsize); + bitmap_flip_bit(map, test_bit); + if (!bitmap_is_set(map, test_bit)) + goto error1; + bitmap_flip_bit(map, test_bit); + if (bitmap_is_set(map, test_bit)) + goto error2; + } + return FALSE; +error1: + printf("Error in flip bit 1, bit %u, bitsize = %u", test_bit, bitsize); + return TRUE; +error2: + printf("Error in flip bit 2, bit %u, bitsize = %u", test_bit, bitsize); + return TRUE; +} + +my_bool test_operators(MY_BITMAP *map __attribute__((unused)), + uint bitsize __attribute__((unused))) +{ + return FALSE; +} + +my_bool test_get_all_bits(MY_BITMAP *map, uint bitsize) +{ + uint i; + bitmap_set_all(map); + if (!bitmap_is_set_all(map)) + goto error1; + if (!bitmap_is_prefix(map, bitsize)) + goto error5; + bitmap_clear_all(map); + if (!bitmap_is_clear_all(map)) + goto error2; + if (!bitmap_is_prefix(map, 0)) + goto error6; + for (i=0; i<bitsize;i++) + bitmap_set_bit(map, i); + if (!bitmap_is_set_all(map)) + goto error3; + for (i=0; i<bitsize;i++) + bitmap_clear_bit(map, i); + if (!bitmap_is_clear_all(map)) + goto error4; + return FALSE; +error1: + printf("Error in set_all, bitsize = %u", bitsize); + return TRUE; +error2: + printf("Error in clear_all, bitsize = %u", bitsize); + return TRUE; +error3: + printf("Error in bitmap_is_set_all, bitsize = %u", bitsize); + return TRUE; +error4: + printf("Error in bitmap_is_clear_all, bitsize = %u", bitsize); + return TRUE; +error5: + printf("Error in set_all through set_prefix, bitsize = %u", bitsize); + return TRUE; +error6: + printf("Error in clear_all through set_prefix, bitsize = %u", bitsize); + return TRUE; +} + +my_bool test_compare_operators(MY_BITMAP *map, uint bitsize) +{ + uint i, j, test_bit1, test_bit2, test_bit3,test_bit4; + uint no_loops= bitsize > 128 ? 128 : bitsize; + MY_BITMAP map2_obj, map3_obj; + MY_BITMAP *map2= &map2_obj, *map3= &map3_obj; + my_bitmap_map map2buf[1024]; + my_bitmap_map map3buf[1024]; + bitmap_init(&map2_obj, map2buf, bitsize, FALSE); + bitmap_init(&map3_obj, map3buf, bitsize, FALSE); + bitmap_clear_all(map2); + bitmap_clear_all(map3); + for (i=0; i < no_loops; i++) + { + test_bit1=get_rand_bit(bitsize); + bitmap_set_prefix(map, test_bit1); + test_bit2=get_rand_bit(bitsize); + bitmap_set_prefix(map2, test_bit2); + bitmap_intersect(map, map2); + test_bit3= test_bit2 < test_bit1 ? test_bit2 : test_bit1; + bitmap_set_prefix(map3, test_bit3); + if (!bitmap_cmp(map, map3)) + goto error1; + bitmap_clear_all(map); + bitmap_clear_all(map2); + bitmap_clear_all(map3); + test_bit1=get_rand_bit(bitsize); + test_bit2=get_rand_bit(bitsize); + test_bit3=get_rand_bit(bitsize); + bitmap_set_prefix(map, test_bit1); + bitmap_set_prefix(map2, test_bit2); + test_bit3= test_bit2 > test_bit1 ? test_bit2 : test_bit1; + bitmap_set_prefix(map3, test_bit3); + bitmap_union(map, map2); + if (!bitmap_cmp(map, map3)) + goto error2; + bitmap_clear_all(map); + bitmap_clear_all(map2); + bitmap_clear_all(map3); + test_bit1=get_rand_bit(bitsize); + test_bit2=get_rand_bit(bitsize); + test_bit3=get_rand_bit(bitsize); + bitmap_set_prefix(map, test_bit1); + bitmap_set_prefix(map2, test_bit2); + bitmap_xor(map, map2); + test_bit3= test_bit2 > test_bit1 ? test_bit2 : test_bit1; + test_bit4= test_bit2 < test_bit1 ? test_bit2 : test_bit1; + bitmap_set_prefix(map3, test_bit3); + for (j=0; j < test_bit4; j++) + bitmap_clear_bit(map3, j); + if (!bitmap_cmp(map, map3)) + goto error3; + bitmap_clear_all(map); + bitmap_clear_all(map2); + bitmap_clear_all(map3); + test_bit1=get_rand_bit(bitsize); + test_bit2=get_rand_bit(bitsize); + test_bit3=get_rand_bit(bitsize); + bitmap_set_prefix(map, test_bit1); + bitmap_set_prefix(map2, test_bit2); + bitmap_subtract(map, map2); + if (test_bit2 < test_bit1) + { + bitmap_set_prefix(map3, test_bit1); + for (j=0; j < test_bit2; j++) + bitmap_clear_bit(map3, j); + } + if (!bitmap_cmp(map, map3)) + goto error4; + bitmap_clear_all(map); + bitmap_clear_all(map2); + bitmap_clear_all(map3); + test_bit1=get_rand_bit(bitsize); + bitmap_set_prefix(map, test_bit1); + bitmap_invert(map); + bitmap_set_all(map3); + for (j=0; j < test_bit1; j++) + bitmap_clear_bit(map3, j); + if (!bitmap_cmp(map, map3)) + goto error5; + bitmap_clear_all(map); + bitmap_clear_all(map3); + } + return FALSE; +error1: + printf("intersect error bitsize=%u,size1=%u,size2=%u", bitsize, + test_bit1,test_bit2); + return TRUE; +error2: + printf("union error bitsize=%u,size1=%u,size2=%u", bitsize, + test_bit1,test_bit2); + return TRUE; +error3: + printf("xor error bitsize=%u,size1=%u,size2=%u", bitsize, + test_bit1,test_bit2); + return TRUE; +error4: + printf("subtract error bitsize=%u,size1=%u,size2=%u", bitsize, + test_bit1,test_bit2); + return TRUE; +error5: + printf("invert error bitsize=%u,size=%u", bitsize, + test_bit1); + return TRUE; +} + +my_bool test_count_bits_set(MY_BITMAP *map, uint bitsize) +{ + uint i, bit_count=0, test_bit; + uint no_loops= bitsize > 128 ? 128 : bitsize; + for (i=0; i < no_loops; i++) + { + test_bit=get_rand_bit(bitsize); + if (!bitmap_is_set(map, test_bit)) + { + bitmap_set_bit(map, test_bit); + bit_count++; + } + } + if (bit_count==0 && bitsize > 0) + goto error1; + if (bitmap_bits_set(map) != bit_count) + goto error2; + return FALSE; +error1: + printf("No bits set bitsize = %u", bitsize); + return TRUE; +error2: + printf("Wrong count of bits set, bitsize = %u", bitsize); + return TRUE; +} + +my_bool test_get_first_bit(MY_BITMAP *map, uint bitsize) +{ + uint i, test_bit; + uint no_loops= bitsize > 128 ? 128 : bitsize; + for (i=0; i < no_loops; i++) + { + test_bit=get_rand_bit(bitsize); + bitmap_set_bit(map, test_bit); + if (bitmap_get_first_set(map) != test_bit) + goto error1; + bitmap_set_all(map); + bitmap_clear_bit(map, test_bit); + if (bitmap_get_first(map) != test_bit) + goto error2; + bitmap_clear_all(map); + } + return FALSE; +error1: + printf("get_first_set error bitsize=%u,prefix_size=%u",bitsize,test_bit); + return TRUE; +error2: + printf("get_first error bitsize= %u, prefix_size= %u",bitsize,test_bit); + return TRUE; +} + +my_bool test_get_next_bit(MY_BITMAP *map, uint bitsize) +{ + uint i, j, test_bit; + uint no_loops= bitsize > 128 ? 128 : bitsize; + for (i=0; i < no_loops; i++) + { + test_bit=get_rand_bit(bitsize); + for (j=0; j < test_bit; j++) + bitmap_set_next(map); + if (!bitmap_is_prefix(map, test_bit)) + goto error1; + bitmap_clear_all(map); + } + return FALSE; +error1: + printf("get_next error bitsize= %u, prefix_size= %u", bitsize,test_bit); + return TRUE; +} + +my_bool test_prefix(MY_BITMAP *map, uint bitsize) +{ + uint i, j, test_bit; + uint no_loops= bitsize > 128 ? 128 : bitsize; + for (i=0; i < no_loops; i++) + { + test_bit=get_rand_bit(bitsize); + bitmap_set_prefix(map, test_bit); + if (!bitmap_is_prefix(map, test_bit)) + goto error1; + bitmap_clear_all(map); + for (j=0; j < test_bit; j++) + bitmap_set_bit(map, j); + if (!bitmap_is_prefix(map, test_bit)) + goto error2; + bitmap_set_all(map); + for (j=bitsize - 1; ~(j-test_bit); j--) + bitmap_clear_bit(map, j); + if (!bitmap_is_prefix(map, test_bit)) + goto error3; + bitmap_clear_all(map); + } + return FALSE; +error1: + printf("prefix1 error bitsize = %u, prefix_size = %u", bitsize,test_bit); + return TRUE; +error2: + printf("prefix2 error bitsize = %u, prefix_size = %u", bitsize,test_bit); + return TRUE; +error3: + printf("prefix3 error bitsize = %u, prefix_size = %u", bitsize,test_bit); + return TRUE; +} + + +my_bool do_test(uint bitsize) +{ + MY_BITMAP map; + my_bitmap_map buf[1024]; + if (bitmap_init(&map, buf, bitsize, FALSE)) + { + printf("init error for bitsize %d", bitsize); + goto error; + } + if (test_set_get_clear_bit(&map,bitsize)) + goto error; + bitmap_clear_all(&map); + if (test_flip_bit(&map,bitsize)) + goto error; + bitmap_clear_all(&map); + if (test_operators(&map,bitsize)) + goto error; + bitmap_clear_all(&map); + if (test_get_all_bits(&map, bitsize)) + goto error; + bitmap_clear_all(&map); + if (test_compare_operators(&map,bitsize)) + goto error; + bitmap_clear_all(&map); + if (test_count_bits_set(&map,bitsize)) + goto error; + bitmap_clear_all(&map); + if (test_get_first_bit(&map,bitsize)) + goto error; + bitmap_clear_all(&map); + if (test_get_next_bit(&map,bitsize)) + goto error; + if (test_prefix(&map,bitsize)) + goto error; + return FALSE; +error: + printf("\n"); + return TRUE; +} + +int main() +{ + int i; + for (i= 1; i < 4096; i++) + { + printf("Start test for bitsize=%u\n",i); + if (do_test(i)) + return -1; + } + printf("OK\n"); + return 0; +} + +/* + In directory mysys: + make test_bitmap + will build the bitmap tests and ./test_bitmap will execute it +*/ + +#endif diff --git a/externals/mysql/mysys/my_chmod.c b/externals/mysql/mysys/my_chmod.c new file mode 100644 index 00000000000..afdea758833 --- /dev/null +++ b/externals/mysql/mysys/my_chmod.c @@ -0,0 +1,48 @@ +/* Copyright (C) 2000 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#include "mysys_priv.h" +#include "mysys_err.h" + +/** + @brief Change mode of file. + + @fn my_chmod() + @param name Filename + @param mode_t Mode + @param my_flags Flags + + @notes + The mode of the file given by path or referenced by fildes is changed + + @retval 0 Ok + @retval # Error +*/ + +int my_chmod(const char *name, mode_t mode, myf my_flags) +{ + DBUG_ENTER("my_chmod"); + DBUG_PRINT("my",("name: %s mode: %lu flags: %d", name, (ulong) mode, + my_flags)); + + if (chmod(name, mode)) + { + my_errno= errno; + if (my_flags & MY_WME) + my_error(EE_CANT_CHMOD, MYF(0), name, (ulong) mode, my_errno); + DBUG_RETURN(1); + } + DBUG_RETURN(0); +} diff --git a/externals/mysql/mysys/my_chsize.c b/externals/mysql/mysys/my_chsize.c new file mode 100644 index 00000000000..b9013811b34 --- /dev/null +++ b/externals/mysql/mysys/my_chsize.c @@ -0,0 +1,107 @@ +/* Copyright (C) 2000 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#include "mysys_priv.h" +#include "mysys_err.h" +#include "m_string.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)) + { + my_errno= errno; + goto err; + } + DBUG_RETURN(0); +#elif defined(HAVE_FTRUNCATE) + if (ftruncate(fd, (off_t) newlength)) + { + my_errno= errno; + goto err; + } + DBUG_RETURN(0); +#elif defined(HAVE_CHSIZE) + if (chsize(fd, (off_t) newlength)) + { + my_errno=errno; + goto err; + } + DBUG_RETURN(0); +#else + /* + Fill space between requested length and true length with 'filler' + We should never come here on any modern machine + */ + 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 */ + bfill(buff, IO_SIZE, filler); + 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) + my_error(EE_CANT_CHSIZE, MYF(ME_BELL+ME_WAITTANG), my_errno); + DBUG_RETURN(1); +} /* my_chsize */ diff --git a/externals/mysql/mysys/my_clock.c b/externals/mysql/mysys/my_clock.c new file mode 100644 index 00000000000..d17f26ed316 --- /dev/null +++ b/externals/mysql/mysys/my_clock.c @@ -0,0 +1,32 @@ +/* Copyright (C) 2000 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#include "my_global.h" + +#if !defined(_MSC_VER) && !defined(__BORLANDC__) && !defined(__NETWARE__) +#include "mysys_priv.h" +#include <sys/times.h> +#endif + +long my_clock(void) +{ +#if !defined(__WIN__) && !defined(__NETWARE__) + struct tms tmsbuf; + (void) times(&tmsbuf); + return (tmsbuf.tms_utime + tmsbuf.tms_stime); +#else + return clock(); +#endif +} diff --git a/externals/mysql/mysys/my_compress.c b/externals/mysql/mysys/my_compress.c new file mode 100644 index 00000000000..7edd07b18e2 --- /dev/null +++ b/externals/mysql/mysys/my_compress.c @@ -0,0 +1,264 @@ +/* Copyright (C) 2000 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +/* Written by Sinisa Milivojevic <sinisa@mysql.com> */ + +#include <my_global.h> +#ifdef HAVE_COMPRESS +#include <my_sys.h> +#ifndef SCO +#include <m_string.h> +#endif +#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,MYF(MY_WME)); + } + 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(*complen, MYF(MY_WME)))) + return 0; /* Not enough memory */ + + tmp_complen= (uLongf) *complen; + res= compress((Bytef*) compbuf, &tmp_complen, (Bytef*) packet, (uLong) *len); + *complen= tmp_complen; + + if (res != Z_OK) + { + my_free(compbuf, MYF(MY_WME)); + return 0; + } + + if (*complen >= *len) + { + *complen= 0; + my_free(compbuf, MYF(MY_WME)); + 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(*complen,MYF(MY_WME)); + int error; + if (!compbuf) + DBUG_RETURN(1); /* Not enough memory */ + + tmp_complen= (uLongf) *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, MYF(MY_WME)); + DBUG_RETURN(1); + } + memcpy(packet, compbuf, *complen); + my_free(compbuf, MYF(MY_WME)); + } + 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: %p len: %lu", 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(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: %p pack_len: %lu", + *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: %p", 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(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, MYF(0)); + DBUG_RETURN(3); + } + + *unpack_data= data; + *unpack_len= orglen; + + DBUG_PRINT("exit", ("frmdata: %p len: %lu", *unpack_data, + (ulong) *unpack_len)); + DBUG_RETURN(0); +} +#endif /* HAVE_COMPRESS */ diff --git a/externals/mysql/mysys/my_conio.c b/externals/mysql/mysys/my_conio.c new file mode 100644 index 00000000000..b78966446ee --- /dev/null +++ b/externals/mysql/mysys/my_conio.c @@ -0,0 +1,222 @@ +/* Copyright (C) 2000 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + + +#include "mysys_priv.h" + +#ifdef __WIN__ + +static HANDLE my_coninpfh= 0; /* console input */ + +/* + functions my_pthread_auto_mutex_lock & my_pthread_auto_mutex_free + are experimental at this moment, they are intended to bring + ability of protecting code sections without necessity to explicitly + initialize synchronization object in one of threads + + if found useful they are to be exported in mysys +*/ + + +/* + int my_pthread_auto_mutex_lock(HANDLE* ph, const char* name, + int id, int time) + NOTES + creates a mutex with given name and tries to lock it time msec. + mutex name is appended with id to allow system wide or process wide + locks. Handle to created mutex returned in ph argument. + + RETURN + 0 thread owns mutex + <>0 error +*/ + +static +int my_pthread_auto_mutex_lock(HANDLE* ph, const char* name, int id, int time) +{ + int res; + char tname[FN_REFLEN]; + + sprintf(tname, "%s-%08X", name, id); + + *ph= CreateMutex(NULL, FALSE, tname); + if (*ph == NULL) + return GetLastError(); + + res= WaitForSingleObject(*ph, time); + + if (res == WAIT_TIMEOUT) + return ERROR_SEM_TIMEOUT; + + if (res == WAIT_FAILED) + return GetLastError(); + + return 0; +} + +/* + int my_pthread_auto_mutex_free(HANDLE* ph) + + NOTES + releases a mutex. + + RETURN + 0 thread released mutex + <>0 error + +*/ +static +int my_pthread_auto_mutex_free(HANDLE* ph) +{ + if (*ph) + { + ReleaseMutex(*ph); + CloseHandle(*ph); + *ph= NULL; + } + + return 0; +} + + +#define pthread_auto_mutex_decl(name) \ + HANDLE __h##name= NULL; + +#define pthread_auto_mutex_lock(name, proc, time) \ + my_pthread_auto_mutex_lock(&__h##name, #name, (proc), (time)) + +#define pthread_auto_mutex_free(name) \ + my_pthread_auto_mutex_free(&__h##name) + + +/* + char* my_cgets() + + NOTES + Replaces _cgets from libc to support input of more than 255 chars. + Reads from the console via ReadConsole into buffer which + should be at least clen characters. + Actual length of string returned in plen. + + WARNING + my_cgets() does NOT check the pushback character buffer (i.e., _chbuf). + Thus, my_cgets() will not return any character that is pushed back by + the _ungetch() call. + + RETURN + string pointer ok + NULL Error + +*/ + +char* my_cgets(char *buffer, size_t clen, size_t* plen) +{ + ULONG state; + char *result; + DWORD plen_res; + CONSOLE_SCREEN_BUFFER_INFO csbi; + + pthread_auto_mutex_decl(my_conio_cs); + + /* lock the console for the current process*/ + if (pthread_auto_mutex_lock(my_conio_cs, GetCurrentProcessId(), INFINITE)) + { + /* can not lock console */ + pthread_auto_mutex_free(my_conio_cs); + return NULL; + } + + /* init console input */ + if (my_coninpfh == 0) + { + /* same handle will be used until process termination */ + my_coninpfh= CreateFile("CONIN$", GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, OPEN_EXISTING, 0, NULL); + } + + if (my_coninpfh == INVALID_HANDLE_VALUE) + { + /* unlock the console */ + pthread_auto_mutex_free(my_conio_cs); + return(NULL); + } + + GetConsoleMode((HANDLE)my_coninpfh, &state); + SetConsoleMode((HANDLE)my_coninpfh, ENABLE_LINE_INPUT | + ENABLE_PROCESSED_INPUT | ENABLE_ECHO_INPUT); + + GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi); + + /* + there is no known way to determine allowed buffer size for input + though it is known it should not be more than 64K + so we cut 64K and try first size of screen buffer + if it is still to large we cut half of it and try again + later we may want to cycle from min(clen, 65535) to allowed size + with small decrement to determine exact allowed buffer + */ + clen= min(clen, 65535); + do + { + clen= min(clen, (size_t) csbi.dwSize.X*csbi.dwSize.Y); + if (!ReadConsole((HANDLE)my_coninpfh, (LPVOID)buffer, clen - 1, &plen_res, + NULL)) + { + result= NULL; + clen>>= 1; + } + else + { + result= buffer; + break; + } + } + while (GetLastError() == ERROR_NOT_ENOUGH_MEMORY); + *plen= plen_res; + + /* We go here on error reading the string (Ctrl-C for example) */ + if (!*plen) + result= NULL; /* purecov: inspected */ + + if (result != NULL) + { + if (*plen > 1 && buffer[*plen - 2] == '\r') + { + *plen= *plen - 2; + } + else + { + if (*plen > 0 && buffer[*plen - 1] == '\r') + { + char tmp[3]; + int tmplen= sizeof(tmp); + + *plen= *plen - 1; + /* read /n left in the buffer */ + ReadConsole((HANDLE)my_coninpfh, (LPVOID)tmp, tmplen, &tmplen, NULL); + } + } + buffer[*plen]= '\0'; + } + + SetConsoleMode((HANDLE)my_coninpfh, state); + /* unlock the console */ + pthread_auto_mutex_free(my_conio_cs); + + return result; +} + +#endif /* __WIN__ */ diff --git a/externals/mysql/mysys/my_copy.c b/externals/mysql/mysys/my_copy.c new file mode 100644 index 00000000000..60096e9ea80 --- /dev/null +++ b/externals/mysql/mysys/my_copy.c @@ -0,0 +1,122 @@ +/* Copyright (C) 2000 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#include "mysys_priv.h" +#include <my_dir.h> /* for stat */ +#include <m_string.h> +#if defined(HAVE_UTIME_H) +#include <utime.h> +#elif defined(HAVE_SYS_UTIME_H) +#include <sys/utime.h> +#elif !defined(HPUX10) +#include <time.h> +struct utimbuf { + time_t actime; + time_t modtime; +}; +#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) +{ + uint 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; + 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= 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)) + { + 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; + } + + if (my_close(from_file,MyFlags) | my_close(to_file,MyFlags)) + DBUG_RETURN(-1); /* Error on close */ + + /* Copy modes if possible */ + + if (MyFlags & MY_HOLD_ORIGINAL_MODES && !new_file_stat) + DBUG_RETURN(0); /* File copyed but not stat */ + (void) chmod(to, stat_buff.st_mode & 07777); /* Copy modes */ +#if !defined(__WIN__) && !defined(__NETWARE__) + (void) chown(to, stat_buff.st_uid,stat_buff.st_gid); /* Copy ownership */ +#endif +#if !defined(VMS) && !defined(__ZTC__) + 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 */ + } +#endif + 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/externals/mysql/mysys/my_create.c b/externals/mysql/mysys/my_create.c new file mode 100644 index 00000000000..d0436276d03 --- /dev/null +++ b/externals/mysql/mysys/my_create.c @@ -0,0 +1,76 @@ +/* Copyright (C) 2000 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#include "mysys_priv.h" +#include <my_dir.h> +#include "mysys_err.h" +#include <errno.h> +#include <my_sys.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(NO_OPEN_3) + fd= open((char *) FileName, access_flags | O_CREAT, + CreateFlags ? CreateFlags : my_umask); +#elif defined(_WIN32) + fd= my_win_open(FileName, access_flags | O_CREAT); +#else + fd= open(FileName, access_flags); +#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); + my_errno= tmp; + } + + DBUG_RETURN(rc); +} /* my_create */ diff --git a/externals/mysql/mysys/my_delete.c b/externals/mysql/mysys/my_delete.c new file mode 100644 index 00000000000..edee1c4e875 --- /dev/null +++ b/externals/mysql/mysys/my_delete.c @@ -0,0 +1,115 @@ +/* Copyright (C) 2000 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#include "mysys_priv.h" +#include "mysys_err.h" +#include <my_sys.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) + { + my_errno=errno; + if (MyFlags & (MY_FAE+MY_WME)) + my_error(EE_DELETE,MYF(ME_BELL+ME_WAITTANG+(MyFlags & ME_NOINPUT)), + name,errno); + } + else if ((MyFlags & MY_SYNC_DIR) && + my_sync_dir_by_file(name, MyFlags)) + err= -1; + DBUG_RETURN(err); +} /* my_delete */ + +#if defined(__WIN__) +/** + 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) + { + my_errno= ENOENT; // marking, that `name' doesn't exist + } + else if (errno == 0) + { + if (DeleteFile(buf)) + DBUG_RETURN(0); + else if ((my_errno= GetLastError()) == 0) + my_errno= ENOENT; // marking, that `buf' doesn't exist + } else + my_errno= errno; + + if (MyFlags & (MY_FAE+MY_WME)) + my_error(EE_DELETE, MYF(ME_BELL + ME_WAITTANG + (MyFlags & ME_NOINPUT)), + name, my_errno); + DBUG_RETURN(-1); +} +#endif diff --git a/externals/mysql/mysys/my_div.c b/externals/mysql/mysys/my_div.c new file mode 100644 index 00000000000..d29d3668852 --- /dev/null +++ b/externals/mysql/mysys/my_div.c @@ -0,0 +1,37 @@ +/* Copyright (C) 2000 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#include "mysys_priv.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/externals/mysql/mysys/my_dup.c b/externals/mysql/mysys/my_dup.c new file mode 100644 index 00000000000..5fdd6e9f364 --- /dev/null +++ b/externals/mysql/mysys/my_dup.c @@ -0,0 +1,41 @@ +/* Copyright (C) 2000 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#include "mysys_priv.h" +#include "mysys_err.h" +#include <my_dir.h> +#include <errno.h> +#if defined(__WIN__) +#include <share.h> +#endif + + /* Open a file */ + +File my_dup(File file, myf MyFlags) +{ + File fd; + const char *filename; + DBUG_ENTER("my_dup"); + DBUG_PRINT("my",("file: %d MyFlags: %d", file, MyFlags)); +#ifdef _WIN32 + fd= my_win_dup(file); +#else + fd= dup(file); +#endif + filename= (((uint) file < my_file_limit) ? + my_file_info[(int) file].name : "Unknown"); + DBUG_RETURN(my_register_filename(fd, filename, FILE_BY_DUP, + EE_FILENOTFOUND, MyFlags)); +} /* my_open */ diff --git a/externals/mysql/mysys/my_error.c b/externals/mysql/mysys/my_error.c new file mode 100644 index 00000000000..dea57480111 --- /dev/null +++ b/externals/mysql/mysys/my_error.c @@ -0,0 +1,289 @@ +/* Copyright (C) 2000 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#include "mysys_priv.h" +#include "mysys_err.h" +#include <m_string.h> +#include <stdarg.h> +#include <m_ctype.h> + +/* 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 have not parameters use my_message(ER_CODE, ER(ER_CODE), MYF(N)) + - if message registered use my_error(ER_CODE, MYF(N), ...). + - With some special text of errror message use: + my_printf_error(ER_CODE, format, MYF(N), ...) +*/ + +/* + Message texts are registered into a linked list of 'my_err_head' structs. + Each struct contains (1.) an array of pointers to C character strings with + '\0' termination, (2.) the error number for the first message in the array + (array index 0) and (3.) the error number for the last message in the array + (array index (last - first)). + The array may contain gaps with NULL pointers and pointers to empty strings. + Both kinds of gaps will be translated to "Unknown error %d.", if my_error() + is called with a respective error number. + The list of header structs is sorted in increasing order of error numbers. + Negative error numbers are allowed. Overlap of error numbers is not allowed. + Not registered error numbers will be translated to "Unknown error %d.". +*/ +static struct my_err_head +{ + struct my_err_head *meh_next; /* chain link */ + const char **meh_errmsgs; /* error messages array */ + int meh_first; /* error number matching array slot 0 */ + int meh_last; /* error number matching last slot */ +} my_errmsgs_globerrs = {NULL, globerrs, EE_ERROR_FIRST, EE_ERROR_LAST}; + +static struct my_err_head *my_errmsgs_list= &my_errmsgs_globerrs; + + +/* + Error message to user + + SYNOPSIS + my_error() + nr Errno + MyFlags Flags + ... variable list +*/ + +void my_error(int nr, myf MyFlags, ...) +{ + const char *format; + struct my_err_head *meh_p; + va_list args; + char ebuff[ERRMSGSIZE]; + DBUG_ENTER("my_error"); + DBUG_PRINT("my", ("nr: %d MyFlags: %d errno: %d", nr, MyFlags, errno)); + + /* Search for the error messages array, which could contain the message. */ + for (meh_p= my_errmsgs_list; meh_p; meh_p= meh_p->meh_next) + if (nr <= meh_p->meh_last) + break; + + /* get the error message string. Default, if NULL or empty string (""). */ + if (! (format= (meh_p && (nr >= meh_p->meh_first)) ? + meh_p->meh_errmsgs[nr - meh_p->meh_first] : NULL) || ! *format) + (void) my_snprintf(ebuff, sizeof(ebuff), "Unknown error %d", nr); + else + { + va_start(args,MyFlags); + (void) my_vsnprintf(ebuff, sizeof(ebuff), format, args); + va_end(args); + } + (*error_handler_hook)(nr, ebuff, MyFlags); + DBUG_VOID_RETURN; +} + + +/* + Error as printf + + SYNOPSIS + my_printf_error() + error Errno + format Format string + MyFlags Flags + ... variable list +*/ + +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(ebuff, sizeof(ebuff), format, args); + va_end(args); + (*error_handler_hook)(error, ebuff, MyFlags); + DBUG_VOID_RETURN; +} + + +/* + Error with va_list + + SYNOPSIS + my_printv_error() + error Errno + format Format string + MyFlags Flags + ... variable list +*/ + +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; +} + + +/* + Give message using error_handler_hook + + SYNOPSIS + my_message() + error Errno + str Error message + MyFlags Flags +*/ + +void my_message(uint error, const char *str, register myf MyFlags) +{ + (*error_handler_hook)(error, str, MyFlags); +} + + +/* + Register error messages for use with my_error(). + + SYNOPSIS + my_error_register() + errmsgs array of pointers to error messages + first error number of first message in the array + last error number of last message in the array + + DESCRIPTION + The pointer array is expected to contain addresses to NUL-terminated + C character strings. The array contains (last - first + 1) pointers. + NULL pointers and empty strings ("") are allowed. These will be mapped to + "Unknown error" when my_error() is called with a matching error number. + This function registers the error numbers 'first' to 'last'. + No overlapping with previously registered error numbers is allowed. + + RETURN + 0 OK + != 0 Error +*/ + +int my_error_register(const char **errmsgs, int first, int last) +{ + struct my_err_head *meh_p; + struct my_err_head **search_meh_pp; + + /* Allocate a new header structure. */ + if (! (meh_p= (struct my_err_head*) my_malloc(sizeof(struct my_err_head), + MYF(MY_WME)))) + return 1; + meh_p->meh_errmsgs= errmsgs; + meh_p->meh_first= first; + meh_p->meh_last= last; + + /* Search for the right position in the list. */ + for (search_meh_pp= &my_errmsgs_list; + *search_meh_pp; + search_meh_pp= &(*search_meh_pp)->meh_next) + { + if ((*search_meh_pp)->meh_last > first) + break; + } + + /* Error numbers must be unique. No overlapping is allowed. */ + if (*search_meh_pp && ((*search_meh_pp)->meh_first <= last)) + { + my_free((uchar*)meh_p, MYF(0)); + return 1; + } + + /* Insert header into the chain. */ + meh_p->meh_next= *search_meh_pp; + *search_meh_pp= meh_p; + return 0; +} + + +/* + Unregister formerly registered error messages. + + SYNOPSIS + my_error_unregister() + first error number of first message + last error number of last message + + DESCRIPTION + This function unregisters the error numbers 'first' to 'last'. + These must have been previously registered by my_error_register(). + 'first' and 'last' must exactly match the registration. + If a matching registration is present, the header is removed from the + list and the pointer to the error messages pointers array is returned. + Otherwise, NULL is returned. + + RETURN + non-NULL OK, returns address of error messages pointers array. + NULL Error, no such number range registered. +*/ + +const char **my_error_unregister(int first, int last) +{ + struct my_err_head *meh_p; + struct my_err_head **search_meh_pp; + const char **errmsgs; + + /* Search for the registration in the list. */ + for (search_meh_pp= &my_errmsgs_list; + *search_meh_pp; + search_meh_pp= &(*search_meh_pp)->meh_next) + { + if (((*search_meh_pp)->meh_first == first) && + ((*search_meh_pp)->meh_last == last)) + break; + } + if (! *search_meh_pp) + return NULL; + + /* Remove header from the chain. */ + meh_p= *search_meh_pp; + *search_meh_pp= meh_p->meh_next; + + /* Save the return value and free the header. */ + errmsgs= meh_p->meh_errmsgs; + my_free((uchar*) meh_p, MYF(0)); + + return errmsgs; +} + + +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((uchar*) cursor, MYF(0)); + } + my_errmsgs_globerrs.meh_next= NULL; /* Freed in first iteration above. */ + + my_errmsgs_list= &my_errmsgs_globerrs; +} diff --git a/externals/mysql/mysys/my_file.c b/externals/mysql/mysys/my_file.c new file mode 100644 index 00000000000..ec0c9c425ea --- /dev/null +++ b/externals/mysql/mysys/my_file.c @@ -0,0 +1,135 @@ +/* Copyright (C) 2000 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#include "mysys_priv.h" +#include "my_static.h" +#include <m_string.h> + +/* + set how many open files we want to be able to handle + + SYNOPSIS + set_maximum_open_files() + max_file_limit Files to open + + NOTES + The request may not fulfilled becasue of system limitations + + RETURN + Files available to open. + May be more or less than max_file_limit! +*/ + +#if defined(HAVE_GETRLIMIT) && defined(RLIMIT_NOFILE) + +#ifndef RLIM_INFINITY +#define RLIM_INFINITY ((uint) 0xffffffff) +#endif + +static uint set_max_open_files(uint max_file_limit) +{ + struct rlimit rlimit; + uint old_cur; + DBUG_ENTER("set_max_open_files"); + DBUG_PRINT("enter",("files: %u", max_file_limit)); + + if (!getrlimit(RLIMIT_NOFILE,&rlimit)) + { + old_cur= (uint) rlimit.rlim_cur; + DBUG_PRINT("info", ("rlim_cur: %u rlim_max: %u", + (uint) rlimit.rlim_cur, + (uint) rlimit.rlim_max)); + if (rlimit.rlim_cur == RLIM_INFINITY) + rlimit.rlim_cur = max_file_limit; + if (rlimit.rlim_cur >= max_file_limit) + DBUG_RETURN(rlimit.rlim_cur); /* purecov: inspected */ + rlimit.rlim_cur= rlimit.rlim_max= max_file_limit; + if (setrlimit(RLIMIT_NOFILE, &rlimit)) + max_file_limit= old_cur; /* Use original value */ + else + { + rlimit.rlim_cur= 0; /* Safety if next call fails */ + (void) getrlimit(RLIMIT_NOFILE,&rlimit); + DBUG_PRINT("info", ("rlim_cur: %u", (uint) rlimit.rlim_cur)); + if (rlimit.rlim_cur) /* If call didn't fail */ + max_file_limit= (uint) rlimit.rlim_cur; + } + } + DBUG_PRINT("exit",("max_file_limit: %u", max_file_limit)); + DBUG_RETURN(max_file_limit); +} + +#else +static int set_max_open_files(uint max_file_limit) +{ + /* We don't know the limit. Return best guess */ + return min(max_file_limit, OS_FILE_LIMIT); +} +#endif + + +/* + Change number of open files + + SYNOPSIS: + my_set_max_open_files() + files Number of requested files + + RETURN + number of files available for open +*/ + +uint my_set_max_open_files(uint files) +{ + struct st_my_file_info *tmp; + DBUG_ENTER("my_set_max_open_files"); + DBUG_PRINT("enter",("files: %u my_file_limit: %u", files, my_file_limit)); + + files+= MY_FILE_MIN; + files= set_max_open_files(min(files, OS_FILE_LIMIT)); + if (files <= MY_NFILE) + DBUG_RETURN(files); + + if (!(tmp= (struct st_my_file_info*) my_malloc(sizeof(*tmp) * files, + MYF(MY_WME)))) + DBUG_RETURN(MY_NFILE); + + /* Copy any initialized files */ + memcpy((char*) tmp, (char*) my_file_info, + sizeof(*tmp) * min(my_file_limit, files)); + bzero((char*) (tmp + my_file_limit), + 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((char*) my_file_info, MYF(0)); + my_file_info= my_file_info_default; + my_file_limit= MY_NFILE; + } + DBUG_VOID_RETURN; +} diff --git a/externals/mysql/mysys/my_fopen.c b/externals/mysql/mysys/my_fopen.c new file mode 100644 index 00000000000..ef8a385dcfe --- /dev/null +++ b/externals/mysql/mysys/my_fopen.c @@ -0,0 +1,226 @@ +/* Copyright (C) 2000 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#include "mysys_priv.h" +#include "my_static.h" +#include <errno.h> +#include "mysys_err.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]; + 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) + { + thread_safe_increment(my_stream_opened,&THR_LOCK_open); + DBUG_RETURN(fd); /* safeguard */ + } + pthread_mutex_lock(&THR_LOCK_open); + if ((my_file_info[filedesc].name= (char*) + my_strdup(filename,MyFlags))) + { + my_stream_opened++; + my_file_total_opened++; + my_file_info[filedesc].type= STREAM_BY_FOPEN; + pthread_mutex_unlock(&THR_LOCK_open); + DBUG_PRINT("exit",("stream: %p", fd)); + DBUG_RETURN(fd); + } + pthread_mutex_unlock(&THR_LOCK_open); + (void) my_fclose(fd,MyFlags); + my_errno=ENOMEM; + } + else + my_errno=errno; + DBUG_PRINT("error",("Got error %d on open",my_errno)); + if (MyFlags & (MY_FFNF | MY_FAE | MY_WME)) + my_error((flags & O_RDONLY) || (flags == O_RDONLY ) ? EE_FILENOTFOUND : + EE_CANTCREATEFILE, + MYF(ME_BELL+ME_WAITTANG), filename, my_errno); + DBUG_RETURN((FILE*) 0); +} /* my_fopen */ + + + /* Close a stream */ + +/* Close a stream */ +int my_fclose(FILE *fd, myf MyFlags) +{ + int err,file; + DBUG_ENTER("my_fclose"); + DBUG_PRINT("my",("stream: %p MyFlags: %d", fd, MyFlags)); + + pthread_mutex_lock(&THR_LOCK_open); + file= my_fileno(fd); +#ifndef _WIN32 + err= fclose(fd); +#else + err= my_win_fclose(fd); +#endif + if(err < 0) + { + my_errno=errno; + if (MyFlags & (MY_FAE | MY_WME)) + my_error(EE_BADCLOSE, MYF(ME_BELL+ME_WAITTANG), + my_filename(file),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, MYF(MY_ALLOW_ZERO_PTR)); + } + pthread_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) + { + my_errno=errno; + if (MyFlags & (MY_FAE | MY_WME)) + my_error(EE_CANT_OPEN_STREAM, MYF(ME_BELL+ME_WAITTANG),errno); + } + else + { + pthread_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(name,MyFlags); + } + my_file_info[Filedes].type = STREAM_BY_FDOPEN; + } + pthread_mutex_unlock(&THR_LOCK_open); + } + + DBUG_PRINT("exit",("stream: %p", 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(register char * to, register 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/externals/mysql/mysys/my_fstream.c b/externals/mysql/mysys/my_fstream.c new file mode 100644 index 00000000000..c7006e0a955 --- /dev/null +++ b/externals/mysql/mysys/my_fstream.c @@ -0,0 +1,195 @@ +/* Copyright (C) 2000 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +/* USE_MY_STREAM isn't set because we can't thrust my_fclose! */ + +#include "mysys_priv.h" +#include "mysys_err.h" +#include <errno.h> +#include <stdio.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: %p Buffer: %p Count: %u MyFlags: %d", + stream, 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)) + my_error(EE_READ, MYF(ME_BELL+ME_WAITTANG), + my_filename(my_fileno(stream)),errno); + else + if (MyFlags & (MY_NABP | MY_FNABP)) + my_error(EE_EOFERR, MYF(ME_BELL+ME_WAITTANG), + my_filename(my_fileno(stream)),errno); + } + 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; +#if !defined(NO_BACKGROUND) && defined(USE_MY_STREAM) + uint errors; +#endif + DBUG_ENTER("my_fwrite"); + DBUG_PRINT("my",("stream: %p Buffer: %p Count: %u MyFlags: %d", + stream, Buffer, (uint) Count, MyFlags)); + +#if !defined(NO_BACKGROUND) && defined(USE_MY_STREAM) + errors=0; +#endif + 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)); + my_errno=errno; + if (written != (size_t) -1) + { + seekptr+=written; + Buffer+=written; + writtenbytes+=written; + Count-=written; + } +#ifdef EINTR + if (errno == EINTR) + { + (void) my_fseek(stream,seekptr,MY_SEEK_SET,MYF(0)); + continue; + } +#endif +#if !defined(NO_BACKGROUND) && defined(USE_MY_STREAM) +#ifdef THREAD + if (my_thread_var->abort) + MyFlags&= ~ MY_WAIT_IF_FULL; /* End if aborted by user */ +#endif + if ((errno == ENOSPC || errno == EDQUOT) && + (MyFlags & MY_WAIT_IF_FULL)) + { + wait_for_free_space("[stream]", errors); + errors++; + (void) my_fseek(stream,seekptr,MY_SEEK_SET,MYF(0)); + continue; + } +#endif + if (ferror(stream) || (MyFlags & (MY_NABP | MY_FNABP))) + { + if (MyFlags & (MY_WME | MY_FAE | MY_FNABP)) + { + my_error(EE_WRITE, MYF(ME_BELL+ME_WAITTANG), + my_filename(my_fileno(stream)),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 __attribute__((unused))) +{ + DBUG_ENTER("my_fseek"); + DBUG_PRINT("my",("stream: %p pos: %lu whence: %d MyFlags: %d", + 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 __attribute__((unused))) +{ + off_t pos; + DBUG_ENTER("my_ftell"); + DBUG_PRINT("my",("stream: %p MyFlags: %d", 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/externals/mysql/mysys/my_gethostbyname.c b/externals/mysql/mysys/my_gethostbyname.c new file mode 100644 index 00000000000..067fdfee9db --- /dev/null +++ b/externals/mysql/mysys/my_gethostbyname.c @@ -0,0 +1,111 @@ +/* Copyright (C) 2002, 2004 MySQL AB + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; version 2 + of the License. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + MA 02111-1307, USA */ + +/* Thread safe version of gethostbyname_r() */ + +#include "mysys_priv.h" +#if !defined(__WIN__) +#include <netdb.h> +#endif +#include <my_net.h> + +/* This file is not needed if my_gethostbyname_r is a macro */ +#if !defined(my_gethostbyname_r) + +/* + Emulate SOLARIS style calls, not because it's better, but just to make the + usage of getbostbyname_r simpler. +*/ + +#if defined(HAVE_GETHOSTBYNAME_R) + +#if defined(HAVE_GETHOSTBYNAME_R_GLIBC2_STYLE) + +struct hostent *my_gethostbyname_r(const char *name, + struct hostent *result, char *buffer, + int buflen, int *h_errnop) +{ + struct hostent *hp; + DBUG_ASSERT((size_t) buflen >= sizeof(*result)); + if (gethostbyname_r(name,result, buffer, (size_t) buflen, &hp, h_errnop)) + return 0; + return hp; +} + +#elif defined(HAVE_GETHOSTBYNAME_R_RETURN_INT) + +struct hostent *my_gethostbyname_r(const char *name, + struct hostent *result, char *buffer, + int buflen, int *h_errnop) +{ + if (gethostbyname_r(name,result,(struct hostent_data *) buffer) == -1) + { + *h_errnop= errno; + return 0; + } + return result; +} + +#else + +/* gethostbyname_r with similar interface as gethostbyname() */ + +struct hostent *my_gethostbyname_r(const char *name, + struct hostent *result, char *buffer, + int buflen, int *h_errnop) +{ + struct hostent *hp; + DBUG_ASSERT(buflen >= sizeof(struct hostent_data)); + hp= gethostbyname_r(name,result,(struct hostent_data *) buffer); + *h_errnop= errno; + return hp; +} +#endif /* GLIBC2_STYLE_GETHOSTBYNAME_R */ + +#else /* !HAVE_GETHOSTBYNAME_R */ + +#ifdef THREAD +extern pthread_mutex_t LOCK_gethostbyname_r; +#endif + +/* + No gethostbyname_r() function exists. + In this case we have to keep a mutex over the call to ensure that no + other thread is going to reuse the internal memory. + + The user is responsible to call my_gethostbyname_r_free() when he + is finished with the structure. +*/ + +struct hostent *my_gethostbyname_r(const char *name, + struct hostent *result, char *buffer, + int buflen, int *h_errnop) +{ + struct hostent *hp; + pthread_mutex_lock(&LOCK_gethostbyname_r); + hp= gethostbyname(name); + *h_errnop= h_errno; + return hp; +} + +void my_gethostbyname_r_free() +{ + pthread_mutex_unlock(&LOCK_gethostbyname_r); +} + +#endif /* !HAVE_GETHOSTBYNAME_R */ +#endif /* !my_gethostbyname_r */ diff --git a/externals/mysql/mysys/my_gethwaddr.c b/externals/mysql/mysys/my_gethwaddr.c new file mode 100644 index 00000000000..7fae1a51446 --- /dev/null +++ b/externals/mysql/mysys/my_gethwaddr.c @@ -0,0 +1,222 @@ +/* Copyright (C) 2004 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +/* get hardware address for an interface */ +/* if there are many available, any non-zero one can be used */ + +#include "mysys_priv.h" +#include <m_string.h> + +#ifndef MAIN + +#if defined(__FreeBSD__) || defined(__linux__) +static my_bool memcpy_and_test(uchar *to, uchar *from, uint len) +{ + uint i, res=1; + + for (i=0; i < len; i++) + if ((*to++= *from++)) + res=0; + return res; +} +#endif /* FreeBSD || linux */ + +#ifdef __FreeBSD__ + +#include <net/ethernet.h> +#include <sys/sysctl.h> +#include <net/route.h> +#include <net/if.h> +#include <net/if_dl.h> + +my_bool my_gethwaddr(uchar *to) +{ + size_t len; + uchar *buf, *next, *end, *addr; + struct if_msghdr *ifm; + struct sockaddr_dl *sdl; + int i, res=1, mib[6]={CTL_NET, AF_ROUTE, 0, AF_LINK, NET_RT_IFLIST, 0}; + + if (sysctl(mib, 6, NULL, &len, NULL, 0) == -1) + goto err; + if (!(buf = alloca(len))) + goto err; + if (sysctl(mib, 6, buf, &len, NULL, 0) < 0) + goto err; + + end = buf + len; + + for (next = buf ; res && next < end ; next += ifm->ifm_msglen) + { + ifm = (struct if_msghdr *)next; + if (ifm->ifm_type == RTM_IFINFO) + { + sdl = (struct sockaddr_dl *)(ifm + 1); + addr=LLADDR(sdl); + res=memcpy_and_test(to, addr, ETHER_ADDR_LEN); + } + } + +err: + return res; +} + +#elif __linux__ + +#include <net/if.h> +#include <sys/ioctl.h> +#include <net/ethernet.h> + +my_bool my_gethwaddr(uchar *to) +{ + int fd, res=1; + struct ifreq ifr; + + fd = socket(AF_INET, SOCK_DGRAM, 0); + if (fd < 0) + goto err; + + bzero(&ifr, sizeof(ifr)); + strnmov(ifr.ifr_name, "eth0", sizeof(ifr.ifr_name) - 1); + + do { + if (ioctl(fd, SIOCGIFHWADDR, &ifr) >= 0) + res=memcpy_and_test(to, (uchar *)&ifr.ifr_hwaddr.sa_data, ETHER_ADDR_LEN); + } while (res && (errno == 0 || errno == ENODEV) && ifr.ifr_name[3]++ < '6'); + + close(fd); +err: + return res; +} + +#elif defined(__WIN__) +#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(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, 0); + + return return_val; +} + +#else /* __FreeBSD__ || __linux__ || __WIN__ */ +/* just fail */ +my_bool my_gethwaddr(uchar *to __attribute__((unused))) +{ + return 1; +} +#endif + +#else /* MAIN */ +int main(int argc __attribute__((unused)),char **argv) +{ + uchar mac[6]; + uint i; + MY_INIT(argv[0]); + if (my_gethwaddr(mac)) + { + printf("my_gethwaddr failed with errno %d\n", errno); + exit(1); + } + for (i=0; i < sizeof(mac); i++) + { + if (i) printf(":"); + printf("%02x", mac[i]); + } + printf("\n"); + return 0; +} +#endif + diff --git a/externals/mysql/mysys/my_getncpus.c b/externals/mysql/mysys/my_getncpus.c new file mode 100644 index 00000000000..c09684abe5b --- /dev/null +++ b/externals/mysql/mysys/my_getncpus.c @@ -0,0 +1,49 @@ +/* Copyright (C) 2006 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +/* get the number of (online) CPUs */ + +#include "mysys_priv.h" +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif + +static int ncpus=0; + +int my_getncpus() +{ + if (!ncpus) + { +#ifdef _SC_NPROCESSORS_ONLN + ncpus= sysconf(_SC_NPROCESSORS_ONLN); +#elif defined(__WIN__) + SYSTEM_INFO sysinfo; + + /* + * We are not calling GetNativeSystemInfo here because (1) we + * don't believe that they return different values for number + * of processors and (2) if WOW64 limits processors for Win32 + * then we don't want to try to override that. + */ + GetSystemInfo(&sysinfo); + + ncpus= sysinfo.dwNumberOfProcessors; +#else +/* unknown so play safe: assume SMP and forbid uniprocessor build */ + ncpus= 2; +#endif + } + return ncpus; +} diff --git a/externals/mysql/mysys/my_getopt.c b/externals/mysql/mysys/my_getopt.c new file mode 100644 index 00000000000..a16cc0897a8 --- /dev/null +++ b/externals/mysql/mysys/my_getopt.c @@ -0,0 +1,1301 @@ +/* Copyright (C) 2002-2006 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#include <my_global.h> +#include <m_string.h> +#include <stdlib.h> +#include <my_sys.h> +#include <mysys_err.h> +#include <my_getopt.h> +#include <errno.h> + +typedef void (*init_func_p)(const struct my_option *option, uchar* *variable, + longlong value); + +static void default_reporter(enum loglevel level, const char *format, ...); +my_error_reporter my_getopt_error_reporter= &default_reporter; + +static int findopt(char *, uint, const struct my_option **, char **); +my_bool getopt_compare_strings(const char *, const char *, uint); +static longlong getopt_ll(char *, const struct my_option *, int *); +static ulonglong getopt_ull(char *, const struct my_option *, int *); +static double getopt_double(char *, const struct my_option *, int *); +static void init_variables(const struct my_option *, init_func_p); +static void init_one_value(const struct my_option *opt, uchar **, longlong); +static void fini_one_value(const struct my_option *, uchar **, longlong); +static int setval(const struct my_option *, uchar **, char *, my_bool); +static char *check_struct_option(char *cur_arg, char *key_name); + +/* + The following three variables belong to same group and the number and + order of their arguments must correspond to each other. +*/ +static const char *special_opt_prefix[]= +{"skip", "disable", "enable", "maximum", "loose", 0}; +static const uint special_opt_prefix_lengths[]= +{ 4, 7, 6, 7, 5, 0}; +enum enum_special_opt +{ OPT_SKIP, OPT_DISABLE, OPT_ENABLE, OPT_MAXIMUM, OPT_LOOSE}; + +char *disabled_my_option= (char*) "0"; + +/* + This is a flag that can be set in client programs. 0 means that + my_getopt will not print error messages, but the client should do + it by itself +*/ + +my_bool my_getopt_print_errors= 1; + +/* + This is a flag that can be set in client programs. 1 means that + my_getopt will skip over options it does not know how to handle. +*/ + +my_bool my_getopt_skip_unknown= 0; + +static void default_reporter(enum loglevel level, + const char *format, ...) +{ + va_list args; + va_start(args, format); + if (level == WARNING_LEVEL) + fprintf(stderr, "%s", "Warning: "); + else if (level == INFORMATION_LEVEL) + fprintf(stderr, "%s", "Info: "); + vfprintf(stderr, format, args); + va_end(args); + fputc('\n', stderr); + fflush(stderr); +} + +/* + function: handle_options + + Sort options; put options first, until special end of options (--), or + until end of argv. Parse options; check that the given option matches with + one of the options in struct 'my_option', return error in case of ambiguous + or unknown option. Check that option was given an argument if it requires + one. Call function 'get_one_option()' once for each option. +*/ + +static uchar** (*getopt_get_addr)(const char *, uint, const struct my_option *, int *); + +void my_getopt_register_get_addr(uchar** (*func_addr)(const char *, uint, + const struct my_option *, int *)) +{ + getopt_get_addr= func_addr; +} + +int handle_options(int *argc, char ***argv, + const struct my_option *longopts, + my_get_one_option get_one_option) +{ + uint opt_found, argvpos= 0, length; + my_bool end_of_options= 0, must_be_var, set_maximum_value, + option_is_loose; + char **pos, **pos_end, *optend, *prev_found, + *opt_str, key_name[FN_REFLEN]; + const struct my_option *optp; + uchar* *value; + int error, i; + my_bool is_cmdline_arg= 1; + + LINT_INIT(opt_found); + /* handle_options() assumes arg0 (program name) always exists */ + DBUG_ASSERT(argc && *argc >= 1); + DBUG_ASSERT(argv && *argv); + (*argc)--; /* Skip the program name */ + (*argv)++; /* --- || ---- */ + init_variables(longopts, init_one_value); + + /* + Search for args_separator, if found, then the first part of the + arguments are loaded from configs + */ + for (pos= *argv, pos_end=pos+ *argc; pos != pos_end ; pos++) + { + if (*pos == args_separator) + { + is_cmdline_arg= 0; + break; + } + } + + for (pos= *argv, pos_end=pos+ *argc; pos != pos_end ; pos++) + { + char **first= pos; + char *cur_arg= *pos; + if (!is_cmdline_arg && (cur_arg == args_separator)) + { + is_cmdline_arg= 1; + + /* save the separator too if skip unkown options */ + if (my_getopt_skip_unknown) + (*argv)[argvpos++]= cur_arg; + else + (*argc)--; + continue; + } + if (cur_arg[0] == '-' && cur_arg[1] && !end_of_options) /* must be opt */ + { + char *argument= 0; + must_be_var= 0; + set_maximum_value= 0; + option_is_loose= 0; + + cur_arg++; /* skip '-' */ + if (*cur_arg == '-' || *cur_arg == 'O') /* check for long option, */ + { /* --set-variable, or -O */ + if (*cur_arg == 'O') + { + must_be_var= 1; + + if (!(*++cur_arg)) /* If not -Ovar=# */ + { + /* the argument must be in next argv */ + if (!*++pos) + { + if (my_getopt_print_errors) + my_getopt_error_reporter(ERROR_LEVEL, + "%s: Option '-O' requires an argument", + my_progname); + return EXIT_ARGUMENT_REQUIRED; + } + cur_arg= *pos; + (*argc)--; + } + } + else if (!getopt_compare_strings(cur_arg, "-set-variable", 13)) + { + must_be_var= 1; + if (cur_arg[13] == '=') + { + cur_arg+= 14; + if (!*cur_arg) + { + if (my_getopt_print_errors) + my_getopt_error_reporter(ERROR_LEVEL, + "%s: Option '--set-variable' requires an argument", + my_progname); + return EXIT_ARGUMENT_REQUIRED; + } + } + else if (cur_arg[14]) /* garbage, or another option. break out */ + must_be_var= 0; + else + { + /* the argument must be in next argv */ + if (!*++pos) + { + if (my_getopt_print_errors) + my_getopt_error_reporter(ERROR_LEVEL, + "%s: Option '--set-variable' requires an argument", + my_progname); + return EXIT_ARGUMENT_REQUIRED; + } + cur_arg= *pos; + (*argc)--; + } + } + else if (!must_be_var) + { + if (!*++cur_arg) /* skip the double dash */ + { + /* '--' means end of options, look no further */ + end_of_options= 1; + (*argc)--; + continue; + } + } + opt_str= check_struct_option(cur_arg, key_name); + optend= strcend(opt_str, '='); + length= (uint) (optend - opt_str); + if (*optend == '=') + optend++; + else + optend= 0; + + /* + Find first the right option. Return error in case of an ambiguous, + or unknown option + */ + LINT_INIT(prev_found); + optp= longopts; + if (!(opt_found= findopt(opt_str, length, &optp, &prev_found))) + { + /* + Didn't find any matching option. Let's see if someone called + option with a special option prefix + */ + if (!must_be_var) + { + if (optend) + must_be_var= 1; /* option is followed by an argument */ + for (i= 0; special_opt_prefix[i]; i++) + { + if (!getopt_compare_strings(special_opt_prefix[i], opt_str, + special_opt_prefix_lengths[i]) && + (opt_str[special_opt_prefix_lengths[i]] == '-' || + opt_str[special_opt_prefix_lengths[i]] == '_')) + { + /* + We were called with a special prefix, we can reuse opt_found + */ + opt_str+= special_opt_prefix_lengths[i] + 1; + length-= special_opt_prefix_lengths[i] + 1; + if (i == OPT_LOOSE) + option_is_loose= 1; + if ((opt_found= findopt(opt_str, length, &optp, &prev_found))) + { + if (opt_found > 1) + { + if (my_getopt_print_errors) + my_getopt_error_reporter(ERROR_LEVEL, + "%s: ambiguous option '--%s-%s' (--%s-%s)", + my_progname, special_opt_prefix[i], + cur_arg, special_opt_prefix[i], + prev_found); + return EXIT_AMBIGUOUS_OPTION; + } + switch (i) { + case OPT_SKIP: + case OPT_DISABLE: /* fall through */ + /* + double negation is actually enable again, + for example: --skip-option=0 -> option = TRUE + */ + optend= (optend && *optend == '0' && !(*(optend + 1))) ? + (char*) "1" : disabled_my_option; + break; + case OPT_ENABLE: + optend= (optend && *optend == '0' && !(*(optend + 1))) ? + disabled_my_option : (char*) "1"; + break; + case OPT_MAXIMUM: + set_maximum_value= 1; + must_be_var= 1; + break; + } + break; /* break from the inner loop, main loop continues */ + } + i= -1; /* restart the loop */ + } + } + } + if (!opt_found) + { + if (my_getopt_skip_unknown) + { + /* + preserve all the components of this unknown option, this may + occurr when the user provides options like: "-O foo" or + "--set-variable foo" (note that theres a space in there) + Generally, these kind of options are to be avoided + */ + do { + (*argv)[argvpos++]= *first++; + } while (first <= pos); + continue; + } + if (must_be_var) + { + if (my_getopt_print_errors) + my_getopt_error_reporter(option_is_loose ? + WARNING_LEVEL : ERROR_LEVEL, + "%s: unknown variable '%s'", + my_progname, cur_arg); + if (!option_is_loose) + return EXIT_UNKNOWN_VARIABLE; + } + else + { + if (my_getopt_print_errors) + my_getopt_error_reporter(option_is_loose ? + WARNING_LEVEL : ERROR_LEVEL, + "%s: unknown option '--%s'", + my_progname, cur_arg); + if (!option_is_loose) + return EXIT_UNKNOWN_OPTION; + } + if (option_is_loose) + { + (*argc)--; + continue; + } + } + } + if (opt_found > 1) + { + if (must_be_var) + { + if (my_getopt_print_errors) + my_getopt_error_reporter(ERROR_LEVEL, + "%s: variable prefix '%s' is not unique", + my_progname, opt_str); + return EXIT_VAR_PREFIX_NOT_UNIQUE; + } + else + { + if (my_getopt_print_errors) + my_getopt_error_reporter(ERROR_LEVEL, + "%s: ambiguous option '--%s' (%s, %s)", + my_progname, opt_str, prev_found, + optp->name); + return EXIT_AMBIGUOUS_OPTION; + } + } + if ((optp->var_type & GET_TYPE_MASK) == GET_DISABLED) + { + if (my_getopt_print_errors) + fprintf(stderr, + "%s: %s: Option '%s' used, but is disabled\n", my_progname, + option_is_loose ? "WARNING" : "ERROR", opt_str); + if (option_is_loose) + { + (*argc)--; + continue; + } + return EXIT_OPTION_DISABLED; + } + if (must_be_var && (optp->var_type & GET_TYPE_MASK) == GET_NO_ARG) + { + if (my_getopt_print_errors) + my_getopt_error_reporter(ERROR_LEVEL, + "%s: option '%s' cannot take an argument", + my_progname, optp->name); + return EXIT_NO_ARGUMENT_ALLOWED; + } + error= 0; + value= optp->var_type & GET_ASK_ADDR ? + (*getopt_get_addr)(key_name, (uint) strlen(key_name), optp, &error) : + optp->value; + if (error) + return error; + + if (optp->arg_type == NO_ARG) + { + if (optend && (optp->var_type & GET_TYPE_MASK) != GET_BOOL) + { + if (my_getopt_print_errors) + my_getopt_error_reporter(ERROR_LEVEL, + "%s: option '--%s' cannot take an argument", + my_progname, optp->name); + return EXIT_NO_ARGUMENT_ALLOWED; + } + if ((optp->var_type & GET_TYPE_MASK) == GET_BOOL) + { + /* + Set bool to 1 if no argument or if the user has used + --enable-'option-name'. + *optend was set to '0' if one used --disable-option + */ + (*argc)--; + if (!optend || *optend == '1' || + !my_strcasecmp(&my_charset_latin1, optend, "true")) + *((my_bool*) value)= (my_bool) 1; + else if (*optend == '0' || + !my_strcasecmp(&my_charset_latin1, optend, "false")) + *((my_bool*) value)= (my_bool) 0; + else + { + my_getopt_error_reporter(WARNING_LEVEL, + "%s: ignoring option '--%s' due to \ +invalid value '%s'", + my_progname, optp->name, optend); + continue; + } + if (get_one_option(optp->id, optp, + *((my_bool*) value) ? + (char*) "1" : disabled_my_option)) + return EXIT_ARGUMENT_INVALID; + continue; + } + argument= optend; + } + else if (optp->arg_type == OPT_ARG && + (optp->var_type & GET_TYPE_MASK) == GET_BOOL) + { + if (optend == disabled_my_option) + *((my_bool*) value)= (my_bool) 0; + else + { + if (!optend) /* No argument -> enable option */ + *((my_bool*) value)= (my_bool) 1; + else + argument= optend; + } + } + else if (optp->arg_type == REQUIRED_ARG && !optend) + { + /* Check if there are more arguments after this one, + + Note: options loaded from config file that requires value + should always be in the form '--option=value'. + */ + if (!is_cmdline_arg || !*++pos) + { + if (my_getopt_print_errors) + my_getopt_error_reporter(ERROR_LEVEL, + "%s: option '--%s' requires an argument", + my_progname, optp->name); + return EXIT_ARGUMENT_REQUIRED; + } + argument= *pos; + (*argc)--; + } + else + argument= optend; + } + else /* must be short option */ + { + for (optend= cur_arg; *optend; optend++) + { + opt_found= 0; + for (optp= longopts; optp->id; optp++) + { + if (optp->id == (int) (uchar) *optend) + { + /* Option recognized. Find next what to do with it */ + opt_found= 1; + if ((optp->var_type & GET_TYPE_MASK) == GET_DISABLED) + { + if (my_getopt_print_errors) + fprintf(stderr, + "%s: ERROR: Option '-%c' used, but is disabled\n", + my_progname, optp->id); + return EXIT_OPTION_DISABLED; + } + if ((optp->var_type & GET_TYPE_MASK) == GET_BOOL && + optp->arg_type == NO_ARG) + { + *((my_bool*) optp->value)= (my_bool) 1; + if (get_one_option(optp->id, optp, argument)) + return EXIT_UNSPECIFIED_ERROR; + continue; + } + else if (optp->arg_type == REQUIRED_ARG || + optp->arg_type == OPT_ARG) + { + if (*(optend + 1)) + { + /* The rest of the option is option argument */ + argument= optend + 1; + /* This is in effect a jump out of the outer loop */ + optend= (char*) " "; + } + else + { + if (optp->arg_type == OPT_ARG) + { + if (optp->var_type == GET_BOOL) + *((my_bool*) optp->value)= (my_bool) 1; + if (get_one_option(optp->id, optp, argument)) + return EXIT_UNSPECIFIED_ERROR; + continue; + } + /* Check if there are more arguments after this one */ + if (!pos[1]) + { + if (my_getopt_print_errors) + my_getopt_error_reporter(ERROR_LEVEL, + "%s: option '-%c' requires an argument", + my_progname, optp->id); + return EXIT_ARGUMENT_REQUIRED; + } + argument= *++pos; + (*argc)--; + /* the other loop will break, because *optend + 1 == 0 */ + } + } + if ((error= setval(optp, optp->value, argument, + set_maximum_value))) + { + my_getopt_error_reporter(ERROR_LEVEL, + "%s: Error while setting value '%s' to '%s'", + my_progname, argument, optp->name); + return error; + } + if (get_one_option(optp->id, optp, argument)) + return EXIT_UNSPECIFIED_ERROR; + break; + } + } + if (!opt_found) + { + if (my_getopt_print_errors) + my_getopt_error_reporter(ERROR_LEVEL, + "%s: unknown option '-%c'", + my_progname, *optend); + return EXIT_UNKNOWN_OPTION; + } + } + (*argc)--; /* option handled (short), decrease argument count */ + continue; + } + if ((error= setval(optp, value, argument, set_maximum_value))) + { + my_getopt_error_reporter(ERROR_LEVEL, + "%s: Error while setting value '%s' to '%s'", + my_progname, argument, optp->name); + return error; + } + if (get_one_option(optp->id, optp, argument)) + return EXIT_UNSPECIFIED_ERROR; + + (*argc)--; /* option handled (short or long), decrease argument count */ + } + else /* non-option found */ + (*argv)[argvpos++]= cur_arg; + } + /* + Destroy the first, already handled option, so that programs that look + for arguments in 'argv', without checking 'argc', know when to stop. + Items in argv, before the destroyed one, are all non-option -arguments + to the program, yet to be (possibly) handled. + */ + (*argv)[argvpos]= 0; + return 0; +} + + +/* + function: check_struct_option + + Arguments: Current argument under processing from argv and a variable + where to store the possible key name. + + Return value: In case option is a struct option, returns a pointer to + the current argument at the position where the struct option (key_name) + ends, the next character after the dot. In case argument is not a struct + option, returns a pointer to the argument. + + key_name will hold the name of the key, or 0 if not found. +*/ + +static char *check_struct_option(char *cur_arg, char *key_name) +{ + char *ptr, *end; + + ptr= strcend(cur_arg + 1, '.'); /* Skip the first character */ + end= strcend(cur_arg, '='); + + /* + If the first dot is after an equal sign, then it is part + of a variable value and the option is not a struct option. + Also, if the last character in the string before the ending + NULL, or the character right before equal sign is the first + dot found, the option is not a struct option. + */ + if (end - ptr > 1) + { + uint len= (uint) (ptr - cur_arg); + set_if_smaller(len, FN_REFLEN-1); + strmake(key_name, cur_arg, len); + return ++ptr; + } + else + { + key_name[0]= 0; + return cur_arg; + } +} + +/* + function: setval + + Arguments: opts, argument + Will set the option value to given value +*/ + +static int setval(const struct my_option *opts, uchar* *value, char *argument, + my_bool set_maximum_value) +{ + int err= 0; + + if (value && argument) + { + uchar* *result_pos= ((set_maximum_value) ? + opts->u_max_value : value); + + if (!result_pos) + return EXIT_NO_PTR_TO_VARIABLE; + + switch ((opts->var_type & GET_TYPE_MASK)) { + case GET_BOOL: /* If argument differs from 0, enable option, else disable */ + *((my_bool*) result_pos)= (my_bool) atoi(argument) != 0; + break; + case GET_INT: + *((int*) result_pos)= (int) getopt_ll(argument, opts, &err); + break; + case GET_UINT: + *((uint*) result_pos)= (uint) getopt_ull(argument, opts, &err); + break; + case GET_LONG: + *((long*) result_pos)= (long) getopt_ll(argument, opts, &err); + break; + case GET_ULONG: + *((long*) result_pos)= (long) getopt_ull(argument, opts, &err); + break; + case GET_LL: + *((longlong*) result_pos)= getopt_ll(argument, opts, &err); + break; + case GET_ULL: + *((ulonglong*) result_pos)= getopt_ull(argument, opts, &err); + break; + case GET_DOUBLE: + *((double*) result_pos)= getopt_double(argument, opts, &err); + break; + case GET_STR: + *((char**) result_pos)= argument; + break; + case GET_STR_ALLOC: + if ((*((char**) result_pos))) + my_free((*(char**) result_pos), MYF(MY_WME | MY_FAE)); + if (!(*((char**) result_pos)= my_strdup(argument, MYF(MY_WME)))) + return EXIT_OUT_OF_MEMORY; + break; + case GET_ENUM: + if (((*(int*)result_pos)= find_type(argument, opts->typelib, 2) - 1) < 0) + return EXIT_ARGUMENT_INVALID; + break; + case GET_SET: + *((ulonglong*)result_pos)= find_typeset(argument, opts->typelib, &err); + if (err) + return EXIT_ARGUMENT_INVALID; + break; + default: /* dummy default to avoid compiler warnings */ + break; + } + if (err) + return EXIT_UNKNOWN_SUFFIX; + } + return 0; +} + + +/* + Find option + + SYNOPSIS + findopt() + optpat Prefix of option to find (with - or _) + length Length of optpat + opt_res Options + ffname Place for pointer to first found name + + IMPLEMENTATION + Go through all options in the my_option struct. Return number + of options found that match the pattern and in the argument + list the option found, if any. In case of ambiguous option, store + the name in ffname argument + + RETURN + 0 No matching options + # Number of matching options + ffname points to first matching option +*/ + +static int findopt(char *optpat, uint length, + const struct my_option **opt_res, + char **ffname) +{ + uint count; + struct my_option *opt= (struct my_option *) *opt_res; + + for (count= 0; opt->name; opt++) + { + if (!getopt_compare_strings(opt->name, optpat, length)) /* match found */ + { + (*opt_res)= opt; + if (!opt->name[length]) /* Exact match */ + return 1; + if (!count) + { + count= 1; + *ffname= (char *) opt->name; /* We only need to know one prev */ + } + else if (strcmp(*ffname, opt->name)) + { + /* + The above test is to not count same option twice + (see mysql.cc, option "help") + */ + count++; + } + } + } + return count; +} + + +/* + function: compare_strings + + Works like strncmp, other than 1.) considers '-' and '_' the same. + 2.) Returns -1 if strings differ, 0 if they are equal +*/ + +my_bool getopt_compare_strings(register const char *s, register const char *t, + uint length) +{ + char const *end= s + length; + for (;s != end ; s++, t++) + { + if ((*s != '-' ? *s : '_') != (*t != '-' ? *t : '_')) + return 1; + } + return 0; +} + +/* + function: eval_num_suffix + + Transforms a number with a suffix to real number. Suffix can + be k|K for kilo, m|M for mega or g|G for giga. +*/ + +static longlong eval_num_suffix(char *argument, int *error, char *option_name) +{ + char *endchar; + longlong num; + + *error= 0; + errno= 0; + num= strtoll(argument, &endchar, 10); + if (errno == ERANGE) + { + my_getopt_error_reporter(ERROR_LEVEL, + "Incorrect integer value: '%s'", argument); + *error= 1; + return 0; + } + if (*endchar == 'k' || *endchar == 'K') + num*= 1024L; + else if (*endchar == 'm' || *endchar == 'M') + num*= 1024L * 1024L; + else if (*endchar == 'g' || *endchar == 'G') + num*= 1024L * 1024L * 1024L; + else if (*endchar) + { + fprintf(stderr, + "Unknown suffix '%c' used for variable '%s' (value '%s')\n", + *endchar, option_name, argument); + *error= 1; + return 0; + } + return num; +} + +/* + function: getopt_ll + + Evaluates and returns the value that user gave as an argument + to a variable. Recognizes (case insensitive) K as KILO, M as MEGA + and G as GIGA bytes. Some values must be in certain blocks, as + defined in the given my_option struct, this function will check + that those values are honored. + In case of an error, set error value in *err. +*/ + +static longlong getopt_ll(char *arg, const struct my_option *optp, int *err) +{ + longlong num=eval_num_suffix(arg, err, (char*) optp->name); + return getopt_ll_limit_value(num, optp, NULL); +} + +/* + function: getopt_ll_limit_value + + Applies min/max/block_size to a numeric value of an option. + Returns "fixed" value. +*/ + +longlong getopt_ll_limit_value(longlong num, const struct my_option *optp, + my_bool *fix) +{ + longlong old= num; + my_bool adjusted= FALSE; + char buf1[255], buf2[255]; + ulonglong block_size= (optp->block_size ? (ulonglong) optp->block_size : 1L); + + if (num > 0 && ((ulonglong) num > (ulonglong) optp->max_value) && + optp->max_value) /* if max value is not set -> no upper limit */ + { + num= (ulonglong) optp->max_value; + adjusted= TRUE; + } + + switch ((optp->var_type & GET_TYPE_MASK)) { + case GET_INT: + if (num > (longlong) INT_MAX) + { + num= ((longlong) INT_MAX); + adjusted= TRUE; + } + break; + case GET_LONG: +#if SIZEOF_LONG < SIZEOF_LONG_LONG + if (num > (longlong) LONG_MAX) + { + num= ((longlong) LONG_MAX); + adjusted= TRUE; + } +#endif + break; + default: + DBUG_ASSERT((optp->var_type & GET_TYPE_MASK) == GET_LL); + break; + } + + num= ((num - optp->sub_size) / block_size); + num= (longlong) (num * block_size); + + if (num < optp->min_value) + { + num= optp->min_value; + if (old < optp->min_value) + adjusted= TRUE; + } + + if (fix) + *fix= adjusted; + else if (adjusted) + my_getopt_error_reporter(WARNING_LEVEL, + "option '%s': signed value %s adjusted to %s", + optp->name, llstr(old, buf1), llstr(num, buf2)); + return num; +} + +/* + function: getopt_ull + + This is the same as getopt_ll, but is meant for unsigned long long + values. +*/ + +static ulonglong getopt_ull(char *arg, const struct my_option *optp, int *err) +{ + ulonglong num= eval_num_suffix(arg, err, (char*) optp->name); + return getopt_ull_limit_value(num, optp, NULL); +} + + +ulonglong getopt_ull_limit_value(ulonglong num, const struct my_option *optp, + my_bool *fix) +{ + my_bool adjusted= FALSE; + ulonglong old= num; + char buf1[255], buf2[255]; + + if ((ulonglong) num > (ulonglong) optp->max_value && + optp->max_value) /* if max value is not set -> no upper limit */ + { + num= (ulonglong) optp->max_value; + adjusted= TRUE; + } + + switch ((optp->var_type & GET_TYPE_MASK)) { + case GET_UINT: + if (num > (ulonglong) UINT_MAX) + { + num= ((ulonglong) UINT_MAX); + adjusted= TRUE; + } + break; + case GET_ULONG: +#if SIZEOF_LONG < SIZEOF_LONG_LONG + if (num > (ulonglong) ULONG_MAX) + { + num= ((ulonglong) ULONG_MAX); + adjusted= TRUE; + } +#endif + break; + default: + DBUG_ASSERT((optp->var_type & GET_TYPE_MASK) == GET_ULL); + break; + } + + if (optp->block_size > 1) + { + num/= (ulonglong) optp->block_size; + num*= (ulonglong) optp->block_size; + } + + if (num < (ulonglong) optp->min_value) + { + num= (ulonglong) optp->min_value; + if (old < (ulonglong) optp->min_value) + adjusted= TRUE; + } + + if (fix) + *fix= adjusted; + else if (adjusted) + my_getopt_error_reporter(WARNING_LEVEL, + "option '%s': unsigned value %s adjusted to %s", + optp->name, ullstr(old, buf1), ullstr(num, buf2)); + return num; +} + + +/* + Get double value withing ranges + + Evaluates and returns the value that user gave as an argument to a variable. + + RETURN + decimal value of arg + + In case of an error, prints an error message and sets *err to + EXIT_ARGUMENT_INVALID. Otherwise err is not touched +*/ + +static double getopt_double(char *arg, const struct my_option *optp, int *err) +{ + double num; + int error; + char *end= arg + 1000; /* Big enough as *arg is \0 terminated */ + num= my_strtod(arg, &end, &error); + if (end[0] != 0 || error) + { + fprintf(stderr, + "%s: ERROR: Invalid decimal value for option '%s'\n", + my_progname, optp->name); + *err= EXIT_ARGUMENT_INVALID; + return 0.0; + } + if (optp->max_value && num > (double) optp->max_value) + num= (double) optp->max_value; + return max(num, (double) optp->min_value); +} + +/* + Init one value to it's default values + + SYNOPSIS + init_one_value() + option Option to initialize + value Pointer to variable +*/ + +static void init_one_value(const struct my_option *option, uchar* *variable, + longlong value) +{ + DBUG_ENTER("init_one_value"); + switch ((option->var_type & GET_TYPE_MASK)) { + case GET_BOOL: + *((my_bool*) variable)= (my_bool) value; + break; + case GET_INT: + *((int*) variable)= (int) getopt_ll_limit_value((int) value, option, NULL); + break; + case GET_ENUM: + *((uint*) variable)= (uint) value; + break; + case GET_UINT: + *((uint*) variable)= (uint) getopt_ull_limit_value((uint) value, option, NULL); + break; + case GET_LONG: + *((long*) variable)= (long) getopt_ll_limit_value((long) value, option, NULL); + break; + case GET_ULONG: + *((ulong*) variable)= (ulong) getopt_ull_limit_value((ulong) value, option, NULL); + break; + case GET_LL: + *((longlong*) variable)= (longlong) getopt_ll_limit_value((longlong) value, option, NULL); + break; + case GET_ULL: /* Fall through */ + case GET_SET: + *((ulonglong*) variable)= (ulonglong) getopt_ull_limit_value((ulonglong) value, option, NULL); + break; + case GET_DOUBLE: + *((double*) variable)= (double) value; + break; + case GET_STR: + /* + Do not clear variable value if it has no default value. + The default value may already be set. + NOTE: To avoid compiler warnings, we first cast longlong to intptr, + so that the value has the same size as a pointer. + */ + if ((char*) (intptr) value) + *((char**) variable)= (char*) (intptr) value; + break; + case GET_STR_ALLOC: + /* + Do not clear variable value if it has no default value. + The default value may already be set. + NOTE: To avoid compiler warnings, we first cast longlong to intptr, + so that the value has the same size as a pointer. + */ + if ((char*) (intptr) value) + { + my_free((*(char**) variable), MYF(MY_ALLOW_ZERO_PTR)); + *((char**) variable)= my_strdup((char*) (intptr) value, MYF(MY_WME)); + } + break; + default: /* dummy default to avoid compiler warnings */ + break; + } + DBUG_VOID_RETURN; +} + + +/* + Init one value to it's default values + + SYNOPSIS + init_one_value() + option Option to initialize + value Pointer to variable +*/ + +static void fini_one_value(const struct my_option *option, uchar* *variable, + longlong value __attribute__ ((unused))) +{ + DBUG_ENTER("fini_one_value"); + switch ((option->var_type & GET_TYPE_MASK)) { + case GET_STR_ALLOC: + my_free((*(char**) variable), MYF(MY_ALLOW_ZERO_PTR)); + *((char**) variable)= NULL; + break; + default: /* dummy default to avoid compiler warnings */ + break; + } + DBUG_VOID_RETURN; +} + + +void my_cleanup_options(const struct my_option *options) +{ + init_variables(options, fini_one_value); +} + + +/* + initialize all variables to their default values + + SYNOPSIS + init_variables() + options Array of options + + NOTES + We will initialize the value that is pointed to by options->value. + If the value is of type GET_ASK_ADDR, we will also ask for the address + for a value and initialize. +*/ + +static void init_variables(const struct my_option *options, + init_func_p init_one_value) +{ + DBUG_ENTER("init_variables"); + for (; options->name; options++) + { + uchar* *variable; + DBUG_PRINT("options", ("name: '%s'", options->name)); + /* + We must set u_max_value first as for some variables + options->u_max_value == options->value and in this case we want to + set the value to default value. + */ + if (options->u_max_value) + init_one_value(options, options->u_max_value, options->max_value); + if (options->value) + init_one_value(options, options->value, options->def_value); + if (options->var_type & GET_ASK_ADDR && + (variable= (*getopt_get_addr)("", 0, options, 0))) + init_one_value(options, variable, options->def_value); + } + DBUG_VOID_RETURN; +} + + +/* + function: my_print_options + + Print help for all options and variables. +*/ + +void my_print_help(const struct my_option *options) +{ + uint col, name_space= 22, comment_space= 57; + const char *line_end; + const struct my_option *optp; + + for (optp= options; optp->id; optp++) + { + if (optp->id < 256) + { + printf(" -%c%s", optp->id, strlen(optp->name) ? ", " : " "); + col= 6; + } + else + { + printf(" "); + col= 2; + } + if (strlen(optp->name)) + { + printf("--%s", optp->name); + col+= 2 + (uint) strlen(optp->name); + if ((optp->var_type & GET_TYPE_MASK) == GET_STR || + (optp->var_type & GET_TYPE_MASK) == GET_STR_ALLOC) + { + printf("%s=name%s ", optp->arg_type == OPT_ARG ? "[" : "", + optp->arg_type == OPT_ARG ? "]" : ""); + col+= (optp->arg_type == OPT_ARG) ? 8 : 6; + } + else if ((optp->var_type & GET_TYPE_MASK) == GET_NO_ARG || + (optp->var_type & GET_TYPE_MASK) == GET_BOOL) + { + putchar(' '); + col++; + } + else + { + printf("%s=#%s ", optp->arg_type == OPT_ARG ? "[" : "", + optp->arg_type == OPT_ARG ? "]" : ""); + col+= (optp->arg_type == OPT_ARG) ? 5 : 3; + } + if (col > name_space && optp->comment && *optp->comment) + { + putchar('\n'); + col= 0; + } + } + for (; col < name_space; col++) + putchar(' '); + if (optp->comment && *optp->comment) + { + const char *comment= optp->comment, *end= strend(comment); + + while ((uint) (end - comment) > comment_space) + { + for (line_end= comment + comment_space; + *line_end != ' ' && line_end > comment; + line_end--); + if (line_end == comment) + { + /* There was no space, try to find one going forward */ + for (line_end= comment + comment_space; + *line_end != ' ' && line_end < end; + line_end++); + } + for (; comment != line_end; comment++) + putchar(*comment); + if (comment < end) + { + comment++; /* skip the space, as a newline will take it's place now */ + putchar('\n'); + for (col= 0; col < name_space; col++) + putchar(' '); + } + } + printf("%s", comment); + } + putchar('\n'); + if ((optp->var_type & GET_TYPE_MASK) == GET_NO_ARG || + (optp->var_type & GET_TYPE_MASK) == GET_BOOL) + { + if (optp->def_value != 0) + { + printf("%*s(Defaults to on; use --skip-%s to disable.)\n", name_space, "", optp->name); + } + } + } +} + + +/* + function: my_print_options + + Print variables. +*/ + +void my_print_variables(const struct my_option *options) +{ + uint name_space= 34, length, nr; + ulonglong bit, llvalue; + char buff[255]; + const struct my_option *optp; + + printf("\nVariables (--variable-name=value)\n"); + printf("and boolean options {FALSE|TRUE} Value (after reading options)\n"); + printf("--------------------------------- -----------------------------\n"); + for (optp= options; optp->id; optp++) + { + uchar* *value= (optp->var_type & GET_ASK_ADDR ? + (*getopt_get_addr)("", 0, optp, 0) : optp->value); + if (value) + { + printf("%s ", optp->name); + length= (uint) strlen(optp->name)+1; + for (; length < name_space; length++) + putchar(' '); + switch ((optp->var_type & GET_TYPE_MASK)) { + case GET_SET: + if (!(llvalue= *(ulonglong*) value)) + printf("%s\n", "(No default value)"); + else + for (nr= 0, bit= 1; llvalue && nr < optp->typelib->count; nr++, bit<<=1) + { + if (!(bit & llvalue)) + continue; + llvalue&= ~bit; + printf( llvalue ? "%s," : "%s\n", get_type(optp->typelib, nr)); + } + break; + case GET_ENUM: + printf("%s\n", get_type(optp->typelib, *(uint*) value)); + break; + case GET_STR: + case GET_STR_ALLOC: /* fall through */ + printf("%s\n", *((char**) value) ? *((char**) value) : + "(No default value)"); + break; + case GET_BOOL: + printf("%s\n", *((my_bool*) value) ? "TRUE" : "FALSE"); + break; + case GET_INT: + printf("%d\n", *((int*) value)); + break; + case GET_UINT: + printf("%d\n", *((uint*) value)); + break; + case GET_LONG: + printf("%ld\n", *((long*) value)); + break; + case GET_ULONG: + printf("%lu\n", *((ulong*) value)); + break; + case GET_LL: + printf("%s\n", llstr(*((longlong*) value), buff)); + break; + case GET_ULL: + longlong2str(*((ulonglong*) value), buff, 10); + printf("%s\n", buff); + break; + case GET_DOUBLE: + printf("%g\n", *(double*) value); + break; + default: + printf("(Disabled)\n"); + break; + } + } + } +} diff --git a/externals/mysql/mysys/my_getpagesize.c b/externals/mysql/mysys/my_getpagesize.c new file mode 100644 index 00000000000..b0560cede35 --- /dev/null +++ b/externals/mysql/mysys/my_getpagesize.c @@ -0,0 +1,40 @@ +/* Copyright (C) 2000-2003 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#include "mysys_priv.h" + +#ifndef HAVE_GETPAGESIZE + +#if defined __WIN__ + +int my_getpagesize(void) +{ + SYSTEM_INFO si; + GetSystemInfo(&si); + return si.dwPageSize; +} + +#else + +/* Default implementation */ +int my_getpagesize(void) +{ + return (int)8192; +} + +#endif + +#endif + diff --git a/externals/mysql/mysys/my_getsystime.c b/externals/mysql/mysys/my_getsystime.c new file mode 100644 index 00000000000..46e0c5469e4 --- /dev/null +++ b/externals/mysql/mysys/my_getsystime.c @@ -0,0 +1,225 @@ +/* Copyright (C) 2004 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +/* get time since epoc in 100 nanosec units */ +/* thus to get the current time we should use the system function + with the highest possible resolution */ + +/* + TODO: in functions my_micro_time() and my_micro_time_and_time() there + exists some common code that should be merged into a function. +*/ + +#include "mysys_priv.h" +#include "my_static.h" + +#ifdef __NETWARE__ +#include <nks/time.h> +#endif + +ulonglong my_getsystime() +{ +#ifdef HAVE_CLOCK_GETTIME + struct timespec tp; + clock_gettime(CLOCK_REALTIME, &tp); + return (ulonglong)tp.tv_sec*10000000+(ulonglong)tp.tv_nsec/100; +#elif defined(__WIN__) + 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; +#elif defined(__NETWARE__) + NXTime_t tm; + NXGetTime(NX_SINCE_1970, NX_NSECONDS, &tm); + return (ulonglong)tm/100; +#else + /* TODO: check for other possibilities for hi-res timestamping */ + struct timeval tv; + gettimeofday(&tv,NULL); + return (ulonglong)tv.tv_sec*10000000+(ulonglong)tv.tv_usec*10; +#endif +} + + +/* + Return current time + + SYNOPSIS + my_time() + flags If MY_WME is set, write error if time call fails + +*/ + +time_t my_time(myf flags __attribute__((unused))) +{ + time_t t; +#ifdef HAVE_GETHRTIME + (void) my_micro_time_and_time(&t); + return t; +#else + /* The following loop is here beacuse time() may fail on some systems */ + while ((t= time(0)) == (time_t) -1) + { + if (flags & MY_WME) + fprintf(stderr, "%s: Warning: time() call failed\n", my_progname); + } + return t; +#endif +} + + +/* + Return time in micro seconds + + SYNOPSIS + my_micro_time() + + NOTES + 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. + + For windows platforms we need the frequency value of the CUP. This is + initalized in my_init.c through QueryPerformanceFrequency(). + + If Windows platform doesn't support QueryPerformanceFrequency() we will + obtain the time via GetClockCount, which only supports milliseconds. + + RETURN + Value in microseconds from some undefined point in time +*/ + +ulonglong my_micro_time() +{ +#if defined(__WIN__) + ulonglong newtime; + GetSystemTimeAsFileTime((FILETIME*)&newtime); + return (newtime/10); +#elif defined(HAVE_GETHRTIME) + return gethrtime()/1000; +#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 /* defined(__WIN__) */ +} + + +/* + Return time in seconds and timer in microseconds (not different start!) + + SYNOPSIS + my_micro_time_and_time() + time_arg Will be set to seconds since epoch (00:00:00 UTC, + January 1, 1970) + + NOTES + This function is to be useful when we need both the time and microtime. + For example in MySQL this is used to get the query time start of a query + and to measure the time of a query (for the slow query log) + + IMPLEMENTATION + Value of time is as in time() call. + Value of microtime is same as my_micro_time(), which may be totally + unrealated to time() + + RETURN + Value in microseconds from some undefined point in time +*/ + +#define DELTA_FOR_SECONDS 500000000LL /* Half a second */ + +ulonglong my_micro_time_and_time(time_t *time_arg) +{ +#if defined(__WIN__) + ulonglong newtime; + GetSystemTimeAsFileTime((FILETIME*)&newtime); + *time_arg= (time_t) ((newtime - OFFSET_TO_EPOCH) / 10000000); + return (newtime/10); +#elif defined(HAVE_GETHRTIME) + /* + Solaris has a very slow time() call. We optimize this by using the very + fast gethrtime() call and only calling time() every 1/2 second + */ + static hrtime_t prev_gethrtime= 0; + static time_t cur_time= 0; + hrtime_t cur_gethrtime; + + pthread_mutex_lock(&THR_LOCK_time); + cur_gethrtime= gethrtime(); + if ((cur_gethrtime - prev_gethrtime) > DELTA_FOR_SECONDS) + { + cur_time= time(0); + prev_gethrtime= cur_gethrtime; + } + *time_arg= cur_time; + pthread_mutex_unlock(&THR_LOCK_time); + return cur_gethrtime/1000; +#else + ulonglong newtime; + struct timeval t; + /* + The following loop is here because gettimeofday may fail on some systems + */ + while (gettimeofday(&t, NULL) != 0) + {} + *time_arg= t.tv_sec; + newtime= (ulonglong)t.tv_sec * 1000000 + t.tv_usec; + return newtime; +#endif /* defined(__WIN__) */ +} + + +/* + Returns current time + + SYNOPSIS + my_time_possible_from_micro() + microtime Value from very recent my_micro_time() + + NOTES + This function returns the current time. The microtime argument is only used + if my_micro_time() uses a function that can safely be converted to the + current time. + + RETURN + current time +*/ + +time_t my_time_possible_from_micro(ulonglong microtime __attribute__((unused))) +{ +#if defined(__WIN__) + time_t t; + while ((t= time(0)) == (time_t) -1) + {} + return t; +#elif defined(HAVE_GETHRTIME) + return my_time(0); /* Cached time */ +#else + return (time_t) (microtime / 1000000); +#endif /* defined(__WIN__) */ +} + diff --git a/externals/mysql/mysys/my_getwd.c b/externals/mysql/mysys/my_getwd.c new file mode 100644 index 00000000000..0b467351f4b --- /dev/null +++ b/externals/mysql/mysys/my_getwd.c @@ -0,0 +1,185 @@ +/* Copyright (C) 2000 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +/* my_setwd() and my_getwd() works with intern_filenames !! */ + +#include "mysys_priv.h" +#include <m_string.h> +#include "mysys_err.h" +#ifdef HAVE_GETWD +#include <sys/param.h> +#endif +#if defined(__WIN__) +#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: %p size: %u MyFlags %d", + buf, (uint) size, MyFlags)); + + if (curr_dir[0]) /* Current pos is saved here */ + (void) strmake(buf,&curr_dir[0],size-1); + else + { +#if defined(HAVE_GETCWD) + if (!getcwd(buf,size-2) && MyFlags & MY_WME) + { + my_errno=errno; + my_error(EE_GETWD,MYF(ME_BELL+ME_WAITTANG),errno); + return(-1); + } +#elif defined(HAVE_GETWD) + { + char pathname[MAXPATHLEN]; + getwd(pathname); + strmake(buf,pathname,size-1); + } +#elif defined(VMS) + if (!getcwd(buf,size-2,1) && MyFlags & MY_WME) + { + my_errno=errno; + my_error(EE_GETWD,MYF(ME_BELL+ME_WAITTANG),errno); + return(-1); + } + intern_filename(buf,buf); +#else +#error "No way to get current directory" +#endif + 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; +#if defined(VMS) + char buff[FN_REFLEN]; +#endif + 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; +#ifdef VMS + { + pos=strmov(buff,dir); + if (pos[-1] != FN_LIBCHAR) + { + pos[0]=FN_LIBCHAR; /* Mark as directory */ + pos[1]=0; + } + system_filename(buff,buff); /* Change to VMS format */ + dir=buff; + } +#endif /* VMS */ + if ((res=chdir((char*) dir)) != 0) + { + my_errno=errno; + if (MyFlags & MY_WME) + my_error(EE_SETWD,MYF(ME_BELL+ME_WAITTANG),start,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(register 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 test(strchr(name, FN_LIBCHAR)) +#if FN_LIBCHAR != '/' + || test(strchr(name,'/')) +#endif +#ifdef FN_DEVCHAR + || test(strchr(name, FN_DEVCHAR)) +#endif + ; +} diff --git a/externals/mysql/mysys/my_handler_errors.h b/externals/mysql/mysys/my_handler_errors.h new file mode 100644 index 00000000000..47cce9dcd2d --- /dev/null +++ b/externals/mysql/mysys/my_handler_errors.h @@ -0,0 +1,137 @@ + +/* + Errors a handler can give you +*/ + +static const char *handler_error_messages[]= +{ + /* HA_ERR_KEY_NOT_FOUND */ + "Didn't find key on read or update", + /* HA_ERR_FOUND_DUPP_KEY */ + "Duplicate key on write or update", + /* HA_ERR_INTERNAL_ERROR */ + "Internal (unspecified) error in handler", + /* HA_ERR_RECORD_CHANGED */ + "Someone has changed the row since it was read (while the table was locked to prevent it)", + /* HA_ERR_WRONG_INDEX */ + "Wrong index given to function", + /* empty */ + "Undefined handler error 125", + /* HA_ERR_CRASHED */ + "Index file is crashed", + /* HA_ERR_WRONG_IN_RECORD */ + "Record file is crashed", + /* HA_ERR_OUT_OF_MEM */ + "Out of memory in engine", + /* empty */ + "Undefined handler error 129", + /* HA_ERR_NOT_A_TABLE */ + "Incorrect file format", + /* HA_ERR_WRONG_COMMAND */ + "Command not supported by database", + /* HA_ERR_OLD_FILE */ + "Old database file", + /* HA_ERR_NO_ACTIVE_RECORD */ + "No record read before update", + /* HA_ERR_RECORD_DELETED */ + "Record was already deleted (or record file crashed)", + /* HA_ERR_RECORD_FILE_FULL */ + "No more room in record file", + /* HA_ERR_INDEX_FILE_FULL */ + "No more room in index file", + /* HA_ERR_END_OF_FILE */ + "No more records (read after end of file)", + /* HA_ERR_UNSUPPORTED */ + "Unsupported extension used for table", + /* HA_ERR_TO_BIG_ROW */ + "Too big row", + /* HA_WRONG_CREATE_OPTION */ + "Wrong create options", + /* HA_ERR_FOUND_DUPP_UNIQUE */ + "Duplicate unique key or constraint on write or update", + /* HA_ERR_UNKNOWN_CHARSET */ + "Unknown character set used in table", + /* HA_ERR_WRONG_MRG_TABLE_DEF */ + "Conflicting table definitions in sub-tables of MERGE table", + /* HA_ERR_CRASHED_ON_REPAIR */ + "Table is crashed and last repair failed", + /* HA_ERR_CRASHED_ON_USAGE */ + "Table was marked as crashed and should be repaired", + /* HA_ERR_LOCK_WAIT_TIMEOUT */ + "Lock timed out; Retry transaction", + /* HA_ERR_LOCK_TABLE_FULL */ + "Lock table is full; Restart program with a larger lock table", + /* HA_ERR_READ_ONLY_TRANSACTION */ + "Updates are not allowed under a read only transactions", + /* HA_ERR_LOCK_DEADLOCK */ + "Lock deadlock; Retry transaction", + /* HA_ERR_CANNOT_ADD_FOREIGN */ + "Foreign key constraint is incorrectly formed", + /* HA_ERR_NO_REFERENCED_ROW */ + "Cannot add a child row", + /* HA_ERR_ROW_IS_REFERENCED */ + "Cannot delete a parent row", + /* HA_ERR_NO_SAVEPOINT */ + "No savepoint with that name", + /* HA_ERR_NON_UNIQUE_BLOCK_SIZE */ + "Non unique key block size", + /* HA_ERR_NO_SUCH_TABLE */ + "The table does not exist in engine", + /* HA_ERR_TABLE_EXIST */ + "The table already existed in storage engine", + /* HA_ERR_NO_CONNECTION */ + "Could not connect to storage engine", + /* HA_ERR_NULL_IN_SPATIAL */ + "Unexpected null pointer found when using spatial index", + /* HA_ERR_TABLE_DEF_CHANGED */ + "The table changed in storage engine", + /* HA_ERR_NO_PARTITION_FOUND */ + "There's no partition in table for the given value", + /* HA_ERR_RBR_LOGGING_FAILED */ + "Row-based binary logging of row failed", + /* HA_ERR_DROP_INDEX_FK */ + "Index needed in foreign key constraint", + /* HA_ERR_FOREIGN_DUPLICATE_KEY */ + "Upholding foreign key constraints would lead to a duplicate key error in " + "some other table", + /* HA_ERR_TABLE_NEEDS_UPGRADE */ + "Table needs to be upgraded before it can be used", + /* HA_ERR_TABLE_READONLY */ + "Table is read only", + /* HA_ERR_AUTOINC_READ_FAILED */ + "Failed to get next auto increment value", + /* HA_ERR_AUTOINC_ERANGE */ + "Failed to set row auto increment value", + /* HA_ERR_GENERIC */ + "Unknown (generic) error from engine", + /* HA_ERR_RECORD_IS_THE_SAME */ + "Record was not update. Original values was same as new values", + /* HA_ERR_LOGGING_IMPOSSIBLE */ + "It is not possible to log this statement", + /* HA_ERR_TABLESPACE_EXIST */ + "Tablespace exists", + /* HA_ERR_CORRUPT_EVENT */ + "The event was corrupt, leading to illegal data being read", + /* HA_ERR_NEW_FILE */ + "The table is of a new format not supported by this version", + /* HA_ERR_ROWS_EVENT_APPLY */ + "The event could not be processed. No other handler error happened", + /* HA_ERR_INITIALIZATION */ + "Got a fatal error during initialization of handler", + /* HA_ERR_FILE_TOO_SHORT */ + "File too short; Expected more data in file", + + /* HA_ERR_WRONG_CRC */ + "Read page with wrong checksum", + /* HA_ERR_LOCK_OR_ACTIVE_TRANSACTION */ + "Lock or active transaction", /* TODO: get a better message */ + /* HA_ERR_NO_SUCH_TABLESPACE */ + "No such table space", /* TODO: get a better message */ + /* HA_ERR_TABLESPACE_NOT_EMPTY */ + "Tablespace not empty", /* TODO: get a better message */ + /* HA_ERR_TABLESPACE_DATAFILE_EXIST */ + "Tablespace data file already exists", /* TODO: get a better message */ + /* HA_ERR_ROW_NOT_VISIBLE */ + "Row is not visible by the current transaction" +}; + diff --git a/externals/mysql/mysys/my_init.c b/externals/mysql/mysys/my_init.c new file mode 100644 index 00000000000..45545fb75d7 --- /dev/null +++ b/externals/mysql/mysys/my_init.c @@ -0,0 +1,558 @@ +/* Copyright (C) 2000-2003 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#include "mysys_priv.h" +#include "my_static.h" +#include "mysys_err.h" +#include <m_string.h> +#include <m_ctype.h> +#include <signal.h> +#ifdef VMS +#include <my_static.c> +#include <m_ctype.h> +#endif +#ifdef __WIN__ +#ifdef _MSC_VER +#include <locale.h> +#include <crtdbg.h> +/* WSAStartup needs winsock library*/ +#pragma comment(lib, "ws2_32") +#endif +my_bool have_tcpip=0; +static void my_win_init(void); +static my_bool win32_init_tcp_ip(); +#else +#define my_win_init() +#endif +#ifdef __NETWARE__ +static void netware_init(); +#else +#define netware_init() +#endif + +my_bool my_init_done= 0; +uint mysys_usage_id= 0; /* Incremented for each my_init() */ +ulong my_thread_stack_size= 65536; + +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; +} + + +/* + Init my_sys functions and my_sys variabels + + SYNOPSIS + my_init() + + RETURN + 0 ok + 1 Couldn't initialize environment +*/ + +my_bool my_init(void) +{ + char * str; + if (my_init_done) + return 0; + my_init_done=1; + mysys_usage_id++; + my_umask= 0660; /* Default umask for new files */ + my_umask_dir= 0700; /* Default umask for new directories */ + init_glob_errs(); + my_progname_short= "unknown"; + if (my_progname) + my_progname_short= my_progname + dirname_length(my_progname); + +#if defined(THREAD) + (void) my_threadattr_global_init(); +# if defined(SAFE_MUTEX) + safe_mutex_global_init(); /* Must be called early */ +# elif defined(MY_PTHREAD_FASTMUTEX) + fastmutex_global_init(); /* Must be called early */ +# endif +#endif + netware_init(); +#ifdef THREAD +#if defined(HAVE_PTHREAD_INIT) + pthread_init(); /* Must be called before DBUG_ENTER */ +#endif + if (my_thread_global_init()) + return 1; +#if !defined( __WIN__) && !defined(__NETWARE__) + sigfillset(&my_signals); /* signals blocked by mf_brkhant */ +#endif +#endif /* THREAD */ + { + DBUG_ENTER("my_init"); + DBUG_PROCESS((char*) (my_progname ? my_progname : "unknown")); + if (!home_dir) + { /* Don't initialize twice */ + my_win_init(); + if ((home_dir=getenv("HOME")) != 0) + home_dir=intern_filename(home_dir_buff,home_dir); +#ifndef VMS + /* Default creation of new files */ + if ((str=getenv("UMASK")) != 0) + my_umask=(int) (atoi_octal(str) | 0600); + /* Default creation of new dir's */ + if ((str=getenv("UMASK_DIR")) != 0) + my_umask_dir=(int) (atoi_octal(str) | 0700); +#endif +#ifdef VMS + init_ctype(); /* Stupid linker don't link _ctype.c */ +#endif + DBUG_PRINT("exit",("home: '%s'",home_dir)); + } +#ifdef __WIN__ + win32_init_tcp_ip(); +#endif + DBUG_RETURN(0); + } +} /* my_init */ + + + /* End my_sys */ + +void my_end(int infoflag) +{ + /* + this code is suboptimal to workaround a bug in + Sun CC: Sun C++ 5.6 2004/06/02 for x86, and should not be + optimized until this compiler is not in use anymore + */ + FILE *info_file= DBUG_FILE; + my_bool print_info= (info_file != stderr); + + if (!my_init_done) + return; + + /* + We do not use DBUG_ENTER here, as after cleanup DBUG is no longer + operational, so we cannot use DBUG_RETURN. + */ + DBUG_PRINT("info",("Shutting down: infoflag: %d print_info: %d", + infoflag, print_info)); + if (!info_file) + { + info_file= stderr; + print_info= 0; + } + + if ((infoflag & MY_CHECK_ERROR) || print_info) + + { /* Test if some file is left open */ + if (my_file_opened | my_stream_opened) + { + char ebuff[MYSYS_ERRMSG_SIZE]; + my_snprintf(ebuff, sizeof(ebuff), EE(EE_OPEN_WARNING), + my_file_opened, my_stream_opened); + my_message_no_curses(EE_OPEN_WARNING, ebuff, ME_BELL); + DBUG_PRINT("error", ("%s", ebuff)); + my_print_open_files(); + } + } + free_charsets(); + my_error_unregister_all(); + my_once_free(); +#ifdef THREAD + my_thread_destroy_mutex(); +#endif + + if ((infoflag & MY_GIVE_INFO) || print_info) + { +#ifdef HAVE_GETRUSAGE + struct rusage rus; +#ifdef HAVE_purify + /* Purify assumes that rus is uninitialized after getrusage call */ + bzero((char*) &rus, sizeof(rus)); +#endif + if (!getrusage(RUSAGE_SELF, &rus)) + fprintf(info_file,"\n\ +User time %.2f, System time %.2f\n\ +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(__NETWARE__) && !defined(__WIN__) + fprintf(info_file,"\nRun time: %.1f\n",(double) clock()/CLOCKS_PER_SEC); +#endif +#if defined(SAFEMALLOC) + /* Wait for other threads to free mysys_var */ +#ifdef THREAD + (void) my_wait_for_other_threads_to_die(1); +#endif + TERMINATE(stderr, (infoflag & MY_GIVE_INFO) != 0); +#elif defined(__WIN__) && defined(_MSC_VER) + _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 + } + else if (infoflag & MY_CHECK_ERROR) + { + TERMINATE(stderr, 0); /* Print memory leaks on screen */ + } + + if (!(infoflag & MY_DONT_FREE_DBUG)) + { + DBUG_END(); /* Must be done before my_thread_end */ + } +#ifdef THREAD + my_thread_end(); + my_thread_global_end(); +#if defined(SAFE_MUTEX) + /* + Check on destroying of mutexes. A few may be left that will get cleaned + up by C++ destructors + */ + safe_mutex_end((infoflag & (MY_GIVE_INFO | MY_CHECK_ERROR)) ? stderr : + (FILE *) 0); +#endif /* defined(SAFE_MUTEX) */ +#endif /* THREAD */ + +#ifdef __WIN__ + if (have_tcpip) + WSACleanup(); +#endif /* __WIN__ */ + my_init_done=0; +} /* my_end */ + +#ifndef DBUG_OFF +/* Dummy tag function for debugging */ + +void my_debug_put_break_here(void) +{ +} +#endif + +#ifdef __WIN__ + + +/* + 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 + Catch the RTC error and dump it to stderr +*/ + +int handle_rtc_failure(int err_type, const char *file, int line, + const char* module, const char *format, ...) +{ + va_list args; + va_start(args, format); + fprintf(stderr, "Error:"); + vfprintf(stderr, format, args); + fprintf(stderr, " At %s:%d\n", file, line); + va_end(args); + (void) fflush(stderr); + + return 0; /* Error is handled */ +} +#pragma runtime_checks("", restore) +#endif + + +static void my_win_init(void) +{ + DBUG_ENTER("my_win_init"); + +#if defined(_MSC_VER) +#if _MSC_VER < 1300 + /* + Clear the OS system variable TZ and avoid the 100% CPU usage + Only for old versions of Visual C++ + */ + _putenv( "TZ=" ); +#endif +#if _MSC_VER >= 1400 + /* this is required to make crt functions return -1 appropriately */ + _set_invalid_parameter_handler(my_parameter_handler); +#endif +#endif +#ifdef __MSVC_RUNTIME_CHECKS + /* + Install handler to send RTC (Runtime Error Check) warnings + to log file + */ + _RTC_SetErrorFunc(handle_rtc_failure); +#endif + + _tzset(); + + + + + + + + + + + + + + + + + + + + + + + + + + /* The following is used by time functions */ +#define OFFSET_TO_EPOC ((__int64) 134774 * 24 * 60 * 60 * 1000 * 1000 * 10) +#define MS 10000000 + { + 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 + */ + 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) ; + } + } + DBUG_VOID_RETURN ; +} + + +/*------------------------------------------------------------------ + 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(void) +{ + 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, 0 ); + 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); +} +#endif /* __WIN__ */ + + +#ifdef __NETWARE__ +/* + Basic initialisation for netware +*/ + +static void netware_init() +{ + char cwd[PATH_MAX], *name; + + DBUG_ENTER("netware_init"); + + /* init only if we are not a client library */ + if (my_progname) + { +#if SUPPORTED_BY_LIBC /* Removed until supported in Libc */ + struct termios tp; + /* Disable control characters */ + tcgetattr(STDIN_FILENO, &tp); + tp.c_cc[VINTR] = _POSIX_VDISABLE; + tp.c_cc[VEOF] = _POSIX_VDISABLE; + tp.c_cc[VSUSP] = _POSIX_VDISABLE; + tcsetattr(STDIN_FILENO, TCSANOW, &tp); +#endif /* SUPPORTED_BY_LIBC */ + + /* With stdout redirection */ + if (!isatty(STDOUT_FILENO)) + { + setscreenmode(SCR_AUTOCLOSE_ON_EXIT); /* auto close the screen */ + } + else + { + setscreenmode(SCR_NO_MODE); /* keep the screen up */ + } + + /* Parse program name and change to base format */ + name= (char*) my_progname; + for (; *name; name++) + { + if (*name == '\\') + { + *name = '/'; + } + else + { + *name = tolower(*name); + } + } + } + + DBUG_VOID_RETURN; +} +#endif /* __NETWARE__ */ diff --git a/externals/mysql/mysys/my_largepage.c b/externals/mysql/mysys/my_largepage.c new file mode 100644 index 00000000000..9fa5b73bf88 --- /dev/null +++ b/externals/mysql/mysys/my_largepage.c @@ -0,0 +1,166 @@ +/* Copyright (C) 2004 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#include "mysys_priv.h" + +#ifdef HAVE_LARGE_PAGES + +#ifdef HAVE_SYS_IPC_H +#include <sys/ipc.h> +#endif + +#ifdef HAVE_SYS_SHM_H +#include <sys/shm.h> +#endif + +static uint my_get_large_page_size_int(void); +static uchar* my_large_malloc_int(size_t size, myf my_flags); +static my_bool my_large_free_int(uchar* ptr, myf my_flags); + +/* Gets the size of large pages from the OS */ + +uint my_get_large_page_size(void) +{ + uint size; + DBUG_ENTER("my_get_large_page_size"); + + if (!(size = my_get_large_page_size_int())) + fprintf(stderr, "Warning: Failed to determine large page size\n"); + + DBUG_RETURN(size); +} + +/* + General large pages allocator. + Tries to allocate memory from large pages pool and falls back to + my_malloc_lock() in case of failure +*/ + +uchar* my_large_malloc(size_t size, myf my_flags) +{ + uchar* ptr; + DBUG_ENTER("my_large_malloc"); + + if (my_use_large_pages && my_large_page_size) + { + if ((ptr = my_large_malloc_int(size, my_flags)) != NULL) + DBUG_RETURN(ptr); + if (my_flags & MY_WME) + fprintf(stderr, "Warning: Using conventional memory pool\n"); + } + + DBUG_RETURN(my_malloc_lock(size, my_flags)); +} + +/* + General large pages deallocator. + Tries to deallocate memory as if it was from large pages pool and falls back + to my_free_lock() in case of failure + */ + +void my_large_free(uchar* ptr, myf my_flags __attribute__((unused))) +{ + DBUG_ENTER("my_large_free"); + + /* + my_large_free_int() can only fail if ptr was not allocated with + my_large_malloc_int(), i.e. my_malloc_lock() was used so we should free it + with my_free_lock() + */ + if (!my_use_large_pages || !my_large_page_size || + !my_large_free_int(ptr, my_flags)) + my_free_lock(ptr, my_flags); + + DBUG_VOID_RETURN; +} + +#ifdef HUGETLB_USE_PROC_MEMINFO +/* Linux-specific function to determine the size of large pages */ + +uint my_get_large_page_size_int(void) +{ + FILE *f; + uint size = 0; + char buf[256]; + DBUG_ENTER("my_get_large_page_size_int"); + + if (!(f = my_fopen("/proc/meminfo", O_RDONLY, MYF(MY_WME)))) + goto finish; + + while (fgets(buf, sizeof(buf), f)) + if (sscanf(buf, "Hugepagesize: %u kB", &size)) + break; + + my_fclose(f, MYF(MY_WME)); + +finish: + DBUG_RETURN(size * 1024); +} +#endif /* HUGETLB_USE_PROC_MEMINFO */ + +#if HAVE_DECL_SHM_HUGETLB +/* Linux-specific large pages allocator */ + +uchar* my_large_malloc_int(size_t size, myf my_flags) +{ + int shmid; + uchar* ptr; + struct shmid_ds buf; + DBUG_ENTER("my_large_malloc_int"); + + /* Align block size to my_large_page_size */ + size = ((size - 1) & ~(my_large_page_size - 1)) + my_large_page_size; + + shmid = shmget(IPC_PRIVATE, size, SHM_HUGETLB | SHM_R | SHM_W); + if (shmid < 0) + { + if (my_flags & MY_WME) + fprintf(stderr, + "Warning: Failed to allocate %lu bytes from HugeTLB memory." + " errno %d\n", (ulong) size, errno); + + DBUG_RETURN(NULL); + } + + ptr = (uchar*) shmat(shmid, NULL, 0); + if (ptr == (uchar *) -1) + { + if (my_flags& MY_WME) + fprintf(stderr, "Warning: Failed to attach shared memory segment," + " errno %d\n", errno); + shmctl(shmid, IPC_RMID, &buf); + + DBUG_RETURN(NULL); + } + + /* + Remove the shared memory segment so that it will be automatically freed + after memory is detached or process exits + */ + shmctl(shmid, IPC_RMID, &buf); + + DBUG_RETURN(ptr); +} + +/* Linux-specific large pages deallocator */ + +my_bool my_large_free_int(uchar *ptr, myf my_flags __attribute__((unused))) +{ + DBUG_ENTER("my_large_free_int"); + DBUG_RETURN(shmdt(ptr) == 0); +} +#endif /* HAVE_DECL_SHM_HUGETLB */ + +#endif /* HAVE_LARGE_PAGES */ diff --git a/externals/mysql/mysys/my_lib.c b/externals/mysql/mysys/my_lib.c new file mode 100644 index 00000000000..30d0f89b14d --- /dev/null +++ b/externals/mysql/mysys/my_lib.c @@ -0,0 +1,559 @@ +/* Copyright (C) 2000 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +/* TODO: check for overun of memory for names. */ +/* Convert MSDOS-TIME to standar time_t (still needed?) */ + +#include "mysys_priv.h" +#include <m_string.h> +#include <my_dir.h> /* Structs used by my_dir,includes sys/types */ +#include "mysys_err.h" +#if defined(HAVE_DIRENT_H) +# include <dirent.h> +# define NAMLEN(dirent) strlen((dirent)->d_name) +#else +# define dirent direct +# define NAMLEN(dirent) (dirent)->d_namlen +# if defined(HAVE_SYS_NDIR_H) +# include <sys/ndir.h> +# endif +# if defined(HAVE_SYS_DIR_H) +# include <sys/dir.h> +# endif +# if defined(HAVE_NDIR_H) +# include <ndir.h> +# endif +# if defined(_WIN32) +# ifdef __BORLANDC__ +# include <dir.h> +# endif +# endif +#endif +#ifdef VMS +#include <rms.h> +#include <iodef.h> +#include <descrip.h> +#endif + +#if defined(THREAD) && defined(HAVE_READDIR_R) +#define READDIR(A,B,C) ((errno=readdir_r(A,B,&C)) != 0 || !C) +#else +#define READDIR(A,B,C) (!(C=readdir(A))) +#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((uchar*) buffer,MYF(0)); + } + 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) + +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; + struct dirent *dp; + char tmp_path[FN_REFLEN+1],*tmp_file; +#ifdef THREAD + char dirent_tmp[sizeof(struct dirent)+_POSIX_PATH_MAX+1]; +#endif + DBUG_ENTER("my_dir"); + DBUG_PRINT("my",("path: '%s' MyFlags: %d",path,MyFlags)); + +#if defined(THREAD) && !defined(HAVE_READDIR_R) + pthread_mutex_lock(&THR_LOCK_open); +#endif + + dirp = opendir(directory_file_name(tmp_path,(char *) path)); +#if defined(__amiga__) + if ((dirp->dd_fd) < 0) /* Directory doesn't exists */ + goto error; +#endif + if (dirp == NULL || + ! (buffer= my_malloc(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, sizeof(FILEINFO), + ENTRIES_START_SIZE, ENTRIES_INCREMENT)) + { + my_free((uchar*) buffer,MYF(0)); + goto error; + } + init_alloc_root(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); + +#ifdef THREAD + dp= (struct dirent*) dirent_tmp; +#else + dp=0; +#endif + + while (!(READDIR(dirp,(struct dirent*) dirent_tmp,dp))) + { + 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; + + bzero(finfo.mystat, sizeof(MY_STAT)); + (void) strmov(tmp_file,dp->d_name); + (void) my_stat(tmp_path, finfo.mystat, MyFlags); + if (!(finfo.mystat->st_mode & MY_S_IREAD)) + continue; + } + else + finfo.mystat= NULL; + + if (push_dynamic(dir_entries_storage, (uchar*)&finfo)) + goto error; + } + + (void) closedir(dirp); +#if defined(THREAD) && !defined(HAVE_READDIR_R) + pthread_mutex_unlock(&THR_LOCK_open); +#endif + 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: +#if defined(THREAD) && !defined(HAVE_READDIR_R) + pthread_mutex_unlock(&THR_LOCK_open); +#endif + my_errno=errno; + if (dirp) + (void) closedir(dirp); + my_dirend(result); + if (MyFlags & (MY_FAE | MY_WME)) + my_error(EE_DIR,MYF(ME_BELL+ME_WAITTANG),path,my_errno); + DBUG_RETURN((MY_DIR *) NULL); +} /* my_dir */ + + +/* + * Convert from directory name to filename. + * On VMS: + * xyzzy:[mukesh.emacs] => xyzzy:[mukesh]emacs.dir.1 + * xyzzy:[mukesh] => xyzzy:[000000]mukesh.dir.1 + * On UNIX, it's simple: just make sure there is a terminating / + + * Returns pointer to dst; + */ + +char * directory_file_name (char * dst, const char *src) +{ +#ifndef VMS + + /* Process as Unix format: just remove test the final slash. */ + + char * end; + + if (src[0] == 0) + src= (char*) "."; /* Use empty as current */ + end=strmov(dst, src); + if (end[-1] != FN_LIBCHAR) + { + end[0]=FN_LIBCHAR; /* Add last '/' */ + end[1]='\0'; + } + return dst; + +#else /* VMS */ + + long slen; + long rlen; + char * ptr, rptr; + char bracket; + struct FAB fab = cc$rms_fab; + struct NAM nam = cc$rms_nam; + char esa[NAM$C_MAXRSS]; + + if (! src[0]) + src="[.]"; /* Empty is == current dir */ + + slen = strlen (src) - 1; + if (src[slen] == FN_C_AFTER_DIR || src[slen] == FN_C_AFTER_DIR_2 || + src[slen] == FN_DEVCHAR) + { + /* VMS style - convert [x.y.z] to [x.y]z, [x] to [000000]x */ + fab.fab$l_fna = src; + fab.fab$b_fns = slen + 1; + fab.fab$l_nam = &nam; + fab.fab$l_fop = FAB$M_NAM; + + nam.nam$l_esa = esa; + nam.nam$b_ess = sizeof esa; + nam.nam$b_nop |= NAM$M_SYNCHK; + + /* We call SYS$PARSE to handle such things as [--] for us. */ + if (SYS$PARSE(&fab, 0, 0) == RMS$_NORMAL) + { + slen = nam.nam$b_esl - 1; + if (esa[slen] == ';' && esa[slen - 1] == '.') + slen -= 2; + esa[slen + 1] = '\0'; + src = esa; + } + if (src[slen] != FN_C_AFTER_DIR && src[slen] != FN_C_AFTER_DIR_2) + { + /* what about when we have logical_name:???? */ + if (src[slen] == FN_DEVCHAR) + { /* Xlate logical name and see what we get */ + (void) strmov(dst,src); + dst[slen] = 0; /* remove colon */ + if (!(src = getenv (dst))) + return dst; /* Can't translate */ + + /* should we jump to the beginning of this procedure? + Good points: allows us to use logical names that xlate + to Unix names, + Bad points: can be a problem if we just translated to a device + name... + For now, I'll punt and always expect VMS names, and hope for + the best! */ + + slen = strlen (src) - 1; + if (src[slen] != FN_C_AFTER_DIR && src[slen] != FN_C_AFTER_DIR_2) + { /* no recursion here! */ + (void) strmov(dst, src); + return(dst); + } + } + else + { /* not a directory spec */ + (void) strmov(dst, src); + return(dst); + } + } + + bracket = src[slen]; /* End char */ + if (!(ptr = strchr (src, bracket - 2))) + { /* no opening bracket */ + (void) strmov (dst, src); + return dst; + } + if (!(rptr = strrchr (src, '.'))) + rptr = ptr; + slen = rptr - src; + (void) strmake (dst, src, slen); + + if (*rptr == '.') + { /* Put bracket and add */ + dst[slen++] = bracket; /* (rptr+1) after this */ + } + else + { + /* If we have the top-level of a rooted directory (i.e. xx:[000000]), + then translate the device and recurse. */ + + if (dst[slen - 1] == ':' + && dst[slen - 2] != ':' /* skip decnet nodes */ + && strcmp(src + slen, "[000000]") == 0) + { + dst[slen - 1] = '\0'; + if ((ptr = getenv (dst)) + && (rlen = strlen (ptr) - 1) > 0 + && (ptr[rlen] == FN_C_AFTER_DIR || ptr[rlen] == FN_C_AFTER_DIR_2) + && ptr[rlen - 1] == '.') + { + (void) strmov(esa,ptr); + esa[rlen - 1] = FN_C_AFTER_DIR; + esa[rlen] = '\0'; + return (directory_file_name (dst, esa)); + } + else + dst[slen - 1] = ':'; + } + (void) strmov(dst+slen,"[000000]"); + slen += 8; + } + (void) strmov(strmov(dst+slen,rptr+1)-1,".DIR.1"); + return dst; + } + (void) strmov(dst, src); + if (dst[slen] == '/' && slen > 1) + dst[slen] = 0; + return dst; +#endif /* VMS */ +} /* directory_file_name */ + +#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; +#ifdef __BORLANDC__ + struct ffblk find; +#else + struct _finddata_t find; +#endif + 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= strnmov(tmp_file, path, FN_REFLEN-5); + if (tmp_file[-1] == FN_DEVCHAR) + *tmp_file++= '.'; /* From current dev-dir */ + if (tmp_file[-1] != FN_LIBCHAR) + *tmp_file++ =FN_LIBCHAR; + tmp_file[0]='*'; /* Windows needs this !??? */ + tmp_file[1]='.'; + tmp_file[2]='*'; + tmp_file[3]='\0'; + + if (!(buffer= my_malloc(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, sizeof(FILEINFO), + ENTRIES_START_SIZE, ENTRIES_INCREMENT)) + { + my_free((uchar*) buffer,MYF(0)); + goto error; + } + init_alloc_root(names_storage, NAMES_START_SIZE, NAMES_START_SIZE); + + /* MY_DIR structure is allocated and completly initialized at this point */ + result= (MY_DIR*)buffer; + +#ifdef __BORLANDC__ + if ((handle= findfirst(tmp_path,&find,0)) == -1L) +#else + if ((handle=_findfirst(tmp_path,&find)) == -1L) +#endif + { + 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 + { +#ifdef __BORLANDC__ + attrib= find.ff_attrib; +#else + attrib= find.attrib; + /* + Do not show hidden and system files which Windows sometimes create. + Note. Because Borland's findfirst() is called with the third + argument = 0 hidden/system files are excluded from the search. + */ + if (attrib & (_A_HIDDEN | _A_SYSTEM)) + continue; +#endif +#ifdef __BORLANDC__ + if (!(finfo.name= strdup_root(names_storage, find.ff_name))) + goto error; +#else + if (!(finfo.name= strdup_root(names_storage, find.name))) + goto error; +#endif + if (MyFlags & MY_WANT_STAT) + { + if (!(finfo.mystat= (MY_STAT*)alloc_root(names_storage, + sizeof(MY_STAT)))) + goto error; + + bzero(finfo.mystat, sizeof(MY_STAT)); +#ifdef __BORLANDC__ + finfo.mystat->st_size=find.ff_fsize; +#else + finfo.mystat->st_size=find.size; +#endif + mode= MY_S_IREAD; + if (!(attrib & _A_RDONLY)) + mode|= MY_S_IWRITE; + if (attrib & _A_SUBDIR) + mode|= MY_S_IFDIR; + finfo.mystat->st_mode= mode; +#ifdef __BORLANDC__ + finfo.mystat->st_mtime= ((uint32) find.ff_ftime); +#else + finfo.mystat->st_mtime= ((uint32) find.time_write); +#endif + } + else + finfo.mystat= NULL; + + if (push_dynamic(dir_entries_storage, (uchar*)&finfo)) + goto error; + } +#ifdef __BORLANDC__ + while (findnext(&find) == 0); +#else + while (_findnext(handle,&find) == 0); + + _findclose(handle); +#endif + } + + result->dir_entry= (FILEINFO *)dir_entries_storage->buffer; + result->number_off_files= dir_entries_storage->elements; + + if (!(MyFlags & MY_DONT_SORT)) + 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: + my_errno=errno; +#ifndef __BORLANDC__ + if (handle != -1) + _findclose(handle); +#endif + my_dirend(result); + if (MyFlags & MY_FAE+MY_WME) + my_error(EE_DIR,MYF(ME_BELL+ME_WAITTANG),path,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 __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) +{ + int m_used; + DBUG_ENTER("my_stat"); + DBUG_PRINT("my", ("path: '%s' stat_area: %p MyFlags: %d", path, + stat_area, my_flags)); + + if ((m_used= (stat_area == NULL))) + if (!(stat_area= (MY_STAT *) my_malloc(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)); + my_errno= errno; + if (m_used) /* Free if new area */ + my_free((uchar*) stat_area,MYF(0)); + +error: + if (my_flags & (MY_FAE+MY_WME)) + { + my_error(EE_STAT, MYF(ME_BELL+ME_WAITTANG),path,my_errno); + DBUG_RETURN((MY_STAT *) NULL); + } + DBUG_RETURN((MY_STAT *) NULL); +} /* my_stat */ diff --git a/externals/mysql/mysys/my_libwrap.c b/externals/mysql/mysys/my_libwrap.c new file mode 100644 index 00000000000..e72334ba806 --- /dev/null +++ b/externals/mysql/mysys/my_libwrap.c @@ -0,0 +1,41 @@ +/* Copyright (C) 2003 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +/* + This is needed to be able to compile with original libwrap header + files that don't have the prototypes +*/ + +#include <my_global.h> +#include <my_libwrap.h> + +#ifdef HAVE_LIBWRAP + +void my_fromhost(struct request_info *req) +{ + fromhost(req); +} + +int my_hosts_access(struct request_info *req) +{ + return hosts_access(req); +} + +char *my_eval_client(struct request_info *req) +{ + return eval_client(req); +} + +#endif /* HAVE_LIBWRAP */ diff --git a/externals/mysql/mysys/my_lock.c b/externals/mysql/mysys/my_lock.c new file mode 100644 index 00000000000..408089df3e9 --- /dev/null +++ b/externals/mysql/mysys/my_lock.c @@ -0,0 +1,290 @@ +/* Copyright (C) 2000-2003 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#include "mysys_priv.h" +#include "mysys_err.h" +#include <errno.h> +#undef MY_HOW_OFTEN_TO_ALARM +#define MY_HOW_OFTEN_TO_ALARM ((int) my_time_to_wait_for_lock) +#ifdef NO_ALARM_LOOP +#undef NO_ALARM_LOOP +#endif +#include <my_alarm.h> +#ifdef __NETWARE__ +#include <nks/fsio.h> +#endif + +#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) +{ +#ifdef HAVE_FCNTL + int value; + ALARM_VARIABLES; +#endif +#ifdef __NETWARE__ + int nxErrno; +#endif + + DBUG_ENTER("my_lock"); + DBUG_PRINT("my",("fd: %d Op: %d start: %ld Length: %ld MyFlags: %d", + fd,locktype,(long) start,(long) length,MyFlags)); +#ifdef VMS + DBUG_RETURN(0); +#else + if (my_disable_locking && ! (MyFlags & MY_FORCE_LOCK)) + DBUG_RETURN(0); + +#if defined(__NETWARE__) + { + NXSOffset_t nxLength = length; + unsigned long nxLockFlags = 0; + + if ((MyFlags & MY_SHORT_WAIT)) + { + /* not yet implemented */ + MyFlags|= MY_NO_WAIT; + } + + if (length == F_TO_EOF) + { + /* EOF is interpreted as a very large length. */ + nxLength = 0x7FFFFFFFFFFFFFFF; + } + + if (locktype == F_UNLCK) + { + /* The lock flags are currently ignored by NKS. */ + if (!(nxErrno= NXFileRangeUnlock(fd, 0L, start, nxLength))) + DBUG_RETURN(0); + } + else + { + if (locktype == F_RDLCK) + { + /* A read lock is mapped to a shared lock. */ + nxLockFlags = NX_RANGE_LOCK_SHARED; + } + else + { + /* A write lock is mapped to an exclusive lock. */ + nxLockFlags = NX_RANGE_LOCK_EXCL; + } + + if (MyFlags & MY_NO_WAIT) + { + /* Don't block on the lock. */ + nxLockFlags |= NX_RANGE_LOCK_TRYLOCK; + } + + if (!(nxErrno= NXFileRangeLock(fd, nxLockFlags, start, nxLength))) + DBUG_RETURN(0); + } + } +#elif defined(_WIN32) + { + int timeout_sec; + if (MyFlags & MY_NO_WAIT) + timeout_sec= 0; + else if(MyFlags & MY_SHORT_WAIT) + timeout_sec= my_time_to_wait_for_lock; + else + timeout_sec= WIN_LOCK_INFINITE; + + if(win_lock(fd, locktype, start, length, timeout_sec) == 0) + DBUG_RETURN(0); + } +#else +#if defined(HAVE_FCNTL) + { + struct flock lock; + + lock.l_type= (short) locktype; + lock.l_whence= SEEK_SET; + lock.l_start= (off_t) start; + lock.l_len= (off_t) length; + + if (MyFlags & (MY_NO_WAIT | MY_SHORT_WAIT)) + { + if (fcntl(fd,F_SETLK,&lock) != -1) /* Check if we can lock */ + DBUG_RETURN(0); /* Ok, file locked */ + if (MyFlags & MY_NO_WAIT) + { + my_errno= (errno == EACCES) ? EAGAIN : errno ? errno : -1; + DBUG_RETURN(-1); + } + + DBUG_PRINT("info",("Was locked, trying with alarm")); + ALARM_INIT; + while ((value=fcntl(fd,F_SETLKW,&lock)) && ! ALARM_TEST && + errno == EINTR) + { /* Setup again so we don`t miss it */ + ALARM_REINIT; + } + ALARM_END; + 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); + } +#else + if (MyFlags & MY_SEEK_NOT_DONE) + { + if (my_seek(fd,start,MY_SEEK_SET,MYF(MyFlags & ~MY_SEEK_NOT_DONE)) + == MY_FILEPOS_ERROR) + { + /* + If an error has occured in my_seek then we will already + have an error code in my_errno; Just return error code. + */ + DBUG_RETURN(-1); + } + } + if (lockf(fd,locktype,length) != -1) + DBUG_RETURN(0); +#endif /* HAVE_FCNTL */ +#endif /* HAVE_LOCKING */ + +#ifdef __NETWARE__ + my_errno = nxErrno; +#else + /* We got an error. We don't want EACCES errors */ + my_errno=(errno == EACCES) ? EAGAIN : errno ? errno : -1; +#endif + if (MyFlags & MY_WME) + { + if (locktype == F_UNLCK) + my_error(EE_CANTUNLOCK,MYF(ME_BELL+ME_WAITTANG),my_errno); + else + my_error(EE_CANTLOCK,MYF(ME_BELL+ME_WAITTANG),my_errno); + } + DBUG_PRINT("error",("my_errno: %d (%d)",my_errno,errno)); + DBUG_RETURN(-1); +#endif /* ! VMS */ +} /* my_lock */ diff --git a/externals/mysql/mysys/my_lockmem.c b/externals/mysql/mysys/my_lockmem.c new file mode 100644 index 00000000000..7d226b187d8 --- /dev/null +++ b/externals/mysql/mysys/my_lockmem.c @@ -0,0 +1,103 @@ +/* Copyright (C) 2000 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +/* Alloc a block of locked memory */ + +#include "mysys_priv.h" +#include "mysys_err.h" +#include <my_list.h> + +#ifdef HAVE_MLOCK +#include <sys/mman.h> + +struct st_mem_list +{ + LIST list; + uchar *page; + uint size; +}; + +LIST *mem_list; + +void *my_malloc_lock(size_t size,myf MyFlags) +{ + int success; + uint pagesize=sysconf(_SC_PAGESIZE); + uchar *ptr; + struct st_mem_list *element; + DBUG_ENTER("my_malloc_lock"); + + size=((size-1) & ~(pagesize-1))+pagesize; +#if HAVE_MEMALIGN + if (!(ptr=memalign(pagesize,size))) +#else + if (!(ptr=valloc(size))) +#endif + { + if (MyFlags & (MY_FAE+MY_WME)) + my_error(EE_OUTOFMEMORY, MYF(ME_BELL+ME_WAITTANG),size); + DBUG_RETURN(0); + } + success = mlock((uchar*) ptr,size); + if (success != 0 && geteuid() == 0) + { + DBUG_PRINT("warning",("Failed to lock memory. errno %d\n", + errno)); + fprintf(stderr, "Warning: Failed to lock memory. errno %d\n", + errno); + } + else + { + /* Add block in a list for munlock */ + if (!(element=(struct st_mem_list*) my_malloc(sizeof(*element),MyFlags))) + { + (void) munlock((uchar*) ptr,size); + free(ptr); + DBUG_RETURN(0); + } + element->list.data=(uchar*) element; + element->page=ptr; + element->size=size; + pthread_mutex_lock(&THR_LOCK_malloc); + mem_list=list_add(mem_list,&element->list); + pthread_mutex_unlock(&THR_LOCK_malloc); + } + DBUG_RETURN(ptr); +} + + +void my_free_lock(void *ptr,myf Myflags __attribute__((unused))) +{ + LIST *list; + struct st_mem_list *element=0; + + pthread_mutex_lock(&THR_LOCK_malloc); + for (list=mem_list ; list ; list=list->next) + { + element=(struct st_mem_list*) list->data; + if (ptr == element->page) + { /* Found locked mem */ + (void) munlock((uchar*) ptr,element->size); + mem_list=list_delete(mem_list,list); + break; + } + } + pthread_mutex_unlock(&THR_LOCK_malloc); + if (element) + my_free((uchar*) element,MYF(0)); + free(ptr); /* Free even if not locked */ +} + +#endif /* HAVE_MLOCK */ diff --git a/externals/mysql/mysys/my_malloc.c b/externals/mysql/mysys/my_malloc.c new file mode 100644 index 00000000000..f1254c0f31c --- /dev/null +++ b/externals/mysql/mysys/my_malloc.c @@ -0,0 +1,100 @@ +/* Copyright (C) 2000 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#ifdef SAFEMALLOC /* We don't need SAFEMALLOC here */ +#undef SAFEMALLOC +#endif + +#include "mysys_priv.h" +#include "mysys_err.h" +#include <m_string.h> + + /* My memory allocator */ + +void *my_malloc(size_t size, myf my_flags) +{ + void* point; + DBUG_ENTER("my_malloc"); + DBUG_PRINT("my",("size: %lu my_flags: %d", (ulong) size, my_flags)); + + if (!size) + size=1; /* Safety */ + + /* If compiled with DBUG, test for error injection. Described in my_sys.h. */ + /* purecov: begin tested */ + if (!(point= IF_DBUG(my_malloc_error_inject ? NULL :) (char*) malloc(size))) + { + IF_DBUG(if (my_malloc_error_inject) errno= ENOMEM; + my_malloc_error_inject= 0); + 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_BELL+ME_WAITTANG+ME_NOREFRESH),size); + if (my_flags & MY_FAE) + exit(1); + } + else if (my_flags & MY_ZEROFILL) + bzero(point,size); + DBUG_PRINT("exit",("ptr: %p", point)); + DBUG_RETURN((void*) point); + /* purecov: end */ +} /* my_malloc */ + + + /* Free memory allocated with my_malloc */ + /*ARGSUSED*/ + +void my_no_flags_free(void* ptr) +{ + DBUG_ENTER("my_free"); + DBUG_PRINT("my",("ptr: %p", ptr)); + if (ptr) + free(ptr); + DBUG_VOID_RETURN; +} /* my_free */ + + + /* malloc and copy */ + +void* my_memdup(const void *from, size_t length, myf my_flags) +{ + void *ptr; + if ((ptr= my_malloc(length,my_flags)) != 0) + memcpy(ptr, from, length); + return(ptr); +} + + +char *my_strdup(const char *from, myf my_flags) +{ + char *ptr; + size_t length= strlen(from)+1; + if ((ptr= (char*) my_malloc(length, my_flags))) + memcpy((uchar*) ptr, (uchar*) from,(size_t) length); + return(ptr); +} + + +char *my_strndup(const char *from, size_t length, myf my_flags) +{ + char *ptr; + if ((ptr= (char*) my_malloc(length+1,my_flags)) != 0) + { + memcpy((uchar*) ptr, (uchar*) from, length); + ptr[length]=0; + } + return((char*) ptr); +} diff --git a/externals/mysql/mysys/my_memmem.c b/externals/mysql/mysys/my_memmem.c new file mode 100644 index 00000000000..c000f14bc66 --- /dev/null +++ b/externals/mysql/mysys/my_memmem.c @@ -0,0 +1,83 @@ +/* Copyright (C) 2000 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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/externals/mysql/mysys/my_messnc.c b/externals/mysql/mysys/my_messnc.c new file mode 100644 index 00000000000..6f86bf34782 --- /dev/null +++ b/externals/mysql/mysys/my_messnc.c @@ -0,0 +1,38 @@ +/* Copyright (C) 2000 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#include "mysys_priv.h" + +void my_message_no_curses(uint error __attribute__((unused)), + const char *str, myf MyFlags) +{ + DBUG_ENTER("my_message_no_curses"); + DBUG_PRINT("enter",("message: %s",str)); + (void) fflush(stdout); + if (MyFlags & ME_BELL) +#ifdef __NETWARE__ + ringbell(); /* Bell */ +#else + (void) fputc('\007',stderr); /* Bell */ +#endif /* __NETWARE__ */ + if (my_progname) + { + (void)fputs(my_progname,stderr); (void)fputs(": ",stderr); + } + (void)fputs(str,stderr); + (void)fputc('\n',stderr); + (void)fflush(stderr); + DBUG_VOID_RETURN; +} diff --git a/externals/mysql/mysys/my_mkdir.c b/externals/mysql/mysys/my_mkdir.c new file mode 100644 index 00000000000..676c6c1cd51 --- /dev/null +++ b/externals/mysql/mysys/my_mkdir.c @@ -0,0 +1,42 @@ +/* Copyright (C) 2000 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#include "mysys_priv.h" +#include "mysys_err.h" +#include <sys/types.h> +#include <sys/stat.h> +#ifdef __WIN__ +#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(__WIN__) + if (mkdir((char*) dir)) +#else + if (mkdir((char*) dir, Flags & my_umask_dir)) +#endif + { + my_errno=errno; + DBUG_PRINT("error",("error %d when creating direcory %s",my_errno,dir)); + if (MyFlags & (MY_FFNF | MY_FAE | MY_WME)) + my_error(EE_CANT_MKDIR, MYF(ME_BELL+ME_WAITTANG), dir, my_errno); + DBUG_RETURN(-1); + } + DBUG_RETURN(0); +} diff --git a/externals/mysql/mysys/my_mmap.c b/externals/mysql/mysys/my_mmap.c new file mode 100644 index 00000000000..303d8efaf30 --- /dev/null +++ b/externals/mysql/mysys/my_mmap.c @@ -0,0 +1,80 @@ +/* Copyright (C) 2000-2003 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#include "mysys_priv.h" + +#ifdef HAVE_SYS_MMAN_H + +/* + system msync() only syncs mmap'ed area to fs cache. + fsync() is required to really sync to disc +*/ +int my_msync(int fd, void *addr, size_t len, int flags) +{ + msync(addr, len, flags); + return my_sync(fd, MYF(0)); +} + +#elif defined(_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); + if (hFile == INVALID_HANDLE_VALUE) + return MAP_FAILED; + + hFileMap=CreateFileMapping(hFile, &mmap_security_attributes, + PAGE_READWRITE, 0, (DWORD) len, NULL); + if (hFileMap == 0) + 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) + return ptr; + + return MAP_FAILED; +} + +int my_munmap(void *addr, size_t len) +{ + return UnmapViewOfFile(addr) ? 0 : -1; +} + +int my_msync(int fd, void *addr, size_t len, int flags) +{ + return FlushViewOfFile(addr, len) ? 0 : -1; +} + +#else +#warning "no mmap!" +#endif + diff --git a/externals/mysql/mysys/my_net.c b/externals/mysql/mysys/my_net.c new file mode 100644 index 00000000000..81d977210f8 --- /dev/null +++ b/externals/mysql/mysys/my_net.c @@ -0,0 +1,42 @@ +/* Copyright (C) 2000 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +/* thread safe version of some common functions */ + +#include "mysys_priv.h" +#include <m_string.h> + +/* for thread safe my_inet_ntoa */ +#if !defined(__WIN__) +#include <netdb.h> +#ifdef HAVE_SYS_SOCKET_H +#include <sys/socket.h> +#endif +#ifdef HAVE_NETINET_IN_H +#include <netinet/in.h> +#endif +#ifdef HAVE_ARPA_INET_H +#include <arpa/inet.h> +#endif +#endif /* !defined(__WIN__) */ + +void my_inet_ntoa(struct in_addr in, char *buf) +{ + char *ptr; + pthread_mutex_lock(&THR_LOCK_net); + ptr=inet_ntoa(in); + strmov(buf,ptr); + pthread_mutex_unlock(&THR_LOCK_net); +} diff --git a/externals/mysql/mysys/my_netware.c b/externals/mysql/mysys/my_netware.c new file mode 100644 index 00000000000..5b5c39c0ac0 --- /dev/null +++ b/externals/mysql/mysys/my_netware.c @@ -0,0 +1,150 @@ +/* Copyright (C) 2003 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +/* + Functions specific to netware +*/ + +#include <mysys_priv.h> +#ifdef __NETWARE__ + #include <string.h> + #include <library.h> + +/* + PMUserLicenseRequest is an API exported by the polimgr.nlm + (loaded by the NetWare OS when it comes up) for use by other + NLM-based NetWare products/services. + PMUserLicenseRequest provides a couple of functions: + 1) it will optionally request a User license or ensure that + one already exists for the specified User in userInfo + 2) it utilizes the NetWare usage metering service to + record usage information about your product/service. +*/ + +long PMMeteredUsageRequest +( + /* + NDS distinguished name or IP address or ??. asciiz string, e.g. + ".CN=Admin.O=this.T=MYTREE." + */ + char *userInfo, + long infoType, /* see defined values */ + /* + string used to identify the calling service, used to index the + metered info e.g. "iPrint" + */ + char *serviceID, + char tranAddrType, /* type of address that follows */ + char *tranAddr, /* ptr to a 10-byte array */ + long flags, /* see defined values */ + /* NLS error code, if any. NULL input is okay */ + long *licRequestErrCode, + /* meter service error code, if any. NULL input is okay */ + long *storeMeterInfoErrCode, + /* + error code from NLSMeter if + storeMeterInfoErrCode == PM_LICREQ_NLSMETERERROR. + NULL input is okay + */ + long *NLSMeterErrCode +); + +typedef long(*PMUR)(const char*, long, const char*, char, + const char*, long, long*, long*, long*); + +/* infoType */ +/* indicates that the info in the userInfo param is an NDS user */ +#define PM_USERINFO_TYPE_NDS 1 +/* indicates that the info in the userInfo param is NOT an NDS user */ +#define PM_USERINFO_TYPE_ADDRESS 2 + +/* Flags */ + +/* + Tells the service that it should not check to see if the NDS user + contained in the userInfo param has a NetWare User License - just + record metering information; this is ignored if infoType != + PM_USERINFO_TYPE_NDS +*/ + +#define PM_FLAGS_METER_ONLY 0x0000001 + +/* + Indicates that the values in the userInfo and serviceID parameters + are unicode strings, so that the metering service bypasses + converting these to unicode (again) +*/ +#define PM_LICREQ_ALREADY_UNICODE 0x0000002 +/* + Useful only if infoType is PM_USERINFO_TYPE_NDS - indicates a "no + stop" policy of the calling service +*/ +#define PM_LICREQ_ALWAYS_METER 0x0000004 + + +/* + net Address Types - system-defined types of net addresses that can + be used in the tranAddrType field +*/ + +#define NLS_TRAN_TYPE_IPX 0x00000001 /* An IPX address */ +#define NLS_TRAN_TYPE_IP 0x00000008 /* An IP address */ +#define NLS_ADDR_TYPE_MAC 0x000000F1 /* a MAC address */ + +/* + Net Address Sizes - lengths that correspond to the tranAddrType + field (just fyi) +*/ +#define NLS_IPX_ADDR_SIZE 10 /* the size of an IPX address */ +#define NLS_IP_ADDR_SIZE 4 /* the size of an IP address */ +#define NLS_MAC_ADDR_SIZE 6 /* the size of a MAC address */ + + +void netware_reg_user(const char *ip, const char *user, + const char *application) +{ + PMUR usage_request; + long licRequestErrCode = 0; + long storeMeterInfoErrCode = 0; + long nlsMeterErrCode = 0; + + /* import the symbol */ + usage_request= ((PMUR)ImportPublicObject(getnlmhandle(), + "PMMeteredUsageRequest")); + if (usage_request != NULL) + { + unsigned long iaddr; + char addr[NLS_IPX_ADDR_SIZE]; + + /* create address */ + iaddr = htonl(inet_addr(ip)); + bzero(addr, NLS_IPX_ADDR_SIZE); + memcpy(addr, &iaddr, NLS_IP_ADDR_SIZE); + + /* call to NLS */ + usage_request(user, + PM_USERINFO_TYPE_ADDRESS, + application, + NLS_TRAN_TYPE_IP, + addr, + PM_FLAGS_METER_ONLY, + &licRequestErrCode, + &storeMeterInfoErrCode, + &nlsMeterErrCode); + /* release symbol */ + UnImportPublicObject(getnlmhandle(), "PMMeteredUsageRequest"); + } +} +#endif /* __NETWARE__ */ diff --git a/externals/mysql/mysys/my_new.cc b/externals/mysql/mysys/my_new.cc new file mode 100644 index 00000000000..7da54ffac87 --- /dev/null +++ b/externals/mysql/mysys/my_new.cc @@ -0,0 +1,58 @@ +/* Copyright (C) 2000 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +/* + This is a replacement of new/delete operators to be used when compiling + with gcc 3.0.x to avoid including libstdc++ +*/ + +#include "mysys_priv.h" + +#ifdef USE_MYSYS_NEW + +void *operator new (size_t sz) +{ + return (void *) malloc (sz ? sz : 1); +} + +void *operator new[] (size_t sz) +{ + return (void *) malloc (sz ? sz : 1); +} + +void operator delete (void *ptr) +{ + if (ptr) + free(ptr); +} + +void operator delete[] (void *ptr) throw () +{ + if (ptr) + free(ptr); +} + +C_MODE_START + +int __cxa_pure_virtual() +{ + assert(! "Aborted: pure virtual method called."); + return 0; +} + +C_MODE_END + +#endif /* USE_MYSYS_NEW */ + diff --git a/externals/mysql/mysys/my_once.c b/externals/mysql/mysys/my_once.c new file mode 100644 index 00000000000..b6f6656fce2 --- /dev/null +++ b/externals/mysql/mysys/my_once.c @@ -0,0 +1,122 @@ +/* Copyright (C) 2000 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +/* Not MT-SAFE */ + +#ifdef SAFEMALLOC /* We don't need SAFEMALLOC here */ +#undef SAFEMALLOC +#endif + +#include "mysys_priv.h" +#include "my_static.h" +#include "mysys_err.h" +#include <m_string.h> + +/* + Alloc for things we don't nead to free + + 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; + reg1 USED_MEM *next; + reg2 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) + { + my_errno=errno; + if (MyFlags & (MY_FAE+MY_WME)) + my_error(EE_OUTOFMEMORY, MYF(ME_BELL+ME_WAITTANG),get_size); + return((uchar*) 0); + } + DBUG_PRINT("test",("my_once_malloc %lu byte malloced", (ulong) get_size)); + next->next= 0; + next->size= get_size; + next->left= get_size-ALIGN_SIZE(sizeof(USED_MEM)); + *prev=next; + } + point= (uchar*) ((char*) next+ (next->size-next->left)); + next->left-= Size; + + if (MyFlags & MY_ZEROFILL) + bzero(point, 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 used by my_once_alloc + + SYNOPSIS + my_once_free() +*/ + +void my_once_free(void) +{ + reg1 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/externals/mysql/mysys/my_open.c b/externals/mysql/mysys/my_open.c new file mode 100644 index 00000000000..ebdf9e38ab3 --- /dev/null +++ b/externals/mysql/mysys/my_open.c @@ -0,0 +1,191 @@ +/* Copyright (C) 2000 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#include "mysys_priv.h" +#include "mysys_err.h" +#include <my_dir.h> +#include <errno.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); +#elif !defined(NO_OPEN_3) + fd = open(FileName, Flags, my_umask); /* Normal unix */ +#else + fd = open((char *) FileName, Flags); +#endif + + DBUG_RETURN(my_register_filename(fd, FileName, FILE_BY_OPEN, + EE_FILENOTFOUND, MyFlags)); +} /* 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)); + + pthread_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)); + my_errno=errno; + if (MyFlags & (MY_FAE | MY_WME)) + my_error(EE_BADCLOSE, MYF(ME_BELL+ME_WAITTANG),my_filename(fd),errno); + } + if ((uint) fd < my_file_limit && my_file_info[fd].type != UNOPEN) + { + my_free(my_file_info[fd].name, MYF(0)); +#if defined(THREAD) && !defined(HAVE_PREAD) && !defined (_WIN32) + pthread_mutex_destroy(&my_file_info[fd].mutex); +#endif + my_file_info[fd].type = UNOPEN; + } + my_file_opened--; + pthread_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) +{ + DBUG_ENTER("my_register_filename"); + if ((int) fd >= MY_FILE_MIN) + { + if ((uint) fd >= my_file_limit) + { +#if defined(THREAD) && !defined(HAVE_PREAD) + my_errno= EMFILE; +#else + thread_safe_increment(my_file_opened,&THR_LOCK_open); + DBUG_RETURN(fd); /* safeguard */ +#endif + } + else + { + pthread_mutex_lock(&THR_LOCK_open); + if ((my_file_info[fd].name = (char*) my_strdup(FileName,MyFlags))) + { + my_file_opened++; + my_file_total_opened++; + my_file_info[fd].type = type_of_file; +#if defined(THREAD) && !defined(HAVE_PREAD) && !defined(_WIN32) + pthread_mutex_init(&my_file_info[fd].mutex,MY_MUTEX_INIT_FAST); +#endif + pthread_mutex_unlock(&THR_LOCK_open); + DBUG_PRINT("exit",("fd: %d",fd)); + DBUG_RETURN(fd); + } + pthread_mutex_unlock(&THR_LOCK_open); + my_errno= ENOMEM; + } + (void) my_close(fd, MyFlags); + } + else + my_errno= errno; + + DBUG_PRINT("error",("Got error %d on open", my_errno)); + if (MyFlags & (MY_FFNF | MY_FAE | MY_WME)) + { + 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(ME_BELL+ME_WAITTANG), + FileName, 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) + { + fprintf(stderr, EE(EE_FILE_NOT_CLOSED), my_file_info[i].name, i); + fputc('\n', stderr); + } + } + } +} + +#endif diff --git a/externals/mysql/mysys/my_port.c b/externals/mysql/mysys/my_port.c new file mode 100644 index 00000000000..9ad333421ca --- /dev/null +++ b/externals/mysql/mysys/my_port.c @@ -0,0 +1,40 @@ +/* Copyright (C) 2002 MySQL AB + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; version 2 + of the License. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + MA 02111-1307, USA */ + +/* + Small functions to make code portable +*/ + +#include "mysys_priv.h" + +#ifdef _AIX + +/* + On AIX, at least with gcc 3.1, the expression + '(double) (ulonglong) var' doesn't always work for big unsigned + integers like '18446744073709551615'. The end result is that the + high bit is simply dropped. (probably bug in gcc optimizations) + Handling the conversion in a sub function seems to work. +*/ + + + +double my_ulonglong2double(unsigned long long nr) +{ + return (double) nr; +} +#endif /* _AIX */ diff --git a/externals/mysql/mysys/my_pread.c b/externals/mysql/mysys/my_pread.c new file mode 100644 index 00000000000..eaabcb1b728 --- /dev/null +++ b/externals/mysql/mysys/my_pread.c @@ -0,0 +1,206 @@ +/* Copyright (C) 2000 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#include "mysys_priv.h" +#include "mysys_err.h" +#include "my_base.h" +#include <m_string.h> +#include <errno.h> +#if defined (HAVE_PREAD) && !defined(_WIN32) +#include <unistd.h> +#endif + + + +/* + 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; +#if !defined (HAVE_PREAD) && !defined (_WIN32) + int save_errno; +#endif + 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 (HAVE_PREAD) && !defined (_WIN32) + pthread_mutex_lock(&my_file_info[Filedes].mutex); + readbytes= (uint) -1; + error= (lseek(Filedes, offset, MY_SEEK_SET) == (my_off_t) -1 || + (readbytes= read(Filedes, Buffer, Count)) != Count); + save_errno= errno; + pthread_mutex_unlock(&my_file_info[Filedes].mutex); + if (error) + errno= save_errno; +#else +#if defined(_WIN32) + readbytes= my_win_pread(Filedes, Buffer, Count, offset); +#else + readbytes= pread(Filedes, Buffer, Count, offset); +#endif + error= (readbytes != Count); +#endif + if(error) + { + my_errno= errno ? errno : -1; + if (errno == 0 || (readbytes != (size_t) -1 && + (MyFlags & (MY_NABP | MY_FNABP)))) + 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)); +#ifdef THREAD + if ((readbytes == 0 || readbytes == (size_t) -1) && errno == EINTR) + { + DBUG_PRINT("debug", ("my_pread() was interrupted and returned %d", + (int) readbytes)); + continue; /* Interrupted */ + } +#endif + if (MyFlags & (MY_WME | MY_FAE | MY_FNABP)) + { + if (readbytes == (size_t) -1) + my_error(EE_READ, MYF(ME_BELL+ME_WAITTANG), + my_filename(Filedes),my_errno); + else if (MyFlags & (MY_NABP | MY_FNABP)) + my_error(EE_EOFERR, MYF(ME_BELL+ME_WAITTANG), + my_filename(Filedes),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() + + RETURN + (size_t) -1 Error + # Number of bytes read +*/ + +size_t my_pwrite(File Filedes, const uchar *Buffer, size_t Count, + my_off_t offset, myf MyFlags) +{ + size_t writtenbytes, written; + uint errors; + + DBUG_ENTER("my_pwrite"); + DBUG_PRINT("my",("fd: %d Seek: %llu Buffer: %p Count: %lu MyFlags: %d", + Filedes, offset, Buffer, (ulong)Count, MyFlags)); + errors= 0; + written= 0; + + for (;;) + { +#if !defined (HAVE_PREAD) && !defined (_WIN32) + int error; + writtenbytes= (size_t) -1; + pthread_mutex_lock(&my_file_info[Filedes].mutex); + error= (lseek(Filedes, offset, MY_SEEK_SET) != (my_off_t) -1 && + (writtenbytes= write(Filedes, Buffer, Count)) == Count); + pthread_mutex_unlock(&my_file_info[Filedes].mutex); + if (error) + break; +#elif defined (_WIN32) + writtenbytes= my_win_pwrite(Filedes, Buffer, Count, offset); +#else + writtenbytes= pwrite(Filedes, Buffer, Count, offset); +#endif + if(writtenbytes == Count) + break; + my_errno= errno; + if (writtenbytes != (size_t) -1) + { + written+= writtenbytes; + Buffer+= writtenbytes; + Count-= writtenbytes; + offset+= writtenbytes; + } + DBUG_PRINT("error",("Write only %u bytes", (uint) writtenbytes)); +#ifndef NO_BACKGROUND +#ifdef THREAD + if (my_thread_var->abort) + MyFlags&= ~ MY_WAIT_IF_FULL; /* End if aborted by user */ +#endif + if ((my_errno == ENOSPC || my_errno == EDQUOT) && + (MyFlags & MY_WAIT_IF_FULL)) + { + wait_for_free_space(my_filename(Filedes), errors); + errors++; + continue; + } + if ((writtenbytes && writtenbytes != (size_t) -1) || my_errno == EINTR) + continue; /* Retry */ +#endif + if (MyFlags & (MY_NABP | MY_FNABP)) + { + if (MyFlags & (MY_WME | MY_FAE | MY_FNABP)) + { + my_error(EE_WRITE, MYF(ME_BELL | ME_WAITTANG), + my_filename(Filedes),my_errno); + } + DBUG_RETURN(MY_FILE_ERROR); /* Error on read */ + } + else + break; /* Return bytes written */ + } + DBUG_EXECUTE_IF("check", my_seek(Filedes, -1, SEEK_SET, MYF(0));); + if (MyFlags & (MY_NABP | MY_FNABP)) + DBUG_RETURN(0); /* Want only errors */ + DBUG_RETURN(writtenbytes+written); /* purecov: inspected */ +} /* my_pwrite */ diff --git a/externals/mysql/mysys/my_pthread.c b/externals/mysql/mysys/my_pthread.c new file mode 100644 index 00000000000..848397f38ab --- /dev/null +++ b/externals/mysql/mysys/my_pthread.c @@ -0,0 +1,501 @@ +/* Copyright (C) 2000-2003 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +/* Functions to get threads more portable */ + +#define DONT_REMAP_PTHREAD_FUNCTIONS + +#include "mysys_priv.h" +#ifdef THREAD +#include <signal.h> +#include <m_string.h> +#include <thr_alarm.h> + +#if (defined(__BSD__) || defined(_BSDI_VERSION)) +#define SCHED_POLICY SCHED_RR +#else +#define SCHED_POLICY SCHED_OTHER +#endif + +uint thd_lib_detected= 0; + +/* To allow use of pthread_getspecific with two arguments */ + +#ifdef HAVE_NONPOSIX_PTHREAD_GETSPECIFIC +#undef pthread_getspecific + +void *my_pthread_getspecific_imp(pthread_key_t key) +{ + void *value; + if (pthread_getspecific(key,(void *) &value)) + return 0; + return value; +} +#endif + +#ifdef __NETWARE__ +/* + Don't kill the LibC Reaper thread or the main thread +*/ +#include <nks/thread.h> +#undef pthread_exit +void my_pthread_exit(void *status) +{ + NXThreadId_t tid; + NXContext_t ctx; + char name[NX_MAX_OBJECT_NAME_LEN+1] = ""; + + tid= NXThreadGetId(); + if (tid == NX_INVALID_THREAD_ID || !tid) + return; + if (NXThreadGetContext(tid, &ctx) || + NXContextGetName(ctx, name, sizeof(name)-1)) + return; + + /* + "MYSQLD.NLM's LibC Reaper" or "MYSQLD.NLM's main thread" + with a debug build of LibC the reaper can have different names + */ + if (!strindex(name, "\'s")) + pthread_exit(status); +} +#endif + +/* + Some functions for RTS threads, AIX, Siemens Unix and UnixWare 7 + (and DEC OSF/1 3.2 too) +*/ + +int my_pthread_create_detached=1; + +#if defined(HAVE_NONPOSIX_SIGWAIT) || defined(HAVE_DEC_3_2_THREADS) + +int my_sigwait(const sigset_t *set,int *sig) +{ + int signal=sigwait((sigset_t*) set); + if (signal < 0) + return errno; + *sig=signal; + return 0; +} +#endif + +/* localtime_r for SCO 3.2V4.2 */ + +#if !defined(HAVE_LOCALTIME_R) || !defined(HAVE_GMTIME_R) + +extern pthread_mutex_t LOCK_localtime_r; + +#endif + +#if !defined(HAVE_LOCALTIME_R) +struct tm *localtime_r(const time_t *clock, struct tm *res) +{ + struct tm *tmp; + pthread_mutex_lock(&LOCK_localtime_r); + tmp=localtime(clock); + *res= *tmp; + pthread_mutex_unlock(&LOCK_localtime_r); + return res; +} +#endif + +#if !defined(HAVE_GMTIME_R) +/* + Reentrant version of standard gmtime() function. + Needed on some systems which don't implement it. +*/ + +struct tm *gmtime_r(const time_t *clock, struct tm *res) +{ + struct tm *tmp; + pthread_mutex_lock(&LOCK_localtime_r); + tmp= gmtime(clock); + *res= *tmp; + pthread_mutex_unlock(&LOCK_localtime_r); + return res; +} +#endif + +/**************************************************************************** +** Replacement of sigwait if the system doesn't have one (like BSDI 3.0) +** +** Note: +** This version of sigwait() is assumed to called in a loop so the signalmask +** is permanently modified to reflect the signal set. This is done to get +** a much faster implementation. +** +** This implementation isn't thread safe: It assumes that only one +** thread is using sigwait. +** +** If one later supplies a different signal mask, all old signals that +** was used before are unblocked and set to SIGDFL. +** +** Author: Gary Wisniewski <garyw@spidereye.com.au>, much modified by Monty +****************************************************************************/ + +#if !defined(HAVE_SIGWAIT) && !defined(sigwait) && !defined(__WIN__) && !defined(HAVE_rts_threads) && !defined(HAVE_NONPOSIX_SIGWAIT) && !defined(HAVE_DEC_3_2_THREADS) + +#if !defined(DONT_USE_SIGSUSPEND) + +static sigset_t sigwait_set,rev_sigwait_set,px_recd; + +void px_handle_sig(int sig) +{ + sigaddset(&px_recd, sig); +} + + +void sigwait_setup(sigset_t *set) +{ + int i; + struct sigaction sact,sact1; + sigset_t unblock_mask; + + sact.sa_flags = 0; + sact.sa_handler = px_handle_sig; + memcpy_fixed(&sact.sa_mask,set,sizeof(*set)); /* handler isn't thread_safe */ + sigemptyset(&unblock_mask); + pthread_sigmask(SIG_UNBLOCK,(sigset_t*) 0,&rev_sigwait_set); + + for (i = 1; i <= sizeof(sigwait_set)*8; i++) + { + if (sigismember(set,i)) + { + sigdelset(&rev_sigwait_set,i); + if (!sigismember(&sigwait_set,i)) + sigaction(i, &sact, (struct sigaction*) 0); + } + else + { + sigdelset(&px_recd,i); /* Don't handle this */ + if (sigismember(&sigwait_set,i)) + { /* Remove the old handler */ + sigaddset(&unblock_mask,i); + sigdelset(&rev_sigwait_set,i); + sact1.sa_flags = 0; + sact1.sa_handler = SIG_DFL; + sigemptyset(&sact1.sa_mask); + sigaction(i, &sact1, 0); + } + } + } + memcpy_fixed(&sigwait_set,set,sizeof(*set)); + pthread_sigmask(SIG_BLOCK,(sigset_t*) set,(sigset_t*) 0); + pthread_sigmask(SIG_UNBLOCK,&unblock_mask,(sigset_t*) 0); +} + + +int sigwait(sigset_t *setp, int *sigp) +{ + if (memcmp(setp,&sigwait_set,sizeof(sigwait_set))) + sigwait_setup(setp); /* Init or change of set */ + + for (;;) + { + /* + This is a fast, not 100% portable implementation to find the signal. + Because the handler is blocked there should be at most 1 bit set, but + the specification on this is somewhat shady so we use a set instead a + single variable. + */ + + ulong *ptr= (ulong*) &px_recd; + ulong *end=ptr+sizeof(px_recd)/sizeof(ulong); + + for ( ; ptr != end ; ptr++) + { + if (*ptr) + { + ulong set= *ptr; + int found= (int) ((char*) ptr - (char*) &px_recd)*8+1; + while (!(set & 1)) + { + found++; + set>>=1; + } + *sigp=found; + sigdelset(&px_recd,found); + return 0; + } + } + sigsuspend(&rev_sigwait_set); + } + return 0; +} +#else /* !DONT_USE_SIGSUSPEND */ + +/**************************************************************************** +** Replacement of sigwait if the system doesn't have one (like BSDI 3.0) +** +** Note: +** This version of sigwait() is assumed to called in a loop so the signalmask +** is permanently modified to reflect the signal set. This is done to get +** a much faster implementation. +** +** This implementation uses a extra thread to handle the signals and one +** must always call sigwait() with the same signal mask! +** +** BSDI 3.0 NOTE: +** +** pthread_kill() doesn't work on a thread in a select() or sleep() loop? +** After adding the sleep to sigwait_thread, all signals are checked and +** delivered every second. This isn't that terrible performance vice, but +** someone should report this to BSDI and ask for a fix! +** Another problem is that when the sleep() ends, every select() in other +** threads are interrupted! +****************************************************************************/ + +static sigset_t pending_set; +static my_bool inited=0; +static pthread_cond_t COND_sigwait; +static pthread_mutex_t LOCK_sigwait; + + +void sigwait_handle_sig(int sig) +{ + pthread_mutex_lock(&LOCK_sigwait); + sigaddset(&pending_set, sig); + pthread_cond_signal(&COND_sigwait); /* inform sigwait() about signal */ + pthread_mutex_unlock(&LOCK_sigwait); +} + +void *sigwait_thread(void *set_arg) +{ + sigset_t *set=(sigset_t*) set_arg; + + int i; + struct sigaction sact; + sact.sa_flags = 0; + sact.sa_handler = sigwait_handle_sig; + memcpy_fixed(&sact.sa_mask,set,sizeof(*set)); /* handler isn't thread_safe */ + sigemptyset(&pending_set); + + for (i = 1; i <= sizeof(pending_set)*8; i++) + { + if (sigismember(set,i)) + { + sigaction(i, &sact, (struct sigaction*) 0); + } + } + /* Ensure that init_thr_alarm() is called */ + DBUG_ASSERT(thr_client_alarm); + sigaddset(set, thr_client_alarm); + pthread_sigmask(SIG_UNBLOCK,(sigset_t*) set,(sigset_t*) 0); + alarm_thread=pthread_self(); /* For thr_alarm */ + + for (;;) + { /* Wait for signals */ +#ifdef HAVE_NOT_BROKEN_SELECT + fd_set fd; + FD_ZERO(&fd); + select(0,&fd,0,0,0); +#else + sleep(1); /* Because of broken BSDI */ +#endif + } +} + + +int sigwait(sigset_t *setp, int *sigp) +{ + if (!inited) + { + pthread_attr_t thr_attr; + pthread_t sigwait_thread_id; + inited=1; + sigemptyset(&pending_set); + pthread_mutex_init(&LOCK_sigwait,MY_MUTEX_INIT_FAST); + pthread_cond_init(&COND_sigwait,NULL); + + pthread_attr_init(&thr_attr); + pthread_attr_setscope(&thr_attr,PTHREAD_SCOPE_PROCESS); + pthread_attr_setdetachstate(&thr_attr,PTHREAD_CREATE_DETACHED); + pthread_attr_setstacksize(&thr_attr,8196); + pthread_create(&sigwait_thread_id,&thr_attr,sigwait_thread,setp); + pthread_attr_destroy(&thr_attr); + } + + pthread_mutex_lock(&LOCK_sigwait); + for (;;) + { + ulong *ptr= (ulong*) &pending_set; + ulong *end=ptr+sizeof(pending_set)/sizeof(ulong); + + for ( ; ptr != end ; ptr++) + { + if (*ptr) + { + ulong set= *ptr; + int found= (int) ((char*) ptr - (char*) &pending_set)*8+1; + while (!(set & 1)) + { + found++; + set>>=1; + } + *sigp=found; + sigdelset(&pending_set,found); + pthread_mutex_unlock(&LOCK_sigwait); + return 0; + } + } + pthread_cond_wait(&COND_sigwait,&LOCK_sigwait); + } + return 0; +} + +#endif /* DONT_USE_SIGSUSPEND */ +#endif /* HAVE_SIGWAIT */ + + +/**************************************************************************** + The following functions fixes that all pthread functions should work + according to latest posix standard +****************************************************************************/ + +/* Undefined wrappers set my_pthread.h so that we call os functions */ +#undef pthread_mutex_init +#undef pthread_mutex_lock +#undef pthread_mutex_unlock +#undef pthread_mutex_destroy +#undef pthread_mutex_wait +#undef pthread_mutex_timedwait +#undef pthread_mutex_trylock +#undef pthread_mutex_t +#undef pthread_cond_init +#undef pthread_cond_wait +#undef pthread_cond_timedwait +#undef pthread_cond_t +#undef pthread_attr_getstacksize + +/***************************************************************************** +** Patches for AIX and DEC OSF/1 3.2 +*****************************************************************************/ + +#if defined(HAVE_NONPOSIX_PTHREAD_MUTEX_INIT) + +#include <netdb.h> + +int my_pthread_mutex_noposix_init(pthread_mutex_t *mp, + const pthread_mutexattr_t *attr) +{ + int error; + if (!attr) + error=pthread_mutex_init(mp,pthread_mutexattr_default); + else + error=pthread_mutex_init(mp,*attr); + return error; +} + +int my_pthread_cond_noposix_init(pthread_cond_t *mp, + const pthread_condattr_t *attr) +{ + int error; + if (!attr) + error=pthread_cond_init(mp,pthread_condattr_default); + else + error=pthread_cond_init(mp,*attr); + return error; +} + +#endif + + +/***************************************************************************** + Patches for HPUX + We need these because the pthread_mutex.. code returns -1 on error, + instead of the error code. + + Note that currently we only remap pthread_ functions used by MySQL. + If we are depending on the value for some other pthread_xxx functions, + this has to be added here. +****************************************************************************/ + +#if defined(HPUX10) || defined(HAVE_BROKEN_PTHREAD_COND_TIMEDWAIT) + +int my_pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, + struct timespec *abstime) +{ + int error=pthread_cond_timedwait(cond, mutex, abstime); + if (error == -1) /* Safety if the lib is fixed */ + { + if (!(error=errno)) + error= ETIMEDOUT; /* Can happen on HPUX */ + } + if (error == EAGAIN) /* Correct errno to Posix */ + error= ETIMEDOUT; + return error; +} +#endif + +#if defined(HPUX10) + +void my_pthread_attr_getstacksize(pthread_attr_t *connection_attrib, + size_t *stack_size) +{ + *stack_size= pthread_attr_getstacksize(*connection_attrib); +} +#endif + + +#ifdef HAVE_POSIX1003_4a_MUTEX +/* + In HP-UX-10.20 and other old Posix 1003.4a Draft 4 implementations + pthread_mutex_trylock returns 1 on success, not 0 like + pthread_mutex_lock + + From the HP-UX-10.20 man page: + RETURN VALUES + If the function fails, errno may be set to one of the following + values: + Return | Error | Description + _______|__________|_________________________________________ + 1 | | Successful completion. + 0 | | The mutex is locked; therefore, it was + | | not acquired. + -1 | [EINVAL] | The value specified by mutex is invalid. + +*/ + +/* + Convert pthread_mutex_trylock to return values according to latest POSIX + + RETURN VALUES + 0 If we are able successfully lock the mutex. + EBUSY Mutex was locked by another thread + # Other error number returned by pthread_mutex_trylock() + (Not likely) +*/ + +int my_pthread_mutex_trylock(pthread_mutex_t *mutex) +{ + int error= pthread_mutex_trylock(mutex); + if (error == 1) + return 0; /* Got lock on mutex */ + if (error == 0) /* Someon else is locking mutex */ + return EBUSY; + if (error == -1) /* Safety if the lib is fixed */ + error= errno; /* Probably invalid parameter */ + return error; +} +#endif /* HAVE_POSIX1003_4a_MUTEX */ + +/* Some help functions */ + +int pthread_dummy(int ret) +{ + return ret; +} +#endif /* THREAD */ diff --git a/externals/mysql/mysys/my_quick.c b/externals/mysql/mysys/my_quick.c new file mode 100644 index 00000000000..b93e7e17224 --- /dev/null +++ b/externals/mysql/mysys/my_quick.c @@ -0,0 +1,81 @@ +/* Copyright (C) 2000 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +/* Quicker interface to read & write. Used with my_nosys.h */ + +#include "mysys_priv.h" +#include "my_nosys.h" + + +#ifdef _WIN32 +extern size_t my_win_read(File Filedes,uchar *Buffer,size_t Count); +#endif + +size_t my_quick_read(File Filedes,uchar *Buffer,size_t Count,myf MyFlags) +{ + size_t readbytes; +#ifdef _WIN32 + readbytes= my_win_read(Filedes, Buffer, Count); +#else + readbytes= read(Filedes, Buffer, Count); +#endif + if(readbytes != Count) + { +#ifndef DBUG_OFF + if ((readbytes == 0 || readbytes == (size_t) -1) && errno == EINTR) + { + DBUG_PRINT("error", ("my_quick_read() was interrupted and returned %d" + ". This function does not retry the read!", + (int) readbytes)); + } +#endif + my_errno=errno; + return readbytes; + } + return (MyFlags & (MY_NABP | MY_FNABP)) ? 0 : readbytes; +} + + + +size_t my_quick_write(File Filedes, const uchar *Buffer, size_t Count) +{ +#ifdef _WIN32 + return my_win_write(Filedes, Buffer, Count); +#else + +#ifndef DBUG_OFF + size_t writtenbytes; +#endif + + if (( +#ifndef DBUG_OFF + writtenbytes = +#endif + (size_t) write(Filedes,Buffer,Count)) != Count) + { +#ifndef DBUG_OFF + if ((writtenbytes == 0 || writtenbytes == (size_t) -1) && errno == EINTR) + { + DBUG_PRINT("error", ("my_quick_write() was interrupted and returned %d" + ". This function does not retry the write!", + (int) writtenbytes)); + } +#endif + my_errno=errno; + return (size_t) -1; + } + return 0; +#endif +} diff --git a/externals/mysql/mysys/my_read.c b/externals/mysql/mysys/my_read.c new file mode 100644 index 00000000000..75f9dd64f1d --- /dev/null +++ b/externals/mysql/mysys/my_read.c @@ -0,0 +1,97 @@ +/* Copyright (C) 2000 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#include "mysys_priv.h" +#include "mysys_err.h" +#include <my_base.h> +#include <errno.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 + + if (readbytes != Count) + { + my_errno= errno; + if (errno == 0 || (readbytes != (size_t) -1 && + (MyFlags & (MY_NABP | MY_FNABP)))) + 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)); +#ifdef THREAD + if ((readbytes == 0 || (int) readbytes == -1) && errno == EINTR) + { + DBUG_PRINT("debug", ("my_read() was interrupted and returned %ld", + (long) readbytes)); + continue; /* Interrupted */ + } +#endif + if (MyFlags & (MY_WME | MY_FAE | MY_FNABP)) + { + if (readbytes == (size_t) -1) + my_error(EE_READ, MYF(ME_BELL+ME_WAITTANG), + my_filename(Filedes),my_errno); + else if (MyFlags & (MY_NABP | MY_FNABP)) + my_error(EE_EOFERR, MYF(ME_BELL+ME_WAITTANG), + my_filename(Filedes),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/externals/mysql/mysys/my_realloc.c b/externals/mysql/mysys/my_realloc.c new file mode 100644 index 00000000000..d686f4cfb17 --- /dev/null +++ b/externals/mysql/mysys/my_realloc.c @@ -0,0 +1,75 @@ +/* Copyright (C) 2000 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#ifdef SAFEMALLOC /* We don't need SAFEMALLOC here */ +#undef SAFEMALLOC +#endif + +#include "mysys_priv.h" +#include "mysys_err.h" + + /* My memory re allocator */ + +/** + @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() +*/ +void* my_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); + if (!oldpoint && (my_flags & MY_ALLOW_ZERO_PTR)) + DBUG_RETURN(my_malloc(size,my_flags)); +#ifdef USE_HALLOC + if (!(point = malloc(size))) + { + if (my_flags & MY_FREE_ON_ERROR) + my_free(oldpoint,my_flags); + if (my_flags & MY_HOLD_ON_ERROR) + DBUG_RETURN(oldpoint); + my_errno=errno; + if (my_flags & MY_FAE+MY_WME) + my_error(EE_OUTOFMEMORY, MYF(ME_BELL+ME_WAITTANG),size); + } + else + { + memcpy(point,oldpoint,size); + free(oldpoint); + } +#else + if ((point= (uchar*) realloc(oldpoint,size)) == NULL) + { + if (my_flags & MY_FREE_ON_ERROR) + my_free(oldpoint, my_flags); + if (my_flags & MY_HOLD_ON_ERROR) + DBUG_RETURN(oldpoint); + my_errno=errno; + if (my_flags & (MY_FAE+MY_WME)) + my_error(EE_OUTOFMEMORY, MYF(ME_BELL+ME_WAITTANG), size); + } +#endif + DBUG_PRINT("exit",("ptr: %p", point)); + DBUG_RETURN(point); +} /* my_realloc */ diff --git a/externals/mysql/mysys/my_redel.c b/externals/mysql/mysys/my_redel.c new file mode 100644 index 00000000000..4e482a34f88 --- /dev/null +++ b/externals/mysql/mysys/my_redel.c @@ -0,0 +1,120 @@ +/* Copyright (C) 2000 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#include "mysys_priv.h" +#include <my_dir.h> +#include <m_string.h> +#include "mysys_err.h" +#if defined(HAVE_UTIME_H) +#include <utime.h> +#elif defined(HAVE_SYS_UTIME_H) +#include <sys/utime.h> +#elif !defined(HPUX10) +struct utimbuf { + time_t actime; + time_t modtime; +}; +#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 + */ + +#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 (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); + strmov(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) +{ + struct stat statbuf; + + if (stat((char*) from, &statbuf)) + { + my_errno=errno; + if (MyFlags & (MY_FAE+MY_WME)) + my_error(EE_STAT, MYF(ME_BELL+ME_WAITTANG),from,errno); + return -1; /* Can't get stat on input file */ + } + if ((statbuf.st_mode & S_IFMT) != S_IFREG) + return 1; + (void) chmod(to, statbuf.st_mode & 07777); /* Copy modes */ + +#if !defined(__WIN__) && !defined(__NETWARE__) + if (statbuf.st_nlink > 1 && MyFlags & MY_LINK_WARNING) + { + if (MyFlags & MY_LINK_WARNING) + my_error(EE_LINK_WARNING,MYF(ME_BELL+ME_WAITTANG),from,statbuf.st_nlink); + } + (void) chown(to, statbuf.st_uid, statbuf.st_gid); /* Copy ownership */ +#endif /* !__WIN__ && !__NETWARE__ */ + +#ifndef VMS +#ifndef __ZTC__ + 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 */ + } +#else + if (MyFlags & MY_COPYTIME) + { + time_t time[2]; + time[0]= statbuf.st_atime; + time[1]= statbuf.st_mtime; + (void) utime((char*) to, time);/* Update last accessed and modified times */ + } +#endif +#endif + return 0; +} /* my_copystat */ diff --git a/externals/mysql/mysys/my_rename.c b/externals/mysql/mysys/my_rename.c new file mode 100644 index 00000000000..39e6056a9e4 --- /dev/null +++ b/externals/mysql/mysys/my_rename.c @@ -0,0 +1,79 @@ +/* Copyright (C) 2000 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#include "mysys_priv.h" +#include <my_dir.h> +#include "mysys_err.h" +#include "m_string.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(HAVE_FILE_VERSIONS) + { /* Check that there isn't a old file */ + int save_errno; + MY_STAT my_stat_result; + save_errno=my_errno; + if (my_stat(to,&my_stat_result,MYF(0))) + { + my_errno=EEXIST; + error= -1; + if (MyFlags & MY_FAE+MY_WME) + my_error(EE_LINK, MYF(ME_BELL+ME_WAITTANG),from,to,my_errno); + DBUG_RETURN(error); + } + my_errno=save_errno; + } +#endif +#if defined(HAVE_RENAME) +#if defined(__WIN__) || defined(__NETWARE__) + /* + On windows we can't rename over an existing file: + Remove any conflicting files: + */ + (void) my_delete(to, MYF(0)); +#endif + if (rename(from,to)) +#else + if (link(from, to) || unlink(from)) +#endif + { + my_errno=errno; + error = -1; + if (MyFlags & (MY_FAE+MY_WME)) + my_error(EE_LINK, MYF(ME_BELL+ME_WAITTANG),from,to,my_errno); + } + else if (MyFlags & MY_SYNC_DIR) + { +#ifdef NEED_EXPLICIT_SYNC_DIR + /* do only the needed amount of syncs: */ + char dir_from[FN_REFLEN], dir_to[FN_REFLEN]; + size_t dir_from_length, dir_to_length; + dirname_part(dir_from, from, &dir_from_length); + dirname_part(dir_to, to, &dir_to_length); + if (my_sync_dir(dir_from, MyFlags) || + (strcmp(dir_from, dir_to) && + my_sync_dir(dir_to, MyFlags))) + error= -1; +#endif + } + DBUG_RETURN(error); +} /* my_rename */ diff --git a/externals/mysql/mysys/my_rnd.c b/externals/mysql/mysys/my_rnd.c new file mode 100644 index 00000000000..b7dca0f2afd --- /dev/null +++ b/externals/mysql/mysys/my_rnd.c @@ -0,0 +1,55 @@ +/* Copyright (C) 2007 MySQL AB & Michael Widenius + + 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#include "mysys_priv.h" +#include <m_string.h> + +/* + Initialize random generator + + NOTES + MySQL's password checks depends on this, so don't do any changes + that changes the random numbers that are generated! +*/ + +void my_rnd_init(struct my_rnd_struct *rand_st, ulong seed1, ulong seed2) +{ +#ifdef HAVE_purify + bzero((char*) rand_st,sizeof(*rand_st)); /* Avoid UMC varnings */ +#endif + rand_st->max_value= 0x3FFFFFFFL; + rand_st->max_value_dbl=(double) rand_st->max_value; + rand_st->seed1=seed1%rand_st->max_value ; + rand_st->seed2=seed2%rand_st->max_value; +} + + +/* + Generate random number. + + SYNOPSIS + my_rnd() + rand_st INOUT Structure used for number generation + + RETURN VALUE + generated pseudo random number +*/ + +double my_rnd(struct my_rnd_struct *rand_st) +{ + rand_st->seed1=(rand_st->seed1*3+rand_st->seed2) % rand_st->max_value; + rand_st->seed2=(rand_st->seed1+rand_st->seed2+33) % rand_st->max_value; + return (((double) rand_st->seed1)/rand_st->max_value_dbl); +} diff --git a/externals/mysql/mysys/my_safehash.c b/externals/mysql/mysys/my_safehash.c new file mode 100644 index 00000000000..bad7e7c7025 --- /dev/null +++ b/externals/mysql/mysys/my_safehash.c @@ -0,0 +1,296 @@ +/* Copyright (C) 2003-2007 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +/* + Handling of multiple key caches + + The idea is to have a thread safe hash on the table name, + with a default key cache value that is returned if the table name is not in + the cache. +*/ + +#include "mysys_priv.h" +#include <m_string.h> +#include "my_safehash.h" + +/***************************************************************************** + General functions to handle SAFE_HASH objects. + + A SAFE_HASH object is used to store the hash, the mutex and default value + needed by the rest of the key cache code. + This is a separate struct to make it easy to later reuse the code for other + purposes + + All entries are linked in a list to allow us to traverse all elements + and delete selected ones. (HASH doesn't allow any easy ways to do this). +*****************************************************************************/ + + +/* + Free a SAFE_HASH_ENTRY + + SYNOPSIS + safe_hash_entry_free() + entry The entry which should be freed + + NOTE + This function is called by the hash object on delete +*/ + +static void safe_hash_entry_free(SAFE_HASH_ENTRY *entry) +{ + DBUG_ENTER("safe_hash_entry_free"); + my_free((uchar*) entry, MYF(0)); + DBUG_VOID_RETURN; +} + + +/* + Get key and length for a SAFE_HASH_ENTRY + + SYNOPSIS + safe_hash_entry_get() + entry The entry for which the key should be returned + length Length of the key + + RETURN + # reference on the key +*/ + +static uchar *safe_hash_entry_get(SAFE_HASH_ENTRY *entry, size_t *length, + my_bool not_used __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 +*/ + +my_bool safe_hash_init(SAFE_HASH *hash, uint elements, + uchar *default_value) +{ + DBUG_ENTER("safe_hash_init"); + 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)) + { + hash->default_value= 0; + DBUG_RETURN(1); + } + my_rwlock_init(&hash->mutex, 0); + hash->default_value= default_value; + hash->root= 0; + DBUG_RETURN(0); +} + + +/* + Free a SAFE_HASH object + + SYNOPSIS + safe_hash_free() + hash Hash handle + + NOTES + This is safe to call on any object that has been sent to safe_hash_init() +*/ + +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); + rwlock_destroy(&hash->mutex); + hash->default_value=0; + } +} + + +/* + Return the value stored for a key or default value if no key + + SYNOPSIS + safe_hash_search() + hash Hash handle + key key (path to table etc..) + length Length of key + def Default value of data + + RETURN + # data associated with the key of default value if data was not found +*/ + +uchar *safe_hash_search(SAFE_HASH *hash, const uchar *key, uint length, + uchar *def) +{ + uchar *result; + DBUG_ENTER("safe_hash_search"); + rw_rdlock(&hash->mutex); + result= my_hash_search(&hash->hash, key, length); + rw_unlock(&hash->mutex); + if (!result) + result= def; + else + result= ((SAFE_HASH_ENTRY*) result)->data; + DBUG_PRINT("exit",("data: %p", result)); + DBUG_RETURN(result); +} + + +/* + Associate a key with some data + + SYNOPSIS + 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. +*/ + +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)); + + rw_wrlock(&hash->mutex); + 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(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((char*) entry, MYF(0)); + error= 1; + goto end; + } + } + +end: + rw_unlock(&hash->mutex); + DBUG_RETURN(error); +} + + +/* + Change all entries with one data value to another data value + + SYNOPSIS + 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. +*/ + +void safe_hash_change(SAFE_HASH *hash, uchar *old_data, uchar *new_data) +{ + SAFE_HASH_ENTRY *entry, *next; + DBUG_ENTER("safe_hash_change"); + + rw_wrlock(&hash->mutex); + + for (entry= hash->root ; entry ; entry= next) + { + next= entry->next; + if (entry->data == old_data) + { + if (new_data == hash->default_value) + { + if ((*entry->prev= entry->next)) + entry->next->prev= entry->prev; + my_hash_delete(&hash->hash, (uchar*) entry); + } + else + entry->data= new_data; + } + } + + rw_unlock(&hash->mutex); + DBUG_VOID_RETURN; +} diff --git a/externals/mysql/mysys/my_safehash.h b/externals/mysql/mysys/my_safehash.h new file mode 100644 index 00000000000..110f4ed325a --- /dev/null +++ b/externals/mysql/mysys/my_safehash.h @@ -0,0 +1,57 @@ +/* Copyright (C) 2003 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +/* + Handling of multiple key caches + + The idea is to have a thread safe hash on the table name, + with a default key cache value that is returned if the table name is not in + the cache. +*/ + +#include <hash.h> + +/* + 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 +{ +#ifdef THREAD + rw_lock_t mutex; +#endif + HASH hash; + uchar *default_value; + SAFE_HASH_ENTRY *root; +} SAFE_HASH; + + +my_bool safe_hash_init(SAFE_HASH *hash, uint elements, + uchar *default_value); +void safe_hash_free(SAFE_HASH *hash); +uchar *safe_hash_search(SAFE_HASH *hash, const uchar *key, uint length, + uchar *def); +my_bool safe_hash_set(SAFE_HASH *hash, const uchar *key, uint length, + uchar *data); +void safe_hash_change(SAFE_HASH *hash, uchar *old_data, uchar *new_data); diff --git a/externals/mysql/mysys/my_seek.c b/externals/mysql/mysys/my_seek.c new file mode 100644 index 00000000000..8502c259353 --- /dev/null +++ b/externals/mysql/mysys/my_seek.c @@ -0,0 +1,95 @@ +/* Copyright (C) 2000 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#include "mysys_priv.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 __attribute__((unused))) +{ + 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)); + DBUG_ASSERT(pos != MY_FILEPOS_ERROR); /* safety check */ + + /* + 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) + { + my_errno= 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 __attribute__((unused))) +{ + 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) + my_errno= errno; + DBUG_PRINT("exit",("pos: %llu", (ulonglong) pos)); + DBUG_RETURN((my_off_t) pos); +} /* my_tell */ diff --git a/externals/mysql/mysys/my_sleep.c b/externals/mysql/mysys/my_sleep.c new file mode 100644 index 00000000000..cb21c15a925 --- /dev/null +++ b/externals/mysql/mysys/my_sleep.c @@ -0,0 +1,37 @@ +/* Copyright (C) 2000 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +/* Wait a given number of microseconds */ + +#include "mysys_priv.h" +#include <m_string.h> + +void my_sleep(ulong m_seconds) +{ +#ifdef __NETWARE__ + delay(m_seconds/1000+1); +#elif defined(__WIN__) + Sleep(m_seconds/1000+1); /* Sleep() has millisecond arg */ +#elif defined(HAVE_SELECT) + struct timeval t; + t.tv_sec= m_seconds / 1000000L; + t.tv_usec= m_seconds % 1000000L; + select(0,0,0,0,&t); /* sleep */ +#else + uint sec= (uint) ((m_seconds + 999999L) / 1000000L); + ulong start= (ulong) time((time_t*) 0); + while ((ulong) time((time_t*) 0) < start+sec); +#endif +} diff --git a/externals/mysql/mysys/my_static.c b/externals/mysql/mysys/my_static.c new file mode 100644 index 00000000000..57158772d9f --- /dev/null +++ b/externals/mysql/mysys/my_static.c @@ -0,0 +1,129 @@ +/* Copyright (C) 2000-2008 MySQL AB, 2008-2009 Sun Microsystems, Inc. + + 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +/* + Static variables for mysys library. All definied here for easy making of + a shared library +*/ + +#include "mysys_priv.h" +#include "my_static.h" +#include "my_alarm.h" + +my_bool timed_mutexes= 0; + + /* from my_init */ +char * home_dir=0; +const char *my_progname= NULL, *my_progname_short= NULL; +char NEAR curr_dir[FN_REFLEN]= {0}, + NEAR home_dir_buff[FN_REFLEN]= {0}; +ulong my_stream_opened=0,my_file_opened=0, my_tmp_file_created=0; +ulong my_file_total_opened= 0; +int NEAR my_umask=0664, NEAR my_umask_dir=0777; +#ifndef THREAD +int NEAR my_errno=0; +#endif +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_brkhant */ +int NEAR my_dont_interrupt=0; +volatile int _my_signals=0; +struct st_remember _my_sig_remember[MAX_SIGNALS]={{0,0}}; +#ifdef THREAD +sigset_t my_signals; /* signals blocked by mf_brkhant */ +#endif + + /* 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_LARGE_PAGES +my_bool my_use_large_pages= 0; +uint my_large_page_size= 0; +#endif + +/* ERROR INJECTION: Described in my_sys.h. */ +IF_DBUG(int my_malloc_error_inject= 0); + + /* from safe_malloc */ +uint sf_malloc_prehunc=0, /* If you have problem with core- */ + sf_malloc_endhunc=0, /* dump when malloc-message.... */ + /* set theese to 64 or 128 */ + sf_malloc_quick=0; /* set if no calls to sanity */ +size_t sf_malloc_cur_memory= 0L; /* Current memory usage */ +size_t sf_malloc_max_memory= 0L; /* Maximum memory usage */ +uint sf_malloc_count= 0; /* Number of times NEW() was called */ +uchar *sf_min_adress= (uchar*) ~(unsigned long) 0L, + *sf_max_adress= (uchar*) 0L; +/* Root of the linked list of struct st_irem */ +struct st_irem *sf_malloc_root = NULL; + + /* from my_alarm */ +int volatile my_have_got_alarm=0; /* declare variable to reset */ +ulong my_time_to_wait_for_lock=2; /* In seconds */ + + /* from errors.c */ +#ifdef SHARED_LIBRARY +char * NEAR globerrs[GLOBERRS]; /* my_error_messages is here */ +#endif +void (*my_abort_hook)(int) = (void(*)(int)) exit; +void (*error_handler_hook)(uint error,const char *str,myf MyFlags)= + my_message_no_curses; +void (*fatal_error_handler_hook)(uint error,const char *str,myf MyFlags)= + my_message_no_curses; + +static const char *proc_info_dummy(void *a __attribute__((unused)), + const char *b __attribute__((unused)), + const char *c __attribute__((unused)), + const char *d __attribute__((unused)), + const unsigned int e __attribute__((unused))) +{ + return 0; +} + +/* this is to be able to call set_thd_proc_info from the C code */ +const char *(*proc_info_hook)(void *, const char *, const char *, const char *, + const unsigned int)= proc_info_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 __WIN__ +/* from my_getsystime.c */ +ulonglong query_performance_frequency, query_performance_offset; +#endif + + /* How to disable options */ +my_bool NEAR my_disable_locking=0; +my_bool NEAR my_disable_async_io=0; +my_bool NEAR my_disable_flush_key_blocks=0; +my_bool NEAR my_disable_symlinks=0; +my_bool NEAR mysys_uses_curses=0; diff --git a/externals/mysql/mysys/my_static.h b/externals/mysql/mysys/my_static.h new file mode 100644 index 00000000000..90168b099a8 --- /dev/null +++ b/externals/mysql/mysys/my_static.h @@ -0,0 +1,74 @@ +/* Copyright (C) 2000 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +/* + Static variables for mysys library. All definied here for easy making of + a shared library +*/ + +C_MODE_START +#include <signal.h> + +#define MAX_SIGNALS 10 /* Max signals under a dont-allow */ +#define MIN_KEYBLOCK (min(IO_SIZE,1024)) +#define MAX_KEYBLOCK 8192 /* Max keyblocklength == 8*IO_SIZE */ +#define MAX_BLOCK_TYPES MAX_KEYBLOCK/MIN_KEYBLOCK + +struct st_remember { + int number; + sig_handler (*func)(int number); +}; + +/* + Structure that stores information of a allocated memory block + The data is at &struct_adr+sizeof(ALIGN_SIZE(sizeof(struct irem))) + The lspecialvalue is at the previous 4 bytes from this, which may not + necessarily be in the struct if the struct size isn't aligned at a 8 byte + boundary. +*/ + +struct st_irem +{ + struct st_irem *next; /* Linked list of structures */ + struct st_irem *prev; /* Other link */ + char *filename; /* File in which memory was new'ed */ + size_t datasize; /* Size requested */ + uint32 linenum; /* Line number in above file */ + uint32 SpecialValue; /* Underrun marker value */ +}; + + +extern char NEAR curr_dir[FN_REFLEN],NEAR home_dir_buff[FN_REFLEN]; + +extern volatile int _my_signals; +extern struct st_remember _my_sig_remember[MAX_SIGNALS]; + +extern const char *soundex_map; + +extern USED_MEM* my_once_root_block; +extern uint my_once_extra; + +extern uchar *sf_min_adress,*sf_max_adress; +extern uint sf_malloc_count; +extern struct st_irem *sf_malloc_root; + +extern struct st_my_file_info my_file_info_default[MY_NFILE]; + +extern ulonglong query_performance_frequency, query_performance_offset; + +#if defined(THREAD) && !defined(__WIN__) +extern sigset_t my_signals; /* signals blocked by mf_brkhant */ +#endif +C_MODE_END diff --git a/externals/mysql/mysys/my_symlink.c b/externals/mysql/mysys/my_symlink.c new file mode 100644 index 00000000000..258e227bb7b --- /dev/null +++ b/externals/mysql/mysys/my_symlink.c @@ -0,0 +1,153 @@ +/* Copyright (C) 2000 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#include "mysys_priv.h" +#include "mysys_err.h" +#include <m_string.h> +#include <errno.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 + strmov(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 */ + if ((my_errno=errno) == EINVAL) + { + result= 1; + strmov(to,filename); + } + else + { + if (MyFlags & MY_WME) + my_error(EE_CANT_READLINK, MYF(0), filename, 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; + my_errno=errno; + if (MyFlags & MY_WME) + my_error(EE_CANT_SYMLINK, MYF(0), linkname, content, errno); + } + else if ((MyFlags & MY_SYNC_DIR) && my_sync_dir_by_file(linkname, MyFlags)) + result= -1; + DBUG_RETURN(result); +#endif /* HAVE_READLINK */ +} + +#if defined(SCO) +#define BUFF_LEN 4097 +#elif defined(MAXPATHLEN) +#define BUFF_LEN MAXPATHLEN +#else +#define BUFF_LEN FN_LEN +#endif + + +int my_is_symlink(const char *filename __attribute__((unused))) +{ +#if defined (HAVE_LSTAT) && defined (S_ISLNK) + struct stat stat_buff; + return !lstat(filename, &stat_buff) && S_ISLNK(stat_buff.st_mode); +#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 __attribute__((unused))) +{ +#if defined(HAVE_REALPATH) && !defined(HAVE_BROKEN_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)); + my_errno=errno; + if (MyFlags & MY_WME) + my_error(EE_REALPATH, MYF(0), filename, my_errno); + my_load_path(to, filename, NullS); + result= -1; + } + DBUG_RETURN(result); +#else + my_load_path(to, filename, NullS); + return 0; +#endif +} diff --git a/externals/mysql/mysys/my_symlink2.c b/externals/mysql/mysys/my_symlink2.c new file mode 100644 index 00000000000..7c3ddbb911c --- /dev/null +++ b/externals/mysql/mysys/my_symlink2.c @@ -0,0 +1,183 @@ +/* Copyright (C) 2000 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 "mysys_err.h" +#include <m_string.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_disable_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)) + { + my_errno= errno= EEXIST; + my_error(EE_CANTCREATEFILE, MYF(0), filename, EEXIST); + DBUG_RETURN(-1); + } + if (create_link && !access(linkname,F_OK)) + { + my_errno= errno= EEXIST; + my_error(EE_CANTCREATEFILE, MYF(0), linkname, 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; + 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_disable_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_disable_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 */ + strmov(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)) + { + my_errno= EEXIST; + if (MyFlags & MY_WME) + my_error(EE_CANTCREATEFILE, MYF(0), tmp_name, 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 */ + 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); + my_errno=save_errno; + result= 1; + } + DBUG_RETURN(result); +#endif /* HAVE_READLINK */ +} diff --git a/externals/mysql/mysys/my_sync.c b/externals/mysql/mysys/my_sync.c new file mode 100644 index 00000000000..4d5b96704a5 --- /dev/null +++ b/externals/mysql/mysys/my_sync.c @@ -0,0 +1,155 @@ +/* Copyright (C) 2003 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#include "mysys_priv.h" +#include "mysys_err.h" +#include <errno.h> + +/* + 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)); + + 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) + 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; + if (!(my_errno= er)) + my_errno= -1; /* Unknown error */ + if ((my_flags & MY_IGNORE_BADFD) && + (er == EBADF || er == EINVAL || er == EROFS)) + { + DBUG_PRINT("info", ("ignoring errno %d", er)); + res= 0; + } + else if (my_flags & MY_WME) + my_error(EE_SYNC, MYF(ME_BELL+ME_WAITTANG), my_filename(fd), my_errno); + } + DBUG_RETURN(res); +} /* my_sync */ + + +static const char cur_dir_name[]= {FN_CURLIB, 0}; +/* + 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 +*/ +int my_sync_dir(const char *dir_name, myf my_flags) +{ +#ifdef NEED_EXPLICIT_SYNC_DIR + DBUG_ENTER("my_sync_dir"); + DBUG_PRINT("my",("Dir: '%s' my_flags: %d", dir_name, my_flags)); + File dir_fd; + int res= 0; + const char *correct_dir_name; + /* 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, myf my_flags) +{ +#ifdef NEED_EXPLICIT_SYNC_DIR + 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/externals/mysql/mysys/my_thr_init.c b/externals/mysql/mysys/my_thr_init.c new file mode 100644 index 00000000000..ec56e20e793 --- /dev/null +++ b/externals/mysql/mysys/my_thr_init.c @@ -0,0 +1,524 @@ +/* Copyright (C) 2000-2008 MySQL AB, 2008-2009 Sun Microsystems, Inc. + + 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +/** + @file + Functions for initialization and allocation of all mysys & debug + thread variables. +*/ + +#include "mysys_priv.h" +#include <m_string.h> +#include <signal.h> + +#ifdef THREAD +pthread_key(struct st_my_thread_var*, THR_KEY_mysys); +pthread_mutex_t THR_LOCK_malloc,THR_LOCK_open, + THR_LOCK_lock,THR_LOCK_isam,THR_LOCK_heap, THR_LOCK_net, + THR_LOCK_charset, THR_LOCK_threads, THR_LOCK_time; +/** For insert/delete in the list of MyISAM open tables */ +pthread_mutex_t THR_LOCK_myisam; +/** For writing to the MyISAM logs */ +pthread_mutex_t THR_LOCK_myisam_log; +pthread_cond_t THR_COND_threads; +uint THR_thread_count= 0; +uint my_thread_end_wait_time= 5; +#if !defined(HAVE_LOCALTIME_R) || !defined(HAVE_GMTIME_R) +pthread_mutex_t LOCK_localtime_r; +#endif +#ifndef HAVE_GETHOSTBYNAME_R +pthread_mutex_t LOCK_gethostbyname_r; +#endif +#ifdef PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP +pthread_mutexattr_t my_fast_mutexattr; +#endif +#ifdef PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP +pthread_mutexattr_t my_errorcheck_mutexattr; +#endif +#ifdef _MSC_VER +static void install_sigabrt_handler(); +#endif +#ifdef TARGET_OS_LINUX + +/* + Dummy thread spawned in my_thread_global_init() below to avoid + race conditions in NPTL pthread_exit code. +*/ + +static pthread_handler_t +nptl_pthread_exit_hack_handler(void *arg __attribute((unused))) +{ + /* Do nothing! */ + pthread_exit(0); + return 0; +} + +#endif /* TARGET_OS_LINUX */ + + + +/** + Initialize thread attributes. +*/ + +void my_threadattr_global_init(void) +{ +#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); /* ?= MY_MUTEX_INIT_FAST */ + 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 +} + + +static uint get_thread_lib(void); + +/* + initialize thread environment + + SYNOPSIS + my_thread_global_init() + + RETURN + 0 ok + 1 error (Couldn't create THR_KEY_mysys) +*/ + +my_bool my_thread_global_init(void) +{ + int pth_ret; + thd_lib_detected= get_thread_lib(); + + if ((pth_ret= pthread_key_create(&THR_KEY_mysys, NULL)) != 0) + { + fprintf(stderr,"Can't initialize threads: error %d\n", pth_ret); + return 1; + } + +#ifdef TARGET_OS_LINUX + /* + BUG#24507: Race conditions inside current NPTL pthread_exit() + implementation. + + To avoid a possible segmentation fault during concurrent + executions of pthread_exit(), a dummy thread is spawned which + initializes internal variables of pthread lib. See bug description + for a full explanation. + + TODO: Remove this code when fixed versions of glibc6 are in common + use. + */ + if (thd_lib_detected == THD_LIB_NPTL) + { + pthread_t dummy_thread; + pthread_attr_t dummy_thread_attr; + + pthread_attr_init(&dummy_thread_attr); + pthread_attr_setdetachstate(&dummy_thread_attr, PTHREAD_CREATE_DETACHED); + + pthread_create(&dummy_thread,&dummy_thread_attr, + nptl_pthread_exit_hack_handler, NULL); + } +#endif /* TARGET_OS_LINUX */ + + /* Mutex used by my_thread_init() and after my_thread_destroy_mutex() */ + my_pthread_mutex_init(&THR_LOCK_threads, MY_MUTEX_INIT_FAST, + "THR_LOCK_threads", MYF_NO_DEADLOCK_DETECTION); + my_pthread_mutex_init(&THR_LOCK_malloc, MY_MUTEX_INIT_FAST, + "THR_LOCK_malloc", MYF_NO_DEADLOCK_DETECTION); + + if (my_thread_init()) + return 1; + + + /* Mutex uses by mysys */ + pthread_mutex_init(&THR_LOCK_open,MY_MUTEX_INIT_FAST); + pthread_mutex_init(&THR_LOCK_lock,MY_MUTEX_INIT_FAST); + pthread_mutex_init(&THR_LOCK_isam,MY_MUTEX_INIT_SLOW); + pthread_mutex_init(&THR_LOCK_myisam,MY_MUTEX_INIT_SLOW); + pthread_mutex_init(&THR_LOCK_myisam_log,MY_MUTEX_INIT_SLOW); + pthread_mutex_init(&THR_LOCK_heap,MY_MUTEX_INIT_FAST); + pthread_mutex_init(&THR_LOCK_net,MY_MUTEX_INIT_FAST); + pthread_mutex_init(&THR_LOCK_charset,MY_MUTEX_INIT_FAST); + pthread_mutex_init(&THR_LOCK_time,MY_MUTEX_INIT_FAST); + pthread_cond_init(&THR_COND_threads, NULL); + +#if !defined(HAVE_LOCALTIME_R) || !defined(HAVE_GMTIME_R) + pthread_mutex_init(&LOCK_localtime_r,MY_MUTEX_INIT_SLOW); +#endif +#ifndef HAVE_GETHOSTBYNAME_R + pthread_mutex_init(&LOCK_gethostbyname_r,MY_MUTEX_INIT_SLOW); +#endif + return 0; +} + + +/** + Wait for all threads in system to die + @fn my_wait_for_other_threads_to_die() + @param number_of_threads Wait until this number of threads + + @retval 0 Less or equal to number_of_threads left + @retval 1 Wait failed +*/ + +my_bool my_wait_for_other_threads_to_die(uint number_of_threads) +{ + struct timespec abstime; + my_bool all_threads_killed= 1; + + set_timespec(abstime, my_thread_end_wait_time); + pthread_mutex_lock(&THR_LOCK_threads); + while (THR_thread_count > number_of_threads) + { + int error= pthread_cond_timedwait(&THR_COND_threads, &THR_LOCK_threads, + &abstime); + if (error == ETIMEDOUT || error == ETIME) + { + all_threads_killed= 0; + break; + } + } + pthread_mutex_unlock(&THR_LOCK_threads); + return all_threads_killed; +} + + +/** + End the mysys thread system. Called when ending the last thread +*/ + + +void my_thread_global_end(void) +{ + my_bool all_threads_killed; + + if (!(all_threads_killed= my_wait_for_other_threads_to_die(0))) + { +#ifdef HAVE_PTHREAD_KILL + /* + 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) + fprintf(stderr, + "Error in my_thread_global_end(): %d threads didn't exit\n", + THR_thread_count); +#endif + } + + pthread_key_delete(THR_KEY_mysys); +#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 + if (all_threads_killed) + { + pthread_mutex_destroy(&THR_LOCK_threads); + pthread_cond_destroy(&THR_COND_threads); + pthread_mutex_destroy(&THR_LOCK_malloc); + } +} + +/* Free all mutex used by mysys */ + +void my_thread_destroy_mutex(void) +{ + struct st_my_thread_var *tmp; + tmp= my_pthread_getspecific(struct st_my_thread_var*,THR_KEY_mysys); +#ifdef SAFE_MUTEX + if (tmp) + { + safe_mutex_free_deadlock_data(&tmp->mutex); + } +#endif + + pthread_mutex_destroy(&THR_LOCK_open); + pthread_mutex_destroy(&THR_LOCK_lock); + pthread_mutex_destroy(&THR_LOCK_isam); + pthread_mutex_destroy(&THR_LOCK_myisam); + pthread_mutex_destroy(&THR_LOCK_myisam_log); + pthread_mutex_destroy(&THR_LOCK_heap); + pthread_mutex_destroy(&THR_LOCK_net); + pthread_mutex_destroy(&THR_LOCK_time); + pthread_mutex_destroy(&THR_LOCK_charset); +#if !defined(HAVE_LOCALTIME_R) || !defined(HAVE_GMTIME_R) + pthread_mutex_destroy(&LOCK_localtime_r); +#endif +#ifndef HAVE_GETHOSTBYNAME_R + pthread_mutex_destroy(&LOCK_gethostbyname_r); +#endif +} + +static my_thread_id thread_id= 0; + +/* + Allocate thread specific memory for the thread, used by mysys and dbug + + SYNOPSIS + my_thread_init() + + NOTES + We can't use mutex_locks here if we are using windows as + we may have compiled the program with SAFE_MUTEX, in which + case the checking of mutex_locks will not work until + the pthread_self thread specific variable is initialized. + + This function may called multiple times for a thread, for example + if one uses my_init() followed by mysql_server_init(). + + RETURN + 0 ok + 1 Fatal error; mysys/dbug functions can't be used +*/ + +my_bool my_thread_init(void) +{ + struct st_my_thread_var *tmp; + my_bool error=0; + +#ifdef EXTRA_DEBUG_THREADS + fprintf(stderr,"my_thread_init(): thread_id: 0x%lx\n", + (ulong) pthread_self()); +#endif + + if (my_pthread_getspecific(struct st_my_thread_var *,THR_KEY_mysys)) + { +#ifdef EXTRA_DEBUG_THREADS + fprintf(stderr,"my_thread_init() called more than once in thread 0x%lx\n", + (long) pthread_self()); +#endif + goto end; + } + +#ifdef _MSC_VER + install_sigabrt_handler(); +#endif + + if (!(tmp= (struct st_my_thread_var *) calloc(1, sizeof(*tmp)))) + { + error= 1; + goto end; + } + pthread_setspecific(THR_KEY_mysys,tmp); + tmp->pthread_self= pthread_self(); + my_pthread_mutex_init(&tmp->mutex, MY_MUTEX_INIT_FAST, "mysys_var->mutex", + 0); + pthread_cond_init(&tmp->suspend, NULL); + + tmp->stack_ends_here= (char*)&tmp + + STACK_DIRECTION * (long)my_thread_stack_size; + + pthread_mutex_lock(&THR_LOCK_threads); + tmp->id= ++thread_id; + ++THR_thread_count; + pthread_mutex_unlock(&THR_LOCK_threads); + tmp->init= 1; +#ifndef DBUG_OFF + /* Generate unique name for thread */ + (void) my_thread_name(); +#endif + +end: + return error; +} + + +/* + Deallocate memory used by the thread for book-keeping + + SYNOPSIS + my_thread_end() + + 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(void) +{ + struct st_my_thread_var *tmp; + tmp= my_pthread_getspecific(struct st_my_thread_var*,THR_KEY_mysys); + +#ifdef EXTRA_DEBUG_THREADS + fprintf(stderr,"my_thread_end(): tmp: 0x%lx pthread_self: 0x%lx thread_id: %ld\n", + (long) tmp, (long) pthread_self(), tmp ? (long) tmp->id : 0L); +#endif + if (tmp && tmp->init) + { + +#if !defined(__bsdi__) && !defined(__OpenBSD__) + /* bsdi and openbsd 3.5 dumps core here */ + pthread_cond_destroy(&tmp->suspend); +#endif + pthread_mutex_destroy(&tmp->mutex); + +#if !defined(DBUG_OFF) + /* tmp->dbug is allocated inside DBUG library */ + if (tmp->dbug) + { + /* + Frees memory allocated in SET DEBUG=...; does nothing if there were no + SET DEBUG in a thread. + */ + DBUG_POP(); + free(tmp->dbug); + tmp->dbug=0; + } +#endif +#ifndef DBUG_OFF + /* To find bugs when accessing unallocated data */ + bfill(tmp, sizeof(tmp), 0x8F); +#endif + free(tmp); + pthread_setspecific(THR_KEY_mysys,0); + /* + 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 + */ + pthread_mutex_lock(&THR_LOCK_threads); + DBUG_ASSERT(THR_thread_count != 0); + if (--THR_thread_count == 0) + pthread_cond_signal(&THR_COND_threads); + pthread_mutex_unlock(&THR_LOCK_threads); + } + else + { + pthread_setspecific(THR_KEY_mysys,0); + } +} + +struct st_my_thread_var *_my_thread_var(void) +{ + return my_pthread_getspecific(struct st_my_thread_var*,THR_KEY_mysys); +} + +#ifndef DBUG_OFF +/* Return pointer to DBUG for holding current state */ + +extern void **my_thread_var_dbug() +{ + struct st_my_thread_var *tmp= + my_pthread_getspecific(struct st_my_thread_var*,THR_KEY_mysys); + return tmp && tmp->init ? &tmp->dbug : 0; +} +#endif + +/* Return pointer to mutex_in_use */ + +safe_mutex_t **my_thread_var_mutex_in_use() +{ + struct st_my_thread_var *tmp= + my_pthread_getspecific(struct st_my_thread_var*,THR_KEY_mysys); + return tmp ? &tmp->mutex_in_use : 0; +} + +/**************************************************************************** + Get name of current thread. +****************************************************************************/ + +my_thread_id my_thread_dbug_id() +{ + return my_thread_var->id; +} + +#ifdef DBUG_OFF +const char *my_thread_name(void) +{ + return "no_name"; +} + +#else + +const char *my_thread_name(void) +{ + char name_buff[100]; + struct st_my_thread_var *tmp=my_thread_var; + if (!tmp->name[0]) + { + my_thread_id id= my_thread_dbug_id(); + sprintf(name_buff,"T@%lu", (ulong) id); + strmake(tmp->name,name_buff,THREAD_NAME_SIZE); + } + return tmp->name; +} +#endif /* DBUG_OFF */ + + +static uint get_thread_lib(void) +{ +#ifdef _CS_GNU_LIBPTHREAD_VERSION + char buff[64]; + + confstr(_CS_GNU_LIBPTHREAD_VERSION, buff, sizeof(buff)); + + if (!strncasecmp(buff, "NPTL", 4)) + return THD_LIB_NPTL; + if (!strncasecmp(buff, "linuxthreads", 12)) + return THD_LIB_LT; +#endif + return THD_LIB_OTHER; +} + +#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. +*/ + +#if (_MSC_VER >= 1400) +static void my_sigabrt_handler(int sig) +{ + __debugbreak(); +} +#endif /*_MSC_VER >=1400 */ + +static void install_sigabrt_handler(void) +{ +#if (_MSC_VER >=1400) + /*abort() should not override our exception filter*/ + _set_abort_behavior(0,_CALL_REPORTFAULT); + signal(SIGABRT,my_sigabrt_handler); +#endif /* _MSC_VER >=1400 */ +} +#endif + +#endif /* THREAD */ diff --git a/externals/mysql/mysys/my_uuid.c b/externals/mysql/mysys/my_uuid.c new file mode 100644 index 00000000000..64db822cfb0 --- /dev/null +++ b/externals/mysql/mysys/my_uuid.c @@ -0,0 +1,243 @@ +/* Copyright (C) 2007 MySQL AB, Sergei Golubchik & Michael Widenius + + 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +/* + implements Universal Unique Identifiers (UUIDs), as in + DCE 1.1: Remote Procedure Call, + Open Group Technical Standard Document Number C706, October 1997, + (supersedes C309 DCE: Remote Procedure Call 8/1994, + which was basis for ISO/IEC 11578:1996 specification) + + A UUID has the following structure: + + Field NDR Data Type Octet # Note + time_low unsigned long 0-3 The low field of the + timestamp. + time_mid unsigned short 4-5 The middle field of + the timestamp. + time_hi_and_version unsigned short 6-7 The high field of the + timestamp multiplexed + with the version number. + clock_seq_hi_and_reserved unsigned small 8 The high field of the + clock sequence multi- + plexed with the variant. + clock_seq_low unsigned small 9 The low field of the + clock sequence. + node character 10-15 The spatially unique node + identifier. +*/ + +#include "mysys_priv.h" +#include <m_string.h> +#include <myisampack.h> /* mi_int2store, mi_int4store */ + +static my_bool my_uuid_inited= 0; +static struct my_rnd_struct uuid_rand; +static uint nanoseq; +static ulonglong uuid_time= 0; +static uchar uuid_suffix[2+6]; /* clock_seq and node */ + +#ifdef THREAD +static pthread_mutex_t LOCK_uuid_generator; +#endif + +/* + Number of 100-nanosecond intervals between + 1582-10-15 00:00:00.00 and 1970-01-01 00:00:00.00 +*/ + +#define UUID_TIME_OFFSET ((ulonglong) 141427 * 24 * 60 * 60 * \ + 1000 * 1000 * 10) +#define UUID_VERSION 0x1000 +#define UUID_VARIANT 0x8000 + + +/* Helper function */ + +static void set_clock_seq() +{ + uint16 clock_seq= ((uint)(my_rnd(&uuid_rand)*16383)) | UUID_VARIANT; + mi_int2store(uuid_suffix, clock_seq); +} + + +/** + Init structures needed for my_uuid + + @func my_uuid_init() + @param seed1 Seed for random generator + @param seed2 Seed for random generator + + @note + Seed1 & seed2 should NOT depend on clock. This is to be able to + generate a random mac address according to UUID specs. +*/ + +void my_uuid_init(ulong seed1, ulong seed2) +{ + uchar *mac= uuid_suffix+2; + ulonglong now; + + if (my_uuid_inited) + return; + my_uuid_inited= 1; + now= my_getsystime(); + nanoseq= 0; + + if (my_gethwaddr(mac)) + { + uint i; + /* + Generating random "hardware addr" + + Specs explicitly specify that node identifier should NOT + correlate with a clock_seq value, so we use a separate + randominit() here. + */ + /* purecov: begin inspected */ + my_rnd_init(&uuid_rand, (ulong) (seed2+ now/2), (ulong) (now+rand())); + for (i=0; i < sizeof(mac); i++) + mac[i]= (uchar)(my_rnd(&uuid_rand)*255); + /* purecov: end */ + } + my_rnd_init(&uuid_rand, (ulong) (seed1 + now), (ulong) (now/2+ getpid())); + set_clock_seq(); + pthread_mutex_init(&LOCK_uuid_generator, MY_MUTEX_INIT_FAST); +} + + +/** + Create a global unique identifier (uuid) + + @func my_uuid() + @param to Store uuid here. Must be of size MY_uuid_SIZE (16) +*/ + +void my_uuid(uchar *to) +{ + ulonglong tv; + uint32 time_low; + uint16 time_mid, time_hi_and_version; + + DBUG_ASSERT(my_uuid_inited); + + pthread_mutex_lock(&LOCK_uuid_generator); + tv= my_getsystime() + UUID_TIME_OFFSET + nanoseq; + + if (likely(tv > uuid_time)) + { + /* + Current time is ahead of last timestamp, as it should be. + If we "borrowed time", give it back, just as long as we + stay ahead of the previous timestamp. + */ + if (nanoseq) + { + uint delta; + DBUG_ASSERT((tv > uuid_time) && (nanoseq > 0)); + /* + -1 so we won't make tv= uuid_time for nanoseq >= (tv - uuid_time) + */ + delta= min(nanoseq, (uint)(tv - uuid_time -1)); + tv-= delta; + nanoseq-= delta; + } + } + else + { + if (unlikely(tv == uuid_time)) + { + /* + For low-res system clocks. If several requests for UUIDs + end up on the same tick, we add a nano-second to make them + different. + ( current_timestamp + nanoseq * calls_in_this_period ) + may end up > next_timestamp; this is OK. Nonetheless, we'll + try to unwind nanoseq when we get a chance to. + If nanoseq overflows, we'll start over with a new numberspace + (so the if() below is needed so we can avoid the ++tv and thus + match the follow-up if() if nanoseq overflows!). + */ + if (likely(++nanoseq)) + ++tv; + } + + if (unlikely(tv <= uuid_time)) + { + /* + If the admin changes the system clock (or due to Daylight + Saving Time), the system clock may be turned *back* so we + go through a period once more for which we already gave out + UUIDs. To avoid duplicate UUIDs despite potentially identical + times, we make a new random component. + We also come here if the nanoseq "borrowing" overflows. + In either case, we throw away any nanoseq borrowing since it's + irrelevant in the new numberspace. + */ + set_clock_seq(); + tv= my_getsystime() + UUID_TIME_OFFSET; + nanoseq= 0; + DBUG_PRINT("uuid",("making new numberspace")); + } + } + + uuid_time=tv; + pthread_mutex_unlock(&LOCK_uuid_generator); + + time_low= (uint32) (tv & 0xFFFFFFFF); + time_mid= (uint16) ((tv >> 32) & 0xFFFF); + time_hi_and_version= (uint16) ((tv >> 48) | UUID_VERSION); + + /* + Note, that the standard does NOT specify byte ordering in + multi-byte fields. it's implementation defined (but must be + the same for all fields). + We use big-endian, so we can use memcmp() to compare UUIDs + and for straightforward UUID to string conversion. + */ + mi_int4store(to, time_low); + mi_int2store(to+4, time_mid); + mi_int2store(to+6, time_hi_and_version); + bmove(to+8, uuid_suffix, sizeof(uuid_suffix)); +} + + +/** + Convert uuid to string representation + + @func my_uuid2str() + @param guid uuid + @param s Output buffer.Must be at least MY_UUID_STRING_LENGTH+1 large. +*/ +void my_uuid2str(const uchar *guid, char *s) +{ + int i; + for (i=0; i < MY_UUID_SIZE; i++) + { + *s++= _dig_vec_lower[guid[i] >>4]; + *s++= _dig_vec_lower[guid[i] & 15]; + if(i == 3 || i == 5 || i == 7 || i == 9) + *s++= '-'; + } +} + +void my_uuid_end() +{ + if (my_uuid_inited) + { + my_uuid_inited= 0; + pthread_mutex_destroy(&LOCK_uuid_generator); + } +} diff --git a/externals/mysql/mysys/my_vle.c b/externals/mysql/mysys/my_vle.c new file mode 100644 index 00000000000..09f297eb553 --- /dev/null +++ b/externals/mysql/mysys/my_vle.c @@ -0,0 +1,109 @@ +/* Copyright (C) 2005 MySQL AB + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +/* + Variable length encoding. + + A method to store an arbitrary-size non-negative integer. We let the + most significant bit of the number indicate that the next byte + should be contatenated to form the real number. +*/ + +#include "my_vle.h" + +/* + Function to encode an unsigned long as VLE. The bytes for the VLE + will be written to the location pointed to by 'out'. The maximum + number of bytes written will be 'max'. + + PARAMETERS + + out Pointer to beginning of where to store VLE bytes. + max Maximum number of bytes to write. + n Number to encode. + + RETURN VALUE + On success, one past the end of the array containing the VLE + bytes. On failure, the 'out' pointer is returned. +*/ + +uchar* +my_vle_encode(uchar* out, size_t max, ulong n) +{ + uchar buf[my_vle_sizeof(n)]; + uchar *ptr= buf; + size_t len; + + do + { + *ptr++= (uchar) (n & 0x7F); + n>>= 7; + } + while (n > 0); + + len= ptr - buf; + + if (len <= max) + { + /* + The bytes are stored in reverse order in 'buf'. Let's write them + in correct order to the output buffer and set the MSB at the + same time. + */ + while (ptr-- > buf) + { + uchar v= *ptr; + if (ptr > buf) + v|= 0x80; + *out++= v; + } + } + + return out; +} + +/* + Function to decode a VLE representation of an integral value. + + + PARAMETERS + + result_ptr Pointer to an unsigned long where the value will be written. + vle Pointer to the VLE bytes. + + RETURN VALUE + + One-past the end of the VLE bytes. The routine will never read + more than sizeof(*result_ptr) + 1 bytes. +*/ + +uchar const* +my_vle_decode(ulong *result_ptr, uchar const *vle) +{ + ulong result= 0; + size_t cnt= 1; + + do + { + result<<= 7; + result|= (*vle & 0x7F); + } + while ((*vle++ & 0x80) && ++cnt <= sizeof(*result_ptr) + 1); + + if (cnt <= sizeof(*result_ptr) + 1) + *result_ptr= result; + + return vle; +} diff --git a/externals/mysql/mysys/my_wincond.c b/externals/mysql/mysys/my_wincond.c new file mode 100644 index 00000000000..862eafce435 --- /dev/null +++ b/externals/mysql/mysys/my_wincond.c @@ -0,0 +1,222 @@ +/* Copyright (C) 2000 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +/***************************************************************************** +** The following is a simple implementation of posix conditions +*****************************************************************************/ +#if defined(_WIN32) + +#undef SAFE_MUTEX /* Avoid safe_mutex redefinitions */ +#include "mysys_priv.h" +#include <m_string.h> +#include <process.h> +#include <sys/timeb.h> + +int pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *attr) +{ + cond->waiting= 0; + InitializeCriticalSection(&cond->lock_waiting); + + cond->events[SIGNAL]= CreateEvent(NULL, /* no security */ + FALSE, /* auto-reset event */ + FALSE, /* non-signaled initially */ + NULL); /* unnamed */ + + /* Create a manual-reset event. */ + cond->events[BROADCAST]= CreateEvent(NULL, /* no security */ + TRUE, /* manual-reset */ + FALSE, /* non-signaled initially */ + NULL); /* unnamed */ + + + cond->broadcast_block_event= CreateEvent(NULL, /* no security */ + TRUE, /* manual-reset */ + TRUE, /* signaled initially */ + NULL); /* unnamed */ + + if( cond->events[SIGNAL] == NULL || + cond->events[BROADCAST] == NULL || + cond->broadcast_block_event == NULL ) + return ENOMEM; + return 0; +} + +int pthread_cond_destroy(pthread_cond_t *cond) +{ + DeleteCriticalSection(&cond->lock_waiting); + + if (CloseHandle(cond->events[SIGNAL]) == 0 || + CloseHandle(cond->events[BROADCAST]) == 0 || + CloseHandle(cond->broadcast_block_event) == 0) + return EINVAL; + return 0; +} + + +int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex) +{ + return pthread_cond_timedwait(cond,mutex,NULL); +} + + +int pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, + struct timespec *abstime) +{ + int result; + long timeout; + union ft64 now; + + if( abstime != NULL ) + { + GetSystemTimeAsFileTime(&now.ft); + + /* + Calculate time left to abstime + - subtract start time from current time(values are in 100ns units) + - convert to millisec by dividing with 10000 + */ + timeout= (long)((abstime->tv.i64 - now.i64) / 10000); + + /* Don't allow the timeout to be negative */ + if (timeout < 0) + timeout= 0L; + + /* + Make sure the calucated timeout does not exceed original timeout + value which could cause "wait for ever" if system time changes + */ + if (timeout > abstime->max_timeout_msec) + timeout= abstime->max_timeout_msec; + + } + else + { + /* No time specified; don't expire */ + timeout= INFINITE; + } + + /* + Block access if previous broadcast hasn't finished. + This is just for safety and should normally not + affect the total time spent in this function. + */ + WaitForSingleObject(cond->broadcast_block_event, INFINITE); + + EnterCriticalSection(&cond->lock_waiting); + cond->waiting++; + LeaveCriticalSection(&cond->lock_waiting); + + LeaveCriticalSection(mutex); + + result= WaitForMultipleObjects(2, cond->events, FALSE, timeout); + + EnterCriticalSection(&cond->lock_waiting); + cond->waiting--; + + if (cond->waiting == 0) + { + /* + We're the last waiter to be notified or to stop waiting, so + reset the manual event. + */ + /* Close broadcast gate */ + ResetEvent(cond->events[BROADCAST]); + /* Open block gate */ + SetEvent(cond->broadcast_block_event); + } + LeaveCriticalSection(&cond->lock_waiting); + + EnterCriticalSection(mutex); + + return result == WAIT_TIMEOUT ? ETIMEDOUT : 0; +} + +int pthread_cond_signal(pthread_cond_t *cond) +{ + EnterCriticalSection(&cond->lock_waiting); + + if(cond->waiting > 0) + SetEvent(cond->events[SIGNAL]); + + LeaveCriticalSection(&cond->lock_waiting); + + return 0; +} + + +int pthread_cond_broadcast(pthread_cond_t *cond) +{ + EnterCriticalSection(&cond->lock_waiting); + /* + The mutex protect us from broadcasting if + there isn't any thread waiting to open the + block gate after this call has closed it. + */ + if(cond->waiting > 0) + { + /* Close block gate */ + ResetEvent(cond->broadcast_block_event); + /* Open broadcast gate */ + SetEvent(cond->events[BROADCAST]); + } + + LeaveCriticalSection(&cond->lock_waiting); + + return 0; +} + + +int pthread_attr_init(pthread_attr_t *connect_att) +{ + connect_att->dwStackSize = 0; + connect_att->dwCreatingFlag = 0; + return 0; +} + +int pthread_attr_setstacksize(pthread_attr_t *connect_att,DWORD stack) +{ + connect_att->dwStackSize=stack; + return 0; +} + +int pthread_attr_destroy(pthread_attr_t *connect_att) +{ + bzero((uchar*) connect_att,sizeof(*connect_att)); + return 0; +} + +/**************************************************************************** +** Fix localtime_r() to be a bit safer +****************************************************************************/ + +struct tm *localtime_r(const time_t *timep,struct tm *tmp) +{ + if (*timep == (time_t) -1) /* This will crash win32 */ + { + bzero(tmp,sizeof(*tmp)); + } + else + { + struct tm *res=localtime(timep); + if (!res) /* Wrong date */ + { + bzero(tmp,sizeof(*tmp)); /* Keep things safe */ + return 0; + } + *tmp= *res; + } + return tmp; +} +#endif /* __WIN__ */ diff --git a/externals/mysql/mysys/my_windac.c b/externals/mysql/mysys/my_windac.c new file mode 100644 index 00000000000..f846853f7be --- /dev/null +++ b/externals/mysql/mysys/my_windac.c @@ -0,0 +1,223 @@ +/* Copyright (C) 2000-2005 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#include "mysys_priv.h" +#include "m_string.h" +#ifdef __WIN__ + +/* Windows NT/2000 discretionary access control utility functions. */ + +/* + Check if the operating system is built on NT technology. + + RETURN + 0 Windows 95/98/Me + 1 otherwise +*/ + +static my_bool is_nt() +{ + return GetVersion() < 0x80000000; +} + +/* + Auxilary structure to store pointers to the data which we need to keep + around while SECURITY_ATTRIBUTES is in use. +*/ + +typedef struct st_my_security_attr +{ + PSID everyone_sid; + PACL dacl; +} My_security_attr; + + +/* + Allocate and initialize SECURITY_ATTRIBUTES setting up access + rights for the owner and group `Everybody'. + + SYNOPSIS + my_security_attr_create() + psa [OUT] pointer to store the pointer to SA in + perror [OUT] pointer to store error message if there was an + error + owner_rights [IN] access rights for the owner + everyone_rights [IN] access rights for group Everybody + + DESCRIPTION + Set up the security attributes to provide clients with sufficient + access rights to a kernel object. We need this function + because if we simply grant all access to everybody (by installing + a NULL DACL) a mailicious user can attempt a denial of service + attack by taking ownership over the kernel object. Upon successful + return `psa' contains a pointer to SECUIRITY_ATTRIBUTES that can be used + to create kernel objects with proper access rights. + + RETURN + 0 success, psa is 0 or points to a valid SA structure, + perror is left intact + !0 error, SA is set to 0, error message is stored in perror +*/ + +int my_security_attr_create(SECURITY_ATTRIBUTES **psa, const char **perror, + DWORD owner_rights, DWORD everyone_rights) +{ + /* Top-level SID authority */ + SID_IDENTIFIER_AUTHORITY world_auth= SECURITY_WORLD_SID_AUTHORITY; + PSID everyone_sid= 0; + HANDLE htoken= 0; + SECURITY_ATTRIBUTES *sa= 0; + PACL dacl= 0; + DWORD owner_token_length, dacl_length; + SECURITY_DESCRIPTOR *sd; + PTOKEN_USER owner_token; + PSID owner_sid; + My_security_attr *attr; + + if (! is_nt()) + { + *psa= 0; + return 0; + } + + /* + Get SID of Everyone group. Easier to retrieve all SIDs each time + this function is called than worry about thread safety. + */ + if (! AllocateAndInitializeSid(&world_auth, 1, SECURITY_WORLD_RID, + 0, 0, 0, 0, 0, 0, 0, &everyone_sid)) + { + *perror= "Failed to retrieve the SID of Everyone group"; + goto error; + } + + /* + Get SID of the owner. Using GetSecurityInfo this task can be done + in just one call instead of five, but GetSecurityInfo declared in + aclapi.h, so I hesitate to use it. + SIC: OpenThreadToken works only if there is an active impersonation + token, hence OpenProcessToken is used. + */ + if (! OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &htoken)) + { + *perror= "Failed to retrieve thread access token"; + goto error; + } + GetTokenInformation(htoken, TokenUser, 0, 0, &owner_token_length); + + if (! my_multi_malloc(MYF(MY_WME), + &sa, ALIGN_SIZE(sizeof(SECURITY_ATTRIBUTES)) + + sizeof(My_security_attr), + &sd, sizeof(SECURITY_DESCRIPTOR), + &owner_token, owner_token_length, + 0)) + { + *perror= "Failed to allocate memory for SECURITY_ATTRIBUTES"; + goto error; + } + bzero(owner_token, owner_token_length); + if (! GetTokenInformation(htoken, TokenUser, owner_token, + owner_token_length, &owner_token_length)) + { + *perror= "GetTokenInformation failed"; + goto error; + } + owner_sid= owner_token->User.Sid; + + if (! IsValidSid(owner_sid)) + { + *perror= "IsValidSid failed"; + goto error; + } + + /* Calculate the amount of memory that must be allocated for the DACL */ + dacl_length= sizeof(ACL) + (sizeof(ACCESS_ALLOWED_ACE)-sizeof(DWORD)) * 2 + + GetLengthSid(everyone_sid) + GetLengthSid(owner_sid); + + /* Create an ACL */ + if (! (dacl= (PACL) my_malloc(dacl_length, MYF(MY_ZEROFILL|MY_WME)))) + { + *perror= "Failed to allocate memory for DACL"; + goto error; + } + if (! InitializeAcl(dacl, dacl_length, ACL_REVISION)) + { + *perror= "Failed to initialize DACL"; + goto error; + } + if (! AddAccessAllowedAce(dacl, ACL_REVISION, everyone_rights, everyone_sid)) + { + *perror= "Failed to set up DACL"; + goto error; + } + if (! AddAccessAllowedAce(dacl, ACL_REVISION, owner_rights, owner_sid)) + { + *perror= "Failed to set up DACL"; + goto error; + } + if (! InitializeSecurityDescriptor(sd, SECURITY_DESCRIPTOR_REVISION)) + { + *perror= "Could not initialize security descriptor"; + goto error; + } + if (! SetSecurityDescriptorDacl(sd, TRUE, dacl, FALSE)) + { + *perror= "Failed to install DACL"; + goto error; + } + + sa->nLength= sizeof(*sa); + sa->bInheritHandle= TRUE; + sa->lpSecurityDescriptor= sd; + /* Save pointers to everyone_sid and dacl to be able to clean them up */ + attr= (My_security_attr*) (((char*) sa) + ALIGN_SIZE(sizeof(*sa))); + attr->everyone_sid= everyone_sid; + attr->dacl= dacl; + *psa= sa; + + CloseHandle(htoken); + return 0; +error: + if (everyone_sid) + FreeSid(everyone_sid); + if (htoken) + CloseHandle(htoken); + my_free((uchar*) sa, MYF(MY_ALLOW_ZERO_PTR)); + my_free((uchar*) dacl, MYF(MY_ALLOW_ZERO_PTR)); + *psa= 0; + return 1; +} + +/* + Cleanup security attributes freeing used memory. + + SYNOPSIS + my_security_attr_free() + sa security attributes +*/ + +void my_security_attr_free(SECURITY_ATTRIBUTES *sa) +{ + if (sa) + { + My_security_attr *attr= (My_security_attr*) + (((char*)sa) + ALIGN_SIZE(sizeof(*sa))); + FreeSid(attr->everyone_sid); + my_free((uchar*) attr->dacl, MYF(0)); + my_free((uchar*) sa, MYF(0)); + } +} + +#endif /* __WIN__ */ diff --git a/externals/mysql/mysys/my_winerr.c b/externals/mysql/mysys/my_winerr.c new file mode 100644 index 00000000000..179909970a0 --- /dev/null +++ b/externals/mysql/mysys/my_winerr.c @@ -0,0 +1,127 @@ +/* Copyright (C) 2008 MySQL AB + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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). +*/ + +#ifdef _WIN32 + +#include <my_global.h> +#include <my_sys.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_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) +{ + errno= get_errno_from_oserr(oserrno); +} + +#endif diff --git a/externals/mysql/mysys/my_winfile.c b/externals/mysql/mysys/my_winfile.c new file mode 100644 index 00000000000..d5917d4e5ac --- /dev/null +++ b/externals/mysql/mysys/my_winfile.c @@ -0,0 +1,667 @@ +/* Copyright (C) 2008 MySQL AB + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 <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"); + + pthread_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; + } + } + pthread_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_osfhandle"); + 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(); + if(lastError == ERROR_HANDLE_EOF) + DBUG_RETURN(0); /*return 0 at EOF*/ + my_osmaperr(lastError); + DBUG_RETURN(-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) + 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(-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: %zd, offset: %llu", + Filedes, Buffer, 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(-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 %zd", fd, Buffer, Count)); + 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)) + { + nWritten= (size_t)-1; + my_osmaperr(GetLastError()); + } + DBUG_RETURN((size_t)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()); + 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/externals/mysql/mysys/my_winthread.c b/externals/mysql/mysys/my_winthread.c new file mode 100644 index 00000000000..9e8458b0799 --- /dev/null +++ b/externals/mysql/mysys/my_winthread.c @@ -0,0 +1,132 @@ +/* Copyright (C) 2000 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +/***************************************************************************** +** Simulation of posix threads calls for Windows +*****************************************************************************/ +#if defined (_WIN32) +/* SAFE_MUTEX will not work until the thread structure is up to date */ +#undef SAFE_MUTEX +#include "mysys_priv.h" +#include <process.h> +#include <signal.h> + +static void install_sigabrt_handler(void); + +struct thread_start_parameter +{ + pthread_handler func; + void *arg; +}; + +/** + Adapter to @c pthread_mutex_trylock() + + @retval 0 Mutex was acquired + @retval EBUSY Mutex was already locked by a thread + */ +int +win_pthread_mutex_trylock(pthread_mutex_t *mutex) +{ + if (TryEnterCriticalSection(mutex)) + { + /* Don't allow recursive lock */ + if (mutex->RecursionCount > 1){ + LeaveCriticalSection(mutex); + return EBUSY; + } + return 0; + } + return EBUSY; +} + +static unsigned int __stdcall pthread_start(void *p) +{ + struct thread_start_parameter *par= (struct thread_start_parameter *)p; + pthread_handler func= par->func; + void *arg= par->arg; + free(p); + (*func)(arg); + return 0; +} + + +int pthread_create(pthread_t *thread_id, pthread_attr_t *attr, + pthread_handler func, void *param) +{ + uintptr_t handle; + struct thread_start_parameter *par; + unsigned int stack_size; + DBUG_ENTER("pthread_create"); + + par= (struct thread_start_parameter *)malloc(sizeof(*par)); + if (!par) + goto error_return; + + par->func= func; + par->arg= param; + stack_size= attr?attr->dwStackSize:0; + + handle= _beginthreadex(NULL, stack_size , pthread_start, par, 0, thread_id); + if (!handle) + goto error_return; + DBUG_PRINT("info", ("thread id=%u",*thread_id)); + + /* Do not need thread handle, close it */ + CloseHandle((HANDLE)handle); + DBUG_RETURN(0); + +error_return: + DBUG_PRINT("error", + ("Can't create thread to handle request (error %d)",errno)); + DBUG_RETURN(-1); +} + + +void pthread_exit(void *a) +{ + _endthreadex(0); +} + +int pthread_join(pthread_t thread, void **value_ptr) +{ + DWORD ret; + HANDLE handle; + + handle= OpenThread(SYNCHRONIZE, FALSE, thread); + if (!handle) + { + errno= EINVAL; + goto error_return; + } + + ret= WaitForSingleObject(handle, INFINITE); + + if(ret != WAIT_OBJECT_0) + { + errno= EINVAL; + goto error_return; + } + + CloseHandle(handle); + return 0; + +error_return: + if(handle) + CloseHandle(handle); + return -1; +} + +#endif diff --git a/externals/mysql/mysys/my_write.c b/externals/mysql/mysys/my_write.c new file mode 100644 index 00000000000..3eac1364f46 --- /dev/null +++ b/externals/mysql/mysys/my_write.c @@ -0,0 +1,101 @@ +/* Copyright (C) 2000 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#include "mysys_priv.h" +#include "mysys_err.h" +#include <errno.h> + + + /* Write a chunk of bytes to a file */ + +size_t my_write(File Filedes, const uchar *Buffer, size_t Count, myf MyFlags) +{ + size_t writtenbytes, written; + uint errors; + DBUG_ENTER("my_write"); + DBUG_PRINT("my",("fd: %d Buffer: %p Count: %lu MyFlags: %d", + Filedes, Buffer, (ulong) Count, MyFlags)); + errors= 0; written= 0; + + /* The behavior of write(fd, buf, 0) is not portable */ + if (unlikely(!Count)) + DBUG_RETURN(0); + + for (;;) + { +#ifdef _WIN32 + writtenbytes= my_win_write(Filedes, Buffer, Count); +#else + writtenbytes= write(Filedes, Buffer, Count); +#endif + if (writtenbytes == Count) + break; + if (writtenbytes != (size_t) -1) + { /* Safeguard */ + written+= writtenbytes; + Buffer+= writtenbytes; + Count-= writtenbytes; + } + my_errno= errno; + DBUG_PRINT("error",("Write only %ld bytes, error: %d", + (long) writtenbytes, my_errno)); +#ifndef NO_BACKGROUND +#ifdef THREAD + if (my_thread_var->abort) + MyFlags&= ~ MY_WAIT_IF_FULL; /* End if aborted by user */ +#endif + 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)) + { + if (my_errno == EINTR) + { + DBUG_PRINT("debug", ("my_write() was interrupted and returned %ld", + (long) writtenbytes)); + continue; /* Interrupted */ + } + + if (!writtenbytes && !errors++) /* Retry once */ + { + /* We may come here if the file quota is exeeded */ + errno= EFBIG; /* Assume this is the error */ + continue; + } + } + else + continue; /* Retry */ +#endif + if (MyFlags & (MY_NABP | MY_FNABP)) + { + if (MyFlags & (MY_WME | MY_FAE | MY_FNABP)) + { + my_error(EE_WRITE, MYF(ME_BELL+ME_WAITTANG), + my_filename(Filedes),my_errno); + } + DBUG_RETURN(MY_FILE_ERROR); /* Error on read */ + } + else + break; /* Return bytes written */ + } + if (MyFlags & (MY_NABP | MY_FNABP)) + DBUG_RETURN(0); /* Want only errors */ + DBUG_RETURN(writtenbytes+written); +} /* my_write */ diff --git a/externals/mysql/mysys/mysys_priv.h b/externals/mysql/mysys/mysys_priv.h new file mode 100644 index 00000000000..73f8f85f1b9 --- /dev/null +++ b/externals/mysql/mysys/mysys_priv.h @@ -0,0 +1,72 @@ +/* Copyright (C) 2000 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#include <my_global.h> +#include <my_sys.h> + +#ifdef USE_SYSTEM_WRAPPERS +#include "system_wrappers.h" +#endif + +#ifdef HAVE_GETRUSAGE +#include <sys/resource.h> +#endif + +#ifdef THREAD +#include <my_pthread.h> +extern pthread_mutex_t THR_LOCK_malloc, THR_LOCK_open, THR_LOCK_keycache; +extern pthread_mutex_t THR_LOCK_lock, THR_LOCK_isam, THR_LOCK_net; +extern pthread_mutex_t THR_LOCK_charset, THR_LOCK_time; +#else +#include <my_no_pthread.h> +#endif + + +/* + 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 +/* 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 + +void my_thread_destroy_mutex(void); +my_bool my_wait_for_other_threads_to_die(uint number_of_threads); diff --git a/externals/mysql/mysys/ptr_cmp.c b/externals/mysql/mysys/ptr_cmp.c new file mode 100644 index 00000000000..24ab6a1ea9c --- /dev/null +++ b/externals/mysql/mysys/ptr_cmp.c @@ -0,0 +1,192 @@ +/* Copyright (C) 2000 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +/* + get_ptr_compare(len) returns a pointer to a optimal byte-compare function + for a array of stringpointer where all strings have size len. + The bytes are compare as unsigned chars. + */ + +#include "mysys_priv.h" +#include <myisampack.h> + +static int ptr_compare(size_t *compare_length, uchar **a, uchar **b); +static int ptr_compare_0(size_t *compare_length, uchar **a, uchar **b); +static int ptr_compare_1(size_t *compare_length, uchar **a, uchar **b); +static int ptr_compare_2(size_t *compare_length, uchar **a, uchar **b); +static int ptr_compare_3(size_t *compare_length, uchar **a, uchar **b); + + /* Get a pointer to a optimal byte-compare function for a given size */ + +qsort2_cmp get_ptr_compare (size_t size) +{ + if (size < 4) + return (qsort2_cmp) ptr_compare; + switch (size & 3) { + case 0: return (qsort2_cmp) ptr_compare_0; + case 1: return (qsort2_cmp) ptr_compare_1; + case 2: return (qsort2_cmp) ptr_compare_2; + case 3: return (qsort2_cmp) ptr_compare_3; + } + return 0; /* Impossible */ +} + + + /* + Compare to keys to see witch is smaller. + Loop unrolled to make it quick !! + */ + +#define cmp(N) if (first[N] != last[N]) return (int) first[N] - (int) last[N] + +static int ptr_compare(size_t *compare_length, uchar **a, uchar **b) +{ + reg3 int length= *compare_length; + reg1 uchar *first,*last; + + first= *a; last= *b; + while (--length) + { + if (*first++ != *last++) + return (int) first[-1] - (int) last[-1]; + } + return (int) first[0] - (int) last[0]; +} + + +static int ptr_compare_0(size_t *compare_length,uchar **a, uchar **b) +{ + reg3 int length= *compare_length; + reg1 uchar *first,*last; + + first= *a; last= *b; + loop: + cmp(0); + cmp(1); + cmp(2); + cmp(3); + if ((length-=4)) + { + first+=4; + last+=4; + goto loop; + } + return (0); +} + + +static int ptr_compare_1(size_t *compare_length,uchar **a, uchar **b) +{ + reg3 int length= *compare_length-1; + reg1 uchar *first,*last; + + first= *a+1; last= *b+1; + cmp(-1); + loop: + cmp(0); + cmp(1); + cmp(2); + cmp(3); + if ((length-=4)) + { + first+=4; + last+=4; + goto loop; + } + return (0); +} + +static int ptr_compare_2(size_t *compare_length,uchar **a, uchar **b) +{ + reg3 int length= *compare_length-2; + reg1 uchar *first,*last; + + first= *a +2 ; last= *b +2; + cmp(-2); + cmp(-1); + loop: + cmp(0); + cmp(1); + cmp(2); + cmp(3); + if ((length-=4)) + { + first+=4; + last+=4; + goto loop; + } + return (0); +} + +static int ptr_compare_3(size_t *compare_length,uchar **a, uchar **b) +{ + reg3 int length= *compare_length-3; + reg1 uchar *first,*last; + + first= *a +3 ; last= *b +3; + cmp(-3); + cmp(-2); + cmp(-1); + loop: + cmp(0); + cmp(1); + cmp(2); + cmp(3); + if ((length-=4)) + { + first+=4; + last+=4; + goto loop; + } + return (0); +} + +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/externals/mysql/mysys/queues.c b/externals/mysql/mysys/queues.c new file mode 100644 index 00000000000..7ca978f185f --- /dev/null +++ b/externals/mysql/mysys/queues.c @@ -0,0 +1,690 @@ +/* Copyright (C) 2000, 2005 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +/* + Code for generell 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 "mysys_err.h" +#include <queues.h> + + +/* + 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((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((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"); + if (queue->root) + { + my_free((uchar*) queue->root,MYF(0)); + queue->root=0; + } + DBUG_VOID_RETURN; +} + + + /* Code for insert, search and delete of elements */ + +void queue_insert(register QUEUE *queue, uchar *element) +{ + reg2 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; +} + +/* + Does safe insert. If no more space left on the queue resize it. + Return codes: + 0 - OK + 1 - Cannot allocate more memory + 2 - auto_extend is 0, the operation would + +*/ + +int queue_insert_safe(register QUEUE *queue, uchar *element) +{ + + if (queue->elements == queue->max_elements) + { + if (!queue->auto_extent) + return 2; + else if (resize_queue(queue, queue->max_elements + queue->auto_extent)) + return 1; + } + + queue_insert(queue, element); + return 0; +} + + + /* Remove item from queue */ + /* Returns pointer to removed element */ + +uchar *queue_remove(register 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; +} + + /* Fix when element on top has been replaced */ + +#ifndef queue_replaced +void queue_replaced(QUEUE *queue) +{ + _downheap(queue,1); +} +#endif + +#ifndef OLD_VERSION + +void _downheap(register 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; +} + +#else + /* + The old _downheap version is kept for comparisons with the benchmark + suit or new benchmarks anyone wants to run for comparisons. + */ + /* Fix heap when index have changed */ +void _downheap(register QUEUE *queue, uint idx) +{ + uchar *element; + uint elements,half_queue,next_index,offset_to_key; + + 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 ((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; + } + queue->root[idx]=element; +} + + +#endif + +/* + 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 test_priority_queue + ./test_priority_queue + + 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; + 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); + if ((result= perform_ins_del(&queue, max_ind))) + delete_queue(&queue); + if (result) + { + printf("Error\n"); + return TRUE; + } + 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(); +} + +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/externals/mysql/mysys/rijndael.c b/externals/mysql/mysys/rijndael.c new file mode 100644 index 00000000000..539d94d28c6 --- /dev/null +++ b/externals/mysql/mysys/rijndael.c @@ -0,0 +1,1395 @@ +/* Copyright (C) 2002, 2004 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + + +/* + Based on version 3.0 (December 2000) + + Optimised ANSI C code for the Rijndael cipher (now AES) + + author Vincent Rijmen <vincent.rijmen@esat.kuleuven.ac.be> + author Antoon Bosselaers <antoon.bosselaers@esat.kuleuven.ac.be> + author Paulo Barreto <paulo.barreto@terra.com.br> +*/ + +#include <my_global.h> +#include "mysys/rijndael.h" + +/* + Define the following to use fastest and much larger code (~10K extra code) + #define FULL_UNROLL +*/ + + +#ifdef NOT_USED +Te0[x] = S [x].[02, 01, 01, 03]; +Te1[x] = S [x].[03, 02, 01, 01]; +Te2[x] = S [x].[01, 03, 02, 01]; +Te3[x] = S [x].[01, 01, 03, 02]; +Te4[x] = S [x].[01, 01, 01, 01]; + +Td0[x] = Si[x].[0e, 09, 0d, 0b]; +Td1[x] = Si[x].[0b, 0e, 09, 0d]; +Td2[x] = Si[x].[0d, 0b, 0e, 09]; +Td3[x] = Si[x].[09, 0d, 0b, 0e]; +Td4[x] = Si[x].[01, 01, 01, 01]; +#endif + + +static const uint32 Te0[256]= +{ + 0xc66363a5U, 0xf87c7c84U, 0xee777799U, 0xf67b7b8dU, + 0xfff2f20dU, 0xd66b6bbdU, 0xde6f6fb1U, 0x91c5c554U, + 0x60303050U, 0x02010103U, 0xce6767a9U, 0x562b2b7dU, + 0xe7fefe19U, 0xb5d7d762U, 0x4dababe6U, 0xec76769aU, + 0x8fcaca45U, 0x1f82829dU, 0x89c9c940U, 0xfa7d7d87U, + 0xeffafa15U, 0xb25959ebU, 0x8e4747c9U, 0xfbf0f00bU, + 0x41adadecU, 0xb3d4d467U, 0x5fa2a2fdU, 0x45afafeaU, + 0x239c9cbfU, 0x53a4a4f7U, 0xe4727296U, 0x9bc0c05bU, + 0x75b7b7c2U, 0xe1fdfd1cU, 0x3d9393aeU, 0x4c26266aU, + 0x6c36365aU, 0x7e3f3f41U, 0xf5f7f702U, 0x83cccc4fU, + 0x6834345cU, 0x51a5a5f4U, 0xd1e5e534U, 0xf9f1f108U, + 0xe2717193U, 0xabd8d873U, 0x62313153U, 0x2a15153fU, + 0x0804040cU, 0x95c7c752U, 0x46232365U, 0x9dc3c35eU, + 0x30181828U, 0x379696a1U, 0x0a05050fU, 0x2f9a9ab5U, + 0x0e070709U, 0x24121236U, 0x1b80809bU, 0xdfe2e23dU, + 0xcdebeb26U, 0x4e272769U, 0x7fb2b2cdU, 0xea75759fU, + 0x1209091bU, 0x1d83839eU, 0x582c2c74U, 0x341a1a2eU, + 0x361b1b2dU, 0xdc6e6eb2U, 0xb45a5aeeU, 0x5ba0a0fbU, + 0xa45252f6U, 0x763b3b4dU, 0xb7d6d661U, 0x7db3b3ceU, + 0x5229297bU, 0xdde3e33eU, 0x5e2f2f71U, 0x13848497U, + 0xa65353f5U, 0xb9d1d168U, 0x00000000U, 0xc1eded2cU, + 0x40202060U, 0xe3fcfc1fU, 0x79b1b1c8U, 0xb65b5bedU, + 0xd46a6abeU, 0x8dcbcb46U, 0x67bebed9U, 0x7239394bU, + 0x944a4adeU, 0x984c4cd4U, 0xb05858e8U, 0x85cfcf4aU, + 0xbbd0d06bU, 0xc5efef2aU, 0x4faaaae5U, 0xedfbfb16U, + 0x864343c5U, 0x9a4d4dd7U, 0x66333355U, 0x11858594U, + 0x8a4545cfU, 0xe9f9f910U, 0x04020206U, 0xfe7f7f81U, + 0xa05050f0U, 0x783c3c44U, 0x259f9fbaU, 0x4ba8a8e3U, + 0xa25151f3U, 0x5da3a3feU, 0x804040c0U, 0x058f8f8aU, + 0x3f9292adU, 0x219d9dbcU, 0x70383848U, 0xf1f5f504U, + 0x63bcbcdfU, 0x77b6b6c1U, 0xafdada75U, 0x42212163U, + 0x20101030U, 0xe5ffff1aU, 0xfdf3f30eU, 0xbfd2d26dU, + 0x81cdcd4cU, 0x180c0c14U, 0x26131335U, 0xc3ecec2fU, + 0xbe5f5fe1U, 0x359797a2U, 0x884444ccU, 0x2e171739U, + 0x93c4c457U, 0x55a7a7f2U, 0xfc7e7e82U, 0x7a3d3d47U, + 0xc86464acU, 0xba5d5de7U, 0x3219192bU, 0xe6737395U, + 0xc06060a0U, 0x19818198U, 0x9e4f4fd1U, 0xa3dcdc7fU, + 0x44222266U, 0x542a2a7eU, 0x3b9090abU, 0x0b888883U, + 0x8c4646caU, 0xc7eeee29U, 0x6bb8b8d3U, 0x2814143cU, + 0xa7dede79U, 0xbc5e5ee2U, 0x160b0b1dU, 0xaddbdb76U, + 0xdbe0e03bU, 0x64323256U, 0x743a3a4eU, 0x140a0a1eU, + 0x924949dbU, 0x0c06060aU, 0x4824246cU, 0xb85c5ce4U, + 0x9fc2c25dU, 0xbdd3d36eU, 0x43acacefU, 0xc46262a6U, + 0x399191a8U, 0x319595a4U, 0xd3e4e437U, 0xf279798bU, + 0xd5e7e732U, 0x8bc8c843U, 0x6e373759U, 0xda6d6db7U, + 0x018d8d8cU, 0xb1d5d564U, 0x9c4e4ed2U, 0x49a9a9e0U, + 0xd86c6cb4U, 0xac5656faU, 0xf3f4f407U, 0xcfeaea25U, + 0xca6565afU, 0xf47a7a8eU, 0x47aeaee9U, 0x10080818U, + 0x6fbabad5U, 0xf0787888U, 0x4a25256fU, 0x5c2e2e72U, + 0x381c1c24U, 0x57a6a6f1U, 0x73b4b4c7U, 0x97c6c651U, + 0xcbe8e823U, 0xa1dddd7cU, 0xe874749cU, 0x3e1f1f21U, + 0x964b4bddU, 0x61bdbddcU, 0x0d8b8b86U, 0x0f8a8a85U, + 0xe0707090U, 0x7c3e3e42U, 0x71b5b5c4U, 0xcc6666aaU, + 0x904848d8U, 0x06030305U, 0xf7f6f601U, 0x1c0e0e12U, + 0xc26161a3U, 0x6a35355fU, 0xae5757f9U, 0x69b9b9d0U, + 0x17868691U, 0x99c1c158U, 0x3a1d1d27U, 0x279e9eb9U, + 0xd9e1e138U, 0xebf8f813U, 0x2b9898b3U, 0x22111133U, + 0xd26969bbU, 0xa9d9d970U, 0x078e8e89U, 0x339494a7U, + 0x2d9b9bb6U, 0x3c1e1e22U, 0x15878792U, 0xc9e9e920U, + 0x87cece49U, 0xaa5555ffU, 0x50282878U, 0xa5dfdf7aU, + 0x038c8c8fU, 0x59a1a1f8U, 0x09898980U, 0x1a0d0d17U, + 0x65bfbfdaU, 0xd7e6e631U, 0x844242c6U, 0xd06868b8U, + 0x824141c3U, 0x299999b0U, 0x5a2d2d77U, 0x1e0f0f11U, + 0x7bb0b0cbU, 0xa85454fcU, 0x6dbbbbd6U, 0x2c16163aU, +}; + +static const uint32 Te1[256]= +{ + 0xa5c66363U, 0x84f87c7cU, 0x99ee7777U, 0x8df67b7bU, + 0x0dfff2f2U, 0xbdd66b6bU, 0xb1de6f6fU, 0x5491c5c5U, + 0x50603030U, 0x03020101U, 0xa9ce6767U, 0x7d562b2bU, + 0x19e7fefeU, 0x62b5d7d7U, 0xe64dababU, 0x9aec7676U, + 0x458fcacaU, 0x9d1f8282U, 0x4089c9c9U, 0x87fa7d7dU, + 0x15effafaU, 0xebb25959U, 0xc98e4747U, 0x0bfbf0f0U, + 0xec41adadU, 0x67b3d4d4U, 0xfd5fa2a2U, 0xea45afafU, + 0xbf239c9cU, 0xf753a4a4U, 0x96e47272U, 0x5b9bc0c0U, + 0xc275b7b7U, 0x1ce1fdfdU, 0xae3d9393U, 0x6a4c2626U, + 0x5a6c3636U, 0x417e3f3fU, 0x02f5f7f7U, 0x4f83ccccU, + 0x5c683434U, 0xf451a5a5U, 0x34d1e5e5U, 0x08f9f1f1U, + 0x93e27171U, 0x73abd8d8U, 0x53623131U, 0x3f2a1515U, + 0x0c080404U, 0x5295c7c7U, 0x65462323U, 0x5e9dc3c3U, + 0x28301818U, 0xa1379696U, 0x0f0a0505U, 0xb52f9a9aU, + 0x090e0707U, 0x36241212U, 0x9b1b8080U, 0x3ddfe2e2U, + 0x26cdebebU, 0x694e2727U, 0xcd7fb2b2U, 0x9fea7575U, + 0x1b120909U, 0x9e1d8383U, 0x74582c2cU, 0x2e341a1aU, + 0x2d361b1bU, 0xb2dc6e6eU, 0xeeb45a5aU, 0xfb5ba0a0U, + 0xf6a45252U, 0x4d763b3bU, 0x61b7d6d6U, 0xce7db3b3U, + 0x7b522929U, 0x3edde3e3U, 0x715e2f2fU, 0x97138484U, + 0xf5a65353U, 0x68b9d1d1U, 0x00000000U, 0x2cc1ededU, + 0x60402020U, 0x1fe3fcfcU, 0xc879b1b1U, 0xedb65b5bU, + 0xbed46a6aU, 0x468dcbcbU, 0xd967bebeU, 0x4b723939U, + 0xde944a4aU, 0xd4984c4cU, 0xe8b05858U, 0x4a85cfcfU, + 0x6bbbd0d0U, 0x2ac5efefU, 0xe54faaaaU, 0x16edfbfbU, + 0xc5864343U, 0xd79a4d4dU, 0x55663333U, 0x94118585U, + 0xcf8a4545U, 0x10e9f9f9U, 0x06040202U, 0x81fe7f7fU, + 0xf0a05050U, 0x44783c3cU, 0xba259f9fU, 0xe34ba8a8U, + 0xf3a25151U, 0xfe5da3a3U, 0xc0804040U, 0x8a058f8fU, + 0xad3f9292U, 0xbc219d9dU, 0x48703838U, 0x04f1f5f5U, + 0xdf63bcbcU, 0xc177b6b6U, 0x75afdadaU, 0x63422121U, + 0x30201010U, 0x1ae5ffffU, 0x0efdf3f3U, 0x6dbfd2d2U, + 0x4c81cdcdU, 0x14180c0cU, 0x35261313U, 0x2fc3ececU, + 0xe1be5f5fU, 0xa2359797U, 0xcc884444U, 0x392e1717U, + 0x5793c4c4U, 0xf255a7a7U, 0x82fc7e7eU, 0x477a3d3dU, + 0xacc86464U, 0xe7ba5d5dU, 0x2b321919U, 0x95e67373U, + 0xa0c06060U, 0x98198181U, 0xd19e4f4fU, 0x7fa3dcdcU, + 0x66442222U, 0x7e542a2aU, 0xab3b9090U, 0x830b8888U, + 0xca8c4646U, 0x29c7eeeeU, 0xd36bb8b8U, 0x3c281414U, + 0x79a7dedeU, 0xe2bc5e5eU, 0x1d160b0bU, 0x76addbdbU, + 0x3bdbe0e0U, 0x56643232U, 0x4e743a3aU, 0x1e140a0aU, + 0xdb924949U, 0x0a0c0606U, 0x6c482424U, 0xe4b85c5cU, + 0x5d9fc2c2U, 0x6ebdd3d3U, 0xef43acacU, 0xa6c46262U, + 0xa8399191U, 0xa4319595U, 0x37d3e4e4U, 0x8bf27979U, + 0x32d5e7e7U, 0x438bc8c8U, 0x596e3737U, 0xb7da6d6dU, + 0x8c018d8dU, 0x64b1d5d5U, 0xd29c4e4eU, 0xe049a9a9U, + 0xb4d86c6cU, 0xfaac5656U, 0x07f3f4f4U, 0x25cfeaeaU, + 0xafca6565U, 0x8ef47a7aU, 0xe947aeaeU, 0x18100808U, + 0xd56fbabaU, 0x88f07878U, 0x6f4a2525U, 0x725c2e2eU, + 0x24381c1cU, 0xf157a6a6U, 0xc773b4b4U, 0x5197c6c6U, + 0x23cbe8e8U, 0x7ca1ddddU, 0x9ce87474U, 0x213e1f1fU, + 0xdd964b4bU, 0xdc61bdbdU, 0x860d8b8bU, 0x850f8a8aU, + 0x90e07070U, 0x427c3e3eU, 0xc471b5b5U, 0xaacc6666U, + 0xd8904848U, 0x05060303U, 0x01f7f6f6U, 0x121c0e0eU, + 0xa3c26161U, 0x5f6a3535U, 0xf9ae5757U, 0xd069b9b9U, + 0x91178686U, 0x5899c1c1U, 0x273a1d1dU, 0xb9279e9eU, + 0x38d9e1e1U, 0x13ebf8f8U, 0xb32b9898U, 0x33221111U, + 0xbbd26969U, 0x70a9d9d9U, 0x89078e8eU, 0xa7339494U, + 0xb62d9b9bU, 0x223c1e1eU, 0x92158787U, 0x20c9e9e9U, + 0x4987ceceU, 0xffaa5555U, 0x78502828U, 0x7aa5dfdfU, + 0x8f038c8cU, 0xf859a1a1U, 0x80098989U, 0x171a0d0dU, + 0xda65bfbfU, 0x31d7e6e6U, 0xc6844242U, 0xb8d06868U, + 0xc3824141U, 0xb0299999U, 0x775a2d2dU, 0x111e0f0fU, + 0xcb7bb0b0U, 0xfca85454U, 0xd66dbbbbU, 0x3a2c1616U, +}; + +static const uint32 Te2[256]= +{ + 0x63a5c663U, 0x7c84f87cU, 0x7799ee77U, 0x7b8df67bU, + 0xf20dfff2U, 0x6bbdd66bU, 0x6fb1de6fU, 0xc55491c5U, + 0x30506030U, 0x01030201U, 0x67a9ce67U, 0x2b7d562bU, + 0xfe19e7feU, 0xd762b5d7U, 0xabe64dabU, 0x769aec76U, + 0xca458fcaU, 0x829d1f82U, 0xc94089c9U, 0x7d87fa7dU, + 0xfa15effaU, 0x59ebb259U, 0x47c98e47U, 0xf00bfbf0U, + 0xadec41adU, 0xd467b3d4U, 0xa2fd5fa2U, 0xafea45afU, + 0x9cbf239cU, 0xa4f753a4U, 0x7296e472U, 0xc05b9bc0U, + 0xb7c275b7U, 0xfd1ce1fdU, 0x93ae3d93U, 0x266a4c26U, + 0x365a6c36U, 0x3f417e3fU, 0xf702f5f7U, 0xcc4f83ccU, + 0x345c6834U, 0xa5f451a5U, 0xe534d1e5U, 0xf108f9f1U, + 0x7193e271U, 0xd873abd8U, 0x31536231U, 0x153f2a15U, + 0x040c0804U, 0xc75295c7U, 0x23654623U, 0xc35e9dc3U, + 0x18283018U, 0x96a13796U, 0x050f0a05U, 0x9ab52f9aU, + 0x07090e07U, 0x12362412U, 0x809b1b80U, 0xe23ddfe2U, + 0xeb26cdebU, 0x27694e27U, 0xb2cd7fb2U, 0x759fea75U, + 0x091b1209U, 0x839e1d83U, 0x2c74582cU, 0x1a2e341aU, + 0x1b2d361bU, 0x6eb2dc6eU, 0x5aeeb45aU, 0xa0fb5ba0U, + 0x52f6a452U, 0x3b4d763bU, 0xd661b7d6U, 0xb3ce7db3U, + 0x297b5229U, 0xe33edde3U, 0x2f715e2fU, 0x84971384U, + 0x53f5a653U, 0xd168b9d1U, 0x00000000U, 0xed2cc1edU, + 0x20604020U, 0xfc1fe3fcU, 0xb1c879b1U, 0x5bedb65bU, + 0x6abed46aU, 0xcb468dcbU, 0xbed967beU, 0x394b7239U, + 0x4ade944aU, 0x4cd4984cU, 0x58e8b058U, 0xcf4a85cfU, + 0xd06bbbd0U, 0xef2ac5efU, 0xaae54faaU, 0xfb16edfbU, + 0x43c58643U, 0x4dd79a4dU, 0x33556633U, 0x85941185U, + 0x45cf8a45U, 0xf910e9f9U, 0x02060402U, 0x7f81fe7fU, + 0x50f0a050U, 0x3c44783cU, 0x9fba259fU, 0xa8e34ba8U, + 0x51f3a251U, 0xa3fe5da3U, 0x40c08040U, 0x8f8a058fU, + 0x92ad3f92U, 0x9dbc219dU, 0x38487038U, 0xf504f1f5U, + 0xbcdf63bcU, 0xb6c177b6U, 0xda75afdaU, 0x21634221U, + 0x10302010U, 0xff1ae5ffU, 0xf30efdf3U, 0xd26dbfd2U, + 0xcd4c81cdU, 0x0c14180cU, 0x13352613U, 0xec2fc3ecU, + 0x5fe1be5fU, 0x97a23597U, 0x44cc8844U, 0x17392e17U, + 0xc45793c4U, 0xa7f255a7U, 0x7e82fc7eU, 0x3d477a3dU, + 0x64acc864U, 0x5de7ba5dU, 0x192b3219U, 0x7395e673U, + 0x60a0c060U, 0x81981981U, 0x4fd19e4fU, 0xdc7fa3dcU, + 0x22664422U, 0x2a7e542aU, 0x90ab3b90U, 0x88830b88U, + 0x46ca8c46U, 0xee29c7eeU, 0xb8d36bb8U, 0x143c2814U, + 0xde79a7deU, 0x5ee2bc5eU, 0x0b1d160bU, 0xdb76addbU, + 0xe03bdbe0U, 0x32566432U, 0x3a4e743aU, 0x0a1e140aU, + 0x49db9249U, 0x060a0c06U, 0x246c4824U, 0x5ce4b85cU, + 0xc25d9fc2U, 0xd36ebdd3U, 0xacef43acU, 0x62a6c462U, + 0x91a83991U, 0x95a43195U, 0xe437d3e4U, 0x798bf279U, + 0xe732d5e7U, 0xc8438bc8U, 0x37596e37U, 0x6db7da6dU, + 0x8d8c018dU, 0xd564b1d5U, 0x4ed29c4eU, 0xa9e049a9U, + 0x6cb4d86cU, 0x56faac56U, 0xf407f3f4U, 0xea25cfeaU, + 0x65afca65U, 0x7a8ef47aU, 0xaee947aeU, 0x08181008U, + 0xbad56fbaU, 0x7888f078U, 0x256f4a25U, 0x2e725c2eU, + 0x1c24381cU, 0xa6f157a6U, 0xb4c773b4U, 0xc65197c6U, + 0xe823cbe8U, 0xdd7ca1ddU, 0x749ce874U, 0x1f213e1fU, + 0x4bdd964bU, 0xbddc61bdU, 0x8b860d8bU, 0x8a850f8aU, + 0x7090e070U, 0x3e427c3eU, 0xb5c471b5U, 0x66aacc66U, + 0x48d89048U, 0x03050603U, 0xf601f7f6U, 0x0e121c0eU, + 0x61a3c261U, 0x355f6a35U, 0x57f9ae57U, 0xb9d069b9U, + 0x86911786U, 0xc15899c1U, 0x1d273a1dU, 0x9eb9279eU, + 0xe138d9e1U, 0xf813ebf8U, 0x98b32b98U, 0x11332211U, + 0x69bbd269U, 0xd970a9d9U, 0x8e89078eU, 0x94a73394U, + 0x9bb62d9bU, 0x1e223c1eU, 0x87921587U, 0xe920c9e9U, + 0xce4987ceU, 0x55ffaa55U, 0x28785028U, 0xdf7aa5dfU, + 0x8c8f038cU, 0xa1f859a1U, 0x89800989U, 0x0d171a0dU, + 0xbfda65bfU, 0xe631d7e6U, 0x42c68442U, 0x68b8d068U, + 0x41c38241U, 0x99b02999U, 0x2d775a2dU, 0x0f111e0fU, + 0xb0cb7bb0U, 0x54fca854U, 0xbbd66dbbU, 0x163a2c16U, +}; + +static const uint32 Te3[256]= +{ + 0x6363a5c6U, 0x7c7c84f8U, 0x777799eeU, 0x7b7b8df6U, + 0xf2f20dffU, 0x6b6bbdd6U, 0x6f6fb1deU, 0xc5c55491U, + 0x30305060U, 0x01010302U, 0x6767a9ceU, 0x2b2b7d56U, + 0xfefe19e7U, 0xd7d762b5U, 0xababe64dU, 0x76769aecU, + 0xcaca458fU, 0x82829d1fU, 0xc9c94089U, 0x7d7d87faU, + 0xfafa15efU, 0x5959ebb2U, 0x4747c98eU, 0xf0f00bfbU, + 0xadadec41U, 0xd4d467b3U, 0xa2a2fd5fU, 0xafafea45U, + 0x9c9cbf23U, 0xa4a4f753U, 0x727296e4U, 0xc0c05b9bU, + 0xb7b7c275U, 0xfdfd1ce1U, 0x9393ae3dU, 0x26266a4cU, + 0x36365a6cU, 0x3f3f417eU, 0xf7f702f5U, 0xcccc4f83U, + 0x34345c68U, 0xa5a5f451U, 0xe5e534d1U, 0xf1f108f9U, + 0x717193e2U, 0xd8d873abU, 0x31315362U, 0x15153f2aU, + 0x04040c08U, 0xc7c75295U, 0x23236546U, 0xc3c35e9dU, + 0x18182830U, 0x9696a137U, 0x05050f0aU, 0x9a9ab52fU, + 0x0707090eU, 0x12123624U, 0x80809b1bU, 0xe2e23ddfU, + 0xebeb26cdU, 0x2727694eU, 0xb2b2cd7fU, 0x75759feaU, + 0x09091b12U, 0x83839e1dU, 0x2c2c7458U, 0x1a1a2e34U, + 0x1b1b2d36U, 0x6e6eb2dcU, 0x5a5aeeb4U, 0xa0a0fb5bU, + 0x5252f6a4U, 0x3b3b4d76U, 0xd6d661b7U, 0xb3b3ce7dU, + 0x29297b52U, 0xe3e33eddU, 0x2f2f715eU, 0x84849713U, + 0x5353f5a6U, 0xd1d168b9U, 0x00000000U, 0xeded2cc1U, + 0x20206040U, 0xfcfc1fe3U, 0xb1b1c879U, 0x5b5bedb6U, + 0x6a6abed4U, 0xcbcb468dU, 0xbebed967U, 0x39394b72U, + 0x4a4ade94U, 0x4c4cd498U, 0x5858e8b0U, 0xcfcf4a85U, + 0xd0d06bbbU, 0xefef2ac5U, 0xaaaae54fU, 0xfbfb16edU, + 0x4343c586U, 0x4d4dd79aU, 0x33335566U, 0x85859411U, + 0x4545cf8aU, 0xf9f910e9U, 0x02020604U, 0x7f7f81feU, + 0x5050f0a0U, 0x3c3c4478U, 0x9f9fba25U, 0xa8a8e34bU, + 0x5151f3a2U, 0xa3a3fe5dU, 0x4040c080U, 0x8f8f8a05U, + 0x9292ad3fU, 0x9d9dbc21U, 0x38384870U, 0xf5f504f1U, + 0xbcbcdf63U, 0xb6b6c177U, 0xdada75afU, 0x21216342U, + 0x10103020U, 0xffff1ae5U, 0xf3f30efdU, 0xd2d26dbfU, + 0xcdcd4c81U, 0x0c0c1418U, 0x13133526U, 0xecec2fc3U, + 0x5f5fe1beU, 0x9797a235U, 0x4444cc88U, 0x1717392eU, + 0xc4c45793U, 0xa7a7f255U, 0x7e7e82fcU, 0x3d3d477aU, + 0x6464acc8U, 0x5d5de7baU, 0x19192b32U, 0x737395e6U, + 0x6060a0c0U, 0x81819819U, 0x4f4fd19eU, 0xdcdc7fa3U, + 0x22226644U, 0x2a2a7e54U, 0x9090ab3bU, 0x8888830bU, + 0x4646ca8cU, 0xeeee29c7U, 0xb8b8d36bU, 0x14143c28U, + 0xdede79a7U, 0x5e5ee2bcU, 0x0b0b1d16U, 0xdbdb76adU, + 0xe0e03bdbU, 0x32325664U, 0x3a3a4e74U, 0x0a0a1e14U, + 0x4949db92U, 0x06060a0cU, 0x24246c48U, 0x5c5ce4b8U, + 0xc2c25d9fU, 0xd3d36ebdU, 0xacacef43U, 0x6262a6c4U, + 0x9191a839U, 0x9595a431U, 0xe4e437d3U, 0x79798bf2U, + 0xe7e732d5U, 0xc8c8438bU, 0x3737596eU, 0x6d6db7daU, + 0x8d8d8c01U, 0xd5d564b1U, 0x4e4ed29cU, 0xa9a9e049U, + 0x6c6cb4d8U, 0x5656faacU, 0xf4f407f3U, 0xeaea25cfU, + 0x6565afcaU, 0x7a7a8ef4U, 0xaeaee947U, 0x08081810U, + 0xbabad56fU, 0x787888f0U, 0x25256f4aU, 0x2e2e725cU, + 0x1c1c2438U, 0xa6a6f157U, 0xb4b4c773U, 0xc6c65197U, + 0xe8e823cbU, 0xdddd7ca1U, 0x74749ce8U, 0x1f1f213eU, + 0x4b4bdd96U, 0xbdbddc61U, 0x8b8b860dU, 0x8a8a850fU, + 0x707090e0U, 0x3e3e427cU, 0xb5b5c471U, 0x6666aaccU, + 0x4848d890U, 0x03030506U, 0xf6f601f7U, 0x0e0e121cU, + 0x6161a3c2U, 0x35355f6aU, 0x5757f9aeU, 0xb9b9d069U, + 0x86869117U, 0xc1c15899U, 0x1d1d273aU, 0x9e9eb927U, + 0xe1e138d9U, 0xf8f813ebU, 0x9898b32bU, 0x11113322U, + 0x6969bbd2U, 0xd9d970a9U, 0x8e8e8907U, 0x9494a733U, + 0x9b9bb62dU, 0x1e1e223cU, 0x87879215U, 0xe9e920c9U, + 0xcece4987U, 0x5555ffaaU, 0x28287850U, 0xdfdf7aa5U, + 0x8c8c8f03U, 0xa1a1f859U, 0x89898009U, 0x0d0d171aU, + 0xbfbfda65U, 0xe6e631d7U, 0x4242c684U, 0x6868b8d0U, + 0x4141c382U, 0x9999b029U, 0x2d2d775aU, 0x0f0f111eU, + 0xb0b0cb7bU, 0x5454fca8U, 0xbbbbd66dU, 0x16163a2cU, +}; + +static const uint32 Te4[256]= +{ + 0x63636363U, 0x7c7c7c7cU, 0x77777777U, 0x7b7b7b7bU, + 0xf2f2f2f2U, 0x6b6b6b6bU, 0x6f6f6f6fU, 0xc5c5c5c5U, + 0x30303030U, 0x01010101U, 0x67676767U, 0x2b2b2b2bU, + 0xfefefefeU, 0xd7d7d7d7U, 0xababababU, 0x76767676U, + 0xcacacacaU, 0x82828282U, 0xc9c9c9c9U, 0x7d7d7d7dU, + 0xfafafafaU, 0x59595959U, 0x47474747U, 0xf0f0f0f0U, + 0xadadadadU, 0xd4d4d4d4U, 0xa2a2a2a2U, 0xafafafafU, + 0x9c9c9c9cU, 0xa4a4a4a4U, 0x72727272U, 0xc0c0c0c0U, + 0xb7b7b7b7U, 0xfdfdfdfdU, 0x93939393U, 0x26262626U, + 0x36363636U, 0x3f3f3f3fU, 0xf7f7f7f7U, 0xccccccccU, + 0x34343434U, 0xa5a5a5a5U, 0xe5e5e5e5U, 0xf1f1f1f1U, + 0x71717171U, 0xd8d8d8d8U, 0x31313131U, 0x15151515U, + 0x04040404U, 0xc7c7c7c7U, 0x23232323U, 0xc3c3c3c3U, + 0x18181818U, 0x96969696U, 0x05050505U, 0x9a9a9a9aU, + 0x07070707U, 0x12121212U, 0x80808080U, 0xe2e2e2e2U, + 0xebebebebU, 0x27272727U, 0xb2b2b2b2U, 0x75757575U, + 0x09090909U, 0x83838383U, 0x2c2c2c2cU, 0x1a1a1a1aU, + 0x1b1b1b1bU, 0x6e6e6e6eU, 0x5a5a5a5aU, 0xa0a0a0a0U, + 0x52525252U, 0x3b3b3b3bU, 0xd6d6d6d6U, 0xb3b3b3b3U, + 0x29292929U, 0xe3e3e3e3U, 0x2f2f2f2fU, 0x84848484U, + 0x53535353U, 0xd1d1d1d1U, 0x00000000U, 0xededededU, + 0x20202020U, 0xfcfcfcfcU, 0xb1b1b1b1U, 0x5b5b5b5bU, + 0x6a6a6a6aU, 0xcbcbcbcbU, 0xbebebebeU, 0x39393939U, + 0x4a4a4a4aU, 0x4c4c4c4cU, 0x58585858U, 0xcfcfcfcfU, + 0xd0d0d0d0U, 0xefefefefU, 0xaaaaaaaaU, 0xfbfbfbfbU, + 0x43434343U, 0x4d4d4d4dU, 0x33333333U, 0x85858585U, + 0x45454545U, 0xf9f9f9f9U, 0x02020202U, 0x7f7f7f7fU, + 0x50505050U, 0x3c3c3c3cU, 0x9f9f9f9fU, 0xa8a8a8a8U, + 0x51515151U, 0xa3a3a3a3U, 0x40404040U, 0x8f8f8f8fU, + 0x92929292U, 0x9d9d9d9dU, 0x38383838U, 0xf5f5f5f5U, + 0xbcbcbcbcU, 0xb6b6b6b6U, 0xdadadadaU, 0x21212121U, + 0x10101010U, 0xffffffffU, 0xf3f3f3f3U, 0xd2d2d2d2U, + 0xcdcdcdcdU, 0x0c0c0c0cU, 0x13131313U, 0xececececU, + 0x5f5f5f5fU, 0x97979797U, 0x44444444U, 0x17171717U, + 0xc4c4c4c4U, 0xa7a7a7a7U, 0x7e7e7e7eU, 0x3d3d3d3dU, + 0x64646464U, 0x5d5d5d5dU, 0x19191919U, 0x73737373U, + 0x60606060U, 0x81818181U, 0x4f4f4f4fU, 0xdcdcdcdcU, + 0x22222222U, 0x2a2a2a2aU, 0x90909090U, 0x88888888U, + 0x46464646U, 0xeeeeeeeeU, 0xb8b8b8b8U, 0x14141414U, + 0xdedededeU, 0x5e5e5e5eU, 0x0b0b0b0bU, 0xdbdbdbdbU, + 0xe0e0e0e0U, 0x32323232U, 0x3a3a3a3aU, 0x0a0a0a0aU, + 0x49494949U, 0x06060606U, 0x24242424U, 0x5c5c5c5cU, + 0xc2c2c2c2U, 0xd3d3d3d3U, 0xacacacacU, 0x62626262U, + 0x91919191U, 0x95959595U, 0xe4e4e4e4U, 0x79797979U, + 0xe7e7e7e7U, 0xc8c8c8c8U, 0x37373737U, 0x6d6d6d6dU, + 0x8d8d8d8dU, 0xd5d5d5d5U, 0x4e4e4e4eU, 0xa9a9a9a9U, + 0x6c6c6c6cU, 0x56565656U, 0xf4f4f4f4U, 0xeaeaeaeaU, + 0x65656565U, 0x7a7a7a7aU, 0xaeaeaeaeU, 0x08080808U, + 0xbabababaU, 0x78787878U, 0x25252525U, 0x2e2e2e2eU, + 0x1c1c1c1cU, 0xa6a6a6a6U, 0xb4b4b4b4U, 0xc6c6c6c6U, + 0xe8e8e8e8U, 0xddddddddU, 0x74747474U, 0x1f1f1f1fU, + 0x4b4b4b4bU, 0xbdbdbdbdU, 0x8b8b8b8bU, 0x8a8a8a8aU, + 0x70707070U, 0x3e3e3e3eU, 0xb5b5b5b5U, 0x66666666U, + 0x48484848U, 0x03030303U, 0xf6f6f6f6U, 0x0e0e0e0eU, + 0x61616161U, 0x35353535U, 0x57575757U, 0xb9b9b9b9U, + 0x86868686U, 0xc1c1c1c1U, 0x1d1d1d1dU, 0x9e9e9e9eU, + 0xe1e1e1e1U, 0xf8f8f8f8U, 0x98989898U, 0x11111111U, + 0x69696969U, 0xd9d9d9d9U, 0x8e8e8e8eU, 0x94949494U, + 0x9b9b9b9bU, 0x1e1e1e1eU, 0x87878787U, 0xe9e9e9e9U, + 0xcecececeU, 0x55555555U, 0x28282828U, 0xdfdfdfdfU, + 0x8c8c8c8cU, 0xa1a1a1a1U, 0x89898989U, 0x0d0d0d0dU, + 0xbfbfbfbfU, 0xe6e6e6e6U, 0x42424242U, 0x68686868U, + 0x41414141U, 0x99999999U, 0x2d2d2d2dU, 0x0f0f0f0fU, + 0xb0b0b0b0U, 0x54545454U, 0xbbbbbbbbU, 0x16161616U, +}; + +static const uint32 Td0[256]= +{ + 0x51f4a750U, 0x7e416553U, 0x1a17a4c3U, 0x3a275e96U, + 0x3bab6bcbU, 0x1f9d45f1U, 0xacfa58abU, 0x4be30393U, + 0x2030fa55U, 0xad766df6U, 0x88cc7691U, 0xf5024c25U, + 0x4fe5d7fcU, 0xc52acbd7U, 0x26354480U, 0xb562a38fU, + 0xdeb15a49U, 0x25ba1b67U, 0x45ea0e98U, 0x5dfec0e1U, + 0xc32f7502U, 0x814cf012U, 0x8d4697a3U, 0x6bd3f9c6U, + 0x038f5fe7U, 0x15929c95U, 0xbf6d7aebU, 0x955259daU, + 0xd4be832dU, 0x587421d3U, 0x49e06929U, 0x8ec9c844U, + 0x75c2896aU, 0xf48e7978U, 0x99583e6bU, 0x27b971ddU, + 0xbee14fb6U, 0xf088ad17U, 0xc920ac66U, 0x7dce3ab4U, + 0x63df4a18U, 0xe51a3182U, 0x97513360U, 0x62537f45U, + 0xb16477e0U, 0xbb6bae84U, 0xfe81a01cU, 0xf9082b94U, + 0x70486858U, 0x8f45fd19U, 0x94de6c87U, 0x527bf8b7U, + 0xab73d323U, 0x724b02e2U, 0xe31f8f57U, 0x6655ab2aU, + 0xb2eb2807U, 0x2fb5c203U, 0x86c57b9aU, 0xd33708a5U, + 0x302887f2U, 0x23bfa5b2U, 0x02036abaU, 0xed16825cU, + 0x8acf1c2bU, 0xa779b492U, 0xf307f2f0U, 0x4e69e2a1U, + 0x65daf4cdU, 0x0605bed5U, 0xd134621fU, 0xc4a6fe8aU, + 0x342e539dU, 0xa2f355a0U, 0x058ae132U, 0xa4f6eb75U, + 0x0b83ec39U, 0x4060efaaU, 0x5e719f06U, 0xbd6e1051U, + 0x3e218af9U, 0x96dd063dU, 0xdd3e05aeU, 0x4de6bd46U, + 0x91548db5U, 0x71c45d05U, 0x0406d46fU, 0x605015ffU, + 0x1998fb24U, 0xd6bde997U, 0x894043ccU, 0x67d99e77U, + 0xb0e842bdU, 0x07898b88U, 0xe7195b38U, 0x79c8eedbU, + 0xa17c0a47U, 0x7c420fe9U, 0xf8841ec9U, 0x00000000U, + 0x09808683U, 0x322bed48U, 0x1e1170acU, 0x6c5a724eU, + 0xfd0efffbU, 0x0f853856U, 0x3daed51eU, 0x362d3927U, + 0x0a0fd964U, 0x685ca621U, 0x9b5b54d1U, 0x24362e3aU, + 0x0c0a67b1U, 0x9357e70fU, 0xb4ee96d2U, 0x1b9b919eU, + 0x80c0c54fU, 0x61dc20a2U, 0x5a774b69U, 0x1c121a16U, + 0xe293ba0aU, 0xc0a02ae5U, 0x3c22e043U, 0x121b171dU, + 0x0e090d0bU, 0xf28bc7adU, 0x2db6a8b9U, 0x141ea9c8U, + 0x57f11985U, 0xaf75074cU, 0xee99ddbbU, 0xa37f60fdU, + 0xf701269fU, 0x5c72f5bcU, 0x44663bc5U, 0x5bfb7e34U, + 0x8b432976U, 0xcb23c6dcU, 0xb6edfc68U, 0xb8e4f163U, + 0xd731dccaU, 0x42638510U, 0x13972240U, 0x84c61120U, + 0x854a247dU, 0xd2bb3df8U, 0xaef93211U, 0xc729a16dU, + 0x1d9e2f4bU, 0xdcb230f3U, 0x0d8652ecU, 0x77c1e3d0U, + 0x2bb3166cU, 0xa970b999U, 0x119448faU, 0x47e96422U, + 0xa8fc8cc4U, 0xa0f03f1aU, 0x567d2cd8U, 0x223390efU, + 0x87494ec7U, 0xd938d1c1U, 0x8ccaa2feU, 0x98d40b36U, + 0xa6f581cfU, 0xa57ade28U, 0xdab78e26U, 0x3fadbfa4U, + 0x2c3a9de4U, 0x5078920dU, 0x6a5fcc9bU, 0x547e4662U, + 0xf68d13c2U, 0x90d8b8e8U, 0x2e39f75eU, 0x82c3aff5U, + 0x9f5d80beU, 0x69d0937cU, 0x6fd52da9U, 0xcf2512b3U, + 0xc8ac993bU, 0x10187da7U, 0xe89c636eU, 0xdb3bbb7bU, + 0xcd267809U, 0x6e5918f4U, 0xec9ab701U, 0x834f9aa8U, + 0xe6956e65U, 0xaaffe67eU, 0x21bccf08U, 0xef15e8e6U, + 0xbae79bd9U, 0x4a6f36ceU, 0xea9f09d4U, 0x29b07cd6U, + 0x31a4b2afU, 0x2a3f2331U, 0xc6a59430U, 0x35a266c0U, + 0x744ebc37U, 0xfc82caa6U, 0xe090d0b0U, 0x33a7d815U, + 0xf104984aU, 0x41ecdaf7U, 0x7fcd500eU, 0x1791f62fU, + 0x764dd68dU, 0x43efb04dU, 0xccaa4d54U, 0xe49604dfU, + 0x9ed1b5e3U, 0x4c6a881bU, 0xc12c1fb8U, 0x4665517fU, + 0x9d5eea04U, 0x018c355dU, 0xfa877473U, 0xfb0b412eU, + 0xb3671d5aU, 0x92dbd252U, 0xe9105633U, 0x6dd64713U, + 0x9ad7618cU, 0x37a10c7aU, 0x59f8148eU, 0xeb133c89U, + 0xcea927eeU, 0xb761c935U, 0xe11ce5edU, 0x7a47b13cU, + 0x9cd2df59U, 0x55f2733fU, 0x1814ce79U, 0x73c737bfU, + 0x53f7cdeaU, 0x5ffdaa5bU, 0xdf3d6f14U, 0x7844db86U, + 0xcaaff381U, 0xb968c43eU, 0x3824342cU, 0xc2a3405fU, + 0x161dc372U, 0xbce2250cU, 0x283c498bU, 0xff0d9541U, + 0x39a80171U, 0x080cb3deU, 0xd8b4e49cU, 0x6456c190U, + 0x7bcb8461U, 0xd532b670U, 0x486c5c74U, 0xd0b85742U, +}; + +static const uint32 Td1[256]= +{ + 0x5051f4a7U, 0x537e4165U, 0xc31a17a4U, 0x963a275eU, + 0xcb3bab6bU, 0xf11f9d45U, 0xabacfa58U, 0x934be303U, + 0x552030faU, 0xf6ad766dU, 0x9188cc76U, 0x25f5024cU, + 0xfc4fe5d7U, 0xd7c52acbU, 0x80263544U, 0x8fb562a3U, + 0x49deb15aU, 0x6725ba1bU, 0x9845ea0eU, 0xe15dfec0U, + 0x02c32f75U, 0x12814cf0U, 0xa38d4697U, 0xc66bd3f9U, + 0xe7038f5fU, 0x9515929cU, 0xebbf6d7aU, 0xda955259U, + 0x2dd4be83U, 0xd3587421U, 0x2949e069U, 0x448ec9c8U, + 0x6a75c289U, 0x78f48e79U, 0x6b99583eU, 0xdd27b971U, + 0xb6bee14fU, 0x17f088adU, 0x66c920acU, 0xb47dce3aU, + 0x1863df4aU, 0x82e51a31U, 0x60975133U, 0x4562537fU, + 0xe0b16477U, 0x84bb6baeU, 0x1cfe81a0U, 0x94f9082bU, + 0x58704868U, 0x198f45fdU, 0x8794de6cU, 0xb7527bf8U, + 0x23ab73d3U, 0xe2724b02U, 0x57e31f8fU, 0x2a6655abU, + 0x07b2eb28U, 0x032fb5c2U, 0x9a86c57bU, 0xa5d33708U, + 0xf2302887U, 0xb223bfa5U, 0xba02036aU, 0x5ced1682U, + 0x2b8acf1cU, 0x92a779b4U, 0xf0f307f2U, 0xa14e69e2U, + 0xcd65daf4U, 0xd50605beU, 0x1fd13462U, 0x8ac4a6feU, + 0x9d342e53U, 0xa0a2f355U, 0x32058ae1U, 0x75a4f6ebU, + 0x390b83ecU, 0xaa4060efU, 0x065e719fU, 0x51bd6e10U, + 0xf93e218aU, 0x3d96dd06U, 0xaedd3e05U, 0x464de6bdU, + 0xb591548dU, 0x0571c45dU, 0x6f0406d4U, 0xff605015U, + 0x241998fbU, 0x97d6bde9U, 0xcc894043U, 0x7767d99eU, + 0xbdb0e842U, 0x8807898bU, 0x38e7195bU, 0xdb79c8eeU, + 0x47a17c0aU, 0xe97c420fU, 0xc9f8841eU, 0x00000000U, + 0x83098086U, 0x48322bedU, 0xac1e1170U, 0x4e6c5a72U, + 0xfbfd0effU, 0x560f8538U, 0x1e3daed5U, 0x27362d39U, + 0x640a0fd9U, 0x21685ca6U, 0xd19b5b54U, 0x3a24362eU, + 0xb10c0a67U, 0x0f9357e7U, 0xd2b4ee96U, 0x9e1b9b91U, + 0x4f80c0c5U, 0xa261dc20U, 0x695a774bU, 0x161c121aU, + 0x0ae293baU, 0xe5c0a02aU, 0x433c22e0U, 0x1d121b17U, + 0x0b0e090dU, 0xadf28bc7U, 0xb92db6a8U, 0xc8141ea9U, + 0x8557f119U, 0x4caf7507U, 0xbbee99ddU, 0xfda37f60U, + 0x9ff70126U, 0xbc5c72f5U, 0xc544663bU, 0x345bfb7eU, + 0x768b4329U, 0xdccb23c6U, 0x68b6edfcU, 0x63b8e4f1U, + 0xcad731dcU, 0x10426385U, 0x40139722U, 0x2084c611U, + 0x7d854a24U, 0xf8d2bb3dU, 0x11aef932U, 0x6dc729a1U, + 0x4b1d9e2fU, 0xf3dcb230U, 0xec0d8652U, 0xd077c1e3U, + 0x6c2bb316U, 0x99a970b9U, 0xfa119448U, 0x2247e964U, + 0xc4a8fc8cU, 0x1aa0f03fU, 0xd8567d2cU, 0xef223390U, + 0xc787494eU, 0xc1d938d1U, 0xfe8ccaa2U, 0x3698d40bU, + 0xcfa6f581U, 0x28a57adeU, 0x26dab78eU, 0xa43fadbfU, + 0xe42c3a9dU, 0x0d507892U, 0x9b6a5fccU, 0x62547e46U, + 0xc2f68d13U, 0xe890d8b8U, 0x5e2e39f7U, 0xf582c3afU, + 0xbe9f5d80U, 0x7c69d093U, 0xa96fd52dU, 0xb3cf2512U, + 0x3bc8ac99U, 0xa710187dU, 0x6ee89c63U, 0x7bdb3bbbU, + 0x09cd2678U, 0xf46e5918U, 0x01ec9ab7U, 0xa8834f9aU, + 0x65e6956eU, 0x7eaaffe6U, 0x0821bccfU, 0xe6ef15e8U, + 0xd9bae79bU, 0xce4a6f36U, 0xd4ea9f09U, 0xd629b07cU, + 0xaf31a4b2U, 0x312a3f23U, 0x30c6a594U, 0xc035a266U, + 0x37744ebcU, 0xa6fc82caU, 0xb0e090d0U, 0x1533a7d8U, + 0x4af10498U, 0xf741ecdaU, 0x0e7fcd50U, 0x2f1791f6U, + 0x8d764dd6U, 0x4d43efb0U, 0x54ccaa4dU, 0xdfe49604U, + 0xe39ed1b5U, 0x1b4c6a88U, 0xb8c12c1fU, 0x7f466551U, + 0x049d5eeaU, 0x5d018c35U, 0x73fa8774U, 0x2efb0b41U, + 0x5ab3671dU, 0x5292dbd2U, 0x33e91056U, 0x136dd647U, + 0x8c9ad761U, 0x7a37a10cU, 0x8e59f814U, 0x89eb133cU, + 0xeecea927U, 0x35b761c9U, 0xede11ce5U, 0x3c7a47b1U, + 0x599cd2dfU, 0x3f55f273U, 0x791814ceU, 0xbf73c737U, + 0xea53f7cdU, 0x5b5ffdaaU, 0x14df3d6fU, 0x867844dbU, + 0x81caaff3U, 0x3eb968c4U, 0x2c382434U, 0x5fc2a340U, + 0x72161dc3U, 0x0cbce225U, 0x8b283c49U, 0x41ff0d95U, + 0x7139a801U, 0xde080cb3U, 0x9cd8b4e4U, 0x906456c1U, + 0x617bcb84U, 0x70d532b6U, 0x74486c5cU, 0x42d0b857U, +}; + +static const uint32 Td2[256]= +{ + 0xa75051f4U, 0x65537e41U, 0xa4c31a17U, 0x5e963a27U, + 0x6bcb3babU, 0x45f11f9dU, 0x58abacfaU, 0x03934be3U, + 0xfa552030U, 0x6df6ad76U, 0x769188ccU, 0x4c25f502U, + 0xd7fc4fe5U, 0xcbd7c52aU, 0x44802635U, 0xa38fb562U, + 0x5a49deb1U, 0x1b6725baU, 0x0e9845eaU, 0xc0e15dfeU, + 0x7502c32fU, 0xf012814cU, 0x97a38d46U, 0xf9c66bd3U, + 0x5fe7038fU, 0x9c951592U, 0x7aebbf6dU, 0x59da9552U, + 0x832dd4beU, 0x21d35874U, 0x692949e0U, 0xc8448ec9U, + 0x896a75c2U, 0x7978f48eU, 0x3e6b9958U, 0x71dd27b9U, + 0x4fb6bee1U, 0xad17f088U, 0xac66c920U, 0x3ab47dceU, + 0x4a1863dfU, 0x3182e51aU, 0x33609751U, 0x7f456253U, + 0x77e0b164U, 0xae84bb6bU, 0xa01cfe81U, 0x2b94f908U, + 0x68587048U, 0xfd198f45U, 0x6c8794deU, 0xf8b7527bU, + 0xd323ab73U, 0x02e2724bU, 0x8f57e31fU, 0xab2a6655U, + 0x2807b2ebU, 0xc2032fb5U, 0x7b9a86c5U, 0x08a5d337U, + 0x87f23028U, 0xa5b223bfU, 0x6aba0203U, 0x825ced16U, + 0x1c2b8acfU, 0xb492a779U, 0xf2f0f307U, 0xe2a14e69U, + 0xf4cd65daU, 0xbed50605U, 0x621fd134U, 0xfe8ac4a6U, + 0x539d342eU, 0x55a0a2f3U, 0xe132058aU, 0xeb75a4f6U, + 0xec390b83U, 0xefaa4060U, 0x9f065e71U, 0x1051bd6eU, + + 0x8af93e21U, 0x063d96ddU, 0x05aedd3eU, 0xbd464de6U, + 0x8db59154U, 0x5d0571c4U, 0xd46f0406U, 0x15ff6050U, + 0xfb241998U, 0xe997d6bdU, 0x43cc8940U, 0x9e7767d9U, + 0x42bdb0e8U, 0x8b880789U, 0x5b38e719U, 0xeedb79c8U, + 0x0a47a17cU, 0x0fe97c42U, 0x1ec9f884U, 0x00000000U, + 0x86830980U, 0xed48322bU, 0x70ac1e11U, 0x724e6c5aU, + 0xfffbfd0eU, 0x38560f85U, 0xd51e3daeU, 0x3927362dU, + 0xd9640a0fU, 0xa621685cU, 0x54d19b5bU, 0x2e3a2436U, + 0x67b10c0aU, 0xe70f9357U, 0x96d2b4eeU, 0x919e1b9bU, + 0xc54f80c0U, 0x20a261dcU, 0x4b695a77U, 0x1a161c12U, + 0xba0ae293U, 0x2ae5c0a0U, 0xe0433c22U, 0x171d121bU, + 0x0d0b0e09U, 0xc7adf28bU, 0xa8b92db6U, 0xa9c8141eU, + 0x198557f1U, 0x074caf75U, 0xddbbee99U, 0x60fda37fU, + 0x269ff701U, 0xf5bc5c72U, 0x3bc54466U, 0x7e345bfbU, + 0x29768b43U, 0xc6dccb23U, 0xfc68b6edU, 0xf163b8e4U, + 0xdccad731U, 0x85104263U, 0x22401397U, 0x112084c6U, + 0x247d854aU, 0x3df8d2bbU, 0x3211aef9U, 0xa16dc729U, + 0x2f4b1d9eU, 0x30f3dcb2U, 0x52ec0d86U, 0xe3d077c1U, + 0x166c2bb3U, 0xb999a970U, 0x48fa1194U, 0x642247e9U, + 0x8cc4a8fcU, 0x3f1aa0f0U, 0x2cd8567dU, 0x90ef2233U, + 0x4ec78749U, 0xd1c1d938U, 0xa2fe8ccaU, 0x0b3698d4U, + 0x81cfa6f5U, 0xde28a57aU, 0x8e26dab7U, 0xbfa43fadU, + 0x9de42c3aU, 0x920d5078U, 0xcc9b6a5fU, 0x4662547eU, + 0x13c2f68dU, 0xb8e890d8U, 0xf75e2e39U, 0xaff582c3U, + 0x80be9f5dU, 0x937c69d0U, 0x2da96fd5U, 0x12b3cf25U, + 0x993bc8acU, 0x7da71018U, 0x636ee89cU, 0xbb7bdb3bU, + 0x7809cd26U, 0x18f46e59U, 0xb701ec9aU, 0x9aa8834fU, + 0x6e65e695U, 0xe67eaaffU, 0xcf0821bcU, 0xe8e6ef15U, + 0x9bd9bae7U, 0x36ce4a6fU, 0x09d4ea9fU, 0x7cd629b0U, + 0xb2af31a4U, 0x23312a3fU, 0x9430c6a5U, 0x66c035a2U, + 0xbc37744eU, 0xcaa6fc82U, 0xd0b0e090U, 0xd81533a7U, + 0x984af104U, 0xdaf741ecU, 0x500e7fcdU, 0xf62f1791U, + 0xd68d764dU, 0xb04d43efU, 0x4d54ccaaU, 0x04dfe496U, + 0xb5e39ed1U, 0x881b4c6aU, 0x1fb8c12cU, 0x517f4665U, + 0xea049d5eU, 0x355d018cU, 0x7473fa87U, 0x412efb0bU, + 0x1d5ab367U, 0xd25292dbU, 0x5633e910U, 0x47136dd6U, + 0x618c9ad7U, 0x0c7a37a1U, 0x148e59f8U, 0x3c89eb13U, + 0x27eecea9U, 0xc935b761U, 0xe5ede11cU, 0xb13c7a47U, + 0xdf599cd2U, 0x733f55f2U, 0xce791814U, 0x37bf73c7U, + 0xcdea53f7U, 0xaa5b5ffdU, 0x6f14df3dU, 0xdb867844U, + 0xf381caafU, 0xc43eb968U, 0x342c3824U, 0x405fc2a3U, + 0xc372161dU, 0x250cbce2U, 0x498b283cU, 0x9541ff0dU, + 0x017139a8U, 0xb3de080cU, 0xe49cd8b4U, 0xc1906456U, + 0x84617bcbU, 0xb670d532U, 0x5c74486cU, 0x5742d0b8U, +}; + +static const uint32 Td3[256]= +{ + 0xf4a75051U, 0x4165537eU, 0x17a4c31aU, 0x275e963aU, + 0xab6bcb3bU, 0x9d45f11fU, 0xfa58abacU, 0xe303934bU, + 0x30fa5520U, 0x766df6adU, 0xcc769188U, 0x024c25f5U, + 0xe5d7fc4fU, 0x2acbd7c5U, 0x35448026U, 0x62a38fb5U, + 0xb15a49deU, 0xba1b6725U, 0xea0e9845U, 0xfec0e15dU, + 0x2f7502c3U, 0x4cf01281U, 0x4697a38dU, 0xd3f9c66bU, + 0x8f5fe703U, 0x929c9515U, 0x6d7aebbfU, 0x5259da95U, + 0xbe832dd4U, 0x7421d358U, 0xe0692949U, 0xc9c8448eU, + 0xc2896a75U, 0x8e7978f4U, 0x583e6b99U, 0xb971dd27U, + 0xe14fb6beU, 0x88ad17f0U, 0x20ac66c9U, 0xce3ab47dU, + 0xdf4a1863U, 0x1a3182e5U, 0x51336097U, 0x537f4562U, + 0x6477e0b1U, 0x6bae84bbU, 0x81a01cfeU, 0x082b94f9U, + 0x48685870U, 0x45fd198fU, 0xde6c8794U, 0x7bf8b752U, + 0x73d323abU, 0x4b02e272U, 0x1f8f57e3U, 0x55ab2a66U, + 0xeb2807b2U, 0xb5c2032fU, 0xc57b9a86U, 0x3708a5d3U, + 0x2887f230U, 0xbfa5b223U, 0x036aba02U, 0x16825cedU, + 0xcf1c2b8aU, 0x79b492a7U, 0x07f2f0f3U, 0x69e2a14eU, + 0xdaf4cd65U, 0x05bed506U, 0x34621fd1U, 0xa6fe8ac4U, + 0x2e539d34U, 0xf355a0a2U, 0x8ae13205U, 0xf6eb75a4U, + 0x83ec390bU, 0x60efaa40U, 0x719f065eU, 0x6e1051bdU, + 0x218af93eU, 0xdd063d96U, 0x3e05aeddU, 0xe6bd464dU, + 0x548db591U, 0xc45d0571U, 0x06d46f04U, 0x5015ff60U, + 0x98fb2419U, 0xbde997d6U, 0x4043cc89U, 0xd99e7767U, + 0xe842bdb0U, 0x898b8807U, 0x195b38e7U, 0xc8eedb79U, + 0x7c0a47a1U, 0x420fe97cU, 0x841ec9f8U, 0x00000000U, + 0x80868309U, 0x2bed4832U, 0x1170ac1eU, 0x5a724e6cU, + 0x0efffbfdU, 0x8538560fU, 0xaed51e3dU, 0x2d392736U, + 0x0fd9640aU, 0x5ca62168U, 0x5b54d19bU, 0x362e3a24U, + 0x0a67b10cU, 0x57e70f93U, 0xee96d2b4U, 0x9b919e1bU, + 0xc0c54f80U, 0xdc20a261U, 0x774b695aU, 0x121a161cU, + 0x93ba0ae2U, 0xa02ae5c0U, 0x22e0433cU, 0x1b171d12U, + 0x090d0b0eU, 0x8bc7adf2U, 0xb6a8b92dU, 0x1ea9c814U, + 0xf1198557U, 0x75074cafU, 0x99ddbbeeU, 0x7f60fda3U, + 0x01269ff7U, 0x72f5bc5cU, 0x663bc544U, 0xfb7e345bU, + 0x4329768bU, 0x23c6dccbU, 0xedfc68b6U, 0xe4f163b8U, + 0x31dccad7U, 0x63851042U, 0x97224013U, 0xc6112084U, + 0x4a247d85U, 0xbb3df8d2U, 0xf93211aeU, 0x29a16dc7U, + 0x9e2f4b1dU, 0xb230f3dcU, 0x8652ec0dU, 0xc1e3d077U, + 0xb3166c2bU, 0x70b999a9U, 0x9448fa11U, 0xe9642247U, + 0xfc8cc4a8U, 0xf03f1aa0U, 0x7d2cd856U, 0x3390ef22U, + 0x494ec787U, 0x38d1c1d9U, 0xcaa2fe8cU, 0xd40b3698U, + 0xf581cfa6U, 0x7ade28a5U, 0xb78e26daU, 0xadbfa43fU, + 0x3a9de42cU, 0x78920d50U, 0x5fcc9b6aU, 0x7e466254U, + 0x8d13c2f6U, 0xd8b8e890U, 0x39f75e2eU, 0xc3aff582U, + 0x5d80be9fU, 0xd0937c69U, 0xd52da96fU, 0x2512b3cfU, + 0xac993bc8U, 0x187da710U, 0x9c636ee8U, 0x3bbb7bdbU, + 0x267809cdU, 0x5918f46eU, 0x9ab701ecU, 0x4f9aa883U, + 0x956e65e6U, 0xffe67eaaU, 0xbccf0821U, 0x15e8e6efU, + 0xe79bd9baU, 0x6f36ce4aU, 0x9f09d4eaU, 0xb07cd629U, + 0xa4b2af31U, 0x3f23312aU, 0xa59430c6U, 0xa266c035U, + 0x4ebc3774U, 0x82caa6fcU, 0x90d0b0e0U, 0xa7d81533U, + 0x04984af1U, 0xecdaf741U, 0xcd500e7fU, 0x91f62f17U, + 0x4dd68d76U, 0xefb04d43U, 0xaa4d54ccU, 0x9604dfe4U, + 0xd1b5e39eU, 0x6a881b4cU, 0x2c1fb8c1U, 0x65517f46U, + 0x5eea049dU, 0x8c355d01U, 0x877473faU, 0x0b412efbU, + 0x671d5ab3U, 0xdbd25292U, 0x105633e9U, 0xd647136dU, + 0xd7618c9aU, 0xa10c7a37U, 0xf8148e59U, 0x133c89ebU, + 0xa927eeceU, 0x61c935b7U, 0x1ce5ede1U, 0x47b13c7aU, + 0xd2df599cU, 0xf2733f55U, 0x14ce7918U, 0xc737bf73U, + 0xf7cdea53U, 0xfdaa5b5fU, 0x3d6f14dfU, 0x44db8678U, + 0xaff381caU, 0x68c43eb9U, 0x24342c38U, 0xa3405fc2U, + 0x1dc37216U, 0xe2250cbcU, 0x3c498b28U, 0x0d9541ffU, + 0xa8017139U, 0x0cb3de08U, 0xb4e49cd8U, 0x56c19064U, + 0xcb84617bU, 0x32b670d5U, 0x6c5c7448U, 0xb85742d0U, +}; + +static const uint32 Td4[256]= +{ + 0x52525252U, 0x09090909U, 0x6a6a6a6aU, 0xd5d5d5d5U, + 0x30303030U, 0x36363636U, 0xa5a5a5a5U, 0x38383838U, + 0xbfbfbfbfU, 0x40404040U, 0xa3a3a3a3U, 0x9e9e9e9eU, + 0x81818181U, 0xf3f3f3f3U, 0xd7d7d7d7U, 0xfbfbfbfbU, + 0x7c7c7c7cU, 0xe3e3e3e3U, 0x39393939U, 0x82828282U, + 0x9b9b9b9bU, 0x2f2f2f2fU, 0xffffffffU, 0x87878787U, + 0x34343434U, 0x8e8e8e8eU, 0x43434343U, 0x44444444U, + 0xc4c4c4c4U, 0xdedededeU, 0xe9e9e9e9U, 0xcbcbcbcbU, + 0x54545454U, 0x7b7b7b7bU, 0x94949494U, 0x32323232U, + 0xa6a6a6a6U, 0xc2c2c2c2U, 0x23232323U, 0x3d3d3d3dU, + 0xeeeeeeeeU, 0x4c4c4c4cU, 0x95959595U, 0x0b0b0b0bU, + 0x42424242U, 0xfafafafaU, 0xc3c3c3c3U, 0x4e4e4e4eU, + 0x08080808U, 0x2e2e2e2eU, 0xa1a1a1a1U, 0x66666666U, + 0x28282828U, 0xd9d9d9d9U, 0x24242424U, 0xb2b2b2b2U, + 0x76767676U, 0x5b5b5b5bU, 0xa2a2a2a2U, 0x49494949U, + 0x6d6d6d6dU, 0x8b8b8b8bU, 0xd1d1d1d1U, 0x25252525U, + 0x72727272U, 0xf8f8f8f8U, 0xf6f6f6f6U, 0x64646464U, + 0x86868686U, 0x68686868U, 0x98989898U, 0x16161616U, + 0xd4d4d4d4U, 0xa4a4a4a4U, 0x5c5c5c5cU, 0xccccccccU, + 0x5d5d5d5dU, 0x65656565U, 0xb6b6b6b6U, 0x92929292U, + 0x6c6c6c6cU, 0x70707070U, 0x48484848U, 0x50505050U, + 0xfdfdfdfdU, 0xededededU, 0xb9b9b9b9U, 0xdadadadaU, + 0x5e5e5e5eU, 0x15151515U, 0x46464646U, 0x57575757U, + 0xa7a7a7a7U, 0x8d8d8d8dU, 0x9d9d9d9dU, 0x84848484U, + 0x90909090U, 0xd8d8d8d8U, 0xababababU, 0x00000000U, + 0x8c8c8c8cU, 0xbcbcbcbcU, 0xd3d3d3d3U, 0x0a0a0a0aU, + 0xf7f7f7f7U, 0xe4e4e4e4U, 0x58585858U, 0x05050505U, + 0xb8b8b8b8U, 0xb3b3b3b3U, 0x45454545U, 0x06060606U, + 0xd0d0d0d0U, 0x2c2c2c2cU, 0x1e1e1e1eU, 0x8f8f8f8fU, + 0xcacacacaU, 0x3f3f3f3fU, 0x0f0f0f0fU, 0x02020202U, + 0xc1c1c1c1U, 0xafafafafU, 0xbdbdbdbdU, 0x03030303U, + 0x01010101U, 0x13131313U, 0x8a8a8a8aU, 0x6b6b6b6bU, + 0x3a3a3a3aU, 0x91919191U, 0x11111111U, 0x41414141U, + 0x4f4f4f4fU, 0x67676767U, 0xdcdcdcdcU, 0xeaeaeaeaU, + 0x97979797U, 0xf2f2f2f2U, 0xcfcfcfcfU, 0xcecececeU, + 0xf0f0f0f0U, 0xb4b4b4b4U, 0xe6e6e6e6U, 0x73737373U, + 0x96969696U, 0xacacacacU, 0x74747474U, 0x22222222U, + 0xe7e7e7e7U, 0xadadadadU, 0x35353535U, 0x85858585U, + 0xe2e2e2e2U, 0xf9f9f9f9U, 0x37373737U, 0xe8e8e8e8U, + 0x1c1c1c1cU, 0x75757575U, 0xdfdfdfdfU, 0x6e6e6e6eU, + 0x47474747U, 0xf1f1f1f1U, 0x1a1a1a1aU, 0x71717171U, + 0x1d1d1d1dU, 0x29292929U, 0xc5c5c5c5U, 0x89898989U, + 0x6f6f6f6fU, 0xb7b7b7b7U, 0x62626262U, 0x0e0e0e0eU, + 0xaaaaaaaaU, 0x18181818U, 0xbebebebeU, 0x1b1b1b1bU, + 0xfcfcfcfcU, 0x56565656U, 0x3e3e3e3eU, 0x4b4b4b4bU, + 0xc6c6c6c6U, 0xd2d2d2d2U, 0x79797979U, 0x20202020U, + 0x9a9a9a9aU, 0xdbdbdbdbU, 0xc0c0c0c0U, 0xfefefefeU, + 0x78787878U, 0xcdcdcdcdU, 0x5a5a5a5aU, 0xf4f4f4f4U, + 0x1f1f1f1fU, 0xddddddddU, 0xa8a8a8a8U, 0x33333333U, + 0x88888888U, 0x07070707U, 0xc7c7c7c7U, 0x31313131U, + 0xb1b1b1b1U, 0x12121212U, 0x10101010U, 0x59595959U, + 0x27272727U, 0x80808080U, 0xececececU, 0x5f5f5f5fU, + 0x60606060U, 0x51515151U, 0x7f7f7f7fU, 0xa9a9a9a9U, + 0x19191919U, 0xb5b5b5b5U, 0x4a4a4a4aU, 0x0d0d0d0dU, + 0x2d2d2d2dU, 0xe5e5e5e5U, 0x7a7a7a7aU, 0x9f9f9f9fU, + 0x93939393U, 0xc9c9c9c9U, 0x9c9c9c9cU, 0xefefefefU, + 0xa0a0a0a0U, 0xe0e0e0e0U, 0x3b3b3b3bU, 0x4d4d4d4dU, + 0xaeaeaeaeU, 0x2a2a2a2aU, 0xf5f5f5f5U, 0xb0b0b0b0U, + 0xc8c8c8c8U, 0xebebebebU, 0xbbbbbbbbU, 0x3c3c3c3cU, + 0x83838383U, 0x53535353U, 0x99999999U, 0x61616161U, + 0x17171717U, 0x2b2b2b2bU, 0x04040404U, 0x7e7e7e7eU, + 0xbabababaU, 0x77777777U, 0xd6d6d6d6U, 0x26262626U, + 0xe1e1e1e1U, 0x69696969U, 0x14141414U, 0x63636363U, + 0x55555555U, 0x21212121U, 0x0c0c0c0cU, 0x7d7d7d7dU, +}; + + +/* for 128-bit blocks, Rijndael never uses more than 10 rcon values */ +static const uint32 rcon[]= +{ + 0x01000000, 0x02000000, 0x04000000, 0x08000000, + 0x10000000, 0x20000000, 0x40000000, 0x80000000, + 0x1B000000, 0x36000000, +}; + +#if defined(_MSC_VER) && defined(__i386__) + +#define RJ_SWAP(x) (_lrotl(x, 8) & 0x00ff00ff | _lrotr(x, 8) & 0xff00ff00) +#define GETuint32(p) RJ_SWAP(*((uint32 *)(p))) +#define PUTuint32(ct, st) { *((uint32 *)(ct)) = RJ_SWAP((st)); } + +#else + +#define GETuint32(pt) (((uint32)(pt)[0] << 24) ^ ((uint32)(pt)[1] << 16)\ + ^ ((uint32)(pt)[2] << 8) ^ ((uint32)(pt)[3])) +#define PUTuint32(ct, st) { (ct)[0] = (uint8)((st) >> 24); (ct)[1]\ += (uint8)((st) >> 16); (ct)[2] = (uint8)((st) >> 8); (ct)[3] = (uint8)(st); } + +#endif /* defined(_MSC_VER) && defined(__i386__) */ + + +/* + Expand the cipher key into the encryption key schedule. + + RETURN + The number of rounds for the given cipher key size. +*/ + +int rijndaelKeySetupEnc(uint32 rk[/*4*(Nr + 1)*/], const uint8 cipherKey[], + int keyBits) +{ + int i = 0; + uint32 temp; + + rk[0] = GETuint32(cipherKey ); + rk[1] = GETuint32(cipherKey + 4); + rk[2] = GETuint32(cipherKey + 8); + rk[3] = GETuint32(cipherKey + 12); + if (keyBits == 128) + { + for (;;) + { + temp = rk[3]; + rk[4] = (rk[0] ^ + (Te4[(temp >> 16) & 0xff] & 0xff000000) ^ + (Te4[(temp >> 8) & 0xff] & 0x00ff0000) ^ + (Te4[(temp ) & 0xff] & 0x0000ff00) ^ + (Te4[(temp >> 24) ] & 0x000000ff) ^ + rcon[i]); + rk[5] = rk[1] ^ rk[4]; + rk[6] = rk[2] ^ rk[5]; + rk[7] = rk[3] ^ rk[6]; + if (++i == 10) + return 10; + rk += 4; + } + } + rk[4] = GETuint32(cipherKey + 16); + rk[5] = GETuint32(cipherKey + 20); + if (keyBits == 192) + { + for (;;) + { + temp = rk[ 5]; + rk[ 6] = (rk[ 0] ^ + (Te4[(temp >> 16) & 0xff] & 0xff000000) ^ + (Te4[(temp >> 8) & 0xff] & 0x00ff0000) ^ + (Te4[(temp ) & 0xff] & 0x0000ff00) ^ + (Te4[(temp >> 24) ] & 0x000000ff) ^ + rcon[i]); + rk[ 7] = rk[ 1] ^ rk[ 6]; + rk[ 8] = rk[ 2] ^ rk[ 7]; + rk[ 9] = rk[ 3] ^ rk[ 8]; + if (++i == 8) + { + return 12; + } + rk[10] = rk[ 4] ^ rk[ 9]; + rk[11] = rk[ 5] ^ rk[10]; + rk += 6; + } + } + rk[6] = GETuint32(cipherKey + 24); + rk[7] = GETuint32(cipherKey + 28); + if (keyBits == 256) + { + for (;;) + { + temp = rk[ 7]; + rk[ 8] = (rk[ 0] ^ + (Te4[(temp >> 16) & 0xff] & 0xff000000) ^ + (Te4[(temp >> 8) & 0xff] & 0x00ff0000) ^ + (Te4[(temp ) & 0xff] & 0x0000ff00) ^ + (Te4[(temp >> 24) ] & 0x000000ff) ^ + rcon[i]); + rk[ 9] = rk[ 1] ^ rk[ 8]; + rk[10] = rk[ 2] ^ rk[ 9]; + rk[11] = rk[ 3] ^ rk[10]; + if (++i == 7) + { + return 14; + } + temp = rk[11]; + rk[12] = (rk[ 4] ^ + (Te4[(temp >> 24) ] & 0xff000000) ^ + (Te4[(temp >> 16) & 0xff] & 0x00ff0000) ^ + (Te4[(temp >> 8) & 0xff] & 0x0000ff00) ^ + (Te4[(temp ) & 0xff] & 0x000000ff)); + rk[13] = rk[ 5] ^ rk[12]; + rk[14] = rk[ 6] ^ rk[13]; + rk[15] = rk[ 7] ^ rk[14]; + rk += 8; + } + } + return 0; +} + + +/* + Expand the cipher key into the decryption key schedule. + + RETURN + The number of rounds for the given cipher key size. +*/ + +int rijndaelKeySetupDec(uint32 rk[/*4*(Nr + 1)*/], const uint8 cipherKey[], + int keyBits) +{ + int nr, i, j; + uint32 temp; + + /* expand the cipher key: */ + nr = rijndaelKeySetupEnc(rk, cipherKey, keyBits); + /* invert the order of the round keys: */ + for (i = 0, j = 4*nr; i < j; i += 4, j -= 4) + { + temp = rk[i ]; rk[i ] = rk[j ]; rk[j ] = temp; + temp = rk[i + 1]; rk[i + 1] = rk[j + 1]; rk[j + 1] = temp; + temp = rk[i + 2]; rk[i + 2] = rk[j + 2]; rk[j + 2] = temp; + temp = rk[i + 3]; rk[i + 3] = rk[j + 3]; rk[j + 3] = temp; + } + /* + Apply the inverse MixColumn transform to all round keys but the first + and the last: + */ + for (i = 1; i < nr; i++) + { + rk += 4; + + rk[0]= ( + Td0[Te4[(rk[0] >> 24) ] & 0xff] ^ + Td1[Te4[(rk[0] >> 16) & 0xff] & 0xff] ^ + Td2[Te4[(rk[0] >> 8) & 0xff] & 0xff] ^ + Td3[Te4[(rk[0] ) & 0xff] & 0xff]); + + rk[1]= (Td0[Te4[(rk[1] >> 24) ] & 0xff] ^ + Td1[Te4[(rk[1] >> 16) & 0xff] & 0xff] ^ + Td2[Te4[(rk[1] >> 8) & 0xff] & 0xff] ^ + Td3[Te4[(rk[1] ) & 0xff] & 0xff]); + + rk[2]= (Td0[Te4[(rk[2] >> 24) ] & 0xff] ^ + Td1[Te4[(rk[2] >> 16) & 0xff] & 0xff] ^ + Td2[Te4[(rk[2] >> 8) & 0xff] & 0xff] ^ + Td3[Te4[(rk[2] ) & 0xff] & 0xff]); + + rk[3]= (Td0[Te4[(rk[3] >> 24) ] & 0xff] ^ + Td1[Te4[(rk[3] >> 16) & 0xff] & 0xff] ^ + Td2[Te4[(rk[3] >> 8) & 0xff] & 0xff] ^ + Td3[Te4[(rk[3] ) & 0xff] & 0xff]); + } + return nr; +} + + +void rijndaelEncrypt(const uint32 rk[/*4*(Nr + 1)*/], int Nr, + const uint8 pt[16], uint8 ct[16]) +{ + uint32 s0, s1, s2, s3, t0, t1, t2, t3; +#ifndef FULL_UNROLL + int r; +#endif /* FULL_UNROLL */ + + /* map byte array block to cipher state and add initial round key: */ + s0 = GETuint32(pt ) ^ rk[0]; + s1 = GETuint32(pt + 4) ^ rk[1]; + s2 = GETuint32(pt + 8) ^ rk[2]; + s3 = GETuint32(pt + 12) ^ rk[3]; + +#ifdef FULL_UNROLL + /* round 1: */ + t0= (Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] + ^ Te3[s3 & 0xff] ^ rk[ 4]); + t1= (Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] + ^ Te3[s0 & 0xff] ^ rk[ 5]); + t2= (Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] + ^ Te3[s1 & 0xff] ^ rk[ 6]); + t3= (Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] + ^ Te3[s2 & 0xff] ^ rk[ 7]); + + /* round 2: */ + s0= (Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & 0xff] + ^ Te3[t3 & 0xff] ^ rk[ 8]); + s1= (Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & 0xff] + ^ Te3[t0 & 0xff] ^ rk[ 9]); + s2= (Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & 0xff] + ^ Te3[t1 & 0xff] ^ rk[10]); + s3= (Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & 0xff] + ^ Te3[t2 & 0xff] ^ rk[11]); + + /* round 3: */ + t0= (Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] + ^ Te3[s3 & 0xff] ^ rk[12]); + t1= (Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] + ^ Te3[s0 & 0xff] ^ rk[13]); + t2= (Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] + ^ Te3[s1 & 0xff] ^ rk[14]); + t3= (Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] + ^ Te3[s2 & 0xff] ^ rk[15]); + + /* round 4: */ + s0= (Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & 0xff] + ^ Te3[t3 & 0xff] ^ rk[16]); + s1= (Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & 0xff] + ^ Te3[t0 & 0xff] ^ rk[17]); + s2= (Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & 0xff] + ^ Te3[t1 & 0xff] ^ rk[18]); + s3= (Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & 0xff] + ^ Te3[t2 & 0xff] ^ rk[19]); + + /* round 5: */ + t0= (Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] + ^ Te3[s3 & 0xff] ^ rk[20]); + t1= (Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] + ^ Te3[s0 & 0xff] ^ rk[21]); + t2= (Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] + ^ Te3[s1 & 0xff] ^ rk[22]); + t3= (Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] + ^ Te3[s2 & 0xff] ^ rk[23]); + + /* round 6: */ + s0= (Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & 0xff] + ^ Te3[t3 & 0xff] ^ rk[24]); + s1= (Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & 0xff] + ^ Te3[t0 & 0xff] ^ rk[25]); + s2= (Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & 0xff] + ^ Te3[t1 & 0xff] ^ rk[26]); + s3= (Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & 0xff] + ^ Te3[t2 & 0xff] ^ rk[27]); + + /* round 7: */ + t0= (Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] + ^ Te3[s3 & 0xff] ^ rk[28]); + t1= (Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] + ^ Te3[s0 & 0xff] ^ rk[29]); + t2= (Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] + ^ Te3[s1 & 0xff] ^ rk[30]); + t3= (Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] + ^ Te3[s2 & 0xff] ^ rk[31]); + + /* round 8: */ + s0= (Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & 0xff] + ^ Te3[t3 & 0xff] ^ rk[32]); + s1= (Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & 0xff] + ^ Te3[t0 & 0xff] ^ rk[33]); + s2= (Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & 0xff] + ^ Te3[t1 & 0xff] ^ rk[34]); + s3= (Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & 0xff] + ^ Te3[t2 & 0xff] ^ rk[35]); + + /* round 9: */ + t0= (Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] + ^ Te3[s3 & 0xff] ^ rk[36]); + t1= (Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] + ^ Te3[s0 & 0xff] ^ rk[37]); + t2= (Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] + ^ Te3[s1 & 0xff] ^ rk[38]); + t3= (Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] + ^ Te3[s2 & 0xff] ^ rk[39]); + + if (Nr > 10) + { + /* round 10: */ + s0= (Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & 0xff] + ^ Te3[t3 & 0xff] ^ rk[40]); + s1= (Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & 0xff] + ^ Te3[t0 & 0xff] ^ rk[41]); + s2= (Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & 0xff] + ^ Te3[t1 & 0xff] ^ rk[42]); + s3= (Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & 0xff] + ^ Te3[t2 & 0xff] ^ rk[43]); + + /* round 11: */ + t0= (Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] + ^ Te3[s3 & 0xff] ^ rk[44]); + t1= (Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] + ^ Te3[s0 & 0xff] ^ rk[45]); + t2= (Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] + ^ Te3[s1 & 0xff] ^ rk[46]); + t3= (Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] + ^ Te3[s2 & 0xff] ^ rk[47]); + + if (Nr > 12) + { + /* round 12: */ + s0= (Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & 0xff] + ^ Te3[t3 & 0xff] ^ rk[48]); + s1= (Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & 0xff] + ^ Te3[t0 & 0xff] ^ rk[49]); + s2= (Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & 0xff] + ^ Te3[t1 & 0xff] ^ rk[50]); + s3= (Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & 0xff] + ^ Te3[t2 & 0xff] ^ rk[51]); + + /* round 13: */ + t0= (Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] + ^ Te3[s3 & 0xff] ^ rk[52]); + t1= (Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] + ^ Te3[s0 & 0xff] ^ rk[53]); + t2= (Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] + ^ Te3[s1 & 0xff] ^ rk[54]); + t3= (Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] + ^ Te3[s2 & 0xff] ^ rk[55]); + } + } + rk += Nr << 2; +#else /* !FULL_UNROLL */ + + /* Nr - 1 full rounds: */ + + r = Nr >> 1; + for (;;) + { + t0= (Te0[(s0 >> 24) ] ^ + Te1[(s1 >> 16) & 0xff] ^ + Te2[(s2 >> 8) & 0xff] ^ + Te3[(s3 ) & 0xff] ^ + rk[4]); + + t1= (Te0[(s1 >> 24) ] ^ + Te1[(s2 >> 16) & 0xff] ^ + Te2[(s3 >> 8) & 0xff] ^ + Te3[(s0 ) & 0xff] ^ + rk[5]); + + t2= (Te0[(s2 >> 24) ] ^ + Te1[(s3 >> 16) & 0xff] ^ + Te2[(s0 >> 8) & 0xff] ^ + Te3[(s1 ) & 0xff] ^ + rk[6]); + + t3= (Te0[(s3 >> 24) ] ^ + Te1[(s0 >> 16) & 0xff] ^ + Te2[(s1 >> 8) & 0xff] ^ + Te3[(s2 ) & 0xff] ^ + rk[7]); + + rk+= 8; + if (--r == 0) + break; + + s0= (Te0[(t0 >> 24) ] ^ + Te1[(t1 >> 16) & 0xff] ^ + Te2[(t2 >> 8) & 0xff] ^ + Te3[(t3 ) & 0xff] ^ + rk[0]); + + s1= (Te0[(t1 >> 24) ] ^ + Te1[(t2 >> 16) & 0xff] ^ + Te2[(t3 >> 8) & 0xff] ^ + Te3[(t0 ) & 0xff] ^ + rk[1]); + + s2= (Te0[(t2 >> 24) ] ^ + Te1[(t3 >> 16) & 0xff] ^ + Te2[(t0 >> 8) & 0xff] ^ + Te3[(t1 ) & 0xff] ^ + rk[2]); + + s3= (Te0[(t3 >> 24) ] ^ + Te1[(t0 >> 16) & 0xff] ^ + Te2[(t1 >> 8) & 0xff] ^ + Te3[(t2 ) & 0xff] ^ + rk[3]); + } +#endif /* FULL_UNROLL */ + + /* Apply last round and map cipher state to byte array block: */ + s0= ((Te4[(t0 >> 24) ] & 0xff000000) ^ + (Te4[(t1 >> 16) & 0xff] & 0x00ff0000) ^ + (Te4[(t2 >> 8) & 0xff] & 0x0000ff00) ^ + (Te4[(t3 ) & 0xff] & 0x000000ff) ^ + rk[0]); + PUTuint32(ct , s0); + + s1= ((Te4[(t1 >> 24) ] & 0xff000000) ^ + (Te4[(t2 >> 16) & 0xff] & 0x00ff0000) ^ + (Te4[(t3 >> 8) & 0xff] & 0x0000ff00) ^ + (Te4[(t0 ) & 0xff] & 0x000000ff) ^ + rk[1]); + PUTuint32(ct + 4, s1); + + s2= ((Te4[(t2 >> 24) ] & 0xff000000) ^ + (Te4[(t3 >> 16) & 0xff] & 0x00ff0000) ^ + (Te4[(t0 >> 8) & 0xff] & 0x0000ff00) ^ + (Te4[(t1 ) & 0xff] & 0x000000ff) ^ + rk[2]); + PUTuint32(ct + 8, s2); + + s3= ((Te4[(t3 >> 24) ] & 0xff000000) ^ + (Te4[(t0 >> 16) & 0xff] & 0x00ff0000) ^ + (Te4[(t1 >> 8) & 0xff] & 0x0000ff00) ^ + (Te4[(t2 ) & 0xff] & 0x000000ff) ^ + rk[3]); + PUTuint32(ct + 12, s3); +} + + +void rijndaelDecrypt(const uint32 rk[/*4*(Nr + 1)*/], int Nr, + const uint8 ct[16], uint8 pt[16]) +{ + uint32 s0, s1, s2, s3, t0, t1, t2, t3; +#ifndef FULL_UNROLL + int r; +#endif /* FULL_UNROLL */ + + /* Map byte array block to cipher state and add initial round key: */ + + s0 = GETuint32(ct ) ^ rk[0]; + s1 = GETuint32(ct + 4) ^ rk[1]; + s2 = GETuint32(ct + 8) ^ rk[2]; + s3 = GETuint32(ct + 12) ^ rk[3]; + +#ifdef FULL_UNROLL + /* round 1: */ + t0= (Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] + ^ Td3[s1 & 0xff] ^ rk[ 4]); + t1= (Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] + ^ Td3[s2 & 0xff] ^ rk[ 5]); + t2= (Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] + ^ Td3[s3 & 0xff] ^ rk[ 6]); + t3= (Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] + ^ Td3[s0 & 0xff] ^ rk[ 7]); + + /* round 2: */ + s0= (Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >> 8) & 0xff] + ^ Td3[t1 & 0xff] ^ rk[ 8]); + s1= (Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >> 8) & 0xff] + ^ Td3[t2 & 0xff] ^ rk[ 9]); + s2= (Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >> 8) & 0xff] + ^ Td3[t3 & 0xff] ^ rk[10]); + s3= (Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >> 8) & 0xff] + ^ Td3[t0 & 0xff] ^ rk[11]); + + /* round 3: */ + t0= (Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] + ^ Td3[s1 & 0xff] ^ rk[12]); + t1= (Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] + ^ Td3[s2 & 0xff] ^ rk[13]); + t2= (Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] + ^ Td3[s3 & 0xff] ^ rk[14]); + t3= (Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] + ^ Td3[s0 & 0xff] ^ rk[15]); + + /* round 4: */ + s0= (Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >> 8) & 0xff] + ^ Td3[t1 & 0xff] ^ rk[16]); + s1= (Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >> 8) & 0xff] + ^ Td3[t2 & 0xff] ^ rk[17]); + s2= (Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >> 8) & 0xff] + ^ Td3[t3 & 0xff] ^ rk[18]); + s3= (Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >> 8) & 0xff] + ^ Td3[t0 & 0xff] ^ rk[19]); + + /* round 5: */ + t0= (Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] + ^ Td3[s1 & 0xff] ^ rk[20]); + t1= (Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] + ^ Td3[s2 & 0xff] ^ rk[21]); + t2= (Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] + ^ Td3[s3 & 0xff] ^ rk[22]); + t3= (Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] + ^ Td3[s0 & 0xff] ^ rk[23]); + + /* round 6: */ + s0= (Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >> 8) & 0xff] + ^ Td3[t1 & 0xff] ^ rk[24]); + s1= (Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >> 8) & 0xff] + ^ Td3[t2 & 0xff] ^ rk[25]); + s2= (Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >> 8) & 0xff] + ^ Td3[t3 & 0xff] ^ rk[26]); + s3= (Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >> 8) & 0xff] + ^ Td3[t0 & 0xff] ^ rk[27]); + + /* round 7: */ + t0= (Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] + ^ Td3[s1 & 0xff] ^ rk[28]); + t1= (Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] + ^ Td3[s2 & 0xff] ^ rk[29]); + t2= (Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] + ^ Td3[s3 & 0xff] ^ rk[30]); + t3= (Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] + ^ Td3[s0 & 0xff] ^ rk[31]); + + /* round 8: */ + s0= (Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >> 8) & 0xff] + ^ Td3[t1 & 0xff] ^ rk[32]); + s1= (Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >> 8) & 0xff] + ^ Td3[t2 & 0xff] ^ rk[33]); + s2= (Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >> 8) & 0xff] + ^ Td3[t3 & 0xff] ^ rk[34]); + s3= (Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >> 8) & 0xff] + ^ Td3[t0 & 0xff] ^ rk[35]); + + /* round 9: */ + t0= (Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] + ^ Td3[s1 & 0xff] ^ rk[36]); + t1= (Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] + ^ Td3[s2 & 0xff] ^ rk[37]); + t2= (Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] + ^ Td3[s3 & 0xff] ^ rk[38]); + t3= (Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] + ^ Td3[s0 & 0xff] ^ rk[39]); + + if (Nr > 10) + { + /* round 10: */ + s0= (Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >> 8) & 0xff] + ^ Td3[t1 & 0xff] ^ rk[40]); + s1= (Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >> 8) & 0xff] + ^ Td3[t2 & 0xff] ^ rk[41]); + s2= (Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >> 8) & 0xff] + ^ Td3[t3 & 0xff] ^ rk[42]); + s3= (Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >> 8) & 0xff] + ^ Td3[t0 & 0xff] ^ rk[43]); + + /* round 11: */ + t0= (Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] + ^ Td3[s1 & 0xff] ^ rk[44]); + t1= (Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] + ^ Td3[s2 & 0xff] ^ rk[45]); + t2= (Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] + ^ Td3[s3 & 0xff] ^ rk[46]); + t3= (Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] + ^ Td3[s0 & 0xff] ^ rk[47]); + + if (Nr > 12) + { + /* round 12: */ + s0= (Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >> 8) & 0xff] + ^ Td3[t1 & 0xff] ^ rk[48]); + s1= (Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >> 8) & 0xff] + ^ Td3[t2 & 0xff] ^ rk[49]); + s2= (Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >> 8) & 0xff] + ^ Td3[t3 & 0xff] ^ rk[50]); + s3= (Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >> 8) & 0xff] + ^ Td3[t0 & 0xff] ^ rk[51]); + + /* round 13: */ + t0= (Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] + ^ Td3[s1 & 0xff] ^ rk[52]); + t1= (Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] + ^ Td3[s2 & 0xff] ^ rk[53]); + t2= (Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] + ^ Td3[s3 & 0xff] ^ rk[54]); + t3= (Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] + ^ Td3[s0 & 0xff] ^ rk[55]); + } + } + rk += Nr << 2; +#else /* !FULL_UNROLL */ + + /* Nr - 1 full rounds: */ + r= (Nr >> 1); + for (;;) + { + t0= (Td0[(s0 >> 24) ] ^ + Td1[(s3 >> 16) & 0xff] ^ + Td2[(s2 >> 8) & 0xff] ^ + Td3[(s1 ) & 0xff] ^ + rk[4]); + + t1= (Td0[(s1 >> 24) ] ^ + Td1[(s0 >> 16) & 0xff] ^ + Td2[(s3 >> 8) & 0xff] ^ + Td3[(s2 ) & 0xff] ^ + rk[5]); + + t2= (Td0[(s2 >> 24) ] ^ + Td1[(s1 >> 16) & 0xff] ^ + Td2[(s0 >> 8) & 0xff] ^ + Td3[(s3 ) & 0xff] ^ + rk[6]); + + t3= (Td0[(s3 >> 24) ] ^ + Td1[(s2 >> 16) & 0xff] ^ + Td2[(s1 >> 8) & 0xff] ^ + Td3[(s0 ) & 0xff] ^ + rk[7]); + + rk+= 8; + if (--r == 0) + break; + + s0= (Td0[(t0 >> 24) ] ^ + Td1[(t3 >> 16) & 0xff] ^ + Td2[(t2 >> 8) & 0xff] ^ + Td3[(t1 ) & 0xff] ^ + rk[0]); + + s1= (Td0[(t1 >> 24) ] ^ + Td1[(t0 >> 16) & 0xff] ^ + Td2[(t3 >> 8) & 0xff] ^ + Td3[(t2 ) & 0xff] ^ + rk[1]); + + s2= (Td0[(t2 >> 24) ] ^ + Td1[(t1 >> 16) & 0xff] ^ + Td2[(t0 >> 8) & 0xff] ^ + Td3[(t3 ) & 0xff] ^ + rk[2]); + + s3= (Td0[(t3 >> 24) ] ^ + Td1[(t2 >> 16) & 0xff] ^ + Td2[(t1 >> 8) & 0xff] ^ + Td3[(t0 ) & 0xff] ^ + rk[3]); + } + +#endif /* FULL_UNROLL */ + + /* Apply last round and map cipher state to byte array block: */ + + s0= ((Td4[(t0 >> 24) ] & 0xff000000) ^ + (Td4[(t3 >> 16) & 0xff] & 0x00ff0000) ^ + (Td4[(t2 >> 8) & 0xff] & 0x0000ff00) ^ + (Td4[(t1 ) & 0xff] & 0x000000ff) ^ + rk[0]); + PUTuint32(pt , s0); + + s1= ((Td4[(t1 >> 24) ] & 0xff000000) ^ + (Td4[(t0 >> 16) & 0xff] & 0x00ff0000) ^ + (Td4[(t3 >> 8) & 0xff] & 0x0000ff00) ^ + (Td4[(t2 ) & 0xff] & 0x000000ff) ^ + rk[1]); + PUTuint32(pt + 4, s1); + + s2= ((Td4[(t2 >> 24) ] & 0xff000000) ^ + (Td4[(t1 >> 16) & 0xff] & 0x00ff0000) ^ + (Td4[(t0 >> 8) & 0xff] & 0x0000ff00) ^ + (Td4[(t3 ) & 0xff] & 0x000000ff) ^ + rk[2]); + PUTuint32(pt + 8, s2); + + s3= ((Td4[(t3 >> 24) ] & 0xff000000) ^ + (Td4[(t2 >> 16) & 0xff] & 0x00ff0000) ^ + (Td4[(t1 >> 8) & 0xff] & 0x0000ff00) ^ + (Td4[(t0 ) & 0xff] & 0x000000ff) ^ + rk[3]); + PUTuint32(pt + 12, s3); +} diff --git a/externals/mysql/mysys/rijndael.h b/externals/mysql/mysys/rijndael.h new file mode 100644 index 00000000000..89963a85c99 --- /dev/null +++ b/externals/mysql/mysys/rijndael.h @@ -0,0 +1,41 @@ +/* Copyright (C) 2002 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + + +/* + rijndael-alg-fst.h + + @version 3.0 (December 2000) + Optimised ANSI C code for the Rijndael cipher (now AES) + @author Vincent Rijmen <vincent.rijmen@esat.kuleuven.ac.be> + @author Antoon Bosselaers <antoon.bosselaers@esat.kuleuven.ac.be> + @author Paulo Barreto <paulo.barreto@terra.com.br> + + This code is hereby placed in the public domain. + Modified by Peter Zaitsev to fit MySQL coding style. + */ + +#define AES_MAXKC (256/32) +#define AES_MAXKB (256/8) +#define AES_MAXNR 14 + +int rijndaelKeySetupEnc(uint32 rk[/*4*(Nr + 1)*/], const uint8 cipherKey[], + int keyBits); +int rijndaelKeySetupDec(uint32 rk[/*4*(Nr + 1)*/], const uint8 cipherKey[], + int keyBits); +void rijndaelEncrypt(const uint32 rk[/*4*(Nr + 1)*/], int Nr, + const uint8 pt[16], uint8 ct[16]); +void rijndaelDecrypt(const uint32 rk[/*4*(Nr + 1)*/], int Nr, + const uint8 ct[16], uint8 pt[16]); diff --git a/externals/mysql/mysys/safemalloc.c b/externals/mysql/mysys/safemalloc.c new file mode 100644 index 00000000000..fc32df728b7 --- /dev/null +++ b/externals/mysql/mysys/safemalloc.c @@ -0,0 +1,576 @@ +/* Copyright (C) 2000-2003 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +/* + * Memory sub-system, written by Bjorn Benson + Fixed to use my_sys scheme by Michael Widenius + + [This posting refers to an article entitled "oops, corrupted memory + again!" in net.lang.c. I am posting it here because it is source.] + + My tool for approaching this problem is to build another level of data + abstraction on top of malloc() and free() that implements some checking. + This does a number of things for you: + - Checks for overruns and underruns on allocated data + - Keeps track of where in the program the memory was malloc'ed + - Reports on pieces of memory that were not free'ed + - Records some statistics such as maximum memory used + - Marks newly malloc'ed and newly free'ed memory with special values + You can use this scheme to: + - Find bugs such as overrun, underrun, etc because you know where + a piece of data was malloc'ed and where it was free'ed + - Find bugs where memory was not free'ed + - Find bugs where newly malloc'ed memory is used without initializing + - Find bugs where newly free'ed memory is still used + - Determine how much memory your program really uses + - and other things + + To implement my scheme you must have a C compiler that has __LINE__ and + __FILE__ macros. If your compiler doesn't have these then (a) buy another: + compilers that do are available on UNIX 4.2bsd based systems and the PC, + and probably on other machines; or (b) change my scheme somehow. I have + recomendations on both these points if you would like them (e-mail please). + + There are 4 functions in my package: + char *NEW( uSize ) Allocate memory of uSize bytes + (equivalent to malloc()) + char *REA( pPtr, uSize) Allocate memory of uSize bytes, move data and + free pPtr. + (equivalent to realloc()) + FREE( pPtr ) Free memory allocated by NEW + (equivalent to free()) + TERMINATE(file,flag) End system, report errors and stats on file + I personally use two more functions, but have not included them here: + char *STRSAVE( sPtr ) Save a copy of the string in dynamic memory + char *RENEW( pPtr, uSize ) + (equivalent to realloc()) + +*/ + +#ifndef SAFEMALLOC +#define SAFEMALLOC /* Get protos from my_sys */ +#endif + +#include "mysys_priv.h" +#include <m_string.h> +#include "my_static.h" +#include "mysys_err.h" + +ulonglong sf_malloc_mem_limit= ~(ulonglong)0; + +#ifndef PEDANTIC_SAFEMALLOC +/* + Set to 1 after TERMINATE() if we had to fiddle with sf_malloc_count and + the linked list of blocks so that _sanity() will not fuss when it + is not supposed to +*/ +static int sf_malloc_tampered= 0; +#endif + + + /* Static functions prototypes */ + +static int check_ptr(const char *where, uchar *ptr, const char *sFile, + uint uLine); +static int _checkchunk(struct st_irem *pRec, const char *sFile, uint uLine); + +/* + Note: We only fill up the allocated block. This do not include + malloc() roundoff or the extra space required by the irem + structures. +*/ + +/* + NEW'ed memory is filled with this value so that references to it will + end up being very strange. +*/ +#define ALLOC_VAL (uchar) 0xA5 +/* + FEEE'ed memory is filled with this value so that references to it will + end up being very strange. +*/ +#define FREE_VAL (uchar) 0x8F +#define MAGICKEY 0x14235296 /* A magic value for underrun key */ + +/* + Warning: do not change the MAGICEND? values to something with the + high bit set. Various C compilers (like the 4.2bsd one) do not do + the sign extension right later on in this code and you will get + erroneous errors. +*/ + +#define MAGICEND0 0x68 /* Magic values for overrun keys */ +#define MAGICEND1 0x34 /* " */ +#define MAGICEND2 0x7A /* " */ +#define MAGICEND3 0x15 /* " */ + + +/* Allocate some memory. */ + +void *_mymalloc(size_t size, const char *filename, uint lineno, myf MyFlags) +{ + struct st_irem *irem; + uchar *data; + DBUG_ENTER("_mymalloc"); + DBUG_PRINT("enter",("Size: %lu", (ulong) size)); + + if (!sf_malloc_quick) + (void) _sanity (filename, lineno); + + /* + Test for memory limit overrun. + If compiled with DBUG, test for error injection. Described in my_sys.h. + */ + if ((size + sf_malloc_cur_memory > sf_malloc_mem_limit) + IF_DBUG(|| my_malloc_error_inject)) + { + IF_DBUG(if (my_malloc_error_inject) + errno= ENOMEM; + my_malloc_error_inject= 0); + irem= 0; + } + else + { + /* Allocate the physical memory */ + irem= (struct st_irem *) malloc (ALIGN_SIZE(sizeof(struct st_irem)) + + sf_malloc_prehunc + + size + /* size requested */ + 4 + /* overrun mark */ + sf_malloc_endhunc); + } + /* Check if there isn't anymore memory avaiable */ + if (!irem) + { + if (MyFlags & MY_FAE) + error_handler_hook=fatal_error_handler_hook; + if (MyFlags & (MY_FAE+MY_WME)) + { + char buff[MYSYS_ERRMSG_SIZE]; + my_errno=errno; + my_snprintf(buff, sizeof(buff), "Out of memory at line %d, '%s'", + lineno, filename); + my_message(EE_OUTOFMEMORY, buff, MYF(ME_BELL+ME_WAITTANG+ME_NOREFRESH)); + my_snprintf(buff, sizeof(buff), + "needed %lu byte (%luk), memory in use: %lu bytes (%luk)", + (ulong) size, (ulong) (size + 1023L) / 1024L, + (ulong) sf_malloc_max_memory, + (ulong) (sf_malloc_max_memory + 1023L) / 1024L); + my_message(EE_OUTOFMEMORY, buff, MYF(ME_BELL+ME_WAITTANG+ME_NOREFRESH)); + } + DBUG_PRINT("error",("Out of memory, in use: %ld at line %d, '%s'", + sf_malloc_max_memory,lineno, filename)); + if (MyFlags & MY_FAE) + exit(1); + DBUG_RETURN ((void*) 0); + } + + /* Fill up the structure */ + data= (((uchar*) irem) + ALIGN_SIZE(sizeof(struct st_irem)) + + sf_malloc_prehunc); + *((uint32*) (data-sizeof(uint32)))= MAGICKEY; + data[size + 0]= MAGICEND0; + data[size + 1]= MAGICEND1; + data[size + 2]= MAGICEND2; + data[size + 3]= MAGICEND3; + irem->filename= (char *) filename; + irem->linenum= lineno; + irem->datasize= size; + irem->prev= NULL; + + /* Add this remember structure to the linked list */ + pthread_mutex_lock(&THR_LOCK_malloc); + if ((irem->next= sf_malloc_root)) + sf_malloc_root->prev= irem; + sf_malloc_root= irem; + + /* Keep the statistics */ + sf_malloc_cur_memory+= size; + if (sf_malloc_cur_memory > sf_malloc_max_memory) + sf_malloc_max_memory= sf_malloc_cur_memory; + sf_malloc_count++; + pthread_mutex_unlock(&THR_LOCK_malloc); + + /* Set the memory to the aribtrary wierd value */ + if ((MyFlags & MY_ZEROFILL) || !sf_malloc_quick) + bfill(data, size, (char) (MyFlags & MY_ZEROFILL ? 0 : ALLOC_VAL)); + /* Return a pointer to the real data */ + DBUG_PRINT("exit",("ptr: %p", data)); + if (sf_min_adress > data) + sf_min_adress= data; + if (sf_max_adress < data) + sf_max_adress= data; + DBUG_RETURN((void*) data); +} + + +/* + Allocate some new memory and move old memoryblock there. + Free then old memoryblock +*/ + +void *_myrealloc(register void *ptr, register size_t size, + const char *filename, uint lineno, myf MyFlags) +{ + struct st_irem *irem; + char *data; + DBUG_ENTER("_myrealloc"); + + if (!ptr && (MyFlags & MY_ALLOW_ZERO_PTR)) + DBUG_RETURN(_mymalloc(size, filename, lineno, MyFlags)); + + if (!sf_malloc_quick) + (void) _sanity (filename, lineno); + + if (check_ptr("Reallocating", (uchar*) ptr, filename, lineno)) + DBUG_RETURN((uchar*) NULL); + + irem= (struct st_irem *) (((char*) ptr) - ALIGN_SIZE(sizeof(struct st_irem))- + sf_malloc_prehunc); + if (*((uint32*) (((char*) ptr)- sizeof(uint32))) != MAGICKEY) + { + fprintf(stderr, "Error: Reallocating unallocated data at line %d, '%s'\n", + lineno, filename); + DBUG_PRINT("safe",("Reallocating unallocated data at line %d, '%s'", + lineno, filename)); + (void) fflush(stderr); + DBUG_RETURN((uchar*) NULL); + } + + if ((data= _mymalloc(size,filename,lineno,MyFlags))) /* Allocate new area */ + { + size=min(size, irem->datasize); /* Move as much as possibly */ + memcpy((uchar*) data, ptr, (size_t) size); /* Copy old data */ + _myfree(ptr, filename, lineno, 0); /* Free not needed area */ + } + else + { + if (MyFlags & MY_HOLD_ON_ERROR) + DBUG_RETURN(ptr); + if (MyFlags & MY_FREE_ON_ERROR) + _myfree(ptr, filename, lineno, 0); + } + DBUG_RETURN(data); +} /* _myrealloc */ + + +/* Deallocate some memory. */ + +void _myfree(void *ptr, const char *filename, uint lineno, myf myflags) +{ + struct st_irem *irem; + DBUG_ENTER("_myfree"); + DBUG_PRINT("enter",("ptr: %p", ptr)); + + if (!sf_malloc_quick) + (void) _sanity (filename, lineno); + + if ((!ptr && (myflags & MY_ALLOW_ZERO_PTR)) || + check_ptr("Freeing",(uchar*) ptr,filename,lineno)) + DBUG_VOID_RETURN; + + /* Calculate the address of the remember structure */ + irem= (struct st_irem *) ((char*) ptr- ALIGN_SIZE(sizeof(struct st_irem))- + sf_malloc_prehunc); + + /* + Check to make sure that we have a real remember structure. + Note: this test could fail for four reasons: + (1) The memory was already free'ed + (2) The memory was never new'ed + (3) There was an underrun + (4) A stray pointer hit this location + */ + + if (*((uint32*) ((char*) ptr- sizeof(uint32))) != MAGICKEY) + { + fprintf(stderr, "Error: Freeing unallocated data at line %d, '%s'\n", + lineno, filename); + DBUG_PRINT("safe",("Unallocated data at line %d, '%s'",lineno,filename)); + (void) fflush(stderr); + DBUG_VOID_RETURN; + } + + /* Remove this structure from the linked list */ + pthread_mutex_lock(&THR_LOCK_malloc); + if (irem->prev) + irem->prev->next= irem->next; + else + sf_malloc_root= irem->next; + + if (irem->next) + irem->next->prev= irem->prev; + /* Handle the statistics */ + sf_malloc_cur_memory-= irem->datasize; + sf_malloc_count--; + pthread_mutex_unlock(&THR_LOCK_malloc); + +#ifndef HAVE_purify + /* Mark this data as free'ed */ + if (!sf_malloc_quick) + bfill(ptr, irem->datasize, (pchar) FREE_VAL); +#endif + *((uint32*) ((char*) ptr- sizeof(uint32)))= ~MAGICKEY; + /* Actually free the memory */ + free((char*) irem); + DBUG_VOID_RETURN; +} + + /* Check if we have a wrong pointer */ + +static int check_ptr(const char *where, uchar *ptr, const char *filename, + uint lineno) +{ + if (!ptr) + { + fprintf(stderr, "Error: %s NULL pointer at line %d, '%s'\n", + where,lineno, filename); + DBUG_PRINT("safe",("Null pointer at line %d '%s'", lineno, filename)); + (void) fflush(stderr); + return 1; + } +#ifndef _MSC_VER + if ((long) ptr & (ALIGN_SIZE(1)-1)) + { + fprintf(stderr, "Error: %s wrong aligned pointer at line %d, '%s'\n", + where,lineno, filename); + DBUG_PRINT("safe",("Wrong aligned pointer at line %d, '%s'", + lineno,filename)); + (void) fflush(stderr); + return 1; + } +#endif + if (ptr < sf_min_adress || ptr > sf_max_adress) + { + fprintf(stderr, "Error: %s pointer out of range at line %d, '%s'\n", + where,lineno, filename); + DBUG_PRINT("safe",("Pointer out of range at line %d '%s'", + lineno,filename)); + (void) fflush(stderr); + return 1; + } + return 0; +} + + +/* + Report on all the memory pieces that have not been free'ed + + SYNOPSIS + TERMINATE() + file Write output to this file + flag If <> 0, also write statistics + */ + +void TERMINATE(FILE *file, uint flag) +{ + struct st_irem *irem; + DBUG_ENTER("TERMINATE"); + pthread_mutex_lock(&THR_LOCK_malloc); + + /* + Report the difference between number of calls to + NEW and the number of calls to FREE. >0 means more + NEWs than FREEs. <0, etc. + */ + + if (sf_malloc_count) + { + if (file) + { + fprintf(file, "Warning: Not freed memory segments: %u\n", sf_malloc_count); + (void) fflush(file); + } + DBUG_PRINT("safe",("sf_malloc_count: %u", sf_malloc_count)); + } + + /* + Report on all the memory that was allocated with NEW + but not free'ed with FREE. + */ + + if ((irem= sf_malloc_root)) + { + if (file) + { + fprintf(file, "Warning: Memory that was not free'ed (%lu bytes):\n", + (ulong) sf_malloc_cur_memory); + (void) fflush(file); + } + DBUG_PRINT("safe",("Memory that was not free'ed (%lu bytes):", + (ulong) sf_malloc_cur_memory)); + while (irem) + { + char *data= (((char*) irem) + ALIGN_SIZE(sizeof(struct st_irem)) + + sf_malloc_prehunc); + if (file) + { + fprintf(file, + "\t%6lu bytes at %p, allocated at line %4u in '%s'", + (ulong) irem->datasize, data, irem->linenum, irem->filename); + fprintf(file, "\n"); + (void) fflush(file); + } + DBUG_PRINT("safe", + ("%6lu bytes at %p, allocated at line %4d in '%s'", + (ulong) irem->datasize, + data, irem->linenum, irem->filename)); + irem= irem->next; + } + } + /* Report the memory usage statistics */ + if (file && flag) + { + fprintf(file, "Maximum memory usage: %lu bytes (%luk)\n", + (ulong) sf_malloc_max_memory, + (ulong) (sf_malloc_max_memory + 1023L) / 1024L); + (void) fflush(file); + } + DBUG_PRINT("safe",("Maximum memory usage: %lu bytes (%luk)", + (ulong) sf_malloc_max_memory, + (ulong) (sf_malloc_max_memory + 1023L) /1024L)); + pthread_mutex_unlock(&THR_LOCK_malloc); + DBUG_VOID_RETURN; +} + + +/* + Report where a piece of memory was allocated + + This is usefull to call from withing a debugger +*/ + + +void sf_malloc_report_allocated(void *memory) +{ + struct st_irem *irem; + for (irem= sf_malloc_root ; irem ; irem=irem->next) + { + char *data= (((char*) irem) + ALIGN_SIZE(sizeof(struct st_irem)) + + sf_malloc_prehunc); + if (data <= (char*) memory && (char*) memory <= data + irem->datasize) + { + printf("%lu bytes at %p, allocated at line %u in '%s'\n", + (ulong) irem->datasize, data, irem->linenum, irem->filename); + break; + } + } +} + + /* Returns 0 if chunk is ok */ + +static int _checkchunk(register struct st_irem *irem, const char *filename, + uint lineno) +{ + int flag=0; + char *magicp, *data; + + data= (((char*) irem) + ALIGN_SIZE(sizeof(struct st_irem)) + + sf_malloc_prehunc); + /* Check for a possible underrun */ + if (*((uint32*) (data- sizeof(uint32))) != MAGICKEY) + { + fprintf(stderr, "Error: Memory allocated at %s:%d was underrun,", + irem->filename, irem->linenum); + fprintf(stderr, " discovered at %s:%d\n", filename, lineno); + (void) fflush(stderr); + DBUG_PRINT("safe",("Underrun at %p, allocated at %s:%d", + data, irem->filename, irem->linenum)); + flag=1; + } + + /* Check for a possible overrun */ + magicp= data + irem->datasize; + if (*magicp++ != MAGICEND0 || + *magicp++ != MAGICEND1 || + *magicp++ != MAGICEND2 || + *magicp++ != MAGICEND3) + { + fprintf(stderr, "Error: Memory allocated at %s:%d was overrun,", + irem->filename, irem->linenum); + fprintf(stderr, " discovered at '%s:%d'\n", filename, lineno); + (void) fflush(stderr); + DBUG_PRINT("safe",("Overrun at %p, allocated at %s:%d", + data, irem->filename, irem->linenum)); + flag=1; + } + return(flag); +} + + + /* Returns how many wrong chunks */ + +int _sanity(const char *filename, uint lineno) +{ + reg1 struct st_irem *irem; + reg2 int flag=0; + uint count=0; + + pthread_mutex_lock(&THR_LOCK_malloc); +#ifndef PEDANTIC_SAFEMALLOC + if (sf_malloc_tampered && (int) sf_malloc_count < 0) + sf_malloc_count=0; +#endif + count=sf_malloc_count; + for (irem= sf_malloc_root; irem != NULL && count-- ; irem= irem->next) + flag+= _checkchunk (irem, filename, lineno); + pthread_mutex_unlock(&THR_LOCK_malloc); + if (count || irem) + { + const char *format="Error: Safemalloc link list destroyed, discovered at '%s:%d'"; + fprintf(stderr, format, filename, lineno); fputc('\n',stderr); + fprintf(stderr, "root=%p,count=%d,irem=%p\n", sf_malloc_root,count,irem); + (void) fflush(stderr); + DBUG_PRINT("safe",(format, filename, lineno)); + flag=1; + } + return flag; +} /* _sanity */ + + + /* malloc and copy */ + +void *_my_memdup(const void *from, size_t length, const char *filename, + uint lineno, myf MyFlags) +{ + void *ptr; + if ((ptr= _mymalloc(length,filename,lineno,MyFlags)) != 0) + memcpy(ptr, from, length); + return(ptr); +} /*_my_memdup */ + + +char *_my_strdup(const char *from, const char *filename, uint lineno, + myf MyFlags) +{ + char *ptr; + size_t length= strlen(from)+1; + if ((ptr= (char*) _mymalloc(length,filename,lineno,MyFlags)) != 0) + memcpy((uchar*) ptr, (uchar*) from, (size_t) length); + return(ptr); +} /* _my_strdup */ + + +char *_my_strndup(const char *from, size_t length, + const char *filename, uint lineno, + myf MyFlags) +{ + char *ptr; + if ((ptr= (char*) _mymalloc(length+1,filename,lineno,MyFlags)) != 0) + { + memcpy((uchar*) ptr, (uchar*) from, (size_t) length); + ptr[length]=0; + } + return(ptr); +} diff --git a/externals/mysql/mysys/sha1.c b/externals/mysql/mysys/sha1.c new file mode 100644 index 00000000000..3469e480c26 --- /dev/null +++ b/externals/mysql/mysys/sha1.c @@ -0,0 +1,391 @@ +/* Copyright (C) 2002, 2004, 2006 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +/* + Original Source from: http://www.faqs.org/rfcs/rfc3174.html + + DESCRIPTION + This file implements the Secure Hashing Algorithm 1 as + defined in FIPS PUB 180-1 published April 17, 1995. + + The SHA-1, produces a 160-bit message digest for a given data + stream. It should take about 2**n steps to find a message with the + same digest as a given message and 2**(n/2) to find any two + messages with the same digest, when n is the digest size in bits. + Therefore, this algorithm can serve as a means of providing a + "fingerprint" for a message. + + PORTABILITY ISSUES + SHA-1 is defined in terms of 32-bit "words". This code uses + <stdint.h> (included via "sha1.h" to define 32 and 8 bit unsigned + integer types. If your C compiler does not support 32 bit unsigned + integers, this code is not appropriate. + + CAVEATS + SHA-1 is designed to work with messages less than 2^64 bits long. + Although SHA-1 allows a message digest to be generated for messages + of any number of bits less than 2^64, this implementation only + works with messages with a length that is a multiple of the size of + an 8-bit character. + + CHANGES + 2002 by Peter Zaitsev to + - fit to new prototypes according to MySQL standard + - Some optimizations + - All checking is now done in debug only mode + - More comments +*/ + +#include "my_global.h" +#include "m_string.h" +#include "sha1.h" + +/* + Define the SHA1 circular left shift macro +*/ + +#define SHA1CircularShift(bits,word) \ + (((word) << (bits)) | ((word) >> (32-(bits)))) + +/* Local Function Prototyptes */ +static void SHA1PadMessage(SHA1_CONTEXT*); +static void SHA1ProcessMessageBlock(SHA1_CONTEXT*); + + +/* + Initialize SHA1Context + + SYNOPSIS + mysql_sha1_reset() + context [in/out] The context to reset. + + DESCRIPTION + This function will initialize the SHA1Context in preparation + for computing a new SHA1 message digest. + + RETURN + SHA_SUCCESS ok + != SHA_SUCCESS sha Error Code. +*/ + + +const uint32 sha_const_key[5]= +{ + 0x67452301, + 0xEFCDAB89, + 0x98BADCFE, + 0x10325476, + 0xC3D2E1F0 +}; + + +int mysql_sha1_reset(SHA1_CONTEXT *context) +{ +#ifndef DBUG_OFF + if (!context) + return SHA_NULL; +#endif + + context->Length = 0; + context->Message_Block_Index = 0; + + context->Intermediate_Hash[0] = sha_const_key[0]; + context->Intermediate_Hash[1] = sha_const_key[1]; + context->Intermediate_Hash[2] = sha_const_key[2]; + context->Intermediate_Hash[3] = sha_const_key[3]; + context->Intermediate_Hash[4] = sha_const_key[4]; + + context->Computed = 0; + context->Corrupted = 0; + + return SHA_SUCCESS; +} + + +/* + Return the 160-bit message digest into the array provided by the caller + + SYNOPSIS + mysql_sha1_result() + context [in/out] The context to use to calculate the SHA-1 hash. + Message_Digest: [out] Where the digest is returned. + + DESCRIPTION + NOTE: The first octet of hash is stored in the 0th element, + the last octet of hash in the 19th element. + + RETURN + SHA_SUCCESS ok + != SHA_SUCCESS sha Error Code. +*/ + +int mysql_sha1_result(SHA1_CONTEXT *context, + uint8 Message_Digest[SHA1_HASH_SIZE]) +{ + int i; + +#ifndef DBUG_OFF + if (!context || !Message_Digest) + return SHA_NULL; + + if (context->Corrupted) + return context->Corrupted; +#endif + + if (!context->Computed) + { + SHA1PadMessage(context); + /* message may be sensitive, clear it out */ + bzero((char*) context->Message_Block,64); + context->Length = 0; /* and clear length */ + context->Computed = 1; + } + + for (i = 0; i < SHA1_HASH_SIZE; i++) + Message_Digest[i] = (int8)((context->Intermediate_Hash[i>>2] >> 8 + * ( 3 - ( i & 0x03 ) ))); + return SHA_SUCCESS; +} + + +/* + Accepts an array of octets as the next portion of the message. + + SYNOPSIS + mysql_sha1_input() + context [in/out] The SHA context to update + message_array An array of characters representing the next portion + of the message. + length The length of the message in message_array + + RETURN + SHA_SUCCESS ok + != SHA_SUCCESS sha Error Code. +*/ + +int mysql_sha1_input(SHA1_CONTEXT *context, const uint8 *message_array, + unsigned length) +{ + if (!length) + return SHA_SUCCESS; + +#ifndef DBUG_OFF + /* We assume client konows what it is doing in non-debug mode */ + if (!context || !message_array) + return SHA_NULL; + if (context->Computed) + return (context->Corrupted= SHA_STATE_ERROR); + if (context->Corrupted) + return context->Corrupted; +#endif + + while (length--) + { + context->Message_Block[context->Message_Block_Index++]= + (*message_array & 0xFF); + context->Length += 8; /* Length is in bits */ + +#ifndef DBUG_OFF + /* + Then we're not debugging we assume we never will get message longer + 2^64 bits. + */ + if (context->Length == 0) + return (context->Corrupted= 1); /* Message is too long */ +#endif + + if (context->Message_Block_Index == 64) + { + SHA1ProcessMessageBlock(context); + } + message_array++; + } + return SHA_SUCCESS; +} + + +/* + Process the next 512 bits of the message stored in the Message_Block array. + + SYNOPSIS + SHA1ProcessMessageBlock() + + DESCRIPTION + Many of the variable names in this code, especially the single + character names, were used because those were the names used in + the publication. +*/ + +/* Constants defined in SHA-1 */ +static const uint32 K[]= +{ + 0x5A827999, + 0x6ED9EBA1, + 0x8F1BBCDC, + 0xCA62C1D6 +}; + + +static void SHA1ProcessMessageBlock(SHA1_CONTEXT *context) +{ + int t; /* Loop counter */ + uint32 temp; /* Temporary word value */ + uint32 W[80]; /* Word sequence */ + uint32 A, B, C, D, E; /* Word buffers */ + int idx; + + /* + Initialize the first 16 words in the array W + */ + + for (t = 0; t < 16; t++) + { + idx=t*4; + W[t] = context->Message_Block[idx] << 24; + W[t] |= context->Message_Block[idx + 1] << 16; + W[t] |= context->Message_Block[idx + 2] << 8; + W[t] |= context->Message_Block[idx + 3]; + } + + + for (t = 16; t < 80; t++) + { + W[t] = SHA1CircularShift(1,W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16]); + } + + A = context->Intermediate_Hash[0]; + B = context->Intermediate_Hash[1]; + C = context->Intermediate_Hash[2]; + D = context->Intermediate_Hash[3]; + E = context->Intermediate_Hash[4]; + + for (t = 0; t < 20; t++) + { + temp= SHA1CircularShift(5,A) + ((B & C) | ((~B) & D)) + E + W[t] + K[0]; + E = D; + D = C; + C = SHA1CircularShift(30,B); + B = A; + A = temp; + } + + for (t = 20; t < 40; t++) + { + temp = SHA1CircularShift(5,A) + (B ^ C ^ D) + E + W[t] + K[1]; + E = D; + D = C; + C = SHA1CircularShift(30,B); + B = A; + A = temp; + } + + for (t = 40; t < 60; t++) + { + temp= (SHA1CircularShift(5,A) + ((B & C) | (B & D) | (C & D)) + E + W[t] + + K[2]); + E = D; + D = C; + C = SHA1CircularShift(30,B); + B = A; + A = temp; + } + + for (t = 60; t < 80; t++) + { + temp = SHA1CircularShift(5,A) + (B ^ C ^ D) + E + W[t] + K[3]; + E = D; + D = C; + C = SHA1CircularShift(30,B); + B = A; + A = temp; + } + + context->Intermediate_Hash[0] += A; + context->Intermediate_Hash[1] += B; + context->Intermediate_Hash[2] += C; + context->Intermediate_Hash[3] += D; + context->Intermediate_Hash[4] += E; + + context->Message_Block_Index = 0; +} + + +/* + Pad message + + SYNOPSIS + SHA1PadMessage() + context: [in/out] The context to pad + + DESCRIPTION + According to the standard, the message must be padded to an even + 512 bits. The first padding bit must be a '1'. The last 64 bits + represent the length of the original message. All bits in between + should be 0. This function will pad the message according to + those rules by filling the Message_Block array accordingly. It + will also call the ProcessMessageBlock function provided + appropriately. When it returns, it can be assumed that the message + digest has been computed. + +*/ + +static void SHA1PadMessage(SHA1_CONTEXT *context) +{ + /* + Check to see if the current message block is too small to hold + the initial padding bits and length. If so, we will pad the + block, process it, and then continue padding into a second + block. + */ + + int i=context->Message_Block_Index; + + if (i > 55) + { + context->Message_Block[i++] = 0x80; + bzero((char*) &context->Message_Block[i], + sizeof(context->Message_Block[0])*(64-i)); + context->Message_Block_Index=64; + + /* This function sets context->Message_Block_Index to zero */ + SHA1ProcessMessageBlock(context); + + bzero((char*) &context->Message_Block[0], + sizeof(context->Message_Block[0])*56); + context->Message_Block_Index=56; + } + else + { + context->Message_Block[i++] = 0x80; + bzero((char*) &context->Message_Block[i], + sizeof(context->Message_Block[0])*(56-i)); + context->Message_Block_Index=56; + } + + /* + Store the message length as the last 8 octets + */ + + context->Message_Block[56] = (int8) (context->Length >> 56); + context->Message_Block[57] = (int8) (context->Length >> 48); + context->Message_Block[58] = (int8) (context->Length >> 40); + context->Message_Block[59] = (int8) (context->Length >> 32); + context->Message_Block[60] = (int8) (context->Length >> 24); + context->Message_Block[61] = (int8) (context->Length >> 16); + context->Message_Block[62] = (int8) (context->Length >> 8); + context->Message_Block[63] = (int8) (context->Length); + + SHA1ProcessMessageBlock(context); +} diff --git a/externals/mysql/mysys/stacktrace.c b/externals/mysql/mysys/stacktrace.c new file mode 100644 index 00000000000..fc7d6e41d95 --- /dev/null +++ b/externals/mysql/mysys/stacktrace.c @@ -0,0 +1,651 @@ +/* Copyright (C) 2000 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#include <my_global.h> +#include <my_stacktrace.h> + +#ifndef __WIN__ +#include <signal.h> +#include <my_pthread.h> +#include <m_string.h> +#ifdef HAVE_STACKTRACE +#include <unistd.h> +#include <strings.h> + +#if HAVE_EXECINFO_H +#include <execinfo.h> +#endif + +#define PTR_SANE(p) ((p) && (char*)(p) >= heap_start && (char*)(p) <= heap_end) + +static char *heap_start; + +#ifdef HAVE_BSS_START +extern char *__bss_start; +#endif + +void my_init_stacktrace() +{ +#ifdef HAVE_BSS_START + heap_start = (char*) &__bss_start; +#endif +} + +void my_safe_print_str(const char* name, const char* val, int max_len) +{ + char *heap_end= (char*) sbrk(0); + fprintf(stderr, "%s at %p ", name, val); + + if (!PTR_SANE(val)) + { + fprintf(stderr, "is an invalid pointer\n"); + return; + } + + fprintf(stderr, "= "); + for (; max_len && PTR_SANE(val) && *val; --max_len) + fputc(*val++, stderr); + fputc('\n', stderr); +} + +#if HAVE_BACKTRACE && (HAVE_BACKTRACE_SYMBOLS || HAVE_BACKTRACE_SYMBOLS_FD) + +#if BACKTRACE_DEMANGLE + +char __attribute__ ((weak)) +*my_demangle(const char *mangled_name __attribute__((unused)), + int *status __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) + fprintf(stderr, "%s(%s+%s\n", addrs[i], demangled, end); + else + fprintf(stderr, "%s\n", addrs[i]); + } +} + +#endif /* BACKTRACE_DEMANGLE */ + +void my_print_stacktrace(uchar* stack_bottom, ulong thread_stack) +{ + void *addrs[128]; + char **strings= NULL; + int n = backtrace(addrs, array_elements(addrs)); + fprintf(stderr, "stack_bottom = %p thread_stack 0x%lx\n", + stack_bottom, thread_stack); +#if BACKTRACE_DEMANGLE + if ((strings= backtrace_symbols(addrs, n))) + { + my_demangle_symbols(strings, n); + free(strings); + } +#endif +#if HAVE_BACKTRACE_SYMBOLS_FD + if (!strings) + { + backtrace_symbols_fd(addrs, n, fileno(stderr)); + } +#endif +} + +#elif defined(TARGET_OS_LINUX) + +#ifdef __i386__ +#define SIGRETURN_FRAME_OFFSET 17 +#endif + +#ifdef __x86_64__ +#define SIGRETURN_FRAME_OFFSET 23 +#endif + +#if defined(__alpha__) && defined(__GNUC__) +/* + The only way to backtrace without a symbol table on alpha + is to find stq fp,N(sp), and the first byte + of the instruction opcode will give us the value of N. From this + we can find where the old value of fp is stored +*/ + +#define MAX_INSTR_IN_FUNC 10000 + +inline uchar** find_prev_fp(uint32* pc, uchar** fp) +{ + int i; + for (i = 0; i < MAX_INSTR_IN_FUNC; ++i,--pc) + { + uchar* p = (uchar*)pc; + if (p[2] == 222 && p[3] == 35) + { + return (uchar**)((uchar*)fp - *(short int*)p); + } + } + return 0; +} + +inline uint32* find_prev_pc(uint32* pc, uchar** fp) +{ + int i; + for (i = 0; i < MAX_INSTR_IN_FUNC; ++i,--pc) + { + char* p = (char*)pc; + if (p[1] == 0 && p[2] == 94 && p[3] == -73) + { + uint32* prev_pc = (uint32*)*((fp+p[0]/sizeof(fp))); + return prev_pc; + } + } + return 0; +} +#endif /* defined(__alpha__) && defined(__GNUC__) */ + +void my_print_stacktrace(uchar* stack_bottom, ulong thread_stack) +{ + uchar** fp; + uint frame_count = 0, sigreturn_frame_count; +#if defined(__alpha__) && defined(__GNUC__) + uint32* pc; +#endif + LINT_INIT(fp); + + +#ifdef __i386__ + __asm __volatile__ ("movl %%ebp,%0" + :"=r"(fp) + :"r"(fp)); +#endif +#ifdef __x86_64__ + __asm __volatile__ ("movq %%rbp,%0" + :"=r"(fp) + :"r"(fp)); +#endif +#if defined(__alpha__) && defined(__GNUC__) + __asm __volatile__ ("mov $30,%0" + :"=r"(fp) + :"r"(fp)); +#endif + if (!fp) + { + fprintf(stderr, "frame pointer is NULL, did you compile with\n\ +-fomit-frame-pointer? Aborting backtrace!\n"); + return; + } + + if (!stack_bottom || (uchar*) stack_bottom > (uchar*) &fp) + { + ulong tmp= min(0x10000,thread_stack); + /* Assume that the stack starts at the previous even 65K */ + stack_bottom= (uchar*) (((ulong) &fp + tmp) & + ~(ulong) 0xFFFF); + fprintf(stderr, "Cannot determine thread, fp=%p, backtrace may not be correct.\n", fp); + } + if (fp > (uchar**) stack_bottom || + fp < (uchar**) stack_bottom - thread_stack) + { + fprintf(stderr, "Bogus stack limit or frame pointer,\ + fp=%p, stack_bottom=%p, thread_stack=%ld, aborting backtrace.\n", + fp, stack_bottom, thread_stack); + return; + } + + fprintf(stderr, "Stack range sanity check OK, backtrace follows:\n"); +#if defined(__alpha__) && defined(__GNUC__) + fprintf(stderr, "Warning: Alpha stacks are difficult -\ + will be taking some wild guesses, stack trace may be incorrect or \ + terminate abruptly\n"); + /* On Alpha, we need to get pc */ + __asm __volatile__ ("bsr %0, do_next; do_next: " + :"=r"(pc) + :"r"(pc)); +#endif /* __alpha__ */ + + /* We are 1 frame above signal frame with NPTL and 2 frames above with LT */ + sigreturn_frame_count = thd_lib_detected == THD_LIB_LT ? 2 : 1; + + while (fp < (uchar**) stack_bottom) + { +#if defined(__i386__) || defined(__x86_64__) + uchar** new_fp = (uchar**)*fp; + fprintf(stderr, "%p\n", frame_count == sigreturn_frame_count ? + *(fp + SIGRETURN_FRAME_OFFSET) : *(fp + 1)); +#endif /* defined(__386__) || defined(__x86_64__) */ + +#if defined(__alpha__) && defined(__GNUC__) + uchar** new_fp = find_prev_fp(pc, fp); + if (frame_count == sigreturn_frame_count - 1) + { + new_fp += 90; + } + + if (fp && pc) + { + pc = find_prev_pc(pc, fp); + if (pc) + fprintf(stderr, "%p\n", pc); + else + { + fprintf(stderr, "Not smart enough to deal with the rest\ + of this stack\n"); + goto end; + } + } + else + { + fprintf(stderr, "Not smart enough to deal with the rest of this stack\n"); + goto end; + } +#endif /* defined(__alpha__) && defined(__GNUC__) */ + if (new_fp <= fp ) + { + fprintf(stderr, "New value of fp=%p failed sanity check,\ + terminating stack trace!\n", new_fp); + goto end; + } + fp = new_fp; + ++frame_count; + } + + fprintf(stderr, "Stack trace seems successful - bottom reached\n"); + +end: + fprintf(stderr, + "Please read http://dev.mysql.com/doc/refman/5.1/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"); +} +#endif /* TARGET_OS_LINUX */ +#endif /* HAVE_STACKTRACE */ + +/* Produce a core for the thread */ +void my_write_core(int sig) +{ + signal(sig, SIG_DFL); +#ifdef HAVE_gcov + /* + For GCOV build, crashing will prevent the writing of code coverage + information from this process, causing gcov output to be incomplete. + So we force the writing of coverage information here before terminating. + */ + extern void __gcov_flush(void); + __gcov_flush(); +#endif + pthread_kill(pthread_self(), sig); +#if defined(P_MYID) && !defined(SCO) + /* On Solaris, the above kill is not enough */ + sigsend(P_PID,P_MYID,sig); +#endif +} + +#else /* __WIN__*/ + +#include <dbghelp.h> +#include <tlhelp32.h> + +/* + Stack tracing on Windows is implemented using Debug Helper library(dbghelp.dll) + We do not redistribute dbghelp and the one comes with older OS (up to Windows 2000) + is missing some important functions like functions StackWalk64 or MinidumpWriteDump. + Hence, we have to load functions at runtime using LoadLibrary/GetProcAddress. +*/ + +typedef DWORD (WINAPI *SymSetOptions_FctType)(DWORD dwOptions); +typedef BOOL (WINAPI *SymGetModuleInfo64_FctType) + (HANDLE,DWORD64,PIMAGEHLP_MODULE64) ; +typedef BOOL (WINAPI *SymGetSymFromAddr64_FctType) + (HANDLE,DWORD64,PDWORD64,PIMAGEHLP_SYMBOL64) ; +typedef BOOL (WINAPI *SymGetLineFromAddr64_FctType) + (HANDLE,DWORD64,PDWORD,PIMAGEHLP_LINE64); +typedef BOOL (WINAPI *SymInitialize_FctType) + (HANDLE,PSTR,BOOL); +typedef BOOL (WINAPI *StackWalk64_FctType) + (DWORD,HANDLE,HANDLE,LPSTACKFRAME64,PVOID,PREAD_PROCESS_MEMORY_ROUTINE64, + PFUNCTION_TABLE_ACCESS_ROUTINE64,PGET_MODULE_BASE_ROUTINE64 , + PTRANSLATE_ADDRESS_ROUTINE64); +typedef BOOL (WINAPI *MiniDumpWriteDump_FctType)( + IN HANDLE hProcess, + IN DWORD ProcessId, + IN HANDLE hFile, + IN MINIDUMP_TYPE DumpType, + IN CONST PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam, OPTIONAL + IN CONST PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam, OPTIONAL + IN CONST PMINIDUMP_CALLBACK_INFORMATION CallbackParam OPTIONAL + ); + +static SymSetOptions_FctType pSymSetOptions; +static SymGetModuleInfo64_FctType pSymGetModuleInfo64; +static SymGetSymFromAddr64_FctType pSymGetSymFromAddr64; +static SymInitialize_FctType pSymInitialize; +static StackWalk64_FctType pStackWalk64; +static SymGetLineFromAddr64_FctType pSymGetLineFromAddr64; +static MiniDumpWriteDump_FctType pMiniDumpWriteDump; + +static EXCEPTION_POINTERS *exception_ptrs; + +#define MODULE64_SIZE_WINXP 576 +#define STACKWALK_MAX_FRAMES 64 + +void my_init_stacktrace() +{ +} + +/* + Dynamically load dbghelp functions +*/ +BOOL init_dbghelp_functions() +{ + static BOOL first_time= TRUE; + static BOOL rc; + HMODULE hDbghlp; + + if(first_time) + { + first_time= FALSE; + hDbghlp= LoadLibrary("dbghelp"); + if(!hDbghlp) + { + rc= FALSE; + return rc; + } + pSymSetOptions= (SymSetOptions_FctType) + GetProcAddress(hDbghlp,"SymSetOptions"); + pSymInitialize= (SymInitialize_FctType) + GetProcAddress(hDbghlp,"SymInitialize"); + pSymGetModuleInfo64= (SymGetModuleInfo64_FctType) + GetProcAddress(hDbghlp,"SymGetModuleInfo64"); + pSymGetLineFromAddr64= (SymGetLineFromAddr64_FctType) + GetProcAddress(hDbghlp,"SymGetLineFromAddr64"); + pSymGetSymFromAddr64=(SymGetSymFromAddr64_FctType) + GetProcAddress(hDbghlp,"SymGetSymFromAddr64"); + pStackWalk64= (StackWalk64_FctType) + GetProcAddress(hDbghlp,"StackWalk64"); + pMiniDumpWriteDump = (MiniDumpWriteDump_FctType) + GetProcAddress(hDbghlp,"MiniDumpWriteDump"); + + rc = (BOOL)(pSymSetOptions && pSymInitialize && pSymGetModuleInfo64 + && pSymGetLineFromAddr64 && pSymGetSymFromAddr64 && pStackWalk64); + } + return rc; +} + +void my_set_exception_pointers(EXCEPTION_POINTERS *ep) +{ + exception_ptrs = ep; +} + + +/* + 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; + + path[0]= '\0'; + /* + 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; + char *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]= '.'; + p= module_dir + 1; + } + *p++= ';'; + *p= '\0'; + + if (!strstr(path, module_dir)) + { + size_t dir_len = strlen(module_dir); + if (size > dir_len) + { + strncat(path, module_dir, size-1); + size -= dir_len; + } + } + } + CloseHandle(hSnap); + } + + /* Add _NT_SYMBOL_PATH, if present. */ + envvar= getenv("_NT_SYMBOL_PATH"); + if(envvar && size) + { + strncat(path, envvar, size-1); + } +} + +#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 || !init_dbghelp_functions()) + return; + + /* Copy context, as stackwalking on original will unwind the stack */ + context = *(exception_ptrs->ContextRecord); + /*Initialize symbols.*/ + pSymSetOptions(SYMOPT_LOAD_LINES|SYMOPT_NO_PROMPTS|SYMOPT_DEFERRED_LOADS|SYMOPT_DEBUG); + get_symbol_path(symbol_path, sizeof(symbol_path)); + pSymInitialize(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(!pStackWalk64(machine, hProcess, hThread, &frame, &context, 0, 0, 0 ,0)) + break; + addr= frame.AddrPC.Offset; + + have_module= pSymGetModuleInfo64(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= pSymGetModuleInfo64(hProcess, addr, &module); + } +#endif + + have_symbol= pSymGetSymFromAddr64(hProcess, addr, &function_offset, + &(package.sym)); + have_source= pSymGetLineFromAddr64(hProcess, addr, &line_offset, &line); + + fprintf(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; + fprintf(stderr, "%s!", base_image_name); + } + if(have_symbol) + fprintf(stderr, "%s()", package.sym.Name); + else if(have_module) + fprintf(stderr, "???"); + + if(have_source) + { + char *base_file_name= strrchr(line.FileName, '\\'); + if(base_file_name) + base_file_name++; + else + base_file_name= line.FileName; + fprintf(stderr,"[%s:%u]", base_file_name, line.LineNumber); + } + fprintf(stderr, "\n"); + } + fflush(stderr); +} + + +/* + 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"; + MINIDUMP_EXCEPTION_INFORMATION info; + HANDLE hFile; + + if(!exception_ptrs || !init_dbghelp_functions() || !pMiniDumpWriteDump) + return; + + info.ExceptionPointers= exception_ptrs; + info.ClientPointers= FALSE; + info.ThreadId= GetCurrentThreadId(); + + if(GetModuleFileName(NULL, path, sizeof(path))) + { + _splitpath(path, NULL, NULL,dump_fname,NULL); + strncat(dump_fname, ".dmp", sizeof(dump_fname)); + } + + hFile= CreateFile(dump_fname, GENERIC_WRITE, 0, 0, CREATE_ALWAYS, + FILE_ATTRIBUTE_NORMAL, 0); + if(hFile) + { + /* Create minidump */ + MINIDUMP_TYPE dump_type = (MINIDUMP_TYPE) + (MiniDumpWithDataSegs|MiniDumpWithPrivateReadWriteMemory); + + if(pMiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), + hFile, dump_type, &info, 0, 0)) + { + fprintf(stderr, "Minidump written to %s\n", + _fullpath(path, dump_fname, sizeof(path)) ? path : dump_fname); + } + else + { + fprintf(stderr,"MiniDumpWriteDump() failed, last error %u\n", + GetLastError()); + } + CloseHandle(hFile); + } + else + { + fprintf(stderr, "CreateFile(%s) failed, last error %u\n", dump_fname, + GetLastError()); + } + fflush(stderr); +} + + +void my_safe_print_str(const char *name, const char *val, int len) +{ + fprintf(stderr,"%s at %p", name, val); + __try + { + fprintf(stderr,"=%.*s\n", len, val); + } + __except(EXCEPTION_EXECUTE_HANDLER) + { + fprintf(stderr,"is an invalid string pointer\n"); + } +} +#endif /*__WIN__*/ diff --git a/externals/mysql/mysys/string.c b/externals/mysql/mysys/string.c new file mode 100644 index 00000000000..b234a589406 --- /dev/null +++ b/externals/mysql/mysys/string.c @@ -0,0 +1,185 @@ +/* Copyright (C) 2000 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +/* + Code for handling strings with can grow dynamicly. + Copyright Monty Program KB. + By monty. +*/ + +#include "mysys_priv.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) +{ + uint 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(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(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(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) + { + uint new_length=(str->length+length+str->alloc_increment)/ + str->alloc_increment; + new_length*=str->alloc_increment; + if (!(new_ptr=(char*) my_realloc(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 __WIN__ + const char *quote_str= "\""; + const uint quote_len= 1; +#else + const char *quote_str= "\'"; + const uint quote_len= 1; +#endif /* __WIN__ */ + 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, 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, 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) +{ + if (str->str) + { + my_free(str->str,MYF(MY_WME)); + str->str=0; + } +} diff --git a/externals/mysql/mysys/test_fn.c b/externals/mysql/mysys/test_fn.c new file mode 100644 index 00000000000..249cc878390 --- /dev/null +++ b/externals/mysql/mysys/test_fn.c @@ -0,0 +1,69 @@ +/* Copyright (C) 2000 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#include "mysys_priv.h" + +const char *test_names[]= +{ + "/usr/my/include/srclib/myfunc/dbug/test", + "test", + "dbug/test", + "/usr/my/srclib/myfunc/dbug/test", + "/usr/monty/oldcopy/jazz/setupp.frm", + "~/monty.tst", + "~/dbug/monty.tst", + "./hejsan", + "./dbug/test", + "../dbug/test", + "../myfunc/test", + "../../monty/rutedit", + "/usr/monty//usr/monty/rutedit", + "/usr/./monty/rutedit", + "/usr/my/../monty/rutedit", + "/usr/my/~/rutedit", + "~/../my", + "~/../my/srclib/myfunc/test", + "~/../my/srclib/myfunc/./dbug/test", + "/../usr/my/srclib/dbug", + "c/../my", + "/c/../my", + NullS, +}; + +int main(int argc __attribute__((unused)), char **argv) +{ + const char **pos; + char buff[FN_REFLEN],buff2[FN_REFLEN]; + DBUG_ENTER ("main"); + DBUG_PROCESS (argv[0]); + MY_INIT(argv[0]); + + if (argv[1] && argv[1][1] == '#') + DBUG_PUSH(argv[1]+2); + + for (pos=test_names; *pos ; pos++) + { + printf("org : '%s'\n",*pos); + printf("pack: '%s'\n",fn_format(buff,*pos,"","",8)); + printf("unpack: '%s'\n",fn_format(buff2,*pos,"","",4)); + unpack_filename(buff,buff); + if (strcmp(buff,buff2) != 0) + { + printf("error on cmp: '%s' != '%s'\n",buff,buff2); + } + puts(""); + } + DBUG_RETURN(0); +} diff --git a/externals/mysql/mysys/test_xml.c b/externals/mysql/mysys/test_xml.c new file mode 100644 index 00000000000..0cb10e1c8d9 --- /dev/null +++ b/externals/mysql/mysys/test_xml.c @@ -0,0 +1,104 @@ +/* Copyright (C) 2000 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <fcntl.h> +#include "my_xml.h" + +static void mstr(char *str,const char *src,uint l1,uint l2) +{ + l1 = l1<l2 ? l1 : l2; + memcpy(str,src,l1); + str[l1]='\0'; +} + +static int dstr(MY_XML_PARSER *st,const char *attr, uint len) +{ + char str[1024]; + + mstr(str,attr,len,sizeof(str)-1); + printf("VALUE '%s'\n",str); + return MY_XML_OK; +} + +static int bstr(MY_XML_PARSER *st,const char *attr, uint len) +{ + char str[1024]; + + mstr(str,attr,len,sizeof(str)-1); + printf("ENTER %s\n",str); + return MY_XML_OK; +} + + +static int estr(MY_XML_PARSER *st,const char *attr, uint len) +{ + char str[1024]; + + mstr(str,attr,len,sizeof(str)-1); + printf("LEAVE %s\n",str); + return MY_XML_OK; +} + +static void usage(const char *prog) +{ + printf("Usage:\n"); + printf("%s xmlfile\n",prog); +} + +int main(int ac, char **av) +{ + char str[1024*64]=""; + const char *fn; + int f; + uint len; + MY_XML_PARSER p; + + if (ac<2) + { + usage(av[0]); + return 0; + } + + fn=av[1]?av[1]:"test.xml"; + if ((f=open(fn,O_RDONLY))<0) + { + fprintf(stderr,"Err '%s'\n",fn); + return 1; + } + + len=read(f,str,sizeof(str)-1); + str[len]='\0'; + + my_xml_parser_create(&p); + + my_xml_set_enter_handler(&p,bstr); + my_xml_set_value_handler(&p,dstr); + my_xml_set_leave_handler(&p,estr); + + if (MY_XML_OK!=(f=my_xml_parse(&p,str,len))) + { + printf("ERROR at line %d pos %d '%s'\n", + my_xml_error_lineno(&p)+1, + my_xml_error_pos(&p), + my_xml_error_string(&p)); + } + + my_xml_parser_free(&p); + + return 0; +} diff --git a/externals/mysql/mysys/thr_alarm.c b/externals/mysql/mysys/thr_alarm.c new file mode 100644 index 00000000000..e33c08dffcf --- /dev/null +++ b/externals/mysql/mysys/thr_alarm.c @@ -0,0 +1,977 @@ +/* Copyright (C) 2000 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +/* To avoid problems with alarms in debug code, we disable DBUG here */ +#define FORCE_DBUG_OFF +#include <my_global.h> + +#if defined(THREAD) && !defined(DONT_USE_THR_ALARM) +#include <errno.h> +#include <my_pthread.h> +#include <signal.h> +#include <my_sys.h> +#include <m_string.h> +#include <queues.h> +#include "thr_alarm.h" + +#ifdef HAVE_SYS_SELECT_H +#include <sys/select.h> /* AIX needs this for fd_set */ +#endif + +#ifndef ETIME +#define ETIME ETIMEDOUT +#endif + +uint thr_client_alarm; +static int alarm_aborted=1; /* No alarm thread */ +my_bool thr_alarm_inited= 0; +volatile my_bool alarm_thread_running= 0; +time_t next_alarm_expire_time= ~ (time_t) 0; +static sig_handler process_alarm_part2(int sig); + +#if !defined(__WIN__) + +static pthread_mutex_t LOCK_alarm; +static pthread_cond_t COND_alarm; +static sigset_t full_signal_set; +static QUEUE alarm_queue; +static uint max_used_alarms=0; +pthread_t alarm_thread; + +#ifdef USE_ALARM_THREAD +static void *alarm_handler(void *arg); +#define reschedule_alarms() pthread_cond_signal(&COND_alarm) +#else +#define reschedule_alarms() pthread_kill(alarm_thread,THR_SERVER_ALARM) +#endif + +static sig_handler thread_alarm(int sig __attribute__((unused))); + +static int compare_ulong(void *not_used __attribute__((unused)), + uchar *a_ptr,uchar* b_ptr) +{ + ulong a=*((ulong*) a_ptr),b= *((ulong*) b_ptr); + return (a < b) ? -1 : (a == b) ? 0 : 1; +} + +void init_thr_alarm(uint max_alarms) +{ + sigset_t s; + DBUG_ENTER("init_thr_alarm"); + alarm_aborted=0; + next_alarm_expire_time= ~ (time_t) 0; + init_queue(&alarm_queue,max_alarms+1,offsetof(ALARM,expire_time),0, + compare_ulong,NullS); + sigfillset(&full_signal_set); /* Neaded to block signals */ + pthread_mutex_init(&LOCK_alarm,MY_MUTEX_INIT_FAST); + pthread_cond_init(&COND_alarm,NULL); + if (thd_lib_detected == THD_LIB_LT) + thr_client_alarm= SIGALRM; + else + thr_client_alarm= SIGUSR1; +#ifndef USE_ALARM_THREAD + if (thd_lib_detected != THD_LIB_LT) +#endif + { + my_sigset(thr_client_alarm, thread_alarm); + } + sigemptyset(&s); + sigaddset(&s, THR_SERVER_ALARM); + alarm_thread=pthread_self(); +#if defined(USE_ALARM_THREAD) + { + pthread_attr_t thr_attr; + pthread_attr_init(&thr_attr); + pthread_attr_setscope(&thr_attr,PTHREAD_SCOPE_PROCESS); + pthread_attr_setdetachstate(&thr_attr,PTHREAD_CREATE_DETACHED); + pthread_attr_setstacksize(&thr_attr,8196); + pthread_create(&alarm_thread,&thr_attr,alarm_handler,NULL); + pthread_attr_destroy(&thr_attr); + } +#elif defined(USE_ONE_SIGNAL_HAND) + pthread_sigmask(SIG_BLOCK, &s, NULL); /* used with sigwait() */ + if (thd_lib_detected == THD_LIB_LT) + { + my_sigset(thr_client_alarm, process_alarm); /* Linuxthreads */ + pthread_sigmask(SIG_UNBLOCK, &s, NULL); + } +#else + my_sigset(THR_SERVER_ALARM, process_alarm); + pthread_sigmask(SIG_UNBLOCK, &s, NULL); +#endif + DBUG_VOID_RETURN; +} + + +void resize_thr_alarm(uint max_alarms) +{ + pthread_mutex_lock(&LOCK_alarm); + /* + It's ok not to shrink the queue as there may be more pending alarms than + than max_alarms + */ + if (alarm_queue.elements < max_alarms) + resize_queue(&alarm_queue,max_alarms+1); + pthread_mutex_unlock(&LOCK_alarm); +} + + +/* + Request alarm after sec seconds. + + SYNOPSIS + thr_alarm() + alrm Pointer to alarm detection + alarm_data Structure to store in alarm queue + + NOTES + This function can't be called from the alarm-handling thread. + + RETURN VALUES + 0 ok + 1 If no more alarms are allowed (aborted by process) + + Stores in first argument a pointer to a non-zero int which is set to 0 + when the alarm has been given +*/ + +my_bool thr_alarm(thr_alarm_t *alrm, uint sec, ALARM *alarm_data) +{ + time_t now; +#ifndef USE_ONE_SIGNAL_HAND + sigset_t old_mask; +#endif + my_bool reschedule; + struct st_my_thread_var *current_my_thread_var= my_thread_var; + DBUG_ENTER("thr_alarm"); + DBUG_PRINT("enter",("thread: %s sec: %d",my_thread_name(),sec)); + + now= my_time(0); +#ifndef USE_ONE_SIGNAL_HAND + pthread_sigmask(SIG_BLOCK,&full_signal_set,&old_mask); +#endif + pthread_mutex_lock(&LOCK_alarm); /* Lock from threads & alarms */ + if (alarm_aborted > 0) + { /* No signal thread */ + DBUG_PRINT("info", ("alarm aborted")); + *alrm= 0; /* No alarm */ + pthread_mutex_unlock(&LOCK_alarm); +#ifndef USE_ONE_SIGNAL_HAND + pthread_sigmask(SIG_SETMASK,&old_mask,NULL); +#endif + DBUG_RETURN(1); + } + if (alarm_aborted < 0) + sec= 1; /* Abort mode */ + + if (alarm_queue.elements >= max_used_alarms) + { + if (alarm_queue.elements == alarm_queue.max_elements) + { + DBUG_PRINT("info", ("alarm queue full")); + fprintf(stderr,"Warning: thr_alarm queue is full\n"); + *alrm= 0; /* No alarm */ + pthread_mutex_unlock(&LOCK_alarm); +#ifndef USE_ONE_SIGNAL_HAND + pthread_sigmask(SIG_SETMASK,&old_mask,NULL); +#endif + DBUG_RETURN(1); + } + max_used_alarms=alarm_queue.elements+1; + } + reschedule= (ulong) next_alarm_expire_time > (ulong) now + sec; + if (!alarm_data) + { + if (!(alarm_data=(ALARM*) my_malloc(sizeof(ALARM),MYF(MY_WME)))) + { + DBUG_PRINT("info", ("failed my_malloc()")); + *alrm= 0; /* No alarm */ + pthread_mutex_unlock(&LOCK_alarm); +#ifndef USE_ONE_SIGNAL_HAND + pthread_sigmask(SIG_SETMASK,&old_mask,NULL); +#endif + DBUG_RETURN(1); + } + alarm_data->malloced=1; + } + else + alarm_data->malloced=0; + alarm_data->expire_time=now+sec; + alarm_data->alarmed=0; + alarm_data->thread= current_my_thread_var->pthread_self; + alarm_data->thread_id= current_my_thread_var->id; + queue_insert(&alarm_queue,(uchar*) alarm_data); + + /* Reschedule alarm if the current one has more than sec left */ + if (reschedule) + { + DBUG_PRINT("info", ("reschedule")); + if (pthread_equal(pthread_self(),alarm_thread)) + { + alarm(sec); /* purecov: inspected */ + next_alarm_expire_time= now + sec; + } + else + reschedule_alarms(); /* Reschedule alarms */ + } + pthread_mutex_unlock(&LOCK_alarm); +#ifndef USE_ONE_SIGNAL_HAND + pthread_sigmask(SIG_SETMASK,&old_mask,NULL); +#endif + (*alrm)= &alarm_data->alarmed; + DBUG_RETURN(0); +} + + +/* + Remove alarm from list of alarms +*/ + +void thr_end_alarm(thr_alarm_t *alarmed) +{ + ALARM *alarm_data; +#ifndef USE_ONE_SIGNAL_HAND + sigset_t old_mask; +#endif + uint i, found=0; + DBUG_ENTER("thr_end_alarm"); + +#ifndef USE_ONE_SIGNAL_HAND + pthread_sigmask(SIG_BLOCK,&full_signal_set,&old_mask); +#endif + pthread_mutex_lock(&LOCK_alarm); + + alarm_data= (ALARM*) ((uchar*) *alarmed - offsetof(ALARM,alarmed)); + for (i=0 ; i < alarm_queue.elements ; i++) + { + if ((ALARM*) queue_element(&alarm_queue,i) == alarm_data) + { + queue_remove(&alarm_queue,i),MYF(0); + if (alarm_data->malloced) + my_free((uchar*) alarm_data,MYF(0)); + found++; +#ifdef DBUG_OFF + break; +#endif + } + } + DBUG_ASSERT(!*alarmed || found == 1); + if (!found) + { + if (*alarmed) + fprintf(stderr,"Warning: Didn't find alarm 0x%lx in queue of %d alarms\n", + (long) *alarmed, alarm_queue.elements); + DBUG_PRINT("warning",("Didn't find alarm %p in queue\n", + *alarmed)); + } + pthread_mutex_unlock(&LOCK_alarm); +#ifndef USE_ONE_SIGNAL_HAND + pthread_sigmask(SIG_SETMASK,&old_mask,NULL); +#endif + DBUG_VOID_RETURN; +} + +/* + Come here when some alarm in queue is due. + Mark all alarms with are finnished in list. + Shedule alarms to be sent again after 1-10 sec (many alarms at once) + If alarm_aborted is set then all alarms are given and resent + every second. +*/ + +sig_handler process_alarm(int sig __attribute__((unused))) +{ + sigset_t old_mask; +/* + This must be first as we can't call DBUG inside an alarm for a normal thread +*/ + + if (thd_lib_detected == THD_LIB_LT && + !pthread_equal(pthread_self(),alarm_thread)) + { +#if defined(MAIN) && !defined(__bsdi__) + printf("thread_alarm in process_alarm\n"); fflush(stdout); +#endif +#ifdef DONT_REMEMBER_SIGNAL + my_sigset(thr_client_alarm, process_alarm); /* int. thread system calls */ +#endif + return; + } + + /* + We have to do do the handling of the alarm in a sub function, + because otherwise we would get problems with two threads calling + DBUG_... functions at the same time (as two threads may call + process_alarm() at the same time + */ + +#ifndef USE_ALARM_THREAD + pthread_sigmask(SIG_SETMASK,&full_signal_set,&old_mask); + pthread_mutex_lock(&LOCK_alarm); +#endif + process_alarm_part2(sig); +#ifndef USE_ALARM_THREAD +#if defined(DONT_REMEMBER_SIGNAL) && !defined(USE_ONE_SIGNAL_HAND) + my_sigset(THR_SERVER_ALARM,process_alarm); +#endif + pthread_mutex_unlock(&LOCK_alarm); + pthread_sigmask(SIG_SETMASK,&old_mask,NULL); +#endif + return; +} + + +static sig_handler process_alarm_part2(int sig __attribute__((unused))) +{ + ALARM *alarm_data; + DBUG_ENTER("process_alarm"); + DBUG_PRINT("info",("sig: %d active alarms: %d",sig,alarm_queue.elements)); + +#if defined(MAIN) && !defined(__bsdi__) + printf("process_alarm\n"); fflush(stdout); +#endif + if (alarm_queue.elements) + { + if (alarm_aborted) + { + uint i; + for (i=0 ; i < alarm_queue.elements ;) + { + alarm_data=(ALARM*) queue_element(&alarm_queue,i); + alarm_data->alarmed=1; /* Info to thread */ + if (pthread_equal(alarm_data->thread,alarm_thread) || + pthread_kill(alarm_data->thread, thr_client_alarm)) + { +#ifdef MAIN + printf("Warning: pthread_kill couldn't find thread!!!\n"); +#endif + queue_remove(&alarm_queue,i); /* No thread. Remove alarm */ + } + else + i++; /* Signal next thread */ + } +#ifndef USE_ALARM_THREAD + if (alarm_queue.elements) + alarm(1); /* Signal soon again */ +#endif + } + else + { + ulong now=(ulong) my_time(0); + ulong next=now+10-(now%10); + while ((alarm_data=(ALARM*) queue_top(&alarm_queue))->expire_time <= now) + { + alarm_data->alarmed=1; /* Info to thread */ + DBUG_PRINT("info",("sending signal to waiting thread")); + if (pthread_equal(alarm_data->thread,alarm_thread) || + pthread_kill(alarm_data->thread, thr_client_alarm)) + { +#ifdef MAIN + printf("Warning: pthread_kill couldn't find thread!!!\n"); +#endif + queue_remove(&alarm_queue,0); /* No thread. Remove alarm */ + if (!alarm_queue.elements) + break; + } + else + { + alarm_data->expire_time=next; + queue_replaced(&alarm_queue); + } + } +#ifndef USE_ALARM_THREAD + if (alarm_queue.elements) + { +#ifdef __bsdi__ + alarm(0); /* Remove old alarm */ +#endif + alarm((uint) (alarm_data->expire_time-now)); + next_alarm_expire_time= alarm_data->expire_time; + } +#endif + } + } + else + { + /* + Ensure that next time we call thr_alarm(), we will schedule a new alarm + */ + next_alarm_expire_time= ~(time_t) 0; + } + DBUG_VOID_RETURN; +} + + +/* + Schedule all alarms now and optionally free all structures + + SYNPOSIS + end_thr_alarm() + free_structures Set to 1 if we should free memory used for + the alarm queue. + When we call this we should KNOW that there + is no active alarms + IMPLEMENTATION + Set alarm_abort to -1 which will change the behavior of alarms as follows: + - All old alarms will be rescheduled at once + - All new alarms will be rescheduled to one second +*/ + +void end_thr_alarm(my_bool free_structures) +{ + DBUG_ENTER("end_thr_alarm"); + if (alarm_aborted != 1) /* If memory not freed */ + { + pthread_mutex_lock(&LOCK_alarm); + DBUG_PRINT("info",("Resheduling %d waiting alarms",alarm_queue.elements)); + alarm_aborted= -1; /* mark aborted */ + if (alarm_queue.elements || (alarm_thread_running && free_structures)) + { + if (pthread_equal(pthread_self(),alarm_thread)) + alarm(1); /* Shut down everything soon */ + else + reschedule_alarms(); + } + if (free_structures) + { + struct timespec abstime; + + DBUG_ASSERT(!alarm_queue.elements); + + /* Wait until alarm thread dies */ + set_timespec(abstime, 10); /* Wait up to 10 seconds */ + while (alarm_thread_running) + { + int error= pthread_cond_timedwait(&COND_alarm, &LOCK_alarm, &abstime); + if (error == ETIME || error == ETIMEDOUT) + break; /* Don't wait forever */ + } + delete_queue(&alarm_queue); + alarm_aborted= 1; + pthread_mutex_unlock(&LOCK_alarm); + if (!alarm_thread_running) /* Safety */ + { + pthread_mutex_destroy(&LOCK_alarm); + pthread_cond_destroy(&COND_alarm); + } + } + else + pthread_mutex_unlock(&LOCK_alarm); + } + DBUG_VOID_RETURN; +} + + +/* + Remove another thread from the alarm +*/ + +void thr_alarm_kill(my_thread_id thread_id) +{ + uint i; + if (alarm_aborted) + return; + pthread_mutex_lock(&LOCK_alarm); + for (i=0 ; i < alarm_queue.elements ; i++) + { + if (((ALARM*) queue_element(&alarm_queue,i))->thread_id == thread_id) + { + ALARM *tmp=(ALARM*) queue_remove(&alarm_queue,i); + tmp->expire_time=0; + queue_insert(&alarm_queue,(uchar*) tmp); + reschedule_alarms(); + break; + } + } + pthread_mutex_unlock(&LOCK_alarm); +} + + +void thr_alarm_info(ALARM_INFO *info) +{ + pthread_mutex_lock(&LOCK_alarm); + info->next_alarm_time= 0; + info->max_used_alarms= max_used_alarms; + if ((info->active_alarms= alarm_queue.elements)) + { + ulong now=(ulong) my_time(0); + long time_diff; + ALARM *alarm_data= (ALARM*) queue_top(&alarm_queue); + time_diff= (long) (alarm_data->expire_time - now); + info->next_alarm_time= (ulong) (time_diff < 0 ? 0 : time_diff); + } + pthread_mutex_unlock(&LOCK_alarm); +} + +/* + This is here for thread to get interruptet from read/write/fcntl + ARGSUSED +*/ + + +static sig_handler thread_alarm(int sig) +{ +#ifdef MAIN + printf("thread_alarm\n"); fflush(stdout); +#endif +#ifdef DONT_REMEMBER_SIGNAL + my_sigset(sig,thread_alarm); /* int. thread system calls */ +#endif +} + + +#ifdef HAVE_TIMESPEC_TS_SEC +#define tv_sec ts_sec +#define tv_nsec ts_nsec +#endif + +/* set up a alarm thread with uses 'sleep' to sleep between alarms */ + +#ifdef USE_ALARM_THREAD +static void *alarm_handler(void *arg __attribute__((unused))) +{ + int error; + struct timespec abstime; +#ifdef MAIN + puts("Starting alarm thread"); +#endif + my_thread_init(); + alarm_thread_running= 1; + pthread_mutex_lock(&LOCK_alarm); + for (;;) + { + if (alarm_queue.elements) + { + ulong sleep_time,now= my_time(0); + if (alarm_aborted) + sleep_time=now+1; + else + sleep_time= ((ALARM*) queue_top(&alarm_queue))->expire_time; + if (sleep_time > now) + { + abstime.tv_sec=sleep_time; + abstime.tv_nsec=0; + next_alarm_expire_time= sleep_time; + if ((error=pthread_cond_timedwait(&COND_alarm,&LOCK_alarm,&abstime)) && + error != ETIME && error != ETIMEDOUT) + { +#ifdef MAIN + printf("Got error: %d from ptread_cond_timedwait (errno: %d)\n", + error,errno); +#endif + } + } + } + else if (alarm_aborted == -1) + break; + else + { + next_alarm_expire_time= ~ (time_t) 0; + if ((error=pthread_cond_wait(&COND_alarm,&LOCK_alarm))) + { +#ifdef MAIN + printf("Got error: %d from ptread_cond_wait (errno: %d)\n", + error,errno); +#endif + } + } + process_alarm(0); + } + bzero((char*) &alarm_thread,sizeof(alarm_thread)); /* For easy debugging */ + alarm_thread_running= 0; + pthread_cond_signal(&COND_alarm); + pthread_mutex_unlock(&LOCK_alarm); + pthread_exit(0); + return 0; /* Impossible */ +} +#endif /* USE_ALARM_THREAD */ + +/***************************************************************************** + thr_alarm for win95 +*****************************************************************************/ + +#else /* __WIN__ */ + +void thr_alarm_kill(my_thread_id thread_id) +{ + /* Can't do this yet */ +} + +sig_handler process_alarm(int sig __attribute__((unused))) +{ + /* Can't do this yet */ +} + + +my_bool thr_alarm(thr_alarm_t *alrm, uint sec, ALARM *alarm) +{ + (*alrm)= &alarm->alarmed; + if (alarm_aborted) + { + alarm->alarmed.crono=0; + return 1; + } + if (!(alarm->alarmed.crono=SetTimer((HWND) NULL,0, sec*1000, + (TIMERPROC) NULL))) + return 1; + return 0; +} + + +my_bool thr_got_alarm(thr_alarm_t *alrm_ptr) +{ + thr_alarm_t alrm= *alrm_ptr; + MSG msg; + if (alrm->crono) + { + PeekMessage(&msg,NULL,WM_TIMER,WM_TIMER,PM_REMOVE) ; + if (msg.message == WM_TIMER || alarm_aborted) + { + KillTimer(NULL, alrm->crono); + alrm->crono = 0; + } + } + return !alrm->crono || alarm_aborted; +} + + +void thr_end_alarm(thr_alarm_t *alrm_ptr) +{ + thr_alarm_t alrm= *alrm_ptr; + /* alrm may be zero if thr_alarm aborted with an error */ + if (alrm && alrm->crono) + + { + KillTimer(NULL, alrm->crono); + alrm->crono = 0; + } +} + +void end_thr_alarm(my_bool free_structures) +{ + DBUG_ENTER("end_thr_alarm"); + alarm_aborted=1; /* No more alarms */ + DBUG_VOID_RETURN; +} + +void init_thr_alarm(uint max_alarm) +{ + DBUG_ENTER("init_thr_alarm"); + alarm_aborted=0; /* Yes, Gimmie alarms */ + DBUG_VOID_RETURN; +} + +void thr_alarm_info(ALARM_INFO *info) +{ + bzero((char*) info, sizeof(*info)); +} + +void resize_thr_alarm(uint max_alarms) +{ +} + +#endif /* __WIN__ */ + +#endif /* THREAD */ + + +/**************************************************************************** + Handling of test case (when compiled with -DMAIN) +***************************************************************************/ + +#ifdef MAIN +#if defined(THREAD) && !defined(DONT_USE_THR_ALARM) + +static pthread_cond_t COND_thread_count; +static pthread_mutex_t LOCK_thread_count; +static uint thread_count; + +#ifdef HPUX10 +typedef int * fd_set_ptr; +#else +typedef fd_set * fd_set_ptr; +#endif /* HPUX10 */ + +static void *test_thread(void *arg) +{ + int i,param=*((int*) arg),wait_time,retry; + time_t start_time; + thr_alarm_t got_alarm; + fd_set fd; + FD_ZERO(&fd); + my_thread_init(); + printf("Thread %d (%s) started\n",param,my_thread_name()); fflush(stdout); + for (i=1 ; i <= 10 ; i++) + { + wait_time=param ? 11-i : i; + start_time= my_time(0); + if (thr_alarm(&got_alarm,wait_time,0)) + { + printf("Thread: %s Alarms aborted\n",my_thread_name()); + break; + } + if (wait_time == 3) + { + printf("Thread: %s Simulation of no alarm needed\n",my_thread_name()); + fflush(stdout); + } + else + { + for (retry=0 ; !thr_got_alarm(&got_alarm) && retry < 10 ; retry++) + { + printf("Thread: %s Waiting %d sec\n",my_thread_name(),wait_time); + select(0,(fd_set_ptr) &fd,0,0,0); + } + if (!thr_got_alarm(&got_alarm)) + { + printf("Thread: %s didn't get an alarm. Aborting!\n", + my_thread_name()); + break; + } + if (wait_time == 7) + { /* Simulate alarm-miss */ + fd_set readFDs; + uint max_connection=fileno(stdin); + FD_ZERO(&readFDs); + FD_SET(max_connection,&readFDs); + retry=0; + for (;;) + { + printf("Thread: %s Simulating alarm miss\n",my_thread_name()); + fflush(stdout); + if (select(max_connection+1, (fd_set_ptr) &readFDs,0,0,0) < 0) + { + if (errno == EINTR) + break; /* Got new interrupt */ + printf("Got errno: %d from select. Retrying..\n",errno); + if (retry++ >= 3) + { + printf("Warning: Interrupt of select() doesn't set errno!\n"); + break; + } + } + else /* This shouldn't happen */ + { + if (!FD_ISSET(max_connection,&readFDs)) + { + printf("Select interrupted, but errno not set\n"); + fflush(stdout); + if (retry++ >= 3) + break; + continue; + } + (void) getchar(); /* Somebody was playing */ + } + } + } + } + printf("Thread: %s Slept for %d (%d) sec\n",my_thread_name(), + (int) (my_time(0)-start_time), wait_time); fflush(stdout); + thr_end_alarm(&got_alarm); + fflush(stdout); + } + pthread_mutex_lock(&LOCK_thread_count); + thread_count--; + pthread_cond_signal(&COND_thread_count); /* Tell main we are ready */ + pthread_mutex_unlock(&LOCK_thread_count); + free((uchar*) arg); + return 0; +} + +#ifdef USE_ONE_SIGNAL_HAND +static sig_handler print_signal_warning(int sig) +{ + printf("Warning: Got signal %d from thread %s\n",sig,my_thread_name()); + fflush(stdout); +#ifdef DONT_REMEMBER_SIGNAL + my_sigset(sig,print_signal_warning); /* int. thread system calls */ +#endif + if (sig == SIGALRM) + alarm(2); /* reschedule alarm */ +} +#endif /* USE_ONE_SIGNAL_HAND */ + + +static void *signal_hand(void *arg __attribute__((unused))) +{ + sigset_t set; + int sig,error,err_count=0;; + + my_thread_init(); + pthread_detach_this_thread(); + init_thr_alarm(10); /* Setup alarm handler */ + pthread_mutex_lock(&LOCK_thread_count); /* Required by bsdi */ + pthread_cond_signal(&COND_thread_count); /* Tell main we are ready */ + pthread_mutex_unlock(&LOCK_thread_count); + + sigemptyset(&set); /* Catch all signals */ + sigaddset(&set,SIGINT); + sigaddset(&set,SIGQUIT); + sigaddset(&set,SIGTERM); + sigaddset(&set,SIGHUP); +#ifdef SIGTSTP + sigaddset(&set,SIGTSTP); +#endif +#ifdef USE_ONE_SIGNAL_HAND + sigaddset(&set,THR_SERVER_ALARM); /* For alarms */ + puts("Starting signal and alarm handling thread"); +#else + puts("Starting signal handling thread"); +#endif + printf("server alarm: %d thread alarm: %d\n", + THR_SERVER_ALARM, thr_client_alarm); + DBUG_PRINT("info",("Starting signal and alarm handling thread")); + for(;;) + { + while ((error=my_sigwait(&set,&sig)) == EINTR) + printf("sigwait restarted\n"); + if (error) + { + fprintf(stderr,"Got error %d from sigwait\n",error); + if (err_count++ > 5) + exit(1); /* Too many errors in test */ + continue; + } +#ifdef USE_ONE_SIGNAL_HAND + if (sig != THR_SERVER_ALARM) +#endif + printf("Main thread: Got signal %d\n",sig); + switch (sig) { + case SIGINT: + case SIGQUIT: + case SIGTERM: + case SIGHUP: + printf("Aborting nicely\n"); + end_thr_alarm(0); + break; +#ifdef SIGTSTP + case SIGTSTP: + printf("Aborting\n"); + exit(1); + return 0; /* Keep some compilers happy */ +#endif +#ifdef USE_ONE_SIGNAL_HAND + case THR_SERVER_ALARM: + process_alarm(sig); + break; +#endif + } + } +} + + +int main(int argc __attribute__((unused)),char **argv __attribute__((unused))) +{ + pthread_t tid; + pthread_attr_t thr_attr; + int i,*param,error; + sigset_t set; + ALARM_INFO alarm_info; + MY_INIT(argv[0]); + + if (argc > 1 && argv[1][0] == '-' && argv[1][1] == '#') + { + DBUG_PUSH(argv[1]+2); + } + pthread_mutex_init(&LOCK_thread_count,MY_MUTEX_INIT_FAST); + pthread_cond_init(&COND_thread_count,NULL); + + /* Start a alarm handling thread */ + sigemptyset(&set); + sigaddset(&set,SIGINT); + sigaddset(&set,SIGQUIT); + sigaddset(&set,SIGTERM); + sigaddset(&set,SIGHUP); + signal(SIGTERM,SIG_DFL); /* If it's blocked by parent */ +#ifdef SIGTSTP + sigaddset(&set,SIGTSTP); +#endif + sigaddset(&set,THR_SERVER_ALARM); + sigdelset(&set, thr_client_alarm); + (void) pthread_sigmask(SIG_SETMASK,&set,NULL); +#ifdef NOT_USED + sigemptyset(&set); + sigaddset(&set, thr_client_alarm); + pthread_sigmask(SIG_UNBLOCK, &set, (sigset_t*) 0); +#endif + + pthread_attr_init(&thr_attr); + pthread_attr_setscope(&thr_attr,PTHREAD_SCOPE_PROCESS); + pthread_attr_setdetachstate(&thr_attr,PTHREAD_CREATE_DETACHED); + pthread_attr_setstacksize(&thr_attr,65536L); + + /* Start signal thread and wait for it to start */ + pthread_mutex_lock(&LOCK_thread_count); + pthread_create(&tid,&thr_attr,signal_hand,NULL); + pthread_cond_wait(&COND_thread_count,&LOCK_thread_count); + pthread_mutex_unlock(&LOCK_thread_count); + DBUG_PRINT("info",("signal thread created")); + + thr_setconcurrency(3); + pthread_attr_setscope(&thr_attr,PTHREAD_SCOPE_PROCESS); + printf("Main thread: %s\n",my_thread_name()); + for (i=0 ; i < 2 ; i++) + { + param=(int*) malloc(sizeof(int)); + *param= i; + pthread_mutex_lock(&LOCK_thread_count); + if ((error=pthread_create(&tid,&thr_attr,test_thread,(void*) param))) + { + printf("Can't create thread %d, error: %d\n",i,error); + exit(1); + } + thread_count++; + pthread_mutex_unlock(&LOCK_thread_count); + } + + pthread_attr_destroy(&thr_attr); + pthread_mutex_lock(&LOCK_thread_count); + thr_alarm_info(&alarm_info); + printf("Main_thread: Alarms: %u max_alarms: %u next_alarm_time: %lu\n", + alarm_info.active_alarms, alarm_info.max_used_alarms, + alarm_info.next_alarm_time); + while (thread_count) + { + pthread_cond_wait(&COND_thread_count,&LOCK_thread_count); + if (thread_count == 1) + { + printf("Calling end_thr_alarm. This should cancel the last thread\n"); + end_thr_alarm(0); + } + } + pthread_mutex_unlock(&LOCK_thread_count); + thr_alarm_info(&alarm_info); + end_thr_alarm(1); + printf("Main_thread: Alarms: %u max_alarms: %u next_alarm_time: %lu\n", + alarm_info.active_alarms, alarm_info.max_used_alarms, + alarm_info.next_alarm_time); + printf("Test succeeded\n"); + return 0; +} + +#else /* THREAD */ + +int main(int argc __attribute__((unused)),char **argv __attribute__((unused))) +{ +#ifndef THREAD + printf("thr_alarm disabled because we are not using threads\n"); +#else + printf("thr_alarm disabled with DONT_USE_THR_ALARM\n"); +#endif + exit(1); +} + +#endif /* THREAD */ +#endif /* MAIN */ diff --git a/externals/mysql/mysys/thr_lock.c b/externals/mysql/mysys/thr_lock.c new file mode 100644 index 00000000000..f9f32933fdf --- /dev/null +++ b/externals/mysql/mysys/thr_lock.c @@ -0,0 +1,1687 @@ +/* Copyright (C) 2000 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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_ALLOW_READ # Write lock, but allow reading +TL_WRITE_CONCURRENT_INSERT + # Insert that can be mixed when selects +TL_WRITE_DELAYED # Used by delayed insert + # 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_ALLOW_READ, WRITE_CONCURRENT_INSERT, WRITE_DELAYED, +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: + in thr_reschedule_write_lock(), when an insert delayed thread + downgrades TL_WRITE lock to TL_WRITE_DELAYED, to allow SELECT + threads to proceed. + A storage engine should also call update_status internally + in the ::external_lock(F_UNLCK) method. + In MyISAM and CSV this functions updates the 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_ALLOW_READ, +TL_WRITE_CONCURRENT_INSERT or one TL_WRITE_DELAYED lock at the same +time as multiple read locks. + +In addition, if lock->allow_multiple_concurrent_insert is set then there can +be any number of TL_WRITE_CONCURRENT_INSERT locks aktive at the same time. +*/ + +#if !defined(MAIN) && !defined(DBUG_OFF) && !defined(EXTRA_DEBUG) +#define FORCE_DBUG_OFF +#endif + +#include "mysys_priv.h" + +#ifdef THREAD +#include "thr_lock.h" +#include <m_string.h> +#include <errno.h> + +my_bool thr_lock_inited=0; +ulong locks_immediate = 0L, locks_waited = 0L; +ulong table_lock_wait_timeout; +enum thr_lock_type thr_upgraded_concurrent_insert_lock = TL_WRITE; + +/* The following constants are only for debug output */ +#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 inline pthread_cond_t *get_cond(void) +{ + return &my_thread_var->suspend; +} + +/* +** For the future (now the thread specific cond is alloced by my_pthread.c) +*/ + +my_bool init_thr_lock() +{ + thr_lock_inited=1; + return 0; +} + +static inline my_bool +thr_lock_owner_equal(THR_LOCK_OWNER *rhs, THR_LOCK_OWNER *lhs) +{ + return rhs == lhs; +} + + +#ifdef EXTRA_DEBUG +#define MAX_FOUND_ERRORS 10 /* Report 10 first errors */ +static uint found_errors=0; + +static int check_lock(struct st_lock_list *list, const char* lock_type, + const char *where, my_bool same_owner, my_bool no_cond) +{ + THR_LOCK_DATA *data,**prev; + uint count=0; + THR_LOCK_OWNER *first_owner; + LINT_INIT(first_owner); + + prev= &list->data; + if (list->data) + { + enum thr_lock_type last_lock_type=list->data->type; + + if (same_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) + { + fprintf(stderr, + "Warning: prev link %d didn't point at previous lock at %s: %s\n", + count, lock_type, where); + return 1; + } + if (same_owner && + !thr_lock_owner_equal(data->owner, first_owner) && + last_lock_type != TL_WRITE_ALLOW_WRITE && + last_lock_type != TL_WRITE_CONCURRENT_INSERT) + { + fprintf(stderr, + "Warning: Found locks from different threads in %s: %s\n", + lock_type,where); + return 1; + } + if (no_cond && data->cond) + { + fprintf(stderr, + "Warning: Found active lock with not reset cond %s: %s\n", + lock_type,where); + return 1; + } + prev= &data->next; + } + if (data) + { + fprintf(stderr,"Warning: found too many locks at %s: %s\n", + lock_type,where); + return 1; + } + } + if (prev != list->last) + { + fprintf(stderr,"Warning: last didn't point at last lock at %s: %s\n", + 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 (data->type == 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++; + fprintf(stderr, + "Warning at '%s': Locks read_no_write_count was %u when it should have been %u\n", 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++; + fprintf(stderr, + "Warning at '%s': No locks in use but locks are in wait queue\n", + where); + } + if (!lock->write_wait.data) + { + if (!allow_no_locks && lock->read_wait.data) + { + found_errors++; + fprintf(stderr, + "Warning at '%s': No write locks and waiting read locks\n", + 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) || + lock->write_wait.data->type == TL_WRITE_ALLOW_READ || + (lock->write_wait.data->type == TL_WRITE_DELAYED && + !lock->read.data))) + { + found_errors++; + fprintf(stderr, + "Warning at '%s': Write lock %d waiting while no exclusive read locks\n",where,(int) lock->write_wait.data->type); + } + } + } + else + { + /* We have at least one write lock */ + if (lock->write.data->type == TL_WRITE_CONCURRENT_INSERT) + { + THR_LOCK_DATA *data; + for (data=lock->write.data->next ; data ; data=data->next) + { + if (data->type != TL_WRITE_CONCURRENT_INSERT) + { + fprintf(stderr, + "Warning at '%s': Found TL_WRITE_CONCURRENT_INSERT lock mixed with other write locks\n", + where); + break; + } + } + } + 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++; + fprintf(stderr, + "Warning at '%s': Found WRITE_ALLOW_WRITE lock waiting for WRITE_ALLOW_WRITE lock\n", + where); + } + } + if (lock->read.data) + { + if (!thr_lock_owner_equal(lock->write.data->owner, + lock->read.data->owner) && + ((lock->write.data->type > TL_WRITE_DELAYED && + lock->write.data->type != TL_WRITE_ONLY) || + ((lock->write.data->type == TL_WRITE_CONCURRENT_INSERT || + lock->write.data->type == TL_WRITE_ALLOW_WRITE) && + lock->read_no_write_count))) + { + found_errors++; + fprintf(stderr, + "Warning at '%s': Found lock of type %d that is write and read locked\n", + 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_DELAYED && + lock->read_wait.data->type <= TL_READ_HIGH_PRIORITY) + { + found_errors++; + fprintf(stderr, + "Warning at '%s': Found read lock of type %d waiting for write lock of type %d\n", + where, + (int) lock->read_wait.data->type, + (int) lock->write.data->type); + } + } + } + } + if (found_errors != old_found_errors) + { + fflush(stderr); + 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"); + bzero((char*) lock,sizeof(*lock)); + pthread_mutex_init(&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; + + pthread_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); + pthread_mutex_unlock(&THR_LOCK_lock); + DBUG_VOID_RETURN; +} + + +void thr_lock_delete(THR_LOCK *lock) +{ + DBUG_ENTER("thr_lock_delete"); + pthread_mutex_lock(&THR_LOCK_lock); + thr_lock_thread_list=list_delete(thr_lock_thread_list,&lock->list); + pthread_mutex_unlock(&THR_LOCK_lock); + pthread_mutex_destroy(&lock->mutex); + DBUG_VOID_RETURN; +} + + +void thr_lock_info_init(THR_LOCK_INFO *info) +{ + struct st_my_thread_var *tmp= my_thread_var; + info->thread= tmp->pthread_self; + info->thread_id= tmp->id; + info->n_cursors= 0; +} + + /* Initialize a lock instance */ + +void thr_lock_data_init(THR_LOCK *lock,THR_LOCK_DATA *data, void *param) +{ + data->lock=lock; + data->type=TL_UNLOCK; + data->owner= 0; /* no owner yet */ + data->status_param=param; + data->cond=0; +} + + +static inline my_bool +have_old_read_lock(THR_LOCK_DATA *data, THR_LOCK_OWNER *owner) +{ + for ( ; data ; data=data->next) + { + if (thr_lock_owner_equal(data->owner, owner)) + return 1; /* Already locked by thread */ + } + return 0; +} + +static inline my_bool have_specific_lock(THR_LOCK_DATA *data, + enum thr_lock_type type) +{ + for ( ; data ; data=data->next) + { + if (data->type == type) + return 1; + } + 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, + my_bool in_wait_list) +{ + struct st_my_thread_var *thread_var= my_thread_var; + pthread_cond_t *cond= &thread_var->suspend; + struct timespec wait_timeout; + enum enum_thr_lock_result result= THR_LOCK_ABORTED; + my_bool can_deadlock= test(data->owner->info->n_cursors); + const char *old_proc_info; + 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; + } + + statistic_increment(locks_waited, &THR_LOCK_lock); + + /* Set up control struct to allow others to abort locks */ + thread_var->current_mutex= &data->lock->mutex; + thread_var->current_cond= cond; + data->cond= cond; + + old_proc_info= proc_info_hook(NULL, "Table lock", + __func__, __FILE__, __LINE__); + + if (can_deadlock) + set_timespec(wait_timeout, table_lock_wait_timeout); + while (!thread_var->abort || in_wait_list) + { + int rc= (can_deadlock ? + pthread_cond_timedwait(cond, &data->lock->mutex, + &wait_timeout) : + pthread_cond_wait(cond, &data->lock->mutex)); + /* + We must break the wait if one of the following occurs: + - the connection has been aborted (!thread_var->abort), but + this is not a delayed insert thread (in_wait_list). For a delayed + insert thread the proper action at shutdown is, apparently, to + acquire the lock and complete the insert. + - the lock has been granted (data->cond is set to NULL by the granter), + or the waiting has been aborted (additionally data->type is set to + TL_UNLOCK). + - the wait has timed out (rc == ETIMEDOUT) + Order of checks below is important to not report about timeout + if the predicate is true. + */ + if (data->cond == 0) + { + 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 */ + } + } + DBUG_PRINT("thr_lock", ("aborted: %d in_wait_list: %d", + thread_var->abort, in_wait_list)); + + 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, + data->type == TL_WRITE_CONCURRENT_INSERT); + check_locks(data->lock,"got wait_for_lock",0); + } + pthread_mutex_unlock(&data->lock->mutex); + + /* The following must be done after unlock of lock->mutex */ + pthread_mutex_lock(&thread_var->mutex); + thread_var->current_mutex= 0; + thread_var->current_cond= 0; + pthread_mutex_unlock(&thread_var->mutex); + + proc_info_hook(NULL, old_proc_info, __func__, __FILE__, __LINE__); + + DBUG_RETURN(result); +} + + +enum enum_thr_lock_result +thr_lock(THR_LOCK_DATA *data, THR_LOCK_OWNER *owner, + enum thr_lock_type lock_type) +{ + THR_LOCK *lock=data->lock; + enum enum_thr_lock_result result= THR_LOCK_SUCCESS; + struct st_lock_list *wait_queue; + THR_LOCK_DATA *lock_owner; + DBUG_ENTER("thr_lock"); + + data->next=0; + data->cond=0; /* safety */ + data->type=lock_type; + data->owner= owner; /* Must be reset ! */ + pthread_mutex_lock(&lock->mutex); + DBUG_PRINT("lock",("data: %p thread: 0x%lx lock: %p type: %d", + data, data->owner->info->thread_id, + lock, (int) lock_type)); + check_locks(lock,(uint) lock_type <= (uint) TL_READ_NO_INSERT ? + "enter read_lock" : "enter write_lock",0); + if ((int) lock_type <= (int) TL_READ_NO_INSERT) + { + /* 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 in one the following cases: + - This thread alread have a write lock on the table + - The write lock is TL_WRITE_ALLOW_READ or TL_WRITE_DELAYED + and the read lock is TL_READ_HIGH_PRIORITY or TL_READ + - The write lock is TL_WRITE_CONCURRENT_INSERT or TL_WRITE_ALLOW_WRITE + and the read lock is not TL_READ_NO_INSERT + */ + + DBUG_PRINT("lock",("write locked 1 by thread: 0x%lx", + lock->write.data->owner->info->thread_id)); + if (thr_lock_owner_equal(data->owner, lock->write.data->owner) || + (lock->write.data->type <= TL_WRITE_DELAYED && + (((int) lock_type <= (int) TL_READ_HIGH_PRIORITY) || + (lock->write.data->type != TL_WRITE_CONCURRENT_INSERT && + lock->write.data->type != TL_WRITE_ALLOW_READ)))) + { /* 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); + statistic_increment(locks_immediate,&THR_LOCK_lock); + 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 || + have_old_read_lock(lock->read.data, data->owner)) + { /* No important write-locks */ + (*lock->read.last)=data; /* Add to running FIFO */ + data->prev=lock->read.last; + lock->read.last= &data->next; + if (lock_type == TL_READ_NO_INSERT) + lock->read_no_write_count++; + check_locks(lock,"read lock with no write locks",0); + if (lock->get_status) + (*lock->get_status)(data->status_param, 0); + statistic_increment(locks_immediate,&THR_LOCK_lock); + 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_DELAYED) + { + if (lock->write.data && lock->write.data->type == TL_WRITE_ONLY) + { + data->type=TL_UNLOCK; + result= THR_LOCK_ABORTED; /* Can't wait for this one */ + goto end; + } + /* + if there is a TL_WRITE_ALLOW_READ lock, we have to wait for a lock + (TL_WRITE_ALLOW_READ is used for ALTER TABLE in MySQL) + */ + if ((!lock->write.data || + lock->write.data->type != TL_WRITE_ALLOW_READ) && + !have_specific_lock(lock->write_wait.data,TL_WRITE_ALLOW_READ) && + (lock->write.data || lock->read.data)) + { + /* Add delayed write lock to write_wait queue, and return at once */ + (*lock->write_wait.last)=data; + data->prev=lock->write_wait.last; + lock->write_wait.last= &data->next; + data->cond=get_cond(); + /* + We don't have to do get_status here as we will do it when we change + the delayed lock to a real write lock + */ + statistic_increment(locks_immediate,&THR_LOCK_lock); + goto end; + } + } + else 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 following test will not work if the old lock was a + TL_WRITE_ALLOW_WRITE, TL_WRITE_ALLOW_READ or TL_WRITE_DELAYED in + the same thread, but this will never happen within MySQL. + + 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 the same type and are either + TL_WRITE_ALLOW_WRITE or TL_WRITE_CONCURRENT_INSERT + */ + if (thr_lock_owner_equal(data->owner, lock->write.data->owner) || + (!lock->write_wait.data && lock_type == lock->write.data->type && + (lock_type == TL_WRITE_ALLOW_WRITE || + (lock_type == TL_WRITE_CONCURRENT_INSERT && + lock->allow_multiple_concurrent_insert)))) + { + DBUG_PRINT("info", ("write_wait.data: %p old_type: %d", + 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 (lock->get_status) + (*lock->get_status)(data->status_param, + lock_type == TL_WRITE_CONCURRENT_INSERT); + statistic_increment(locks_immediate,&THR_LOCK_lock); + goto end; + } + DBUG_PRINT("lock",("write locked 2 by thread: 0x%lx", + lock->write.data->owner->info->thread_id)); + } + else + { + DBUG_PRINT("info", ("write_wait.data: %p", + 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_DELAYED && + ((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 (lock->get_status) + (*lock->get_status)(data->status_param, concurrent_insert); + check_locks(lock,"only write lock",0); + statistic_increment(locks_immediate,&THR_LOCK_lock); + goto end; + } + } + DBUG_PRINT("lock",("write locked 3 by thread: 0x%lx type: %d", + lock->read.data->owner->info->thread_id, data->type)); + } + wait_queue= &lock->write_wait; + } + /* + Try to detect a trivial deadlock when using cursors: attempt to + lock a table that is already locked by an open cursor within the + same connection. lock_owner can be zero if we succumbed to a high + priority writer in the write_wait queue. + */ + lock_owner= lock->read.data ? lock->read.data : lock->write.data; + if (lock_owner && lock_owner->owner->info == owner->info) + { + DBUG_PRINT("lock",("deadlock")); + result= THR_LOCK_DEADLOCK; + goto end; + } + /* Can't get lock yet; Wait for it */ + DBUG_RETURN(wait_for_lock(wait_queue, data, 0)); +end: + pthread_mutex_unlock(&lock->mutex); + DBUG_RETURN(result); +} + + +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 + { + pthread_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%lx", + data->owner->info->thread_id)); + /* purecov: end */ + data->cond=0; /* Mark thread free */ + pthread_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: %p thread: 0x%lx lock: %p", + data, data->owner->info->thread_id, lock)); + pthread_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 if (lock_type == TL_WRITE_DELAYED && data->cond) + { + /* + This only happens in extreme circumstances when a + write delayed lock that is waiting for a lock + */ + lock->write_wait.last=data->prev; /* Put it on wait queue */ + } + else + lock->write.last=data->prev; + if (lock_type == TL_READ_NO_INSERT) + lock->read_no_write_count--; + data->type=TL_UNLOCK; /* Mark unlocked */ + check_locks(lock,"after releasing lock",1); + wake_up_waiters(lock); + pthread_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%lx", + data->type, data->owner->info->thread_id)); + /* purecov: end */ + { + pthread_cond_t *cond=data->cond; + data->cond=0; /* Mark thread free */ + pthread_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_DELAYED && + ((lock_type != TL_WRITE_CONCURRENT_INSERT && + lock_type != TL_WRITE_ALLOW_WRITE) || + !lock->read_no_write_count)) + { + /* + For DELAYED, 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 { + pthread_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 */ + pthread_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_OWNER *owner) +{ + THR_LOCK_DATA **pos,**end; + DBUG_ENTER("thr_multi_lock"); + DBUG_PRINT("lock",("data: %p count: %d", 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); + if (result != THR_LOCK_SUCCESS) + { /* Aborted */ + thr_multi_unlock(data,(uint) (pos-data)); + DBUG_RETURN(result); + } +#ifdef MAIN + printf("Thread: %s Got lock: 0x%lx type: %d\n",my_thread_name(), + (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) +{ +#if !defined(DONT_USE_RW_LOCKS) + 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); + } +#endif +} + + /* 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: %p count: %d", data, count)); + + for (pos=data,end=data+count; pos < end ; pos++) + { +#ifdef MAIN + printf("Thread: %s Rel lock: 0x%lx type: %d\n", + my_thread_name(), (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: %p thread: 0x%lx lock: %p", + *pos, (*pos)->owner->info->thread_id, + (*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"); + pthread_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. */ + pthread_cond_signal(data->cond); + data->cond=0; /* Removed from list */ + } + for (data=lock->write_wait.data; data ; data=data->next) + { + data->type=TL_UNLOCK; + pthread_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; + pthread_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 +*/ + +my_bool thr_abort_locks_for_thread(THR_LOCK *lock, my_thread_id thread_id) +{ + THR_LOCK_DATA *data; + my_bool found= FALSE; + DBUG_ENTER("thr_abort_locks_for_thread"); + + pthread_mutex_lock(&lock->mutex); + for (data= lock->read_wait.data; data ; data= data->next) + { + if (data->owner->info->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. */ + found= TRUE; + pthread_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->info->thread_id == thread_id) /* purecov: tested */ + { + DBUG_PRINT("info",("Aborting write-wait lock")); + data->type= TL_UNLOCK; + found= TRUE; + pthread_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); + pthread_mutex_unlock(&lock->mutex); + DBUG_RETURN(found); +} + + +/* + 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 and TL_WRITE_ALLOW_READ. The normal downgrade variants are + TL_WRITE_ONLY => TL_WRITE_ALLOW_READ After a short exclusive lock + TL_WRITE_ALLOW_READ => TL_WRITE_ALLOW_WRITE After discovering that the + operation didn't need such a high lock. + 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"); + + pthread_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); + + pthread_mutex_unlock(&lock->mutex); + DBUG_VOID_RETURN; +} + +/* Upgrade a WRITE_DELAY lock to a WRITE_LOCK */ + +my_bool thr_upgrade_write_delay_lock(THR_LOCK_DATA *data, + enum thr_lock_type new_lock_type) +{ + THR_LOCK *lock=data->lock; + DBUG_ENTER("thr_upgrade_write_delay_lock"); + + pthread_mutex_lock(&lock->mutex); + if (data->type == TL_UNLOCK || data->type >= TL_WRITE_LOW_PRIORITY) + { + pthread_mutex_unlock(&lock->mutex); + DBUG_RETURN(data->type == TL_UNLOCK); /* Test if Aborted */ + } + check_locks(lock,"before upgrading lock",0); + /* TODO: Upgrade to TL_WRITE_CONCURRENT_INSERT in some cases */ + data->type= new_lock_type; /* Upgrade lock */ + + /* Check if someone has given us the lock */ + if (!data->cond) + { + if (!lock->read.data) /* No read locks */ + { /* We have the lock */ + if (lock->get_status) + (*lock->get_status)(data->status_param, 0); + pthread_mutex_unlock(&lock->mutex); + DBUG_RETURN(0); + } + + if (((*data->prev)=data->next)) /* remove from lock-list */ + data->next->prev= data->prev; + else + lock->write.last=data->prev; + + if ((data->next=lock->write_wait.data)) /* Put first in lock_list */ + data->next->prev= &data->next; + else + lock->write_wait.last= &data->next; + data->prev= &lock->write_wait.data; + lock->write_wait.data=data; + check_locks(lock,"upgrading lock",0); + } + else + { + check_locks(lock,"waiting for lock",0); + } + DBUG_RETURN(wait_for_lock(&lock->write_wait,data,1)); +} + + +/* downgrade a WRITE lock to a WRITE_DELAY lock if there is pending locks */ + +my_bool thr_reschedule_write_lock(THR_LOCK_DATA *data) +{ + THR_LOCK *lock=data->lock; + enum thr_lock_type write_lock_type; + DBUG_ENTER("thr_reschedule_write_lock"); + + pthread_mutex_lock(&lock->mutex); + if (!lock->read_wait.data) /* No waiting read locks */ + { + pthread_mutex_unlock(&lock->mutex); + DBUG_RETURN(0); + } + + write_lock_type= data->type; + data->type=TL_WRITE_DELAYED; + if (lock->update_status) + (*lock->update_status)(data->status_param); + if (((*data->prev)=data->next)) /* remove from lock-list */ + data->next->prev= data->prev; + else + lock->write.last=data->prev; + + if ((data->next=lock->write_wait.data)) /* Put first in lock_list */ + data->next->prev= &data->next; + else + lock->write_wait.last= &data->next; + data->prev= &lock->write_wait.data; + data->cond=get_cond(); /* This was zero */ + lock->write_wait.data=data; + free_all_read_locks(lock,0); + + pthread_mutex_unlock(&lock->mutex); + DBUG_RETURN(thr_upgrade_write_delay_lock(data, write_lock_type)); +} + + +#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 (%lu:%d); ", (ulong) data, data->owner->info->thread_id, + (int) data->type); + if (data->prev != prev) + printf("\nWarning: prev didn't point at previous lock\n"); + prev= &data->next; + } + 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; + + pthread_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; + pthread_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); + pthread_mutex_unlock(&lock->mutex); + puts(""); + } + fflush(stdout); + pthread_mutex_unlock(&THR_LOCK_lock); +} + +#endif /* THREAD */ + +/***************************************************************************** +** Test of thread locks +****************************************************************************/ + +#ifdef MAIN + +#ifdef THREAD + +struct st_test { + uint lock_nr; + enum thr_lock_type lock_type; +}; + +THR_LOCK locks[6]; /* Number of locks +1 */ + +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_ALLOW_READ},{1,TL_WRITE_ALLOW_READ},{2,TL_WRITE_ALLOW_READ},{3,TL_WRITE_ALLOW_READ}}; /* Many writes */ +struct st_test test_13[] = {{0,TL_WRITE_CONCURRENT_INSERT},{1,TL_WRITE_CONCURRENT_INSERT},{2,TL_WRITE_CONCURRENT_INSERT},{3,TL_WRITE_CONCURRENT_INSERT}}; +struct st_test test_14[] = {{0,TL_WRITE_CONCURRENT_INSERT},{1,TL_READ}}; +struct st_test test_15[] = {{0,TL_WRITE_ALLOW_WRITE},{1,TL_READ}}; +struct st_test test_16[] = {{0,TL_WRITE_ALLOW_WRITE},{1,TL_WRITE_ALLOW_WRITE}}; + +struct st_test test_17[] = {{5,TL_WRITE_CONCURRENT_INSERT}}; +struct st_test test_18[] = {{5,TL_WRITE_CONCURRENT_INSERT}}; +struct st_test test_19[] = {{5,TL_READ}}; +struct st_test test_20[] = {{5,TL_READ_NO_INSERT}}; +struct st_test test_21[] = {{5,TL_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, test_16, + test_17, test_18, test_19, test_20, test_21 +}; + +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), + sizeof(test_16)/sizeof(struct st_test), + sizeof(test_17)/sizeof(struct st_test), + sizeof(test_18)/sizeof(struct st_test), + sizeof(test_19)/sizeof(struct st_test), + sizeof(test_20)/sizeof(struct st_test), + sizeof(test_21)/sizeof(struct st_test) +}; + + +static pthread_cond_t COND_thread_count; +static pthread_mutex_t LOCK_thread_count; +static uint thread_count; +static ulong sum=0; + +#define MAX_LOCK_COUNT 8 + +/* The following functions is for WRITE_CONCURRENT_INSERT */ + +static void test_get_status(void* param __attribute__((unused)), + int concurrent_insert __attribute__((unused))) +{ +} + +static void test_update_status(void* param __attribute__((unused))) +{ +} + +static void test_copy_status(void* to __attribute__((unused)) , + void *from __attribute__((unused))) +{ +} + +static my_bool test_check_status(void* param __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_OWNER owner; + THR_LOCK_INFO lock_info; + THR_LOCK_DATA *multi_locks[MAX_LOCK_COUNT]; + my_thread_init(); + + printf("Thread %s (%d) started\n",my_thread_name(),param); fflush(stdout); + + thr_lock_info_init(&lock_info); + thr_lock_owner_init(&owner, &lock_info); + for (i=0; i < lock_counts[param] ; i++) + thr_lock_data_init(locks+tests[param][i].lock_nr,data+i,NULL); + for (j=1 ; j < 10 ; j++) /* try locking 10 times */ + { + 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], &owner); + pthread_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; + } + } + pthread_mutex_unlock(&LOCK_thread_count); + thr_multi_unlock(multi_locks,lock_counts[param]); + } + + printf("Thread %s (%d) ended\n",my_thread_name(),param); fflush(stdout); + thr_print_locks(); + pthread_mutex_lock(&LOCK_thread_count); + thread_count--; + pthread_cond_signal(&COND_thread_count); /* Tell main we are ready */ + pthread_mutex_unlock(&LOCK_thread_count); + free((uchar*) arg); + return 0; +} + + +int main(int argc __attribute__((unused)),char **argv __attribute__((unused))) +{ + pthread_t tid; + pthread_attr_t thr_attr; + int *param,error; + uint i; + MY_INIT(argv[0]); + if (argc > 1 && argv[1][0] == '-' && argv[1][1] == '#') + DBUG_PUSH(argv[1]+2); + + printf("Main thread: %s\n",my_thread_name()); + + if ((error=pthread_cond_init(&COND_thread_count,NULL))) + { + fprintf(stderr,"Got error: %d from pthread_cond_init (errno: %d)", + error,errno); + exit(1); + } + if ((error=pthread_mutex_init(&LOCK_thread_count,MY_MUTEX_INIT_FAST))) + { + fprintf(stderr,"Got error: %d from pthread_cond_init (errno: %d)", + error,errno); + exit(1); + } + + for (i=0 ; i < 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; + locks[i].allow_multiple_concurrent_insert= 1; + } + if ((error=pthread_attr_init(&thr_attr))) + { + fprintf(stderr,"Got error: %d from pthread_attr_init (errno: %d)", + error,errno); + exit(1); + } + if ((error=pthread_attr_setdetachstate(&thr_attr,PTHREAD_CREATE_DETACHED))) + { + fprintf(stderr, + "Got error: %d from pthread_attr_setdetachstate (errno: %d)", + error,errno); + exit(1); + } +#ifndef pthread_attr_setstacksize /* void return value */ + if ((error=pthread_attr_setstacksize(&thr_attr,65536L))) + { + fprintf(stderr,"Got error: %d from pthread_attr_setstacksize (errno: %d)", + error,errno); + exit(1); + } +#endif +#ifdef HAVE_THR_SETCONCURRENCY + (void) thr_setconcurrency(2); +#endif + for (i=0 ; i < array_elements(lock_counts) ; i++) + { + param=(int*) malloc(sizeof(int)); + *param=i; + + if ((error=pthread_mutex_lock(&LOCK_thread_count))) + { + fprintf(stderr,"Got error: %d from pthread_mutex_lock (errno: %d)", + error,errno); + exit(1); + } + if ((error=pthread_create(&tid,&thr_attr,test_thread,(void*) param))) + { + fprintf(stderr,"Got error: %d from pthread_create (errno: %d)\n", + error,errno); + pthread_mutex_unlock(&LOCK_thread_count); + exit(1); + } + thread_count++; + pthread_mutex_unlock(&LOCK_thread_count); + } + + pthread_attr_destroy(&thr_attr); + if ((error=pthread_mutex_lock(&LOCK_thread_count))) + fprintf(stderr,"Got error: %d from pthread_mutex_lock\n",error); + while (thread_count) + { + if ((error=pthread_cond_wait(&COND_thread_count,&LOCK_thread_count))) + fprintf(stderr,"Got error: %d from pthread_cond_wait\n",error); + } + if ((error=pthread_mutex_unlock(&LOCK_thread_count))) + fprintf(stderr,"Got error: %d from pthread_mutex_unlock\n",error); + for (i=0 ; i < 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; +} + +#else /* THREAD */ + +int main(int argc __attribute__((unused)),char **argv __attribute__((unused))) +{ + printf("thr_lock disabled because we are not using threads\n"); + exit(1); +} + +#endif /* THREAD */ +#endif /* MAIN */ diff --git a/externals/mysql/mysys/thr_mutex.c b/externals/mysql/mysys/thr_mutex.c new file mode 100644 index 00000000000..9c7eb714326 --- /dev/null +++ b/externals/mysql/mysys/thr_mutex.c @@ -0,0 +1,875 @@ +/* Copyright (C) 2000-2008 MySQL AB, 2008 Sun Microsystems, Inc + + 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +/* This makes a wrapper for mutex handling to make it easier to debug mutex */ + +#include <my_global.h> +#if defined(TARGET_OS_LINUX) && !defined (__USE_UNIX98) +#define __USE_UNIX98 /* To get rw locks under Linux */ +#endif +#if defined(THREAD) && defined(SAFE_MUTEX) +#undef SAFE_MUTEX /* Avoid safe_mutex redefinitions */ +#include "mysys_priv.h" +#include "my_static.h" +#include <m_string.h> +#include <hash.h> + +#ifndef DO_NOT_REMOVE_THREAD_WRAPPERS +/* Remove wrappers */ +#undef pthread_mutex_t +#undef pthread_mutex_init +#undef pthread_mutex_lock +#undef pthread_mutex_unlock +#undef pthread_mutex_destroy +#undef pthread_cond_wait +#undef pthread_cond_timedwait +#ifdef HAVE_NONPOSIX_PTHREAD_MUTEX_INIT +#define pthread_mutex_init(a,b) my_pthread_noposix_mutex_init((a),(b)) +#endif +#endif /* DO_NOT_REMOVE_THREAD_WRAPPERS */ + +static pthread_mutex_t THR_LOCK_mutex; +static ulong safe_mutex_count= 0; /* Number of mutexes created */ +static ulong safe_mutex_id= 0; +my_bool safe_mutex_deadlock_detector= 1; /* On by default */ + +#ifdef SAFE_MUTEX_DETECT_DESTROY +static struct st_safe_mutex_create_info_t *safe_mutex_create_root= NULL; +#endif + +static my_bool add_used_to_locked_mutex(safe_mutex_t *used_mutex, + safe_mutex_deadlock_t *locked_mutex); +static my_bool add_to_locked_mutex(safe_mutex_deadlock_t *locked_mutex, + safe_mutex_t *current_mutex); +static my_bool remove_from_locked_mutex(safe_mutex_t *mp, + safe_mutex_t *delete_mutex); +static my_bool remove_from_used_mutex(safe_mutex_deadlock_t *locked_mutex, + safe_mutex_t *mutex); +static void print_deadlock_warning(safe_mutex_t *new_mutex, + safe_mutex_t *conflicting_mutex); + +void safe_mutex_global_init(void) +{ + pthread_mutex_init(&THR_LOCK_mutex,MY_MUTEX_INIT_FAST); + safe_mutex_id= safe_mutex_count= 0; + safe_mutex_deadlock_detector= 1; + +#ifdef SAFE_MUTEX_DETECT_DESTROY + safe_mutex_create_root= 0; +#endif +} + +static inline void remove_from_active_list(safe_mutex_t *mp) +{ + if (!(mp->active_flags & (MYF_NO_DEADLOCK_DETECTION | MYF_TRY_LOCK))) + { + /* Remove mutex from active mutex linked list */ + if (mp->next) + mp->next->prev= mp->prev; + if (mp->prev) + mp->prev->next= mp->next; + else + *my_thread_var_mutex_in_use()= mp->next; + } + mp->prev= mp->next= 0; +} + + +int safe_mutex_init(safe_mutex_t *mp, + const pthread_mutexattr_t *attr __attribute__((unused)), + const char *name, + myf my_flags, + const char *file, + uint line) +{ + DBUG_ENTER("safe_mutex_init"); + DBUG_PRINT("enter",("mutex: 0x%lx name: %s", (ulong) mp, name)); + bzero((char*) mp,sizeof(*mp)); + pthread_mutex_init(&mp->global,MY_MUTEX_INIT_ERRCHK); + pthread_mutex_init(&mp->mutex,attr); + /* Mark that mutex is initialized */ + mp->file= file; + mp->line= line; + /* Skip the very common '&' prefix from the autogenerated name */ + mp->name= name[0] == '&' ? name + 1 : name; + + if (safe_mutex_deadlock_detector && !( my_flags & MYF_NO_DEADLOCK_DETECTION)) + { + if (!my_multi_malloc(MY_FAE | MY_WME, + &mp->locked_mutex, sizeof(*mp->locked_mutex), + &mp->used_mutex, sizeof(*mp->used_mutex), NullS)) + { + /* Disable deadlock handling for this mutex */ + my_flags|= MYF_NO_DEADLOCK_DETECTION; + } + else + { + pthread_mutex_lock(&THR_LOCK_mutex); + mp->id= ++safe_mutex_id; + pthread_mutex_unlock(&THR_LOCK_mutex); + my_hash_init(mp->locked_mutex, &my_charset_bin, + 1000, + offsetof(safe_mutex_deadlock_t, id), + sizeof(mp->id), + 0, 0, HASH_UNIQUE); + my_hash_init(mp->used_mutex, &my_charset_bin, + 1000, + offsetof(safe_mutex_t, id), + sizeof(mp->id), + 0, 0, HASH_UNIQUE); + } + } + else + my_flags|= MYF_NO_DEADLOCK_DETECTION; + mp->create_flags= my_flags; + +#ifdef SAFE_MUTEX_DETECT_DESTROY + /* + Monitor the freeing of mutexes. This code depends on single thread init + and destroy + */ + if ((mp->info= (safe_mutex_info_t *) malloc(sizeof(safe_mutex_info_t)))) + { + struct st_safe_mutex_info_t *info= mp->info; + + info->init_file= file; + info->init_line= line; + info->prev= NULL; + info->next= NULL; + + pthread_mutex_lock(&THR_LOCK_mutex); + if ((info->next= safe_mutex_create_root)) + safe_mutex_create_root->prev= info; + safe_mutex_create_root= info; + safe_mutex_count++; + pthread_mutex_unlock(&THR_LOCK_mutex); + } +#else + thread_safe_increment(safe_mutex_count, &THR_LOCK_mutex); +#endif /* SAFE_MUTEX_DETECT_DESTROY */ + DBUG_RETURN(0); +} + + +int safe_mutex_lock(safe_mutex_t *mp, myf my_flags, const char *file, + uint line) +{ + int error; + DBUG_PRINT("mutex", ("%s (0x%lx) locking", mp->name ? mp->name : "Null", + (ulong) mp)); + if (!mp->file) + { + fprintf(stderr, + "safe_mutex: Trying to lock unitialized mutex at %s, line %d\n", + file, line); + fflush(stderr); + abort(); + } + + pthread_mutex_lock(&mp->global); + if (mp->count > 0) + { + /* + Check that we are not trying to lock mutex twice. This is an error + even if we are using 'try_lock' as it's not portably what happens + if you lock the mutex many times and this is in any case bad + behaviour that should not be encouraged + */ + if (pthread_equal(pthread_self(),mp->thread)) + { + fprintf(stderr, + "safe_mutex: Trying to lock mutex at %s, line %d, when the" + " mutex was already locked at %s, line %d in thread %s\n", + file,line,mp->file, mp->line, my_thread_name()); + fflush(stderr); + abort(); + } + } + pthread_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 (my_flags & MYF_TRY_LOCK) + { + error= pthread_mutex_trylock(&mp->mutex); + if (error == EBUSY) + return error; + } + else + error= pthread_mutex_lock(&mp->mutex); + + if (error || (error=pthread_mutex_lock(&mp->global))) + { + fprintf(stderr,"Got error %d when trying to lock mutex %s at %s, line %d\n", + error, mp->name, file, line); + fflush(stderr); + abort(); + } + mp->thread= pthread_self(); + if (mp->count++) + { + fprintf(stderr,"safe_mutex: Error in thread libray: Got mutex %s at %s, " + "line %d more than 1 time\n", mp->name, file,line); + fflush(stderr); + abort(); + } + mp->file= file; + mp->line= line; + mp->active_flags= mp->create_flags | my_flags; + pthread_mutex_unlock(&mp->global); + + /* Deadlock detection */ + + mp->prev= mp->next= 0; + if (!(mp->active_flags & (MYF_TRY_LOCK | MYF_NO_DEADLOCK_DETECTION))) + { + safe_mutex_t **mutex_in_use= my_thread_var_mutex_in_use(); + + if (!mutex_in_use) + { + /* thread has not called my_thread_init() */ + mp->active_flags|= MYF_NO_DEADLOCK_DETECTION; + } + else + { + safe_mutex_t *mutex_root; + if ((mutex_root= *mutex_in_use)) /* If not first locked */ + { + /* + Protect locked_mutex against changes if a mutex is deleted + */ + pthread_mutex_lock(&THR_LOCK_mutex); + + if (! my_hash_search(mutex_root->locked_mutex, (uchar*) &mp->id, 0)) + { + safe_mutex_deadlock_t *deadlock; + safe_mutex_t *mutex; + + /* Create object to store mutex info */ + if (!(deadlock= my_malloc(sizeof(*deadlock), + MYF(MY_ZEROFILL | MY_WME | MY_FAE)))) + goto abort_loop; + deadlock->name= mp->name; + deadlock->id= mp->id; + deadlock->mutex= mp; + /* The following is useful for debugging wrong mutex usage */ + deadlock->file= file; + deadlock->line= line; + + /* Check if potential deadlock */ + mutex= mutex_root; + do + { + if (my_hash_search(mp->locked_mutex, (uchar*) &mutex->id, 0)) + { + print_deadlock_warning(mp, mutex); + /* Mark wrong usage to avoid future warnings for same error */ + deadlock->warning_only= 1; + add_to_locked_mutex(deadlock, mutex_root); + DBUG_ASSERT(deadlock->count > 0); + goto abort_loop; + } + } + while ((mutex= mutex->next)); + + /* + Copy current mutex and all mutex that has been locked + after current mutex (mp->locked_mutex) to all mutex that + was locked before previous mutex (mutex_root->used_mutex) + + For example if A->B would have been done before and we + are now locking (C) in B->C, then we would add C into + B->locked_mutex and A->locked_mutex + */ + my_hash_iterate(mutex_root->used_mutex, + (my_hash_walk_action) add_used_to_locked_mutex, + deadlock); + + /* + Copy all current mutex and all mutex locked after current one + into the prev mutex + */ + add_used_to_locked_mutex(mutex_root, deadlock); + DBUG_ASSERT(deadlock->count > 0); + } + abort_loop: + pthread_mutex_unlock(&THR_LOCK_mutex); + } + /* Link mutex into mutex_in_use list */ + if ((mp->next= *mutex_in_use)) + (*mutex_in_use)->prev= mp; + *mutex_in_use= mp; + } + } + + DBUG_PRINT("mutex", ("%s (0x%lx) locked", mp->name, (ulong) mp)); + return error; +} + + +int safe_mutex_unlock(safe_mutex_t *mp,const char *file, uint line) +{ + int error; + DBUG_PRINT("mutex", ("%s (0x%lx) unlocking", mp->name, (ulong) mp)); + pthread_mutex_lock(&mp->global); + if (mp->count == 0) + { + fprintf(stderr, + "safe_mutex: Trying to unlock mutex %s that wasn't locked at " + "%s, line %d\n" + "Last used at %s, line: %d\n", + mp->name ? mp->name : "Null", file, line, + mp->file ? mp->file : "Null", mp->line); + fflush(stderr); + abort(); + } + if (!pthread_equal(pthread_self(),mp->thread)) + { + fprintf(stderr, + "safe_mutex: Trying to unlock mutex %s at %s, line %d that was " + "locked by " + "another thread at: %s, line: %d\n", + mp->name, file, line, mp->file, mp->line); + fflush(stderr); + abort(); + } + mp->thread= 0; + mp->count--; + + remove_from_active_list(mp); + +#ifdef __WIN__ + pthread_mutex_unlock(&mp->mutex); + error=0; +#else + error=pthread_mutex_unlock(&mp->mutex); + if (error) + { + fprintf(stderr, + "safe_mutex: Got error: %d (%d) when trying to unlock mutex " + "%s at %s, line %d\n", error, errno, mp->name, file, line); + fflush(stderr); + abort(); + } +#endif /* __WIN__ */ + pthread_mutex_unlock(&mp->global); + return error; +} + + +int safe_cond_wait(pthread_cond_t *cond, safe_mutex_t *mp, const char *file, + uint line) +{ + int error; + safe_mutex_t save_state; + + pthread_mutex_lock(&mp->global); + if (mp->count == 0) + { + fprintf(stderr, + "safe_mutex: Trying to cond_wait on a unlocked mutex %s at %s, " + "line %d\n", + mp->name ? mp->name : "Null", file, line); + fflush(stderr); + abort(); + } + if (!pthread_equal(pthread_self(),mp->thread)) + { + fprintf(stderr, + "safe_mutex: Trying to cond_wait on a mutex %s at %s, line %d " + "that was locked by another thread at: %s, line: %d\n", + mp->name, file, line, mp->file, mp->line); + fflush(stderr); + abort(); + } + + if (mp->count-- != 1) + { + fprintf(stderr, + "safe_mutex: Count was %d on locked mutex %s at %s, line %d\n", + mp->count+1, mp->name, file, line); + fflush(stderr); + abort(); + } + save_state= *mp; + remove_from_active_list(mp); + pthread_mutex_unlock(&mp->global); + error=pthread_cond_wait(cond,&mp->mutex); + pthread_mutex_lock(&mp->global); + + if (error) + { + fprintf(stderr, + "safe_mutex: Got error: %d (%d) when doing a safe_mutex_wait on " + "%s at %s, line %d\n", error, errno, mp->name, file, line); + fflush(stderr); + abort(); + } + /* Restore state as it was before */ + mp->thread= save_state.thread; + mp->active_flags= save_state.active_flags; + mp->next= save_state.next; + mp->prev= save_state.prev; + + if (mp->count++) + { + fprintf(stderr, + "safe_mutex: Count was %d in thread 0x%lx when locking mutex %s " + "at %s, line %d\n", + mp->count-1, my_thread_dbug_id(), mp->name, file, line); + fflush(stderr); + abort(); + } + mp->file= file; + mp->line=line; + pthread_mutex_unlock(&mp->global); + return error; +} + + +int safe_cond_timedwait(pthread_cond_t *cond, safe_mutex_t *mp, + struct timespec *abstime, + const char *file, uint line) +{ + int error; + safe_mutex_t save_state; + + pthread_mutex_lock(&mp->global); + if (mp->count != 1 || !pthread_equal(pthread_self(),mp->thread)) + { + fprintf(stderr, + "safe_mutex: Trying to cond_wait at %s, line %d on a not hold " + "mutex %s\n", + file, line, mp->name ? mp->name : "Null"); + fflush(stderr); + abort(); + } + mp->count--; /* Mutex will be released */ + save_state= *mp; + remove_from_active_list(mp); + pthread_mutex_unlock(&mp->global); + error=pthread_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_mutex_timedwait " + "on %s at %s, line %d\n", + error, errno, mp->name, file, line); + } +#endif + pthread_mutex_lock(&mp->global); + /* Restore state as it was before */ + mp->thread= save_state.thread; + mp->active_flags= save_state.active_flags; + mp->next= save_state.next; + mp->prev= save_state.prev; + + if (mp->count++) + { + fprintf(stderr, + "safe_mutex: Count was %d in thread 0x%lx when locking mutex " + "%s at %s, line %d (error: %d (%d))\n", + mp->count-1, my_thread_dbug_id(), mp->name, file, line, + error, error); + fflush(stderr); + abort(); + } + mp->file= file; + mp->line=line; + pthread_mutex_unlock(&mp->global); + return error; +} + + +int safe_mutex_destroy(safe_mutex_t *mp, const char *file, uint line) +{ + int error=0; + DBUG_ENTER("safe_mutex_destroy"); + DBUG_PRINT("enter", ("mutex: 0x%lx name: %s", (ulong) mp, mp->name)); + if (!mp->file) + { + fprintf(stderr, + "safe_mutex: Trying to destroy unitialized mutex at %s, line %d\n", + file, line); + fflush(stderr); + abort(); + } + if (mp->count != 0) + { + fprintf(stderr, + "safe_mutex: Trying to destroy a mutex %s that was locked at %s, " + "line %d at %s, line %d\n", + mp->name, mp->file, mp->line, file, line); + fflush(stderr); + abort(); + } + + /* Free all entries that points to this one */ + safe_mutex_free_deadlock_data(mp); + +#ifdef __WIN__ + pthread_mutex_destroy(&mp->global); + pthread_mutex_destroy(&mp->mutex); +#else + if (pthread_mutex_destroy(&mp->global)) + error=1; + if (pthread_mutex_destroy(&mp->mutex)) + error=1; +#endif + mp->file= 0; /* Mark destroyed */ + +#ifdef SAFE_MUTEX_DETECT_DESTROY + if (mp->info) + { + struct st_safe_mutex_info_t *info= mp->info; + pthread_mutex_lock(&THR_LOCK_mutex); + + if (info->prev) + info->prev->next = info->next; + else + safe_mutex_create_root = info->next; + if (info->next) + info->next->prev = info->prev; + safe_mutex_count--; + + pthread_mutex_unlock(&THR_LOCK_mutex); + free(info); + mp->info= NULL; /* Get crash if double free */ + } +#else + thread_safe_sub(safe_mutex_count, 1, &THR_LOCK_mutex); +#endif /* SAFE_MUTEX_DETECT_DESTROY */ + DBUG_RETURN(error); +} + + +/** + Free all data related to deadlock detection + + This is also useful together with safemalloc when you don't want to + have reports of not freed memory for mysys mutexes. +*/ + +void safe_mutex_free_deadlock_data(safe_mutex_t *mp) +{ + /* Free all entries that points to this one */ + if (!(mp->create_flags & MYF_NO_DEADLOCK_DETECTION)) + { + pthread_mutex_lock(&THR_LOCK_mutex); + my_hash_iterate(mp->used_mutex, + (my_hash_walk_action) remove_from_locked_mutex, + mp); + my_hash_iterate(mp->locked_mutex, + (my_hash_walk_action) remove_from_used_mutex, + mp); + pthread_mutex_unlock(&THR_LOCK_mutex); + + my_hash_free(mp->used_mutex); + my_hash_free(mp->locked_mutex); + my_free(mp->locked_mutex, 0); + mp->create_flags|= MYF_NO_DEADLOCK_DETECTION; + } +} + +/* + Free global resources and check that all mutex has been destroyed + + SYNOPSIS + safe_mutex_end() + file Print errors on this file + + NOTES + We can't use DBUG_PRINT() here as we have in my_end() disabled + DBUG handling before calling this function. + + In MySQL one may get one warning for a mutex created in my_thr_init.c + This is ok, as this thread may not yet have been exited. +*/ + +void safe_mutex_end(FILE *file __attribute__((unused))) +{ + if (!safe_mutex_count) /* safetly */ + pthread_mutex_destroy(&THR_LOCK_mutex); +#ifdef SAFE_MUTEX_DETECT_DESTROY + if (!file) + return; + + if (safe_mutex_count) + { + fprintf(file, "Warning: Not destroyed mutex: %lu\n", safe_mutex_count); + (void) fflush(file); + } + { + struct st_safe_mutex_info_t *ptr; + for (ptr= safe_mutex_create_root ; ptr ; ptr= ptr->next) + { + fprintf(file, "\tMutex %s initiated at line %4u in '%s'\n", + ptr->name, ptr->init_line, ptr->init_file); + (void) fflush(file); + } + } +#endif /* SAFE_MUTEX_DETECT_DESTROY */ +} + + +static my_bool add_used_to_locked_mutex(safe_mutex_t *used_mutex, + safe_mutex_deadlock_t *locked_mutex) +{ + /* Add mutex to all parent of the current mutex */ + if (!locked_mutex->warning_only) + { + (void) my_hash_iterate(locked_mutex->mutex->locked_mutex, + (my_hash_walk_action) add_to_locked_mutex, + used_mutex); + /* mark that locked_mutex is locked after used_mutex */ + (void) add_to_locked_mutex(locked_mutex, used_mutex); + } + return 0; +} + + +/** + register that locked_mutex was locked after current_mutex +*/ + +static my_bool add_to_locked_mutex(safe_mutex_deadlock_t *locked_mutex, + safe_mutex_t *current_mutex) +{ + DBUG_ENTER("add_to_locked_mutex"); + DBUG_PRINT("info", ("inserting 0x%lx into 0x%lx (id: %lu -> %lu)", + (ulong) locked_mutex, (long) current_mutex, + locked_mutex->id, current_mutex->id)); + if (my_hash_insert(current_mutex->locked_mutex, (uchar*) locked_mutex)) + { + /* Got mutex through two paths; ignore */ + DBUG_RETURN(0); + } + locked_mutex->count++; + if (my_hash_insert(locked_mutex->mutex->used_mutex, + (uchar*) current_mutex)) + { + DBUG_ASSERT(0); + } + DBUG_RETURN(0); +} + + +/** + Remove mutex from the locked mutex hash + @fn remove_from_used_mutex() + @param mp Mutex that has delete_mutex in it's locked_mutex hash + @param delete_mutex Mutex should be removed from the hash + + @notes + safe_mutex_deadlock_t entries in the locked hash are shared. + When counter goes to 0, we delete the safe_mutex_deadlock_t entry. +*/ + +static my_bool remove_from_locked_mutex(safe_mutex_t *mp, + safe_mutex_t *delete_mutex) +{ + safe_mutex_deadlock_t *found; + DBUG_ENTER("remove_from_locked_mutex"); + DBUG_PRINT("enter", ("delete_mutex: 0x%lx mutex: 0x%lx (id: %lu <- %lu)", + (ulong) delete_mutex, (ulong) mp, + delete_mutex->id, mp->id)); + + found= (safe_mutex_deadlock_t*) my_hash_search(mp->locked_mutex, + (uchar*) &delete_mutex->id, 0); + DBUG_ASSERT(found); + if (found) + { + if (my_hash_delete(mp->locked_mutex, (uchar*) found)) + { + DBUG_ASSERT(0); + } + if (!--found->count) + my_free(found, MYF(0)); + } + DBUG_RETURN(0); +} + +static my_bool remove_from_used_mutex(safe_mutex_deadlock_t *locked_mutex, + safe_mutex_t *mutex) +{ + DBUG_ENTER("remove_from_used_mutex"); + DBUG_PRINT("enter", ("delete_mutex: 0x%lx mutex: 0x%lx (id: %lu <- %lu)", + (ulong) mutex, (ulong) locked_mutex, + mutex->id, locked_mutex->id)); + if (my_hash_delete(locked_mutex->mutex->used_mutex, (uchar*) mutex)) + { + DBUG_ASSERT(0); + } + if (!--locked_mutex->count) + my_free(locked_mutex, MYF(0)); + DBUG_RETURN(0); +} + + +static void print_deadlock_warning(safe_mutex_t *new_mutex, + safe_mutex_t *parent_mutex) +{ + safe_mutex_t *mutex_root; + DBUG_ENTER("print_deadlock_warning"); + DBUG_PRINT("enter", ("mutex: %s parent: %s", + new_mutex->name, parent_mutex->name)); + + fprintf(stderr, "safe_mutex: Found wrong usage of mutex " + "'%s' and '%s'\n", + parent_mutex->name, new_mutex->name); + DBUG_PRINT("info", ("safe_mutex: Found wrong usage of mutex " + "'%s' and '%s'", + parent_mutex->name, new_mutex->name)); + fprintf(stderr, "Mutex currently locked (in reverse order):\n"); + DBUG_PRINT("info", ("Mutex currently locked (in reverse order):")); + fprintf(stderr, "%-32.32s %s line %u\n", new_mutex->name, new_mutex->file, + new_mutex->line); + DBUG_PRINT("info", ("%-32.32s %s line %u\n", new_mutex->name, + new_mutex->file, new_mutex->line)); + for (mutex_root= *my_thread_var_mutex_in_use() ; + mutex_root; + mutex_root= mutex_root->next) + { + fprintf(stderr, "%-32.32s %s line %u\n", mutex_root->name, + mutex_root->file, mutex_root->line); + DBUG_PRINT("info", ("%-32.32s %s line %u", mutex_root->name, + mutex_root->file, mutex_root->line)); + } + fflush(stderr); + DBUG_VOID_RETURN; +} + + +#endif /* THREAD && SAFE_MUTEX */ + +#if defined(THREAD) && defined(MY_PTHREAD_FASTMUTEX) && !defined(SAFE_MUTEX) + +#include "mysys_priv.h" +#include "my_static.h" +#include <m_string.h> + +#include <m_ctype.h> +#include <hash.h> +#include <myisampack.h> +#include <mysys_err.h> +#include <my_sys.h> + +#undef pthread_mutex_t +#undef pthread_mutex_init +#undef pthread_mutex_lock +#undef pthread_mutex_trylock +#undef pthread_mutex_unlock +#undef pthread_mutex_destroy +#undef pthread_cond_wait +#undef pthread_cond_timedwait + +ulong mutex_delay(ulong delayloops) +{ + ulong i; + volatile ulong j; + + j = 0; + + for (i = 0; i < delayloops * 50; i++) + j += i; + + return(j); +} + +#define MY_PTHREAD_FASTMUTEX_SPINS 8 +#define MY_PTHREAD_FASTMUTEX_DELAY 4 + +static int cpu_count= 0; + +int my_pthread_fastmutex_init(my_pthread_fastmutex_t *mp, + const pthread_mutexattr_t *attr) +{ + if ((cpu_count > 1) && (attr == MY_MUTEX_INIT_FAST)) + mp->spins= MY_PTHREAD_FASTMUTEX_SPINS; + else + mp->spins= 0; + mp->rng_state= 1; + return pthread_mutex_init(&mp->mutex, attr); +} + +/** + Park-Miller random number generator. A simple linear congruential + generator that operates in multiplicative group of integers modulo n. + + x_{k+1} = (x_k g) mod n + + Popular pair of parameters: n = 2^32 − 5 = 4294967291 and g = 279470273. + The period of the generator is about 2^31. + Largest value that can be returned: 2147483646 (RAND_MAX) + + Reference: + + S. K. Park and K. W. Miller + "Random number generators: good ones are hard to find" + Commun. ACM, October 1988, Volume 31, No 10, pages 1192-1201. +*/ + +static double park_rng(my_pthread_fastmutex_t *mp) +{ + mp->rng_state= ((my_ulonglong)mp->rng_state * 279470273U) % 4294967291U; + return (mp->rng_state / 2147483647.0); +} + +int my_pthread_fastmutex_lock(my_pthread_fastmutex_t *mp) +{ + int res; + uint i; + uint maxdelay= MY_PTHREAD_FASTMUTEX_DELAY; + + for (i= 0; i < mp->spins; i++) + { + res= pthread_mutex_trylock(&mp->mutex); + + if (res == 0) + return 0; + + if (res != EBUSY) + return res; + + mutex_delay(maxdelay); + maxdelay += park_rng(mp) * MY_PTHREAD_FASTMUTEX_DELAY + 1; + } + return pthread_mutex_lock(&mp->mutex); +} + + +void fastmutex_global_init(void) +{ +#ifdef _SC_NPROCESSORS_CONF + cpu_count= sysconf(_SC_NPROCESSORS_CONF); +#endif +} + +#endif /* defined(THREAD) && defined(MY_PTHREAD_FASTMUTEX) && !defined(SAFE_MUTEX) */ diff --git a/externals/mysql/mysys/thr_rwlock.c b/externals/mysql/mysys/thr_rwlock.c new file mode 100644 index 00000000000..280a0ec19e7 --- /dev/null +++ b/externals/mysql/mysys/thr_rwlock.c @@ -0,0 +1,171 @@ +/* Copyright (C) 2000 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +/* Synchronization - readers / writer thread locks */ + +#include "mysys_priv.h" +#if defined(THREAD) && !defined(HAVE_PTHREAD_RWLOCK_RDLOCK) && !defined(HAVE_RWLOCK_INIT) +#include <errno.h> + +/* + Source base from Sun Microsystems SPILT, simplified for MySQL use + -- Joshua Chamas + Some cleanup and additional code by Monty +*/ + +/* +* Multithreaded Demo Source +* +* Copyright (C) 1995 by Sun Microsystems, Inc. +* All rights reserved. +* +* This file is a product of SunSoft, Inc. and is provided for +* unrestricted use provided that this legend is included on all +* media and as a part of the software program in whole or part. +* Users may copy, modify or distribute this file at will. +* +* THIS FILE IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING +* THE WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR +* PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. +* +* This file is provided with no support and without any obligation on the +* part of SunSoft, Inc. to assist in its use, correction, modification or +* enhancement. +* +* SUNSOFT AND SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT +* TO THE INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY THIS +* FILE OR ANY PART THEREOF. +* +* IN NO EVENT WILL SUNSOFT OR SUN MICROSYSTEMS, INC. BE LIABLE FOR ANY +* LOST REVENUE OR PROFITS OR OTHER SPECIAL, INDIRECT AND CONSEQUENTIAL +* DAMAGES, EVEN IF THEY HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH +* DAMAGES. +* +* SunSoft, Inc. +* 2550 Garcia Avenue +* Mountain View, California 94043 +*/ + +int my_rwlock_init(rw_lock_t *rwp, void *arg __attribute__((unused))) +{ + pthread_condattr_t cond_attr; + + pthread_mutex_init( &rwp->lock, MY_MUTEX_INIT_FAST); + pthread_condattr_init( &cond_attr ); + pthread_cond_init( &rwp->readers, &cond_attr ); + pthread_cond_init( &rwp->writers, &cond_attr ); + pthread_condattr_destroy(&cond_attr); + + rwp->state = 0; + rwp->waiters = 0; + + return(0); +} + + +int my_rwlock_destroy(rw_lock_t *rwp) +{ + pthread_mutex_destroy( &rwp->lock ); + pthread_cond_destroy( &rwp->readers ); + pthread_cond_destroy( &rwp->writers ); + return(0); +} + + +int my_rw_rdlock(rw_lock_t *rwp) +{ + pthread_mutex_lock(&rwp->lock); + + /* active or queued writers */ + while ((rwp->state < 0 ) || rwp->waiters) + pthread_cond_wait( &rwp->readers, &rwp->lock); + + rwp->state++; + pthread_mutex_unlock(&rwp->lock); + return(0); +} + +int my_rw_tryrdlock(rw_lock_t *rwp) +{ + int res; + pthread_mutex_lock(&rwp->lock); + if ((rwp->state < 0 ) || rwp->waiters) + res= EBUSY; /* Can't get lock */ + else + { + res=0; + rwp->state++; + } + pthread_mutex_unlock(&rwp->lock); + return(res); +} + + +int my_rw_wrlock(rw_lock_t *rwp) +{ + pthread_mutex_lock(&rwp->lock); + rwp->waiters++; /* another writer queued */ + + while (rwp->state) + pthread_cond_wait(&rwp->writers, &rwp->lock); + rwp->state = -1; + rwp->waiters--; + pthread_mutex_unlock(&rwp->lock); + return(0); +} + + +int my_rw_trywrlock(rw_lock_t *rwp) +{ + int res; + pthread_mutex_lock(&rwp->lock); + if (rwp->state) + res= EBUSY; /* Can't get lock */ + else + { + res=0; + rwp->state = -1; + } + pthread_mutex_unlock(&rwp->lock); + return(res); +} + + +int my_rw_unlock(rw_lock_t *rwp) +{ + DBUG_PRINT("rw_unlock", + ("state: %d waiters: %d", rwp->state, rwp->waiters)); + pthread_mutex_lock(&rwp->lock); + + if (rwp->state == -1) /* writer releasing */ + { + rwp->state= 0; /* mark as available */ + + if ( rwp->waiters ) /* writers queued */ + pthread_cond_signal( &rwp->writers ); + else + pthread_cond_broadcast( &rwp->readers ); + } + else + { + if ( --rwp->state == 0 ) /* no more readers */ + pthread_cond_signal( &rwp->writers ); + } + + pthread_mutex_unlock( &rwp->lock ); + return(0); +} + +#endif diff --git a/externals/mysql/mysys/tree.c b/externals/mysql/mysys/tree.c new file mode 100644 index 00000000000..e721cc0cc5e --- /dev/null +++ b/externals/mysql/mysys/tree.c @@ -0,0 +1,757 @@ +/* Copyright (C) 2000 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 <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, ulong default_alloc_size, ulong memory_limit, + int size, qsort_cmp2 compare, my_bool with_delete, + tree_element_free free_element, void *custom_arg) +{ + DBUG_ENTER("init_tree"); + DBUG_PRINT("enter",("tree: %p size: %d", 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); + bzero((uchar*) &tree->null_element,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(&tree->mem_root, (uint) 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: %p", 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((char*) element,MYF(0)); + } +} + + +/* + 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, + 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(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; /* May give warning in purify */ + 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, 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((uchar*) element,MYF(0)); + tree->elements_in_tree--; + return 0; +} + + +void *tree_search(TREE *tree, void *key, 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, void *custom_arg) +{ + int cmp; + TREE_ELEMENT *element= tree->root; + TREE_ELEMENT **last_left_step_parent= NULL, **last_right_step_parent= NULL; + TREE_ELEMENT **last_equal_element= NULL; + +/* + TODO: support for HA_READ_KEY_OR_PREV, HA_READ_PREFIX flags if needed. +*/ + + *parents = &tree->null_element; + while (element != &tree->null_element) + { + *++parents= element; + if ((cmp= (*tree->compare)(custom_arg, ELEMENT_KEY(tree, element), + key)) == 0) + { + switch (flag) { + case HA_READ_KEY_EXACT: + case HA_READ_KEY_OR_NEXT: + case HA_READ_BEFORE_KEY: + last_equal_element= parents; + cmp= 1; + break; + case HA_READ_AFTER_KEY: + cmp= -1; + break; + case HA_READ_PREFIX_LAST: + case HA_READ_PREFIX_LAST_OR_PREV: + last_equal_element= parents; + cmp= -1; + break; + default: + return NULL; + } + } + if (cmp < 0) /* element < key */ + { + last_right_step_parent= parents; + element= element->right; + } + else + { + last_left_step_parent= parents; + element= element->left; + } + } + switch (flag) { + case HA_READ_KEY_EXACT: + case HA_READ_PREFIX_LAST: + *last_pos= last_equal_element; + break; + case HA_READ_KEY_OR_NEXT: + *last_pos= last_equal_element ? last_equal_element : last_left_step_parent; + break; + case HA_READ_AFTER_KEY: + *last_pos= last_left_step_parent; + break; + case HA_READ_PREFIX_LAST_OR_PREV: + *last_pos= last_equal_element ? last_equal_element : last_right_step_parent; + break; + case HA_READ_BEFORE_KEY: + *last_pos= last_right_step_parent; + break; + default: + return NULL; + } + return *last_pos ? ELEMENT_KEY(tree, **last_pos) : NULL; +} + +/* + Search first (the most left) or last (the most right) tree element +*/ +void *tree_search_edge(TREE *tree, TREE_ELEMENT **parents, + TREE_ELEMENT ***last_pos, int child_offs) +{ + TREE_ELEMENT *element= tree->root; + + *parents= &tree->null_element; + while (element != &tree->null_element) + { + *++parents= element; + element= ELEMENT_CHILD(element, child_offs); + } + *last_pos= parents; + return **last_pos != &tree->null_element ? + ELEMENT_KEY(tree, **last_pos) : NULL; +} + +void *tree_search_next(TREE *tree, TREE_ELEMENT ***last_pos, int l_offs, + int r_offs) +{ + TREE_ELEMENT *x= **last_pos; + + if (ELEMENT_CHILD(x, r_offs) != &tree->null_element) + { + x= ELEMENT_CHILD(x, r_offs); + *++*last_pos= x; + while (ELEMENT_CHILD(x, l_offs) != &tree->null_element) + { + x= ELEMENT_CHILD(x, l_offs); + *++*last_pos= x; + } + return ELEMENT_KEY(tree, x); + } + else + { + TREE_ELEMENT *y= *--*last_pos; + while (y != &tree->null_element && x == ELEMENT_CHILD(y, r_offs)) + { + x= y; + y= *--*last_pos; + } + return y == &tree->null_element ? NULL : ELEMENT_KEY(tree, y); + } +} + +/* + Expected that tree is fully balanced + (each path from root to leaf has the same length) +*/ +ha_rows tree_record_pos(TREE *tree, const void *key, + enum ha_rkey_function flag, void *custom_arg) +{ + int cmp; + TREE_ELEMENT *element= tree->root; + double left= 1; + double right= tree->elements_in_tree; + + while (element != &tree->null_element) + { + if ((cmp= (*tree->compare)(custom_arg, ELEMENT_KEY(tree, element), + key)) == 0) + { + switch (flag) { + case HA_READ_KEY_EXACT: + case HA_READ_BEFORE_KEY: + cmp= 1; + break; + case HA_READ_AFTER_KEY: + cmp= -1; + break; + default: + return HA_POS_ERROR; + } + } + if (cmp < 0) /* element < key */ + { + element= element->right; + left= (left + right) / 2; + } + else + { + element= element->left; + right= (left + right) / 2; + } + } + switch (flag) { + case HA_READ_KEY_EXACT: + case HA_READ_BEFORE_KEY: + return (ha_rows) right; + case HA_READ_AFTER_KEY: + return (ha_rows) left; + default: + return HA_POS_ERROR; + } +} + +int tree_walk(TREE *tree, tree_walk_action action, void *argument, TREE_WALK visit) +{ + 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/externals/mysql/mysys/trie.c b/externals/mysql/mysys/trie.c new file mode 100644 index 00000000000..5738b9b866b --- /dev/null +++ b/externals/mysql/mysys/trie.c @@ -0,0 +1,236 @@ +/* Copyright (C) 2005 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +/* + Implementation of trie and Aho-Corasick automaton. + Supports only charsets that can be compared byte-wise. + + TODO: + Add character frequencies. Can increase lookup speed + up to 30%. + Implement character-wise comparision. +*/ + + +#include "mysys_priv.h" +#include <m_string.h> +#include <my_trie.h> +#include <my_base.h> + + +/* + SYNOPSIS + TRIE *trie_init (TRIE *trie, CHARSET_INFO *charset); + + DESCRIPTION + Allocates or initializes a `TRIE' object. If `trie' is a `NULL' + pointer, the function allocates, initializes, and returns a new + object. Otherwise, the object is initialized and the address of + the object is returned. If `trie_init()' allocates a new object, + it will be freed when `trie_free()' is called. + + RETURN VALUE + An initialized `TRIE*' object. `NULL' if there was insufficient + memory to allocate a new object. +*/ + +TRIE *trie_init (TRIE *trie, CHARSET_INFO *charset) +{ + MEM_ROOT mem_root; + DBUG_ENTER("trie_init"); + DBUG_ASSERT(charset); + init_alloc_root(&mem_root, + (sizeof(TRIE_NODE) * 128) + ALLOC_ROOT_MIN_BLOCK_SIZE, + sizeof(TRIE_NODE) * 128); + if (! trie) + { + if (! (trie= (TRIE *)alloc_root(&mem_root, sizeof(TRIE)))) + { + free_root(&mem_root, MYF(0)); + DBUG_RETURN(NULL); + } + } + + memcpy(&trie->mem_root, &mem_root, sizeof(MEM_ROOT)); + trie->root.leaf= 0; + trie->root.c= 0; + trie->root.next= NULL; + trie->root.links= NULL; + trie->root.fail= NULL; + trie->charset= charset; + trie->nnodes= 0; + trie->nwords= 0; + DBUG_RETURN(trie); +} + + +/* + SYNOPSIS + void trie_free (TRIE *trie); + trie - valid pointer to `TRIE' + + DESCRIPTION + Frees the memory allocated for a `trie'. + + RETURN VALUE + None. +*/ + +void trie_free (TRIE *trie) +{ + MEM_ROOT mem_root; + DBUG_ENTER("trie_free"); + DBUG_ASSERT(trie); + memcpy(&mem_root, &trie->mem_root, sizeof(MEM_ROOT)); + free_root(&mem_root, MYF(0)); + DBUG_VOID_RETURN; +} + + +/* + SYNOPSIS + my_bool trie_insert (TRIE *trie, const uchar *key, uint keylen); + trie - valid pointer to `TRIE' + key - valid pointer to key to insert + keylen - non-0 key length + + DESCRIPTION + Inserts new key into trie. + + RETURN VALUE + Upon successful completion, `trie_insert' returns `FALSE'. Otherwise + `TRUE' is returned. + + NOTES + If this function fails you must assume `trie' is broken. + However it can be freed with trie_free(). +*/ + +my_bool trie_insert (TRIE *trie, const uchar *key, uint keylen) +{ + TRIE_NODE *node; + TRIE_NODE *next; + uchar p; + uint k; + DBUG_ENTER("trie_insert"); + DBUG_ASSERT(trie && key && keylen); + node= &trie->root; + trie->root.fail= NULL; + for (k= 0; k < keylen; k++) + { + p= key[k]; + for (next= node->links; next; next= next->next) + if (next->c == p) + break; + + if (! next) + { + TRIE_NODE *tmp= (TRIE_NODE *)alloc_root(&trie->mem_root, + sizeof(TRIE_NODE)); + if (! tmp) + DBUG_RETURN(TRUE); + tmp->leaf= 0; + tmp->c= p; + tmp->links= tmp->fail= tmp->next= NULL; + trie->nnodes++; + if (! node->links) + { + node->links= tmp; + } + else + { + for (next= node->links; next->next; next= next->next) /* no-op */; + next->next= tmp; + } + node= tmp; + } + else + { + node= next; + } + } + node->leaf= keylen; + trie->nwords++; + DBUG_RETURN(FALSE); +} + + +/* + SYNOPSIS + my_bool trie_prepare (TRIE *trie); + trie - valid pointer to `TRIE' + + DESCRIPTION + Constructs Aho-Corasick automaton. + + RETURN VALUE + Upon successful completion, `trie_prepare' returns `FALSE'. Otherwise + `TRUE' is returned. +*/ + +my_bool ac_trie_prepare (TRIE *trie) +{ + TRIE_NODE **tmp_nodes; + TRIE_NODE *node; + uint32 fnode= 0; + uint32 lnode= 0; + DBUG_ENTER("trie_prepare"); + DBUG_ASSERT(trie); + + tmp_nodes= (TRIE_NODE **)my_malloc(trie->nnodes * sizeof(TRIE_NODE *), MYF(0)); + if (! tmp_nodes) + DBUG_RETURN(TRUE); + + trie->root.fail= &trie->root; + for (node= trie->root.links; node; node= node->next) + { + node->fail= &trie->root; + tmp_nodes[lnode++]= node; + } + + while (fnode < lnode) + { + TRIE_NODE *current= (TRIE_NODE *)tmp_nodes[fnode++]; + for (node= current->links; node; node= node->next) + { + TRIE_NODE *fail= current->fail; + tmp_nodes[lnode++]= node; + while (! (node->fail= trie_goto(&trie->root, fail, node->c))) + fail= fail->fail; + } + } + my_free((uchar*)tmp_nodes, MYF(0)); + DBUG_RETURN(FALSE); +} + + +/* + SYNOPSIS + void ac_trie_init (TRIE *trie, AC_TRIE_STATE *state); + trie - valid pointer to `TRIE' + state - value pointer to `AC_TRIE_STATE' + + DESCRIPTION + Initializes `AC_TRIE_STATE' object. +*/ + +void ac_trie_init (TRIE *trie, AC_TRIE_STATE *state) +{ + DBUG_ENTER("ac_trie_init"); + DBUG_ASSERT(trie && state); + state->trie= trie; + state->node= &trie->root; + DBUG_VOID_RETURN; +} diff --git a/externals/mysql/mysys/typelib.c b/externals/mysql/mysys/typelib.c new file mode 100644 index 00000000000..5c3e30488d7 --- /dev/null +++ b/externals/mysql/mysys/typelib.c @@ -0,0 +1,243 @@ +/* Copyright (C) 2000 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +/* Functions to handle typelib */ + +#include "mysys_priv.h" +#include <m_string.h> +#include <m_ctype.h> + + +static const char field_separator=','; + +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, 2)) <= 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. + + SYNOPSIS + find_type() + x String to find + lib TYPELIB (struct of pointer to values + count) + full_name bitmap of what to do + If & 1 accept only whole names + If & 2 don't expand if half field + If & 4 allow #number# as type + If & 8 use ',' as string terminator + + NOTES + If part, uniq field is found and full_name == 0 then x is expanded + to full field. + + RETURN + -1 Too many matching values + 0 No matching value + >0 Offset+1 in typelib for matched string +*/ + + +int find_type(char *x, const TYPELIB *typelib, uint full_name) +{ + int find,pos,findpos; + reg1 char * i; + reg2 const char *j; + DBUG_ENTER("find_type"); + DBUG_PRINT("enter",("x: '%s' lib: %p", x, typelib)); + + if (!typelib->count) + { + DBUG_PRINT("exit",("no count")); + DBUG_RETURN(0); + } + LINT_INIT(findpos); + find=0; + for (pos=0 ; (j=typelib->type_names[pos]) ; pos++) + { + for (i=x ; + *i && (!(full_name & 8) || *i != field_separator) && + my_toupper(&my_charset_latin1,*i) == + my_toupper(&my_charset_latin1,*j) ; i++, j++) ; + if (! *j) + { + while (*i == ' ') + i++; /* skip_end_space */ + if (! *i || ((full_name & 8) && *i == field_separator)) + DBUG_RETURN(pos+1); + } + if ((!*i && (!(full_name & 8) || *i != field_separator)) && + (!*j || !(full_name & 1))) + { + find++; + findpos=pos; + } + } + if (find == 0 && (full_name & 4) && 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 || (full_name & 1)) + { + DBUG_PRINT("exit",("Too many possybilities")); + DBUG_RETURN(-1); + } + if (!(full_name & 2)) + (void) strmov(x,typelib->type_names[findpos]); + DBUG_RETURN(findpos+1); +} /* find_type */ + + + /* Get name of type nr 'nr' */ + /* Warning first type is 1, 0 = empty field */ + +void make_type(register char * to, register uint nr, + register TYPELIB *typelib) +{ + DBUG_ENTER("make_type"); + if (!nr) + to[0]=0; + else + (void) strmov(to,get_type(typelib,nr-1)); + DBUG_VOID_RETURN; +} /* make_type */ + + + /* Get type */ + /* Warning 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. + + SYNOPSIS + find_typeset() + x string to decompose + lib TYPELIB (struct of pointer to values + count) + err index (not char position) of string element which was not + found or 0 if there was no error + + RETURN + 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: %p", x, lib)); + + if (!lib->count) + { + DBUG_PRINT("exit",("no count")); + DBUG_RETURN(0); + } + result= 0; + *err= 0; + while (*x) + { + (*err)++; + i= x; + while (*x && *x != field_separator) x++; + if ((find= find_type(i, lib, 2 | 8) - 1) < 0) + DBUG_RETURN(0); + result|= (1ULL << find); + } + *err= 0; + DBUG_RETURN(result); +} /* find_set */ + + +/* + Create a copy of a specified TYPELIB structure. + + SYNOPSIS + copy_typelib() + root pointer to a MEM_ROOT object for allocations + from pointer to a source TYPELIB structure + + RETURN + pointer to the new TYPELIB structure on successful copy, or + 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; +} diff --git a/externals/mysql/mysys/waiting_threads.c b/externals/mysql/mysys/waiting_threads.c new file mode 100644 index 00000000000..46b3c1d4910 --- /dev/null +++ b/externals/mysql/mysys/waiting_threads.c @@ -0,0 +1,1153 @@ +/* Copyright (C) 2008 MySQL AB, 2008-2009 Sun Microsystems, Inc. + + 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +/** + @file + + "waiting threads" subsystem - a unified interface for threads to wait + on each other, with built-in deadlock detection. + + Main concepts + ^^^^^^^^^^^^^ + a thread - is represented by a WT_THD structure. One physical thread + can have only one WT_THD descriptor at any given moment. + + a resource - a thread does not wait for other threads directly, + instead it waits for a "resource", which is "owned" by other threads. + It waits, exactly, for all "owners" to "release" a resource. + It does not have to correspond to a physical resource. For example, it + may be convenient in certain cases to force resource == thread. + A resource is represented by a WT_RESOURCE structure. + + a resource identifier - a pair of {resource type, value}. A value is + an ulonglong number. Represented by a WT_RESOURCE_ID structure. + + a resource type - a pointer to a statically defined instance of + WT_RESOURCE_TYPE structure. This structure contains a pointer to + a function that knows how to compare values of this resource type. + In the simple case it could be wt_resource_id_memcmp(). + + a wait-for graph - a graph, that represenst "wait-for" relationships. + It has two types of nodes - threads and resources. There are directed + edges from a thread to a resource it is waiting for (WT_THD::waiting_for), + from a thread to resources that it "owns" (WT_THD::my_resources), + and from a resource to threads that "own" it (WT_RESOURCE::owners) + + Graph completeness + ^^^^^^^^^^^^^^^^^^ + + For flawless deadlock detection wait-for graph must be complete. + It means that when a thread starts waiting it needs to know *all* its + blockers, and call wt_thd_will_wait_for() for every one of them. + Otherwise two phenomena should be expected: + + 1. Fuzzy timeouts: + + thread A needs to get a lock, and is blocked by a thread B. + it waits. + Just before the timeout thread B releases the lock. + thread A is ready to grab the lock but discovers that it is also + blocked by a thread C. + It waits and times out. + + As a result thread A has waited two timeout intervals, instead of one. + + 2. Unreliable cycle detection: + + Thread A waits for threads B and C + Thread C waits for D + Thread D wants to start waiting for A + + one can see immediately that thread D creates a cycle, and thus + a deadlock is detected. + + But if thread A would only wait for B, and start waiting for C + when B would unlock, thread D would be allowed to wait, a deadlock + would be only detected when B unlocks or somebody times out. + + These two phenomena don't affect a correctness, and strictly speaking, + the caller is not required to call wt_thd_will_wait_for() for *all* + blockers - it may optimize wt_thd_will_wait_for() calls. But they + may be perceived as bugs by users, it must be understood that such + an optimization comes with its price. + + Usage + ^^^^^ + + First, the wt* subsystem must be initialized by calling + wt_init(). In the server you don't need to do it, it's done + in mysqld.cc. + + Similarly, wt_end() frees wt* structures, should be called + at the end, but in the server mysqld.cc takes care of that. + + Every WT_THD should be initialized with wt_thd_lazy_init(). + After that they can be used in other wt_thd_* calls. + Before discarding, WT_THD should be free'd with + wt_thd_destroy(). In the server both are handled in sql_class.cc, + it's an error to try to do it manually. + + To use the deadlock detection one needs to use this thread's WT_THD, + call wt_thd_will_wait_for() for every thread it needs to wait on, + then call wt_thd_cond_timedwait(). When thread releases a resource + it should call wt_thd_release() (or wt_thd_release_all()) - it will + notify (send a signal) threads waiting in wt_thd_cond_timedwait(), + if appropriate. + + Just like with pthread's cond_wait, there could be spurious + wake-ups from wt_thd_cond_timedwait(). A caller is expected to + handle that (that is, to re-check the blocking criteria). + + wt_thd_will_wait_for() and wt_thd_cond_timedwait() return either + WT_OK or WT_DEADLOCK. Additionally wt_thd_cond_timedwait() can return + WT_TIMEOUT. Out of memory and other fatal errors are reported as + WT_DEADLOCK - and a transaction must be aborted just the same. + + Configuration + ^^^^^^^^^^^^^ + There are four config variables. Two deadlock search depths - short and + long - and two timeouts. Deadlock search is performed with the short + depth on every wt_thd_will_wait_for() call. wt_thd_cond_timedwait() + waits with a short timeout, performs a deadlock search with the long + depth, and waits with a long timeout. As most deadlock cycles are supposed + to be short, most deadlocks will be detected at once, and waits will + rarely be necessary. + + These config variables are thread-local. Different threads may have + different search depth and timeout values. + + Also, deadlock detector supports different killing strategies, the victim + in a deadlock cycle is selected based on the "weight". See "weight" + description in waiting_threads.h for details. It's up to the caller to + set weights accordingly. + + Status + ^^^^^^ + We calculate the number of successfull waits (WT_OK returned from + wt_thd_cond_timedwait()), a number of timeouts, a deadlock cycle + length distribution - number of deadlocks with every length from + 1 to WT_CYCLE_STATS, and a wait time distribution - number + of waits with a time from 1 us to 1 min in WT_WAIT_STATS + intervals on a log e scale. +*/ + +/* + Note that if your lock system satisfy the following condition: + + there exist four lock levels A, B, C, D, such as + A is compatible with B + A is not compatible with C + D is not compatible with B + + (example A=IX, B=IS, C=S, D=X) + + you need to include lock level in the resource identifier - a + thread waiting for lock of the type A on resource R and another + thread waiting for lock of the type B on resource R should wait on + different WT_RESOURCE structures, on different {lock, resource} + pairs. Otherwise the following is possible: + + thread1> take S-lock on R + thread2> take IS-lock on R + thread3> wants X-lock on R, starts waiting for threads 1 and 2 on R. + thread3 is killed (or timeout or whatever) + WT_RESOURCE structure for R is still in the hash, as it has two owners + thread4> wants an IX-lock on R + WT_RESOURCE for R is found in the hash, thread4 starts waiting on it. + !! now thread4 is waiting for both thread1 and thread2 + !! while, in fact, IX-lock and IS-lock are compatible and + !! thread4 should not wait for thread2. +*/ + +#include <waiting_threads.h> +#include <m_string.h> + +/* status variables */ + +/** + preset table of wait intervals +*/ +ulonglong wt_wait_table[WT_WAIT_STATS]; +/** + wait time distribution (log e scale) +*/ +uint32 wt_wait_stats[WT_WAIT_STATS+1]; +/** + distribution of cycle lengths + first column tells whether this was during short or long detection +*/ +uint32 wt_cycle_stats[2][WT_CYCLE_STATS+1]; +uint32 wt_success_stats; + +static my_atomic_rwlock_t cycle_stats_lock, wait_stats_lock, success_stats_lock; + +#ifdef SAFE_STATISTICS +#define incr(VAR, LOCK) \ + do { \ + my_atomic_rwlock_wrlock(&(LOCK)); \ + my_atomic_add32(&(VAR), 1); \ + my_atomic_rwlock_wrunlock(&(LOCK)); \ + } while(0) +#else +#define incr(VAR,LOCK) do { (VAR)++; } while(0) +#endif + +static void increment_success_stats() +{ + incr(wt_success_stats, success_stats_lock); +} + +static void increment_cycle_stats(uint depth, uint slot) +{ + if (depth >= WT_CYCLE_STATS) + depth= WT_CYCLE_STATS; + incr(wt_cycle_stats[slot][depth], cycle_stats_lock); +} + +static void increment_wait_stats(ulonglong waited,int ret) +{ + uint i; + if ((ret) == ETIMEDOUT) + i= WT_WAIT_STATS; + else + for (i= 0; i < WT_WAIT_STATS && waited/10 > wt_wait_table[i]; i++) ; + incr(wt_wait_stats[i], wait_stats_lock); +} + +/* + 'lock' protects 'owners', 'state', and 'waiter_count' + 'id' is read-only + + a resource is picked up from a hash in a lock-free manner + it's returned pinned, so it cannot be freed at once + but it may be freed right after the pin is removed + to free a resource it should + 1. have no owners + 2. have no waiters + + two ways to access a resource: + 1. find it in a hash + - it's returned pinned. + a) take a lock in exclusive mode + b) check the state, it should be ACTIVE to be usable + c) unpin + 2. by a direct reference + - could only used if a resource cannot be freed + e.g. accessing a resource by thd->waiting_for is safe, + a resource cannot be freed as there's a thread waiting for it +*/ +struct st_wt_resource { + WT_RESOURCE_ID id; + uint waiter_count; + enum { ACTIVE, FREE } state; +#ifndef DBUG_OFF + pthread_mutex_t *cond_mutex; /* a mutex for the 'cond' below */ +#endif + /* + before the 'lock' all elements are mutable, after (and including) - + immutable in the sense that lf_hash_insert() won't memcpy() over them. + See wt_init(). + */ +#ifdef WT_RWLOCKS_USE_MUTEXES + /* + we need a special rwlock-like 'lock' to allow readers bypass + waiting writers, otherwise readers can deadlock. For example: + + A waits on resource x, owned by B, B waits on resource y, owned + by A, we have a cycle (A->x->B->y->A) + Both A and B start deadlock detection: + + A locks x B locks y + A goes deeper B goes deeper + A locks y B locks x + + with mutexes it would deadlock. With rwlocks it won't, as long + as both A and B are taking read locks (and they do). + But other threads may take write locks. Assume there's + C who wants to start waiting on x, and D who wants to start + waiting on y. + + A read-locks x B read-locks y + A goes deeper B goes deeper + => C write-locks x (to add a new edge) D write-locks y + .. C is blocked D is blocked + A read-locks y B read-locks x + + Now, if a read lock can bypass a pending wrote lock request, we're fine. + If it can not, we have a deadlock. + + writer starvation is technically possible, but unlikely, because + the contention is expected to be low. + */ + struct { + pthread_cond_t cond; + pthread_mutex_t mutex; + uint readers: 16; + uint pending_writers: 15; + uint write_locked: 1; + } lock; +#else + rw_lock_t lock; +#endif + pthread_cond_t cond; /* the corresponding mutex is provided by the caller */ + DYNAMIC_ARRAY owners; +}; + +#ifdef WT_RWLOCKS_USE_MUTEXES +static void rc_rwlock_init(WT_RESOURCE *rc) +{ + pthread_cond_init(&rc->lock.cond, 0); + pthread_mutex_init(&rc->lock.mutex, MY_MUTEX_INIT_FAST); +} +static void rc_rwlock_destroy(WT_RESOURCE *rc) +{ + DBUG_ASSERT(rc->lock.write_locked == 0); + DBUG_ASSERT(rc->lock.readers == 0); + pthread_cond_destroy(&rc->lock.cond); + pthread_mutex_destroy(&rc->lock.mutex); +} +static void rc_rdlock(WT_RESOURCE *rc) +{ + DBUG_PRINT("wt", ("TRYLOCK resid=%ld for READ", (ulong)rc->id.value)); + pthread_mutex_lock(&rc->lock.mutex); + while (rc->lock.write_locked) + pthread_cond_wait(&rc->lock.cond, &rc->lock.mutex); + rc->lock.readers++; + pthread_mutex_unlock(&rc->lock.mutex); + DBUG_PRINT("wt", ("LOCK resid=%ld for READ", (ulong)rc->id.value)); +} +static void rc_wrlock(WT_RESOURCE *rc) +{ + DBUG_PRINT("wt", ("TRYLOCK resid=%ld for WRITE", (ulong)rc->id.value)); + pthread_mutex_lock(&rc->lock.mutex); + while (rc->lock.write_locked || rc->lock.readers) + pthread_cond_wait(&rc->lock.cond, &rc->lock.mutex); + rc->lock.write_locked= 1; + pthread_mutex_unlock(&rc->lock.mutex); + DBUG_PRINT("wt", ("LOCK resid=%ld for WRITE", (ulong)rc->id.value)); +} +static void rc_unlock(WT_RESOURCE *rc) +{ + DBUG_PRINT("wt", ("UNLOCK resid=%ld", (ulong)rc->id.value)); + pthread_mutex_lock(&rc->lock.mutex); + if (rc->lock.write_locked) + { + rc->lock.write_locked= 0; + pthread_cond_broadcast(&rc->lock.cond); + } + else if (--rc->lock.readers == 0) + pthread_cond_broadcast(&rc->lock.cond); + pthread_mutex_unlock(&rc->lock.mutex); +} +#else +static void rc_rwlock_init(WT_RESOURCE *rc) +{ + my_rwlock_init(&rc->lock, 0); +} +static void rc_rwlock_destroy(WT_RESOURCE *rc) +{ + rwlock_destroy(&rc->lock); +} +static void rc_rdlock(WT_RESOURCE *rc) +{ + DBUG_PRINT("wt", ("TRYLOCK resid=%ld for READ", (ulong)rc->id.value)); + rw_rdlock(&rc->lock); + DBUG_PRINT("wt", ("LOCK resid=%ld for READ", (ulong)rc->id.value)); +} +static void rc_wrlock(WT_RESOURCE *rc) +{ + DBUG_PRINT("wt", ("TRYLOCK resid=%ld for WRITE", (ulong)rc->id.value)); + rw_wrlock(&rc->lock); + DBUG_PRINT("wt", ("LOCK resid=%ld for WRITE", (ulong)rc->id.value)); +} +static void rc_unlock(WT_RESOURCE *rc) +{ + DBUG_PRINT("wt", ("UNLOCK resid=%ld", (ulong)rc->id.value)); + rw_unlock(&rc->lock); +} +#endif + +/* + All resources are stored in a lock-free hash. Different threads + may add new resources and perform deadlock detection concurrently. +*/ +static LF_HASH reshash; + +/** + WT_RESOURCE constructor + + It's called from lf_hash and takes a pointer to an LF_SLIST instance. + WT_RESOURCE is located at arg+sizeof(LF_SLIST) +*/ +static void wt_resource_init(uchar *arg) +{ + WT_RESOURCE *rc= (WT_RESOURCE*)(arg+LF_HASH_OVERHEAD); + DBUG_ENTER("wt_resource_init"); + + bzero(rc, sizeof(*rc)); + rc_rwlock_init(rc); + pthread_cond_init(&rc->cond, 0); + my_init_dynamic_array(&rc->owners, sizeof(WT_THD *), 0, 5); + DBUG_VOID_RETURN; +} + +/** + WT_RESOURCE destructor + + It's called from lf_hash and takes a pointer to an LF_SLIST instance. + WT_RESOURCE is located at arg+sizeof(LF_SLIST) +*/ +static void wt_resource_destroy(uchar *arg) +{ + WT_RESOURCE *rc= (WT_RESOURCE*)(arg+LF_HASH_OVERHEAD); + DBUG_ENTER("wt_resource_destroy"); + + DBUG_ASSERT(rc->owners.elements == 0); + rc_rwlock_destroy(rc); + pthread_cond_destroy(&rc->cond); + delete_dynamic(&rc->owners); + DBUG_VOID_RETURN; +} + +void wt_init() +{ + DBUG_ENTER("wt_init"); + DBUG_ASSERT(reshash.alloc.constructor != wt_resource_init); + + lf_hash_init(&reshash, sizeof(WT_RESOURCE), LF_HASH_UNIQUE, 0, + sizeof_WT_RESOURCE_ID, 0, 0); + reshash.alloc.constructor= wt_resource_init; + reshash.alloc.destructor= wt_resource_destroy; + /* + Note a trick: we initialize the hash with the real element size, + but fix it later to a shortened element size. This way + the allocator will allocate elements correctly, but + lf_hash_insert() will only overwrite part of the element with memcpy(). + lock, condition, and dynamic array will be intact. + */ + reshash.element_size= offsetof(WT_RESOURCE, lock); + bzero(wt_wait_stats, sizeof(wt_wait_stats)); + bzero(wt_cycle_stats, sizeof(wt_cycle_stats)); + wt_success_stats= 0; + { /* initialize wt_wait_table[]. from 1 us to 1 min, log e scale */ + int i; + double from= log(1); /* 1 us */ + double to= log(60e6); /* 1 min */ + for (i= 0; i < WT_WAIT_STATS; i++) + { + wt_wait_table[i]= (ulonglong)exp((to-from)/(WT_WAIT_STATS-1)*i+from); + DBUG_ASSERT(i == 0 || wt_wait_table[i-1] != wt_wait_table[i]); + } + } + my_atomic_rwlock_init(&cycle_stats_lock); + my_atomic_rwlock_init(&success_stats_lock); + my_atomic_rwlock_init(&wait_stats_lock); + DBUG_VOID_RETURN; +} + +void wt_end() +{ + DBUG_ENTER("wt_end"); + + DBUG_ASSERT(reshash.count == 0); + lf_hash_destroy(&reshash); + my_atomic_rwlock_destroy(&cycle_stats_lock); + my_atomic_rwlock_destroy(&success_stats_lock); + my_atomic_rwlock_destroy(&wait_stats_lock); + DBUG_VOID_RETURN; +} + +/** + Lazy WT_THD initialization + + Cheap initialization of WT_THD. Only initialize fields that don't require + memory allocations - basically, it only does assignments. The rest of the + WT_THD structure will be initialized on demand, on the first use. + This allows one to initialize lazily all WT_THD structures, even if some + (or even most) of them will never be used for deadlock detection. + + @param ds a pointer to deadlock search depth short value + @param ts a pointer to deadlock timeout short value + @param dl a pointer to deadlock search depth long value + @param tl a pointer to deadlock timeout long value + + @note these are pointers to values, and WT_THD stores them as pointers. + It allows one later to change search depths and timeouts for existing + threads. It also means that the pointers must stay valid for the lifetime + of WT_THD. +*/ +void wt_thd_lazy_init(WT_THD *thd, const ulong *ds, const ulong *ts, + const ulong *dl, const ulong *tl) +{ + DBUG_ENTER("wt_thd_lazy_init"); + thd->waiting_for= 0; + thd->weight= 0; + thd->deadlock_search_depth_short= ds; + thd->timeout_short= ts; + thd->deadlock_search_depth_long= dl; + thd->timeout_long= tl; + /* dynamic array is also initialized lazily - without memory allocations */ + my_init_dynamic_array(&thd->my_resources, sizeof(WT_RESOURCE *), 0, 5); +#ifndef DBUG_OFF + thd->name= my_thread_name(); +#endif + DBUG_VOID_RETURN; +} + +/** + Finalize WT_THD initialization + + After lazy WT_THD initialization, parts of the structure are still + uninitialized. This function completes the initialization, allocating + memory, if necessary. It's called automatically on demand, when WT_THD + is about to be used. +*/ +static int fix_thd_pins(WT_THD *thd) +{ + if (unlikely(thd->pins == 0)) + { + thd->pins= lf_hash_get_pins(&reshash); +#ifndef DBUG_OFF + thd->name= my_thread_name(); +#endif + } + return thd->pins == 0; +} + +void wt_thd_destroy(WT_THD *thd) +{ + DBUG_ENTER("wt_thd_destroy"); + + DBUG_ASSERT(thd->my_resources.elements == 0); + DBUG_ASSERT(thd->waiting_for == 0); + + if (thd->pins != 0) + lf_hash_put_pins(thd->pins); + + delete_dynamic(&thd->my_resources); + DBUG_VOID_RETURN; +} +/** + Trivial resource id comparison function - bytewise memcmp. + + It can be used in WT_RESOURCE_TYPE structures where bytewise + comparison of values is sufficient. +*/ +my_bool wt_resource_id_memcmp(const void *a, const void *b) +{ + /* we use the fact that there's no padding in the middle of WT_RESOURCE_ID */ + compile_time_assert(offsetof(WT_RESOURCE_ID, type) == sizeof(ulonglong)); + return memcmp(a, b, sizeof_WT_RESOURCE_ID); +} + +/** + arguments for the recursive deadlock_search function +*/ +struct deadlock_arg { + WT_THD * const thd; /**< starting point of a search */ + uint const max_depth; /**< search depth limit */ + WT_THD *victim; /**< a thread to be killed to resolve a deadlock */ + WT_RESOURCE *last_locked_rc; /**< see comment at the end of deadlock_search() */ +}; + +/** + helper function to change the victim, according to the weight +*/ +static void change_victim(WT_THD* found, struct deadlock_arg *arg) +{ + if (found->weight < arg->victim->weight) + { + if (arg->victim != arg->thd) + { + rc_unlock(arg->victim->waiting_for); /* release the previous victim */ + DBUG_ASSERT(arg->last_locked_rc == found->waiting_for); + } + arg->victim= found; + arg->last_locked_rc= 0; + } +} + +/** + recursive loop detection in a wait-for graph with a limited search depth +*/ +static int deadlock_search(struct deadlock_arg *arg, WT_THD *blocker, + uint depth) +{ + WT_RESOURCE *rc, *volatile *shared_ptr= &blocker->waiting_for; + WT_THD *cursor; + uint i; + int ret= WT_OK; + DBUG_ENTER("deadlock_search"); + DBUG_PRINT("wt", ("enter: thd=%s, blocker=%s, depth=%u", + arg->thd->name, blocker->name, depth)); + + LF_REQUIRE_PINS(1); + + arg->last_locked_rc= 0; + + if (depth > arg->max_depth) + { + DBUG_PRINT("wt", ("exit: WT_DEPTH_EXCEEDED (early)")); + DBUG_RETURN(WT_DEPTH_EXCEEDED); + } + +retry: + /* + safe dereference as explained in lf_alloc-pin.c + (in short: protects against lf_alloc_free() in lf_hash_delete()) + */ + do + { + rc= *shared_ptr; + lf_pin(arg->thd->pins, 0, rc); + } while (rc != *shared_ptr && LF_BACKOFF); + + if (rc == 0) + { + DBUG_PRINT("wt", ("exit: OK (early)")); + DBUG_RETURN(0); + } + + rc_rdlock(rc); + if (rc->state != ACTIVE || *shared_ptr != rc) + { + /* blocker is not waiting on this resource anymore */ + rc_unlock(rc); + lf_unpin(arg->thd->pins, 0); + goto retry; + } + /* as the state is locked, we can unpin now */ + lf_unpin(arg->thd->pins, 0); + + /* + Below is not a pure depth-first search. It's a depth-first with a + slightest hint of breadth-first. Depth-first is: + + check(element, X): + foreach current in element->nodes[] do: + if current == X return error; + check(current, X); + + while we do + + check(element, X): + foreach current in element->nodes[] do: + if current == X return error; + foreach current in element->nodes[] do: + check(current, X); + + preferring shorter deadlocks over longer ones. + */ + for (i= 0; i < rc->owners.elements; i++) + { + cursor= *dynamic_element(&rc->owners, i, WT_THD**); + /* + We're only looking for (and detecting) cycles that include 'arg->thd'. + That is, only deadlocks that *we* have created. For example, + thd->A->B->thd + (thd waits for A, A waits for B, while B is waiting for thd). + While walking the graph we can encounter other cicles, e.g. + thd->A->B->C->A + This will not be detected. Instead we will walk it in circles until + the search depth limit is reached (the latter guarantees that an + infinite loop is impossible). We expect the thread that has created + the cycle (one of A, B, and C) to detect its deadlock. + */ + if (cursor == arg->thd) + { + ret= WT_DEADLOCK; + increment_cycle_stats(depth, arg->max_depth == + *arg->thd->deadlock_search_depth_long); + arg->victim= cursor; + goto end; + } + } + for (i= 0; i < rc->owners.elements; i++) + { + cursor= *dynamic_element(&rc->owners, i, WT_THD**); + switch (deadlock_search(arg, cursor, depth+1)) { + case WT_OK: + break; + case WT_DEPTH_EXCEEDED: + ret= WT_DEPTH_EXCEEDED; + break; + case WT_DEADLOCK: + ret= WT_DEADLOCK; + change_victim(cursor, arg); /* also sets arg->last_locked_rc to 0 */ + i= rc->owners.elements; /* jump out of the loop */ + break; + default: + DBUG_ASSERT(0); + } + if (arg->last_locked_rc) + rc_unlock(arg->last_locked_rc); + } +end: + /* + Note that 'rc' is locked in this function, but it's never unlocked here. + Instead it's saved in arg->last_locked_rc and the *caller* is + expected to unlock it. It's done to support different killing + strategies. This is how it works: + Assuming a graph + + thd->A->B->C->thd + + deadlock_search() function starts from thd, locks it (in fact it locks not + a thd, but a resource it is waiting on, but below, for simplicity, I'll + talk about "locking a thd"). Then it goes down recursively, locks A, and so + on. Goes down recursively, locks B. Goes down recursively, locks C. + Notices that C is waiting on thd. Deadlock detected. Sets arg->victim=thd. + Returns from the last deadlock_search() call. C stays locked! + Now it checks whether C is a more appropriate victim than 'thd'. + If yes - arg->victim=C, otherwise C is unlocked. Returns. B stays locked. + Now it checks whether B is a more appropriate victim than arg->victim. + If yes - old arg->victim is unlocked and arg->victim=B, + otherwise B is unlocked. Return. + And so on. + + In short, a resource is locked in a frame. But it's not unlocked in the + same frame, it's unlocked by the caller, and only after the caller checks + that it doesn't need to use current WT_THD as a victim. If it does - the + lock is kept and the old victim's resource is unlocked. When the recursion + is unrolled and we are back to deadlock() function, there are only two + locks left - on thd and on the victim. + */ + arg->last_locked_rc= rc; + DBUG_PRINT("wt", ("exit: %s", + ret == WT_DEPTH_EXCEEDED ? "WT_DEPTH_EXCEEDED" : + ret ? "WT_DEADLOCK" : "OK")); + DBUG_RETURN(ret); +} + +/** + Deadlock detection in a wait-for graph + + A wrapper for recursive deadlock_search() - prepares deadlock_arg structure, + invokes deadlock_search(), increments statistics, notifies the victim. + + @param thd thread that is going to wait. Deadlock is detected + if, while walking the graph, we reach a thread that + is waiting on thd + @param blocker starting point of a search. In wt_thd_cond_timedwait() + it's thd, in wt_thd_will_wait_for() it's a thread that + thd is going to wait for + @param depth starting search depth. In general it's the number of + edges in the wait-for graph between thd and the + blocker. Practically only two values are used (and + supported) - when thd == blocker it's 0, when thd + waits directly for blocker, it's 1 + @param max_depth search depth limit +*/ +static int deadlock(WT_THD *thd, WT_THD *blocker, uint depth, + uint max_depth) +{ + struct deadlock_arg arg= {thd, max_depth, 0, 0}; + int ret; + DBUG_ENTER("deadlock"); + DBUG_ASSERT(depth < 2); + ret= deadlock_search(&arg, blocker, depth); + if (ret == WT_DEPTH_EXCEEDED) + { + increment_cycle_stats(WT_CYCLE_STATS, max_depth == + *thd->deadlock_search_depth_long); + ret= WT_OK; + } + /* + if we started with depth==1, blocker was never considered for a victim + in deadlock_search(). Do it here. + */ + if (ret == WT_DEADLOCK && depth) + change_victim(blocker, &arg); + if (arg.last_locked_rc) + { + /* + Special return code if there's nobody to wait for. + + depth == 0 means that we start the search from thd (thd == blocker). + ret == WT_OK means that no cycle was found and + arg.last_locked_rc == thd->waiting_for. + and arg.last_locked_rc->owners.elements == 0 means that + (applying the rule above) thd->waiting_for->owners.elements == 0, + and thd doesn't have anybody to wait for. + */ + if (depth == 0 && ret == WT_OK && arg.last_locked_rc->owners.elements == 0) + { + DBUG_ASSERT(thd == blocker); + DBUG_ASSERT(arg.last_locked_rc == thd->waiting_for); + ret= WT_FREE_TO_GO; + } + rc_unlock(arg.last_locked_rc); + } + /* notify the victim, if appropriate */ + if (ret == WT_DEADLOCK && arg.victim != thd) + { + DBUG_PRINT("wt", ("killing %s", arg.victim->name)); + arg.victim->killed= 1; + pthread_cond_broadcast(&arg.victim->waiting_for->cond); + rc_unlock(arg.victim->waiting_for); + ret= WT_OK; + } + DBUG_RETURN(ret); +} + + +/** + Delete an element from reshash if it has no waiters or owners + + rc->lock must be locked by the caller and it's unlocked on return. +*/ +static int unlock_lock_and_free_resource(WT_THD *thd, WT_RESOURCE *rc) +{ + uint keylen; + const void *key; + DBUG_ENTER("unlock_lock_and_free_resource"); + + DBUG_ASSERT(rc->state == ACTIVE); + + if (rc->owners.elements || rc->waiter_count) + { + DBUG_PRINT("wt", ("nothing to do, %u owners, %u waiters", + rc->owners.elements, rc->waiter_count)); + rc_unlock(rc); + DBUG_RETURN(0); + } + + if (fix_thd_pins(thd)) + { + rc_unlock(rc); + DBUG_RETURN(1); + } + + /* XXX if (rc->id.type->make_key) key= rc->id.type->make_key(&rc->id, &keylen); else */ + { + key= &rc->id; + keylen= sizeof_WT_RESOURCE_ID; + } + + /* + To free the element correctly we need to: + 1. take its lock (already done). + 2. set the state to FREE + 3. release the lock + 4. remove from the hash + */ + rc->state= FREE; + rc_unlock(rc); + DBUG_RETURN(lf_hash_delete(&reshash, thd->pins, key, keylen) == -1); +} + + +/** + register the fact that thd is not waiting anymore + + decrease waiter_count, clear waiting_for, free the resource if appropriate. + thd->waiting_for must be locked! +*/ +static int stop_waiting_locked(WT_THD *thd) +{ + int ret; + WT_RESOURCE *rc= thd->waiting_for; + DBUG_ENTER("stop_waiting_locked"); + + DBUG_ASSERT(rc->waiter_count); + DBUG_ASSERT(rc->state == ACTIVE); + rc->waiter_count--; + thd->waiting_for= 0; + ret= unlock_lock_and_free_resource(thd, rc); + DBUG_RETURN((thd->killed || ret) ? WT_DEADLOCK : WT_OK); +} + +/** + register the fact that thd is not waiting anymore + + locks thd->waiting_for and calls stop_waiting_locked(). +*/ +static int stop_waiting(WT_THD *thd) +{ + int ret; + WT_RESOURCE *rc= thd->waiting_for; + DBUG_ENTER("stop_waiting"); + + if (!rc) + DBUG_RETURN(WT_OK); + /* + nobody's trying to free the resource now, + as its waiter_count is guaranteed to be non-zero + */ + rc_wrlock(rc); + ret= stop_waiting_locked(thd); + DBUG_RETURN(ret); +} + +/** + notify the system that a thread needs to wait for another thread + + called by a *waiter* to declare that it (thd) will wait for another + thread (blocker) on a specific resource (resid). + can be called many times, if many blockers own a blocking resource. + but must always be called with the same resource id - a thread cannot + wait for more than one resource at a time. + + @return WT_OK or WT_DEADLOCK + + As a new edge is added to the wait-for graph, a deadlock detection is + performed for this new edge. +*/ +int wt_thd_will_wait_for(WT_THD *thd, WT_THD *blocker, + const WT_RESOURCE_ID *resid) +{ + uint i; + WT_RESOURCE *rc; + DBUG_ENTER("wt_thd_will_wait_for"); + + LF_REQUIRE_PINS(3); + + DBUG_PRINT("wt", ("enter: thd=%s, blocker=%s, resid=%lu", + thd->name, blocker->name, (ulong)resid->value)); + + if (fix_thd_pins(thd)) + DBUG_RETURN(WT_DEADLOCK); + + if (thd->waiting_for == 0) + { + uint keylen; + const void *key; + /* XXX if (restype->make_key) key= restype->make_key(resid, &keylen); else */ + { + key= resid; + keylen= sizeof_WT_RESOURCE_ID; + } + + DBUG_PRINT("wt", ("first blocker")); + +retry: + while ((rc= lf_hash_search(&reshash, thd->pins, key, keylen)) == 0) + { + WT_RESOURCE tmp; + + DBUG_PRINT("wt", ("failed to find rc in hash, inserting")); + bzero(&tmp, sizeof(tmp)); + tmp.id= *resid; + tmp.state= ACTIVE; + + if (lf_hash_insert(&reshash, thd->pins, &tmp) == -1) /* if OOM */ + DBUG_RETURN(WT_DEADLOCK); + /* + Two cases: either lf_hash_insert() failed - because another thread + has just inserted a resource with the same id - and we need to retry. + Or lf_hash_insert() succeeded, and then we need to repeat + lf_hash_search() to find a real address of the newly inserted element. + That is, we don't care what lf_hash_insert() has returned. + And we need to repeat the loop anyway. + */ + } + if (rc == MY_ERRPTR) + DBUG_RETURN(WT_DEADLOCK); + + DBUG_PRINT("wt", ("found in hash rc=%p", rc)); + + rc_wrlock(rc); + if (rc->state != ACTIVE) + { + DBUG_PRINT("wt", ("but it's not active, retrying")); + /* Somebody has freed the element while we weren't looking */ + rc_unlock(rc); + lf_hash_search_unpin(thd->pins); + goto retry; + } + + lf_hash_search_unpin(thd->pins); /* the element cannot go away anymore */ + thd->waiting_for= rc; + rc->waiter_count++; + thd->killed= 0; + } + else + { + DBUG_ASSERT(thd->waiting_for->id.type == resid->type); + DBUG_ASSERT(resid->type->compare(&thd->waiting_for->id, resid) == 0); + DBUG_PRINT("wt", ("adding another blocker")); + + /* + we can safely access the resource here, it's in the hash as it has + non-zero waiter_count + */ + rc= thd->waiting_for; + rc_wrlock(rc); + DBUG_ASSERT(rc->waiter_count); + DBUG_ASSERT(rc->state == ACTIVE); + + if (thd->killed) + { + stop_waiting_locked(thd); + DBUG_RETURN(WT_DEADLOCK); + } + } + /* + Another thread could be waiting on this resource for this very 'blocker'. + In this case we should not add it to the list for the second time. + */ + for (i= 0; i < rc->owners.elements; i++) + if (*dynamic_element(&rc->owners, i, WT_THD**) == blocker) + break; + if (i >= rc->owners.elements) + { + if (push_dynamic(&blocker->my_resources, (void*)&rc)) + { + stop_waiting_locked(thd); + DBUG_RETURN(WT_DEADLOCK); /* deadlock and OOM use the same error code */ + } + if (push_dynamic(&rc->owners, (void*)&blocker)) + { + pop_dynamic(&blocker->my_resources); + stop_waiting_locked(thd); + DBUG_RETURN(WT_DEADLOCK); + } + } + rc_unlock(rc); + + if (deadlock(thd, blocker, 1, *thd->deadlock_search_depth_short) != WT_OK) + { + stop_waiting(thd); + DBUG_RETURN(WT_DEADLOCK); + } + DBUG_RETURN(WT_OK); +} + +/** + called by a *waiter* (thd) to start waiting + + It's supposed to be a drop-in replacement for + pthread_cond_timedwait(), and it takes mutex as an argument. + + @return one of WT_TIMEOUT, WT_DEADLOCK, WT_OK +*/ +int wt_thd_cond_timedwait(WT_THD *thd, pthread_mutex_t *mutex) +{ + int ret= WT_TIMEOUT; + struct timespec timeout; + ulonglong before, after, starttime; + WT_RESOURCE *rc= thd->waiting_for; + DBUG_ENTER("wt_thd_cond_timedwait"); + DBUG_PRINT("wt", ("enter: thd=%s, rc=%p", thd->name, rc)); + +#ifndef DBUG_OFF + if (rc->cond_mutex) + DBUG_ASSERT(rc->cond_mutex == mutex); + else + rc->cond_mutex= mutex; + safe_mutex_assert_owner(mutex); +#endif + + before= starttime= my_getsystime(); + +#ifdef __WIN__ + /* + only for the sake of Windows we distinguish between + 'before' and 'starttime': + + my_getsystime() returns high-resolution value, that cannot be used for + waiting (it doesn't follow system clock changes), but is good for time + intervals. + + GetSystemTimeAsFileTime() follows system clock, but is low-resolution + and will result in lousy intervals. + */ + GetSystemTimeAsFileTime((PFILETIME)&starttime); +#endif + + rc_wrlock(rc); + if (rc->owners.elements == 0) + ret= WT_OK; + rc_unlock(rc); + + set_timespec_time_nsec(timeout, starttime, (*thd->timeout_short)*1000ULL); + if (ret == WT_TIMEOUT && !thd->killed) + ret= pthread_cond_timedwait(&rc->cond, mutex, &timeout); + if (ret == WT_TIMEOUT && !thd->killed) + { + int r= deadlock(thd, thd, 0, *thd->deadlock_search_depth_long); + if (r == WT_FREE_TO_GO) + ret= WT_OK; + else if (r != WT_OK) + ret= WT_DEADLOCK; + else if (*thd->timeout_long > *thd->timeout_short) + { + set_timespec_time_nsec(timeout, starttime, (*thd->timeout_long)*1000ULL); + if (!thd->killed) + ret= pthread_cond_timedwait(&rc->cond, mutex, &timeout); + } + } + after= my_getsystime(); + if (stop_waiting(thd) == WT_DEADLOCK) /* if we're killed */ + ret= WT_DEADLOCK; + increment_wait_stats(after-before, ret); + if (ret == WT_OK) + increment_success_stats(); + DBUG_RETURN(ret); +} + +/** + called by a *blocker* when it releases a resource + + it's conceptually similar to pthread_cond_broadcast, and must be done + under the same mutex as wt_thd_cond_timedwait(). + + @param resid a resource to release. 0 to release all resources +*/ + +void wt_thd_release(WT_THD *thd, const WT_RESOURCE_ID *resid) +{ + uint i; + DBUG_ENTER("wt_thd_release"); + + for (i= 0; i < thd->my_resources.elements; i++) + { + WT_RESOURCE *rc= *dynamic_element(&thd->my_resources, i, WT_RESOURCE**); + if (!resid || (resid->type->compare(&rc->id, resid) == 0)) + { + uint j; + + rc_wrlock(rc); + /* + nobody's trying to free the resource now, + as its owners[] array is not empty (at least thd must be there) + */ + DBUG_ASSERT(rc->state == ACTIVE); + for (j= 0; j < rc->owners.elements; j++) + if (*dynamic_element(&rc->owners, j, WT_THD**) == thd) + break; + DBUG_ASSERT(j < rc->owners.elements); + delete_dynamic_element(&rc->owners, j); + if (rc->owners.elements == 0) + { + pthread_cond_broadcast(&rc->cond); +#ifndef DBUG_OFF + if (rc->cond_mutex) + safe_mutex_assert_owner(rc->cond_mutex); +#endif + } + unlock_lock_and_free_resource(thd, rc); + if (resid) + { + delete_dynamic_element(&thd->my_resources, i); + DBUG_VOID_RETURN; + } + } + } + if (!resid) + reset_dynamic(&thd->my_resources); + DBUG_VOID_RETURN; +} + diff --git a/externals/mysql/mysys/wqueue.c b/externals/mysql/mysys/wqueue.c new file mode 100644 index 00000000000..fcc0a39725d --- /dev/null +++ b/externals/mysql/mysys/wqueue.c @@ -0,0 +1,225 @@ + +#include <wqueue.h> + +#define STRUCT_PTR(TYPE, MEMBER, a) \ + (TYPE *) ((char *) (a) - offsetof(TYPE, MEMBER)) +/* + Link a thread into double-linked queue of waiting threads. + + SYNOPSIS + wqueue_link_into_queue() + wqueue pointer to the queue structure + thread pointer to the thread to be added to the queue + + RETURN VALUE + none + + NOTES. + Queue is represented by a circular list of the thread structures + The list is double-linked of the type (**prev,*next), accessed by + a pointer to the last element. +*/ + +void wqueue_link_into_queue(WQUEUE *wqueue, struct st_my_thread_var *thread) +{ + struct st_my_thread_var *last; + if (!(last= wqueue->last_thread)) + { + /* Queue is empty */ + thread->next= thread; + thread->prev= &thread->next; + } + else + { + thread->prev= last->next->prev; + last->next->prev= &thread->next; + thread->next= last->next; + last->next= thread; + } + wqueue->last_thread= thread; +} + + +/* + Add a thread to single-linked queue of waiting threads + + SYNOPSIS + wqueue_add_to_queue() + wqueue pointer to the queue structure + thread pointer to the thread to be added to the queue + + RETURN VALUE + none + + NOTES. + Queue is represented by a circular list of the thread structures + The list is single-linked of the type (*next), accessed by a pointer + to the last element. +*/ + +void wqueue_add_to_queue(WQUEUE *wqueue, struct st_my_thread_var *thread) +{ + struct st_my_thread_var *last; + if (!(last= wqueue->last_thread)) + thread->next= thread; + else + { + thread->next= last->next; + last->next= thread; + } +#ifndef DBUG_OFF + thread->prev= NULL; /* force segfault if used */ +#endif + wqueue->last_thread= thread; +} + +/* + Unlink a thread from double-linked queue of waiting threads + + SYNOPSIS + wqueue_unlink_from_queue() + wqueue pointer to the queue structure + thread pointer to the thread to be removed from the queue + + RETURN VALUE + none + + NOTES. + See NOTES for link_into_queue +*/ + +void wqueue_unlink_from_queue(WQUEUE *wqueue, struct st_my_thread_var *thread) +{ + if (thread->next == thread) + /* The queue contains only one member */ + wqueue->last_thread= NULL; + else + { + thread->next->prev= thread->prev; + *thread->prev= thread->next; + if (wqueue->last_thread == thread) + wqueue->last_thread= STRUCT_PTR(struct st_my_thread_var, next, + thread->prev); + } + thread->next= NULL; +} + + +/* + Remove all threads from queue signaling them to proceed + + SYNOPSIS + wqueue_realease_queue() + wqueue pointer to the queue structure + thread pointer to the thread to be added to the queue + + RETURN VALUE + none + + NOTES. + See notes for add_to_queue + When removed from the queue each thread is signaled via condition + variable thread->suspend. +*/ + +void wqueue_release_queue(WQUEUE *wqueue) +{ + struct st_my_thread_var *last= wqueue->last_thread; + struct st_my_thread_var *next= last->next; + struct st_my_thread_var *thread; + do + { + thread= next; + pthread_cond_signal(&thread->suspend); + next= thread->next; + thread->next= NULL; + } + while (thread != last); + wqueue->last_thread= NULL; +} + + +/** + @brief Removes all threads waiting for read or first one waiting for write. + + @param wqueue pointer to the queue structure + @param thread pointer to the thread to be added to the queue + + @note This function is applicable only to single linked lists. +*/ + +void wqueue_release_one_locktype_from_queue(WQUEUE *wqueue) +{ + struct st_my_thread_var *last= wqueue->last_thread; + struct st_my_thread_var *next= last->next; + struct st_my_thread_var *thread; + struct st_my_thread_var *new_list= NULL; + uint first_type= next->lock_type; + if (first_type == MY_PTHREAD_LOCK_WRITE) + { + /* release first waiting for write lock */ + pthread_cond_signal(&next->suspend); + if (next == last) + wqueue->last_thread= NULL; + else + last->next= next->next; + next->next= NULL; + return; + } + do + { + thread= next; + next= thread->next; + if (thread->lock_type == MY_PTHREAD_LOCK_WRITE) + { + /* skip waiting for write lock */ + if (new_list) + { + thread->next= new_list->next; + new_list= new_list->next= thread; + } + else + new_list= thread->next= thread; + } + else + { + /* release waiting for read lock */ + pthread_cond_signal(&thread->suspend); + thread->next= NULL; + } + } while (thread != last); + wqueue->last_thread= new_list; +} + + +/* + Add thread and wait + + SYNOPSYS + wqueue_add_and_wait() + wqueue queue to add to + thread thread which is waiting + lock mutex need for the operation +*/ + +void wqueue_add_and_wait(WQUEUE *wqueue, + struct st_my_thread_var *thread, + pthread_mutex_t *lock) +{ + DBUG_ENTER("wqueue_add_and_wait"); + DBUG_PRINT("enter", + ("thread: 0x%lx cond: 0x%lx mutex: 0x%lx", + (ulong) thread, (ulong) &thread->suspend, (ulong) lock)); + wqueue_add_to_queue(wqueue, thread); + do + { + DBUG_PRINT("info", ("wait... cond: 0x%lx mutex: 0x%lx", + (ulong) &thread->suspend, (ulong) lock)); + pthread_cond_wait(&thread->suspend, lock); + DBUG_PRINT("info", ("wait done cond: 0x%lx mutex: 0x%lx next: 0x%lx", + (ulong) &thread->suspend, (ulong) lock, + (ulong) thread->next)); + } + while (thread->next); + DBUG_VOID_RETURN; +} |