Commit 5296a9bf authored by unknown's avatar unknown

WL#1580: --start-datetime, --stop-datetime, --start-position (alias for...

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/
parent d4eaed03
......@@ -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
};
......@@ -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,6 +641,11 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)),
}
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();
......@@ -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 (!to_last_remote_log)
{
if ((rev->ident_len != logname_len) ||
memcmp(rev->new_log_ident, logname, logname_len))
DBUG_RETURN(0);
{
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.
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 ((error= process_event(last_db,ev,old_off,old_format)))
{
error= ((error < 0) ? 0 : 1);
goto err;
}
if (process_event(&rec_count,last_db,ev,old_off,old_format))
DBUG_RETURN(1);
}
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 @@ static int dump_local_log_entries(const char* logname)
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();
......
......@@ -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_ */
......@@ -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_ */
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;
# 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;
......@@ -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 @@ bool str_to_time(const char *str, uint length, MYSQL_TIME *l_time,
}
/*
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, &not_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 */
......@@ -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,
......
......@@ -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, &not_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 .... */
......
......@@ -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.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment