diff options
author | Sergei Golubchik <sergii@pisem.net> | 2011-10-19 21:45:18 +0200 |
---|---|---|
committer | Sergei Golubchik <sergii@pisem.net> | 2011-10-19 21:45:18 +0200 |
commit | 76f0b94bb0b2994d639353530c5b251d0f1a204b (patch) | |
tree | 9ed50628aac34f89a37637bab2fc4915b86b5eb4 /mysys | |
parent | 4e46d8e5bff140f2549841167dc4b65a3c0a645d (diff) | |
parent | 5dc1a2231f55bacc9aaf0e24816f3d9c2ee1f21d (diff) | |
download | mariadb-git-76f0b94bb0b2994d639353530c5b251d0f1a204b.tar.gz |
merge with 5.3
sql/sql_insert.cc:
CREATE ... IF NOT EXISTS may do nothing, but
it is still not a failure. don't forget to my_ok it.
******
CREATE ... IF NOT EXISTS may do nothing, but
it is still not a failure. don't forget to my_ok it.
sql/sql_table.cc:
small cleanup
******
small cleanup
Diffstat (limited to 'mysys')
-rw-r--r-- | mysys/CMakeLists.txt | 2 | ||||
-rw-r--r-- | mysys/charset.c | 5 | ||||
-rw-r--r-- | mysys/hash.c | 22 | ||||
-rw-r--r-- | mysys/ma_dyncol.c | 2111 | ||||
-rw-r--r-- | mysys/mf_getdate.c | 2 | ||||
-rw-r--r-- | mysys/mf_iocache.c | 3 | ||||
-rw-r--r-- | mysys/my_bitmap.c | 385 | ||||
-rw-r--r-- | mysys/my_compare.c | 3 | ||||
-rw-r--r-- | mysys/my_fopen.c | 3 | ||||
-rw-r--r-- | mysys/my_getncpus.c | 2 | ||||
-rw-r--r-- | mysys/my_getsystime.c | 228 | ||||
-rw-r--r-- | mysys/my_handler_errors.h | 8 | ||||
-rw-r--r-- | mysys/my_init.c | 33 | ||||
-rw-r--r-- | mysys/my_open.c | 11 | ||||
-rw-r--r-- | mysys/my_port.c | 40 | ||||
-rw-r--r-- | mysys/my_pread.c | 69 | ||||
-rw-r--r-- | mysys/my_redel.c | 33 | ||||
-rw-r--r-- | mysys/my_seek.c | 2 | ||||
-rw-r--r-- | mysys/my_static.c | 5 | ||||
-rw-r--r-- | mysys/my_static.h | 2 | ||||
-rw-r--r-- | mysys/my_symlink.c | 6 | ||||
-rw-r--r-- | mysys/my_sync.c | 37 | ||||
-rw-r--r-- | mysys/my_thr_init.c | 8 | ||||
-rw-r--r-- | mysys/my_uuid.c | 14 | ||||
-rw-r--r-- | mysys/my_wincond.c | 46 | ||||
-rw-r--r-- | mysys/my_write.c | 5 | ||||
-rw-r--r-- | mysys/string.c | 12 | ||||
-rw-r--r-- | mysys/thr_alarm.c | 26 | ||||
-rw-r--r-- | mysys/thr_lock.c | 129 | ||||
-rw-r--r-- | mysys/thr_mutex.c | 26 | ||||
-rw-r--r-- | mysys/thr_rwlock.c | 2 | ||||
-rw-r--r-- | mysys/tree.c | 9 | ||||
-rw-r--r-- | mysys/waiting_threads.c | 34 |
33 files changed, 2491 insertions, 832 deletions
diff --git a/mysys/CMakeLists.txt b/mysys/CMakeLists.txt index 662a4c79757..ca338369dad 100644 --- a/mysys/CMakeLists.txt +++ b/mysys/CMakeLists.txt @@ -34,7 +34,7 @@ SET(MYSYS_SOURCES array.c charset-def.c charset.c checksum.c default.c thr_rwlock.c tree.c typelib.c base64.c my_memmem.c my_getpagesize.c lf_alloc-pin.c lf_dynarray.c lf_hash.c my_atomic.c my_getncpus.c my_safehash.c my_chmod.c my_rnd.c - my_uuid.c wqueue.c waiting_threads.c + my_uuid.c wqueue.c waiting_threads.c ma_dyncol.c my_rdtsc.c) IF (WIN32) diff --git a/mysys/charset.c b/mysys/charset.c index f859ceae394..98b87ba9feb 100644 --- a/mysys/charset.c +++ b/mysys/charset.c @@ -97,6 +97,9 @@ static my_bool init_state_maps(struct charset_info_st *cs) 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; + state_map[(uchar)'-']= (uchar) MY_LEX_MINUS_OR_COMMENT; + state_map[(uchar)',']= (uchar) MY_LEX_COMMA; + state_map[(uchar)'?']= (uchar) MY_LEX_PLACEHOLDER; /* Create a second map to make it faster to find identifiers @@ -199,6 +202,7 @@ static my_bool simple_cs_is_full(CHARSET_INFO *cs) } +#if defined(HAVE_UCA_COLLATIONS) && (defined(HAVE_CHARSET_ucs2) || defined(HAVE_CHARSET_utf8)) static void copy_uca_collation(struct charset_info_st *to, CHARSET_INFO *from) { @@ -212,6 +216,7 @@ copy_uca_collation(struct charset_info_st *to, CHARSET_INFO *from) to->state|= MY_CS_AVAILABLE | MY_CS_LOADED | MY_CS_STRNXFRM | MY_CS_UNICODE; } +#endif static int add_collation(struct charset_info_st *cs) diff --git a/mysys/hash.c b/mysys/hash.c index 55b96afe615..38914d5f350 100644 --- a/mysys/hash.c +++ b/mysys/hash.c @@ -132,7 +132,8 @@ static inline void my_hash_free_elements(HASH *hash) void my_hash_free(HASH *hash) { DBUG_ENTER("my_hash_free"); - DBUG_PRINT("enter",("hash: 0x%lx", (long) hash)); + DBUG_PRINT("enter",("hash: 0x%lx elements: %ld", + (long) hash, hash->records)); my_hash_free_elements(hash); hash->free= 0; @@ -184,8 +185,9 @@ my_hash_key(const HASH *hash, const uchar *record, size_t *length, static uint my_hash_mask(my_hash_value_type hashnr, size_t buffmax, size_t maxlength) { - if ((hashnr & (buffmax-1)) < maxlength) return (hashnr & (buffmax-1)); - return (hashnr & ((buffmax >> 1) -1)); + if ((hashnr & (buffmax-1)) < maxlength) + return (uint) (hashnr & (buffmax-1)); + return (uint) (hashnr & ((buffmax >> 1) -1)); } static uint my_hash_rec_mask(const HASH *hash, HASH_LINK *pos, @@ -521,8 +523,9 @@ my_bool my_hash_insert(HASH *info, const uchar *record) my_bool my_hash_delete(HASH *hash, uchar *record) { - uint blength,pos2,idx,empty_index; + uint pos2,idx,empty_index; my_hash_value_type pos_hashnr, lastpos_hashnr; + size_t blength; HASH_LINK *data,*lastpos,*gpos,*pos,*pos3,*empty; DBUG_ENTER("my_hash_delete"); if (!hash->records) @@ -611,8 +614,8 @@ exit: 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; + uint new_index,new_pos_index,records; + size_t idx, empty, blength; HASH_LINK org_link,*data,*previous,*pos; DBUG_ENTER("my_hash_update"); @@ -694,7 +697,7 @@ my_bool my_hash_update(HASH *hash, uchar *record, uchar *old_key, if (new_index != new_pos_index) { /* Other record in wrong position */ data[empty] = *pos; - movelink(data,new_index,new_pos_index,empty); + movelink(data,new_index,new_pos_index, (uint) empty); org_link.next=NO_RECORD; data[new_index]= org_link; } @@ -702,7 +705,7 @@ my_bool my_hash_update(HASH *hash, uchar *record, uchar *old_key, { /* Link in chain at right position */ org_link.next=data[new_index].next; data[empty]=org_link; - data[new_index].next=empty; + data[new_index].next= (uint) empty; } DBUG_RETURN(0); } @@ -766,7 +769,8 @@ my_bool my_hash_check(HASH *hash) { int error; uint i,rec_link,found,max_links,seek,links,idx; - uint records,blength; + uint records; + size_t blength; HASH_LINK *data,*hash_info; records=hash->records; blength=hash->blength; diff --git a/mysys/ma_dyncol.c b/mysys/ma_dyncol.c new file mode 100644 index 00000000000..0116cdd2c20 --- /dev/null +++ b/mysys/ma_dyncol.c @@ -0,0 +1,2111 @@ +/* Copyright (c) 2011, Monty Program Ab + Copyright (c) 2011, Oleksandr Byelkin + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ``AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + SUCH DAMAGE. +*/ + +#include "mysys_priv.h" +#include <m_string.h> +#include <ma_dyncol.h> + +/* + Flag byte bits + + 2 bits which determinate size of offset in the header -1 +*/ +/* mask to get above bits */ +#define DYNCOL_FLG_OFFSET 3 +/* All known flags mask */ +#define DYNCOL_FLG_KNOWN 3 + +/* dynamic column size reserve */ +#define DYNCOL_SYZERESERVE 80 + +/* length of fixed string header 1 byte - flags, 2 bytes - columns counter */ +#define FIXED_HEADER_SIZE 3 + +#define COLUMN_NUMBER_SIZE 2 + +#define MAX_OFFSET_LENGTH 5 + +static enum enum_dyncol_func_result +dynamic_column_time_store(DYNAMIC_COLUMN *str, + MYSQL_TIME *value); +static enum enum_dyncol_func_result +dynamic_column_date_store(DYNAMIC_COLUMN *str, + MYSQL_TIME *value); +static enum enum_dyncol_func_result +dynamic_column_time_read_internal(DYNAMIC_COLUMN_VALUE *store_it_here, + uchar *data, size_t length); +static enum enum_dyncol_func_result +dynamic_column_date_read_internal(DYNAMIC_COLUMN_VALUE *store_it_here, + uchar *data, size_t length); + +/** + Initialize dynamic column string with (make it empty but correct format) + + @param str The string to initialize + @param size Amount of preallocated memory for the string. + + @retval FALSE OK + @retval TRUE error +*/ + +static my_bool dynamic_column_init_str(DYNAMIC_COLUMN *str, size_t size) +{ + DBUG_ASSERT(size != 0); + + /* + Make string with no fields (empty header) + - First \0 is flags + - other 2 \0 is number of fields + */ + if (init_dynamic_string(str, NULL, + size + FIXED_HEADER_SIZE, DYNCOL_SYZERESERVE)) + return TRUE; + bzero(str->str, FIXED_HEADER_SIZE); + str->length= FIXED_HEADER_SIZE; + return FALSE; +} + + +/** + Calculate how many bytes needed to store val as variable length integer + where first bit indicate continuation of the sequence. + + @param val The value for which we are calculating length + + @return number of bytes +*/ + +static size_t dynamic_column_var_uint_bytes(ulonglong val) +{ + size_t len= 0; + do + { + len++; + val>>= 7; + } while (val); + return len; +} + + +/** + Stores variable length unsigned integer value to a string + + @param str The string where to append the value + @param val The value to put in the string + + @return ER_DYNCOL_* return code + + @notes + This is used to store a number together with other data in the same + object. (Like decimals, length of string etc) + (As we don't know the length of this object, we can't store 0 in 0 bytes) +*/ + +static enum enum_dyncol_func_result +dynamic_column_var_uint_store(DYNAMIC_COLUMN *str, ulonglong val) +{ + if (dynstr_realloc(str, 10)) /* max what we can use */ + return ER_DYNCOL_RESOURCE; + + do + { + ulonglong rest= val >> 7; + str->str[str->length++]= ((val & 0x7f) | (rest ? 0x80 : 0x00)); + val= rest; + } while (val); + return ER_DYNCOL_OK; +} + + +/** + Reads variable length unsigned integer value from a string + + @param data The string from which the int should be read + @param data_length Max length of data + @param len Where to put length of the string read in bytes + + @return value of the unsigned integer read from the string + + In case of error, *len is set to 0 +*/ + +static ulonglong +dynamic_column_var_uint_get(uchar *data, size_t data_length, + size_t *len) +{ + ulonglong val= 0; + uint length; + uchar *end= data + data_length; + + for (length=0; data < end ; data++) + { + val+= (((ulonglong)((*data) & 0x7f)) << (length * 7)); + length++; + if (!((*data) & 0x80)) + { + /* End of data */ + *len= length; + return val; + } + } + /* Something was wrong with data */ + *len= 0; /* Mark error */ + return 0; +} + + +/** + Calculate how many bytes needed to store val as unsigned. + + @param val The value for which we are calculating length + + @return number of bytes (0-8) +*/ + +static size_t dynamic_column_uint_bytes(ulonglong val) +{ + size_t len; + + for (len= 0; val ; val>>= 8, len++) + ; + return len; +} + + +/** + Append the string with given unsigned int value. + + @param str The string where to put the value + @param val The value to put in the string + + @return ER_DYNCOL_* return code +*/ + +static enum enum_dyncol_func_result +dynamic_column_uint_store(DYNAMIC_COLUMN *str, ulonglong val) +{ + if (dynstr_realloc(str, 8)) /* max what we can use */ + return ER_DYNCOL_RESOURCE; + + for (; val; val>>= 8) + str->str[str->length++]= (char) (val & 0xff); + return ER_DYNCOL_OK; +} + + +/** + Read unsigned int value of given length from the string + + @param store_it_here The structure to store the value + @param data The string which should be read + @param length The length (in bytes) of the value in nthe string + + @return ER_DYNCOL_* return code +*/ + +static enum enum_dyncol_func_result +dynamic_column_uint_read(DYNAMIC_COLUMN_VALUE *store_it_here, + uchar *data, size_t length) +{ + ulonglong value= 0; + size_t i; + + for (i= 0; i < length; i++) + value+= ((ulonglong)data[i]) << (i*8); + + store_it_here->ulong_value= value; + return ER_DYNCOL_OK; +} + +/** + Calculate how many bytes needed to store val as signed in following encoding: + 0 -> 0 + -1 -> 1 + 1 -> 2 + -2 -> 3 + 2 -> 4 + ... + + @param val The value for which we are calculating length + + @return number of bytes +*/ + +static size_t dynamic_column_sint_bytes(longlong val) +{ + return dynamic_column_uint_bytes((val << 1) ^ + (val < 0 ? ULL(0xffffffffffffffff) : 0)); +} + + +/** + Append the string with given signed int value. + + @param str the string where to put the value + @param val the value to put in the string + + @return ER_DYNCOL_* return code +*/ + +static enum enum_dyncol_func_result +dynamic_column_sint_store(DYNAMIC_COLUMN *str, longlong val) +{ + return dynamic_column_uint_store(str, + (val << 1) ^ + (val < 0 ? ULL(0xffffffffffffffff) : 0)); +} + + +/** + Read signed int value of given length from the string + + @param store_it_here The structure to store the value + @param data The string which should be read + @param length The length (in bytes) of the value in the string + + @return ER_DYNCOL_* return code +*/ + +static enum enum_dyncol_func_result +dynamic_column_sint_read(DYNAMIC_COLUMN_VALUE *store_it_here, + uchar *data, size_t length) +{ + ulonglong val; + dynamic_column_uint_read(store_it_here, data, length); + val= store_it_here->ulong_value; + if (val & 1) + val= (val >> 1) ^ ULL(0xffffffffffffffff); + else + val>>= 1; + store_it_here->long_value= (longlong) val; + return ER_DYNCOL_OK; +} + + +/** + Calculate how many bytes needed to store the value. + + @param value The value for which we are calculating length + + @return + Error: (size_t) ~0 + ok number of bytes +*/ + +static size_t +dynamic_column_value_len(DYNAMIC_COLUMN_VALUE *value) +{ + switch (value->type) { + case DYN_COL_NULL: + return 0; + case DYN_COL_INT: + return dynamic_column_sint_bytes(value->long_value); + case DYN_COL_UINT: + return dynamic_column_uint_bytes(value->ulong_value); + case DYN_COL_DOUBLE: + return 8; + case DYN_COL_STRING: + return (dynamic_column_var_uint_bytes(value->charset->number) + + value->string_value.length); + case DYN_COL_DECIMAL: + { + int precision= value->decimal_value.intg + value->decimal_value.frac; + int scale= value->decimal_value.frac; + + if (precision == 0 || decimal_is_zero(&value->decimal_value)) + { + /* This is here to simplify dynamic_column_decimal_store() */ + value->decimal_value.intg= value->decimal_value.frac= 0; + return 0; + } + /* + Check if legal decimal; This is needed to not get an assert in + decimal_bin_size(). However this should be impossible as all + decimals entered here should be valid and we have the special check + above to handle the unlikely but possible case that decimal_value.intg + and decimal.frac is 0. + */ + if (scale < 0 || precision <= 0) + { + DBUG_ASSERT(0); /* Impossible */ + return (size_t) ~0; + } + return (dynamic_column_var_uint_bytes(value->decimal_value.intg) + + dynamic_column_var_uint_bytes(value->decimal_value.frac) + + decimal_bin_size(precision, scale)); + } + case DYN_COL_DATETIME: + /* date+time in bits: 14 + 4 + 5 + 10 + 6 + 6 + 20 + 1 66bits ~= 9 bytes */ + return 9; + case DYN_COL_DATE: + /* date in dits: 14 + 4 + 5 = 23bits ~= 3bytes*/ + return 3; + case DYN_COL_TIME: + /* time in bits: 10 + 6 + 6 + 20 + 1 = 43bits ~= 6bytes*/ + return 6; + } + DBUG_ASSERT(0); + return 0; +} + + +/** + Append double value to a string + + @param str the string where to put the value + @param val the value to put in the string + + @return ER_DYNCOL_* return code +*/ + +static enum enum_dyncol_func_result +dynamic_column_double_store(DYNAMIC_COLUMN *str, double val) +{ + if (dynstr_realloc(str, 8)) + return ER_DYNCOL_RESOURCE; + float8store(str->str + str->length, val); + str->length+= 8; + return ER_DYNCOL_OK; +} + + +/** + Read double value of given length from the string + + @param store_it_here The structure to store the value + @param data The string which should be read + @param length The length (in bytes) of the value in nthe string + + @return ER_DYNCOL_* return code +*/ + +static enum enum_dyncol_func_result +dynamic_column_double_read(DYNAMIC_COLUMN_VALUE *store_it_here, + uchar *data, size_t length) +{ + if (length != 8) + return ER_DYNCOL_FORMAT; + float8get(store_it_here->double_value, data); + return ER_DYNCOL_OK; +} + + +/** + Append the string with given string value. + + @param str the string where to put the value + @param val the value to put in the string + + @return ER_DYNCOL_* return code +*/ + +static enum enum_dyncol_func_result +dynamic_column_string_store(DYNAMIC_COLUMN *str, LEX_STRING *string, + CHARSET_INFO *charset) +{ + enum enum_dyncol_func_result rc; + if ((rc= dynamic_column_var_uint_store(str, charset->number))) + return rc; + if (dynstr_append_mem(str, string->str, string->length)) + return ER_DYNCOL_RESOURCE; + return ER_DYNCOL_OK; +} + + +/** + Read string value of given length from the packed string + + @param store_it_here The structure to store the value + @param data The packed string which should be read + @param length The length (in bytes) of the value in nthe string + + @return ER_DYNCOL_* return code +*/ + +static enum enum_dyncol_func_result +dynamic_column_string_read(DYNAMIC_COLUMN_VALUE *store_it_here, + uchar *data, size_t length) +{ + size_t len; + uint charset_nr= (uint)dynamic_column_var_uint_get(data, length, &len); + if (len == 0) /* Wrong packed number */ + return ER_DYNCOL_FORMAT; + store_it_here->charset= get_charset(charset_nr, MYF(MY_WME)); + if (store_it_here->charset == NULL) + return ER_DYNCOL_UNKNOWN_CHARSET; + data+= len; + store_it_here->string_value.length= (length-= len); + store_it_here->string_value.str= (char*) data; + return ER_DYNCOL_OK; +} + + +/** + Append the string with given decimal value. + + @param str the string where to put the value + @param val the value to put in the string + + @return ER_DYNCOL_* return code +*/ + +static enum enum_dyncol_func_result +dynamic_column_decimal_store(DYNAMIC_COLUMN *str, + decimal_t *value) +{ + uint bin_size; + int precision= value->intg + value->frac; + + /* Store decimal zero as empty string */ + if (precision == 0) + return ER_DYNCOL_OK; + + bin_size= decimal_bin_size(precision, value->frac); + if (dynstr_realloc(str, bin_size + 20)) + return ER_DYNCOL_RESOURCE; + + /* The following can't fail as memory is already allocated */ + (void) dynamic_column_var_uint_store(str, value->intg); + (void) dynamic_column_var_uint_store(str, value->frac); + + decimal2bin(value, (uchar *) str->str + str->length, + precision, value->frac); + str->length+= bin_size; + return ER_DYNCOL_OK; +} + + +/** + Prepare the value to be used as decimal. + + @param value The value structure which sould be setup. +*/ + +void dynamic_column_prepare_decimal(DYNAMIC_COLUMN_VALUE *value) +{ + value->decimal_value.buf= value->decimal_buffer; + value->decimal_value.len= DECIMAL_BUFF_LENGTH; + /* just to be safe */ + value->type= DYN_COL_DECIMAL; + decimal_make_zero(&value->decimal_value); +} + + +/** + Read decimal value of given length from the string + + @param store_it_here The structure to store the value + @param data The string which should be read + @param length The length (in bytes) of the value in nthe string + + @return ER_DYNCOL_* return code +*/ + +static enum enum_dyncol_func_result +dynamic_column_decimal_read(DYNAMIC_COLUMN_VALUE *store_it_here, + uchar *data, size_t length) +{ + size_t intg_len, frac_len; + int intg, frac, precision, scale; + + dynamic_column_prepare_decimal(store_it_here); + /* Decimals 0.0 is stored as a zero length string */ + if (length == 0) + return ER_DYNCOL_OK; /* value contains zero */ + + intg= (int)dynamic_column_var_uint_get(data, length, &intg_len); + data+= intg_len; + frac= (int)dynamic_column_var_uint_get(data, length - intg_len, &frac_len); + data+= frac_len; + + /* Check the size of data is correct */ + precision= intg + frac; + scale= frac; + if (scale < 0 || precision <= 0 || scale > precision || + (length - intg_len - frac_len) > + (size_t) (DECIMAL_BUFF_LENGTH*sizeof(decimal_digit_t)) || + decimal_bin_size(intg + frac, frac) != + (int) (length - intg_len - frac_len)) + return ER_DYNCOL_FORMAT; + + if (bin2decimal(data, &store_it_here->decimal_value, precision, scale) != + E_DEC_OK) + return ER_DYNCOL_FORMAT; + return ER_DYNCOL_OK; +} + + +/** + Append the string with given datetime value. + + @param str the string where to put the value + @param value the value to put in the string + + @return ER_DYNCOL_* return code +*/ + +static enum enum_dyncol_func_result +dynamic_column_date_time_store(DYNAMIC_COLUMN *str, MYSQL_TIME *value) +{ + enum enum_dyncol_func_result rc; + /* + 0<----year----><mn><day>00000!<-hours--><min-><sec-><---microseconds---> + 12345678901234123412345 1123456789012345612345612345678901234567890 + <123456><123456><123456><123456><123456><123456><123456><123456><123456> + */ + if ((rc= dynamic_column_date_store(str, value)) || + (rc= dynamic_column_time_store(str, value))) + return rc; + return ER_DYNCOL_OK; +} + + +/** + Read datetime value of given length from the packed string + + @param store_it_here The structure to store the value + @param data The packed string which should be read + @param length The length (in bytes) of the value in nthe string + + @return ER_DYNCOL_* return code +*/ + +static enum enum_dyncol_func_result +dynamic_column_date_time_read(DYNAMIC_COLUMN_VALUE *store_it_here, + uchar *data, size_t length) +{ + enum enum_dyncol_func_result rc= ER_DYNCOL_FORMAT; + /* + 0<----year----><mn><day>00000!<-hours--><min-><sec-><---microseconds---> + 12345678901234123412345 1123456789012345612345612345678901234567890 + <123456><123456><123456><123456><123456><123456><123456><123456><123456> + */ + if (length != 9) + goto err; + store_it_here->time_value.time_type= MYSQL_TIMESTAMP_DATETIME; + if ((rc= dynamic_column_date_read_internal(store_it_here, data, 3)) || + (rc= dynamic_column_time_read_internal(store_it_here, data + 3, 6))) + goto err; + return ER_DYNCOL_OK; + +err: + store_it_here->time_value.time_type= MYSQL_TIMESTAMP_ERROR; + return rc; +} + + +/** + Append the string with given time value. + + @param str the string where to put the value + @param value the value to put in the string + + @return ER_DYNCOL_* return code +*/ + +static enum enum_dyncol_func_result +dynamic_column_time_store(DYNAMIC_COLUMN *str, MYSQL_TIME *value) +{ + uchar *buf; + if (dynstr_realloc(str, 6)) + return ER_DYNCOL_RESOURCE; + + buf= ((uchar *)str->str) + str->length; + + if (value->time_type == MYSQL_TIMESTAMP_NONE || + value->time_type == MYSQL_TIMESTAMP_ERROR || + value->time_type == MYSQL_TIMESTAMP_DATE) + { + value->neg= 0; + value->second_part= 0; + value->hour= 0; + value->minute= 0; + value->second= 0; + } + DBUG_ASSERT(value->hour <= 838); + DBUG_ASSERT(value->minute <= 59); + DBUG_ASSERT(value->second <= 59); + DBUG_ASSERT(value->second_part <= 999999); + /* + 00000!<-hours--><min-><sec-><---microseconds---> + 1123456789012345612345612345678901234567890 + <123456><123456><123456><123456><123456><123456> + */ + buf[0]= (value->second_part & 0xff); + buf[1]= ((value->second_part & 0xff00) >> 8); + buf[2]= (uchar)(((value->second & 0xf) << 4) | + ((value->second_part & 0xf0000) >> 16)); + buf[3]= ((value->minute << 2) | ((value->second & 0x30) >> 4)); + buf[4]= (value->hour & 0xff); + buf[5]= ((value->neg ? 0x4 : 0) | (value->hour >> 8)); + str->length+= 6; + return ER_DYNCOL_OK; +} + + +/** + Read time value of given length from the packed string + + @param store_it_here The structure to store the value + @param data The packed string which should be read + @param length The length (in bytes) of the value in nthe string + + @return ER_DYNCOL_* return code +*/ + +static enum enum_dyncol_func_result +dynamic_column_time_read(DYNAMIC_COLUMN_VALUE *store_it_here, + uchar *data, size_t length) +{ + store_it_here->time_value.year= store_it_here->time_value.month= + store_it_here->time_value.day= 0; + store_it_here->time_value.time_type= MYSQL_TIMESTAMP_TIME; + return dynamic_column_time_read_internal(store_it_here, data, length); +} + +/** + Internal function for reading time part from the string. + + @param store_it_here The structure to store the value + @param data The packed string which should be read + @param length The length (in bytes) of the value in nthe string + + @return ER_DYNCOL_* return code +*/ + +static enum enum_dyncol_func_result +dynamic_column_time_read_internal(DYNAMIC_COLUMN_VALUE *store_it_here, + uchar *data, size_t length) +{ + if (length != 6) + goto err; + /* + 00000!<-hours--><min-><sec-><---microseconds---> + 1123456789012345612345612345678901234567890 + <123456><123456><123456><123456><123456><123456> + */ + store_it_here->time_value.second_part= (data[0] | + (data[1] << 8) | + ((data[2] & 0xf) << 16)); + store_it_here->time_value.second= ((data[2] >> 4) | + ((data[3] & 0x3) << 4)); + store_it_here->time_value.minute= (data[3] >> 2); + store_it_here->time_value.hour= (((((uint)data[5]) & 0x3 ) << 8) | data[4]); + store_it_here->time_value.neg= ((data[5] & 0x4) ? 1 : 0); + if (store_it_here->time_value.second > 59 || + store_it_here->time_value.minute > 59 || + store_it_here->time_value.hour > 838 || + store_it_here->time_value.second_part > 999999) + goto err; + return ER_DYNCOL_OK; + +err: + store_it_here->time_value.time_type= MYSQL_TIMESTAMP_ERROR; + return ER_DYNCOL_FORMAT; +} + + +/** + Append the string with given date value. + + @param str the string where to put the value + @param value the value to put in the string + + @return ER_DYNCOL_* return code +*/ + +static enum enum_dyncol_func_result +dynamic_column_date_store(DYNAMIC_COLUMN *str, MYSQL_TIME *value) +{ + uchar *buf; + if (dynstr_realloc(str, 3)) + return ER_DYNCOL_RESOURCE; + + buf= ((uchar *)str->str) + str->length; + if (value->time_type == MYSQL_TIMESTAMP_NONE || + value->time_type == MYSQL_TIMESTAMP_ERROR || + value->time_type == MYSQL_TIMESTAMP_TIME) + value->year= value->month= value->day = 0; + DBUG_ASSERT(value->year <= 9999); + DBUG_ASSERT(value->month <= 12); + DBUG_ASSERT(value->day <= 31); + /* + 0<----year----><mn><day> + 012345678901234123412345 + <123456><123456><123456> + */ + buf[0]= (value->day | + ((value->month & 0x7) << 5)); + buf[1]= ((value->month >> 3) | ((value->year & 0x7F) << 1)); + buf[2]= (value->year >> 7); + str->length+= 3; + return ER_DYNCOL_OK; +} + + + +/** + Read date value of given length from the packed string + + @param store_it_here The structure to store the value + @param data The packed string which should be read + @param length The length (in bytes) of the value in nthe string + + @return ER_DYNCOL_* return code +*/ + +static enum enum_dyncol_func_result +dynamic_column_date_read(DYNAMIC_COLUMN_VALUE *store_it_here, + uchar *data, size_t length) +{ + store_it_here->time_value.neg= 0; + store_it_here->time_value.second_part= 0; + store_it_here->time_value.hour= 0; + store_it_here->time_value.minute= 0; + store_it_here->time_value.second= 0; + store_it_here->time_value.time_type= MYSQL_TIMESTAMP_DATE; + return dynamic_column_date_read_internal(store_it_here, data, length); +} + +/** + Internal function for reading date part from the string. + + @param store_it_here The structure to store the value + @param data The packed string which should be read + @param length The length (in bytes) of the value in nthe string + + @return ER_DYNCOL_* return code +*/ + +static enum enum_dyncol_func_result +dynamic_column_date_read_internal(DYNAMIC_COLUMN_VALUE *store_it_here, + uchar *data, + size_t length) +{ + if (length != 3) + goto err; + /* + 0<----year----><mn><day> + 12345678901234123412345 + <123456><123456><123456> + */ + store_it_here->time_value.day= (data[0] & 0x1f); + store_it_here->time_value.month= (((data[1] & 0x1) << 3) | + (data[0] >> 5)); + store_it_here->time_value.year= ((((uint)data[2]) << 7) | + (data[1] >> 1)); + if (store_it_here->time_value.day > 31 || + store_it_here->time_value.month > 12 || + store_it_here->time_value.year > 9999) + goto err; + return ER_DYNCOL_OK; + +err: + store_it_here->time_value.time_type= MYSQL_TIMESTAMP_ERROR; + return ER_DYNCOL_FORMAT; +} + + +/** + Append the string with given value. + + @param str the string where to put the value + @param value the value to put in the string + + @return ER_DYNCOL_* return code +*/ + +static enum enum_dyncol_func_result +data_store(DYNAMIC_COLUMN *str, DYNAMIC_COLUMN_VALUE *value) +{ + switch (value->type) { + case DYN_COL_INT: + return dynamic_column_sint_store(str, value->long_value); + case DYN_COL_UINT: + return dynamic_column_uint_store(str, value->ulong_value); + case DYN_COL_DOUBLE: + return dynamic_column_double_store(str, value->double_value); + case DYN_COL_STRING: + return dynamic_column_string_store(str, &value->string_value, + value->charset); + case DYN_COL_DECIMAL: + return dynamic_column_decimal_store(str, &value->decimal_value); + case DYN_COL_DATETIME: + /* date+time in bits: 14 + 4 + 5 + 5 + 6 + 6 40bits = 5 bytes */ + return dynamic_column_date_time_store(str, &value->time_value); + case DYN_COL_DATE: + /* date in dits: 14 + 4 + 5 = 23bits ~= 3bytes*/ + return dynamic_column_date_store(str, &value->time_value); + case DYN_COL_TIME: + /* time in bits: 5 + 6 + 6 = 17bits ~= 3bytes*/ + return dynamic_column_time_store(str, &value->time_value); + case DYN_COL_NULL: + break; /* Impossible */ + } + DBUG_ASSERT(0); + return ER_DYNCOL_OK; /* Impossible */ +} + + +/** + Calculate length of offset field for given data length + + @param data_length Length of the data segment + + @return number of bytes +*/ + +static size_t dynamic_column_offset_bytes(size_t data_length) +{ + if (data_length < 0x1f) /* all 1 value is reserved */ + return 1; + if (data_length < 0x1fff) /* all 1 value is reserved */ + return 2; + if (data_length < 0x1fffff) /* all 1 value is reserved */ + return 3; + if (data_length < 0x1fffffff) /* all 1 value is reserved */ + return 4; + return MAX_OFFSET_LENGTH; /* For future */ +} + +/** + Store offset and type information in the given place + + @param place Beginning of the index entry + @param offset_size Size of offset field in bytes + @param type Type to be written + @param offset Offset to be written +*/ + +static void type_and_offset_store(uchar *place, size_t offset_size, + DYNAMIC_COLUMN_TYPE type, + size_t offset) +{ + ulong val = (((ulong) offset) << 3) | (type - 1); + DBUG_ASSERT(type != DYN_COL_NULL); + DBUG_ASSERT(((type - 1) & (~7)) == 0); /* fit in 3 bits */ + + /* Index entry starts with column number; Jump over it */ + place+= COLUMN_NUMBER_SIZE; + + switch (offset_size) { + case 1: + DBUG_ASSERT(offset < 0x1f); /* all 1 value is reserved */ + place[0]= (uchar)val; + break; + case 2: + DBUG_ASSERT(offset < 0x1fff); /* all 1 value is reserved */ + int2store(place, val); + break; + case 3: + DBUG_ASSERT(offset < 0x1fffff); /* all 1 value is reserved */ + int3store(place, val); + break; + case 4: + DBUG_ASSERT(offset < 0x1fffffff); /* all 1 value is reserved */ + int4store(place, val); + break; + default: + DBUG_ASSERT(0); /* impossible */ + } +} + + +/** + Read offset and type information from index entry + + @param type Where to put type info + @param offset Where to put offset info + @param place Beginning of the index entry + @param offset_size Size of offset field in bytes +*/ + +static void type_and_offset_read(DYNAMIC_COLUMN_TYPE *type, + size_t *offset, + uchar *place, size_t offset_size) +{ + ulong val; + LINT_INIT(val); + + place+= COLUMN_NUMBER_SIZE; /* skip column number */ + switch (offset_size) { + case 1: + val= (ulong)place[0]; + break; + case 2: + val= uint2korr(place); + break; + case 3: + val= uint3korr(place); + break; + case 4: + val= uint4korr(place); + break; + default: + DBUG_ASSERT(0); /* impossible */ + } + *type= (val & 0x7) + 1; + *offset= val >> 3; +} + + +/** + Comparator function for references on column numbers for qsort +*/ + +static int column_sort(const void *a, const void *b) +{ + return **((uint **)a) - **((uint **)b); +} + + +/** + Write information to the fixed header + + @param str String where to write the header + @param offset_size Size of offset field in bytes + @param column_count Number of columns +*/ + +static void set_fixed_header(DYNAMIC_COLUMN *str, + uint offset_size, + uint column_count) +{ + DBUG_ASSERT(column_count <= 0xffff); + DBUG_ASSERT(offset_size <= 4); + str->str[0]= ((str->str[0] & ~DYNCOL_FLG_OFFSET) | + (offset_size - 1)); /* size of offset */ + int2store(str->str + 1, column_count); /* columns number */ + DBUG_ASSERT((str->str[0] & (~DYNCOL_FLG_KNOWN)) == 0); +} + +/* + Calculate entry size (E) and header size (H) by offset size (O) and column + count (C). +*/ + +#define calc_param(E,H,O,C) do { \ + (*(E))= (O) + COLUMN_NUMBER_SIZE; \ + (*(H))= (*(E)) * (C); \ +}while(0); + + +/** + Adds columns into the empty string + + @param str String where to write the data + @param header_size Size of the header without fixed part + @param offset_size Size of offset field in bytes + @param column_count Number of columns in the arrays + @parem not_null_count Number of non-null columns in the arrays + @param data_size Size of the data segment + @param column_numbers Array of columns numbers + @param values Array of columns values + @param new_str True if we need to allocate new string + + @return ER_DYNCOL_* return code +*/ + +static enum enum_dyncol_func_result +dynamic_new_column_store(DYNAMIC_COLUMN *str, + size_t header_size, + size_t offset_size, + uint column_count, + uint not_null_count, + size_t data_size, + uint *column_numbers, + DYNAMIC_COLUMN_VALUE *values, + my_bool new_str) +{ + uchar *header_end; + uint **columns_order; + uint i; + uint entry_size= COLUMN_NUMBER_SIZE + offset_size; + enum enum_dyncol_func_result rc= ER_DYNCOL_RESOURCE; + + if (!(columns_order= malloc(sizeof(uint*)*column_count))) + return ER_DYNCOL_RESOURCE; + if (new_str) + { + if (dynamic_column_init_str(str, + data_size + header_size + DYNCOL_SYZERESERVE)) + goto err; + } + else + { + str->length= 0; + if (dynstr_realloc(str, data_size + header_size + DYNCOL_SYZERESERVE)) + goto err; + bzero(str->str, FIXED_HEADER_SIZE); + str->length= FIXED_HEADER_SIZE; + } + + /* sort columns for the header */ + for (i= 0; i < column_count; i++) + columns_order[i]= column_numbers + i; + qsort(columns_order, (size_t)column_count, sizeof(uint*), &column_sort); + + /* + For now we don't allow creating two columns with the same number + at the time of create. This can be fixed later to just use the later + by comparing the pointers. + */ + for (i= 0; i < column_count - 1; i++) + { + if (columns_order[i][0] > UINT_MAX16 || + columns_order[i][0] == columns_order[i + 1][0]) + { + rc= ER_DYNCOL_DATA; + goto err; + } + } + if (columns_order[i][0] > UINT_MAX16) + { + rc= ER_DYNCOL_DATA; + goto err; + } + + DBUG_ASSERT(str->max_length >= str->length + header_size); + set_fixed_header(str, offset_size, not_null_count); + str->length+= header_size; /* reserve place for header */ + header_end= (uchar *)str->str + FIXED_HEADER_SIZE; + for (i= 0; i < column_count; i++) + { + uint ord= columns_order[i] - column_numbers; + if (values[ord].type != DYN_COL_NULL) + { + /* Store header first in the str */ + int2store(header_end, column_numbers[ord]); + type_and_offset_store(header_end, offset_size, + values[ord].type, + str->length - header_size - FIXED_HEADER_SIZE); + + /* Store value in 'str + str->length' and increase str->length */ + if ((rc= data_store(str, values + ord))) + goto err; + header_end+= entry_size; + } + } + rc= ER_DYNCOL_OK; +err: + free(columns_order); + return rc; +} + +/** + Create packed string which contains given columns (internal) + + @param str String where to write the data + @param column_count Number of columns in the arrays + @param column_numbers Array of columns numbers + @param values Array of columns values + @param new_str True if we need allocate new string + + @return ER_DYNCOL_* return code +*/ + +static enum enum_dyncol_func_result +dynamic_column_create_many_internal(DYNAMIC_COLUMN *str, + uint column_count, + uint *column_numbers, + DYNAMIC_COLUMN_VALUE *values, + my_bool new_str) +{ + size_t data_size= 0; + size_t header_size, offset_size; + uint i; + int not_null_column_count= 0; + + if (new_str) + { + /* to make dynstr_free() working in case of errors */ + bzero(str, sizeof(DYNAMIC_COLUMN)); + } + + for (i= 0; i < column_count; i++) + { + if (values[i].type != DYN_COL_NULL) + { + size_t tmp; + not_null_column_count++; + data_size+= (tmp=dynamic_column_value_len(values + i)); + if (tmp == (size_t) ~0) + return ER_DYNCOL_DATA; + } + } + + /* We can handle data up to 1fffffff = 536870911 bytes now */ + if ((offset_size= dynamic_column_offset_bytes(data_size)) >= + MAX_OFFSET_LENGTH) + return ER_DYNCOL_LIMIT; + + /* header entry is column number + offset & type */ + header_size= not_null_column_count * (offset_size + 2); + + return dynamic_new_column_store(str, + header_size, offset_size, + column_count, + not_null_column_count, + data_size, + column_numbers, values, + new_str); +} + + +/** + Create packed string which contains given columns + + @param str String where to write the data + @param column_count Number of columns in the arrays + @param column_numbers Array of columns numbers + @param values Array of columns values + + @return ER_DYNCOL_* return code +*/ + +enum enum_dyncol_func_result +dynamic_column_create_many(DYNAMIC_COLUMN *str, + uint column_count, + uint *column_numbers, + DYNAMIC_COLUMN_VALUE *values) +{ + DBUG_ENTER("dynamic_column_create_many"); + DBUG_RETURN(dynamic_column_create_many_internal(str, column_count, + column_numbers, values, + TRUE)); +} + + +/** + Create packed string which contains given column + + @param str String where to write the data + @param column_number Column number + @param value The columns value + + @return ER_DYNCOL_* return code +*/ + +enum enum_dyncol_func_result +dynamic_column_create(DYNAMIC_COLUMN *str, uint column_nr, + DYNAMIC_COLUMN_VALUE *value) +{ + DBUG_ENTER("dynamic_column_create"); + DBUG_RETURN(dynamic_column_create_many(str, 1, &column_nr, value)); +} + + +/** + Calculate length of data between given two header entries + + @param entry Pointer to the first entry + @param entry_next Pointer to the last entry + @param header_end Pointer to the header end + @param offset_size Size of offset field in bytes + @param last_offset Size of the data segment + + @return number of bytes +*/ + +static size_t get_length_interval(uchar *entry, uchar *entry_next, + uchar *header_end, size_t offset_size, + size_t last_offset) +{ + size_t offset, offset_next; + DYNAMIC_COLUMN_TYPE type, type_next; + DBUG_ASSERT(entry < entry_next); + + type_and_offset_read(&type, &offset, entry, offset_size); + if (entry_next >= header_end) + return (last_offset - offset); + type_and_offset_read(&type_next, &offset_next, entry_next, offset_size); + return (offset_next - offset); +} + +/* + Calculate length of data of one column + + + @param entry Pointer to the first entry + @param header_end Pointer to the header end + @param offset_size Size of offset field in bytes + @param last_offset Size of the data segment + + @return number of bytes +*/ + +static size_t get_length(uchar *entry, uchar *header_end, + size_t offset_size, + size_t last_offset) +{ + return get_length_interval(entry, + entry + offset_size + COLUMN_NUMBER_SIZE, + header_end, offset_size, last_offset); +} + + +/** + Comparator function for references to header entries for qsort +*/ + +static int header_compar(const void *a, const void *b) +{ + uint va= uint2korr((uchar*)a), vb= uint2korr((uchar*)b); + return (va > vb ? 1 : (va < vb ? -1 : 0)); +} + + +/** + Find column and fill information about it + + @param type Returns type of the column + @param data Returns a pointer to the data + @param length Returns length of the data + @param offset_size Size of offset field in bytes + @param column_count Number of column in the packed string + @param data_end Pointer to the data end + @param num Number of the column we want to fetch + @param entry_pos NULL or place where to put reference to the entry + + @return 0 ok + @return 1 error in data +*/ + +static my_bool +find_column(DYNAMIC_COLUMN_TYPE *type, uchar **data, size_t *length, + uchar *header, size_t offset_size, uint column_count, + uchar *data_end, uint num, uchar **entry_pos) +{ + uchar *entry; + size_t offset, total_data, header_size, entry_size; + uchar key[2+4]; + + if (!entry_pos) + entry_pos= &entry; + + calc_param(&entry_size, &header_size, offset_size, column_count); + + if (header + header_size > data_end) + return 1; + + int2store(key, num); + entry= bsearch(key, header, (size_t)column_count, entry_size, + &header_compar); + if (!entry) + { + /* Column not found */ + *type= DYN_COL_NULL; + *entry_pos= NULL; + return 0; + } + type_and_offset_read(type, &offset, entry, offset_size); + total_data= data_end - (header + header_size); + if (offset > total_data) + return 1; + *data= header + header_size + offset; + *length= get_length(entry, header + header_size, offset_size, + total_data); + /* + Check that the found data is withing the ranges. This can happen if + we get data with wrong offsets. + */ + if ((long) *length < 0 || offset + *length > total_data) + return 1; + + *entry_pos= entry; + return 0; +} + + +/** + Read and check the header of the dynamic string + + @param str Dynamic string + + @retval FALSE OK + @retval TRUE error + + Note + We don't check for str->length == 0 as all code that calls this + already have handled this case. +*/ + +static inline my_bool read_fixed_header(DYNAMIC_COLUMN *str, + size_t *offset_size, + uint *column_count) +{ + DBUG_ASSERT(str != NULL && str->length != 0); + if ((str->length < FIXED_HEADER_SIZE) || + (str->str[0] & (~DYNCOL_FLG_KNOWN))) + return 1; /* Wrong header */ + *offset_size= (str->str[0] & DYNCOL_FLG_OFFSET) + 1; + *column_count= uint2korr(str->str + 1); + return 0; +} + + +/** + Get dynamic column value + + @param str The packed string to extract the column + @param column_nr Number of column to fetch + @param store_it_here Where to store the extracted value + + @return ER_DYNCOL_* return code +*/ + +int dynamic_column_get(DYNAMIC_COLUMN *str, uint column_nr, + DYNAMIC_COLUMN_VALUE *store_it_here) +{ + uchar *data; + size_t offset_size, length; + uint column_count; + enum enum_dyncol_func_result rc= ER_DYNCOL_FORMAT; + + if (str->length == 0) + goto null; + + if (read_fixed_header(str, &offset_size, &column_count)) + goto err; + + if (column_count == 0) + goto null; + + if (find_column(&store_it_here->type, &data, &length, + (uchar*)str->str + FIXED_HEADER_SIZE, + offset_size, column_count, (uchar*)str->str + str->length, + column_nr, NULL)) + goto err; + + switch (store_it_here->type) { + case DYN_COL_INT: + rc= dynamic_column_sint_read(store_it_here, data, length); + break; + case DYN_COL_UINT: + rc= dynamic_column_uint_read(store_it_here, data, length); + break; + case DYN_COL_DOUBLE: + rc= dynamic_column_double_read(store_it_here, data, length); + break; + case DYN_COL_STRING: + rc= dynamic_column_string_read(store_it_here, data, length); + break; + case DYN_COL_DECIMAL: + rc= dynamic_column_decimal_read(store_it_here, data, length); + break; + case DYN_COL_DATETIME: + rc= dynamic_column_date_time_read(store_it_here, data, length); + break; + case DYN_COL_DATE: + rc= dynamic_column_date_read(store_it_here, data, length); + break; + case DYN_COL_TIME: + rc= dynamic_column_time_read(store_it_here, data, length); + break; + case DYN_COL_NULL: + rc= ER_DYNCOL_OK; + break; + default: + goto err; + } + return rc; + +null: + rc= ER_DYNCOL_OK; +err: + store_it_here->type= DYN_COL_NULL; + return rc; +} + +/** + Delete column with given number from the packed string + + @param str The packed string to delete the column + @param column_nr Number of column to delete + + @return ER_DYNCOL_* return code +*/ + +int dynamic_column_delete(DYNAMIC_COLUMN *str, uint column_nr) +{ + uchar *data, *header_entry, *read, *write; + size_t offset_size, new_offset_size, length, entry_size, new_entry_size, + header_size, new_header_size, data_size, new_data_size, + deleted_entry_offset; + uint column_count, i; + DYNAMIC_COLUMN_TYPE type; + + if (str->length == 0) + return ER_DYNCOL_OK; /* no columns */ + + if (read_fixed_header(str, &offset_size, &column_count)) + return ER_DYNCOL_FORMAT; + + if (column_count == 0) + { + str->length= 0; + return ER_DYNCOL_OK; /* no columns */ + } + + if (find_column(&type, &data, &length, (uchar*)str->str + FIXED_HEADER_SIZE, + offset_size, column_count, (uchar*)str->str + str->length, + column_nr, &header_entry)) + return ER_DYNCOL_FORMAT; + + if (type == DYN_COL_NULL) + return ER_DYNCOL_OK; /* no such column */ + + if (column_count == 1) + { + /* delete the only column; Return empty string */ + str->length= 0; + return ER_DYNCOL_OK; + } + + /* Calculate entry_size and header_size */ + calc_param(&entry_size, &header_size, offset_size, column_count); + data_size= str->length - FIXED_HEADER_SIZE - header_size; + + new_data_size= data_size - length; + if ((new_offset_size= dynamic_column_offset_bytes(new_data_size)) >= + MAX_OFFSET_LENGTH) + return ER_DYNCOL_LIMIT; + DBUG_ASSERT(new_offset_size <= offset_size); + + calc_param(&new_entry_size, &new_header_size, + new_offset_size, column_count - 1); + + deleted_entry_offset= ((data - (uchar*) str->str) - + header_size - FIXED_HEADER_SIZE); + + /* rewrite header*/ + set_fixed_header(str, new_offset_size, column_count - 1); + for (i= 0, write= read= (uchar *)str->str + FIXED_HEADER_SIZE; + i < column_count; + i++, read+= entry_size, write+= new_entry_size) + { + size_t offs; + uint nm; + DYNAMIC_COLUMN_TYPE tp; + if (read == header_entry) + { +#ifndef DBUG_OFF + nm= uint2korr(read); + type_and_offset_read(&tp, &offs, read, + offset_size); + DBUG_ASSERT(nm == column_nr); + DBUG_ASSERT(offs == deleted_entry_offset); +#endif + write-= new_entry_size; /* do not move writer */ + continue; /* skip removed field */ + } + + nm= uint2korr(read), + type_and_offset_read(&tp, &offs, read, + offset_size); + + if (offs > deleted_entry_offset) + offs-= length; /* data stored after removed data */ + + int2store(write, nm); + type_and_offset_store(write, new_offset_size, tp, offs); + } + + /* move data */ + { + size_t first_chunk_len= ((data - (uchar *)str->str) - + FIXED_HEADER_SIZE - header_size); + size_t second_chunk_len= new_data_size - first_chunk_len; + if (first_chunk_len) + memmove(str->str + FIXED_HEADER_SIZE + new_header_size, + str->str + FIXED_HEADER_SIZE + header_size, + first_chunk_len); + if (second_chunk_len) + memmove(str->str + + FIXED_HEADER_SIZE + new_header_size + first_chunk_len, + str->str + + FIXED_HEADER_SIZE + header_size + first_chunk_len + length, + second_chunk_len); + } + + /* fix str length */ + DBUG_ASSERT(str->length >= + FIXED_HEADER_SIZE + new_header_size + new_data_size); + str->length= FIXED_HEADER_SIZE + new_header_size + new_data_size; + + return ER_DYNCOL_OK; +} + + +/** + Check existence of the column in the packed string + + @param str The packed string to check the column + @param column_nr Number of column to check + + @return ER_DYNCOL_* return code +*/ + +enum enum_dyncol_func_result +dynamic_column_exists(DYNAMIC_COLUMN *str, uint column_nr) +{ + uchar *data; + size_t offset_size, length; + uint column_count; + DYNAMIC_COLUMN_TYPE type; + + if (str->length == 0) + return ER_DYNCOL_NO; /* no columns */ + + if (read_fixed_header(str, &offset_size, &column_count)) + return ER_DYNCOL_FORMAT; + + if (column_count == 0) + return ER_DYNCOL_NO; /* no columns */ + + if (find_column(&type, &data, &length, (uchar*)str->str + FIXED_HEADER_SIZE, + offset_size, column_count, (uchar*)str->str + str->length, + column_nr, NULL)) + return ER_DYNCOL_FORMAT; + + return (type != DYN_COL_NULL ? ER_DYNCOL_YES : ER_DYNCOL_NO); +} + + +/** + List not-null columns in the packed string + + @param str The packed string + @param array_of_uint Where to put reference on created array + + @return ER_DYNCOL_* return code +*/ + +enum enum_dyncol_func_result +dynamic_column_list(DYNAMIC_COLUMN *str, DYNAMIC_ARRAY *array_of_uint) +{ + uchar *read; + size_t offset_size, entry_size; + uint column_count, i; + + bzero(array_of_uint, sizeof(*array_of_uint)); /* In case of errors */ + if (str->length == 0) + return ER_DYNCOL_OK; /* no columns */ + + if (read_fixed_header(str, &offset_size, &column_count)) + return ER_DYNCOL_FORMAT; + + entry_size= COLUMN_NUMBER_SIZE + offset_size; + + if (entry_size * column_count + FIXED_HEADER_SIZE > str->length) + return ER_DYNCOL_FORMAT; + + if (init_dynamic_array(array_of_uint, sizeof(uint), column_count, 0)) + return ER_DYNCOL_RESOURCE; + + for (i= 0, read= (uchar *)str->str + FIXED_HEADER_SIZE; + i < column_count; + i++, read+= entry_size) + { + uint nm= uint2korr(read); + /* Insert can't never fail as it's pre-allocated above */ + (void) insert_dynamic(array_of_uint, (uchar *)&nm); + } + return ER_DYNCOL_OK; +} + + +/** + Find the place of the column in the header or place where it should be put + + @param num Number of the column + @param header Pointer to the header + @param entry_size Size of a header entry + @param column_count Number of columns in the packed string + @param entry Return pointer to the entry or next entry + + @retval TRUE found + @retval FALSE pointer set to the next row +*/ + +static my_bool +find_place(uint num, uchar *header, size_t entry_size, + uint column_count, uchar **entry) +{ + uint mid, start, end, val; + int flag; + LINT_INIT(flag); /* 100 % safe */ + + start= 0; + end= column_count -1; + mid= 1; + while (start != end) + { + uint val; + mid= (start + end) / 2; + val= uint2korr(header + mid * entry_size); + if ((flag= CMP_NUM(num, val)) <= 0) + end= mid; + else + start= mid + 1; + } + if (start != mid) + { + val= uint2korr(header + start * entry_size); + flag= CMP_NUM(num, val); + } + *entry= header + start * entry_size; + if (flag > 0) + *entry+= entry_size; /* Point at next bigger key */ + return flag == 0; +} + + +/* + Description of plan of adding/removing/updating a packed string +*/ + +typedef enum {PLAN_REPLACE, PLAN_ADD, PLAN_DELETE, PLAN_NOP} PLAN_ACT; + +struct st_plan { + DYNAMIC_COLUMN_VALUE *val; + uint *num; + uchar *place; + size_t length; + int hdelta, ddelta; + PLAN_ACT act; +}; +typedef struct st_plan PLAN; + + +static int plan_sort(const void *a, const void *b) +{ + return ((PLAN *)a)->num[0] - ((PLAN *)b)->num[0]; +} + +#define DELTA_CHECK(S, D, C) \ + if ((S) == 0) \ + (S)= (D); \ + else if (((S) > 0 && (D) < 0) || \ + ((S) < 0 && (D) > 0)) \ + { \ + (C)= TRUE; \ + break; \ + } \ + + +/** + Update the packed string with the given columns + + @param str String where to write the data + @param add_column_count Number of columns in the arrays + @param column_numbers Array of columns numbers + @param values Array of columns values + + @return ER_DYNCOL_* return code +*/ + +enum enum_dyncol_func_result +dynamic_column_update_many(DYNAMIC_COLUMN *str, + uint add_column_count, + uint *column_numbers, + DYNAMIC_COLUMN_VALUE *values) +{ + PLAN *plan; + uchar *header_end; + long data_delta= 0; + uint i, j, k; + uint new_column_count, column_count, not_null; + enum enum_dyncol_func_result rc; + int header_delta, header_delta_sign, data_delta_sign; + size_t offset_size, entry_size, header_size, data_size; + size_t new_offset_size, new_entry_size, new_header_size, new_data_size; + size_t max_offset; + my_bool copy; + + if (add_column_count == 0) + return ER_DYNCOL_OK; + + /* + Get columns in column order. As the data in 'str' is already + in column order this allows to replace all columns in one loop. + */ + + if (!(plan= my_malloc(sizeof(PLAN) * (add_column_count + 1), MYF(0)))) + return ER_DYNCOL_RESOURCE; + + not_null= add_column_count; + for (i= 0; i < add_column_count; i++) + { + if (column_numbers[i] > UINT_MAX16) + { + rc= ER_DYNCOL_DATA; + goto end; + } + + plan[i].val= values + i; + plan[i].num= column_numbers + i; + if (values[i].type == DYN_COL_NULL) + not_null--; + + } + + if (str->length == 0) + { + /* + Just add new columns. If there was no columns to add we return + an empty string. + */ + goto create_new_string; + } + + /* Check that header is ok */ + if (read_fixed_header(str, &offset_size, &column_count)) + { + rc= ER_DYNCOL_FORMAT; + goto end; + } + if (column_count == 0) + goto create_new_string; + + qsort(plan, (size_t)add_column_count, sizeof(PLAN), &plan_sort); + + new_column_count= column_count; + calc_param(&entry_size, &header_size, offset_size, column_count); + max_offset= str->length - (FIXED_HEADER_SIZE + header_size); + header_end= (uchar*) str->str + FIXED_HEADER_SIZE + header_size; + + if (header_size + FIXED_HEADER_SIZE > str->length) + { + rc= ER_DYNCOL_FORMAT; + goto end; + } + + /* + Calculate how many columns and data is added/deleted and make a 'plan' + for each of them. + */ + header_delta= 0; + for (i= 0; i < add_column_count; i++) + { + uchar *entry; + + /* + For now we don't allow creating two columns with the same number + at the time of create. This can be fixed later to just use the later + by comparing the pointers. + */ + if (i < add_column_count - 1 && plan[i].num[0] == plan[i + 1].num[0]) + { + rc= ER_DYNCOL_DATA; + goto end; + } + + /* Set common variables for all plans */ + plan[i].ddelta= data_delta; + /* get header delta in entries */ + plan[i].hdelta= header_delta; + plan[i].length= 0; /* Length if NULL */ + + if (find_place(plan[i].num[0], + (uchar *)str->str + FIXED_HEADER_SIZE, + entry_size, column_count, &entry)) + { + size_t entry_data_size; + + /* Data existed; We have to replace or delete it */ + + entry_data_size= get_length(entry, header_end, + offset_size, max_offset); + if ((long) entry_data_size < 0) + { + rc= ER_DYNCOL_FORMAT; + goto end; + } + + if (plan[i].val->type == DYN_COL_NULL) + { + /* Inserting a NULL means delete the old data */ + + plan[i].act= PLAN_DELETE; /* Remove old value */ + header_delta--; /* One row less in header */ + data_delta-= entry_data_size; /* Less data to store */ + } + else + { + /* Replace the value */ + + plan[i].act= PLAN_REPLACE; + /* get data delta in bytes */ + if ((plan[i].length= dynamic_column_value_len(plan[i].val)) == + (size_t) ~0) + { + rc= ER_DYNCOL_DATA; + goto end; + } + data_delta+= plan[i].length - entry_data_size; + } + } + else + { + /* Data did not exists. Add if it it's not NULL */ + + if (plan[i].val->type == DYN_COL_NULL) + { + plan[i].act= PLAN_NOP; /* Mark entry to be skiped */ + } + else + { + /* Add new value */ + + plan[i].act= PLAN_ADD; + header_delta++; /* One more row in header */ + /* get data delta in bytes */ + if ((plan[i].length= dynamic_column_value_len(plan[i].val)) == + (size_t) ~0) + { + rc= ER_DYNCOL_DATA; + goto end; + } + data_delta+= plan[i].length; + } + } + plan[i].place= entry; + } + plan[add_column_count].hdelta= header_delta; + plan[add_column_count].ddelta= data_delta; + new_column_count= column_count + header_delta; + + /* + Check if it is only "increasing" or only "decreasing" plan for (header + and data separately). + */ + data_size= str->length - header_size - FIXED_HEADER_SIZE; + new_data_size= data_size + data_delta; + if ((new_offset_size= dynamic_column_offset_bytes(new_data_size)) >= + MAX_OFFSET_LENGTH) + { + rc= ER_DYNCOL_LIMIT; + goto end; + } + + /* if (new_offset_size != offset_size) then we have to rewrite header */ + header_delta_sign= new_offset_size - offset_size; + data_delta_sign= 0; + copy= FALSE; + for (i= 0; i < add_column_count; i++) + { + /* This is the check for increasing/decreasing */ + DELTA_CHECK(header_delta_sign, plan[i].hdelta, copy); + DELTA_CHECK(data_delta_sign, plan[i].ddelta, copy); + } + + calc_param(&new_entry_size, &new_header_size, + new_offset_size, new_column_count); + + /* + The following code always make a copy. In future we can do a more + optimized version when data is only increasing / decreasing. + */ + + /*if (copy) */ + { + DYNAMIC_COLUMN tmp; + uchar *header_base= (uchar *)str->str + FIXED_HEADER_SIZE, + *write; + if (dynamic_column_init_str(&tmp, + (FIXED_HEADER_SIZE + new_header_size + + new_data_size + DYNCOL_SYZERESERVE))) + { + rc= ER_DYNCOL_RESOURCE; + goto end; + } + write= (uchar *)tmp.str + FIXED_HEADER_SIZE; + /* Adjust tmp to contain whole the future header */ + tmp.length= FIXED_HEADER_SIZE + new_header_size; + set_fixed_header(&tmp, new_offset_size, new_column_count); + data_delta= 0; + + /* + Copy data to the new string + i= index in array of changes + j= index in packed string header index + */ + + for (i= 0, j= 0; i < add_column_count || j < column_count; i++) + { + size_t first_offset; + uint start= j, end; + LINT_INIT(first_offset); + + /* + Search in i and j for the next column to add from i and where to + add. + */ + + while (i < add_column_count && plan[i].act == PLAN_NOP) + i++; /* skip NOP */ + if (i == add_column_count) + j= end= column_count; + else + { + /* + old data portion. We don't need to check that j < column_count + as plan[i].place is guaranteed to have a pointer inside the + data. + */ + while (header_base + j * entry_size < plan[i].place) + j++; + end= j; + if ((plan[i].act == PLAN_REPLACE || plan[i].act == PLAN_DELETE)) + j++; /* data at 'j' will be removed */ + } + + if (plan[i].ddelta == 0 && offset_size == new_offset_size) + { + uchar *read= header_base + start * entry_size; + DYNAMIC_COLUMN_TYPE tp; + /* + It's safe to copy the header unchanged. This is usually the + case for the first header block before any changed data. + */ + if (start < end) /* Avoid memcpy with 0 */ + { + size_t length= entry_size * (end - start); + memcpy(write, read, length); + write+= length; + } + /* Read first_offset */ + type_and_offset_read(&tp, &first_offset, read, offset_size); + } + else + { + /* + Adjust all headers since last loop. + We have to do this as the offset for data has moved + */ + for (k= start; k < end; k++) + { + uchar *read= header_base + k * entry_size; + size_t offs; + uint nm; + DYNAMIC_COLUMN_TYPE tp; + + nm= uint2korr(read); /* Column nummber */ + type_and_offset_read(&tp, &offs, read, offset_size); + if (k == start) + first_offset= offs; + else if (offs < first_offset) + { + dynamic_column_column_free(&tmp); + rc= ER_DYNCOL_FORMAT; + goto end; + } + + offs+= plan[i].ddelta; + int2store(write, nm); + /* write rest of data at write + COLUMN_NUMBER_SIZE */ + type_and_offset_store(write, new_offset_size, tp, offs); + write+= new_entry_size; + } + } + + /* copy first the data that was not replaced in original packed data */ + if (start < end) + { + /* Add old data last in 'tmp' */ + size_t data_size= + get_length_interval(header_base + start * entry_size, + header_base + end * entry_size, + header_end, offset_size, max_offset); + if ((long) data_size < 0 || + data_size > max_offset - first_offset) + { + dynamic_column_column_free(&tmp); + rc= ER_DYNCOL_FORMAT; + goto end; + } + + memcpy(tmp.str + tmp.length, (char *)header_end + first_offset, + data_size); + tmp.length+= data_size; + } + + /* new data adding */ + if (i < add_column_count) + { + if( plan[i].act == PLAN_ADD || plan[i].act == PLAN_REPLACE) + { + int2store(write, plan[i].num[0]); + type_and_offset_store(write, new_offset_size, + plan[i].val[0].type, + tmp.length - + (FIXED_HEADER_SIZE + new_header_size)); + write+= new_entry_size; + data_store(&tmp, plan[i].val); /* Append new data */ + } + data_delta= plan[i].ddelta; + } + } + dynamic_column_column_free(str); + *str= tmp; + } + + rc= ER_DYNCOL_OK; + +end: + my_free(plan); + return rc; + +create_new_string: + /* There is no columns from before, so let's just add the new ones */ + rc= ER_DYNCOL_OK; + if (not_null != 0) + rc= dynamic_column_create_many_internal(str, add_column_count, + column_numbers, values, + str->str == NULL); + goto end; +} + + +/** + Update the packed string with the given column + + @param str String where to write the data + @param column_number Array of columns number + @param values Array of columns values + + @return ER_DYNCOL_* return code +*/ + + +int dynamic_column_update(DYNAMIC_COLUMN *str, uint column_nr, + DYNAMIC_COLUMN_VALUE *value) +{ + return dynamic_column_update_many(str, 1, &column_nr, value); +} diff --git a/mysys/mf_getdate.c b/mysys/mf_getdate.c index 9475bebd107..70278e64003 100644 --- a/mysys/mf_getdate.c +++ b/mysys/mf_getdate.c @@ -42,7 +42,7 @@ void get_date(register char * to, int flag, time_t date) struct tm tm_tmp; #endif - skr=date ? (time_t) date : my_time(0); + skr=date ? date : (time_t) my_time(0); #if defined(HAVE_LOCALTIME_R) && defined(_REENTRANT) if (flag & GETDATE_GMT) gmtime_r(&skr,&tm_tmp); diff --git a/mysys/mf_iocache.c b/mysys/mf_iocache.c index 82098a7c9cc..cde11f3cae5 100644 --- a/mysys/mf_iocache.c +++ b/mysys/mf_iocache.c @@ -1683,9 +1683,6 @@ int my_block_write(register IO_CACHE *info, const uchar *Buffer, size_t Count, 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.*/ diff --git a/mysys/my_bitmap.c b/mysys/my_bitmap.c index 762a89cd921..8f86e5832f6 100644 --- a/mysys/my_bitmap.c +++ b/mysys/my_bitmap.c @@ -1,4 +1,6 @@ -/* Copyright (C) 2000 MySQL AB, 2008-2009 Sun Microsystems, Inc +/* Copyright (C) 2000 MySQL AB + Copyright (c) 2000, 2011, Oracle and/or its affiliates. + Copyright (C) 2009- 2011 Monty Program 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 @@ -258,7 +260,10 @@ void bitmap_set_prefix(MY_BITMAP *map, uint prefix_size) memset(m, 0xff, prefix_bytes); m+= prefix_bytes; if ((prefix_bits= prefix_size & 7)) + { *m++= (1 << prefix_bits)-1; + prefix_bytes++; + } if ((d= no_bytes_in_map(map)-prefix_bytes)) bzero(m, d); } @@ -293,6 +298,7 @@ my_bool bitmap_is_prefix(const MY_BITMAP *map, uint prefix_size) return ((*m & last_byte_mask(map->n_bits)) == 0); } + my_bool bitmap_is_set_all(const MY_BITMAP *map) { my_bitmap_map *data_ptr= map->bitmap; @@ -307,8 +313,8 @@ my_bool bitmap_is_set_all(const MY_BITMAP *map) my_bool bitmap_is_clear_all(const MY_BITMAP *map) { my_bitmap_map *data_ptr= map->bitmap; - my_bitmap_map *end; - end= map->last_word_ptr; + my_bitmap_map *end= map->last_word_ptr; + for (; data_ptr < end; data_ptr++) if (*data_ptr) return FALSE; @@ -493,6 +499,7 @@ void bitmap_copy(MY_BITMAP *map, const MY_BITMAP *map2) DBUG_ASSERT(map->bitmap && map2->bitmap && map->n_bits==map2->n_bits); end= map->last_word_ptr; + while (to <= end) *to++ = *from++; } @@ -587,375 +594,3 @@ void bitmap_lock_clear_bit(MY_BITMAP *map, uint bitmap_bit) bitmap_unlock(map); } -#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/mysys/my_compare.c b/mysys/my_compare.c index 4629a3bae18..9e192e52fb7 100644 --- a/mysys/my_compare.c +++ b/mysys/my_compare.c @@ -1,4 +1,5 @@ -/* Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. +/* Copyright (c) 2011, Oracle and/or its affiliates. + Copyright (C) 2009-2011 Monty Program 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 diff --git a/mysys/my_fopen.c b/mysys/my_fopen.c index 9bc740c80fd..54469d2c05a 100644 --- a/mysys/my_fopen.c +++ b/mysys/my_fopen.c @@ -1,4 +1,5 @@ -/* Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved. +/* Copyright (c) 2000, 2011, Oracle and/or its affiliates. + Copyright (c) 1985-2011 Monty Program 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 diff --git a/mysys/my_getncpus.c b/mysys/my_getncpus.c index 5be961e3bc9..a7631e15966 100644 --- a/mysys/my_getncpus.c +++ b/mysys/my_getncpus.c @@ -41,7 +41,7 @@ int my_getncpus() ncpus= sysinfo.dwNumberOfProcessors; #else -/* unknown so play safe: assume SMP and forbid uniprocessor build */ + /* unknown so play safe: assume SMP and forbid uniprocessor build */ ncpus= 2; #endif } diff --git a/mysys/my_getsystime.c b/mysys/my_getsystime.c index 60cd06b3968..bc21b07e24d 100644 --- a/mysys/my_getsystime.c +++ b/mysys/my_getsystime.c @@ -1,4 +1,5 @@ -/* Copyright (C) 2004 MySQL AB, 2008-2009 Sun Microsystems, Inc +/* Copyright (c) 2004, 2011, Oracle and/or its affiliates. + Copyright (c) 2009-2011 Monty Program 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 @@ -13,213 +14,96 @@ 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 __WIN__ +#define OFFSET_TO_EPOC 116444736000000000LL +static ulonglong query_performance_frequency; +#endif #ifdef HAVE_LINUX_UNISTD_H #include <linux/unistd.h> #endif -ulonglong my_getsystime() +/* + return number of nanoseconds since unspecified (but always the same) + point in the past + + NOTE: + Thus to get the current time we should use the system function + with the highest possible resolution + + The value is not anchored to any specific point in time (e.g. epoch) nor + is it subject to resetting or drifting by way of adjtime() or settimeofday(), + and thus it is *NOT* appropriate for getting the current timestamp. It can be + used for calculating time intervals, though. +*/ + +ulonglong my_interval_timer() { #ifdef HAVE_CLOCK_GETTIME struct timespec tp; - clock_gettime(CLOCK_REALTIME, &tp); - return (ulonglong)tp.tv_sec*10000000+(ulonglong)tp.tv_nsec/100; + clock_gettime(CLOCK_MONOTONIC, &tp); + return tp.tv_sec*1000000000ULL+tp.tv_nsec; +#elif defined(HAVE_GETHRTIME) + return gethrtime(); #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 (t_cnt.QuadPart / query_performance_frequency * 1000000000ULL) + + ((t_cnt.QuadPart % query_performance_frequency) * 1000000000ULL / + query_performance_frequency); + } + else + { + ulonglong newtime; + GetSystemTimeAsFileTime((FILETIME*)&newtime); + return newtime*100ULL; } - return 0; #else /* TODO: check for other possibilities for hi-res timestamping */ struct timeval tv; gettimeofday(&tv,NULL); - return (ulonglong)tv.tv_sec*10000000+(ulonglong)tv.tv_usec*10; -#endif -} - - -/* - Return current time - - 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; + return tv.tv_sec*1000000000ULL+tv.tv_usec*1000ULL; #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 current time in HRTIME_RESOLUTION (microseconds) since epoch */ - RETURN - Value in microseconds from some undefined point in time -*/ - -ulonglong my_micro_time() +my_hrtime_t my_hrtime() { + my_hrtime_t hrtime; #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 */ - -/* Difference between GetSystemTimeAsFileTime() and now() */ -#define OFFSET_TO_EPOCH 116444736000000000ULL - -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; - - mysql_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; - mysql_mutex_unlock(&THR_LOCK_time); - return cur_gethrtime/1000; + newtime -= OFFSET_TO_EPOC; + hrtime.val= newtime/10; +#elif defined(HAVE_CLOCK_GETTIME) + struct timespec tp; + clock_gettime(CLOCK_REALTIME, &tp); + hrtime.val= tp.tv_sec*1000000ULL+tp.tv_nsec/1000ULL; #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__) */ + /* The following loop is here because gettimeofday may fail */ + while (gettimeofday(&t, NULL) != 0) {} + hrtime.val= t.tv_sec*1000000ULL + t.tv_usec; +#endif + return hrtime; } -/* - 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))) +void my_time_init() { -#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__) */ +#ifdef __WIN__ + compile_time_assert(sizeof(LARGE_INTEGER) == + sizeof(query_performance_frequency)); + if (QueryPerformanceFrequency((LARGE_INTEGER *)&query_performance_frequency) == 0) + query_performance_frequency= 0; +#endif } diff --git a/mysys/my_handler_errors.h b/mysys/my_handler_errors.h index aed08b0151c..8f2a8b750bd 100644 --- a/mysys/my_handler_errors.h +++ b/mysys/my_handler_errors.h @@ -50,8 +50,7 @@ static const char *handler_error_messages[]= "There's no partition in table for the given value", "Row-based binary logging of row failed", "Index needed in foreign key constraint", - "Upholding foreign key constraints would lead to a duplicate key error in " - "some other table", + "Upholding foreign key constraints would lead to a duplicate key error in some other table", "Table needs to be upgraded before it can be used", "Table is read only", "Failed to get next auto increment value", @@ -67,11 +66,12 @@ static const char *handler_error_messages[]= "Read page with wrong checksum", "Too many active concurrent transactions", "Index column length exceeds limit", - "Row is not visible by the current transaction" + "Row is not visible by the current transaction", + "Operation was interrupted by end user (probably kill command?)", + "Disk full" }; extern void my_handler_error_register(void); extern void my_handler_error_unregister(void); - #endif /* MYSYS_MY_HANDLER_ERRORS_INCLUDED */ diff --git a/mysys/my_init.c b/mysys/my_init.c index 92da047a3fb..580e29c381a 100644 --- a/mysys/my_init.c +++ b/mysys/my_init.c @@ -110,6 +110,7 @@ my_bool my_init(void) { DBUG_ENTER("my_init"); DBUG_PROCESS((char*) (my_progname ? my_progname : "unknown")); + my_time_init(); my_win_init(); DBUG_PRINT("exit", ("home: '%s'", home_dir)); #ifdef __WIN__ @@ -250,6 +251,7 @@ void my_parameter_handler(const wchar_t * expression, const wchar_t * function, { DBUG_PRINT("my",("Expression: %s function: %s file: %s, line: %d", expression, function, file, line)); + __debugbreak(); } @@ -274,41 +276,13 @@ int handle_rtc_failure(int err_type, const char *file, int line, fprintf(stderr, " At %s:%d\n", file, line); va_end(args); (void) fflush(stderr); + __debugbreak(); return 0; /* Error is handled */ } #pragma runtime_checks("", restore) #endif -#define OFFSET_TO_EPOC ((__int64) 134774 * 24 * 60 * 60 * 1000 * 1000 * 10) -#define MS 10000000 - -static void win_init_time(void) -{ - /* The following is used by time functions */ - FILETIME ft; - LARGE_INTEGER li, t_cnt; - - DBUG_ASSERT(sizeof(LARGE_INTEGER) == sizeof(query_performance_frequency)); - - if (QueryPerformanceFrequency((LARGE_INTEGER *)&query_performance_frequency) == 0) - query_performance_frequency= 0; - else - { - GetSystemTimeAsFileTime(&ft); - li.LowPart= ft.dwLowDateTime; - li.HighPart= ft.dwHighDateTime; - query_performance_offset= li.QuadPart-OFFSET_TO_EPOC; - QueryPerformanceCounter(&t_cnt); - query_performance_offset-= (t_cnt.QuadPart / - query_performance_frequency * MS + - t_cnt.QuadPart % - query_performance_frequency * MS / - query_performance_frequency); - } -} - - /* Open HKEY_LOCAL_MACHINE\SOFTWARE\MySQL and set any strings found there as environment variables @@ -392,7 +366,6 @@ static void my_win_init(void) _tzset(); - win_init_time(); win_init_registry(); DBUG_VOID_RETURN; diff --git a/mysys/my_open.c b/mysys/my_open.c index db627c72f91..bac0be46766 100644 --- a/mysys/my_open.c +++ b/mysys/my_open.c @@ -90,9 +90,6 @@ int my_close(File fd, myf MyFlags) if ((uint) fd < my_file_limit && my_file_info[fd].type != UNOPEN) { my_free(my_file_info[fd].name); -#if !defined(HAVE_PREAD) && !defined(_WIN32) - mysql_mutex_destroy(&my_file_info[fd].mutex); -#endif my_file_info[fd].type = UNOPEN; } my_file_opened--; @@ -126,12 +123,8 @@ File my_register_filename(File fd, const char *FileName, enum file_type { if ((uint) fd >= my_file_limit) { -#if !defined(HAVE_PREAD) - my_errno= EMFILE; -#else thread_safe_increment(my_file_opened,&THR_LOCK_open); DBUG_RETURN(fd); /* safeguard */ -#endif } else { @@ -141,10 +134,6 @@ File my_register_filename(File fd, const char *FileName, enum file_type my_file_opened++; my_file_total_opened++; my_file_info[fd].type = type_of_file; -#if !defined(HAVE_PREAD) && !defined(_WIN32) - mysql_mutex_init(key_my_file_info_mutex, &my_file_info[fd].mutex, - MY_MUTEX_INIT_FAST); -#endif mysql_mutex_unlock(&THR_LOCK_open); DBUG_PRINT("exit",("fd: %d",fd)); DBUG_RETURN(fd); diff --git a/mysys/my_port.c b/mysys/my_port.c new file mode 100644 index 00000000000..96dbe10b1bd --- /dev/null +++ b/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. + + It doesn't work to make this function inline. +*/ + +double my_ulonglong2double(unsigned long long nr) +{ + return (double) nr; +} +#endif /* _AIX */ diff --git a/mysys/my_pread.c b/mysys/my_pread.c index e006360c11b..9d513a08418 100644 --- a/mysys/my_pread.c +++ b/mysys/my_pread.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2000 MySQL AB, 2008-2009 Sun Microsystems, Inc +/* 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 @@ -18,12 +18,10 @@ #include "my_base.h" #include <m_string.h> #include <errno.h> -#if defined (HAVE_PREAD) && !defined(_WIN32) +#ifndef _WIN32 #include <unistd.h> #endif - - /* Read a chunk of bytes from a file from a given position @@ -50,49 +48,34 @@ size_t my_pread(File Filedes, uchar *Buffer, size_t Count, my_off_t offset, { 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) - mysql_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; - mysql_mutex_unlock(&my_file_info[Filedes].mutex); - if (error) - errno= save_errno; -#else -#if defined(_WIN32) +#ifdef _WIN32 readbytes= my_win_pread(Filedes, Buffer, Count, offset); -#else +#else readbytes= pread(Filedes, Buffer, Count, offset); #endif - error= (readbytes != Count); -#endif - if(error) + error = (readbytes != Count); + + 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; - + (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)); - if ((readbytes == 0 || readbytes == (size_t) -1) && errno == EINTR) { DBUG_PRINT("debug", ("my_pread() was interrupted and returned %d", (int) readbytes)); continue; /* Interrupted */ } - if (MyFlags & (MY_WME | MY_FAE | MY_FNABP)) { if (readbytes == (size_t) -1) @@ -133,50 +116,38 @@ size_t my_pread(File Filedes, uchar *Buffer, size_t Count, my_off_t offset, # Number of bytes read */ -size_t my_pwrite(File Filedes, const uchar *Buffer, size_t Count, +size_t my_pwrite(int 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)); + Filedes, (ulonglong)offset, Buffer, (ulong)Count, MyFlags)); errors= 0; written= 0; for (;;) { -#if !defined (HAVE_PREAD) && !defined (_WIN32) - int error; - writtenbytes= (size_t) -1; - mysql_mutex_lock(&my_file_info[Filedes].mutex); - error= (lseek(Filedes, offset, MY_SEEK_SET) != (my_off_t) -1 && - (writtenbytes= write(Filedes, Buffer, Count)) == Count); - mysql_mutex_unlock(&my_file_info[Filedes].mutex); - if (error) - break; -#elif defined (_WIN32) - writtenbytes= my_win_pwrite(Filedes, Buffer, Count, offset); +#ifdef _WIN32 + writtenbytes= my_win_pwrite(Filedes, Buffer, Count,offset); #else writtenbytes= pwrite(Filedes, Buffer, Count, offset); #endif - if(writtenbytes == Count) + if (writtenbytes == Count) break; my_errno= errno; if (writtenbytes != (size_t) -1) - { - written+= writtenbytes; - Buffer+= writtenbytes; - Count-= writtenbytes; - offset+= writtenbytes; + { /* Safegueard */ + written+=writtenbytes; + Buffer+=writtenbytes; + Count-=writtenbytes; + offset+=writtenbytes; } DBUG_PRINT("error",("Write only %u bytes", (uint) writtenbytes)); #ifndef NO_BACKGROUND - if (my_thread_var->abort) MyFlags&= ~ MY_WAIT_IF_FULL; /* End if aborted by user */ - if ((my_errno == ENOSPC || my_errno == EDQUOT) && (MyFlags & MY_WAIT_IF_FULL)) { diff --git a/mysys/my_redel.c b/mysys/my_redel.c index 2fa5832bf0d..1b200568a47 100644 --- a/mysys/my_redel.c +++ b/mysys/my_redel.c @@ -40,7 +40,8 @@ struct utimbuf { #define REDEL_EXT ".BAK" -int my_redel(const char *org_name, const char *tmp_name, myf MyFlags) +int my_redel(const char *org_name, const char *tmp_name, + time_t backup_time_stamp, myf MyFlags) { int error=1; DBUG_ENTER("my_redel"); @@ -51,13 +52,9 @@ int my_redel(const char *org_name, const char *tmp_name, myf MyFlags) 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)) + char name_buff[FN_REFLEN + MY_BACKUP_NAME_EXTRA_LENGTH]; + my_create_backup_name(name_buff, org_name, backup_time_stamp); + if (my_rename(org_name, name_buff, MyFlags)) goto end; } else if (my_delete_allow_opened(org_name, MyFlags)) @@ -138,3 +135,23 @@ int my_copystat(const char *from, const char *to, int MyFlags) return 0; } /* my_copystat */ + + +/** + Create a backup file name. + @fn my_create_backup_name() + @param to Store new file name here + @param from Original name + + @info + The backup name is made by adding -YYMMDDHHMMSS.BAK to the file name +*/ + +void my_create_backup_name(char *to, const char *from, time_t backup_start) +{ + char ext[MY_BACKUP_NAME_EXTRA_LENGTH+1]; + ext[0]='-'; + get_date(ext+1, GETDATE_SHORT_DATE | GETDATE_HHMMSSTIME, backup_start); + strmov(strend(ext),REDEL_EXT); + strmov(strmov(to, from), ext); +} diff --git a/mysys/my_seek.c b/mysys/my_seek.c index ca12a2e95d1..efe0416c49a 100644 --- a/mysys/my_seek.c +++ b/mysys/my_seek.c @@ -55,7 +55,7 @@ my_off_t my_seek(File fd, my_off_t pos, int whence, myf MyFlags) Make sure we are using a valid file descriptor! */ DBUG_ASSERT(fd != -1); -#if defined (_WIN32) +#ifdef _WIN32 newpos= my_win_lseek(fd, pos, whence); #else newpos= lseek(fd, pos, whence); diff --git a/mysys/my_static.c b/mysys/my_static.c index ac0ad2467f7..60e23e8dfa9 100644 --- a/mysys/my_static.c +++ b/mysys/my_static.c @@ -94,11 +94,6 @@ const char *(*proc_info_hook)(void *, const char *, const char *, const char *, 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 my_disable_locking=0; my_bool my_disable_sync=0; diff --git a/mysys/my_static.h b/mysys/my_static.h index 7fde15ff133..0f1cd34a04e 100644 --- a/mysys/my_static.h +++ b/mysys/my_static.h @@ -43,8 +43,6 @@ extern uint my_once_extra; extern struct st_my_file_info my_file_info_default[MY_NFILE]; -extern ulonglong query_performance_frequency, query_performance_offset; - C_MODE_END #endif /* MYSYS_MY_STATIC_INCLUDED */ diff --git a/mysys/my_symlink.c b/mysys/my_symlink.c index 9657ea6bf58..b001354275d 100644 --- a/mysys/my_symlink.c +++ b/mysys/my_symlink.c @@ -149,9 +149,7 @@ int my_realpath(char *to, const char *filename, myf MyFlags) DBUG_RETURN(result); #else #ifdef _WIN32 - int ret= GetFullPathName(filename,FN_REFLEN,
- to,
- NULL); + int ret= GetFullPathName(filename,FN_REFLEN, to, NULL); if (ret == 0 || ret > FN_REFLEN) { if (ret > FN_REFLEN) @@ -160,7 +158,7 @@ int my_realpath(char *to, const char *filename, myf MyFlags) my_errno= EACCES; if (MyFlags & MY_WME) my_error(EE_REALPATH, MYF(0), filename, my_errno); - return -1; + return -1; } #else my_load_path(to, filename, NullS); diff --git a/mysys/my_sync.c b/mysys/my_sync.c index f39b10253dd..4d187631786 100644 --- a/mysys/my_sync.c +++ b/mysys/my_sync.c @@ -123,7 +123,6 @@ int my_sync(File fd, myf my_flags) static const char cur_dir_name[]= {FN_CURLIB, 0}; - /* Force directory information to disk. @@ -136,10 +135,10 @@ static const char cur_dir_name[]= {FN_CURLIB, 0}; 0 if ok, !=0 if error */ -#ifdef NEED_EXPLICIT_SYNC_DIR - -int my_sync_dir(const char *dir_name, myf my_flags) +int my_sync_dir(const char *dir_name __attribute__((unused)), + myf my_flags __attribute__((unused))) { +#ifdef NEED_EXPLICIT_SYNC_DIR File dir_fd; int res= 0; const char *correct_dir_name; @@ -161,19 +160,11 @@ int my_sync_dir(const char *dir_name, myf my_flags) else res= 1; DBUG_RETURN(res); -} - -#else /* NEED_EXPLICIT_SYNC_DIR */ - -int my_sync_dir(const char *dir_name __attribute__((unused)), - myf my_flags __attribute__((unused))) -{ +#else return 0; +#endif } -#endif /* NEED_EXPLICIT_SYNC_DIR */ - - /* Force directory information to disk. @@ -186,23 +177,15 @@ int my_sync_dir(const char *dir_name __attribute__((unused)), 0 if ok, !=0 if error */ -#ifdef NEED_EXPLICIT_SYNC_DIR - -int my_sync_dir_by_file(const char *file_name, myf my_flags) +int my_sync_dir_by_file(const char *file_name __attribute__((unused)), + myf my_flags __attribute__((unused))) { +#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 /* NEED_EXPLICIT_SYNC_DIR */ - -int my_sync_dir_by_file(const char *file_name __attribute__((unused)), - myf my_flags __attribute__((unused))) -{ +#else return 0; +#endif } - -#endif /* NEED_EXPLICIT_SYNC_DIR */ - diff --git a/mysys/my_thr_init.c b/mysys/my_thr_init.c index 41f9cdf99ed..121392be8c3 100644 --- a/mysys/my_thr_init.c +++ b/mysys/my_thr_init.c @@ -433,6 +433,14 @@ extern void **my_thread_var_dbug() } #endif /* DBUG_OFF */ +/* 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; +} static uint get_thread_lib(void) { diff --git a/mysys/my_uuid.c b/mysys/my_uuid.c index 0c4d34ace8b..ab1b259ae0f 100644 --- a/mysys/my_uuid.c +++ b/mysys/my_uuid.c @@ -47,9 +47,10 @@ static my_bool my_uuid_inited= 0; static struct my_rnd_struct uuid_rand; static uint nanoseq; static ulonglong uuid_time= 0; +static longlong interval_timer_offset; static uchar uuid_suffix[2+6]; /* clock_seq and node */ -mysql_mutex_t LOCK_uuid_generator; +static mysql_mutex_t LOCK_uuid_generator; /* Number of 100-nanosecond intervals between @@ -68,6 +69,8 @@ static void set_clock_seq() { uint16 clock_seq= ((uint)(my_rnd(&uuid_rand)*16383)) | UUID_VARIANT; mi_int2store(uuid_suffix, clock_seq); + interval_timer_offset= (my_hrtime().val * 10 - my_interval_timer()/100 + + UUID_TIME_OFFSET); } @@ -91,7 +94,7 @@ void my_uuid_init(ulong seed1, ulong seed2) if (my_uuid_inited) return; my_uuid_inited= 1; - now= my_getsystime(); + now= my_interval_timer()/100 + interval_timer_offset; nanoseq= 0; if (my_gethwaddr(mac)) @@ -132,7 +135,7 @@ void my_uuid(uchar *to) DBUG_ASSERT(my_uuid_inited); mysql_mutex_lock(&LOCK_uuid_generator); - tv= my_getsystime() + UUID_TIME_OFFSET + nanoseq; + tv= my_interval_timer()/100 + interval_timer_offset + nanoseq; if (likely(tv > uuid_time)) { @@ -185,7 +188,7 @@ void my_uuid(uchar *to) irrelevant in the new numberspace. */ set_clock_seq(); - tv= my_getsystime() + UUID_TIME_OFFSET; + tv= my_interval_timer()/100 + interval_timer_offset; nanoseq= 0; DBUG_PRINT("uuid",("making new numberspace")); } @@ -226,7 +229,8 @@ void my_uuid2str(const uchar *guid, char *s) { *s++= _dig_vec_lower[guid[i] >>4]; *s++= _dig_vec_lower[guid[i] & 15]; - if(i == 3 || i == 5 || i == 7 || i == 9) + /* Set '-' at intervals 3, 5, 7 and 9 */ + if ((1 << i) & ((1 << 3) | (1 << 5) | (1 << 7) | (1 << 9))) *s++= '-'; } } diff --git a/mysys/my_wincond.c b/mysys/my_wincond.c index 58c09e332d6..bed64230aea 100644 --- a/mysys/my_wincond.c +++ b/mysys/my_wincond.c @@ -31,7 +31,7 @@ */ /* Prototypes and function pointers for condition variable functions */ -typedef VOID (WINAPI * InitializeConditionVariableProc) +typedef void (WINAPI * InitializeConditionVariableProc) (PCONDITION_VARIABLE ConditionVariable); typedef BOOL (WINAPI * SleepConditionVariableCSProc) @@ -39,10 +39,10 @@ typedef BOOL (WINAPI * SleepConditionVariableCSProc) PCRITICAL_SECTION CriticalSection, DWORD dwMilliseconds); -typedef VOID (WINAPI * WakeAllConditionVariableProc) +typedef void (WINAPI * WakeAllConditionVariableProc) (PCONDITION_VARIABLE ConditionVariable); -typedef VOID (WINAPI * WakeConditionVariableProc) +typedef void (WINAPI * WakeConditionVariableProc) (PCONDITION_VARIABLE ConditionVariable); static InitializeConditionVariableProc my_InitializeConditionVariable; @@ -88,36 +88,20 @@ static void check_native_cond_availability(void) static DWORD get_milliseconds(const struct timespec *abstime) { - long long millis; - union ft64 now; + struct timespec current_time; + long long ms; if (abstime == NULL) - return INFINITE; - - 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 - */ - millis= (abstime->tv.i64 - now.i64) / 10000; - - /* Don't allow the timeout to be negative */ - if (millis < 0) - return 0; - - /* - Make sure the calculated timeout does not exceed original timeout - value which could cause "wait for ever" if system time changes - */ - if (millis > abstime->max_timeout_msec) - millis= abstime->max_timeout_msec; - - if (millis > UINT_MAX) - millis= UINT_MAX; - - return (DWORD)millis; + return INFINITE; + + set_timespec_nsec(current_time, 0); + ms= (abstime->tv_sec - current_time.tv_sec)*1000LL + + (abstime->tv_nsec - current_time.tv_nsec)/1000000LL; + if(ms < 0 ) + ms= 0; + if(ms > UINT_MAX) + ms= INFINITE; + return (DWORD)ms; } diff --git a/mysys/my_write.c b/mysys/my_write.c index 64f7546620f..46b0d749944 100644 --- a/mysys/my_write.c +++ b/mysys/my_write.c @@ -36,6 +36,11 @@ size_t my_write(File Filedes, const uchar *Buffer, size_t Count, myf MyFlags) for (;;) { #ifdef _WIN32 + if(Filedes < 0) + { + my_errno= errno= EBADF; + DBUG_RETURN((size_t)-1); + } writtenbytes= my_win_write(Filedes, Buffer, Count); #else writtenbytes= write(Filedes, Buffer, Count); diff --git a/mysys/string.c b/mysys/string.c index 0aa175e8991..fa669ceda3a 100644 --- a/mysys/string.c +++ b/mysys/string.c @@ -181,3 +181,15 @@ void dynstr_free(DYNAMIC_STRING *str) my_free(str->str); str->str= NULL; } + + +/* Give over the control of the dynamic string to caller */ + +void dynstr_reassociate(DYNAMIC_STRING *str, char **ptr, size_t *length, + size_t *alloc_length) +{ + *ptr= str->str; + *length= str->length; + *alloc_length= str->max_length; + str->str=0; +} diff --git a/mysys/thr_alarm.c b/mysys/thr_alarm.c index 725b32fa4a6..f902057a71b 100644 --- a/mysys/thr_alarm.c +++ b/mysys/thr_alarm.c @@ -37,7 +37,7 @@ uint thr_client_alarm; static int alarm_aborted=1; /* No alarm thread */ -my_bool thr_alarm_inited= 0; +my_bool thr_alarm_inited= 0, my_disable_thr_alarm= 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); @@ -173,6 +173,21 @@ my_bool thr_alarm(thr_alarm_t *alrm, uint sec, ALARM *alarm_data) DBUG_ENTER("thr_alarm"); DBUG_PRINT("enter",("thread: %s sec: %d",my_thread_name(),sec)); + if (my_disable_thr_alarm) + { + (*alrm)= &alarm_data->alarmed; + alarm_data->alarmed= 1; /* Abort if interrupted */ + DBUG_RETURN(0); + } + + if (unlikely(alarm_aborted)) + { /* No signal thread */ + DBUG_PRINT("info", ("alarm aborted")); + if (alarm_aborted > 0) + goto abort_no_unlock; + sec= 1; /* Abort mode */ + } + now= my_time(0); if (!alarm_data) { @@ -190,13 +205,6 @@ my_bool thr_alarm(thr_alarm_t *alrm, uint sec, ALARM *alarm_data) one_signal_hand_sigmask(SIG_BLOCK,&full_signal_set,&old_mask); mysql_mutex_lock(&LOCK_alarm); /* Lock from threads & alarms */ - if (unlikely(alarm_aborted)) - { /* No signal thread */ - DBUG_PRINT("info", ("alarm aborted")); - if (alarm_aborted > 0) - goto abort; - sec= 1; /* Abort mode */ - } if (alarm_queue.elements >= max_used_alarms) { if (alarm_queue.elements == alarm_queue.max_elements) @@ -251,6 +259,8 @@ void thr_end_alarm(thr_alarm_t *alarmed) #endif DBUG_ENTER("thr_end_alarm"); + if (my_disable_thr_alarm) + DBUG_VOID_RETURN; one_signal_hand_sigmask(SIG_BLOCK,&full_signal_set,&old_mask); alarm_data= (ALARM*) ((uchar*) *alarmed - offsetof(ALARM,alarmed)); mysql_mutex_lock(&LOCK_alarm); diff --git a/mysys/thr_lock.c b/mysys/thr_lock.c index bc992a3e35b..b2e51cde260 100644 --- a/mysys/thr_lock.c +++ b/mysys/thr_lock.c @@ -116,6 +116,31 @@ static inline mysql_cond_t *get_cond(void) return &my_thread_var->suspend; } + +/* + Sort locks in priority order + + LOCK_CMP() + A First lock + B Second lock + + Return: + 0 if A >= B + 1 if A < B + + Priority for locks (decides in which order locks are locked) + We want all write locks to be first, followed by read locks. + Locks from MERGE tables has a little lower priority than other + locks, to allow one to release merge tables without having + to unlock and re-lock other locks. + The lower the number, the higher the priority for the lock. + For MERGE tables we add 2 (THR_LOCK_MERGE_PRIV) to the lock priority. + THR_LOCK_LATE_PRIV (1) is used when one locks other tables to be merged + with existing locks. This way we prioritize the original locks over the + new locks. +*/ + + static inline int LOCK_CMP(THR_LOCK_DATA *a, THR_LOCK_DATA *b) { if (a->lock != b->lock) @@ -154,15 +179,13 @@ static int check_lock(struct st_lock_list *list, const char* lock_type, { THR_LOCK_DATA *data,**prev; uint count=0; - THR_LOCK_INFO *UNINIT_VAR(first_owner); prev= &list->data; if (list->data) { - enum thr_lock_type last_lock_type=list->data->type; + enum thr_lock_type last_lock_type= list->data->type; + THR_LOCK_INFO *first_owner= list->data->owner; - 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) @@ -180,8 +203,10 @@ static int check_lock(struct st_lock_list *list, const char* lock_type, last_lock_type != TL_WRITE_CONCURRENT_INSERT) { fprintf(stderr, - "Warning: Found locks from different threads in %s: %s\n", - lock_type,where); + "Warning: Found locks from different threads for lock '%s' in '%s' at '%s'. org_lock_type: %d last_lock_type: %d new_lock_type: %d\n", + data->lock->name ? data->lock->name : "", + lock_type, where, list->data->type, last_lock_type, + data->type); return 1; } if (no_cond && data->cond) @@ -211,6 +236,7 @@ static int check_lock(struct st_lock_list *list, const char* lock_type, static void check_locks(THR_LOCK *lock, const char *where, + enum thr_lock_type type, my_bool allow_no_locks) { uint old_found_errors=found_errors; @@ -226,14 +252,16 @@ static void check_locks(THR_LOCK *lock, const char *where, if (found_errors < MAX_FOUND_ERRORS) { - uint count=0; + uint count=0, count2= 0; THR_LOCK_DATA *data; for (data=lock->read.data ; data ; data=data->next) { + count2++; if (data->type == TL_READ_NO_INSERT) count++; /* Protect against infinite loop. */ - DBUG_ASSERT(count <= lock->read_no_write_count); + DBUG_ASSERT(count <= lock->read_no_write_count && + count2 <= MAX_LOCKS); } if (count != lock->read_no_write_count) { @@ -274,6 +302,7 @@ static void check_locks(THR_LOCK *lock, const char *where, found_errors++; fprintf(stderr, "Warning at '%s': Write lock %d waiting while no exclusive read locks\n",where,(int) lock->write_wait.data->type); + DBUG_PRINT("warning", ("Warning at '%s': Write lock %d waiting while no exclusive read locks\n",where,(int) lock->write_wait.data->type)); } } } @@ -283,13 +312,18 @@ static void check_locks(THR_LOCK *lock, const char *where, if (lock->write.data->type == TL_WRITE_CONCURRENT_INSERT) { THR_LOCK_DATA *data; - for (data=lock->write.data->next ; data ; data=data->next) + uint count= 0; + for (data=lock->write.data->next; + data && count < MAX_LOCKS; + 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); + "Warning at '%s': Found TL_WRITE_CONCURRENT_INSERT lock mixed with other write lock: %d\n", + where, data->type); + DBUG_PRINT("warning", ("Warning at '%s': Found TL_WRITE_CONCURRENT_INSERT lock mixed with other write lock: %d\n", + where, data->type)); break; } } @@ -304,26 +338,34 @@ static void check_locks(THR_LOCK *lock, const char *where, fprintf(stderr, "Warning at '%s': Found WRITE_ALLOW_WRITE lock waiting for WRITE_ALLOW_WRITE lock\n", where); + DBUG_PRINT("warning", ("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) && + THR_LOCK_DATA *data; + for (data=lock->read.data ; data ; data=data->next) + { + if (!thr_lock_owner_equal(lock->write.data->owner, + 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)); - - } + data->type == TL_READ_NO_INSERT))) + { + found_errors++; + fprintf(stderr, + "Warning at '%s' for lock: %d: Found lock of type %d that is write and read locked. Read_no_write_count: %d\n", + where, (int) type, lock->write.data->type, + lock->read_no_write_count); + DBUG_PRINT("warning",("At '%s' for lock %d: Found lock of type %d that is write and read locked\n", + where, (int) type, + lock->write.data->type)); + } + } } if (lock->read_wait.data) { @@ -349,7 +391,7 @@ static void check_locks(THR_LOCK *lock, const char *where, } #else /* EXTRA_DEBUG */ -#define check_locks(A,B,C) +#define check_locks(A,B,C,D) #endif @@ -401,6 +443,7 @@ void thr_lock_data_init(THR_LOCK *lock,THR_LOCK_DATA *data, void *param) data->status_param=param; data->cond=0; data->priority= 0; + data->debug_print_param= 0; } @@ -478,7 +521,7 @@ wait_for_lock(struct st_lock_list *wait, THR_LOCK_DATA *data, data->cond= cond; old_proc_info= proc_info_hook(NULL, "Waiting for table level lock", - __func__, __FILE__, __LINE__); + __func__, __FILE__, __LINE__); /* Since before_lock_wait potentially can create more threads to @@ -544,13 +587,14 @@ wait_for_lock(struct st_lock_list *wait, THR_LOCK_DATA *data, else wait->last=data->prev; data->type= TL_UNLOCK; /* No lock */ - check_locks(data->lock, "killed or timed out wait_for_lock", 1); + check_locks(data->lock, "killed or timed out wait_for_lock", data->type, + 1); wake_up_waiters(data->lock); } else { DBUG_PRINT("thr_lock", ("lock aborted")); - check_locks(data->lock, "aborted wait_for_lock", 0); + check_locks(data->lock, "aborted wait_for_lock", data->type, 0); } } else @@ -559,7 +603,7 @@ wait_for_lock(struct st_lock_list *wait, THR_LOCK_DATA *data, 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); + check_locks(data->lock,"got wait_for_lock", data->type, 0); } mysql_mutex_unlock(&data->lock->mutex); @@ -594,7 +638,7 @@ thr_lock(THR_LOCK_DATA *data, THR_LOCK_INFO *owner, (long) data, data->owner->thread_id, (long) lock, (int) lock_type)); check_locks(lock,(uint) lock_type <= (uint) TL_READ_NO_INSERT ? - "enter read_lock" : "enter write_lock",0); + "enter read_lock" : "enter write_lock", lock_type, 0); if ((int) lock_type <= (int) TL_READ_NO_INSERT) { /* Request for READ lock */ @@ -639,7 +683,7 @@ thr_lock(THR_LOCK_DATA *data, THR_LOCK_INFO *owner, 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); + check_locks(lock,"read lock with old write lock", lock_type, 0); if (lock->get_status) (*lock->get_status)(data->status_param, 0); statistic_increment(locks_immediate,&THR_LOCK_lock); @@ -663,7 +707,7 @@ thr_lock(THR_LOCK_DATA *data, THR_LOCK_INFO *owner, 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); + check_locks(lock,"read lock with no write locks", lock_type, 0); if (lock->get_status) (*lock->get_status)(data->status_param, 0); statistic_increment(locks_immediate,&THR_LOCK_lock); @@ -769,7 +813,7 @@ thr_lock(THR_LOCK_DATA *data, THR_LOCK_INFO *owner, (*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); + check_locks(lock,"second write lock", lock_type, 0); if (lock->get_status) (*lock->get_status)(data->status_param, lock_type == TL_WRITE_CONCURRENT_INSERT); @@ -807,7 +851,7 @@ thr_lock(THR_LOCK_DATA *data, THR_LOCK_INFO *owner, lock->write.last= &data->next; if (lock->get_status) (*lock->get_status)(data->status_param, concurrent_insert); - check_locks(lock,"only write lock",0); + check_locks(lock,"only write lock", lock_type, 0); statistic_increment(locks_immediate,&THR_LOCK_lock); goto end; } @@ -830,7 +874,7 @@ static inline void free_all_read_locks(THR_LOCK *lock, { THR_LOCK_DATA *data=lock->read_wait.data; - check_locks(lock,"before freeing read locks",1); + check_locks(lock,"before freeing read locks", TL_UNLOCK, 1); /* move all locks from read_wait list to read list */ (*lock->read.last)=data; @@ -872,7 +916,7 @@ static inline void free_all_read_locks(THR_LOCK *lock, *lock->read_wait.last=0; if (!lock->read_wait.data) lock->write_lock_count=0; - check_locks(lock,"after giving read locks",0); + check_locks(lock,"after giving read locks", TL_UNLOCK, 0); } /* Unlock lock and free next thread on same lock */ @@ -885,7 +929,7 @@ void thr_unlock(THR_LOCK_DATA *data, uint unlock_flags) DBUG_PRINT("lock",("data: 0x%lx thread: 0x%lx lock: 0x%lx", (long) data, data->owner->thread_id, (long) lock)); mysql_mutex_lock(&lock->mutex); - check_locks(lock,"start of release lock",0); + check_locks(lock,"start of release lock", lock_type, 0); if (((*data->prev)=data->next)) /* remove from lock-list */ data->next->prev= data->prev; @@ -919,8 +963,9 @@ void thr_unlock(THR_LOCK_DATA *data, uint unlock_flags) 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); + check_locks(lock,"after releasing lock", lock_type, 1); wake_up_waiters(lock); + check_locks(lock,"end of thr_unlock", lock_type, 1); mysql_mutex_unlock(&lock->mutex); DBUG_VOID_RETURN; } @@ -1045,7 +1090,7 @@ static void wake_up_waiters(THR_LOCK *lock) free_all_read_locks(lock,0); } end: - check_locks(lock, "after waking up waiters", 0); + check_locks(lock, "after waking up waiters", TL_UNLOCK, 0); DBUG_VOID_RETURN; } @@ -1350,7 +1395,7 @@ void thr_downgrade_write_lock(THR_LOCK_DATA *in_data, 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); + check_locks(lock,"after downgrading lock", old_lock_type, 0); mysql_mutex_unlock(&lock->mutex); DBUG_VOID_RETURN; @@ -1372,7 +1417,7 @@ my_bool thr_upgrade_write_delay_lock(THR_LOCK_DATA *data, mysql_mutex_unlock(&lock->mutex); DBUG_RETURN(data->type == TL_UNLOCK); /* Test if Aborted */ } - check_locks(lock,"before upgrading lock",0); + check_locks(lock,"before upgrading lock", data->type, 0); /* TODO: Upgrade to TL_WRITE_CONCURRENT_INSERT in some cases */ data->type= new_lock_type; /* Upgrade lock */ @@ -1400,11 +1445,11 @@ my_bool thr_upgrade_write_delay_lock(THR_LOCK_DATA *data, lock->write_wait.last= &data->next; data->prev= &lock->write_wait.data; lock->write_wait.data=data; - check_locks(lock,"upgrading lock",0); + check_locks(lock,"upgrading lock", new_lock_type, 0); } else { - check_locks(lock,"waiting for lock",0); + check_locks(lock,"waiting for lock", new_lock_type, 0); } res= wait_for_lock(&lock->write_wait, data, 1, lock_wait_timeout); if (res == THR_LOCK_SUCCESS && lock->start_trans) diff --git a/mysys/thr_mutex.c b/mysys/thr_mutex.c index fdc25b67049..86a7ce9684b 100644 --- a/mysys/thr_mutex.c +++ b/mysys/thr_mutex.c @@ -171,16 +171,16 @@ static int safe_mutex_lazy_init_deadlock_detection(safe_mutex_t *mp) 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); + my_hash_init2(mp->locked_mutex, 64, &my_charset_bin, + 128, + offsetof(safe_mutex_deadlock_t, id), + sizeof(mp->id), + 0, 0, HASH_UNIQUE); + my_hash_init2(mp->used_mutex, 64, &my_charset_bin, + 128, + offsetof(safe_mutex_t, id), + sizeof(mp->id), + 0, 0, HASH_UNIQUE); return 0; } @@ -711,12 +711,6 @@ void safe_mutex_end(FILE *file __attribute__((unused))) #endif /* SAFE_MUTEX_DETECT_DESTROY */ } -safe_mutex_t **my_thread_var_mutex_in_use() -{ - struct st_my_thread_var *tmp= my_thread_var; - return tmp ? &tmp->mutex_in_use : 0; -} - static my_bool add_used_to_locked_mutex(safe_mutex_t *used_mutex, safe_mutex_deadlock_t *locked_mutex) { diff --git a/mysys/thr_rwlock.c b/mysys/thr_rwlock.c index bad80b43eef..a6eb16a4b1b 100644 --- a/mysys/thr_rwlock.c +++ b/mysys/thr_rwlock.c @@ -142,7 +142,7 @@ static int srw_unlock(my_rw_lock_t *rwp) * 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 diff --git a/mysys/tree.c b/mysys/tree.c index c922c8f505a..03c4ac5b36b 100644 --- a/mysys/tree.c +++ b/mysys/tree.c @@ -221,7 +221,10 @@ TREE_ELEMENT *tree_insert(TREE *tree, void *key, uint key_size, } if (element == &tree->null_element) { - uint alloc_size=sizeof(TREE_ELEMENT)+key_size+tree->size_of_element; + uint alloc_size; + if (tree->flag & TREE_ONLY_DUPS) + return((TREE_ELEMENT *) 1); + alloc_size=sizeof(TREE_ELEMENT)+key_size+tree->size_of_element; tree->allocated+=alloc_size; if (tree->memory_limit && tree->elements_in_tree @@ -375,6 +378,7 @@ void *tree_search_key(TREE *tree, const void *key, case HA_READ_KEY_EXACT: case HA_READ_KEY_OR_NEXT: case HA_READ_BEFORE_KEY: + case HA_READ_KEY_OR_PREV: last_equal_element= parents; cmp= 1; break; @@ -418,6 +422,9 @@ void *tree_search_key(TREE *tree, const void *key, case HA_READ_BEFORE_KEY: *last_pos= last_right_step_parent; break; + case HA_READ_KEY_OR_PREV: + *last_pos= last_equal_element ? last_equal_element : last_right_step_parent; + break; default: return NULL; } diff --git a/mysys/waiting_threads.c b/mysys/waiting_threads.c index f8980185ae0..ddc06a3ae5e 100644 --- a/mysys/waiting_threads.c +++ b/mysys/waiting_threads.c @@ -481,9 +481,9 @@ void wt_end() (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 ts a pointer to deadlock timeout short value (microseconds) @param dl a pointer to deadlock search depth long value - @param tl a pointer to deadlock timeout long value + @param tl a pointer to deadlock timeout long value (microseconds) @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 @@ -1039,8 +1039,9 @@ int wt_thd_cond_timedwait(WT_THD *thd, mysql_mutex_t *mutex) { int ret= WT_TIMEOUT; struct timespec timeout; - ulonglong before, after, starttime; + my_hrtime_t before, after, starttime; WT_RESOURCE *rc= thd->waiting_for; + ulonglong end_wait_time; DBUG_ENTER("wt_thd_cond_timedwait"); DBUG_PRINT("wt", ("enter: thd=%s, rc=%p", thd->name, rc)); @@ -1052,29 +1053,15 @@ int wt_thd_cond_timedwait(WT_THD *thd, mysql_mutex_t *mutex) mysql_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 + before= starttime= my_hrtime(); rc_wrlock(rc); if (rc->owners.elements == 0) ret= WT_OK; rc_unlock(rc); - set_timespec_time_nsec(timeout, starttime, (*thd->timeout_short)*ULL(1000)); + end_wait_time= starttime.val *1000 + (*thd->timeout_short)*ULL(1000000); + set_timespec_time_nsec(timeout, end_wait_time); if (ret == WT_TIMEOUT && !thd->killed) ret= mysql_cond_timedwait(&rc->cond, mutex, &timeout); if (ret == WT_TIMEOUT && !thd->killed) @@ -1086,15 +1073,16 @@ int wt_thd_cond_timedwait(WT_THD *thd, mysql_mutex_t *mutex) ret= WT_DEADLOCK; else if (*thd->timeout_long > *thd->timeout_short) { - set_timespec_time_nsec(timeout, starttime, (*thd->timeout_long)*ULL(1000)); + end_wait_time= starttime.val *1000 + (*thd->timeout_long)*ULL(1000000); + set_timespec_time_nsec(timeout, end_wait_time); if (!thd->killed) ret= mysql_cond_timedwait(&rc->cond, mutex, &timeout); } } - after= my_getsystime(); + after= my_hrtime(); if (stop_waiting(thd) == WT_DEADLOCK) /* if we're killed */ ret= WT_DEADLOCK; - increment_wait_stats(after-before, ret); + increment_wait_stats(after.val-before.val, ret); if (ret == WT_OK) increment_success_stats(); DBUG_RETURN(ret); |