diff --git a/mysql-test/r/ctype_mb.result b/mysql-test/r/ctype_mb.result new file mode 100644 index 00000000000..4b3b464dea1 --- /dev/null +++ b/mysql-test/r/ctype_mb.result @@ -0,0 +1,21 @@ +CREATE TABLE t1 SELECT _utf8'test' as c1, _utf8'тест' as c2; +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `c1` char(4) character set utf8 NOT NULL default '', + `c2` char(4) character set utf8 NOT NULL default '' +) TYPE=MyISAM CHARSET=latin1 +DELETE FROM t1; +ALTER TABLE t1 ADD c3 CHAR(4) CHARACTER SET utf8; +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `c1` char(4) character set utf8 NOT NULL default '', + `c2` char(4) character set utf8 NOT NULL default '', + `c3` char(4) character set utf8 default NULL +) TYPE=MyISAM CHARSET=latin1 +INSERT INTO t1 VALUES ('aaaabbbbccccdddd','aaaabbbbccccdddd','aaaabbbbccccdddd'); +SELECT * FROM t1; +c1 c2 c3 +aaaabbbbcccc aaaabbbbcccc aaaabbbbcccc +DROP TABLE t1; diff --git a/mysql-test/r/ctype_recoding.result b/mysql-test/r/ctype_recoding.result index b89a90cc6ba..624eab4cf0b 100644 --- a/mysql-test/r/ctype_recoding.result +++ b/mysql-test/r/ctype_recoding.result @@ -1,5 +1,19 @@ SET CHARACTER SET koi8r; DROP TABLE IF EXISTS ; +SET CHARACTER SET koi8r; +CREATE TABLE t1 (a CHAR(10) CHARACTER SET cp1251) SELECT _koi8r'' AS a; +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `a` char(10) character set cp1251 default NULL +) TYPE=MyISAM CHARSET=latin1 +SELECT a FROM t1; +a + +SELECT HEX(a) FROM t1; +HEX(a) +EFF0EEE1E0 +DROP TABLE t1; CREATE TABLE ( CHAR(32) CHARACTER SET koi8r NOT NULL diff --git a/mysql-test/r/func_system.result b/mysql-test/r/func_system.result index cde21ead33c..a52d5613c04 100644 --- a/mysql-test/r/func_system.result +++ b/mysql-test/r/func_system.result @@ -41,9 +41,9 @@ create table t1 (version char(40)) select database(), user(), version() as 'vers show create table t1; Table Create Table t1 CREATE TABLE `t1` ( - `database()` char(102) character set utf8 NOT NULL default '', - `user()` char(231) character set utf8 NOT NULL default '', - `version` char(40) character set utf8 default NULL + `database()` char(34) character set utf8 NOT NULL default '', + `user()` char(77) character set utf8 NOT NULL default '', + `version` char(40) default NULL ) TYPE=MyISAM CHARSET=latin1 drop table t1; select TRUE,FALSE,NULL; diff --git a/mysql-test/t/ctype_mb.test b/mysql-test/t/ctype_mb.test new file mode 100644 index 00000000000..5c3e67eec01 --- /dev/null +++ b/mysql-test/t/ctype_mb.test @@ -0,0 +1,8 @@ +CREATE TABLE t1 SELECT _utf8'test' as c1, _utf8'тест' as c2; +SHOW CREATE TABLE t1; +DELETE FROM t1; +ALTER TABLE t1 ADD c3 CHAR(4) CHARACTER SET utf8; +SHOW CREATE TABLE t1; +INSERT INTO t1 VALUES ('aaaabbbbccccdddd','aaaabbbbccccdddd','aaaabbbbccccdddd'); +SELECT * FROM t1; +DROP TABLE t1; diff --git a/mysql-test/t/ctype_recoding.test b/mysql-test/t/ctype_recoding.test index 8fa31b1f17b..25df9c0a86b 100644 --- a/mysql-test/t/ctype_recoding.test +++ b/mysql-test/t/ctype_recoding.test @@ -4,6 +4,13 @@ SET CHARACTER SET koi8r; DROP TABLE IF EXISTS ; --enable_warnings +SET CHARACTER SET koi8r; +CREATE TABLE t1 (a CHAR(10) CHARACTER SET cp1251) SELECT _koi8r'' AS a; +SHOW CREATE TABLE t1; +SELECT a FROM t1; +SELECT HEX(a) FROM t1; +DROP TABLE t1; + CREATE TABLE ( CHAR(32) CHARACTER SET koi8r NOT NULL diff --git a/sql/field.cc b/sql/field.cc index 2f89dd43c3f..c25f1170b00 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -4012,7 +4012,7 @@ void Field_string::sql_type(String &res) const (table->db_options_in_use & HA_OPTION_PACK_RECORD) ? "varchar" : "char"), - (int) field_length); + (int) field_length / charset()->mbmaxlen); res.length(length); } @@ -4178,7 +4178,7 @@ void Field_varstring::sql_type(String &res) const CHARSET_INFO *cs=res.charset(); ulong length= cs->cset->snprintf(cs,(char*) res.ptr(), res.alloced_length(),"varchar(%u)", - field_length); + field_length / charset()->mbmaxlen); res.length(length); } @@ -5267,6 +5267,26 @@ bool Field_num::eq_def(Field *field) ** Handling of field and create_field *****************************************************************************/ +void create_field::create_length_to_internal_length(void) +{ + switch (sql_type) + { + case MYSQL_TYPE_TINY_BLOB: + case MYSQL_TYPE_MEDIUM_BLOB: + case MYSQL_TYPE_LONG_BLOB: + case MYSQL_TYPE_BLOB: + case MYSQL_TYPE_VAR_STRING: + case MYSQL_TYPE_STRING: + length*= charset->mbmaxlen; + pack_length= calc_pack_length(sql_type == FIELD_TYPE_VAR_STRING ? + FIELD_TYPE_STRING : sql_type, length); + break; + default: + /* do nothing */ + break; + } +} + /* Make a field from the .frm file info */ diff --git a/sql/field.h b/sql/field.h index 79c45a99a43..794b4a89542 100644 --- a/sql/field.h +++ b/sql/field.h @@ -1066,6 +1066,7 @@ public: uint offset,pack_flag; create_field() :after(0) {} create_field(Field *field, Field *orig_field); + void create_length_to_internal_length(void); }; diff --git a/sql/item.h b/sql/item.h index 621dc017d25..3412b43da44 100644 --- a/sql/item.h +++ b/sql/item.h @@ -419,7 +419,7 @@ public: { collation.set(cs, dv); str_value.set(str,length,cs); - max_length=length; + max_length= str_value.numchars()*cs->mbmaxlen; set_name(str, length, cs); decimals=NOT_FIXED_DEC; } @@ -428,7 +428,7 @@ public: { collation.set(cs, dv); str_value.set(str,length,cs); - max_length=length; + max_length= str_value.numchars()*cs->mbmaxlen; set_name(name_par,0,cs); decimals=NOT_FIXED_DEC; } diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 5880a9d4c8a..ed68f487924 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -2078,10 +2078,20 @@ mysql_execute_command(THD *thd) res= mysql_create_like_table(thd, tables, &lex->create_info, (Table_ident *)lex->name); else + { + List_iterator fields(lex->create_list); + create_field *field; + while ((field= fields++)) + { + if (!field->charset) + field->charset= lex->create_info.table_charset; + field->create_length_to_internal_length(); + } res= mysql_create_table(thd,tables->db ? tables->db : thd->db, tables->real_name, &lex->create_info, lex->create_list, lex->key_list,0,0,0); // do logging + } if (!res) send_ok(thd); } @@ -3791,7 +3801,7 @@ bool add_field_to_list(THD *thd, char *field_name, enum_field_types type, break; case FIELD_TYPE_STRING: case FIELD_TYPE_VAR_STRING: - if (new_field->length < MAX_FIELD_WIDTH || default_value) + if (new_field->length <= MAX_FIELD_CHARLENGTH || default_value) break; /* Convert long CHAR() and VARCHAR columns to TEXT or BLOB */ new_field->sql_type= FIELD_TYPE_BLOB; @@ -3957,13 +3967,13 @@ bool add_field_to_list(THD *thd, char *field_name, enum_field_types type, } } - if (new_field->length >= MAX_FIELD_WIDTH || + if (new_field->length > MAX_FIELD_CHARLENGTH || (!new_field->length && !(new_field->flags & BLOB_FLAG) && type != FIELD_TYPE_STRING && type != FIELD_TYPE_VAR_STRING && type != FIELD_TYPE_GEOMETRY)) { net_printf(thd,ER_TOO_BIG_FIELDLENGTH,field_name, - MAX_FIELD_WIDTH-1); /* purecov: inspected */ + MAX_FIELD_CHARLENGTH); /* purecov: inspected */ DBUG_RETURN(1); /* purecov: inspected */ } type_modifier&= AUTO_INCREMENT_FLAG; diff --git a/sql/sql_table.cc b/sql/sql_table.cc index e16d7a0067d..7cb8dfaae0d 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -457,12 +457,14 @@ int mysql_create_table(THD *thd,const char *db, const char *table_name, else { /* Field redefined */ + sql_field->sql_type= dup_field->sql_type; + sql_field->charset= dup_field->charset ? dup_field->charset : create_info->table_charset; sql_field->length= dup_field->length; + sql_field->pack_length= dup_field->pack_length; + sql_field->create_length_to_internal_length(); sql_field->decimals= dup_field->decimals; sql_field->flags= dup_field->flags; - sql_field->pack_length= dup_field->pack_length; sql_field->unireg_check= dup_field->unireg_check; - sql_field->sql_type= dup_field->sql_type; it2.remove(); // Remove first (create) definition select_field_pos--; break; @@ -480,10 +482,7 @@ int mysql_create_table(THD *thd,const char *db, const char *table_name, while ((sql_field=it++)) { if (!sql_field->charset) - sql_field->charset = create_info->table_charset ? - create_info->table_charset : - thd->variables.character_set_database; - + sql_field->charset = create_info->table_charset; switch (sql_field->sql_type) { case FIELD_TYPE_BLOB: case FIELD_TYPE_MEDIUM_BLOB: @@ -1891,18 +1890,42 @@ int mysql_alter_table(THD *thd,char *new_db, char *new_name, } /* Full alter table */ + + /* let new create options override the old ones */ + if (!(used_fields & HA_CREATE_USED_MIN_ROWS)) + create_info->min_rows=table->min_rows; + if (!(used_fields & HA_CREATE_USED_MAX_ROWS)) + create_info->max_rows=table->max_rows; + if (!(used_fields & HA_CREATE_USED_AVG_ROW_LENGTH)) + create_info->avg_row_length=table->avg_row_length; + if (!(used_fields & HA_CREATE_USED_CHARSET)) + create_info->table_charset=table->table_charset; + restore_record(table,default_values); // Empty record for DEFAULT List_iterator drop_it(drop_list); List_iterator def_it(fields); List_iterator alter_it(alter_list); List create_list; // Add new fields here List key_list; // Add new keys here + create_field *def; + + /* + For each column set charset to the table + default if the column charset hasn't been specified + explicitely. Change CREATE length into internal length + */ + def_it.rewind(); + while ((def= def_it++)) + { + if (!def->charset) + def->charset= create_info->table_charset; + def->create_length_to_internal_length(); + } /* First collect all fields from table which isn't in drop_list */ - create_field *def; Field **f_ptr,*field; for (f_ptr=table->field ; (field= *f_ptr) ; f_ptr++) { @@ -2121,16 +2144,6 @@ int mysql_alter_table(THD *thd,char *new_db, char *new_name, if (!create_info->comment) create_info->comment=table->comment; - /* let new create options override the old ones */ - if (!(used_fields & HA_CREATE_USED_MIN_ROWS)) - create_info->min_rows=table->min_rows; - if (!(used_fields & HA_CREATE_USED_MAX_ROWS)) - create_info->max_rows=table->max_rows; - if (!(used_fields & HA_CREATE_USED_AVG_ROW_LENGTH)) - create_info->avg_row_length=table->avg_row_length; - if (!(used_fields & HA_CREATE_USED_CHARSET)) - create_info->table_charset=table->table_charset; - table->file->update_create_info(create_info); if ((create_info->table_options & (HA_OPTION_PACK_KEYS | HA_OPTION_NO_PACK_KEYS)) || diff --git a/sql/unireg.h b/sql/unireg.h index 4bbfa8b0fae..4920d4b609a 100644 --- a/sql/unireg.h +++ b/sql/unireg.h @@ -58,7 +58,12 @@ #endif #define MAX_HOSTNAME 61 /* len+1 in mysql.user */ -#define MAX_FIELD_WIDTH 256 /* Max column width +1 */ +#define MAX_MBWIDTH 3 /* Max multibyte sequence */ +#define MAX_FIELD_CHARLENGTH 255 +/* Max column width +1 */ +#define MAX_FIELD_WIDTH (MAX_FIELD_CHARLENGTH*MAX_MBWIDTH+1) + + #define MAX_TABLES (sizeof(table_map)*8-2) /* Max tables in join */ #define OUTER_REF_TABLE_BIT (((table_map) 1) << (sizeof(table_map)*8-2)) #define RAND_TABLE_BIT (((table_map) 1) << (sizeof(table_map)*8-1))