Merge lp:~sergei.glushchenko/percona-server/mysqlbinlog-stdin into lp:percona-server/5.5

Proposed by Sergei Glushchenko
Status: Merged
Approved by: Alexey Kopytov
Approved revision: no longer in the source branch.
Merged at revision: 224
Proposed branch: lp:~sergei.glushchenko/percona-server/mysqlbinlog-stdin
Merge into: lp:percona-server/5.5
Diff against target: 105 lines (+93/-0)
2 files modified
patches/bug933969.patch (+92/-0)
patches/series (+1/-0)
To merge this branch: bzr merge lp:~sergei.glushchenko/percona-server/mysqlbinlog-stdin
Reviewer Review Type Date Requested Status
Alexey Kopytov (community) Approve
Stewart Smith (community) Needs Fixing
Review via email: mp+93761@code.launchpad.net

Description of the change

Bug #933969: mysqlbinlog doesn't accept stdin
mysqlbinlog can't handle stdin when "|" used.
mysqlbinlog fails after making an attempt to execute seek on pipe handle.
Buffered read used in mysqlbinlog and it's possible to seek inside buffer
window. But in check_header routine seek(0) made before any read
operation, when buffer is empty. Due to it actual seek is performed.
Solution is to avoid seek(0) operation in check_header before any read is
performed, e.g. when we are at the beginning of the file.

To post a comment you must log in.
Revision history for this message
Sergei Glushchenko (sergei.glushchenko) wrote :
Revision history for this message
Alexey Kopytov (akopytov) wrote :

Sergei,

- Wouldn't the following be a more correct fix?

