diff options
author | unknown <andrey@lmy004.> | 2006-01-18 20:41:22 +0100 |
---|---|---|
committer | unknown <andrey@lmy004.> | 2006-01-18 20:41:22 +0100 |
commit | 6bd58a2a3ce27eb744a8d35893f5bfefd9e173dd (patch) | |
tree | 364b1773d1b0b50d5005939089eefee7bc15af75 /sql | |
parent | ef3a610638a4d0742a8b4a33e329f5f963e55bec (diff) | |
download | mariadb-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.cc | 19 | ||||
-rw-r--r-- | sql/event.h | 4 | ||||
-rw-r--r-- | sql/event_executor.cc | 23 | ||||
-rw-r--r-- | sql/event_priv.h | 1 | ||||
-rw-r--r-- | sql/event_timed.cc | 216 | ||||
-rw-r--r-- | sql/item_timefunc.cc | 103 | ||||
-rw-r--r-- | sql/item_timefunc.h | 17 | ||||
-rw-r--r-- | sql/mysql_priv.h | 3 | ||||
-rw-r--r-- | sql/share/errmsg.txt | 4 | ||||
-rw-r--r-- | sql/sql_yacc.yy | 2 | ||||
-rw-r--r-- | sql/time.cc | 108 |
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, <ime->year, <ime->month, - <ime->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,<ime->year,<ime->month,<ime->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, <ime->year, <ime->month, + <ime->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,<ime->year,<ime->month,<ime->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 |