diff options
-rw-r--r-- | mysql-test/r/cast.result | 54 | ||||
-rw-r--r-- | mysql-test/t/cast.test | 41 | ||||
-rw-r--r-- | sql/item_create.cc | 10 | ||||
-rw-r--r-- | sql/item_create.h | 2 | ||||
-rw-r--r-- | sql/item_timefunc.cc | 53 | ||||
-rw-r--r-- | sql/item_timefunc.h | 16 | ||||
-rw-r--r-- | sql/sql_yacc.yy | 41 |
7 files changed, 192 insertions, 25 deletions
diff --git a/mysql-test/r/cast.result b/mysql-test/r/cast.result index 5d185bee005..d604a17d270 100644 --- a/mysql-test/r/cast.result +++ b/mysql-test/r/cast.result @@ -42,6 +42,60 @@ t1 CREATE TABLE `t1` ( `t` char(4) character set cp1251 NOT NULL default '' ) TYPE=MyISAM CHARSET=latin1 drop table t1; +select +cast(_latin1'ab' AS char) as c1, +cast(_latin1'a ' AS char) as c2, +cast(_latin1'abc' AS char(2)) as c3, +cast(_latin1'a ' AS char(2)) as c4, +cast(_latin1'a' AS char(2)) as c5; +c1 c2 c3 c4 c5 +ab a ab a a +create table t1 select +cast(_latin1'ab' AS char) as c1, +cast(_latin1'a ' AS char) as c2, +cast(_latin1'abc' AS char(2)) as c3, +cast(_latin1'a ' AS char(2)) as c4, +cast(_latin1'a' AS char(2)) as c5; +select * from t1; +c1 c2 c3 c4 c5 +ab a ab a a +show create table t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `c1` char(2) binary NOT NULL default '', + `c2` char(2) binary NOT NULL default '', + `c3` char(2) binary NOT NULL default '', + `c4` char(2) binary NOT NULL default '', + `c5` char(2) binary NOT NULL default '' +) TYPE=MyISAM CHARSET=latin1 +drop table t1; +select +cast(_koi8r'ÆÇ' AS nchar) as c1, +cast(_koi8r'Æ ' AS nchar) as c2, +cast(_koi8r'ÆÇÈ' AS nchar(2)) as c3, +cast(_koi8r'Æ ' AS nchar(2)) as c4, +cast(_koi8r'Æ' AS nchar(2)) as c5; +c1 c2 c3 c4 c5 +фг Ñ„ фг Ñ„ Ñ„ +create table t1 select +cast(_koi8r'ÆÇ' AS nchar) as c1, +cast(_koi8r'Æ ' AS nchar) as c2, +cast(_koi8r'ÆÇÈ' AS nchar(2)) as c3, +cast(_koi8r'Æ ' AS nchar(2)) as c4, +cast(_koi8r'Æ' AS nchar(2)) as c5; +select * from t1; +c1 c2 c3 c4 c5 +фг Ñ„ фг Ñ„ Ñ„ +show create table t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `c1` char(2) character set utf8 NOT NULL default '', + `c2` char(2) character set utf8 NOT NULL default '', + `c3` char(2) character set utf8 NOT NULL default '', + `c4` char(2) character set utf8 NOT NULL default '', + `c5` char(2) character set utf8 NOT NULL default '' +) TYPE=MyISAM CHARSET=latin1 +drop table t1; select cast("2001-1-1" as date) = "2001-01-01"; cast("2001-1-1" as date) = "2001-01-01" 1 diff --git a/mysql-test/t/cast.test b/mysql-test/t/cast.test index 8241d491465..b3ae8981671 100644 --- a/mysql-test/t/cast.test +++ b/mysql-test/t/cast.test @@ -24,6 +24,47 @@ show create table t1; drop table t1; # +# CAST to CHAR with/without length +# +select + cast(_latin1'ab' AS char) as c1, + cast(_latin1'a ' AS char) as c2, + cast(_latin1'abc' AS char(2)) as c3, + cast(_latin1'a ' AS char(2)) as c4, + cast(_latin1'a' AS char(2)) as c5; + +create table t1 select + cast(_latin1'ab' AS char) as c1, + cast(_latin1'a ' AS char) as c2, + cast(_latin1'abc' AS char(2)) as c3, + cast(_latin1'a ' AS char(2)) as c4, + cast(_latin1'a' AS char(2)) as c5; +select * from t1; +show create table t1; +drop table t1; + +# +# CAST to NCHAR with/without length +# +select + cast(_koi8r'ÆÇ' AS nchar) as c1, + cast(_koi8r'Æ ' AS nchar) as c2, + cast(_koi8r'ÆÇÈ' AS nchar(2)) as c3, + cast(_koi8r'Æ ' AS nchar(2)) as c4, + cast(_koi8r'Æ' AS nchar(2)) as c5; + +create table t1 select + cast(_koi8r'ÆÇ' AS nchar) as c1, + cast(_koi8r'Æ ' AS nchar) as c2, + cast(_koi8r'ÆÇÈ' AS nchar(2)) as c3, + cast(_koi8r'Æ ' AS nchar(2)) as c4, + cast(_koi8r'Æ' AS nchar(2)) as c5; +select * from t1; +show create table t1; +drop table t1; + + +# # The following should be fixed in 4.1 # diff --git a/sql/item_create.cc b/sql/item_create.cc index e18d1cfa189..e3e3c021a1e 100644 --- a/sql/item_create.cc +++ b/sql/item_create.cc @@ -470,21 +470,21 @@ Item *create_load_file(Item* a) } -Item *create_func_cast(Item *a, Cast_target cast_type, CHARSET_INFO *cs) +Item *create_func_cast(Item *a, Cast_target cast_type, int len, CHARSET_INFO *cs) { Item *res; LINT_INIT(res); switch (cast_type) { case ITEM_CAST_BINARY: res= new Item_func_binary(a); break; - case ITEM_CAST_CHAR: - res= (cs == NULL) ? (Item*) new Item_char_typecast(a) : - (Item*) new Item_func_conv_charset(a,cs); - break; case ITEM_CAST_SIGNED_INT: res= new Item_func_signed(a); break; case ITEM_CAST_UNSIGNED_INT: res= new Item_func_unsigned(a); break; case ITEM_CAST_DATE: res= new Item_date_typecast(a); break; case ITEM_CAST_TIME: res= new Item_time_typecast(a); break; case ITEM_CAST_DATETIME: res= new Item_datetime_typecast(a); break; + case ITEM_CAST_CHAR: + res= new Item_char_typecast(a, len, cs ? cs : + current_thd->variables.collation_connection); + break; } return res; } diff --git a/sql/item_create.h b/sql/item_create.h index 1326077b096..16c4fccf709 100644 --- a/sql/item_create.h +++ b/sql/item_create.h @@ -28,7 +28,7 @@ Item *create_func_bit_length(Item* a); Item *create_func_coercibility(Item* a); Item *create_func_ceiling(Item* a); Item *create_func_char_length(Item* a); -Item *create_func_cast(Item *a, Cast_target cast_type, CHARSET_INFO *cs); +Item *create_func_cast(Item *a, Cast_target cast_type, int len, CHARSET_INFO *cs); Item *create_func_connection_id(void); Item *create_func_conv(Item* a, Item *b, Item *c); Item *create_func_cos(Item* a); diff --git a/sql/item_timefunc.cc b/sql/item_timefunc.cc index 6ddafd40755..31ce2ad9cdc 100644 --- a/sql/item_timefunc.cc +++ b/sql/item_timefunc.cc @@ -1474,6 +1474,59 @@ void Item_typecast::print(String *str) str->append(')'); } +String *Item_char_typecast::val_str(String *str) +{ + String *res, *res1; + uint32 length; + + if (!charset_conversion && !(res= args[0]->val_str(str))) + { + null_value= 1; + return 0; + } + else + { + // Convert character set if differ + if (!(res1= args[0]->val_str(&tmp_value)) || + str->copy(res1->ptr(), res1->length(),res1->charset(), cast_cs)) + { + null_value= 1; + return 0; + } + res= str; + } + + /* + Cut the tail if cast with length + and the result is longer than cast length, e.g. + CAST('string' AS CHAR(1)) + */ + if (cast_length >= 0 && + (res->length() > (length= (uint32) res->charpos(cast_length)))) + { // Safe even if const arg + if (!res->alloced_length()) + { // Don't change const str + str_value= *res; // Not malloced string + res= &str_value; + } + res->length((uint) length); + } + null_value= 0; + return res; +} + +void Item_char_typecast::fix_length_and_dec() +{ + uint32 char_length; + charset_conversion= !my_charset_same(args[0]->collation.collation, cast_cs) && + args[0]->collation.collation != &my_charset_bin && + cast_cs != &my_charset_bin; + collation.set(cast_cs, DERIVATION_IMPLICIT); + char_length= (cast_length >= 0) ? cast_length : + args[0]->max_length/args[0]->collation.collation->mbmaxlen; + max_length= char_length * cast_cs->mbmaxlen; +} + String *Item_datetime_typecast::val_str(String *str) { TIME ltime; diff --git a/sql/item_timefunc.h b/sql/item_timefunc.h index 29cfad2e095..d84267a5066 100644 --- a/sql/item_timefunc.h +++ b/sql/item_timefunc.h @@ -614,13 +614,15 @@ public: class Item_char_typecast :public Item_typecast { -public: - Item_char_typecast(Item *a) :Item_typecast(a) {} - void fix_length_and_dec() - { - collation.set(default_charset()); - max_length=args[0]->max_length; - } + int cast_length; + CHARSET_INFO *cast_cs; + bool charset_conversion; + String tmp_value; +public: + Item_char_typecast(Item *a, int length_arg, CHARSET_INFO *cs_arg) + :Item_typecast(a), cast_length(length_arg), cast_cs(cs_arg) {} + String *val_str(String *a); + void fix_length_and_dec(); }; diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index e27279c148f..539bb90d907 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -642,7 +642,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize); %type <ha_rkey_mode> handler_rkey_mode -%type <cast_type> cast_type +%type <cast_type> cast_type cast_type_finalize %type <udf_type> udf_func_type @@ -2355,11 +2355,19 @@ simple_expr: 6, &my_charset_latin1)); } | CAST_SYM '(' expr AS cast_type ')' - { $$= create_func_cast($3, $5, Lex->charset); } + { + $$= create_func_cast($3, $5, + Lex->length ? atoi(Lex->length) : -1, + Lex->charset); + } | CASE_SYM opt_expr WHEN_SYM when_list opt_else END { $$= new Item_func_case(* $4, $2, $5 ); } | CONVERT_SYM '(' expr ',' cast_type ')' - { $$= create_func_cast($3, $5, Lex->charset); } + { + $$= create_func_cast($3, $5, + Lex->length ? atoi(Lex->length) : -1, + Lex->charset); + } | CONVERT_SYM '(' expr USING charset_name ')' { $$= new Item_func_conv_charset($3,$5); } | CONVERT_SYM '(' expr ',' expr ',' expr ')' @@ -2800,16 +2808,25 @@ in_sum_expr: $$= $3; }; +cast_type_init: + { Lex->charset= NULL; Lex->length= (char*)0; } + ; + +cast_type_finalize: + BINARY { $$=ITEM_CAST_BINARY; } + | CHAR_SYM opt_len opt_binary { $$=ITEM_CAST_CHAR; } + | NCHAR_SYM opt_len { $$=ITEM_CAST_CHAR; Lex->charset= national_charset_info; } + | SIGNED_SYM { $$=ITEM_CAST_SIGNED_INT; } + | SIGNED_SYM INT_SYM { $$=ITEM_CAST_SIGNED_INT; } + | UNSIGNED { $$=ITEM_CAST_UNSIGNED_INT; } + | UNSIGNED INT_SYM { $$=ITEM_CAST_UNSIGNED_INT; } + | DATE_SYM { $$=ITEM_CAST_DATE; } + | TIME_SYM { $$=ITEM_CAST_TIME; } + | DATETIME { $$=ITEM_CAST_DATETIME; } + ; + cast_type: - BINARY { $$=ITEM_CAST_BINARY; Lex->charset= NULL; } - | CHAR_SYM opt_binary { $$=ITEM_CAST_CHAR; } - | SIGNED_SYM { $$=ITEM_CAST_SIGNED_INT; Lex->charset= NULL; } - | SIGNED_SYM INT_SYM { $$=ITEM_CAST_SIGNED_INT; Lex->charset= NULL; } - | UNSIGNED { $$=ITEM_CAST_UNSIGNED_INT; Lex->charset= NULL; } - | UNSIGNED INT_SYM { $$=ITEM_CAST_UNSIGNED_INT; Lex->charset= NULL; } - | DATE_SYM { $$=ITEM_CAST_DATE; Lex->charset= NULL; } - | TIME_SYM { $$=ITEM_CAST_TIME; Lex->charset= NULL; } - | DATETIME { $$=ITEM_CAST_DATETIME; Lex->charset= NULL; } + cast_type_init cast_type_finalize { $$= $2; } ; expr_list: |