--- mysys/mf_iocache2.c 2011-06-30 15:37:13 +0000
+++ mysys/mf_iocache2.c 2012-02-21 10:56:04 +0000
@@ -145,7 +145,8 @@ void my_b_seek(IO_CACHE *info,my_off_t p
   if (info->type == READ_CACHE || info->type == SEQ_READ_APPEND)
   {
     /* TODO: explain why this works if pos < info->pos_in_file */
- if ((ulonglong) offset < (ulonglong) (info->read_end - info->buffer))
+ if (offset == 0 ||
+ (ulonglong) offset < (ulonglong) (info->read_end - info->buffer))
     {
       /* The read is in the current buffer; Reuse it */
       info->read_pos = info->buffer + offset;

As in, if pos == pos_in_file, we literally don't have to do anything, including forcing a seek on next read.

- We usually prefix our test names with "percona_", and it's sometimes handy to have a bug number in the corresponding test name, so "percona_bug933969.{test,result}" would probably be better, but it's optional.

- Similarly, "bug933969.patch" looks a bit more descriptive than "mysqlbinlog.patch" to me. At least I can look up the bug without even opening the patch.

- The test case should also have "--source include/not_windows.inc" and "--source include/not_embedded.inc". We support neither Windows nor embedded, but it would be good to at least try not to break them even further. :)

- Please replace "--exec /bin/bash -c" with "--system". In which case you also don't need --short-form or "> /dev/null".

- Tests usually have SQL statements in uppercase. It's a minor thing, just to make sure all tests use a consistent style.

review: Needs Fixing
Revision history for this message
Sergei Glushchenko (sergei.glushchenko) wrote :

Alexey,

> - Wouldn't the following be a more correct fix?
>
> --- mysys/mf_iocache2.c 2011-06-30 15:37:13 +0000
> +++ mysys/mf_iocache2.c 2012-02-21 10:56:04 +0000
> @@ -145,7 +145,8 @@ void my_b_seek(IO_CACHE *info,my_off_t p
> if (info->type == READ_CACHE || info->type == SEQ_READ_APPEND)
> {
> /* TODO: explain why this works if pos < info->pos_in_file */
> - if ((ulonglong) offset < (ulonglong) (info->read_end - info->buffer))
> + if (offset == 0 ||
> + (ulonglong) offset < (ulonglong) (info->read_end - info->buffer))
> {
> /* The read is in the current buffer; Reuse it */
> info->read_pos = info->buffer + offset;
>
> As in, if pos == pos_in_file, we literally don't have to do anything,
> including forcing a seek on next read.
>

I thought about fixing this bug in similar way. Unfortunately both fixes
are not actually fix the root problem and are kind of workaround.
Drawback of your approach is when we do seek(3) instead of seek(0) it will
not work, however seek(0) was working! I mean that it creates a special case
when actually not working solution looks like working.

I thought also of calling my_b_fill() right after stdin buffer created.
The drawback of this approach is that it's not obvious why we are calling
my_b_fill() in one branch of "if" statement and not doing the same in other :)

Please comment this.

As for other things which you have mentioned, I will fix them. Thanks.
However I've seen many testcases having statements in lowercase and even
in mixed case :)

Revision history for this message
Alexey Kopytov (akopytov) wrote :

On 21.02.12 16:16, Sergei Glushchenko wrote:
> Alexey,
>
>> - Wouldn't the following be a more correct fix?
>>
>> --- mysys/mf_iocache2.c 2011-06-30 15:37:13 +0000
>> +++ mysys/mf_iocache2.c 2012-02-21 10:56:04 +0000
>> @@ -145,7 +145,8 @@ void my_b_seek(IO_CACHE *info,my_off_t p
>> if (info->type == READ_CACHE || info->type == SEQ_READ_APPEND)
>> {
>> /* TODO: explain why this works if pos < info->pos_in_file */
>> - if ((ulonglong) offset < (ulonglong) (info->read_end - info->buffer))
>> + if (offset == 0 ||
>> + (ulonglong) offset < (ulonglong) (info->read_end - info->buffer))
>> {
>> /* The read is in the current buffer; Reuse it */
>> info->read_pos = info->buffer + offset;
>>
>> As in, if pos == pos_in_file, we literally don't have to do anything,
>> including forcing a seek on next read.
>>
>
> I thought about fixing this bug in similar way. Unfortunately both fixes
> are not actually fix the root problem and are kind of workaround.

Sure, the problem problem is that mysqlbinlog doesn't have to use seek().

> Drawback of your approach is when we do seek(3) instead of seek(0) it will
> not work, however seek(0) was working! I mean that it creates a special case
> when actually not working solution looks like working.
>

There's no way to make my_b_seek(file, x) work in a general case for
pipes, because that might require a real seek anyway. The point is to
avoid doing a real seek when we don't really have to do it. In
particular, my_b_seek(file, 0) before accessing the file becomes a no-op
with the fix, as it should be. Again, in particular, it solves the
problem with unnecessary my_b_seek() in check_header().

An alternative approach would be just replace my_b_seek(file, 0) with an
assertion that the IO cache is positioned at the start of the file when
check_header() is called.

> I thought also of calling my_b_fill() right after stdin buffer created.
> The drawback of this approach is that it's not obvious why we are calling
> my_b_fill() in one branch of "if" statement and not doing the same in other :)
>
> Please comment this.
>

I agree, it would be yet another way to fix it, but the code would
become even less obvious :)

> As for other things which you have mentioned, I will fix them. Thanks.
> However I've seen many testcases having statements in lowercase and even
> in mixed case :)

Well, it's a real world, not everyone follows recommendations and common
conventions :) Some recommendations on writing good tests can be found
here: http://forge.mysql.com/wiki/How_to_Create_Good_Tests

Revision history for this message
Sergei Glushchenko (sergei.glushchenko) wrote :

>
> An alternative approach would be just replace my_b_seek(file, 0) with an
> assertion that the IO cache is positioned at the start of the file when
> check_header() is called.
>

As for me, it's the most straight and natural way to fix this particular issue.
It would be not obvious to just remove my_b_seek(file, 0) and could cause some issues
in case of changes. But put an assertion makes things clear.

If you don't mind I would prefer this fix to others.

> Well, it's a real world, not everyone follows recommendations and common
> conventions :) Some recommendations on writing good tests can be found
> here: http://forge.mysql.com/wiki/How_to_Create_Good_Tests

Thanks for that link! I've seen it once, but couldn't remember where.

Revision history for this message
Alexey Kopytov (akopytov) wrote :

On 21.02.12 17:18, Sergei Glushchenko wrote:
> As for me, it's the most straight and natural way to fix this particular issue.
> It would be not obvious to just remove my_b_seek(file, 0) and could cause some issues
> in case of changes. But put an assertion makes things clear.
>
> If you don't mind I would prefer this fix to others.

Sure, fine by me.

Revision history for this message
Alexey Kopytov (akopytov) wrote :

OK to merge, thanks!

review: Approve
Revision history for this message
Stewart Smith (stewart) wrote :
review: Needs Fixing
Revision history for this message
Sergei Glushchenko (sergei.glushchenko) wrote :

The new fix makes mysqlbinlog not to seek to start pos before header checked.
Jenkins:
http://jenkins.percona.com/view/Percona%20Server%205.5/job/percona-server-5.5-param/285/

Not all configurations are finished yed, but tendencies are good.

Fix the same as for 5.1.

(my_off_t)0 was copied from couple of lines below to keep symmetry, and yes, I don't really need typecast there :)

Revision history for this message
Alexey Kopytov (akopytov) wrote :

But the typecast below correctly has a space after it ;)

Revision history for this message
Sergei Glushchenko (sergei.glushchenko) wrote :

Oh... really... my fault :)

