summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--mysql-test/r/cast.result54
-rw-r--r--mysql-test/t/cast.test41
-rw-r--r--sql/item_create.cc10
-rw-r--r--sql/item_create.h2
-rw-r--r--sql/item_timefunc.cc53
-rw-r--r--sql/item_timefunc.h16
-rw-r--r--sql/sql_yacc.yy41
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: