From e2f9201041551188a7b7fa26bc2daafe54269d44 Mon Sep 17 00:00:00 2001 From: "marko@hundin.mysql.fi" <> Date: Fri, 17 Dec 2004 18:35:11 +0200 Subject: [PATCH 01/17] InnoDB: Fixed bugs in the padding and trimming of trailing spaces that affected the UCS2 character set. (Bug #7350) --- innobase/data/data0type.c | 11 ++++++ innobase/include/data0type.h | 7 ++++ innobase/include/row0mysql.h | 2 ++ innobase/include/row0mysql.ic | 31 +++++++++++++--- innobase/rem/rem0cmp.c | 16 --------- innobase/row/row0sel.c | 68 +++++++++++++++++++++++++++++++---- sql/ha_innodb.cc | 2 ++ 7 files changed, 110 insertions(+), 27 deletions(-) diff --git a/innobase/data/data0type.c b/innobase/data/data0type.c index 714cf92bc65..dab14df4240 100644 --- a/innobase/data/data0type.c +++ b/innobase/data/data0type.c @@ -165,6 +165,17 @@ dtype_is_non_binary_string_type( return(FALSE); } +/************************************************************************* +Gets the MySQL charset-collation code for MySQL string types. */ + +ulint +dtype_get_charset_coll_noninline( +/*=============================*/ + ulint prtype) /* in: precise data type */ +{ + return(dtype_get_charset_coll(prtype)); +} + /************************************************************************* Forms a precise type from the < 4.1.2 format precise type plus the charset-collation code. */ diff --git a/innobase/include/data0type.h b/innobase/include/data0type.h index c263d2bf613..02c874836fd 100644 --- a/innobase/include/data0type.h +++ b/innobase/include/data0type.h @@ -234,6 +234,13 @@ dtype_get_prtype( dtype_t* type); /************************************************************************* Gets the MySQL charset-collation code for MySQL string types. */ + +ulint +dtype_get_charset_coll_noninline( +/*=============================*/ + ulint prtype);/* in: precise data type */ +/************************************************************************* +Gets the MySQL charset-collation code for MySQL string types. */ UNIV_INLINE ulint dtype_get_charset_coll( diff --git a/innobase/include/row0mysql.h b/innobase/include/row0mysql.h index f47ce74ce37..062dae4e60c 100644 --- a/innobase/include/row0mysql.h +++ b/innobase/include/row0mysql.h @@ -454,6 +454,8 @@ struct mysql_row_templ_struct { zero if column cannot be NULL */ ulint type; /* column type in Innobase mtype numbers DATA_CHAR... */ + ulint charset; /* MySQL charset-collation code + of the column, or zero */ ulint is_unsigned; /* if a column type is an integer type and this field is != 0, then it is an unsigned integer type */ diff --git a/innobase/include/row0mysql.ic b/innobase/include/row0mysql.ic index 4ecd66e06ec..fc922b52d0a 100644 --- a/innobase/include/row0mysql.ic +++ b/innobase/include/row0mysql.ic @@ -91,12 +91,33 @@ row_mysql_store_col_in_innobase_format( } } else if (type == DATA_VARCHAR || type == DATA_VARMYSQL || type == DATA_BINARY) { + /* Remove trailing spaces. */ + + /* Handle UCS2 strings differently. As no new + collations will be introduced in 4.1, we hardcode the + charset-collation codes here. In 5.0, the logic will + be based on mbminlen. */ + ulint cset = dtype_get_charset_coll( + dtype_get_prtype(dfield_get_type(dfield))); ptr = row_mysql_read_var_ref(&col_len, mysql_data); - - /* Remove trailing spaces */ - while (col_len > 0 && ptr[col_len - 1] == ' ') { - col_len--; - } + if (cset == 35/*ucs2_general_ci*/ + || cset == 90/*ucs2_bin*/ + || (cset >= 128/*ucs2_unicode_ci*/ + && cset <= 144/*ucs2_persian_ci*/)) { + /* space=0x0020 */ + /* Trim "half-chars", just in case. */ + col_len &= ~1; + + while (col_len >= 2 && ptr[col_len - 2] == 0x00 + && ptr[col_len - 1] == 0x20) { + col_len -= 2; + } + } else { + /* space=0x20 */ + while (col_len > 0 && ptr[col_len - 1] == 0x20) { + col_len--; + } + } } else if (type == DATA_BLOB) { ptr = row_mysql_read_blob_ref(&col_len, mysql_data, col_len); } diff --git a/innobase/rem/rem0cmp.c b/innobase/rem/rem0cmp.c index 041fb7914e2..cf549284acc 100644 --- a/innobase/rem/rem0cmp.c +++ b/innobase/rem/rem0cmp.c @@ -261,22 +261,6 @@ cmp_whole_field( "InnoDB: comparison!\n"); } - /* MySQL does not pad the ends of strings with spaces in a - comparison. That would cause a foreign key check to fail for - non-latin1 character sets if we have different length columns. - To prevent that we remove trailing spaces here before doing - the comparison. NOTE that if we in the future map more MySQL - types to DATA_MYSQL or DATA_VARMYSQL, we have to change this - code. */ - - while (a_length > 0 && a[a_length - 1] == ' ') { - a_length--; - } - - while (b_length > 0 && b[b_length - 1] == ' ') { - b_length--; - } - return(innobase_mysql_cmp( (int)(type->prtype & DATA_MYSQL_TYPE_MASK), (uint)dtype_get_charset_coll(type->prtype), diff --git a/innobase/row/row0sel.c b/innobase/row/row0sel.c index ce76f48e7a7..61ba0b53172 100644 --- a/innobase/row/row0sel.c +++ b/innobase/row/row0sel.c @@ -2204,9 +2204,6 @@ row_sel_field_store_in_mysql_format( dest = row_mysql_store_var_len(dest, len); ut_memcpy(dest, data, len); - /* Pad with trailing spaces */ - memset(dest + len, ' ', col_len - len); - /* ut_ad(col_len >= len + 2); No real var implemented in MySQL yet! */ @@ -2334,7 +2331,45 @@ row_sel_store_mysql_rec( mysql_rec + templ->mysql_col_offset, templ->mysql_col_len, data, len, templ->type, templ->is_unsigned); - + + if (templ->type == DATA_VARCHAR + || templ->type == DATA_VARMYSQL + || templ->type == DATA_BINARY) { + /* Pad with trailing spaces */ + data = mysql_rec + templ->mysql_col_offset; + + /* Handle UCS2 strings differently. As no new + collations will be introduced in 4.1, we + hardcode the charset-collation codes here. + 5.0 will use a different approach. */ + if (templ->charset == 35 + || templ->charset == 90 + || (templ->charset >= 128 + && templ->charset <= 144)) { + /* space=0x0020 */ + ulint col_len = templ->mysql_col_len; + + ut_a(!(col_len & 1)); + if (len & 1) { + /* A 0x20 has been stripped + from the column. + Pad it back. */ + goto pad_0x20; + } + /* Pad the rest of the string + with 0x0020 */ + while (len < col_len) { + data[len++] = 0x00; + pad_0x20: + data[len++] = 0x20; + } + } else { + /* space=0x20 */ + memset(data + len, 0x20, + templ->mysql_col_len - len); + } + } + /* Cleanup */ if (extern_field_heap) { mem_heap_free(extern_field_heap); @@ -2368,8 +2403,29 @@ row_sel_store_mysql_rec( pad_char = '\0'; } - memset(mysql_rec + templ->mysql_col_offset, pad_char, - templ->mysql_col_len); + /* Handle UCS2 strings differently. As no new + collations will be introduced in 4.1, + we hardcode the charset-collation codes here. + 5.0 will use a different approach. */ + if (templ->charset == 35 + || templ->charset == 90 + || (templ->charset >= 128 + && templ->charset <= 144)) { + /* There are two bytes per char, so the length + has to be an even number. */ + ut_a(!(templ->mysql_col_len & 1)); + data = mysql_rec + templ->mysql_col_offset; + len = templ->mysql_col_len; + /* Pad with 0x0020. */ + while (len >= 2) { + *data++ = 0x00; + *data++ = 0x20; + len -= 2; + } + } else { + memset(mysql_rec + templ->mysql_col_offset, + pad_char, templ->mysql_col_len); + } } } diff --git a/sql/ha_innodb.cc b/sql/ha_innodb.cc index cc69762cbdb..8110df1063f 100644 --- a/sql/ha_innodb.cc +++ b/sql/ha_innodb.cc @@ -2248,6 +2248,8 @@ build_template( templ->mysql_col_len = (ulint) field->pack_length(); templ->type = get_innobase_type_from_mysql_type(field); + templ->charset = dtype_get_charset_coll_noninline( + index->table->cols[i].type.prtype); templ->is_unsigned = (ulint) (field->flags & UNSIGNED_FLAG); if (templ->type == DATA_BLOB) { From 2f321150924de852439a26d2b26e544f4a5949b7 Mon Sep 17 00:00:00 2001 From: "dlenev@brandersnatch.localdomain" <> Date: Tue, 11 Jan 2005 14:26:40 +0300 Subject: [PATCH 02/17] Fix for bug #7418 "TIMESTAMP not always converted to DATETIME in MAXDB mode". Changed grammar rule for "type" token. Now we have one branch with optional length specification for TIMESTAMP type instead of two separate branches. --- mysql-test/r/type_timestamp.result | 10 ++++++++++ mysql-test/t/type_timestamp.test | 12 ++++++++++++ sql/sql_yacc.yy | 9 +-------- 3 files changed, 23 insertions(+), 8 deletions(-) diff --git a/mysql-test/r/type_timestamp.result b/mysql-test/r/type_timestamp.result index 42fdc7e50c6..6c46d308e7e 100644 --- a/mysql-test/r/type_timestamp.result +++ b/mysql-test/r/type_timestamp.result @@ -422,3 +422,13 @@ max(t) 2004-01-01 01:00:00 2004-02-01 00:00:00 drop table t1; +set sql_mode='maxdb'; +create table t1 (a timestamp, b timestamp(19)); +show create table t1; +Table Create Table +t1 CREATE TABLE "t1" ( + "a" datetime default NULL, + "b" datetime default NULL +) +set sql_mode=''; +drop table t1; diff --git a/mysql-test/t/type_timestamp.test b/mysql-test/t/type_timestamp.test index a8a0cf8703c..783e310f02d 100644 --- a/mysql-test/t/type_timestamp.test +++ b/mysql-test/t/type_timestamp.test @@ -286,3 +286,15 @@ insert into t1 values ('a', '2004-01-01 00:00:00'), ('a', '2004-01-01 01:00:00') ('b', '2004-02-01 00:00:00'); select max(t) from t1 group by a; drop table t1; + +# +# Test for bug #7418 "TIMESTAMP not always converted to DATETIME in MAXDB +# mode". TIMESTAMP columns should be converted DATETIME columns in MAXDB +# mode regardless of whether a display width is given. +# +set sql_mode='maxdb'; +create table t1 (a timestamp, b timestamp(19)); +show create table t1; +# restore default mode +set sql_mode=''; +drop table t1; diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index a09694ee1e6..66f7882c4e7 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -1415,7 +1415,7 @@ type: | YEAR_SYM opt_len field_options { $$=FIELD_TYPE_YEAR; } | DATE_SYM { $$=FIELD_TYPE_DATE; } | TIME_SYM { $$=FIELD_TYPE_TIME; } - | TIMESTAMP + | TIMESTAMP opt_len { if (YYTHD->variables.sql_mode & MODE_MAXDB) $$=FIELD_TYPE_DATETIME; @@ -1428,13 +1428,6 @@ type: $$=FIELD_TYPE_TIMESTAMP; } } - | TIMESTAMP '(' NUM ')' - { - LEX *lex= Lex; - lex->length= $3.str; - lex->type|= NOT_NULL_FLAG; - $$= FIELD_TYPE_TIMESTAMP; - } | DATETIME { $$=FIELD_TYPE_DATETIME; } | TINYBLOB { Lex->charset=&my_charset_bin; $$=FIELD_TYPE_TINY_BLOB; } From 2677a1c46b3656b9dc33acf450981dd22949b09b Mon Sep 17 00:00:00 2001 From: "monty@mysql.com" <> Date: Tue, 11 Jan 2005 18:55:53 +0200 Subject: [PATCH 03/17] Review of new pushed code Indentation cleanup --- client/mysqladmin.cc | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/client/mysqladmin.cc b/client/mysqladmin.cc index 24cd461b1fa..d390a152fc7 100644 --- a/client/mysqladmin.cc +++ b/client/mysqladmin.cc @@ -832,7 +832,8 @@ static int execute_commands(MYSQL *mysql,int argc, char **argv) if (argv[1][0]) { char *pw= argv[1]; - bool old= find_type(argv[0], &command_typelib, 2) == ADMIN_OLD_PASSWORD; + bool old= (find_type(argv[0], &command_typelib, 2) == + ADMIN_OLD_PASSWORD); #ifdef __WIN__ uint pw_len= strlen(pw); if (pw_len > 1 && pw[0] == '\'' && pw[pw_len-1] == '\'') @@ -843,21 +844,29 @@ static int execute_commands(MYSQL *mysql,int argc, char **argv) If we don't already know to use an old-style password, see what the server is using */ - if (!old) { - if (mysql_query(mysql, "SHOW VARIABLES LIKE 'old_passwords'")) { + if (!old) + { + if (mysql_query(mysql, "SHOW VARIABLES LIKE 'old_passwords'")) + { my_printf_error(0, "Could not determine old_passwords setting from server; error: '%s'", MYF(ME_BELL),mysql_error(mysql)); return -1; - } else { + } + else + { MYSQL_RES *res= mysql_store_result(mysql); - if (!res) { - my_printf_error(0, "Could not get old_passwords setting from server; error: '%s'", + if (!res) + { + my_printf_error(0, + "Could not get old_passwords setting from " + "server; error: '%s'", MYF(ME_BELL),mysql_error(mysql)); return -1; } - if (!mysql_num_rows(res)) { + if (!mysql_num_rows(res)) old= 1; - } else { + else + { MYSQL_ROW row= mysql_fetch_row(res); old= !strncmp(row[1], "ON", 2); } From 23c9e0a75a269b880b477ef5b56d22b66c0ade8b Mon Sep 17 00:00:00 2001 From: "dlenev@brandersnatch.localdomain" <> Date: Wed, 12 Jan 2005 12:18:36 +0300 Subject: [PATCH 04/17] Fix for bug #7586 "TIMEDIFF for sec+microsec not working properly". --- mysql-test/r/func_sapdb.result | 7 +++++-- mysql-test/t/func_sapdb.test | 1 + sql/item_timefunc.cc | 30 ++++++++++-------------------- 3 files changed, 16 insertions(+), 22 deletions(-) diff --git a/mysql-test/r/func_sapdb.result b/mysql-test/r/func_sapdb.result index cf2bd687115..c74d6fc5a4e 100644 --- a/mysql-test/r/func_sapdb.result +++ b/mysql-test/r/func_sapdb.result @@ -97,13 +97,16 @@ timediff("1997-12-31 23:59:59.000001","1997-12-30 01:01:01.000002") 46:58:57.999999 select timediff("1997-12-30 23:59:59.000001","1997-12-31 23:59:59.000002"); timediff("1997-12-30 23:59:59.000001","1997-12-31 23:59:59.000002") --23:59:59.999999 +-24:00:00.000001 select timediff("1997-12-31 23:59:59.000001","23:59:59.000001"); timediff("1997-12-31 23:59:59.000001","23:59:59.000001") NULL select timediff("2000:01:01 00:00:00", "2000:01:01 00:00:00.000001"); timediff("2000:01:01 00:00:00", "2000:01:01 00:00:00.000001") -00:00:00.000001 +select timediff("2005-01-11 15:48:49.999999", "2005-01-11 15:48:50"); +timediff("2005-01-11 15:48:49.999999", "2005-01-11 15:48:50") +-00:00:00.000001 select maketime(10,11,12); maketime(10,11,12) 10:11:12 @@ -175,7 +178,7 @@ f8 date YES NULL f9 time YES NULL select * from t1; f1 f2 f3 f4 f5 f6 f7 f8 f9 -1997-01-01 1998-01-02 01:01:00 49:01:01 46:58:57 -23:59:59 10:11:12 2001-12-01 01:01:01 1997-12-31 23:59:59 +1997-01-01 1998-01-02 01:01:00 49:01:01 46:58:57 -24:00:00 10:11:12 2001-12-01 01:01:01 1997-12-31 23:59:59 create table test(t1 datetime, t2 time, t3 time, t4 datetime); insert into test values ('2001-01-01 01:01:01', '01:01:01', null, '2001-02-01 01:01:01'), diff --git a/mysql-test/t/func_sapdb.test b/mysql-test/t/func_sapdb.test index 2ae3c438243..de433485fca 100644 --- a/mysql-test/t/func_sapdb.test +++ b/mysql-test/t/func_sapdb.test @@ -54,6 +54,7 @@ select timediff("1997-12-31 23:59:59.000001","1997-12-30 01:01:01.000002"); select timediff("1997-12-30 23:59:59.000001","1997-12-31 23:59:59.000002"); select timediff("1997-12-31 23:59:59.000001","23:59:59.000001"); select timediff("2000:01:01 00:00:00", "2000:01:01 00:00:00.000001"); +select timediff("2005-01-11 15:48:49.999999", "2005-01-11 15:48:50"); --enable_ps_protocol select maketime(10,11,12); diff --git a/sql/item_timefunc.cc b/sql/item_timefunc.cc index 0d652a431cb..f9c9d1f013d 100644 --- a/sql/item_timefunc.cc +++ b/sql/item_timefunc.cc @@ -2432,8 +2432,7 @@ void Item_func_add_time::print(String *str) String *Item_func_timediff::val_str(String *str) { DBUG_ASSERT(fixed == 1); - longlong seconds; - long microseconds; + longlong microseconds; long days; int l_sign= 1; TIME l_time1 ,l_time2, l_time3; @@ -2457,32 +2456,23 @@ String *Item_func_timediff::val_str(String *str) (uint) l_time2.month, (uint) l_time2.day)); - microseconds= l_time1.second_part - l_sign*l_time2.second_part; - seconds= ((longlong) days*86400L + l_time1.hour*3600L + - l_time1.minute*60L + l_time1.second + microseconds/1000000L - - (longlong)l_sign*(l_time2.hour*3600L+l_time2.minute*60L+ - l_time2.second)); + microseconds= ((longlong)days*86400L + + l_time1.hour*3600L + l_time1.minute*60L + l_time1.second - + (longlong)l_sign*(l_time2.hour*3600L + l_time2.minute*60L + + l_time2.second))*1000000 + + l_time1.second_part - l_sign*l_time2.second_part; l_time3.neg= 0; - if (seconds < 0) - { - seconds= -seconds; - l_time3.neg= 1; - } - else if (seconds == 0 && microseconds < 0) + if (microseconds < 0) { microseconds= -microseconds; l_time3.neg= 1; } - if (microseconds < 0) - { - microseconds+= 1000000L; - seconds--; - } - if ((l_time2.neg == l_time1.neg) && l_time1.neg) + if ((l_time2.neg == l_time1.neg) && l_time1.neg && microseconds) l_time3.neg= l_time3.neg ? 0 : 1; - calc_time_from_sec(&l_time3, (long) seconds, microseconds); + calc_time_from_sec(&l_time3, (long)(microseconds/1000000), + (long)(microseconds%1000000)); if (!make_datetime(l_time1.second_part || l_time2.second_part ? TIME_MICROSECOND : TIME_ONLY, From de4ec3ed30ba1fec675bfcf40810eec4e0eb22c6 Mon Sep 17 00:00:00 2001 From: "monty@mysql.com" <> Date: Thu, 13 Jan 2005 03:02:49 +0200 Subject: [PATCH 05/17] Fix accesses to uninitialized memory (found by valgrind) --- mysys/my_alloc.c | 13 +++++++ sql/filesort.cc | 7 +++- sql/sql_select.cc | 11 ++++-- sql/sql_view.cc | 5 +-- sql/table.cc | 89 ++++++++++++++++++++++------------------------- 5 files changed, 71 insertions(+), 54 deletions(-) diff --git a/mysys/my_alloc.c b/mysys/my_alloc.c index d8c19d86e5c..c14b2899b4b 100644 --- a/mysys/my_alloc.c +++ b/mysys/my_alloc.c @@ -245,6 +245,19 @@ static inline void mark_blocks_free(MEM_ROOT* root) /* Deallocate everything used by alloc_root or just move used blocks to free list if called with MY_USED_TO_FREE + + SYNOPSIS + free_root() + root Memory root + MyFlags Flags for what should be freed: + + MY_MARK_BLOCKS_FREED Don't free blocks, just mark them free + MY_KEEP_PREALLOC If this is not set, then free also the + preallocated block + + NOTES + One can call this function either with root block initialised with + init_alloc_root() or with a bzero()-ed block. */ void free_root(MEM_ROOT *root, myf MyFlags) diff --git a/sql/filesort.cc b/sql/filesort.cc index 41104106048..0a4e747a136 100644 --- a/sql/filesort.cc +++ b/sql/filesort.cc @@ -1176,7 +1176,12 @@ sortlength(SORT_FIELD *sortorder, uint s_length, bool *multi_byte_charset) else { sortorder->length=sortorder->field->pack_length(); - if (use_strnxfrm((cs=sortorder->field->charset()))) + /* + We must test cmp_type() to ensure that ENUM and SET are sorted + as numbers + */ + if (use_strnxfrm((cs=sortorder->field->charset())) && + sortorder->field->cmp_type() == STRING_RESULT) { sortorder->need_strxnfrm= 1; *multi_byte_charset= 1; diff --git a/sql/sql_select.cc b/sql/sql_select.cc index b9c8f6c1237..0273334eca2 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -7823,12 +7823,17 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List &fields, if (temp_pool_slot != MY_BIT_NONE) // we got a slot sprintf(filename, "%s_%lx_%i", tmp_file_prefix, current_pid, temp_pool_slot); - else // if we run out of slots or we are not using tempool + else + { + /* if we run out of slots or we are not using tempool */ sprintf(filename,"%s%lx_%lx_%x",tmp_file_prefix,current_pid, thd->thread_id, thd->tmp_table++); + } - if (lower_case_table_names) - my_casedn_str(files_charset_info, path); + /* + No need for change table name to lower case as we are only creating + MyISAM or HEAP tables here + */ sprintf(path, "%s%s", mysql_tmpdir, filename); if (group) diff --git a/sql/sql_view.cc b/sql/sql_view.cc index 533876b6718..e2c4b1289fd 100644 --- a/sql/sql_view.cc +++ b/sql/sql_view.cc @@ -948,11 +948,12 @@ frm_type_enum mysql_frm_type(char *path) { DBUG_RETURN(FRMTYPE_ERROR); } - length= my_read(file, (byte*) header, 10, MYF(MY_WME)); + length= my_read(file, (byte*) header, sizeof(header), MYF(MY_WME)); my_close(file, MYF(MY_WME)); if (length == (int) MY_FILE_ERROR) DBUG_RETURN(FRMTYPE_ERROR); - if (!strncmp(header, "TYPE=VIEW\n", 10)) + if (length < (int) sizeof(header) || + !strncmp(header, "TYPE=VIEW\n", sizeof(header))) DBUG_RETURN(FRMTYPE_VIEW); DBUG_RETURN(FRMTYPE_TABLE); // Is probably a .frm table } diff --git a/sql/table.cc b/sql/table.cc index 97f17ea2417..71198993009 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -103,11 +103,11 @@ int openfrm(THD *thd, const char *name, const char *alias, uint db_stat, O_RDONLY | O_SHARE, MYF(0))) < 0) - goto err_w_init; + goto err; error= 4; if (my_read(file,(byte*) head,64,MYF(MY_NABP))) - goto err_w_init; + goto err; if (memcmp(head, "TYPE=", 5) == 0) { @@ -116,9 +116,9 @@ int openfrm(THD *thd, const char *name, const char *alias, uint db_stat, if (db_stat & NO_ERR_ON_NEW_FRM) DBUG_RETURN(5); - + file= -1; // caller can't process new .frm - goto err_w_init; + goto err; } share->blob_ptr_size= sizeof(char*); @@ -131,21 +131,21 @@ int openfrm(THD *thd, const char *name, const char *alias, uint db_stat, share->path= strdup_root(&outparam->mem_root, name); outparam->alias= my_strdup(alias, MYF(MY_WME)); if (!share->table_name || !share->path || !outparam->alias) - goto err_not_open; + goto err; *fn_ext(share->table_name)='\0'; // Remove extension *fn_ext(share->path)='\0'; // Remove extension if (head[0] != (uchar) 254 || head[1] != 1 || (head[2] != FRM_VER && head[2] != FRM_VER+1 && ! (head[2] >= FRM_VER+3 && head[2] <= FRM_VER+4))) - goto err_not_open; /* purecov: inspected */ + goto err; /* purecov: inspected */ new_field_pack_flag=head[27]; new_frm_ver= (head[2] - FRM_VER); field_pack_length= new_frm_ver < 2 ? 11 : 17; error=3; if (!(pos=get_form_pos(file,head,(TYPELIB*) 0))) - goto err_not_open; /* purecov: inspected */ + goto err; /* purecov: inspected */ *fn_ext(index_file)='\0'; // Remove .frm extension share->frm_version= head[2]; @@ -181,7 +181,7 @@ int openfrm(THD *thd, const char *name, const char *alias, uint db_stat, key_info_length= (uint) uint2korr(head+28); VOID(my_seek(file,(ulong) uint2korr(head+6),MY_SEEK_SET,MYF(0))); if (read_string(file,(gptr*) &disk_buff,key_info_length)) - goto err_not_open; /* purecov: inspected */ + goto err; /* purecov: inspected */ if (disk_buff[0] & 0x80) { share->keys= keys= (disk_buff[1] << 7) | (disk_buff[0] & 0x7f); @@ -201,7 +201,7 @@ int openfrm(THD *thd, const char *name, const char *alias, uint db_stat, n_length=keys*sizeof(KEY)+key_parts*sizeof(KEY_PART_INFO); if (!(keyinfo = (KEY*) alloc_root(&outparam->mem_root, n_length+uint2korr(disk_buff+4)))) - goto err_not_open; /* purecov: inspected */ + goto err; /* purecov: inspected */ bzero((char*) keyinfo,n_length); outparam->key_info=keyinfo; key_part= my_reinterpret_cast(KEY_PART_INFO*) (keyinfo+keys); @@ -210,7 +210,7 @@ int openfrm(THD *thd, const char *name, const char *alias, uint db_stat, ulong *rec_per_key; if (!(rec_per_key= (ulong*) alloc_root(&outparam->mem_root, sizeof(ulong*)*key_parts))) - goto err_not_open; + goto err; for (i=0 ; i < keys ; i++, keyinfo++) { @@ -279,7 +279,7 @@ int openfrm(THD *thd, const char *name, const char *alias, uint db_stat, /* Allocate handler */ if (!(outparam->file= get_new_handler(outparam, share->db_type))) - goto err_not_open; + goto err; error=4; outparam->reginfo.lock_type= TL_UNLOCK; @@ -296,14 +296,14 @@ int openfrm(THD *thd, const char *name, const char *alias, uint db_stat, share->rec_buff_length= rec_buff_length; if (!(record= (char *) alloc_root(&outparam->mem_root, rec_buff_length * records))) - goto err_not_open; /* purecov: inspected */ + goto err; /* purecov: inspected */ share->default_values= record; if (my_pread(file,(byte*) record, (uint) share->reclength, (ulong) (uint2korr(head+6)+ ((uint2korr(head+14) == 0xffff ? uint4korr(head+47) : uint2korr(head+14)))), MYF(MY_NABP))) - goto err_not_open; /* purecov: inspected */ + goto err; /* purecov: inspected */ if (records == 1) { @@ -332,12 +332,13 @@ int openfrm(THD *thd, const char *name, const char *alias, uint db_stat, } #endif VOID(my_seek(file,pos,MY_SEEK_SET,MYF(0))); - if (my_read(file,(byte*) head,288,MYF(MY_NABP))) goto err_not_open; + if (my_read(file,(byte*) head,288,MYF(MY_NABP))) + goto err; if (crypted) { crypted->decode((char*) head+256,288-256); if (sint2korr(head+284) != 0) // Should be 0 - goto err_not_open; // Wrong password + goto err; // Wrong password } share->fields= uint2korr(head+258); @@ -359,13 +360,13 @@ int openfrm(THD *thd, const char *name, const char *alias, uint db_stat, (share->fields+interval_parts+ keys+3)*sizeof(my_string)+ (n_length+int_length+com_length))))) - goto err_not_open; /* purecov: inspected */ + goto err; /* purecov: inspected */ outparam->field=field_ptr; read_length=(uint) (share->fields * field_pack_length + pos+ (uint) (n_length+int_length+com_length)); if (read_string(file,(gptr*) &disk_buff,read_length)) - goto err_not_open; /* purecov: inspected */ + goto err; /* purecov: inspected */ if (crypted) { crypted->decode((char*) disk_buff,read_length); @@ -398,7 +399,7 @@ int openfrm(THD *thd, const char *name, const char *alias, uint db_stat, uint count= (uint) (interval->count + 1) * sizeof(uint); if (!(interval->type_lengths= (uint *) alloc_root(&outparam->mem_root, count))) - goto err_not_open; + goto err; for (count= 0; count < interval->count; count++) interval->type_lengths[count]= strlen(interval->type_names[count]); interval->type_lengths[count]= 0; @@ -459,7 +460,7 @@ int openfrm(THD *thd, const char *name, const char *alias, uint db_stat, charset= &my_charset_bin; #else error= 4; // unsupported field type - goto err_not_open; + goto err; #endif } else @@ -470,7 +471,7 @@ int openfrm(THD *thd, const char *name, const char *alias, uint db_stat, { error= 5; // Unknown or unavailable charset errarg= (int) strpos[14]; - goto err_not_open; + goto err; } } if (!comment_length) @@ -543,7 +544,7 @@ int openfrm(THD *thd, const char *name, const char *alias, uint db_stat, if (!reg_field) // Not supported field type { error= 4; - goto err_not_open; /* purecov: inspected */ + goto err; /* purecov: inspected */ } reg_field->comment=comment; if (field_type == FIELD_TYPE_BIT) @@ -616,7 +617,7 @@ int openfrm(THD *thd, const char *name, const char *alias, uint db_stat, (uint) key_part->length); #ifdef EXTRA_DEBUG if (key_part->fieldnr > share->fields) - goto err_not_open; // sanity check + goto err; // sanity check #endif if (key_part->fieldnr) { // Should always be true ! @@ -767,7 +768,7 @@ int openfrm(THD *thd, const char *name, const char *alias, uint db_stat, if (!(share->blob_field= save= (uint*) alloc_root(&outparam->mem_root, (uint) (share->blob_fields* sizeof(uint))))) - goto err_not_open; + goto err; for (i=0, ptr= outparam->field ; *ptr ; ptr++, i++) { if ((*ptr)->flags & BLOB_FLAG) @@ -779,25 +780,25 @@ int openfrm(THD *thd, const char *name, const char *alias, uint db_stat, error=2; if (db_stat) { - int err; + int ha_err; unpack_filename(index_file,index_file); - if ((err=(outparam->file-> - ha_open(index_file, - (db_stat & HA_READ_ONLY ? O_RDONLY : O_RDWR), - (db_stat & HA_OPEN_TEMPORARY ? HA_OPEN_TMP_TABLE : - ((db_stat & HA_WAIT_IF_LOCKED) || - (specialflag & SPECIAL_WAIT_IF_LOCKED)) ? - HA_OPEN_WAIT_IF_LOCKED : - (db_stat & (HA_ABORT_IF_LOCKED | HA_GET_INFO)) ? - HA_OPEN_ABORT_IF_LOCKED : - HA_OPEN_IGNORE_IF_LOCKED) | ha_open_flags)))) + if ((ha_err= (outparam->file-> + ha_open(index_file, + (db_stat & HA_READ_ONLY ? O_RDONLY : O_RDWR), + (db_stat & HA_OPEN_TEMPORARY ? HA_OPEN_TMP_TABLE : + ((db_stat & HA_WAIT_IF_LOCKED) || + (specialflag & SPECIAL_WAIT_IF_LOCKED)) ? + HA_OPEN_WAIT_IF_LOCKED : + (db_stat & (HA_ABORT_IF_LOCKED | HA_GET_INFO)) ? + HA_OPEN_ABORT_IF_LOCKED : + HA_OPEN_IGNORE_IF_LOCKED) | ha_open_flags)))) { /* Set a flag if the table is crashed and it can be auto. repaired */ - share->crashed= ((err == HA_ERR_CRASHED_ON_USAGE) && + share->crashed= ((ha_err == HA_ERR_CRASHED_ON_USAGE) && outparam->file->auto_repair() && !(ha_open_flags & HA_OPEN_FOR_REPAIR)); - if (err == HA_ERR_NO_SUCH_TABLE) + if (ha_err == HA_ERR_NO_SUCH_TABLE) { /* The table did not exists in storage engine, use same error message as if the .frm file didn't exist */ @@ -806,10 +807,10 @@ int openfrm(THD *thd, const char *name, const char *alias, uint db_stat, } else { - outparam->file->print_error(err, MYF(0)); + outparam->file->print_error(ha_err, MYF(0)); error_reported= TRUE; } - goto err_not_open; /* purecov: inspected */ + goto err; /* purecov: inspected */ } } share->db_low_byte_first= outparam->file->low_byte_first(); @@ -822,15 +823,7 @@ int openfrm(THD *thd, const char *name, const char *alias, uint db_stat, #endif DBUG_RETURN (0); - err_w_init: - /* - Avoid problem with uninitialized data - Note that we don't have to initialize outparam->s here becasue - the caller will check if the pointer exists in case of errors - */ - bzero((char*) outparam,sizeof(*outparam)); - - err_not_open: + err: x_free((gptr) disk_buff); if (file > 0) VOID(my_close(file,MYF(MY_WME))); @@ -843,7 +836,7 @@ int openfrm(THD *thd, const char *name, const char *alias, uint db_stat, outparam->file=0; // For easier errorchecking outparam->db_stat=0; hash_free(&share->name_hash); - free_root(&outparam->mem_root, MYF(0)); + free_root(&outparam->mem_root, MYF(0)); // Safe to call on bzero'd root my_free((char*) outparam->alias, MYF(MY_ALLOW_ZERO_PTR)); DBUG_RETURN (error); } /* openfrm */ From 284191d19a339985788da8d6bd227a450ab846c9 Mon Sep 17 00:00:00 2001 From: "marko@hundin.mysql.fi" <> Date: Thu, 13 Jan 2005 16:15:14 +0200 Subject: [PATCH 06/17] InnoDB: Detect the availability of the Mac OS X fsync() work-around at run-time, so that an executable compiled on Mac OS X 10.2 can be run on Mac OS X 10.2 (without the work-around) and Mac OS X 10.3 and later with the work-aroud enabled. --- innobase/include/srv0start.h | 4 ++++ innobase/os/os0file.c | 24 ++++++++++++++++++------ innobase/srv/srv0start.c | 27 +++++++++++++++++++++++++++ 3 files changed, 49 insertions(+), 6 deletions(-) diff --git a/innobase/include/srv0start.h b/innobase/include/srv0start.h index 75af1a212b4..8df0f97c4ff 100644 --- a/innobase/include/srv0start.h +++ b/innobase/include/srv0start.h @@ -75,6 +75,10 @@ extern dulint srv_start_lsn; void set_panic_flag_for_netware(void); #endif +#ifdef HAVE_DARWIN_THREADS +extern ibool srv_have_fullfsync; +#endif + extern ulint srv_sizeof_trx_t_in_ha_innodb_cc; extern ibool srv_is_being_started; diff --git a/innobase/os/os0file.c b/innobase/os/os0file.c index 60760d1f8b8..6fb27346a37 100644 --- a/innobase/os/os0file.c +++ b/innobase/os/os0file.c @@ -1763,19 +1763,31 @@ os_file_flush( #else int ret; -#if defined(HAVE_DARWIN_THREADS) && defined(F_FULLFSYNC) +#if defined(HAVE_DARWIN_THREADS) +# ifndef F_FULLFSYNC + /* The following definition is from the Mac OS X 10.3 */ +# define F_FULLFSYNC 51 /* fsync + ask the drive to flush to the media */ +# elif F_FULLFSYNC != 51 +# error "F_FULLFSYNC != 51: ABI incompatibility with Mac OS X 10.3" +# endif /* Apple has disabled fsync() for internal disk drives in OS X. That caused corruption for a user when he tested a power outage. Let us in OS X use a nonstandard flush method recommended by an Apple engineer. */ - ret = fcntl(file, F_FULLFSYNC, NULL); - - if (ret) { - /* If we are not on a file system that supports this, then - fall back to a plain fsync. */ + if (!srv_have_fullfsync) { + /* If we are not on an operating system that supports this, + then fall back to a plain fsync. */ ret = fsync(file); + } else { + ret = fcntl(file, F_FULLFSYNC, NULL); + + if (ret) { + /* If we are not on a file system that supports this, + then fall back to a plain fsync. */ + ret = fsync(file); + } } #elif HAVE_FDATASYNC ret = fdatasync(file); diff --git a/innobase/srv/srv0start.c b/innobase/srv/srv0start.c index 7a61a885ef9..fe05f07df21 100644 --- a/innobase/srv/srv0start.c +++ b/innobase/srv/srv0start.c @@ -61,6 +61,11 @@ dulint srv_start_lsn; /* Log sequence number at shutdown */ dulint srv_shutdown_lsn; +#ifdef HAVE_DARWIN_THREADS +# include +ibool srv_have_fullfsync = FALSE; +#endif + ibool srv_start_raw_disk_in_use = FALSE; static ibool srv_start_has_been_called = FALSE; @@ -935,6 +940,28 @@ innobase_start_or_create_for_mysql(void) ulint i; ibool srv_file_per_table_original_value = srv_file_per_table; mtr_t mtr; +#ifdef HAVE_DARWIN_THREADS +# ifdef F_FULLFSYNC + /* This executable has been compiled on Mac OS X 10.3 or later. + Assume that F_FULLFSYNC is available at run-time. */ + srv_have_fullfsync = TRUE; +# else /* F_FULLFSYNC */ + /* This executable has been compiled on Mac OS X 10.2 + or earlier. Determine if the executable is running + on Mac OS X 10.3 or later. */ + struct utsname utsname; + if (uname(&utsname)) { + fputs("InnoDB: cannot determine Mac OS X version!\n", stderr); + } else { + srv_have_fullfsync = strcmp(utsname.release, "7.") >= 0; + } + if (!srv_have_fullfsync) { + fputs( +"InnoDB: On Mac OS X, fsync() may be broken on internal drives,\n" +"InnoDB: making transactions unsafe!\n", stderr); + } +# endif /* F_FULLFSYNC */ +#endif /* HAVE_DARWIN_THREADS */ if (sizeof(ulint) != sizeof(void*)) { fprintf(stderr, From 249f914c215e0147b1b348e3cd04d7e839b56f96 Mon Sep 17 00:00:00 2001 From: "monty@mysql.com" <> Date: Fri, 14 Jan 2005 00:09:15 +0200 Subject: [PATCH 07/17] Fix bug in INSERT DELAYED with prepared statements The bug was that if you have two TL_WRITE_DELAYED at the same time, mi_lock_databases() could be done in the wrong order and we could write the wrong header to the MyISAM index file. --- sql/mysql_priv.h | 1 + sql/sql_base.cc | 28 ++++++++++++++++++++++++++++ sql/sql_prepare.cc | 6 +++++- 3 files changed, 34 insertions(+), 1 deletion(-) diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index 204b319138e..4b785aafc5f 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -725,6 +725,7 @@ void wait_for_refresh(THD *thd); int open_tables(THD *thd, TABLE_LIST *tables, uint *counter); int simple_open_n_lock_tables(THD *thd,TABLE_LIST *tables); int open_and_lock_tables(THD *thd,TABLE_LIST *tables); +int open_normal_and_derived_tables(THD *thd, TABLE_LIST *tables); void relink_tables_for_derived(THD *thd); int lock_tables(THD *thd, TABLE_LIST *tables, uint counter); TABLE *open_temporary_table(THD *thd, const char *path, const char *db, diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 2f2d9b290ac..263c68a82b7 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -1702,6 +1702,34 @@ int open_and_lock_tables(THD *thd, TABLE_LIST *tables) } +/* + Open all tables in list and process derived tables + + SYNOPSIS + open_normal_and_derived_tables + thd - thread handler + tables - list of tables for open&locking + + RETURN + FALSE - ok + TRUE - error + + NOTE + This is to be used on prepare stage when you don't read any + data from the tables. +*/ + +int open_normal_and_derived_tables(THD *thd, TABLE_LIST *tables) +{ + uint counter; + DBUG_ENTER("open_normal_and_derived_tables"); + if (open_tables(thd, tables, &counter)) + DBUG_RETURN(-1); /* purecov: inspected */ + relink_tables_for_derived(thd); + DBUG_RETURN(mysql_handle_derived(thd->lex)); +} + + /* Let us propagate pointers to open tables from global table list to table lists in particular selects if needed. diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index ecf01824755..1dc46aef4da 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -897,8 +897,12 @@ static int mysql_test_insert(Prepared_statement *stmt, /* open temporary memory pool for temporary data allocated by derived tables & preparation procedure + Note that this is done without locks (should not be needed as we will not + access any data here) + If we would use locks, then we have to ensure we are not using + TL_WRITE_DELAYED as having two such locks can cause table corruption. */ - if (open_and_lock_tables(thd, table_list)) + if (open_normal_and_derived_tables(thd, table_list)) { DBUG_RETURN(-1); } From ebefcc616ac083d90bc0f02273c41a5f07cb196e Mon Sep 17 00:00:00 2001 From: "konstantin@mysql.com" <> Date: Fri, 14 Jan 2005 01:59:03 +0300 Subject: [PATCH 08/17] fix C++ comments in C file (fixed in 5.0 already) --- strings/ctype-uca.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/strings/ctype-uca.c b/strings/ctype-uca.c index 4992ef2dcd1..452ca263433 100644 --- a/strings/ctype-uca.c +++ b/strings/ctype-uca.c @@ -7214,7 +7214,7 @@ static int my_strnxfrm_uca(CHARSET_INFO *cs, uchar *dst, uint dstlen, const uchar *src, uint srclen) { - uchar *de = dst + (dstlen & (uint) ~1); // add even length for easier code + uchar *de = dst + (dstlen & (uint) ~1); /* add even length for easier code */ int s_res; my_uca_scanner scanner; scanner_handler->init(&scanner, cs, src, srclen); @@ -7232,7 +7232,7 @@ static int my_strnxfrm_uca(CHARSET_INFO *cs, dst[1]= s_res & 0xFF; dst+= 2; } - if (dstlen & 1) // if odd number then fill the last char + if (dstlen & 1) /* if odd number then fill the last char */ *dst= '\0'; return dstlen; From d28aa5cc15143000c56ab266e5bc2b4fd21c2de8 Mon Sep 17 00:00:00 2001 From: "gluh@gluh.mysql.r18.ru" <> Date: Fri, 14 Jan 2005 16:23:32 +0300 Subject: [PATCH 09/17] Backport from 5.0 --- strings/ctype-mb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/strings/ctype-mb.c b/strings/ctype-mb.c index bf69fe683ae..731fc460cef 100644 --- a/strings/ctype-mb.c +++ b/strings/ctype-mb.c @@ -274,7 +274,7 @@ uint my_well_formed_len_mb(CHARSET_INFO *cs, my_wc_t wc; int mblen; - if ((mblen= cs->cset->mb_wc(cs, &wc, (uchar*) b, (uchar*) e)) <0) + if ((mblen= cs->cset->mb_wc(cs, &wc, (uchar*) b, (uchar*) e)) <= 0) break; b+= mblen; pos--; From 57b92c20429fd6961340162a6dea6f2a4b1d4215 Mon Sep 17 00:00:00 2001 From: "lenz@mysql.com" <> Date: Fri, 14 Jan 2005 15:35:32 +0100 Subject: [PATCH 10/17] - make sure to enable "--without-ndb-debug" when both --debug and --with-cluster are provided. Yes, this sounds like a contradiction - enabling debugging in NBD enables code parts that may still reveal portability issues when compiled with non-gcc compilers. (request by TomasU) --- Build-tools/Do-compile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Build-tools/Do-compile b/Build-tools/Do-compile index 1e7041e8f7a..b431f9fc1c5 100755 --- a/Build-tools/Do-compile +++ b/Build-tools/Do-compile @@ -264,7 +264,8 @@ if ($opt_stage <= 1) $opt_config_options.= " --with-berkeley-db" if ($opt_bdb); $opt_config_options.= " --with-zlib-dir=bundled" if ($opt_bundled_zlib); $opt_config_options.= " --with-client-ldflags=-all-static" if ($opt_static_client); - $opt_config_options.= " --with-debug" if ($opt_with_debug); + $opt_config_options.= " --with-debug" if ($opt_with_debug); + $opt_config_options.= " --without-ndb-debug" if ($opt_with_debug && $opt_with_cluster); $opt_config_options.= " --with-libwrap" if ($opt_libwrap); $opt_config_options.= " --with-low-memory" if ($opt_with_low_memory); $opt_config_options.= " --with-mysqld-ldflags=-all-static" if ($opt_static_server); From 8830ec9a1d1f8aae49320545e2a0e65a40e9290b Mon Sep 17 00:00:00 2001 From: "tomas@poseidon.ndb.mysql.com" <> Date: Fri, 14 Jan 2005 16:14:50 +0100 Subject: [PATCH 11/17] bumped up ndb version compatible with 4.1.9 --- configure.in | 2 +- ndb/src/common/util/version.c | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/configure.in b/configure.in index 45ca009123e..9f89c635e7f 100644 --- a/configure.in +++ b/configure.in @@ -16,7 +16,7 @@ SHARED_LIB_VERSION=14:0:0 # ndb version NDB_VERSION_MAJOR=4 NDB_VERSION_MINOR=1 -NDB_VERSION_BUILD=9 +NDB_VERSION_BUILD=10 NDB_VERSION_STATUS="" # Set all version vars based on $VERSION. How do we do this more elegant ? diff --git a/ndb/src/common/util/version.c b/ndb/src/common/util/version.c index 9bc3488b078..8cc6de0fc24 100644 --- a/ndb/src/common/util/version.c +++ b/ndb/src/common/util/version.c @@ -90,6 +90,7 @@ void ndbSetOwnVersion() {} #ifndef TEST_VERSION struct NdbUpGradeCompatible ndbCompatibleTable_full[] = { + { MAKE_VERSION(4,1,10), MAKE_VERSION(4,1,9), UG_Exact }, { MAKE_VERSION(4,1,9), MAKE_VERSION(4,1,8), UG_Exact }, { MAKE_VERSION(3,5,2), MAKE_VERSION(3,5,1), UG_Exact }, { 0, 0, UG_Null } From 75935d2a8e93b29a1e1590df755f958d55769e59 Mon Sep 17 00:00:00 2001 From: "lenz@mysql.com" <> Date: Fri, 14 Jan 2005 18:20:03 +0100 Subject: [PATCH 12/17] - replaced obsoleted "BuildPrereq" with "BuildRequires" in the RPM spec file --- support-files/mysql.spec.sh | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/support-files/mysql.spec.sh b/support-files/mysql.spec.sh index 5afddee609b..99280385965 100644 --- a/support-files/mysql.spec.sh +++ b/support-files/mysql.spec.sh @@ -27,7 +27,7 @@ Packager: Lenz Grimmer Vendor: MySQL AB Requires: fileutils sh-utils Provides: msqlormysql MySQL-server mysql -BuildPrereq: ncurses-devel +BuildRequires: ncurses-devel Obsoletes: mysql # Think about what you use here since the first step is to @@ -607,6 +607,10 @@ fi # itself - note that they must be ordered by date (important when # merging BK trees) %changelog +* Fri Jan 14 2005 Lenz Grimmer + +- replaced obsoleted "BuildPrereq" with "BuildRequires" instead + * Thu Dec 31 2004 Lenz Grimmer - enabled the "Archive" storage engine for the max binary From 367bcf8c40202a884ba6b9dfc8f94b7bcf2a2adc Mon Sep 17 00:00:00 2001 From: "serg@serg.mylan" <> Date: Fri, 14 Jan 2005 19:49:45 +0100 Subject: [PATCH 13/17] limit HEAP table size with max_heap_table_size, better estimation for mem_per_row --- heap/hp_create.c | 7 ++++--- heap/hp_write.c | 3 ++- include/heap.h | 7 ++++--- sql/ha_heap.cc | 24 ++++++++++++++++++------ 4 files changed, 28 insertions(+), 13 deletions(-) diff --git a/heap/hp_create.c b/heap/hp_create.c index fdfe78a1e09..af32fefea1b 100644 --- a/heap/hp_create.c +++ b/heap/hp_create.c @@ -123,15 +123,15 @@ int heap_create(const char *name, uint keys, HP_KEYDEF *keydef, keyseg->flag= 0; keyseg->null_bit= 0; keyseg++; - - init_tree(&keyinfo->rb_tree, 0, 0, sizeof(byte*), + + init_tree(&keyinfo->rb_tree, 0, 0, sizeof(byte*), (qsort_cmp2)keys_compare, 1, NULL, NULL); keyinfo->delete_key= hp_rb_delete_key; keyinfo->write_key= hp_rb_write_key; } else { - init_block(&keyinfo->block, sizeof(HASH_INFO), min_records, + init_block(&keyinfo->block, sizeof(HASH_INFO), min_records, max_records); keyinfo->delete_key= hp_delete_key; keyinfo->write_key= hp_write_key; @@ -140,6 +140,7 @@ int heap_create(const char *name, uint keys, HP_KEYDEF *keydef, } share->min_records= min_records; share->max_records= max_records; + share->max_table_size= create_info->max_table_size; share->data_length= share->index_length= 0; share->reclength= reclength; share->blength= 1; diff --git a/heap/hp_write.c b/heap/hp_write.c index 43cee67b39c..808fe6608b1 100644 --- a/heap/hp_write.c +++ b/heap/hp_write.c @@ -143,7 +143,8 @@ static byte *next_free_record_pos(HP_SHARE *info) } if (!(block_pos=(info->records % info->block.records_in_block))) { - if (info->records > info->max_records && info->max_records) + if ((info->records > info->max_records && info->max_records) || + (info->data_length + info->index_length >= info->max_table_size)) { my_errno=HA_ERR_RECORD_FILE_FULL; DBUG_RETURN(NULL); diff --git a/include/heap.h b/include/heap.h index 5e83a6e2cb5..ac2b38d1f2d 100644 --- a/include/heap.h +++ b/include/heap.h @@ -125,8 +125,8 @@ typedef struct st_hp_keydef /* Key definition with open */ TREE rb_tree; int (*write_key)(struct st_heap_info *info, struct st_hp_keydef *keyinfo, const byte *record, byte *recpos); - int (*delete_key)(struct st_heap_info *info, struct st_hp_keydef *keyinfo, - const byte *record, byte *recpos, int flag); + int (*delete_key)(struct st_heap_info *info, struct st_hp_keydef *keyinfo, + const byte *record, byte *recpos, int flag); uint (*get_key_length)(struct st_hp_keydef *keydef, const byte *key); } HP_KEYDEF; @@ -135,7 +135,7 @@ typedef struct st_heap_share HP_BLOCK block; HP_KEYDEF *keydef; ulong min_records,max_records; /* Params to open */ - ulong data_length,index_length; + ulong data_length,index_length,max_table_size; uint records; /* records */ uint blength; /* records rounded up to 2^n */ uint deleted; /* Deleted records in database */ @@ -185,6 +185,7 @@ typedef struct st_heap_create_info { uint auto_key; uint auto_key_type; + ulong max_table_size; ulonglong auto_increment; } HP_CREATE_INFO; diff --git a/sql/ha_heap.cc b/sql/ha_heap.cc index dbcf7bc9197..96c19ce0705 100644 --- a/sql/ha_heap.cc +++ b/sql/ha_heap.cc @@ -460,12 +460,24 @@ int ha_heap::create(const char *name, TABLE *table_arg, KEY_PART_INFO *key_part= pos->key_part; KEY_PART_INFO *key_part_end= key_part + pos->key_parts; - mem_per_row+= (pos->key_length + (sizeof(char*) * 2)); - keydef[key].keysegs= (uint) pos->key_parts; keydef[key].flag= (pos->flags & (HA_NOSAME | HA_NULL_ARE_EQUAL)); keydef[key].seg= seg; - keydef[key].algorithm= ((pos->algorithm == HA_KEY_ALG_UNDEF) ? + + switch (pos->algorithm) { + case HA_KEY_ALG_UNDEF: + case HA_KEY_ALG_HASH: + keydef[key].algorithm= HA_KEY_ALG_HASH; + mem_per_row+= sizeof(char*) * 2; // = sizeof(HASH_INFO) + break; + case HA_KEY_ALG_BTREE: + keydef[key].algorithm= HA_KEY_ALG_BTREE; + mem_per_row+=sizeof(TREE_ELEMENT)+pos->key_length+sizeof(char*); + break; + default: + DBUG_ASSERT(0); // cannot happen + } + keydef[key].algorithm= ((pos->algorithm == HA_KEY_ALG_UNDEF) ? HA_KEY_ALG_HASH : pos->algorithm); for (; key_part != key_part_end; key_part++, seg++) @@ -501,17 +513,17 @@ int ha_heap::create(const char *name, TABLE *table_arg, } } mem_per_row+= MY_ALIGN(table_arg->reclength + 1, sizeof(char*)); - max_rows = (ha_rows) (current_thd->variables.max_heap_table_size / - mem_per_row); HP_CREATE_INFO hp_create_info; hp_create_info.auto_key= auto_key; hp_create_info.auto_key_type= auto_key_type; hp_create_info.auto_increment= (create_info->auto_increment_value ? create_info->auto_increment_value - 1 : 0); + hp_create_info.max_table_size=current_thd->variables.max_heap_table_size; + max_rows = (ha_rows) (hp_create_info.max_table_size / mem_per_row); error= heap_create(fn_format(buff,name,"","",4+2), table_arg->keys,keydef, table_arg->reclength, (ulong) ((table_arg->max_rows < max_rows && - table_arg->max_rows) ? + table_arg->max_rows) ? table_arg->max_rows : max_rows), (ulong) table_arg->min_rows, &hp_create_info); my_free((gptr) keydef, MYF(0)); From 6d280ac1615efe5f67843a27c18af080acadc383 Mon Sep 17 00:00:00 2001 From: "monty@mysql.com" <> Date: Sat, 15 Jan 2005 03:47:06 +0200 Subject: [PATCH 14/17] Fixed possible access to unintialized memory in filesort when using many buffers --- include/my_sys.h | 1 + mysys/mf_iocache.c | 51 ++++++++++++++++++++++++++++++++-------------- sql/filesort.cc | 2 ++ 3 files changed, 39 insertions(+), 15 deletions(-) diff --git a/include/my_sys.h b/include/my_sys.h index 9e43889d0e0..0fdb8d640e7 100644 --- a/include/my_sys.h +++ b/include/my_sys.h @@ -671,6 +671,7 @@ extern int init_io_cache(IO_CACHE *info,File file,uint cachesize, extern my_bool reinit_io_cache(IO_CACHE *info,enum cache_type type, my_off_t seek_offset,pbool use_async_io, pbool clear_cache); +extern void setup_io_cache(IO_CACHE* info); extern int _my_b_read(IO_CACHE *info,byte *Buffer,uint Count); #ifdef THREAD extern int _my_b_read_r(IO_CACHE *info,byte *Buffer,uint Count); diff --git a/mysys/mf_iocache.c b/mysys/mf_iocache.c index 8fb93dc4780..a7937da0cc2 100644 --- a/mysys/mf_iocache.c +++ b/mysys/mf_iocache.c @@ -71,9 +71,40 @@ static void my_aiowait(my_aio_result *result); #define IO_ROUND_UP(X) (((X)+IO_SIZE-1) & ~(IO_SIZE-1)) #define IO_ROUND_DN(X) ( (X) & ~(IO_SIZE-1)) -static void -init_functions(IO_CACHE* info, enum cache_type type) + +/* + Setup internal pointers inside IO_CACHE + + SYNOPSIS + setup_io_cache() + info IO_CACHE handler + + NOTES + This is called on automaticly on init or reinit of IO_CACHE + It must be called externally if one moves or copies an IO_CACHE + object. +*/ + +void setup_io_cache(IO_CACHE* info) { + /* Ensure that my_b_tell() and my_b_bytes_in_cache works */ + if (info->type == WRITE_CACHE) + { + info->current_pos= &info->write_pos; + info->current_end= &info->write_end; + } + else + { + info->current_pos= &info->read_pos; + info->current_end= &info->read_end; + } +} + + +static void +init_functions(IO_CACHE* info) +{ + enum cache_type type= info->type; switch (type) { case READ_NET: /* @@ -97,17 +128,7 @@ init_functions(IO_CACHE* info, enum cache_type type) info->write_function = _my_b_write; } - /* Ensure that my_b_tell() and my_b_bytes_in_cache works */ - if (type == WRITE_CACHE) - { - info->current_pos= &info->write_pos; - info->current_end= &info->write_end; - } - else - { - info->current_pos= &info->read_pos; - info->current_end= &info->read_end; - } + setup_io_cache(info); } /* @@ -211,7 +232,7 @@ int init_io_cache(IO_CACHE *info, File file, uint cachesize, /* End_of_file may be changed by user later */ info->end_of_file= end_of_file; info->error=0; - init_functions(info,type); + init_functions(info); #ifdef HAVE_AIOWAIT if (use_async_io && ! my_disable_async_io) { @@ -333,7 +354,7 @@ my_bool reinit_io_cache(IO_CACHE *info, enum cache_type type, } info->type=type; info->error=0; - init_functions(info,type); + init_functions(info); #ifdef HAVE_AIOWAIT if (use_async_io && ! my_disable_async_io && diff --git a/sql/filesort.cc b/sql/filesort.cc index ae6895b26b9..fe42f391007 100644 --- a/sql/filesort.cc +++ b/sql/filesort.cc @@ -680,6 +680,8 @@ int merge_many_buff(SORTPARAM *param, uchar *sort_buffer, if (flush_io_cache(to_file)) break; /* purecov: inspected */ temp=from_file; from_file=to_file; to_file=temp; + setup_io_cache(from_file); + setup_io_cache(to_file); *maxbuffer= (uint) (lastbuff-buffpek)-1; } close_cached_file(to_file); // This holds old result From cd577f2590ea972f7692cccd4d98197c9c8c51a3 Mon Sep 17 00:00:00 2001 From: "igor@rurik.mysql.com" <> Date: Sat, 15 Jan 2005 01:05:00 -0800 Subject: [PATCH 15/17] func_gconcat.result, func_gconcat.test: Added a test case for bug #7769. item_sum.h: Fixed bug #7769: a crash for queries with group_concat and having when the query table was empty. The bug was due an unsafe dereferencing. --- mysql-test/r/func_gconcat.result | 5 +++++ mysql-test/t/func_gconcat.test | 7 +++++++ sql/item_sum.h | 5 +++-- 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/mysql-test/r/func_gconcat.result b/mysql-test/r/func_gconcat.result index aa202823c77..390b33fdddc 100644 --- a/mysql-test/r/func_gconcat.result +++ b/mysql-test/r/func_gconcat.result @@ -457,3 +457,8 @@ group_concat(distinct b order by b) Warnings: Warning 1260 2 line(s) were cut by GROUP_CONCAT() drop table t1; +CREATE TABLE t1 (id int); +SELECT GROUP_CONCAT(id) AS gc FROM t1 HAVING gc IS NULL; +gc +NULL +DROP TABLE t1; diff --git a/mysql-test/t/func_gconcat.test b/mysql-test/t/func_gconcat.test index e0737a42221..6a91a7ac9c7 100644 --- a/mysql-test/t/func_gconcat.test +++ b/mysql-test/t/func_gconcat.test @@ -277,3 +277,10 @@ select group_concat(b order by b) from t1 group by a; select group_concat(distinct b order by b) from t1 group by a; drop table t1; + +# +# bug #7769: group_concat returning null is checked in having +# +CREATE TABLE t1 (id int); +SELECT GROUP_CONCAT(id) AS gc FROM t1 HAVING gc IS NULL; +DROP TABLE t1; diff --git a/sql/item_sum.h b/sql/item_sum.h index cec611b8854..d1e82387944 100644 --- a/sql/item_sum.h +++ b/sql/item_sum.h @@ -739,9 +739,10 @@ class Item_func_group_concat : public Item_sum String *res; char *end_ptr; int error; - res= val_str(&str_value); + if (!(res= val_str(&str_value))) + return (longlong) 0; end_ptr= (char*) res->ptr()+ res->length(); - return res ? my_strtoll10(res->ptr(), &end_ptr, &error) : (longlong) 0; + return my_strtoll10(res->ptr(), &end_ptr, &error); } String* val_str(String* str); Item *copy_or_same(THD* thd); From a37e91e4353e959764dc7ed5d62847727999b912 Mon Sep 17 00:00:00 2001 From: "monty@mysql.com" <> Date: Sat, 15 Jan 2005 12:28:38 +0200 Subject: [PATCH 16/17] Changed interface for my_strntod() to make it more general and more portable --- BUILD/compile-solaris-sparc-purify | 23 ++-- include/m_string.h | 2 +- mysql-test/mysql-test-run.sh | 16 ++- mysql-test/r/strict.result | 2 +- mysql-test/r/type_float.result | 5 +- mysql-test/t/strict.test | 2 +- mysql-test/t/type_float.test | 3 +- mysys/mf_iocache.c | 13 ++- mysys/thr_lock.c | 6 +- sql/field.cc | 29 +++-- sql/filesort.cc | 5 +- sql/item.cc | 24 ++-- sql/item_strfunc.cc | 10 +- sql/item_sum.cc | 9 +- strings/ctype-cp932.c | 6 +- strings/ctype-simple.c | 27 +---- strings/ctype-ucs2.c | 18 ++- strings/strtod.c | 170 ++++++++++++++++++++--------- 18 files changed, 223 insertions(+), 147 deletions(-) diff --git a/BUILD/compile-solaris-sparc-purify b/BUILD/compile-solaris-sparc-purify index 71a60e45cb0..c895d99c2cf 100755 --- a/BUILD/compile-solaris-sparc-purify +++ b/BUILD/compile-solaris-sparc-purify @@ -3,28 +3,27 @@ while test $# -gt 0 do case "$1" in - --debug) EXTRA_CONFIG_FLAGS=--with-debug; shift ;; - -h | --help ) cat <read_pos is set to info->read_end. If called through open_cached_file(), then the temporary file will only be created if a write exeeds the file buffer or if one calls - flush_io_cache(). + my_b_flush_io_cache(). If one uses SEQ_READ_APPEND, then two buffers are allocated, one for reading and another for writing. Reads are first done from disk and @@ -43,7 +43,7 @@ TODO: each time the write buffer gets full and it's written to disk, we will always do a disk read to read a part of the buffer from disk to the read buffer. - This should be fixed so that when we do a flush_io_cache() and + This should be fixed so that when we do a my_b_flush_io_cache() and we have been reading the write buffer, we should transfer the rest of the write buffer to the read buffer before we start to reuse it. */ @@ -339,7 +339,7 @@ my_bool reinit_io_cache(IO_CACHE *info, enum cache_type type, if (info->type == WRITE_CACHE && type == READ_CACHE) info->end_of_file=my_b_tell(info); /* flush cache if we want to reuse it */ - if (!clear_cache && flush_io_cache(info)) + if (!clear_cache && my_b_flush_io_cache(info,1)) DBUG_RETURN(1); info->pos_in_file=seek_offset; /* Better to do always do a seek */ @@ -948,7 +948,7 @@ int _my_b_write(register IO_CACHE *info, const byte *Buffer, uint Count) Buffer+=rest_length; Count-=rest_length; info->write_pos+=rest_length; - if (flush_io_cache(info)) + if (my_b_flush_io_cache(info,1)) return 1; if (Count >= IO_SIZE) { /* Fill first intern buffer */ @@ -1191,6 +1191,7 @@ int end_io_cache(IO_CACHE *info) int error=0; IO_CACHE_CALLBACK pre_close; DBUG_ENTER("end_io_cache"); + DBUG_PRINT("enter",("cache: 0x%lx", (ulong) info)); #ifdef THREAD /* @@ -1200,7 +1201,7 @@ int end_io_cache(IO_CACHE *info) */ if (info->share) { - pthread_cond_destroy (&info->share->cond); + pthread_cond_destroy(&info->share->cond); pthread_mutex_destroy(&info->share->mutex); info->share=0; } @@ -1215,7 +1216,7 @@ int end_io_cache(IO_CACHE *info) { info->alloced_buffer=0; if (info->file != -1) /* File doesn't exist */ - error=flush_io_cache(info); + error= my_b_flush_io_cache(info,1); my_free((gptr) info->buffer,MYF(MY_WME)); info->buffer=info->read_pos=(byte*) 0; } diff --git a/mysys/thr_lock.c b/mysys/thr_lock.c index d47ca8de183..9ce058f90fc 100644 --- a/mysys/thr_lock.c +++ b/mysys/thr_lock.c @@ -523,8 +523,10 @@ int thr_lock(THR_LOCK_DATA *data,enum thr_lock_type lock_type) data->prev=lock->write_wait.last; lock->write_wait.last= &data->next; data->cond=get_cond(); - if (lock->get_status) - (*lock->get_status)(data->status_param); + /* + We don't have to do get_status here as we will do it when we change + the delayed lock to a real write lock + */ statistic_increment(locks_immediate,&THR_LOCK_lock); goto end; } diff --git a/sql/field.cc b/sql/field.cc index 175ca09df37..29125d9d655 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -1035,7 +1035,9 @@ int Field_decimal::store(longlong nr) double Field_decimal::val_real(void) { int not_used; - return my_strntod(&my_charset_bin, ptr, field_length, NULL, ¬_used); + char *end_not_used; + return my_strntod(&my_charset_bin, ptr, field_length, &end_not_used, + ¬_used); } longlong Field_decimal::val_int(void) @@ -4425,16 +4427,18 @@ int Field_string::store(longlong nr) double Field_string::val_real(void) { int not_used; - CHARSET_INFO *cs=charset(); - return my_strntod(cs,ptr,field_length,(char**)0,¬_used); + char *end_not_used; + CHARSET_INFO *cs= charset(); + return my_strntod(cs,ptr,field_length,&end_not_used,¬_used); } longlong Field_string::val_int(void) { int not_used; + char *end_not_used; CHARSET_INFO *cs=charset(); - return my_strntoll(cs,ptr,field_length,10,NULL,¬_used); + return my_strntoll(cs,ptr,field_length,10,&end_not_used,¬_used); } @@ -4734,8 +4738,9 @@ int Field_varstring::store(longlong nr) double Field_varstring::val_real(void) { int not_used; + char *end_not_used; uint length= length_bytes == 1 ? (uint) (uchar) *ptr : uint2korr(ptr); - return my_strntod(field_charset, ptr+length_bytes, length, (char**) 0, + return my_strntod(field_charset, ptr+length_bytes, length, &end_not_used, ¬_used); } @@ -4743,9 +4748,10 @@ double Field_varstring::val_real(void) longlong Field_varstring::val_int(void) { int not_used; + char *end_not_used; uint length= length_bytes == 1 ? (uint) (uchar) *ptr : uint2korr(ptr); - return my_strntoll(field_charset, ptr+length_bytes, length, 10, NULL, - ¬_used); + return my_strntoll(field_charset, ptr+length_bytes, length, 10, + &end_not_used, ¬_used); } @@ -5339,12 +5345,15 @@ int Field_blob::store(longlong nr) double Field_blob::val_real(void) { int not_used; - char *blob; + char *end_not_used, *blob; + uint32 length; + CHARSET_INFO *cs; + memcpy_fixed(&blob,ptr+packlength,sizeof(char*)); if (!blob) return 0.0; - uint32 length=get_length(ptr); - CHARSET_INFO *cs=charset(); + length= get_length(ptr); + cs= charset(); return my_strntod(cs,blob,length,(char**)0, ¬_used); } diff --git a/sql/filesort.cc b/sql/filesort.cc index 0a4e747a136..b79ea6515c3 100644 --- a/sql/filesort.cc +++ b/sql/filesort.cc @@ -567,10 +567,10 @@ write_keys(SORTPARAM *param, register uchar **sort_keys, uint count, if (!my_b_inited(tempfile) && open_cached_file(tempfile, mysql_tmpdir, TEMP_PREFIX, DISK_BUFFER_SIZE, MYF(MY_WME))) - goto err; /* purecov: inspected */ + goto err; /* purecov: inspected */ buffpek.file_pos= my_b_tell(tempfile); if ((ha_rows) count > param->max_rows) - count=(uint) param->max_rows; /* purecov: inspected */ + count=(uint) param->max_rows; /* purecov: inspected */ buffpek.count=(ha_rows) count; for (end=sort_keys+count ; sort_keys != end ; sort_keys++) if (my_b_write(tempfile, (byte*) *sort_keys, (uint) rec_length)) @@ -844,6 +844,7 @@ int merge_many_buff(SORTPARAM *param, uchar *sort_buffer, if (flush_io_cache(to_file)) break; /* purecov: inspected */ temp=from_file; from_file=to_file; to_file=temp; + *maxbuffer= (uint) (lastbuff-buffpek)-1; } close_cached_file(to_file); // This holds old result diff --git a/sql/item.cc b/sql/item.cc index c84496f8eb7..47dccf5b8da 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -1290,11 +1290,12 @@ double Item_param::val_real() return (double) value.integer; case STRING_VALUE: case LONG_DATA_VALUE: - { - int dummy_err; - return my_strntod(str_value.charset(), (char*) str_value.ptr(), - str_value.length(), (char**) 0, &dummy_err); - } + { + int dummy_err; + char *end_not_used; + return my_strntod(str_value.charset(), (char*) str_value.ptr(), + str_value.length(), &end_not_used, &dummy_err); + } case TIME_VALUE: /* This works for example when user says SELECT ?+0.0 and supplies @@ -2545,8 +2546,9 @@ Item_num *Item_uint::neg() Item_real::Item_real(const char *str_arg, uint length) { int error; - char *end; - value= my_strntod(&my_charset_bin, (char*) str_arg, length, &end, &error); + char *end_not_used; + value= my_strntod(&my_charset_bin, (char*) str_arg, length, &end_not_used, + &error); if (error) { /* @@ -3522,12 +3524,12 @@ void Item_cache_str::store(Item *item) double Item_cache_str::val_real() { DBUG_ASSERT(fixed == 1); - int err; + int err_not_used; + char *end_not_used; if (value) return my_strntod(value->charset(), (char*) value->ptr(), - value->length(), (char**) 0, &err); - else - return (double)0; + value->length(), &end_not_used, &err_not_used); + return (double) 0; } diff --git a/sql/item_strfunc.cc b/sql/item_strfunc.cc index 9e28acdd091..09b6d9cc35d 100644 --- a/sql/item_strfunc.cc +++ b/sql/item_strfunc.cc @@ -58,17 +58,19 @@ uint nr_of_decimals(const char *str) return 0; } + double Item_str_func::val_real() { DBUG_ASSERT(fixed == 1); - int err; - char buff[64]; + int err_not_used; + char *end_not_used, buff[64]; String *res, tmp(buff,sizeof(buff), &my_charset_bin); res= val_str(&tmp); - return res ? my_strntod(res->charset(), (char*) res->ptr(),res->length(), - NULL, &err) : 0.0; + return res ? my_strntod(res->charset(), (char*) res->ptr(), res->length(), + &end_not_used, &err_not_used) : 0.0; } + longlong Item_str_func::val_int() { DBUG_ASSERT(fixed == 1); diff --git a/sql/item_sum.cc b/sql/item_sum.cc index 168c68ad706..be89aa3f86d 100644 --- a/sql/item_sum.cc +++ b/sql/item_sum.cc @@ -591,14 +591,17 @@ void Item_sum_hybrid::clear() double Item_sum_hybrid::val_real() { DBUG_ASSERT(fixed == 1); - int err; if (null_value) return 0.0; switch (hybrid_type) { case STRING_RESULT: + { + char *end_not_used; + int err_not_used; String *res; res=val_str(&str_value); - return (res ? my_strntod(res->charset(), (char*) res->ptr(),res->length(), - (char**) 0, &err) : 0.0); + return (res ? my_strntod(res->charset(), (char*) res->ptr(), res->length(), + &end_not_used, &err_not_used) : 0.0); + } case INT_RESULT: if (unsigned_flag) return ulonglong2double(sum_int); diff --git a/strings/ctype-cp932.c b/strings/ctype-cp932.c index 218fc72ad17..e5429148970 100644 --- a/strings/ctype-cp932.c +++ b/strings/ctype-cp932.c @@ -243,8 +243,10 @@ static int my_strnncoll_cp932(CHARSET_INFO *cs __attribute__((unused)), static int my_strnncollsp_cp932(CHARSET_INFO *cs __attribute__((unused)), - const uchar *a, uint a_length, - const uchar *b, uint b_length) + const uchar *a, uint a_length, + const uchar *b, uint b_length, + my_bool diff_if_only_endspace_difference + __attribute__((unused))) { const uchar *a_end= a + a_length; const uchar *b_end= b + b_length; diff --git a/strings/ctype-simple.c b/strings/ctype-simple.c index 0659cb5d387..fdfe72864b2 100644 --- a/strings/ctype-simple.c +++ b/strings/ctype-simple.c @@ -750,31 +750,10 @@ double my_strntod_8bit(CHARSET_INFO *cs __attribute__((unused)), char *str, uint length, char **end, int *err) { - char end_char; - double result; - - errno= 0; /* Safety */ - - /* - The following define is to avoid warnings from valgrind as str[length] - may not be defined (which is not fatal in real life) - */ - -#ifdef HAVE_purify if (length == INT_MAX32) -#else - if (length == INT_MAX32 || str[length] == 0) -#endif - result= my_strtod(str, end); - else - { - end_char= str[length]; - str[length]= 0; - result= my_strtod(str, end); - str[length]= end_char; /* Restore end char */ - } - *err= errno; - return result; + length= 65535; /* Should be big enough */ + *end= str + length; + return my_strtod(str, end, err); } diff --git a/strings/ctype-ucs2.c b/strings/ctype-ucs2.c index d21b340e768..7b9a7ab3020 100644 --- a/strings/ctype-ucs2.c +++ b/strings/ctype-ucs2.c @@ -925,15 +925,16 @@ bs: return (negative ? -((longlong) res) : (longlong) res); } -double my_strntod_ucs2(CHARSET_INFO *cs __attribute__((unused)), - char *nptr, uint length, - char **endptr, int *err) + +double my_strntod_ucs2(CHARSET_INFO *cs __attribute__((unused)), + char *nptr, uint length, + char **endptr, int *err) { char buf[256]; double res; register char *b=buf; register const uchar *s= (const uchar*) nptr; - register const uchar *end; + const uchar *end; my_wc_t wc; int cnv; @@ -950,13 +951,10 @@ double my_strntod_ucs2(CHARSET_INFO *cs __attribute__((unused)), break; /* Can't be part of double */ *b++= (char) wc; } - *b= 0; - errno= 0; - res=my_strtod(buf, endptr); - *err= errno; - if (endptr) - *endptr=(char*) (*endptr-buf+nptr); + *endptr= b; + res= my_strtod(buf, endptr, err); + *endptr= nptr + (uint) (*endptr- buf); return res; } diff --git a/strings/strtod.c b/strings/strtod.c index bc8105b8040..92d93612dd0 100644 --- a/strings/strtod.c +++ b/strings/strtod.c @@ -2,7 +2,7 @@ An alternative implementation of "strtod()" that is both simplier, and thread-safe. - From mit-threads as bundled with MySQL 3.23 + Original code from mit-threads as bundled with MySQL 3.23 SQL:2003 specifies a number as @@ -29,6 +29,8 @@ #include "my_base.h" /* Includes errno.h */ #include "m_ctype.h" +#define MAX_DBL_EXP 308 +#define MAX_RESULT_FOR_MAX_EXP 1.79769313486232 static double scaler10[] = { 1.0, 1e10, 1e20, 1e30, 1e40, 1e50, 1e60, 1e70, 1e80, 1e90 }; @@ -37,89 +39,154 @@ static double scaler1[] = { }; -double my_strtod(const char *str, char **end) +/* + Convert string to double (string doesn't have to be null terminated) + + SYNOPSIS + my_strtod() + str String to convert + end_ptr Pointer to pointer that points to end of string + Will be updated to point to end of double. + error Will contain error number in case of error (else 0) + + RETURN + value of str as double +*/ + +double my_strtod(const char *str, char **end_ptr, int *error) { double result= 0.0; - int negative, ndigits; - const char *old_str; + uint negative= 0, ndigits, dec_digits= 0, pre_zero, neg_exp= 0; + int exp= 0; + const char *old_str, *end= *end_ptr, *start_of_number; + char next_char; my_bool overflow=0; - while (my_isspace(&my_charset_latin1, *str)) - str++; + *error= 0; + if (str >= end) + goto done; + while (my_isspace(&my_charset_latin1, *str)) + { + if (++str == end) + goto done; + } + + start_of_number= str; if ((negative= (*str == '-')) || *str=='+') - str++; + { + if (++str == end) + goto done; /* Could be changed to error */ + } + + /* Skip pre-zero for easier calculation of overflows */ + while (*str == '0') + { + if (++str == end) + goto done; + start_of_number= 0; /* Found digit */ + } old_str= str; - while (my_isdigit (&my_charset_latin1, *str)) + while ((next_char= *str) >= '0' && next_char <= '9') { - result= result*10.0 + (*str - '0'); - str++; - } - ndigits= str-old_str; - - if (*str == '.') - { - double p10=10; - str++; - old_str= str; - while (my_isdigit (&my_charset_latin1, *str)) + result= result*10.0 + (next_char - '0'); + if (++str == end) { - result+= (*str++ - '0')/p10; - p10*=10; + next_char= 0; /* Found end of string */ + break; } - ndigits+= str-old_str; - if (!ndigits) str--; + start_of_number= 0; /* Found digit */ } - if (ndigits && (*str=='e' || *str=='E')) + ndigits= (uint) (str-old_str); + + pre_zero= 0; + if (next_char == '.' && str < end-1) + { + double p10= 10; + old_str= ++str; + while (my_isdigit(&my_charset_latin1, (next_char= *str))) + { + result+= (next_char - '0')/p10; + if (!result) + pre_zero++; + else + p10*= 10; + if (++str == end) + { + next_char= 0; + break; + } + } + /* If we found just '+.' or '.' then point at first character */ + if (!(dec_digits= (uint) (str-old_str)) && start_of_number) + str= start_of_number; /* Point at '+' or '.' */ + } + if ((next_char == 'e' || next_char == 'E') && + dec_digits + ndigits != 0 && str < end-1) { - int exp= 0; - int neg= 0; const char *old_str= str++; - if ((neg= (*str == '-')) || *str == '+') + if ((neg_exp= (*str == '-')) || *str == '+') str++; - if (!my_isdigit (&my_charset_latin1, *str)) + if (str == end || !my_isdigit(&my_charset_latin1, *str)) str= old_str; else { - double scaler= 1.0; - while (my_isdigit (&my_charset_latin1, *str)) + do { - if (exp < 9999) /* protection against exp overflow */ + if (exp < 9999) /* protec against exp overfl. */ exp= exp*10 + *str - '0'; str++; - } - if (exp >= 1000) + } while (str < end && my_isdigit(&my_charset_latin1, *str)); + } + } + if ((exp= neg_exp ? exp + pre_zero : exp - pre_zero)) + { + double scaler; + if (exp < 0) + { + exp= -exp; + neg_exp= 1; /* neg_exp was 0 before */ + } + if (exp + ndigits >= MAX_DBL_EXP + 1 && result) + { + /* + This is not 100 % as we actually will give an owerflow for + 17E307 but not for 1.7E308 but lets cut some corners to make life + simpler + */ + if (exp + ndigits > MAX_DBL_EXP + 1 || + result >= MAX_RESULT_FOR_MAX_EXP) { - if (neg) - result= 0.0; - else + if (neg_exp) + result= 0.0; + else overflow= 1; goto done; } - while (exp >= 100) - { - scaler*= 1.0e100; - exp-= 100; - } - scaler*= scaler10[exp/10]*scaler1[exp%10]; - if (neg) - result/= scaler; - else - result*= scaler; } + scaler= 1.0; + while (exp >= 100) + { + scaler*= 1.0e100; + exp-= 100; + } + scaler*= scaler10[exp/10]*scaler1[exp%10]; + if (neg_exp) + result/= scaler; + else + result*= scaler; } done: - if (end) - *end = (char *)str; + *end_ptr= (char*) str; /* end of number */ if (overflow || isinf(result)) { result= DBL_MAX; - errno= EOVERFLOW; + *error= EOVERFLOW; } return negative ? -result : result; @@ -127,6 +194,7 @@ done: double my_atof(const char *nptr) { - return (my_strtod(nptr, 0)); + int error; + const char *end= nptr+65535; /* Should be enough */ + return (my_strtod(nptr, (char**) &end, &error)); } - From 97b28521e6f2735ee6488682b15071f604f66cfd Mon Sep 17 00:00:00 2001 From: "monty@mysql.com" <> Date: Sat, 15 Jan 2005 12:36:20 +0200 Subject: [PATCH 17/17] Added ndb_types.h to ignore --- .bzrignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.bzrignore b/.bzrignore index 951a811f8e4..c972e25f2db 100644 --- a/.bzrignore +++ b/.bzrignore @@ -1045,3 +1045,4 @@ vio/test-ssl vio/test-sslclient vio/test-sslserver vio/viotest-ssl +ndb/include/ndb_types.h