Revision history for this message
Alexey Kopytov (akopytov) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added file 'patches/bug933969.patch'
2--- patches/bug933969.patch 1970-01-01 00:00:00 +0000
3+++ patches/bug933969.patch 2012-02-23 09:11:18 +0000
4@@ -0,0 +1,92 @@
5+--- /dev/null
6++++ b/mysql-test/r/percona_bug933969.result
7+@@ -0,0 +1,16 @@
8++RESET MASTER;
9++DROP TABLE IF EXISTS t1;
10++CREATE TABLE t1 (word VARCHAR(20));
11++INSERT INTO t1 VALUES ("hamite");
12++INSERT INTO t1 VALUES ("hoho");
13++INSERT INTO t1 VALUES ("znamenito");
14++INSERT INTO t1 VALUES ("mrachny");
15++INSERT INTO t1 VALUES ("mrak");
16++INSERT INTO t1 VALUES ("zhut");
17++INSERT INTO t1 VALUES ("parnisha");
18++INSERT INTO t1 VALUES ("krrasota!");
19++INSERT INTO t1 VALUES ("podumayesh");
20++INSERT INTO t1 VALUES ("ogo!");
21++FLUSH LOGS;
22++DROP TABLE t1;
23++RESET MASTER;
24+--- /dev/null
25++++ b/mysql-test/t/percona_bug933969.test
26+@@ -0,0 +1,42 @@
27++###################### percona_bug933969.test ########################
28++# Bug #933969: mysqlbinlog doesn't accept stdin #
29++# #
30++# The goal of this testcase is to test that mysqlbinlog handle #
31++# stdin correctly when stdin is pipe. #
32++# i.e. "cat log | mysqlbinlog -" don't cause mysqlbinlog failure #
33++######################################################################
34++-- source include/have_log_bin.inc
35++-- source include/not_windows.inc
36++-- source include/not_embedded.inc
37++
38++# deletes all the binary logs
39++RESET MASTER;
40++
41++--disable_warnings
42++DROP TABLE IF EXISTS t1;
43++--enable_warnings
44++
45++# produce some statements for binlog
46++
47++CREATE TABLE t1 (word VARCHAR(20));
48++
49++INSERT INTO t1 VALUES ("hamite");
50++INSERT INTO t1 VALUES ("hoho");
51++INSERT INTO t1 VALUES ("znamenito");
52++INSERT INTO t1 VALUES ("mrachny");
53++INSERT INTO t1 VALUES ("mrak");
54++INSERT INTO t1 VALUES ("zhut");
55++INSERT INTO t1 VALUES ("parnisha");
56++INSERT INTO t1 VALUES ("krrasota!");
57++INSERT INTO t1 VALUES ("podumayesh");
58++INSERT INTO t1 VALUES ("ogo!");
59++
60++FLUSH LOGS;
61++
62++# run mysqlbinlog and make sure it ends normally
63++
64++let $MYSQLD_DATADIR= `SELECT @@datadir`;
65++--system cat $MYSQLD_DATADIR/master-bin.000001 | $MYSQL_BINLOG - >/dev/null
66++
67++DROP TABLE t1;
68++RESET MASTER;
69+--- a/client/mysqlbinlog.cc
70++++ b/client/mysqlbinlog.cc
71+@@ -1760,7 +1760,7 @@
72+ }
73+
74+ pos= my_b_tell(file);
75+- my_b_seek(file, (my_off_t)0);
76++ DBUG_ASSERT(pos == 0);
77+ if (my_b_read(file, header, sizeof(header)))
78+ {
79+ error("Failed reading header; probably an empty file.");
80+@@ -1920,7 +1920,7 @@
81+ /* read from normal file */
82+ if ((fd = my_open(logname, O_RDONLY | O_BINARY, MYF(MY_WME))) < 0)
83+ return ERROR_STOP;
84+- if (init_io_cache(file, fd, 0, READ_CACHE, start_position_mot, 0,
85++ if (init_io_cache(file, fd, 0, READ_CACHE, (my_off_t) 0, 0,
86+ MYF(MY_WME | MY_NABP)))
87+ {
88+ my_close(fd, MYF(MY_WME));
89+@@ -1928,6 +1928,7 @@
90+ }
91+ if ((retval= check_header(file, print_event_info, logname)) != OK_CONTINUE)
92+ goto end;
93++ my_b_seek(file, start_position_mot);
94+ }
95+ else
96+ {
97
98=== modified file 'patches/series'
99--- patches/series 2012-01-19 08:42:23 +0000
100+++ patches/series 2012-02-23 09:11:18 +0000
101@@ -1,3 +1,4 @@
102+bug933969.patch
103 microsec_process.patch
104 optimizer_fix.patch
105 mysql_dump_ignore_ct.patch

Subscribers

People subscribed via source and target branches