From aa8ab68fea734374521aacf696fe970a7f8334ac Mon Sep 17 00:00:00 2001 From: "bar@mysql.com" <> Date: Mon, 17 Apr 2006 12:33:45 +0500 Subject: [PATCH 1/4] Bug#17939: Wrong table format when using UTF8 strings Lines with column names consisting of national letters were wrongly formatted in "mysql --table" results: mysql> SELECT 'xxx xxx xxx' as 'xxx xxx xxx'; +-------------------+ | xxx xxx xxx | +-------------------+ | xxx xxx xxx | +-------------------+ 1 row in set (0.00 sec) It happened because in UTF-8 (and other multibyte charsets) the number of display cells is not always equal to the number of bytes of the string. Data lines (unlike column name lines) were formatted correctly, because data lines were displayed taking in account number of display cells. This patch takes in account number of cells when displaying column names, the same way like displaying data lines does. Note: The patch is going to be applied to 4.1. Test case will be added after merge to 5.0, into "mysql.test", which appeared in 5.0. mysql.cc: Adding column name allignment using numcells(), the same to data alignment, which was implemented earlier. --- client/mysql.cc | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/client/mysql.cc b/client/mysql.cc index 2f9031b84b8..22fe6e81b25 100644 --- a/client/mysql.cc +++ b/client/mysql.cc @@ -2145,9 +2145,14 @@ print_table_data(MYSQL_RES *result) (void) tee_fputs("|", PAGER); for (uint off=0; (field = mysql_fetch_field(result)) ; off++) { - tee_fprintf(PAGER, " %-*s|",(int) min(field->max_length, + uint name_length= (uint) strlen(field->name); + uint numcells= charset_info->cset->numcells(charset_info, + field->name, + field->name + name_length); + uint display_length= field->max_length + name_length - numcells; + tee_fprintf(PAGER, " %-*s|",(int) min(display_length, MAX_COLUMN_LENGTH), - field->name); + field->name); num_flag[off]= IS_NUM(field->type); } (void) tee_fputs("\n", PAGER); From 093792c6c24439bc81afb30899cd260674cb6f87 Mon Sep 17 00:00:00 2001 From: "bar@mysql.com" <> Date: Thu, 20 Apr 2006 15:09:01 +0500 Subject: [PATCH 2/4] Bug#9509: Optimizer: wrong result after AND with latin1_german2_ci comparisons Fixing part2 of this problem: AND didn't work well with utf8_czech_ci and utf8_lithianian_ci in some cases. The problem was because when during condition optimization field was replaced with a constant, the constant's collation and collation derivation was used later for comparison instead of the field collation and derivation, which led to non-equal new condition in some cases. This patch copies collation and derivation from the field being removed to the new constant, which makes comparison work using the same collation with the one which would be used if no condition optimization were done. In other words: where s1 < 'K' and s1 = 'Y'; was rewritten to: where 'Y' < 'K' and s1 = 'Y'; Now it's rewritten to: where 'Y' collate collation_of_s1 < 'K' and s1 = 'Y' (using derivation of s1) Note, the first problem of this bug (with latin1_german2_ci) was fixed earlier in 5.0 tree, in a separate changeset. --- mysql-test/r/ctype_utf8.result | 31 +++++++++++++++++++++++++++++++ mysql-test/t/ctype_utf8.test | 18 ++++++++++++++++++ sql/sql_select.cc | 4 ++++ 3 files changed, 53 insertions(+) diff --git a/mysql-test/r/ctype_utf8.result b/mysql-test/r/ctype_utf8.result index 69d7577ee77..615b0ae2d52 100644 --- a/mysql-test/r/ctype_utf8.result +++ b/mysql-test/r/ctype_utf8.result @@ -924,6 +924,37 @@ NULL select ifnull(NULL, _utf8'string'); ifnull(NULL, _utf8'string') string +set names utf8; +create table t1 (s1 char(5) character set utf8 collate utf8_lithuanian_ci); +insert into t1 values ('I'),('K'),('Y'); +select * from t1 where s1 < 'K' and s1 = 'Y'; +s1 +I +Y +select * from t1 where 'K' > s1 and s1 = 'Y'; +s1 +I +Y +drop table t1; +create table t1 (s1 char(5) character set utf8 collate utf8_czech_ci); +insert into t1 values ('c'),('d'),('h'),('ch'),('CH'),('cH'),('Ch'),('i'); +select * from t1 where s1 > 'd' and s1 = 'CH'; +s1 +ch +CH +Ch +select * from t1 where 'd' < s1 and s1 = 'CH'; +s1 +ch +CH +Ch +select * from t1 where s1 = 'cH' and s1 <> 'ch'; +s1 +cH +select * from t1 where 'cH' = s1 and s1 <> 'ch'; +s1 +cH +drop table t1; create table t1 (a varchar(255)) default character set utf8; insert into t1 values (1.0); drop table t1; diff --git a/mysql-test/t/ctype_utf8.test b/mysql-test/t/ctype_utf8.test index 5044f7979f1..159e8490f12 100644 --- a/mysql-test/t/ctype_utf8.test +++ b/mysql-test/t/ctype_utf8.test @@ -727,6 +727,24 @@ drop table t1; select repeat(_utf8'+',3) as h union select NULL; select ifnull(NULL, _utf8'string'); +# +# Bug#9509 Optimizer: wrong result after AND with comparisons +# +set names utf8; +create table t1 (s1 char(5) character set utf8 collate utf8_lithuanian_ci); +insert into t1 values ('I'),('K'),('Y'); +select * from t1 where s1 < 'K' and s1 = 'Y'; +select * from t1 where 'K' > s1 and s1 = 'Y'; +drop table t1; + +create table t1 (s1 char(5) character set utf8 collate utf8_czech_ci); +insert into t1 values ('c'),('d'),('h'),('ch'),('CH'),('cH'),('Ch'),('i'); +select * from t1 where s1 > 'd' and s1 = 'CH'; +select * from t1 where 'd' < s1 and s1 = 'CH'; +select * from t1 where s1 = 'cH' and s1 <> 'ch'; +select * from t1 where 'cH' = s1 and s1 <> 'ch'; +drop table t1; + # # Bug#10714: Inserting double value into utf8 column crashes server # diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 91fc808058f..caaace13c6f 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -4456,6 +4456,8 @@ change_cond_ref_to_const(THD *thd, I_List *save_list, left_item->collation.collation == value->collation.collation)) { Item *tmp=value->new_item(); + tmp->collation.set(right_item->collation); + if (tmp) { thd->change_item_tree(args + 1, tmp); @@ -4477,6 +4479,8 @@ change_cond_ref_to_const(THD *thd, I_List *save_list, right_item->collation.collation == value->collation.collation)) { Item *tmp=value->new_item(); + tmp->collation.set(left_item->collation); + if (tmp) { thd->change_item_tree(args, tmp); From c746c08af9287e43b8517307e32be258e0c6f1f1 Mon Sep 17 00:00:00 2001 From: "kroki/tomash@moonlight.intranet" <> Date: Mon, 14 Aug 2006 20:01:19 +0400 Subject: [PATCH 3/4] BUG#9678: Client library hangs after network communication failure Socket timeouts in client library were used only on Windows. The solution is to use socket timeouts in client library on all systems were they are supported. No test case is provided because it is impossible to simulate network failure in current test suit. --- sql/net_serv.cc | 2 +- vio/viosocket.c | 30 ++++++++++++++++++++++-------- 2 files changed, 23 insertions(+), 9 deletions(-) diff --git a/sql/net_serv.cc b/sql/net_serv.cc index 93fa7ac938c..08184537896 100644 --- a/sql/net_serv.cc +++ b/sql/net_serv.cc @@ -747,7 +747,7 @@ my_real_read(NET *net, ulong *complen) #endif /* EXTRA_DEBUG */ } #if defined(THREAD_SAFE_CLIENT) && !defined(MYSQL_SERVER) - if (vio_should_retry(net->vio)) + if (vio_errno(net->vio) == SOCKET_EINTR) { DBUG_PRINT("warning",("Interrupted read. Retrying...")); continue; diff --git a/vio/viosocket.c b/vio/viosocket.c index 8d4c2387632..847e036d3b2 100644 --- a/vio/viosocket.c +++ b/vio/viosocket.c @@ -333,16 +333,30 @@ my_bool vio_poll_read(Vio *vio,uint timeout) } -void vio_timeout(Vio *vio __attribute__((unused)), - uint which __attribute__((unused)), - uint timeout __attribute__((unused))) +void vio_timeout(Vio *vio, uint which, uint timeout) { +/* TODO: some action should be taken if socket timeouts are not supported. */ +#if defined(SO_SNDTIMEO) && defined(SO_RCVTIMEO) + #ifdef __WIN__ - ulong wait_timeout= (ulong) timeout * 1000; - (void) setsockopt(vio->sd, SOL_SOCKET, - which ? SO_SNDTIMEO : SO_RCVTIMEO, (char*) &wait_timeout, - sizeof(wait_timeout)); -#endif /* __WIN__ */ + + /* Windows expects time in milliseconds as int. */ + int wait_timeout= (int) timeout * 1000; + +#else /* ! __WIN__ */ + + /* POSIX specifies time as struct timeval. */ + struct timeval wait_timeout; + wait_timeout.tv_sec= timeout; + wait_timeout.tv_usec= 0; + +#endif /* ! __WIN__ */ + + /* TODO: return value should be checked. */ + (void) setsockopt(vio->sd, SOL_SOCKET, which ? SO_SNDTIMEO : SO_RCVTIMEO, + (char*) &wait_timeout, sizeof(wait_timeout)); + +#endif /* defined(SO_SNDTIMEO) && defined(SO_RCVTIMEO) */ } From 6660f98b6422fa949036bded702fd1a9da80352e Mon Sep 17 00:00:00 2001 From: "ramil/ram@mysql.com/myoffice.izhnet.ru" <> Date: Tue, 15 Aug 2006 15:24:07 +0500 Subject: [PATCH 4/4] Fix for bug #20695: Charset introducer overrides charset definition for column. - if there are two character set definitions in the column declaration, we replace the first one with the second one as we store both in the LEX->charset slot. Add a separate slot to the LEX structure to store underscore charset. - convert default values to the column charset of STRING, VARSTRING fields if necessary as well. --- mysql-test/r/ctype_recoding.result | 11 ++++++ mysql-test/t/ctype_recoding.test | 12 ++++++ sql/sql_lex.cc | 5 ++- sql/sql_lex.h | 2 +- sql/sql_table.cc | 63 ++++++++++++++++-------------- sql/sql_yacc.yy | 4 +- 6 files changed, 63 insertions(+), 34 deletions(-) diff --git a/mysql-test/r/ctype_recoding.result b/mysql-test/r/ctype_recoding.result index 0b5c6f8974c..2daa2d5ba0b 100644 --- a/mysql-test/r/ctype_recoding.result +++ b/mysql-test/r/ctype_recoding.result @@ -247,3 +247,14 @@ lpad(c1,3,' select rpad(c1,3,'ö'), rpad('ö',3,c1) from t1; rpad(c1,3,'ö') rpad('ö',3,c1) ßöö ößß +drop table t1; +set names koi8r; +create table t1(a char character set cp1251 default _koi8r 0xFF); +show create table t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `a` char(1) character set cp1251 default 'ÿ' +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +drop table t1; +create table t1(a char character set latin1 default _cp1251 0xFF); +ERROR 42000: Invalid default value for 'a' diff --git a/mysql-test/t/ctype_recoding.test b/mysql-test/t/ctype_recoding.test index 5648cea7fd3..ddaaa7b9e4f 100644 --- a/mysql-test/t/ctype_recoding.test +++ b/mysql-test/t/ctype_recoding.test @@ -186,5 +186,17 @@ select rpad(c1,3,' # TODO #select case c1 when 'ß' then 'ß' when 'ö' then 'ö' else 'c' end from t1; #select export_set(5,c1,'ö'), export_set(5,'ö',c1) from t1; +drop table t1; + +# +# Bug 20695: problem with field default value's character set +# + +set names koi8r; +create table t1(a char character set cp1251 default _koi8r 0xFF); +show create table t1; +drop table t1; +--error 1067 +create table t1(a char character set latin1 default _cp1251 0xFF); # End of 4.1 tests diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index fb9a765f12c..dfe406c351e 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -643,8 +643,9 @@ int yylex(void *arg, void *yythd) */ if ((yylval->lex_str.str[0]=='_') && - (lex->charset=get_charset_by_csname(yylval->lex_str.str+1, - MY_CS_PRIMARY,MYF(0)))) + (lex->underscore_charset= + get_charset_by_csname(yylval->lex_str.str + 1, + MY_CS_PRIMARY,MYF(0)))) return(UNDERSCORE_CHARSET); return(result_state); // IDENT or IDENT_QUOTED diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 35f02db6cf9..12f89202e2d 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -613,7 +613,7 @@ typedef struct st_lex LEX_USER *grant_user; gptr yacc_yyss,yacc_yyvs; THD *thd; - CHARSET_INFO *charset; + CHARSET_INFO *charset, *underscore_charset; List col_list; List ref_list; diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 631d2d89bbb..a5cb0d45664 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -516,6 +516,40 @@ int mysql_prepare_table(THD *thd, HA_CREATE_INFO *create_info, DBUG_RETURN(-1); } + /* + Convert the default value character + set into the column character set if necessary. + */ + if (sql_field->def && + savecs != sql_field->def->collation.collation && + (sql_field->sql_type == FIELD_TYPE_VAR_STRING || + sql_field->sql_type == FIELD_TYPE_STRING || + sql_field->sql_type == FIELD_TYPE_SET || + sql_field->sql_type == FIELD_TYPE_ENUM)) + { + Item_arena backup_arena; + bool need_to_change_arena= + !thd->current_arena->is_conventional_execution(); + if (need_to_change_arena) + { + /* Assert that we don't do that at every PS execute */ + DBUG_ASSERT(thd->current_arena->is_first_stmt_execute()); + thd->set_n_backup_item_arena(thd->current_arena, &backup_arena); + } + + sql_field->def= sql_field->def->safe_charset_converter(savecs); + + if (need_to_change_arena) + thd->restore_backup_item_arena(thd->current_arena, &backup_arena); + + if (sql_field->def == NULL) + { + /* Could not convert */ + my_error(ER_INVALID_DEFAULT, MYF(0), sql_field->field_name); + DBUG_RETURN(-1); + } + } + if (sql_field->sql_type == FIELD_TYPE_SET || sql_field->sql_type == FIELD_TYPE_ENUM) { @@ -580,35 +614,6 @@ int mysql_prepare_table(THD *thd, HA_CREATE_INFO *create_info, sql_field->interval_list.empty(); // Don't need interval_list anymore } - /* - Convert the default value from client character - set into the column character set if necessary. - */ - if (sql_field->def && cs != sql_field->def->collation.collation) - { - Item_arena backup_arena; - bool need_to_change_arena= - !thd->current_arena->is_conventional_execution(); - if (need_to_change_arena) - { - /* Asser that we don't do that at every PS execute */ - DBUG_ASSERT(thd->current_arena->is_first_stmt_execute()); - thd->set_n_backup_item_arena(thd->current_arena, &backup_arena); - } - - sql_field->def= sql_field->def->safe_charset_converter(cs); - - if (need_to_change_arena) - thd->restore_backup_item_arena(thd->current_arena, &backup_arena); - - if (sql_field->def == NULL) - { - /* Could not convert */ - my_error(ER_INVALID_DEFAULT, MYF(0), sql_field->field_name); - DBUG_RETURN(-1); - } - } - if (sql_field->sql_type == FIELD_TYPE_SET) { if (sql_field->def != NULL) diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 162b4183c84..efd83549312 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -4896,7 +4896,7 @@ text_literal: | NCHAR_STRING { $$= new Item_string($1.str,$1.length,national_charset_info); } | UNDERSCORE_CHARSET TEXT_STRING - { $$ = new Item_string($2.str,$2.length,Lex->charset); } + { $$ = new Item_string($2.str,$2.length,Lex->underscore_charset); } | text_literal TEXT_STRING_literal { ((Item_string*) $1)->append($2.str,$2.length); } ; @@ -4963,7 +4963,7 @@ literal: (String*) 0; $$= new Item_string(str ? str->ptr() : "", str ? str->length() : 0, - Lex->charset); + Lex->underscore_charset); } | DATE_SYM text_literal { $$ = $2; } | TIME_SYM text_literal { $$ = $2; }