diff options
author | unknown <guilhem@mysql.com> | 2004-07-29 23:25:58 +0200 |
---|---|---|
committer | unknown <guilhem@mysql.com> | 2004-07-29 23:25:58 +0200 |
commit | 88e3aead85b7136fab3d8cfcfa19174c4c2e2662 (patch) | |
tree | 2f01af661dcc90a418853b7a1718fd7a0fe688e1 | |
parent | 5ef15478cdbaa65c7d037ce5662117b8ed425dba (diff) | |
download | mariadb-git-88e3aead85b7136fab3d8cfcfa19174c4c2e2662.tar.gz |
WL#1580: --start-datetime, --stop-datetime, --start-position (alias for --position) and --stop-position
options for mysqlbinlog, with a test file.
This enables user to say "recover my database to how it was this morning at 10:30"
(mysqlbinlog "--stop-datetime=2003-07-29 10:30:00").
Using time functions into client/ made me move them out of sql/ into sql-common/.
+ (small) fix for BUG#4507 "mysqlbinlog --read-from-remote-server sometimes
cannot accept 2 binlogs" (that is, on command line).
client/client_priv.h:
new options for mysqlbinlog
client/mysqlbinlog.cc:
WL#1580: --start-datetime, --stop-datetime, --start-position (alias for --position) and --stop-position.
(small) fix for BUG#4507 "mysqlbinlog --read-from-remote-server sometimes
cannot accept 2 binlogs".
include/my_time.h:
importing time functions so that client/ files can use them.
include/mysql_time.h:
importing time types so that client/ files can use them.
sql-common/my_time.c:
importing time functions so that client/ files can use them.
sql/mysql_priv.h:
moving time functions out of sql/ into sql-common/
sql/time.cc:
moving time functions out of sql/ into sql-common/
sql/tztime.h:
moving time functions out of sql/ into sql-common/
-rw-r--r-- | client/client_priv.h | 3 | ||||
-rw-r--r-- | client/mysqlbinlog.cc | 248 | ||||
-rw-r--r-- | include/my_time.h | 7 | ||||
-rw-r--r-- | include/mysql_time.h | 9 | ||||
-rw-r--r-- | mysql-test/r/mysqlbinlog2.result | 446 | ||||
-rw-r--r-- | mysql-test/t/mysqlbinlog2.test | 156 | ||||
-rw-r--r-- | sql-common/my_time.c | 155 | ||||
-rw-r--r-- | sql/mysql_priv.h | 5 | ||||
-rw-r--r-- | sql/time.cc | 157 | ||||
-rw-r--r-- | sql/tztime.h | 9 |
10 files changed, 957 insertions, 238 deletions
diff --git a/client/client_priv.h b/client/client_priv.h index 854d205e585..ad08484b706 100644 --- a/client/client_priv.h +++ b/client/client_priv.h @@ -43,5 +43,6 @@ enum options_client OPT_PROMPT, OPT_IGN_LINES,OPT_TRANSACTION,OPT_MYSQL_PROTOCOL, OPT_SHARED_MEMORY_BASE_NAME, OPT_FRM, OPT_SKIP_OPTIMIZATION, OPT_COMPATIBLE, OPT_RECONNECT, OPT_DELIMITER, OPT_SECURE_AUTH, - OPT_OPEN_FILES_LIMIT, OPT_SET_CHARSET, OPT_CREATE_OPTIONS + OPT_OPEN_FILES_LIMIT, OPT_SET_CHARSET, OPT_CREATE_OPTIONS, + OPT_START_POSITION, OPT_STOP_POSITION, OPT_START_DATETIME, OPT_STOP_DATETIME }; diff --git a/client/mysqlbinlog.cc b/client/mysqlbinlog.cc index ba030379792..97746a52b39 100644 --- a/client/mysqlbinlog.cc +++ b/client/mysqlbinlog.cc @@ -17,7 +17,7 @@ #define MYSQL_CLIENT #undef MYSQL_SERVER #include "client_priv.h" -#include <time.h> +#include <my_time.h> #include "log_event.h" #define BIN_LOG_HEADER_SIZE 4 @@ -53,10 +53,18 @@ static int port = MYSQL_PORT; static const char* sock= 0; static const char* user = 0; static char* pass = 0; -static ulonglong position = 0; + +static ulonglong start_position, stop_position; +#define start_position_mot ((my_off_t)start_position) +#define stop_position_mot ((my_off_t)stop_position) + +static char *start_datetime_str, *stop_datetime_str; +static my_time_t start_datetime= 0, stop_datetime= MY_TIME_T_MAX; +static ulonglong rec_count= 0; static short binlog_flags = 0; static MYSQL* mysql = NULL; static const char* dirname_for_local_load= 0; +static bool stop_passed= 0; static int dump_local_log_entries(const char* logname); static int dump_remote_log_entries(const char* logname); @@ -302,15 +310,36 @@ Create_file event for file_id: %u\n",ae->file_id); Load_log_processor load_processor; +/* + RETURN + 0 ok and continue + 1 error and terminate + -1 ok and terminate -int process_event(ulonglong *rec_count, char *last_db, Log_event *ev, - my_off_t pos, int old_format) + TODO + This function returns 0 even in some error cases. This should be changed. +*/ +int process_event(char *last_db, Log_event *ev, my_off_t pos, int old_format) { char ll_buff[21]; DBUG_ENTER("process_event"); - if ((*rec_count) >= offset) + if ((rec_count >= offset) && + ((my_time_t)(ev->when) >= start_datetime)) { + /* + We have found an event after start_datetime, from now on print + everything (in case the binlog has timestamps increasing and decreasing, + we do this to avoid cutting the middle). + */ + start_datetime= 0; + offset= 0; // print everything and protect against cycling rec_count + if (((my_time_t)(ev->when) >= stop_datetime) + || (pos >= stop_position_mot)) + { + stop_passed= 1; // skip all next binlogs + DBUG_RETURN(-1); + } if (!short_form) fprintf(result_file, "# at %s\n",llstr(pos,ll_buff)); @@ -387,7 +416,7 @@ Create_file event for file_id: %u\n",exv->file_id); } end: - (*rec_count)++; + rec_count++; if (ev) delete ev; DBUG_RETURN(0); @@ -417,13 +446,14 @@ static struct my_option my_long_options[] = {"port", 'P', "Use port to connect to the remote server.", (gptr*) &port, (gptr*) &port, 0, GET_INT, REQUIRED_ARG, MYSQL_PORT, 0, 0, 0, 0, 0}, - {"position", 'j', "Start reading the binlog at position N.", - (gptr*) &position, (gptr*) &position, 0, GET_ULL, REQUIRED_ARG, 0, 0, 0, 0, - 0, 0}, + {"position", 'j', "Deprecated. Use --start-position instead.", + (gptr*) &start_position, (gptr*) &start_position, 0, GET_ULL, + REQUIRED_ARG, BIN_LOG_HEADER_SIZE, BIN_LOG_HEADER_SIZE, + /* COM_BINLOG_DUMP accepts only 4 bytes for the position */ + (ulonglong)(~(uint32)0), 0, 0, 0}, {"protocol", OPT_MYSQL_PROTOCOL, "The protocol of connection (tcp,socket,pipe,memory).", 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, - {"result-file", 'r', "Direct output to a given file.", 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, {"read-from-remote-server", 'R', "Read binary logs from a MySQL server", @@ -439,6 +469,35 @@ static struct my_option my_long_options[] = {"socket", 'S', "Socket file to use for connection.", (gptr*) &sock, (gptr*) &sock, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, + {"start-datetime", OPT_START_DATETIME, + "Start reading the binlog at first event having a datetime equal or " + "posterior to the argument; the argument must be a date and time " + "in the local time zone, in any format accepted by the MySQL server " + "for DATETIME and TIMESTAMP types, for example: 2004-12-25 11:25:56 " + "(you should probably use quotes for your shell to set it properly).", + (gptr*) &start_datetime_str, (gptr*) &start_datetime_str, + 0, GET_STR_ALLOC, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, + {"stop-datetime", OPT_STOP_DATETIME, + "Stop reading the binlog at first event having a datetime equal or " + "posterior to the argument; the argument must be a date and time " + "in the local time zone, in any format accepted by the MySQL server " + "for DATETIME and TIMESTAMP types, for example: 2004-12-25 11:25:56 " + "(you should probably use quotes for your shell to set it properly).", + (gptr*) &stop_datetime_str, (gptr*) &stop_datetime_str, + 0, GET_STR_ALLOC, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, + {"start-position", OPT_START_POSITION, + "Start reading the binlog at position N. Applies to the first binlog " + "passed on the command line.", + (gptr*) &start_position, (gptr*) &start_position, 0, GET_ULL, + REQUIRED_ARG, BIN_LOG_HEADER_SIZE, BIN_LOG_HEADER_SIZE, + /* COM_BINLOG_DUMP accepts only 4 bytes for the position */ + (ulonglong)(~(uint32)0), 0, 0, 0}, + {"stop-position", OPT_STOP_POSITION, + "Stop reading the binlog at position N. Applies to the last binlog " + "passed on the command line.", + (gptr*) &stop_position, (gptr*) &stop_position, 0, GET_ULL, + REQUIRED_ARG, (ulonglong)(~(my_off_t)0), BIN_LOG_HEADER_SIZE, + (ulonglong)(~(my_off_t)0), 0, 0, 0}, {"to-last-log", 't', "Requires -R. Will not stop at the end of the \ requested binlog but rather continue printing until the end of the last \ binlog of the MySQL server. If you send the output to the same MySQL server, \ @@ -513,6 +572,29 @@ the mysql command line client\n\n"); my_print_variables(my_long_options); } + +static my_time_t convert_str_to_timestamp(const char* str) +{ + int was_cut; + MYSQL_TIME l_time; + long dummy_my_timezone; + bool dummy_in_dst_time_gap; + /* We require a total specification (date AND time) */ + if (str_to_datetime(str, strlen(str), &l_time, 0, &was_cut) != + MYSQL_TIMESTAMP_DATETIME || was_cut) + { + fprintf(stderr, "Incorrect date and time argument: %s\n", str); + exit(1); + } + /* + Note that Feb 30th, Apr 31st cause no error messages and are mapped to + the next existing day, like in mysqld. Maybe this could be changed when + mysqld is changed too (with its "strict" mode?). + */ + return + my_system_gmt_sec(&l_time, &dummy_my_timezone, &dummy_in_dst_time_gap); +} + #include <help_end.h> extern "C" my_bool @@ -559,7 +641,12 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)), } break; } - break; + case OPT_START_DATETIME: + start_datetime= convert_str_to_timestamp(start_datetime_str); + break; + case OPT_STOP_DATETIME: + stop_datetime= convert_str_to_timestamp(stop_datetime_str); + break; case 'V': print_version(); exit(0); @@ -604,9 +691,8 @@ static MYSQL* safe_connect() static int dump_log_entries(const char* logname) { - if (remote_opt) - return dump_remote_log_entries(logname); - return dump_local_log_entries(logname); + return (remote_opt ? dump_remote_log_entries(logname) : + dump_local_log_entries(logname)); } @@ -663,21 +749,27 @@ static int dump_remote_log_entries(const char* logname) char buf[128]; char last_db[FN_REFLEN+1] = ""; uint len, logname_len; - NET* net = &mysql->net; + NET* net; int old_format; + int error= 0; + my_off_t old_off= start_position_mot; + char fname[FN_REFLEN+1]; DBUG_ENTER("dump_remote_log_entries"); + /* + Even if we already read one binlog (case of >=2 binlogs on command line), + we cannot re-use the same connection as before, because it is now dead + (COM_BINLOG_DUMP kills the thread when it finishes). + */ + mysql= safe_connect(); + net= &mysql->net; old_format = check_master_version(mysql); - if (!position) - position = BIN_LOG_HEADER_SIZE; // protect the innocent from spam - if (position < BIN_LOG_HEADER_SIZE) - { - position = BIN_LOG_HEADER_SIZE; - // warn the user - sql_print_error("Warning: The position in the binary log can't be less than %d.\nStarting from position %d\n", BIN_LOG_HEADER_SIZE, BIN_LOG_HEADER_SIZE); - } - int4store(buf, position); + /* + COM_BINLOG_DUMP accepts only 4 bytes for the position, so we are forced to + cast to uint32. + */ + int4store(buf, (uint32)start_position); int2store(buf + BIN_LOG_HEADER_SIZE, binlog_flags); logname_len = (uint) strlen(logname); int4store(buf + 6, 0); @@ -685,33 +777,32 @@ static int dump_remote_log_entries(const char* logname) if (simple_command(mysql, COM_BINLOG_DUMP, buf, logname_len + 10, 1)) { fprintf(stderr,"Got fatal error sending the log dump command\n"); - DBUG_RETURN(1); + error= 1; + goto err; } - my_off_t old_off= position; - ulonglong rec_count= 0; - char fname[FN_REFLEN+1]; - for (;;) { - const char *error; + const char *error_msg; len = net_safe_read(mysql); if (len == packet_error) { fprintf(stderr, "Got error reading packet from server: %s\n", mysql_error(mysql)); - DBUG_RETURN(1); + error= 1; + goto err; } if (len < 8 && net->read_pos[0] == 254) break; // end of data DBUG_PRINT("info",( "len= %u, net->read_pos[5] = %d\n", len, net->read_pos[5])); Log_event *ev = Log_event::read_log_event((const char*) net->read_pos + 1 , - len - 1, &error, old_format); + len - 1, &error_msg, old_format); if (!ev) { fprintf(stderr, "Could not construct log event object\n"); - DBUG_RETURN(1); + error= 1; + goto err; } Log_event_type type= ev->get_type_code(); @@ -735,22 +826,32 @@ static int dump_remote_log_entries(const char* logname) which are about the binlogs, so which would trigger the end-detection below. */ - if ((rev->when == 0) && !to_last_remote_log) + if (rev->when == 0) { - if ((rev->ident_len != logname_len) || - memcmp(rev->new_log_ident, logname, logname_len)) - DBUG_RETURN(0); - /* - Otherwise, this is a fake Rotate for our log, at the very beginning - for sure. Skip it, because it was not in the original log. If we - are running with to_last_remote_log, we print it, because it serves - as a useful marker between binlogs then. - */ - continue; + if (!to_last_remote_log) + { + if ((rev->ident_len != logname_len) || + memcmp(rev->new_log_ident, logname, logname_len)) + { + error= 0; + goto err; + } + /* + Otherwise, this is a fake Rotate for our log, at the very + beginning for sure. Skip it, because it was not in the original + log. If we are running with to_last_remote_log, we print it, + because it serves as a useful marker between binlogs then. + */ + continue; + } + len= 1; // fake Rotate, so don't increment old_off } } - if (process_event(&rec_count,last_db,ev,old_off,old_format)) - DBUG_RETURN(1); + if ((error= process_event(last_db,ev,old_off,old_format))) + { + error= ((error < 0) ? 0 : 1); + goto err; + } } else { @@ -760,29 +861,35 @@ static int dump_remote_log_entries(const char* logname) File file; if ((file= load_processor.prepare_new_file_for_old_format(le,fname)) < 0) - DBUG_RETURN(1); + { + error= 1; + goto err; + } - if (process_event(&rec_count,last_db,ev,old_off,old_format)) + if ((error= process_event(last_db,ev,old_off,old_format))) { my_close(file,MYF(MY_WME)); - DBUG_RETURN(1); + error= ((error < 0) ? 0 : 1); + goto err; } if (load_processor.load_old_format_file(net,old_fname,old_len,file)) { my_close(file,MYF(MY_WME)); - DBUG_RETURN(1); + error= 1; + goto err; } my_close(file,MYF(MY_WME)); } /* Let's adjust offset for remote log as for local log to produce - similar text. As we don't print the fake Rotate event, all events are - real so we can simply add the length. + similar text. */ old_off+= len-1; } - DBUG_RETURN(0); +err: + mysql_close(mysql); + DBUG_RETURN(error); } @@ -817,7 +924,6 @@ static int dump_local_log_entries(const char* logname) { File fd = -1; IO_CACHE cache,*file= &cache; - ulonglong rec_count = 0; char last_db[FN_REFLEN+1]; byte tmp_buff[BIN_LOG_HEADER_SIZE]; bool old_format = 0; @@ -829,7 +935,7 @@ static int dump_local_log_entries(const char* logname) { if ((fd = my_open(logname, O_RDONLY | O_BINARY, MYF(MY_WME))) < 0) return 1; - if (init_io_cache(file, fd, 0, READ_CACHE, (my_off_t) position, 0, + if (init_io_cache(file, fd, 0, READ_CACHE, start_position_mot, 0, MYF(MY_WME | MY_NABP))) { my_close(fd, MYF(MY_WME)); @@ -843,12 +949,12 @@ static int dump_local_log_entries(const char* logname) 0, MYF(MY_WME | MY_NABP | MY_DONT_CHECK_FILESIZE))) return 1; old_format = check_header(file); - if (position) + if (start_position) { - /* skip 'position' characters from stdout */ + /* skip 'start_position' characters from stdout */ byte buff[IO_SIZE]; my_off_t length,tmp; - for (length= (my_off_t) position ; length > 0 ; length-=tmp) + for (length= start_position_mot ; length > 0 ; length-=tmp) { tmp=min(length,sizeof(buff)); if (my_b_read(file, buff, (uint) tmp)) @@ -858,11 +964,11 @@ static int dump_local_log_entries(const char* logname) } } } - file->pos_in_file=position; + file->pos_in_file= start_position_mot; file->seek_not_done=0; } - if (!position) + if (!start_position) { // Skip header if (my_b_read(file, tmp_buff, BIN_LOG_HEADER_SIZE)) @@ -891,9 +997,10 @@ static int dump_local_log_entries(const char* logname) // file->error == 0 means EOF, that's OK, we break in this case break; } - if (process_event(&rec_count,last_db,ev,old_off,false)) + if ((error= process_event(last_db,ev,old_off,false))) { - error= 1; + if (error < 0) + error= 0; break; } } @@ -909,11 +1016,14 @@ end: int main(int argc, char** argv) { static char **defaults_argv; - int exit_value; + int exit_value= 0; + ulonglong save_stop_position; MY_INIT(argv[0]); DBUG_ENTER("main"); DBUG_PROCESS(argv[0]); + init_time(); // for time functions + parse_args(&argc, (char***)&argv); defaults_argv=argv; @@ -925,8 +1035,6 @@ int main(int argc, char** argv) } my_set_max_open_files(open_files_limit); - if (remote_opt) - mysql = safe_connect(); MY_TMPDIR tmpdir; tmpdir.list= 0; @@ -944,24 +1052,26 @@ int main(int argc, char** argv) else load_processor.init_by_cur_dir(); - exit_value= 0; fprintf(result_file, "/*!40019 SET @@session.max_insert_delayed_threads=0*/;\n"); - while (--argc >= 0) + for (save_stop_position= stop_position, stop_position= ~(my_off_t)0 ; + (--argc >= 0) && !stop_passed ; ) { + if (argc == 0) // last log, --stop-position applies + stop_position= save_stop_position; if (dump_log_entries(*(argv++))) { exit_value=1; break; } + // For next log, --start-position does not apply + start_position= BIN_LOG_HEADER_SIZE; } if (tmpdir.list) free_tmpdir(&tmpdir); if (result_file != stdout) my_fclose(result_file, MYF(0)); - if (remote_opt) - mysql_close(mysql); cleanup(); free_defaults(defaults_argv); my_free_open_file_info(); diff --git a/include/my_time.h b/include/my_time.h index e42f7e9e402..1212f0533e2 100644 --- a/include/my_time.h +++ b/include/my_time.h @@ -41,6 +41,13 @@ str_to_datetime(const char *str, uint length, MYSQL_TIME *l_time, bool str_to_time(const char *str,uint length, MYSQL_TIME *l_time, int *was_cut); +long calc_daynr(uint year,uint month,uint day); + +void init_time(void); + +my_time_t +my_system_gmt_sec(const MYSQL_TIME *t, long *my_timezone, bool *in_dst_time_gap); + C_MODE_END #endif /* _my_time_h_ */ diff --git a/include/mysql_time.h b/include/mysql_time.h index 943d018fc14..32da27ba33e 100644 --- a/include/mysql_time.h +++ b/include/mysql_time.h @@ -34,4 +34,13 @@ typedef struct st_mysql_time enum enum_mysql_timestamp_type time_type; } MYSQL_TIME; + +/* + Portable time_t replacement. + Should be signed and hold seconds for 1902-2038 range. +*/ +typedef long my_time_t; +#define MY_TIME_T_MAX LONG_MAX +#define MY_TIME_T_MIN LONG_MIN + #endif /* _mysql_time_h_ */ diff --git a/mysql-test/r/mysqlbinlog2.result b/mysql-test/r/mysqlbinlog2.result new file mode 100644 index 00000000000..3c1b85e05a1 --- /dev/null +++ b/mysql-test/r/mysqlbinlog2.result @@ -0,0 +1,446 @@ +drop table if exists t1; +reset master; +set @a=UNIX_TIMESTAMP("2020-01-21 15:32:22"); +set timestamp=@a; +create table t1 (a int auto_increment not null primary key, b char(3)); +insert into t1 values(null, "a"); +insert into t1 values(null, "b"); +set timestamp=@a+2; +insert into t1 values(null, "c"); +set timestamp=@a+4; +insert into t1 values(null, "d"); +insert into t1 values(null, "e"); +flush logs; +set timestamp=@a+1; +insert into t1 values(null, "f"); + +--- Local -- +/*!40019 SET @@session.max_insert_delayed_threads=0*/; +use test; +SET TIMESTAMP=1579609942; +create table t1 (a int auto_increment not null primary key, b char(3)); +SET INSERT_ID=1; +SET TIMESTAMP=1579609942; +insert into t1 values(null, "a"); +SET INSERT_ID=2; +SET TIMESTAMP=1579609942; +insert into t1 values(null, "b"); +SET INSERT_ID=3; +SET TIMESTAMP=1579609944; +insert into t1 values(null, "c"); +SET INSERT_ID=4; +SET TIMESTAMP=1579609946; +insert into t1 values(null, "d"); +SET INSERT_ID=5; +SET TIMESTAMP=1579609946; +insert into t1 values(null, "e"); + +--- offset -- +/*!40019 SET @@session.max_insert_delayed_threads=0*/; +SET INSERT_ID=1; +use test; +SET TIMESTAMP=1579609942; +insert into t1 values(null, "a"); +SET INSERT_ID=2; +SET TIMESTAMP=1579609942; +insert into t1 values(null, "b"); +SET INSERT_ID=3; +SET TIMESTAMP=1579609944; +insert into t1 values(null, "c"); +SET INSERT_ID=4; +SET TIMESTAMP=1579609946; +insert into t1 values(null, "d"); +SET INSERT_ID=5; +SET TIMESTAMP=1579609946; +insert into t1 values(null, "e"); + +--- start-position -- +/*!40019 SET @@session.max_insert_delayed_threads=0*/; +use test; +SET TIMESTAMP=1579609946; +insert into t1 values(null, "d"); +SET INSERT_ID=5; +SET TIMESTAMP=1579609946; +insert into t1 values(null, "e"); + +--- stop-position -- +/*!40019 SET @@session.max_insert_delayed_threads=0*/; +use test; +SET TIMESTAMP=1579609942; +create table t1 (a int auto_increment not null primary key, b char(3)); +SET INSERT_ID=1; +SET TIMESTAMP=1579609942; +insert into t1 values(null, "a"); +SET INSERT_ID=2; +SET TIMESTAMP=1579609942; +insert into t1 values(null, "b"); +SET INSERT_ID=3; +SET TIMESTAMP=1579609944; +insert into t1 values(null, "c"); +SET INSERT_ID=4; + +--- start-datetime -- +/*!40019 SET @@session.max_insert_delayed_threads=0*/; +SET INSERT_ID=3; +use test; +SET TIMESTAMP=1579609944; +insert into t1 values(null, "c"); +SET INSERT_ID=4; +SET TIMESTAMP=1579609946; +insert into t1 values(null, "d"); +SET INSERT_ID=5; +SET TIMESTAMP=1579609946; +insert into t1 values(null, "e"); + +--- stop-datetime -- +/*!40019 SET @@session.max_insert_delayed_threads=0*/; +use test; +SET TIMESTAMP=1579609942; +create table t1 (a int auto_increment not null primary key, b char(3)); +SET INSERT_ID=1; +SET TIMESTAMP=1579609942; +insert into t1 values(null, "a"); +SET INSERT_ID=2; +SET TIMESTAMP=1579609942; +insert into t1 values(null, "b"); + +--- Local with 2 binlogs on command line -- +/*!40019 SET @@session.max_insert_delayed_threads=0*/; +use test; +SET TIMESTAMP=1579609942; +create table t1 (a int auto_increment not null primary key, b char(3)); +SET INSERT_ID=1; +SET TIMESTAMP=1579609942; +insert into t1 values(null, "a"); +SET INSERT_ID=2; +SET TIMESTAMP=1579609942; +insert into t1 values(null, "b"); +SET INSERT_ID=3; +SET TIMESTAMP=1579609944; +insert into t1 values(null, "c"); +SET INSERT_ID=4; +SET TIMESTAMP=1579609946; +insert into t1 values(null, "d"); +SET INSERT_ID=5; +SET TIMESTAMP=1579609946; +insert into t1 values(null, "e"); +SET INSERT_ID=6; +use test; +SET TIMESTAMP=1579609943; +insert into t1 values(null, "f"); + +--- offset -- +/*!40019 SET @@session.max_insert_delayed_threads=0*/; +SET INSERT_ID=1; +use test; +SET TIMESTAMP=1579609942; +insert into t1 values(null, "a"); +SET INSERT_ID=2; +SET TIMESTAMP=1579609942; +insert into t1 values(null, "b"); +SET INSERT_ID=3; +SET TIMESTAMP=1579609944; +insert into t1 values(null, "c"); +SET INSERT_ID=4; +SET TIMESTAMP=1579609946; +insert into t1 values(null, "d"); +SET INSERT_ID=5; +SET TIMESTAMP=1579609946; +insert into t1 values(null, "e"); +SET INSERT_ID=6; +use test; +SET TIMESTAMP=1579609943; +insert into t1 values(null, "f"); + +--- start-position -- +/*!40019 SET @@session.max_insert_delayed_threads=0*/; +use test; +SET TIMESTAMP=1579609946; +insert into t1 values(null, "d"); +SET INSERT_ID=5; +SET TIMESTAMP=1579609946; +insert into t1 values(null, "e"); +SET INSERT_ID=6; +use test; +SET TIMESTAMP=1579609943; +insert into t1 values(null, "f"); + +--- stop-position -- +/*!40019 SET @@session.max_insert_delayed_threads=0*/; +use test; +SET TIMESTAMP=1579609942; +create table t1 (a int auto_increment not null primary key, b char(3)); +SET INSERT_ID=1; +SET TIMESTAMP=1579609942; +insert into t1 values(null, "a"); +SET INSERT_ID=2; +SET TIMESTAMP=1579609942; +insert into t1 values(null, "b"); +SET INSERT_ID=3; +SET TIMESTAMP=1579609944; +insert into t1 values(null, "c"); +SET INSERT_ID=4; +SET TIMESTAMP=1579609946; +insert into t1 values(null, "d"); +SET INSERT_ID=5; +SET TIMESTAMP=1579609946; +insert into t1 values(null, "e"); +SET INSERT_ID=6; + +--- start-datetime -- +/*!40019 SET @@session.max_insert_delayed_threads=0*/; +SET INSERT_ID=3; +use test; +SET TIMESTAMP=1579609944; +insert into t1 values(null, "c"); +SET INSERT_ID=4; +SET TIMESTAMP=1579609946; +insert into t1 values(null, "d"); +SET INSERT_ID=5; +SET TIMESTAMP=1579609946; +insert into t1 values(null, "e"); +SET INSERT_ID=6; +use test; +SET TIMESTAMP=1579609943; +insert into t1 values(null, "f"); + +--- stop-datetime -- +/*!40019 SET @@session.max_insert_delayed_threads=0*/; +use test; +SET TIMESTAMP=1579609942; +create table t1 (a int auto_increment not null primary key, b char(3)); +SET INSERT_ID=1; +SET TIMESTAMP=1579609942; +insert into t1 values(null, "a"); +SET INSERT_ID=2; +SET TIMESTAMP=1579609942; +insert into t1 values(null, "b"); + +--- Remote -- +/*!40019 SET @@session.max_insert_delayed_threads=0*/; +use test; +SET TIMESTAMP=1579609942; +create table t1 (a int auto_increment not null primary key, b char(3)); +SET INSERT_ID=1; +SET TIMESTAMP=1579609942; +insert into t1 values(null, "a"); +SET INSERT_ID=2; +SET TIMESTAMP=1579609942; +insert into t1 values(null, "b"); +SET INSERT_ID=3; +SET TIMESTAMP=1579609944; +insert into t1 values(null, "c"); +SET INSERT_ID=4; +SET TIMESTAMP=1579609946; +insert into t1 values(null, "d"); +SET INSERT_ID=5; +SET TIMESTAMP=1579609946; +insert into t1 values(null, "e"); + +--- offset -- +/*!40019 SET @@session.max_insert_delayed_threads=0*/; +SET INSERT_ID=1; +use test; +SET TIMESTAMP=1579609942; +insert into t1 values(null, "a"); +SET INSERT_ID=2; +SET TIMESTAMP=1579609942; +insert into t1 values(null, "b"); +SET INSERT_ID=3; +SET TIMESTAMP=1579609944; +insert into t1 values(null, "c"); +SET INSERT_ID=4; +SET TIMESTAMP=1579609946; +insert into t1 values(null, "d"); +SET INSERT_ID=5; +SET TIMESTAMP=1579609946; +insert into t1 values(null, "e"); + +--- start-position -- +/*!40019 SET @@session.max_insert_delayed_threads=0*/; +use test; +SET TIMESTAMP=1579609946; +insert into t1 values(null, "d"); +SET INSERT_ID=5; +SET TIMESTAMP=1579609946; +insert into t1 values(null, "e"); + +--- stop-position -- +/*!40019 SET @@session.max_insert_delayed_threads=0*/; +use test; +SET TIMESTAMP=1579609942; +create table t1 (a int auto_increment not null primary key, b char(3)); +SET INSERT_ID=1; +SET TIMESTAMP=1579609942; +insert into t1 values(null, "a"); +SET INSERT_ID=2; +SET TIMESTAMP=1579609942; +insert into t1 values(null, "b"); +SET INSERT_ID=3; +SET TIMESTAMP=1579609944; +insert into t1 values(null, "c"); +SET INSERT_ID=4; + +--- start-datetime -- +/*!40019 SET @@session.max_insert_delayed_threads=0*/; +SET INSERT_ID=3; +use test; +SET TIMESTAMP=1579609944; +insert into t1 values(null, "c"); +SET INSERT_ID=4; +SET TIMESTAMP=1579609946; +insert into t1 values(null, "d"); +SET INSERT_ID=5; +SET TIMESTAMP=1579609946; +insert into t1 values(null, "e"); + +--- stop-datetime -- +/*!40019 SET @@session.max_insert_delayed_threads=0*/; +use test; +SET TIMESTAMP=1579609942; +create table t1 (a int auto_increment not null primary key, b char(3)); +SET INSERT_ID=1; +SET TIMESTAMP=1579609942; +insert into t1 values(null, "a"); +SET INSERT_ID=2; +SET TIMESTAMP=1579609942; +insert into t1 values(null, "b"); + +--- Remote with 2 binlogs on command line -- +/*!40019 SET @@session.max_insert_delayed_threads=0*/; +use test; +SET TIMESTAMP=1579609942; +create table t1 (a int auto_increment not null primary key, b char(3)); +SET INSERT_ID=1; +SET TIMESTAMP=1579609942; +insert into t1 values(null, "a"); +SET INSERT_ID=2; +SET TIMESTAMP=1579609942; +insert into t1 values(null, "b"); +SET INSERT_ID=3; +SET TIMESTAMP=1579609944; +insert into t1 values(null, "c"); +SET INSERT_ID=4; +SET TIMESTAMP=1579609946; +insert into t1 values(null, "d"); +SET INSERT_ID=5; +SET TIMESTAMP=1579609946; +insert into t1 values(null, "e"); +SET INSERT_ID=6; +use test; +SET TIMESTAMP=1579609943; +insert into t1 values(null, "f"); + +--- offset -- +/*!40019 SET @@session.max_insert_delayed_threads=0*/; +SET INSERT_ID=1; +use test; +SET TIMESTAMP=1579609942; +insert into t1 values(null, "a"); +SET INSERT_ID=2; +SET TIMESTAMP=1579609942; +insert into t1 values(null, "b"); +SET INSERT_ID=3; +SET TIMESTAMP=1579609944; +insert into t1 values(null, "c"); +SET INSERT_ID=4; +SET TIMESTAMP=1579609946; +insert into t1 values(null, "d"); +SET INSERT_ID=5; +SET TIMESTAMP=1579609946; +insert into t1 values(null, "e"); +SET INSERT_ID=6; +use test; +SET TIMESTAMP=1579609943; +insert into t1 values(null, "f"); + +--- start-position -- +/*!40019 SET @@session.max_insert_delayed_threads=0*/; +use test; +SET TIMESTAMP=1579609946; +insert into t1 values(null, "d"); +SET INSERT_ID=5; +SET TIMESTAMP=1579609946; +insert into t1 values(null, "e"); +SET INSERT_ID=6; +use test; +SET TIMESTAMP=1579609943; +insert into t1 values(null, "f"); + +--- stop-position -- +/*!40019 SET @@session.max_insert_delayed_threads=0*/; +use test; +SET TIMESTAMP=1579609942; +create table t1 (a int auto_increment not null primary key, b char(3)); +SET INSERT_ID=1; +SET TIMESTAMP=1579609942; +insert into t1 values(null, "a"); +SET INSERT_ID=2; +SET TIMESTAMP=1579609942; +insert into t1 values(null, "b"); +SET INSERT_ID=3; +SET TIMESTAMP=1579609944; +insert into t1 values(null, "c"); +SET INSERT_ID=4; +SET TIMESTAMP=1579609946; +insert into t1 values(null, "d"); +SET INSERT_ID=5; +SET TIMESTAMP=1579609946; +insert into t1 values(null, "e"); +SET INSERT_ID=6; + +--- start-datetime -- +/*!40019 SET @@session.max_insert_delayed_threads=0*/; +SET INSERT_ID=3; +use test; +SET TIMESTAMP=1579609944; +insert into t1 values(null, "c"); +SET INSERT_ID=4; +SET TIMESTAMP=1579609946; +insert into t1 values(null, "d"); +SET INSERT_ID=5; +SET TIMESTAMP=1579609946; +insert into t1 values(null, "e"); +SET INSERT_ID=6; +use test; +SET TIMESTAMP=1579609943; +insert into t1 values(null, "f"); + +--- stop-datetime -- +/*!40019 SET @@session.max_insert_delayed_threads=0*/; +use test; +SET TIMESTAMP=1579609942; +create table t1 (a int auto_increment not null primary key, b char(3)); +SET INSERT_ID=1; +SET TIMESTAMP=1579609942; +insert into t1 values(null, "a"); +SET INSERT_ID=2; +SET TIMESTAMP=1579609942; +insert into t1 values(null, "b"); + +--- to-last-log -- +/*!40019 SET @@session.max_insert_delayed_threads=0*/; +use test; +SET TIMESTAMP=1579609942; +create table t1 (a int auto_increment not null primary key, b char(3)); +SET INSERT_ID=1; +SET TIMESTAMP=1579609942; +insert into t1 values(null, "a"); +SET INSERT_ID=2; +SET TIMESTAMP=1579609942; +insert into t1 values(null, "b"); +SET INSERT_ID=3; +SET TIMESTAMP=1579609944; +insert into t1 values(null, "c"); +SET INSERT_ID=4; +SET TIMESTAMP=1579609946; +insert into t1 values(null, "d"); +SET INSERT_ID=5; +SET TIMESTAMP=1579609946; +insert into t1 values(null, "e"); +SET INSERT_ID=6; +SET TIMESTAMP=1579609943; +insert into t1 values(null, "f"); + +--- end of test -- +drop table t1; diff --git a/mysql-test/t/mysqlbinlog2.test b/mysql-test/t/mysqlbinlog2.test new file mode 100644 index 00000000000..c6cff7558d4 --- /dev/null +++ b/mysql-test/t/mysqlbinlog2.test @@ -0,0 +1,156 @@ +# Test for the new options --start-datetime, stop-datetime, +# and a few others. + +--disable_warnings +drop table if exists t1; +--enable_warnings +reset master; + +# We need this for getting fixed timestamps inside of this test. +# I use a date in the future to keep a growing timestamp along the +# binlog (including the Start_log_event). This test will work +# unchanged everywhere, because mysql-test-run has fixed TZ, which it +# exports (so mysqlbinlog has same fixed TZ). +set @a=UNIX_TIMESTAMP("2020-01-21 15:32:22"); +set timestamp=@a; +create table t1 (a int auto_increment not null primary key, b char(3)); +insert into t1 values(null, "a"); +insert into t1 values(null, "b"); +set timestamp=@a+2; +insert into t1 values(null, "c"); +set timestamp=@a+4; +insert into t1 values(null, "d"); +insert into t1 values(null, "e"); + +flush logs; +set timestamp=@a+1; # this could happen on a slave +insert into t1 values(null, "f"); + +# delimiters are for easier debugging in future + +--disable_query_log +select "--- Local --" as ""; +--enable_query_log + +# +# We should use --short-form everywhere because in other case output will +# be time dependent (the Start events). Better than nothing. +# + +--exec $MYSQL_BINLOG --short-form $MYSQL_TEST_DIR/var/log/master-bin.000001 + +--disable_query_log +select "--- offset --" as ""; +--enable_query_log +--exec $MYSQL_BINLOG --short-form --offset=2 $MYSQL_TEST_DIR/var/log/master-bin.000001 +--disable_query_log +select "--- start-position --" as ""; +--enable_query_log +--exec $MYSQL_BINLOG --short-form --start-position=497 $MYSQL_TEST_DIR/var/log/master-bin.000001 +--disable_query_log +select "--- stop-position --" as ""; +--enable_query_log +--exec $MYSQL_BINLOG --short-form --stop-position=497 $MYSQL_TEST_DIR/var/log/master-bin.000001 +--disable_query_log +select "--- start-datetime --" as ""; +--enable_query_log +--exec $MYSQL_BINLOG --short-form "--start-datetime=2020-01-21 15:32:24" $MYSQL_TEST_DIR/var/log/master-bin.000001 +--disable_query_log +select "--- stop-datetime --" as ""; +--enable_query_log +--exec $MYSQL_BINLOG --short-form "--stop-datetime=2020-01-21 15:32:24" $MYSQL_TEST_DIR/var/log/master-bin.000001 + +--disable_query_log +select "--- Local with 2 binlogs on command line --" as ""; +--enable_query_log + +# This is to verify that some options apply only to first, or last binlog + +--exec $MYSQL_BINLOG --short-form $MYSQL_TEST_DIR/var/log/master-bin.000001 $MYSQL_TEST_DIR/var/log/master-bin.000002 + +--disable_query_log +select "--- offset --" as ""; +--enable_query_log +--exec $MYSQL_BINLOG --short-form --offset=2 $MYSQL_TEST_DIR/var/log/master-bin.000001 $MYSQL_TEST_DIR/var/log/master-bin.000002 +--disable_query_log +select "--- start-position --" as ""; +--enable_query_log +--exec $MYSQL_BINLOG --short-form --start-position=497 $MYSQL_TEST_DIR/var/log/master-bin.000001 $MYSQL_TEST_DIR/var/log/master-bin.000002 +--disable_query_log +select "--- stop-position --" as ""; +--enable_query_log +--exec $MYSQL_BINLOG --short-form --stop-position=32 $MYSQL_TEST_DIR/var/log/master-bin.000001 $MYSQL_TEST_DIR/var/log/master-bin.000002 +--disable_query_log +select "--- start-datetime --" as ""; +--enable_query_log +--exec $MYSQL_BINLOG --short-form "--start-datetime=2020-01-21 15:32:24" $MYSQL_TEST_DIR/var/log/master-bin.000001 $MYSQL_TEST_DIR/var/log/master-bin.000002 +--disable_query_log +select "--- stop-datetime --" as ""; +--enable_query_log +--exec $MYSQL_BINLOG --short-form "--stop-datetime=2020-01-21 15:32:24" $MYSQL_TEST_DIR/var/log/master-bin.000001 $MYSQL_TEST_DIR/var/log/master-bin.000002 + +--disable_query_log +select "--- Remote --" as ""; +--enable_query_log + +--exec $MYSQL_BINLOG --short-form --read-from-remote-server --user=root --host=127.0.0.1 --port=$MASTER_MYPORT master-bin.000001 + +--disable_query_log +select "--- offset --" as ""; +--enable_query_log +--exec $MYSQL_BINLOG --short-form --offset=2 --read-from-remote-server --user=root --host=127.0.0.1 --port=$MASTER_MYPORT master-bin.000001 +--disable_query_log +select "--- start-position --" as ""; +--enable_query_log +--exec $MYSQL_BINLOG --short-form --start-position=497 --read-from-remote-server --user=root --host=127.0.0.1 --port=$MASTER_MYPORT master-bin.000001 +--disable_query_log +select "--- stop-position --" as ""; +--enable_query_log +--exec $MYSQL_BINLOG --short-form --stop-position=497 --read-from-remote-server --user=root --host=127.0.0.1 --port=$MASTER_MYPORT master-bin.000001 +--disable_query_log +select "--- start-datetime --" as ""; +--enable_query_log +--exec $MYSQL_BINLOG --short-form "--start-datetime=2020-01-21 15:32:24" --read-from-remote-server --user=root --host=127.0.0.1 --port=$MASTER_MYPORT master-bin.000001 +--disable_query_log +select "--- stop-datetime --" as ""; +--enable_query_log +--exec $MYSQL_BINLOG --short-form "--stop-datetime=2020-01-21 15:32:24" --read-from-remote-server --user=root --host=127.0.0.1 --port=$MASTER_MYPORT master-bin.000001 + +--disable_query_log +select "--- Remote with 2 binlogs on command line --" as ""; +--enable_query_log + +--exec $MYSQL_BINLOG --short-form --read-from-remote-server --user=root --host=127.0.0.1 --port=$MASTER_MYPORT master-bin.000001 master-bin.000002 + +--disable_query_log +select "--- offset --" as ""; +--enable_query_log +--exec $MYSQL_BINLOG --short-form --offset=2 --read-from-remote-server --user=root --host=127.0.0.1 --port=$MASTER_MYPORT master-bin.000001 master-bin.000002 +--disable_query_log +select "--- start-position --" as ""; +--enable_query_log +--exec $MYSQL_BINLOG --short-form --start-position=497 --read-from-remote-server --user=root --host=127.0.0.1 --port=$MASTER_MYPORT master-bin.000001 master-bin.000002 +--disable_query_log +select "--- stop-position --" as ""; +--enable_query_log +--exec $MYSQL_BINLOG --short-form --stop-position=32 --read-from-remote-server --user=root --host=127.0.0.1 --port=$MASTER_MYPORT master-bin.000001 master-bin.000002 +--disable_query_log +select "--- start-datetime --" as ""; +--enable_query_log +--exec $MYSQL_BINLOG --short-form "--start-datetime=20200121153224" --read-from-remote-server --user=root --host=127.0.0.1 --port=$MASTER_MYPORT master-bin.000001 master-bin.000002 +--disable_query_log +select "--- stop-datetime --" as ""; +--enable_query_log +--exec $MYSQL_BINLOG --short-form "--stop-datetime=2020/01/21 15@32@24" --read-from-remote-server --user=root --host=127.0.0.1 --port=$MASTER_MYPORT master-bin.000001 master-bin.000002 + +--disable_query_log +select "--- to-last-log --" as ""; +--enable_query_log + +--exec $MYSQL_BINLOG --short-form --read-from-remote-server --user=root --host=127.0.0.1 --port=$MASTER_MYPORT --to-last-log master-bin.000001 + +# clean up +--disable_query_log +select "--- end of test --" as ""; +--enable_query_log +drop table t1; diff --git a/sql-common/my_time.c b/sql-common/my_time.c index 46c84ac9ba7..24c19be47ba 100644 --- a/sql-common/my_time.c +++ b/sql-common/my_time.c @@ -35,6 +35,16 @@ static uchar internal_format_positions[]= static char time_separator=':'; +static ulong const days_at_timestart=719528; /* daynr at 1970.01.01 */ +uchar days_in_month[]= {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 0}; + +/* + Offset of system time zone from UTC in seconds used to speed up + work of my_system_gmt_sec() function. +*/ +static long my_time_zone=0; + + /* Convert a timestamp string to a MYSQL_TIME value. @@ -559,3 +569,148 @@ fractional: } +/* + Prepare offset of system time zone from UTC for my_system_gmt_sec() func. + + SYNOPSIS + init_time() +*/ +void init_time(void) +{ + time_t seconds; + struct tm *l_time,tm_tmp;; + MYSQL_TIME my_time; + bool not_used; + + seconds= (time_t) time((time_t*) 0); + localtime_r(&seconds,&tm_tmp); + l_time= &tm_tmp; + my_time_zone= 3600; /* Comp. for -3600 in my_gmt_sec */ + my_time.year= (uint) l_time->tm_year+1900; + my_time.month= (uint) l_time->tm_mon+1; + my_time.day= (uint) l_time->tm_mday; + my_time.hour= (uint) l_time->tm_hour; + my_time.minute= (uint) l_time->tm_min; + my_time.second= (uint) l_time->tm_sec; + my_system_gmt_sec(&my_time, &my_time_zone, ¬_used); /* Init my_time_zone */ +} + + + /* Calculate nr of day since year 0 in new date-system (from 1615) */ + +long calc_daynr(uint year,uint month,uint day) +{ + long delsum; + int temp; + DBUG_ENTER("calc_daynr"); + + if (year == 0 && month == 0 && day == 0) + DBUG_RETURN(0); /* Skip errors */ + if (year < 200) + { + if ((year=year+1900) < 1900+YY_PART_YEAR) + year+=100; + } + delsum= (long) (365L * year+ 31*(month-1) +day); + if (month <= 2) + year--; + else + delsum-= (long) (month*4+23)/10; + temp=(int) ((year/100+1)*3)/4; + DBUG_PRINT("exit",("year: %d month: %d day: %d -> daynr: %ld", + year+(month <= 2),month,day,delsum+year/4-temp)); + DBUG_RETURN(delsum+(int) year/4-temp); +} /* calc_daynr */ + + +/* + Convert time in MYSQL_TIME representation in system time zone to its + my_time_t form (number of seconds in UTC since begginning of Unix Epoch). + + SYNOPSIS + my_system_gmt_sec() + t - time value to be converted + my_timezone - pointer to long where offset of system time zone + from UTC will be stored for caching + in_dst_time_gap - set to true if time falls into spring time-gap + + NOTES + The idea is to cache the time zone offset from UTC (including daylight + saving time) for the next call to make things faster. But currently we + just calculate this offset during startup (by calling init_time() + function) and use it all the time. + Time value provided should be legal time value (e.g. '2003-01-01 25:00:00' + is not allowed). + + RETURN VALUE + Time in UTC seconds since Unix Epoch representation. +*/ +my_time_t +my_system_gmt_sec(const MYSQL_TIME *t, long *my_timezone, bool *in_dst_time_gap) +{ + uint loop; + time_t tmp; + struct tm *l_time,tm_tmp; + long diff, current_timezone; + + /* + Calculate the gmt time based on current time and timezone + The -1 on the end is to ensure that if have a date that exists twice + (like 2002-10-27 02:00:0 MET), we will find the initial date. + + By doing -3600 we will have to call localtime_r() several times, but + I couldn't come up with a better way to get a repeatable result :( + + We can't use mktime() as it's buggy on many platforms and not thread safe. + */ + tmp=(time_t) (((calc_daynr((uint) t->year,(uint) t->month,(uint) t->day) - + (long) days_at_timestart)*86400L + (long) t->hour*3600L + + (long) (t->minute*60 + t->second)) + (time_t) my_time_zone - + 3600); + current_timezone= my_time_zone; + + localtime_r(&tmp,&tm_tmp); + l_time=&tm_tmp; + for (loop=0; + loop < 2 && + (t->hour != (uint) l_time->tm_hour || + t->minute != (uint) l_time->tm_min); + loop++) + { /* One check should be enough ? */ + /* Get difference in days */ + int days= t->day - l_time->tm_mday; + if (days < -1) + days= 1; // Month has wrapped + else if (days > 1) + days= -1; + diff=(3600L*(long) (days*24+((int) t->hour - (int) l_time->tm_hour)) + + (long) (60*((int) t->minute - (int) l_time->tm_min))); + current_timezone+= diff+3600; // Compensate for -3600 above + tmp+= (time_t) diff; + localtime_r(&tmp,&tm_tmp); + l_time=&tm_tmp; + } + /* + Fix that if we are in the not existing daylight saving time hour + we move the start of the next real hour + */ + if (loop == 2 && t->hour != (uint) l_time->tm_hour) + { + int days= t->day - l_time->tm_mday; + if (days < -1) + days=1; // Month has wrapped + else if (days > 1) + days= -1; + diff=(3600L*(long) (days*24+((int) t->hour - (int) l_time->tm_hour))+ + (long) (60*((int) t->minute - (int) l_time->tm_min))); + if (diff == 3600) + tmp+=3600 - t->minute*60 - t->second; // Move to next hour + else if (diff == -3600) + tmp-=t->minute*60 + t->second; // Move to previous hour + + *in_dst_time_gap= 1; + } + *my_timezone= current_timezone; + + return (my_time_t) tmp; +} /* my_system_gmt_sec */ diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index 72ac3af70ff..f68d0951ea1 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -823,7 +823,7 @@ extern Gt_creator gt_creator; extern Lt_creator lt_creator; extern Ge_creator ge_creator; extern Le_creator le_creator; -extern uchar *days_in_month; +extern uchar days_in_month[]; extern char language[LIBLEN],reg_ext[FN_EXTLEN]; extern char glob_hostname[FN_REFLEN], mysql_home[FN_REFLEN]; extern char pidfile_name[FN_REFLEN], system_time_zone[30], *opt_init_file; @@ -989,12 +989,9 @@ void free_blobs(TABLE *table); int set_zone(int nr,int min_zone,int max_zone); ulong convert_period_to_month(ulong period); ulong convert_month_to_period(ulong month); -long calc_daynr(uint year,uint month,uint day); uint calc_days_in_year(uint year); void get_date_from_daynr(long daynr,uint *year, uint *month, uint *day); -void init_time(void); -my_time_t my_system_gmt_sec(const TIME *, long *current_timezone, bool *not_exist); my_time_t TIME_to_timestamp(THD *thd, const TIME *t, bool *not_exist); bool str_to_time_with_warn(const char *str,uint length,TIME *l_time); timestamp_type str_to_datetime_with_warn(const char *str, uint length, diff --git a/sql/time.cc b/sql/time.cc index 132612e53c5..4421b6aa00f 100644 --- a/sql/time.cc +++ b/sql/time.cc @@ -20,166 +20,9 @@ #include "mysql_priv.h" #include <m_ctype.h> -static ulong const days_at_timestart=719528; /* daynr at 1970.01.01 */ -uchar *days_in_month= (uchar*) "\037\034\037\036\037\036\037\037\036\037\036\037"; - - -/* - Offset of system time zone from UTC in seconds used to speed up - work of my_system_gmt_sec() function. -*/ -static long my_time_zone=0; - - -/* - Prepare offset of system time zone from UTC for my_system_gmt_sec() func. - - SYNOPSIS - init_time() -*/ -void init_time(void) -{ - time_t seconds; - struct tm *l_time,tm_tmp;; - TIME my_time; - bool not_used; - - seconds= (time_t) time((time_t*) 0); - localtime_r(&seconds,&tm_tmp); - l_time= &tm_tmp; - my_time_zone= 3600; /* Comp. for -3600 in my_gmt_sec */ - my_time.year= (uint) l_time->tm_year+1900; - my_time.month= (uint) l_time->tm_mon+1; - my_time.day= (uint) l_time->tm_mday; - my_time.hour= (uint) l_time->tm_hour; - my_time.minute= (uint) l_time->tm_min; - my_time.second= (uint) l_time->tm_sec; - my_system_gmt_sec(&my_time, &my_time_zone, ¬_used); /* Init my_time_zone */ -} - - -/* - Convert time in TIME representation in system time zone to its - my_time_t form (number of seconds in UTC since begginning of Unix Epoch). - - SYNOPSIS - my_system_gmt_sec() - t - time value to be converted - my_timezone - pointer to long where offset of system time zone - from UTC will be stored for caching - in_dst_time_gap - set to true if time falls into spring time-gap - - NOTES - The idea is to cache the time zone offset from UTC (including daylight - saving time) for the next call to make things faster. But currently we - just calculate this offset during startup (by calling init_time() - function) and use it all the time. - Time value provided should be legal time value (e.g. '2003-01-01 25:00:00' - is not allowed). - - RETURN VALUE - Time in UTC seconds since Unix Epoch representation. -*/ -my_time_t -my_system_gmt_sec(const TIME *t, long *my_timezone, bool *in_dst_time_gap) -{ - uint loop; - time_t tmp; - struct tm *l_time,tm_tmp; - long diff, current_timezone; - - /* - Calculate the gmt time based on current time and timezone - The -1 on the end is to ensure that if have a date that exists twice - (like 2002-10-27 02:00:0 MET), we will find the initial date. - - By doing -3600 we will have to call localtime_r() several times, but - I couldn't come up with a better way to get a repeatable result :( - - We can't use mktime() as it's buggy on many platforms and not thread safe. - */ - tmp=(time_t) (((calc_daynr((uint) t->year,(uint) t->month,(uint) t->day) - - (long) days_at_timestart)*86400L + (long) t->hour*3600L + - (long) (t->minute*60 + t->second)) + (time_t) my_time_zone - - 3600); - current_timezone= my_time_zone; - - localtime_r(&tmp,&tm_tmp); - l_time=&tm_tmp; - for (loop=0; - loop < 2 && - (t->hour != (uint) l_time->tm_hour || - t->minute != (uint) l_time->tm_min); - loop++) - { /* One check should be enough ? */ - /* Get difference in days */ - int days= t->day - l_time->tm_mday; - if (days < -1) - days= 1; // Month has wrapped - else if (days > 1) - days= -1; - diff=(3600L*(long) (days*24+((int) t->hour - (int) l_time->tm_hour)) + - (long) (60*((int) t->minute - (int) l_time->tm_min))); - current_timezone+= diff+3600; // Compensate for -3600 above - tmp+= (time_t) diff; - localtime_r(&tmp,&tm_tmp); - l_time=&tm_tmp; - } - /* - Fix that if we are in the not existing daylight saving time hour - we move the start of the next real hour - */ - if (loop == 2 && t->hour != (uint) l_time->tm_hour) - { - int days= t->day - l_time->tm_mday; - if (days < -1) - days=1; // Month has wrapped - else if (days > 1) - days= -1; - diff=(3600L*(long) (days*24+((int) t->hour - (int) l_time->tm_hour))+ - (long) (60*((int) t->minute - (int) l_time->tm_min))); - if (diff == 3600) - tmp+=3600 - t->minute*60 - t->second; // Move to next hour - else if (diff == -3600) - tmp-=t->minute*60 + t->second; // Move to previous hour - - *in_dst_time_gap= 1; - } - *my_timezone= current_timezone; - - return (my_time_t) tmp; -} /* my_system_gmt_sec */ - /* Some functions to calculate dates */ - /* Calculate nr of day since year 0 in new date-system (from 1615) */ - -long calc_daynr(uint year,uint month,uint day) -{ - long delsum; - int temp; - DBUG_ENTER("calc_daynr"); - - if (year == 0 && month == 0 && day == 0) - DBUG_RETURN(0); /* Skip errors */ - if (year < 200) - { - if ((year=year+1900) < 1900+YY_PART_YEAR) - year+=100; - } - delsum= (long) (365L * year+ 31*(month-1) +day); - if (month <= 2) - year--; - else - delsum-= (long) (month*4+23)/10; - temp=(int) ((year/100+1)*3)/4; - DBUG_PRINT("exit",("year: %d month: %d day: %d -> daynr: %ld", - year+(month <= 2),month,day,delsum+year/4-temp)); - DBUG_RETURN(delsum+(int) year/4-temp); -} /* calc_daynr */ - - #ifndef TESTTIME /* Calc weekday from daynr */ /* Returns 0 for monday, 1 for tuesday .... */ diff --git a/sql/tztime.h b/sql/tztime.h index 334b14f4fc4..9df5f965f34 100644 --- a/sql/tztime.h +++ b/sql/tztime.h @@ -19,15 +19,10 @@ #pragma interface /* gcc class interface */ #endif -/* - Portable time_t replacement. - Should be signed and hold seconds for 1902-2038 range. -*/ -typedef long my_time_t; -#define MY_TIME_T_MAX LONG_MAX -#define MY_TIME_T_MIN LONG_MIN +#include <mysql_time.h> #if !defined(TESTTIME) && !defined(TZINFO2SQL) + /* This class represents abstract time zone and provides basic interface for TIME <-> my_time_t conversion. |