summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--mysql-test/r/func_misc.result14
-rw-r--r--mysql-test/t/func_misc.test19
-rw-r--r--sql/item_strfunc.cc65
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);