diff options
-rw-r--r-- | mysql-test/r/func_misc.result | 14 | ||||
-rw-r--r-- | mysql-test/t/func_misc.test | 19 | ||||
-rw-r--r-- | sql/item_strfunc.cc | 65 |
3 files changed, 86 insertions, 12 deletions
diff --git a/mysql-test/r/func_misc.result b/mysql-test/r/func_misc.result index c35c2eaccd7..a3df23138e8 100644 --- a/mysql-test/r/func_misc.result +++ b/mysql-test/r/func_misc.result @@ -310,6 +310,20 @@ drop table t1; SELECT NAME_CONST('var', 'value') COLLATE latin1_general_cs; NAME_CONST('var', 'value') COLLATE latin1_general_cs value +select @@session.time_zone into @save_tz; +set @@session.time_zone='UTC'; +select uuid() into @my_uuid; +select mid(@my_uuid,15,1); +mid(@my_uuid,15,1) +1 +select 24 * 60 * 60 * 1000 * 1000 * 10 into @my_uuid_one_day; +select concat('0',mid(@my_uuid,16,3),mid(@my_uuid,10,4),left(@my_uuid,8)) into @my_uuidate; +select floor(conv(@my_uuidate,16,10)/@my_uuid_one_day) into @my_uuid_date; +select 141427 + datediff(curdate(),'1970-01-01') into @my_uuid_synthetic; +select @my_uuid_date - @my_uuid_synthetic; +@my_uuid_date - @my_uuid_synthetic +0 +set @@session.time_zone=@save_tz; End of 5.0 tests select connection_id() > 0; connection_id() > 0 diff --git a/mysql-test/t/func_misc.test b/mysql-test/t/func_misc.test index 8e995c2da9c..61bf285c364 100644 --- a/mysql-test/t/func_misc.test +++ b/mysql-test/t/func_misc.test @@ -426,6 +426,25 @@ drop table t1; # SELECT NAME_CONST('var', 'value') COLLATE latin1_general_cs; +# +# Bug #35848: UUID() returns UUIDs with the wrong time +# +select @@session.time_zone into @save_tz; + +# make sure all times are UTC so the DayNr won't differ +set @@session.time_zone='UTC'; +select uuid() into @my_uuid; +# if version nibble isn't 1, the following calculations will be rubbish +select mid(@my_uuid,15,1); +select 24 * 60 * 60 * 1000 * 1000 * 10 into @my_uuid_one_day; +select concat('0',mid(@my_uuid,16,3),mid(@my_uuid,10,4),left(@my_uuid,8)) into @my_uuidate; +select floor(conv(@my_uuidate,16,10)/@my_uuid_one_day) into @my_uuid_date; +select 141427 + datediff(curdate(),'1970-01-01') into @my_uuid_synthetic; +# these should be identical; date part of UUID should be current date +select @my_uuid_date - @my_uuid_synthetic; + +set @@session.time_zone=@save_tz; + --echo End of 5.0 tests # diff --git a/sql/item_strfunc.cc b/sql/item_strfunc.cc index 56aa44b453e..fbfd21aa006 100644 --- a/sql/item_strfunc.cc +++ b/sql/item_strfunc.cc @@ -3370,7 +3370,8 @@ static char clock_seq_and_node_str[]="-0000-000000000000"; number of 100-nanosecond intervals between 1582-10-15 00:00:00.00 and 1970-01-01 00:00:00.00. */ -#define UUID_TIME_OFFSET ((ulonglong) 141427 * 24 * 60 * 60 * 1000 * 10 ) +#define UUID_TIME_OFFSET ((ulonglong) 141427 * 24 * 60 * 60 * \ + 1000 * 1000 * 10) #define UUID_VERSION 0x1000 #define UUID_VARIANT 0x8000 @@ -3429,24 +3430,64 @@ String *Item_func_uuid::val_str(String *str) set_clock_seq_str(); } - ulonglong tv=my_getsystime() + UUID_TIME_OFFSET + nanoseq; - if (unlikely(tv < uuid_time)) - set_clock_seq_str(); - else if (unlikely(tv == uuid_time)) + ulonglong tv= my_getsystime() + UUID_TIME_OFFSET + nanoseq; + + if (likely(tv > uuid_time)) { - /* special protection from low-res system clocks */ - nanoseq++; - tv++; + /* + Current time is ahead of last timestamp, as it should be. + If we "borrowed time", give it back, just as long as we + stay ahead of the previous timestamp. + */ + if (nanoseq) + { + DBUG_ASSERT((tv > uuid_time) && (nanoseq > 0)); + /* + -1 so we won't make tv= uuid_time for nanoseq >= (tv - uuid_time) + */ + long delta= min(nanoseq, tv - uuid_time -1); + tv-= delta; + nanoseq-= delta; + } } else { - if (nanoseq) + if (unlikely(tv == uuid_time)) { - tv-=nanoseq; - nanoseq=0; + /* + For low-res system clocks. If several requests for UUIDs + end up on the same tick, we add a nano-second to make them + different. + ( current_timestamp + nanoseq * calls_in_this_period ) + may end up > next_timestamp; this is OK. Nonetheless, we'll + try to unwind nanoseq when we get a chance to. + If nanoseq overflows, we'll start over with a new numberspace + (so the if() below is needed so we can avoid the ++tv and thus + match the follow-up if() if nanoseq overflows!). + */ + if (likely(++nanoseq)) + ++tv; + } + + if (unlikely(tv <= uuid_time)) + { + /* + If the admin changes the system clock (or due to Daylight + Saving Time), the system clock may be turned *back* so we + go through a period once more for which we already gave out + UUIDs. To avoid duplicate UUIDs despite potentially identical + times, we make a new random component. + We also come here if the nanoseq "borrowing" overflows. + In either case, we throw away any nanoseq borrowing since it's + irrelevant in the new numberspace. + */ + set_clock_seq_str(); + tv= my_getsystime() + UUID_TIME_OFFSET; + nanoseq= 0; + DBUG_PRINT("uuid",("making new numberspace")); } - DBUG_ASSERT(tv > uuid_time); } + uuid_time=tv; pthread_mutex_unlock(&LOCK_uuid_generator); |