Commit da13a76a authored by unknown's avatar unknown
Browse files

Make SYSDATE() behave as in Oracle: always the current datetime, not the

datetime of when the current statement began. This also makes SYSDATE()
not safe in replication. (Bug #12562)


mysql-test/r/func_time.result:
  Add new results
mysql-test/t/func_time.test:
  Add tests for new SYSDATE() behavior
sql/item_timefunc.cc:
  Add Item_func_sysdate_local implementation
sql/item_timefunc.h:
  Add Item_func_sysdate_local, so SYSDATE() can behave differently
  than NOW().
sql/lex.h:
  SYSDATE() is no longer an alias for NOW().
sql/sql_yacc.yy:
  Handle SYSDATE()
parent a7ce02bc
Loading
Loading
Loading
Loading
+40 −0
Original line number Diff line number Diff line
@@ -720,3 +720,43 @@ Warning 1292 Truncated incorrect datetime value: '2005-01-00'
select time_format('100:00:00', '%H %k %h %I %l');
time_format('100:00:00', '%H %k %h %I %l')
100 100 04 04 4
create table t1 (a timestamp default '2005-05-05 01:01:01',
b timestamp default '2005-05-05 01:01:01');
create function t_slow_sysdate() returns timestamp
begin
do sleep(2);
return sysdate();
end;
//
insert into t1 set a = sysdate(), b = t_slow_sysdate();//
create trigger t_before before insert on t1
for each row begin
set new.b = t_slow_sysdate();
end
//
insert into t1 set a = sysdate();
select a != b from t1;
a != b
1
1
drop trigger t_before;
drop function t_slow_sysdate;
drop table t1;
create table t1 (a datetime, i int, b datetime);
insert into t1 select sysdate(), sleep(1), sysdate() from dual;
select a != b from t1;
a != b
1
drop table t1;
create procedure t_sysdate()
begin
select sysdate() into @a;
do sleep(2);
select sysdate() into @b;
select @a != @b;
end;
//
call t_sysdate();
@a != @b
1
drop procedure t_sysdate;
+52 −0
Original line number Diff line number Diff line
@@ -353,3 +353,55 @@ select last_day('2005-01-00');
# the 0-11 range
#
select time_format('100:00:00', '%H %k %h %I %l');

#
# Bug #12562: Make SYSDATE behave like it does in Oracle: always the current
#             time, regardless of magic to make NOW() always the same for the
#             entirety of a statement.
create table t1 (a timestamp default '2005-05-05 01:01:01',
                 b timestamp default '2005-05-05 01:01:01');
delimiter //;
create function t_slow_sysdate() returns timestamp
begin
  do sleep(2);
  return sysdate();
end;
//

insert into t1 set a = sysdate(), b = t_slow_sysdate();//

create trigger t_before before insert on t1
for each row begin
  set new.b = t_slow_sysdate();
end
//

delimiter ;//

insert into t1 set a = sysdate();

select a != b from t1;

drop trigger t_before;
drop function t_slow_sysdate;
drop table t1;

create table t1 (a datetime, i int, b datetime);
insert into t1 select sysdate(), sleep(1), sysdate() from dual;
select a != b from t1;
drop table t1;

delimiter //;
create procedure t_sysdate()
begin
  select sysdate() into @a;
  do sleep(2);
  select sysdate() into @b;
  select @a != @b;
end;
//
delimiter ;//
call t_sysdate();
drop procedure t_sysdate;

# End of 5.0 tests
+66 −2
Original line number Diff line number Diff line
@@ -1491,6 +1491,70 @@ int Item_func_now::save_in_field(Field *to, bool no_conversions)
}


/*
    Converts current time in my_time_t to TIME represenatation for local
    time zone. Defines time zone (local) used for whole SYSDATE function.
*/
void Item_func_sysdate_local::store_now_in_TIME(TIME *now_time)
{
  THD *thd= current_thd;
  thd->variables.time_zone->gmt_sec_to_TIME(now_time, time(NULL));
  thd->time_zone_used= 1;
}


String *Item_func_sysdate_local::val_str(String *str)
{
  DBUG_ASSERT(fixed == 1);
  store_now_in_TIME(&ltime);
  buff_length= (uint) my_datetime_to_str(&ltime, buff);
  str_value.set(buff, buff_length, &my_charset_bin);
  return &str_value;
}


longlong Item_func_sysdate_local::val_int()
{
  DBUG_ASSERT(fixed == 1);
  store_now_in_TIME(&ltime);
  return (longlong) TIME_to_ulonglong_datetime(&ltime);
}


double Item_func_sysdate_local::val_real()
{
  DBUG_ASSERT(fixed == 1);
  store_now_in_TIME(&ltime);
  return (longlong) TIME_to_ulonglong_datetime(&ltime);
}


void Item_func_sysdate_local::fix_length_and_dec()
{
  decimals= 0;
  collation.set(&my_charset_bin);
  max_length= MAX_DATETIME_WIDTH*MY_CHARSET_BIN_MB_MAXLEN;
}


bool Item_func_sysdate_local::get_date(TIME *res,
                                       uint fuzzy_date __attribute__((unused)))
{
  store_now_in_TIME(&ltime);
  *res= ltime;
  return 0;
}


int Item_func_sysdate_local::save_in_field(Field *to, bool no_conversions)
{
  store_now_in_TIME(&ltime);
  to->set_notnull();
  to->store_time(&ltime, MYSQL_TIMESTAMP_DATETIME);
  return 0;
}


String *Item_func_sec_to_time::val_str(String *str)
{
  DBUG_ASSERT(fixed == 1);
+27 −0
Original line number Diff line number Diff line
@@ -446,6 +446,7 @@ class Item_func_curdate_utc :public Item_func_curdate

class Item_func_now :public Item_date_func
{
protected:
  longlong value;
  char buff[20*2+32];	// +32 to make my_snprintf_{8bit|ucs2} happy
  uint buff_length;
@@ -485,6 +486,32 @@ class Item_func_now_utc :public Item_func_now
};


/*
  This is like NOW(), but always uses the real current time, not the
  query_start(). This matches the Oracle behavior.
*/
class Item_func_sysdate_local :public Item_func_now
{
public:
  Item_func_sysdate_local() :Item_func_now() {}
  Item_func_sysdate_local(Item *a) :Item_func_now(a) {}
  bool const_item() const { return 0; }
  const char *func_name() const { return "sysdate"; }
  void store_now_in_TIME(TIME *now_time);
  double val_real();
  longlong val_int();
  int save_in_field(Field *to, bool no_conversions);
  String *val_str(String *str);
  void fix_length_and_dec();
  bool get_date(TIME *res, uint fuzzy_date);
  void update_used_tables()
  {
    Item_func_now::update_used_tables();
    used_tables_cache|= RAND_TABLE_BIT;
  }
};


class Item_func_from_days :public Item_date
{
public:
+1 −1
Original line number Diff line number Diff line
@@ -751,7 +751,7 @@ static SYMBOL sql_functions[] = {
  { "SUBSTRING_INDEX",	SYM(SUBSTRING_INDEX)},
  { "SUBTIME",          F_SYM(FUNC_ARG2),0,CREATE_FUNC(create_func_subtime)},
  { "SUM",		SYM(SUM_SYM)},
  { "SYSDATE",		SYM(NOW_SYM)},
  { "SYSDATE",		SYM(SYSDATE)},
  { "SYSTEM_USER",	SYM(USER)},
  { "TAN",		F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_tan)},
  { "TIME_FORMAT",	F_SYM(FUNC_ARG2),0,CREATE_FUNC(create_func_time_format)},
Loading