summaryrefslogtreecommitdiff
path: root/sql
diff options
context:
space:
mode:
authorunknown <andrey@lmy004.>2006-01-18 20:41:22 +0100
committerunknown <andrey@lmy004.>2006-01-18 20:41:22 +0100
commit6bd58a2a3ce27eb744a8d35893f5bfefd9e173dd (patch)
tree364b1773d1b0b50d5005939089eefee7bc15af75 /sql
parentef3a610638a4d0742a8b4a33e329f5f963e55bec (diff)
downloadmariadb-git-6bd58a2a3ce27eb744a8d35893f5bfefd9e173dd.tar.gz
- fix bug #16435 (Weekly events execute every second) (WL#1034 Internal CRON)
Before the interval expression was considered to be in seconds, now it is just a number and the type of interval is considered. - this changeset introduces also fix for bug#16432 (Events: error re interval misrepresents the facts) the code of event_timed::set_interval() was refactored anyway so it is meaningful to fix the bug in the same changeset. include/my_time.h: - move enum interval_type to include/my_time.h so it can be used by functions in the whole server sql/event.cc: - don't use second_part - fix small problem with create event xyz, when xyz exists -> make it error instead of warning if create_if_not is false. sql/event.h: pass thd to mark_last_executed() to be able to call thd->end_time() sql/event_executor.cc: - pass thd to event_timed::compute_next_execution_time() - a bit more DBUG info in the server log - handle error returned by event_timed::compute_next_execution_time() sql/event_priv.h: - define the maximal possible value for interval_value sql/event_timed.cc: - more docs - add static get_next_time() which sums a TIME with an interval - fix bug #16435 (Weekly events execute every second) Before the interval expression was considered to be in seconds, now it is just a number and the type of interval is considered. - fix for bug#16432 (Events: error re interval misrepresents the facts) (return an error if a value is too big or is negative - errmsg changed) sql/item_timefunc.cc: - export get_interval_date() - refactor Item_date_add_interval::get_date() and extract the core to date_add_interval() in time.cc so it can be reused by the scheduler code in event_timed.cc sql/item_timefunc.h: - export get_interval_value() so it can be reused in event_timed.cc in function static get_next_time() - move enum interval_type to include/my_time.h so it can be used by functions in the whole server sql/mysql_priv.h: export the new function date_add_interval() added to time.cc sql/share/errmsg.txt: - change error message to be appropriate as fix for bug#16432 (Events: error re interval misrepresents the facts) sql/sql_yacc.yy: - change error message to be appropriate as fix for bug#16432 (Events: error re interval misrepresents the facts) sql/time.cc: extract the core of Item_date_add_interval::get_date() to a function per Serg's request. The code can be reused to add und substract interval from a date.
Diffstat (limited to 'sql')
-rw-r--r--sql/event.cc19
-rw-r--r--sql/event.h4
-rw-r--r--sql/event_executor.cc23
-rw-r--r--sql/event_priv.h1
-rw-r--r--sql/event_timed.cc216
-rw-r--r--sql/item_timefunc.cc103
-rw-r--r--sql/item_timefunc.h17
-rw-r--r--sql/mysql_priv.h3
-rw-r--r--sql/share/errmsg.txt4
-rw-r--r--sql/sql_yacc.yy2
-rw-r--r--sql/time.cc108
11 files changed, 332 insertions, 168 deletions
diff --git a/sql/event.cc b/sql/event.cc
index 6d62be903bd..e637493d102 100644
--- a/sql/event.cc
+++ b/sql/event.cc
@@ -88,8 +88,14 @@ int sortcmp_lex_string(LEX_STRING s, LEX_STRING t, CHARSET_INFO *cs)
int
my_time_compare(TIME *a, TIME *b)
{
+
+#ifdef ENABLE_WHEN_WE_HAVE_MILLISECOND_IN_TIMESTAMPS
my_ulonglong a_t= TIME_to_ulonglong_datetime(a)*100L + a->second_part;
my_ulonglong b_t= TIME_to_ulonglong_datetime(b)*100L + b->second_part;
+#else
+ my_ulonglong a_t= TIME_to_ulonglong_datetime(a);
+ my_ulonglong b_t= TIME_to_ulonglong_datetime(b);
+#endif
if (a_t > b_t)
return 1;
@@ -355,10 +361,15 @@ db_create_event(THD *thd, event_timed *et, my_bool create_if_not,
DBUG_PRINT("info", ("check existance of an event with the same name"));
if (!evex_db_find_event_aux(thd, et->dbname, et->name, table))
{
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
- ER_EVENT_ALREADY_EXISTS, ER(ER_EVENT_ALREADY_EXISTS),
- et->name.str);
- goto ok;
+ if (create_if_not)
+ {
+ push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
+ ER_EVENT_ALREADY_EXISTS, ER(ER_EVENT_ALREADY_EXISTS),
+ et->name.str);
+ goto ok;
+ }
+ my_error(ER_EVENT_ALREADY_EXISTS, MYF(0), et->name.str);
+ goto err;
}
DBUG_PRINT("info", ("non-existant, go forward"));
diff --git a/sql/event.h b/sql/event.h
index d302a2f70ab..80629fa0a70 100644
--- a/sql/event.h
+++ b/sql/event.h
@@ -143,12 +143,12 @@ public:
int
load_from_row(MEM_ROOT *mem_root, TABLE *table);
-
+
bool
compute_next_execution_time();
void
- mark_last_executed();
+ mark_last_executed(THD *thd);
int
drop(THD *thd);
diff --git a/sql/event_executor.cc b/sql/event_executor.cc
index dd426c32545..1cb289efc74 100644
--- a/sql/event_executor.cc
+++ b/sql/event_executor.cc
@@ -334,9 +334,19 @@ event_executor_main(void *arg)
{
pthread_t th;
- DBUG_PRINT("evex main thread",("mark_last_executed"));
- et->mark_last_executed();
- et->compute_next_execution_time();
+ DBUG_PRINT("evex main thread", ("[%10s] this exec at [%llu]", et->name.str,
+ TIME_to_ulonglong_datetime(&et->execute_at)));
+ et->mark_last_executed(thd);
+ if (et->compute_next_execution_time())
+ {
+ sql_print_error("Error while computing time of %s.%s . "
+ "Disabling after execution.",
+ et->dbname.str, et->name.str);
+ et->status= MYSQL_EVENT_DISABLED;
+ }
+ DBUG_PRINT("evex main thread", ("[%10s] next exec at [%llu]", et->name.str,
+ TIME_to_ulonglong_datetime(&et->execute_at)));
+
et->update_fields(thd);
DBUG_PRINT("info", (" Spawning a thread %d", ++iter_num));
#ifndef DBUG_FAULTY_THR
@@ -599,7 +609,12 @@ evex_load_events_from_db(THD *thd)
}
// let's find when to be executed
- et->compute_next_execution_time();
+ if (et->compute_next_execution_time())
+ {
+ sql_print_error("Error while computing execution time of %s.%s. Skipping",
+ et->dbname.str, et->name.str);
+ continue;
+ }
DBUG_PRINT("evex_load_events_from_db", ("Adding to the exec list."));
diff --git a/sql/event_priv.h b/sql/event_priv.h
index 05834383ae5..42c60dd391d 100644
--- a/sql/event_priv.h
+++ b/sql/event_priv.h
@@ -46,6 +46,7 @@ enum evex_table_field
#define EVEX_DB_FIELD_LEN 64
#define EVEX_NAME_FIELD_LEN 64
+#define EVEX_MAX_INTERVAL_VALUE 2147483647L
int
my_time_compare(TIME *a, TIME *b);
diff --git a/sql/event_timed.cc b/sql/event_timed.cc
index cc8849364da..2b7f2cc7ee3 100644
--- a/sql/event_timed.cc
+++ b/sql/event_timed.cc
@@ -191,16 +191,78 @@ int
event_timed::init_interval(THD *thd, Item *expr, interval_type new_interval)
{
longlong tmp;
+ String value;
+ INTERVAL interval;
+
DBUG_ENTER("event_timed::init_interval");
if (expr->fix_fields(thd, &expr))
DBUG_RETURN(EVEX_PARSE_ERROR);
- if ((tmp= expr->val_int()) <= 0)
- DBUG_RETURN(EVEX_BAD_PARAMS);
+ value.alloc(MAX_DATETIME_FULL_WIDTH*MY_CHARSET_BIN_MB_MAXLEN);
+ if (get_interval_value(expr, new_interval, &value, &interval))
+ DBUG_RETURN(EVEX_PARSE_ERROR);
- expression= tmp;
- interval= new_interval;
+ expression= 0;
+
+ switch (new_interval) {
+ case INTERVAL_YEAR:
+ expression= interval.year;
+ break;
+ case INTERVAL_QUARTER:
+ case INTERVAL_MONTH:
+ expression= interval.month;
+ break;
+ case INTERVAL_WEEK:
+ case INTERVAL_DAY:
+ expression= interval.day;
+ break;
+ case INTERVAL_HOUR:
+ expression= interval.hour;
+ break;
+ case INTERVAL_MINUTE:
+ expression= interval.minute;
+ break;
+ case INTERVAL_SECOND:
+ expression= interval.second;
+ break;
+ case INTERVAL_YEAR_MONTH: // Allow YEAR-MONTH YYYYYMM
+ expression= interval.year* 12 + interval.month;
+ break;
+ case INTERVAL_DAY_HOUR:
+ expression= interval.day* 24 + interval.hour;
+ break;
+ case INTERVAL_DAY_MINUTE:
+ expression= (interval.day* 24 + interval.hour) * 60 + interval.minute;
+ break;
+ case INTERVAL_HOUR_SECOND: // day is anyway 0
+ case INTERVAL_DAY_SECOND:
+ /* DAY_SECOND having problems because of leap seconds? */
+ expression= ((interval.day* 24 + interval.hour) * 60 + interval.minute)*60
+ + interval.second;
+ break;
+ case INTERVAL_MINUTE_MICROSECOND: // day and hour are 0
+ case INTERVAL_HOUR_MICROSECOND:// day is anyway 0
+ case INTERVAL_DAY_MICROSECOND:
+ expression= ((((interval.day*24) + interval.hour)*60+interval.minute)*60 +
+ interval.second) * 1000000L + interval.second_part;
+ break;
+ case INTERVAL_HOUR_MINUTE:
+ expression= interval.hour * 60 + interval.minute;
+ break;
+ case INTERVAL_MINUTE_SECOND:
+ expression= interval.minute * 60 + interval.second;
+ break;
+ case INTERVAL_SECOND_MICROSECOND:
+ expression= interval.second * 1000000L + interval.second_part;
+ break;
+ default:
+ break;
+ }
+ if (interval.neg || expression > EVEX_MAX_INTERVAL_VALUE)
+ DBUG_RETURN(EVEX_BAD_PARAMS);
+
+ this->interval= new_interval;
DBUG_RETURN(0);
}
@@ -355,7 +417,7 @@ event_timed::init_definer(THD *thd)
Loads an event from a row from mysql.event
SYNOPSIS
- event_timed::load_from_row()
+ event_timed::load_from_row(MEM_ROOT *mem_root, TABLE *table)
REMARKS
This method is silent on errors and should behave like that. Callers
@@ -499,8 +561,83 @@ error:
/*
- Note: In the comments this->ends is referenced as m_ends
+ Computes the sum of a timestamp plus interval
+
+ SYNOPSIS
+ get_next_time(TIME *start, int interval_value, interval_type interval)
+
+ next - the sum
+ start - add interval_value to this time
+ i_value - quantity of time type interval to add
+ i_type - type of interval to add (SECOND, MINUTE, HOUR, WEEK ...)
+*/
+static
+bool get_next_time(TIME *next, TIME *start, int i_value, interval_type i_type)
+{
+ bool ret;
+ INTERVAL interval;
+ TIME tmp;
+
+ bzero(&interval, sizeof(interval));
+
+ switch (i_type) {
+ case INTERVAL_YEAR:
+ interval.year= (ulong) i_value;
+ break;
+ case INTERVAL_QUARTER:
+ interval.month= (ulong)(i_value*3);
+ break;
+ case INTERVAL_YEAR_MONTH:
+ case INTERVAL_MONTH:
+ interval.month= (ulong) i_value;
+ break;
+ case INTERVAL_WEEK:
+ interval.day= (ulong)(i_value*7);
+ break;
+ case INTERVAL_DAY:
+ interval.day= (ulong) i_value;
+ break;
+ case INTERVAL_DAY_HOUR:
+ case INTERVAL_HOUR:
+ interval.hour= (ulong) i_value;
+ break;
+ case INTERVAL_DAY_MINUTE:
+ case INTERVAL_HOUR_MINUTE:
+ case INTERVAL_MINUTE:
+ interval.minute=i_value;
+ break;
+ case INTERVAL_DAY_SECOND:
+ case INTERVAL_HOUR_SECOND:
+ case INTERVAL_MINUTE_SECOND:
+ case INTERVAL_SECOND:
+ interval.second=i_value;
+ break;
+ case INTERVAL_DAY_MICROSECOND:
+ case INTERVAL_HOUR_MICROSECOND:
+ case INTERVAL_MINUTE_MICROSECOND:
+ case INTERVAL_SECOND_MICROSECOND:
+ case INTERVAL_MICROSECOND:
+ interval.second_part=i_value;
+ break;
+ }
+ tmp= *start;
+ if (!(ret= date_add_interval(&tmp, i_type, interval)))
+ *next= tmp;
+
+ return ret;
+}
+
+
+/*
+ Computes next execution time.
+
+ SYNOPSIS
+ event_timed::compute_next_execution_time()
+
+ REMARKS:
+ The time is set in execute_at, if no more executions the latter is set to
+ 0000-00-00.
*/
bool
@@ -605,14 +742,13 @@ event_timed::compute_next_execution_time()
execute_at= time_now;
else
{
- my_time_t last, ll_ends;
-
- // There was previous execution
- last= sec_since_epoch_TIME(&last_executed) + expression;
- ll_ends= sec_since_epoch_TIME(&ends);
- //now convert back to TIME
- //ToDo Andrey: maybe check for error here?
- if (ll_ends < last)
+ TIME next_exec;
+
+ if (get_next_time(&next_exec, &last_executed, expression, interval))
+ goto err;
+
+ // There was previous execution
+ if (my_time_compare(&ends, &next_exec) == -1)
{
// Next execution after ends. No more executions
set_zero_time(&execute_at, MYSQL_TIMESTAMP_DATETIME);
@@ -620,7 +756,7 @@ event_timed::compute_next_execution_time()
dropped= true;
}
else
- my_tz_UTC->gmt_sec_to_TIME(&execute_at, last);
+ execute_at= next_exec;
}
goto ret;
}
@@ -628,14 +764,14 @@ event_timed::compute_next_execution_time()
{
// both starts and m_ends are not set, se we schedule for the next
// based on last_executed
- if (!last_executed.year)
+ if (last_executed.year)
+ {
+ if (get_next_time(&execute_at, &last_executed, expression, interval))
+ goto err;
+ }
+ else
//last_executed not set. Schedule the event for now
execute_at= time_now;
- else
- //ToDo Andrey: maybe check for error here?
- my_tz_UTC->gmt_sec_to_TIME(&execute_at,
- sec_since_epoch_TIME(&last_executed) + expression);
- goto ret;
}
else
{
@@ -648,17 +784,13 @@ event_timed::compute_next_execution_time()
Hence schedule for starts + m_expression in case last_executed
is not set, otherwise to last_executed + m_expression
*/
- my_time_t last;
-
- //convert either last_executed or starts to seconds
if (last_executed.year)
- last= sec_since_epoch_TIME(&last_executed) + expression;
+ {
+ if (get_next_time(&execute_at, &last_executed, expression, interval))
+ goto err;
+ }
else
- last= sec_since_epoch_TIME(&starts);
-
- //now convert back to TIME
- //ToDo Andrey: maybe check for error here?
- my_tz_UTC->gmt_sec_to_TIME(&execute_at, last);
+ execute_at= starts;
}
else
{
@@ -668,25 +800,24 @@ event_timed::compute_next_execution_time()
Hence check for m_last_execute and increment with m_expression.
If last_executed is not set then schedule for now
*/
- my_time_t last, ll_ends;
if (!last_executed.year)
execute_at= time_now;
else
{
- last= sec_since_epoch_TIME(&last_executed);
- ll_ends= sec_since_epoch_TIME(&ends);
- last+= expression;
- //now convert back to TIME
- //ToDo Andrey: maybe check for error here?
- if (ll_ends < last)
+ TIME next_exec;
+
+ if (get_next_time(&next_exec, &last_executed, expression, interval))
+ goto err;
+
+ if (my_time_compare(&ends, &next_exec) == -1)
{
set_zero_time(&execute_at, MYSQL_TIMESTAMP_DATETIME);
if (on_completion == MYSQL_EVENT_ON_COMPLETION_DROP)
dropped= true;
}
else
- my_tz_UTC->gmt_sec_to_TIME(&execute_at, last);
+ execute_at= next_exec;
}
}
goto ret;
@@ -694,17 +825,18 @@ event_timed::compute_next_execution_time()
ret:
DBUG_RETURN(false);
+err:
+ DBUG_RETURN(true);
}
void
-event_timed::mark_last_executed()
+event_timed::mark_last_executed(THD *thd)
{
TIME time_now;
- my_time_t now;
- time((time_t *)&now);
- my_tz_UTC->gmt_sec_to_TIME(&time_now, now);
+ thd->end_time();
+ my_tz_UTC->gmt_sec_to_TIME(&time_now, (my_time_t) thd->query_start());
last_executed= time_now; // was execute_at
#ifdef ANDREY_0
diff --git a/sql/item_timefunc.cc b/sql/item_timefunc.cc
index 3560a74ddb2..60bb72b699b 100644
--- a/sql/item_timefunc.cc
+++ b/sql/item_timefunc.cc
@@ -1135,7 +1135,7 @@ longlong Item_func_time_to_sec::val_int()
To make code easy, allow interval objects without separators.
*/
-static bool get_interval_value(Item *args,interval_type int_type,
+bool get_interval_value(Item *args,interval_type int_type,
String *str_value, INTERVAL *interval)
{
ulonglong array[5];
@@ -1994,110 +1994,15 @@ bool Item_date_add_interval::get_date(TIME *ltime, uint fuzzy_date)
long period,sign;
INTERVAL interval;
- ltime->neg= 0;
if (args[0]->get_date(ltime, TIME_NO_ZERO_DATE) ||
get_interval_value(args[1],int_type,&value,&interval))
goto null_date;
- sign= (interval.neg ? -1 : 1);
+
if (date_sub_interval)
- sign = -sign;
+ interval.neg = !interval.neg;
- null_value=0;
- switch (int_type) {
- case INTERVAL_SECOND:
- case INTERVAL_SECOND_MICROSECOND:
- case INTERVAL_MICROSECOND:
- case INTERVAL_MINUTE:
- case INTERVAL_HOUR:
- case INTERVAL_MINUTE_MICROSECOND:
- case INTERVAL_MINUTE_SECOND:
- case INTERVAL_HOUR_MICROSECOND:
- case INTERVAL_HOUR_SECOND:
- case INTERVAL_HOUR_MINUTE:
- case INTERVAL_DAY_MICROSECOND:
- case INTERVAL_DAY_SECOND:
- case INTERVAL_DAY_MINUTE:
- case INTERVAL_DAY_HOUR:
- {
- longlong sec, days, daynr, microseconds, extra_sec;
- ltime->time_type= MYSQL_TIMESTAMP_DATETIME; // Return full date
- microseconds= ltime->second_part + sign*interval.second_part;
- extra_sec= microseconds/1000000L;
- microseconds= microseconds%1000000L;
-
- sec=((ltime->day-1)*3600*24L+ltime->hour*3600+ltime->minute*60+
- ltime->second +
- sign* (longlong) (interval.day*3600*24L +
- interval.hour*LL(3600)+interval.minute*LL(60)+
- interval.second))+ extra_sec;
- if (microseconds < 0)
- {
- microseconds+= LL(1000000);
- sec--;
- }
- days= sec/(3600*LL(24));
- sec-= days*3600*LL(24);
- if (sec < 0)
- {
- days--;
- sec+= 3600*LL(24);
- }
- ltime->second_part= (uint) microseconds;
- ltime->second= (uint) (sec % 60);
- ltime->minute= (uint) (sec/60 % 60);
- ltime->hour= (uint) (sec/3600);
- daynr= calc_daynr(ltime->year,ltime->month,1) + days;
- /* Day number from year 0 to 9999-12-31 */
- if ((ulonglong) daynr >= MAX_DAY_NUMBER)
- goto invalid_date;
- get_date_from_daynr((long) daynr, &ltime->year, &ltime->month,
- &ltime->day);
- break;
- }
- case INTERVAL_DAY:
- case INTERVAL_WEEK:
- period= (calc_daynr(ltime->year,ltime->month,ltime->day) +
- sign * (long) interval.day);
- /* Daynumber from year 0 to 9999-12-31 */
- if ((ulong) period >= MAX_DAY_NUMBER)
- goto invalid_date;
- get_date_from_daynr((long) period,&ltime->year,&ltime->month,&ltime->day);
- break;
- case INTERVAL_YEAR:
- ltime->year+= sign * (long) interval.year;
- if ((ulong) ltime->year >= 10000L)
- goto invalid_date;
- if (ltime->month == 2 && ltime->day == 29 &&
- calc_days_in_year(ltime->year) != 366)
- ltime->day=28; // Was leap-year
- break;
- case INTERVAL_YEAR_MONTH:
- case INTERVAL_QUARTER:
- case INTERVAL_MONTH:
- period= (ltime->year*12 + sign * (long) interval.year*12 +
- ltime->month-1 + sign * (long) interval.month);
- if ((ulong) period >= 120000L)
- goto invalid_date;
- ltime->year= (uint) (period / 12);
- ltime->month= (uint) (period % 12L)+1;
- /* Adjust day if the new month doesn't have enough days */
- if (ltime->day > days_in_month[ltime->month-1])
- {
- ltime->day = days_in_month[ltime->month-1];
- if (ltime->month == 2 && calc_days_in_year(ltime->year) == 366)
- ltime->day++; // Leap-year
- }
- break;
- default:
- goto null_date;
- }
- return 0; // Ok
+ return (null_value= date_add_interval(ltime, int_type, interval));
-invalid_date:
- push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
- ER_DATETIME_FUNCTION_OVERFLOW,
- ER(ER_DATETIME_FUNCTION_OVERFLOW),
- "datetime");
null_date:
return (null_value=1);
}
diff --git a/sql/item_timefunc.h b/sql/item_timefunc.h
index 9a2cb7a4c9e..fd2f3945fca 100644
--- a/sql/item_timefunc.h
+++ b/sql/item_timefunc.h
@@ -26,6 +26,9 @@ enum date_time_format_types
TIME_ONLY= 0, TIME_MICROSECOND, DATE_ONLY, DATE_TIME, DATE_TIME_MICROSECOND
};
+bool get_interval_value(Item *args,interval_type int_type,
+ String *str_value, INTERVAL *interval);
+
class Item_func_period_add :public Item_int_func
{
public:
@@ -626,20 +629,6 @@ public:
}
};
-/*
- The following must be sorted so that simple intervals comes first.
- (get_interval_value() depends on this)
-*/
-
-enum interval_type
-{
- INTERVAL_YEAR, INTERVAL_QUARTER, INTERVAL_MONTH, INTERVAL_DAY, INTERVAL_HOUR,
- INTERVAL_MINUTE, INTERVAL_WEEK, INTERVAL_SECOND, INTERVAL_MICROSECOND ,
- INTERVAL_YEAR_MONTH, INTERVAL_DAY_HOUR, INTERVAL_DAY_MINUTE,
- INTERVAL_DAY_SECOND, INTERVAL_HOUR_MINUTE, INTERVAL_HOUR_SECOND,
- INTERVAL_MINUTE_SECOND, INTERVAL_DAY_MICROSECOND, INTERVAL_HOUR_MICROSECOND,
- INTERVAL_MINUTE_MICROSECOND, INTERVAL_SECOND_MICROSECOND
-};
class Item_date_add_interval :public Item_date_func
{
diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h
index b3bc49b31d1..d98a3ebde99 100644
--- a/sql/mysql_priv.h
+++ b/sql/mysql_priv.h
@@ -1452,6 +1452,9 @@ void calc_time_from_sec(TIME *to, long seconds, long microseconds);
void make_truncated_value_warning(THD *thd, const char *str_val,
uint str_length, timestamp_type time_type,
const char *field_name);
+
+bool date_add_interval(TIME *ltime, interval_type int_type, INTERVAL interval);
+
extern DATE_TIME_FORMAT *date_time_format_make(timestamp_type format_type,
const char *format_str,
uint format_length);
diff --git a/sql/share/errmsg.txt b/sql/share/errmsg.txt
index 63fd9db4744..1a574e92b24 100644
--- a/sql/share/errmsg.txt
+++ b/sql/share/errmsg.txt
@@ -5757,8 +5757,8 @@ ER_EVENT_CANT_ALTER
eng "Failed to alter event '%-.64s'"
ER_EVENT_DROP_FAILED
eng "Failed to drop %s"
-ER_EVENT_INTERVAL_NOT_POSITIVE
- eng "INTERVAL must be positive"
+ER_EVENT_INTERVAL_NOT_POSITIVE_OR_TOO_BIG
+ eng "INTERVAL is either not positive or too big"
ER_EVENT_ENDS_BEFORE_STARTS
eng "ENDS must be after STARTS"
ER_EVENT_EXEC_TIME_IN_THE_PAST
diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy
index a3734a48c5c..fb78e47a10d 100644
--- a/sql/sql_yacc.yy
+++ b/sql/sql_yacc.yy
@@ -1417,7 +1417,7 @@ ev_schedule_time: EVERY_SYM expr interval
YYABORT;
break;
case EVEX_BAD_PARAMS:
- my_error(ER_EVENT_INTERVAL_NOT_POSITIVE, MYF(0));
+ my_error(ER_EVENT_INTERVAL_NOT_POSITIVE_OR_TOO_BIG, MYF(0));
YYABORT;
break;
}
diff --git a/sql/time.cc b/sql/time.cc
index 480cafaab34..efe1cbf1c09 100644
--- a/sql/time.cc
+++ b/sql/time.cc
@@ -724,5 +724,113 @@ void make_truncated_value_warning(THD *thd, const char *str_val,
ER_TRUNCATED_WRONG_VALUE, warn_buff);
}
+#define MAX_DAY_NUMBER 3652424L
+
+bool date_add_interval(TIME *ltime, interval_type int_type, INTERVAL interval)
+{
+ long period, sign;
+
+ ltime->neg= 0;
+
+ sign= (interval.neg ? -1 : 1);
+
+ switch (int_type) {
+ case INTERVAL_SECOND:
+ case INTERVAL_SECOND_MICROSECOND:
+ case INTERVAL_MICROSECOND:
+ case INTERVAL_MINUTE:
+ case INTERVAL_HOUR:
+ case INTERVAL_MINUTE_MICROSECOND:
+ case INTERVAL_MINUTE_SECOND:
+ case INTERVAL_HOUR_MICROSECOND:
+ case INTERVAL_HOUR_SECOND:
+ case INTERVAL_HOUR_MINUTE:
+ case INTERVAL_DAY_MICROSECOND:
+ case INTERVAL_DAY_SECOND:
+ case INTERVAL_DAY_MINUTE:
+ case INTERVAL_DAY_HOUR:
+ {
+ longlong sec, days, daynr, microseconds, extra_sec;
+ ltime->time_type= MYSQL_TIMESTAMP_DATETIME; // Return full date
+ microseconds= ltime->second_part + sign*interval.second_part;
+ extra_sec= microseconds/1000000L;
+ microseconds= microseconds%1000000L;
+
+ sec=((ltime->day-1)*3600*24L+ltime->hour*3600+ltime->minute*60+
+ ltime->second +
+ sign* (longlong) (interval.day*3600*24L +
+ interval.hour*LL(3600)+interval.minute*LL(60)+
+ interval.second))+ extra_sec;
+ if (microseconds < 0)
+ {
+ microseconds+= LL(1000000);
+ sec--;
+ }
+ days= sec/(3600*LL(24));
+ sec-= days*3600*LL(24);
+ if (sec < 0)
+ {
+ days--;
+ sec+= 3600*LL(24);
+ }
+ ltime->second_part= (uint) microseconds;
+ ltime->second= (uint) (sec % 60);
+ ltime->minute= (uint) (sec/60 % 60);
+ ltime->hour= (uint) (sec/3600);
+ daynr= calc_daynr(ltime->year,ltime->month,1) + days;
+ /* Day number from year 0 to 9999-12-31 */
+ if ((ulonglong) daynr >= MAX_DAY_NUMBER)
+ goto invalid_date;
+ get_date_from_daynr((long) daynr, &ltime->year, &ltime->month,
+ &ltime->day);
+ break;
+ }
+ case INTERVAL_DAY:
+ case INTERVAL_WEEK:
+ period= (calc_daynr(ltime->year,ltime->month,ltime->day) +
+ sign * (long) interval.day);
+ /* Daynumber from year 0 to 9999-12-31 */
+ if ((ulong) period >= MAX_DAY_NUMBER)
+ goto invalid_date;
+ get_date_from_daynr((long) period,&ltime->year,&ltime->month,&ltime->day);
+ break;
+ case INTERVAL_YEAR:
+ ltime->year+= sign * (long) interval.year;
+ if ((ulong) ltime->year >= 10000L)
+ goto invalid_date;
+ if (ltime->month == 2 && ltime->day == 29 &&
+ calc_days_in_year(ltime->year) != 366)
+ ltime->day=28; // Was leap-year
+ break;
+ case INTERVAL_YEAR_MONTH:
+ case INTERVAL_QUARTER:
+ case INTERVAL_MONTH:
+ period= (ltime->year*12 + sign * (long) interval.year*12 +
+ ltime->month-1 + sign * (long) interval.month);
+ if ((ulong) period >= 120000L)
+ goto invalid_date;
+ ltime->year= (uint) (period / 12);
+ ltime->month= (uint) (period % 12L)+1;
+ /* Adjust day if the new month doesn't have enough days */
+ if (ltime->day > days_in_month[ltime->month-1])
+ {
+ ltime->day = days_in_month[ltime->month-1];
+ if (ltime->month == 2 && calc_days_in_year(ltime->year) == 366)
+ ltime->day++; // Leap-year
+ }
+ break;
+ default:
+ return 1;
+ }
+ return 0; // Ok
+
+invalid_date:
+ push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ ER_DATETIME_FUNCTION_OVERFLOW,
+ ER(ER_DATETIME_FUNCTION_OVERFLOW),
+ "datetime");
+ return 1;
+}
+
#endif