From eeaa3adb036085b6f411c2b5f18db70e2e8c6977 Mon Sep 17 00:00:00 2001 From: "monty@mysql.com/nosik.monty.fi" <> Date: Fri, 27 Apr 2007 01:12:09 +0300 Subject: [PATCH] Added sql_mode PAD_CHAR_TO_FULL_LENGTH (WL#921) This pads the value of CHAR columns with spaces up to full column length (according to ANSI) It's not makde part of oracle or ansi mode yet, as this would cause a notable behaviour change. Added uuid_short(), a generator for increasing 'unique' longlong integers (8 bytes) --- mysql-test/r/func_misc.result | 3 +++ mysql-test/r/sql_mode.result | 19 ++++++++++++++++-- mysql-test/t/func_misc.test | 4 ++++ mysql-test/t/sql_mode.test | 13 +++++++++++- sql/field.cc | 7 ++++++- sql/item.cc | 1 + sql/item_create.cc | 24 +++++++++++++++++++++++ sql/item_func.cc | 37 +++++++++++++++++++++++++++++++++++ sql/item_func.h | 15 ++++++++++++++ sql/mysql_priv.h | 2 ++ sql/mysqld.cc | 6 +++++- 11 files changed, 126 insertions(+), 5 deletions(-) diff --git a/mysql-test/r/func_misc.result b/mysql-test/r/func_misc.result index b4c6f0f6699..7526134c476 100644 --- a/mysql-test/r/func_misc.result +++ b/mysql-test/r/func_misc.result @@ -22,6 +22,9 @@ hex(inet_aton('127.1.1')) select length(uuid()), charset(uuid()), length(unhex(replace(uuid(),_utf8'-',_utf8''))); length(uuid()) charset(uuid()) length(unhex(replace(uuid(),_utf8'-',_utf8''))) 36 utf8 16 +select cast(uuid_short()-uuid_short() as signed); +cast(uuid_short()-uuid_short() as signed) +-1 select length(format('nan', 2)) > 0; length(format('nan', 2)) > 0 1 diff --git a/mysql-test/r/sql_mode.result b/mysql-test/r/sql_mode.result index c7be653ca2e..9998a51fdc8 100644 --- a/mysql-test/r/sql_mode.result +++ b/mysql-test/r/sql_mode.result @@ -475,9 +475,24 @@ set sql_mode=16384+(65536*4); select @@sql_mode; @@sql_mode REAL_AS_FLOAT,PIPES_AS_CONCAT,ANSI_QUOTES,IGNORE_SPACE,NO_TABLE_OPTIONS,ANSI -set sql_mode=2147483648; -ERROR 42000: Variable 'sql_mode' can't be set to the value of '2147483648' +set sql_mode=2147483648*2; +ERROR 42000: Variable 'sql_mode' can't be set to the value of '4294967296' select @@sql_mode; @@sql_mode REAL_AS_FLOAT,PIPES_AS_CONCAT,ANSI_QUOTES,IGNORE_SPACE,NO_TABLE_OPTIONS,ANSI +set sql_mode=PAD_CHAR_TO_FULL_LENGTH; +create table t1 (a int auto_increment primary key, b char(5)); +insert into t1 (b) values('a'),('b\t'),('c '); +select concat('x',b,'x') from t1; +concat('x',b,'x') +xa x +xb x +xc x +set sql_mode=0; +select concat('x',b,'x') from t1; +concat('x',b,'x') +xax +xb x +xcx +drop table t1; SET @@SQL_MODE=@OLD_SQL_MODE; diff --git a/mysql-test/t/func_misc.test b/mysql-test/t/func_misc.test index 8ff62f68e45..7437b0008e4 100644 --- a/mysql-test/t/func_misc.test +++ b/mysql-test/t/func_misc.test @@ -14,6 +14,10 @@ select hex(inet_aton('127.1.1')); select length(uuid()), charset(uuid()), length(unhex(replace(uuid(),_utf8'-',_utf8''))); +# As we can assume we are the only user for the mysqld server, the difference +# between two calls should be -1 +select cast(uuid_short()-uuid_short() as signed); + # # Test for core dump with nan # diff --git a/mysql-test/t/sql_mode.test b/mysql-test/t/sql_mode.test index 8542c2b7927..7a9036c8621 100644 --- a/mysql-test/t/sql_mode.test +++ b/mysql-test/t/sql_mode.test @@ -264,7 +264,18 @@ select @@sql_mode; set sql_mode=16384+(65536*4); select @@sql_mode; --error 1231 -set sql_mode=2147483648; # that mode does not exist +set sql_mode=2147483648*2; # that mode does not exist select @@sql_mode; +# +# Test WL921: Retain spaces when retrieving CHAR column values + +set sql_mode=PAD_CHAR_TO_FULL_LENGTH; +create table t1 (a int auto_increment primary key, b char(5)); +insert into t1 (b) values('a'),('b\t'),('c '); +select concat('x',b,'x') from t1; +set sql_mode=0; +select concat('x',b,'x') from t1; +drop table t1; + SET @@SQL_MODE=@OLD_SQL_MODE; diff --git a/sql/field.cc b/sql/field.cc index 82f8283ba56..eeaad01f094 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -6187,9 +6187,14 @@ String *Field_string::val_str(String *val_buffer __attribute__((unused)), String *val_ptr) { ASSERT_COLUMN_MARKED_FOR_READ; - uint length= field_charset->cset->lengthsp(field_charset, ptr, field_length); /* See the comment for Field_long::store(long long) */ DBUG_ASSERT(table->in_use == current_thd); + uint length; + if (table->in_use->variables.sql_mode & + MODE_PAD_CHAR_TO_FULL_LENGTH) + length= my_charpos(field_charset, ptr, ptr + field_length, field_length); + else + length= field_charset->cset->lengthsp(field_charset, ptr, field_length); val_ptr->set((const char*) ptr, length, field_charset); return val_ptr; } diff --git a/sql/item.cc b/sql/item.cc index bd5e0ae1a8f..3d522f54d5d 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -161,6 +161,7 @@ Hybrid_type_traits_integer::fix_length_and_dec(Item *item, Item *arg) const void item_init(void) { item_user_lock_init(); + uuid_short_init(); } diff --git a/sql/item_create.cc b/sql/item_create.cc index 8ff78ef1b48..8c97b596336 100644 --- a/sql/item_create.cc +++ b/sql/item_create.cc @@ -2141,6 +2141,19 @@ protected: }; +class Create_func_uuid_short : public Create_func_arg0 +{ +public: + virtual Item *create(THD *thd); + + static Create_func_uuid_short s_singleton; + +protected: + Create_func_uuid_short() {} + virtual ~Create_func_uuid_short() {} +}; + + class Create_func_version : public Create_func_arg0 { public: @@ -4532,6 +4545,16 @@ Create_func_uuid::create(THD *thd) } +Create_func_uuid_short Create_func_uuid_short::s_singleton; + +Item* +Create_func_uuid_short::create(THD *thd) +{ + thd->lex->binlog_row_based_if_mixed= TRUE; + return new (thd->mem_root) Item_func_uuid_short(); +} + + Create_func_version Create_func_version::s_singleton; Item* @@ -4871,6 +4894,7 @@ static Native_func_registry func_array[] = { C_STRING_WITH_LEN("UPDATEXML"), BUILDER(Create_func_xml_update)}, { C_STRING_WITH_LEN("UPPER"), BUILDER(Create_func_ucase)}, { C_STRING_WITH_LEN("UUID"), BUILDER(Create_func_uuid)}, + { C_STRING_WITH_LEN("UUID_SHORT"), BUILDER(Create_func_uuid_short)}, { C_STRING_WITH_LEN("VERSION"), BUILDER(Create_func_version)}, { C_STRING_WITH_LEN("WEEKDAY"), BUILDER(Create_func_weekday)}, { C_STRING_WITH_LEN("WEEKOFYEAR"), BUILDER(Create_func_weekofyear)}, diff --git a/sql/item_func.cc b/sql/item_func.cc index c4c35a990fb..63253704748 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -5358,3 +5358,40 @@ Item_func_sp::fix_fields(THD *thd, Item **ref) } DBUG_RETURN(res); } + + +/* + uuid_short handling. + + The short uuid is defined as a longlong that contains the following bytes: + + Bytes Comment + 1 Server_id & 255 + 4 Startup time of server in seconds + 3 Incrementor + + This means that an uuid is guaranteed to be unique + even in a replication environment if the following holds: + + - The last byte of the server id is unique + - If you between two shutdown of the server don't get more than + an average of 2^24 = 16M calls to uuid_short() per second. +*/ + +ulonglong uuid_value; + +void uuid_short_init() +{ + uuid_value= (((ulonglong) server_id << 56) + + (ulonglong) server_start_time << 24); +} + + +longlong Item_func_uuid_short::val_int() +{ + ulonglong val; + pthread_mutex_lock(&LOCK_uuid_generator); + val= uuid_value++; + pthread_mutex_unlock(&LOCK_uuid_generator); + return (longlong) val; +} diff --git a/sql/item_func.h b/sql/item_func.h index 28f11e27306..38241e0ac7b 100644 --- a/sql/item_func.h +++ b/sql/item_func.h @@ -1528,3 +1528,18 @@ public: const char *func_name() const { return "found_rows"; } void fix_length_and_dec() { decimals= 0; maybe_null=0; } }; + + +void uuid_short_init(); + +class Item_func_uuid_short :public Item_int_func +{ +public: + Item_func_uuid_short() :Item_int_func() {} + const char *func_name() const { return "uuid_short"; } + longlong val_int(); + void fix_length_and_dec() + { max_length= 21; unsigned_flag=1; } + bool check_partition_func_processor(byte *int_arg) {return FALSE;} +}; + diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index 3a01ed27f61..51396cc4219 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -401,6 +401,8 @@ MY_LOCALE *my_locale_by_number(uint number); #define MODE_NO_AUTO_CREATE_USER (MODE_TRADITIONAL*2) #define MODE_HIGH_NOT_PRECEDENCE (MODE_NO_AUTO_CREATE_USER*2) #define MODE_NO_ENGINE_SUBSTITUTION (MODE_HIGH_NOT_PRECEDENCE*2) +#define MODE_PAD_CHAR_TO_FULL_LENGTH (MODE_NO_ENGINE_SUBSTITUTION*2) + /* Replication uses 8 bytes to store SQL_MODE in the binary log. The day you use strictly more than 64 bits by adding one more define above, you should diff --git a/sql/mysqld.cc b/sql/mysqld.cc index b7ea52fbef6..face6570593 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -225,8 +225,10 @@ static const char *sql_mode_names[]= "ERROR_FOR_DIVISION_BY_ZERO", "TRADITIONAL", "NO_AUTO_CREATE_USER", "HIGH_NOT_PRECEDENCE", "NO_ENGINE_SUBSTITUTION", + "PAD_CHAR_TO_FULL_LENGTH", NullS }; + static const unsigned int sql_mode_names_len[]= { /*REAL_AS_FLOAT*/ 13, @@ -259,8 +261,10 @@ static const unsigned int sql_mode_names_len[]= /*TRADITIONAL*/ 11, /*NO_AUTO_CREATE_USER*/ 19, /*HIGH_NOT_PRECEDENCE*/ 19, - /*NO_ENGINE_SUBSTITUTION*/ 22 + /*NO_ENGINE_SUBSTITUTION*/ 22, + /*PAD_CHAR_TO_FULL_LENGTH*/ 23 }; + TYPELIB sql_mode_typelib= { array_elements(sql_mode_names)-1,"", sql_mode_names, (unsigned int *)sql_mode_names_len };