|
A test case without a join:
CREATE TABLE t1 (a1 char(2), a2 char(2)) ENGINE=InnoDB;
INSERT INTO t1 VALUES (NULL, 'xx');
CREATE TABLE t2t (b char(2), INDEX(b)) ENGINE=TokuDB;
CREATE TABLE t2i (b char(2), INDEX(b)) ENGINE=InnoDB;
INSERT INTO t2t VALUES ('yy'),('zz');
INSERT INTO t2i VALUES ('yy'),('zz');
SET optimizer_switch = 'materialization=on';
explain extended
SELECT * FROM t1 WHERE (a1, a2) NOT IN ( SELECT b, b FROM t2t);
SELECT * FROM t1 WHERE (a1, a2) NOT IN ( SELECT b, b FROM t2t);
explain extended
SELECT * FROM t1 WHERE (a1, a2) NOT IN ( SELECT b, b FROM t2i);
SELECT * FROM t1 WHERE (a1, a2) NOT IN ( SELECT b, b FROM t2i);
SET optimizer_switch = 'materialization=off';
explain extended
SELECT * FROM t1 WHERE (a1, a2) NOT IN ( SELECT b, b FROM t2t);
SELECT * FROM t1 WHERE (a1, a2) NOT IN ( SELECT b, b FROM t2t);
explain extended
SELECT * FROM t1 WHERE (a1, a2) NOT IN ( SELECT b, b FROM t2i);
SELECT * FROM t1 WHERE (a1, a2) NOT IN ( SELECT b, b FROM t2i);
|
|
The above two queries only differ in that the inner table t2i is InnoDB, t2t is Tokutek.
As one can see, the result for Tokutek is wrong. This is repeatable both with MariaDB+Tokutek, and with Mysql 5.5+Tokutek, so the bug is not MariaDB specific.
mysql> select a1, a2, (a1, a2) IN ( SELECT b, b FROM t2t) as sq from t1;
--------------
--------------
--------------
1 row in set (0.00 sec)
mysql> select a1, a2, (a1, a2) IN ( SELECT b, b FROM t2i) as sq from t1;
--------------
--------------
--------------
|
|
The immediate cause for the wrong result is that Tokutek doesn't set properly Field::null_ptr. It sets it to all bits '1', so a value is interpreted as a NULL when it shouldn't.
|
|
Upon analysis, this seems to be a bug in TokuDB. Filed as:
https://github.com/Tokutek/ft-engine/issues/152
|
|
What is a good method to debug this problem? For example, how did you find that Field::null_ptr is not set correctly? Thanks.
|
|
ha_tokudb::extra gets called to do a key read. then rnd_init gets called to do a scan on the primary. since there is no key on the primary, tokudb gets confused. here are some stack traces:
Breakpoint 4, ha_tokudb::extra (this=0x7ffda38c1078, operation=HA_EXTRA_KEYREAD) at /home/prohaska/ma5534/mariadb-5.5.34/storage/tokudb/ha_tokudb.cc:5971
(gdb) up
#1 0x0000000000674c57 in TABLE::enable_keyread (this=0x7ffda3830660) at /home/prohaska/ma5534/mariadb-5.5.34/sql/table.h:1283
(gdb)
#2 0x0000000000652bf4 in make_join_readinfo (join=0x7ffda3a26b70, options=0, no_jbuf_after=1) at /home/prohaska/ma5534/mariadb-5.5.34/sql/sql_select.cc:10164
(gdb) p tab->ref.key
$36 = 0
(gdb) p table->covering_keys
$37 =
{map = 1}
(gdb) bt
#0 ha_tokudb::extra (this=0x7ffda38c1078, operation=HA_EXTRA_KEYREAD) at /home/prohaska/ma5534/mariadb-5.5.34/storage/tokudb/ha_tokudb.cc:5971
#1 0x0000000000674c57 in TABLE::enable_keyread (this=0x7ffda3830660) at /home/prohaska/ma5534/mariadb-5.5.34/sql/table.h:1283
#2 0x0000000000652bf4 in make_join_readinfo (join=0x7ffda3a26b70, options=0, no_jbuf_after=1) at /home/prohaska/ma5534/mariadb-5.5.34/sql/sql_select.cc:10164
#3 0x000000000063c4db in JOIN::optimize (this=0x7ffda3a26b70) at /home/prohaska/ma5534/mariadb-5.5.34/sql/sql_select.cc:1646
#4 0x00000000006006b6 in st_select_lex::optimize_unflattened_subqueries (this=0x7ffd9b216a60, const_only=false) at /home/prohaska/ma5534/mariadb-5.5.34/sql/sql_lex.cc:3493
#5 0x00000000007524ee in JOIN::optimize_unflattened_subqueries (this=0x7ffda3a26640) at /home/prohaska/ma5534/mariadb-5.5.34/sql/opt_subselect.cc:4924
#6 0x000000000063c560 in JOIN::optimize (this=0x7ffda3a26640) at /home/prohaska/ma5534/mariadb-5.5.34/sql/sql_select.cc:1653
#7 0x0000000000641333 in mysql_select (thd=0x7ffd9b213060, rref_pointer_array=0x7ffd9b216cc8, tables=0x7ffda3a26078, wild_num=0, fields=..., conds=0x0, og_num=0, order=0x0, group=0x0, having=0x0, proc_p\
aram=0x0, select_options=2147748608, result=0x7ffda38d5c10, unit=0x7ffd9b216388, select_lex=0x7ffd9b216a60) at /home/prohaska/ma5534/mariadb-5.5.34/sql/sql_select.cc:3065
#8 0x0000000000637fa5 in handle_select (thd=0x7ffd9b213060, lex=0x7ffd9b2162d8, result=0x7ffda38d5c10, setup_tables_done_option=0) at /home/prohaska/ma5534/mariadb-5.5.34/sql/sql_select.cc:318
#9 0x00000000006116d2 in execute_sqlcom_select (thd=0x7ffd9b213060, all_tables=0x7ffda3a26078) at /home/prohaska/ma5534/mariadb-5.5.34/sql/sql_parse.cc:4701
#10 0x000000000060a7b0 in mysql_execute_command (thd=0x7ffd9b213060) at /home/prohaska/ma5534/mariadb-5.5.34/sql/sql_parse.cc:2234
#11 0x0000000000613de0 in mysql_parse (thd=0x7ffd9b213060, rawbuf=0x7ffda38d4078 "select a1, a2, (a1, a2) IN ( SELECT b, b FROM t2t) as sq from t1", length=64, parser_state=0x7ffff7d9a650) at /home/proha\
ska/ma5534/mariadb-5.5.34/sql/sql_parse.cc:5812
#12 0x0000000000607d28 in dispatch_command (command=COM_QUERY, thd=0x7ffd9b213060, packet=0x7ffd9b235061 "select a1, a2, (a1, a2) IN ( SELECT b, b FROM t2t) as sq from t1", packet_length=64) at /home/pro\
haska/ma5534/mariadb-5.5.34/sql/sql_parse.cc:1082
#13 0x0000000000606ecb in do_command (thd=0x7ffd9b213060) at /home/prohaska/ma5534/mariadb-5.5.34/sql/sql_parse.cc:797
#14 0x000000000070a0c2 in do_handle_one_connection (thd_arg=0x7ffd9b213060) at /home/prohaska/ma5534/mariadb-5.5.34/sql/sql_connect.cc:1266
#15 0x0000000000709b81 in handle_one_connection (arg=0x7ffd9b213060) at /home/prohaska/ma5534/mariadb-5.5.34/sql/sql_connect.cc:1181
#16 0x0000003efa607851 in start_thread () from /lib64/libpthread.so.0
#17 0x0000003ef9ae894d in clone () from /lib64/libc.so.6
(gdb) c
Continuing.
15303:/home/prohaska/ma5534/mariadb-5.5.34/storage/tokudb/ha_tokudb.cc:5985:extra:return 0
Breakpoint 1, ha_tokudb::rnd_init (this=0x7ffda38c1078, scan=true) at /home/prohaska/ma5534/mariadb-5.5.34/storage/tokudb/ha_tokudb.cc:5513
(gdb) bt
#0 ha_tokudb::rnd_init (this=0x7ffda38c1078, scan=true) at /home/prohaska/ma5534/mariadb-5.5.34/storage/tokudb/ha_tokudb.cc:5513
#1 0x00000000005a4df2 in handler::ha_rnd_init (this=0x7ffda38c1078, scan=true) at /home/prohaska/ma5534/mariadb-5.5.34/sql/handler.h:2162
#2 0x0000000000859237 in subselect_uniquesubquery_engine::scan_table (this=0x7ffda3a2a4b0) at /home/prohaska/ma5534/mariadb-5.5.34/sql/item_subselect.cc:3289
#3 0x0000000000859b0c in subselect_indexsubquery_engine::exec (this=0x7ffda3a2a4b0) at /home/prohaska/ma5534/mariadb-5.5.34/sql/item_subselect.cc:3592
#4 0x0000000000851c0a in Item_subselect::exec (this=0x7ffda38d5920) at /home/prohaska/ma5534/mariadb-5.5.34/sql/item_subselect.cc:655
#5 0x0000000000852136 in Item_in_subselect::exec (this=0x7ffda38d5920) at /home/prohaska/ma5534/mariadb-5.5.34/sql/item_subselect.cc:828
#6 0x000000000085455b in Item_in_subselect::val_bool (this=0x7ffda38d5920) at /home/prohaska/ma5534/mariadb-5.5.34/sql/item_subselect.cc:1654
#7 0x000000000057532d in Item::val_bool_result (this=0x7ffda38d5920) at /home/prohaska/ma5534/mariadb-5.5.34/sql/item.h:977
#8 0x00000000007f1db2 in Item_in_optimizer::val_int (this=0x7ffda38d5f98) at /home/prohaska/ma5534/mariadb-5.5.34/sql/item_cmpfunc.cc:1729
#9 0x00000000005752a1 in Item::val_int_result (this=0x7ffda38d5f98) at /home/prohaska/ma5534/mariadb-5.5.34/sql/item.h:973
#10 0x00000000007e3685 in Item_cache_int::cache_value (this=0x7ffda3a2a5b8) at /home/prohaska/ma5534/mariadb-5.5.34/sql/item.cc:8771
#11 0x00000000007ea8a0 in Item_cache_wrapper::cache (this=0x7ffda3a2a500) at /home/prohaska/ma5534/mariadb-5.5.34/sql/item.cc:7577
#12 0x00000000007e0433 in Item_cache_wrapper::val_int (this=0x7ffda3a2a500) at /home/prohaska/ma5534/mariadb-5.5.34/sql/item.cc:7631
#13 0x00000000007dcded in Item::send (this=0x7ffda3a2a500, protocol=0x7ffd9b213628, buffer=0x7ffff7d99120) at /home/prohaska/ma5534/mariadb-5.5.34/sql/item.cc:6421
#14 0x00000000007dff9c in Item_cache_wrapper::send (this=0x7ffda3a2a500, protocol=0x7ffd9b213628, buffer=0x7ffff7d99120) at /home/prohaska/ma5534/mariadb-5.5.34/sql/item.cc:7496
#15 0x0000000000571612 in Protocol::send_result_set_row (this=0x7ffd9b213628, row_items=0x7ffd9b216b70) at /home/prohaska/ma5534/mariadb-5.5.34/sql/protocol.cc:900
#16 0x00000000005d32a0 in select_send::send_data (this=0x7ffda38d5c10, items=...) at /home/prohaska/ma5534/mariadb-5.5.34/sql/sql_class.cc:2370
#17 0x00000000006646a4 in end_send (join=0x7ffda3a26640, join_tab=0x0, end_of_records=false) at /home/prohaska/ma5534/mariadb-5.5.34/sql/sql_select.cc:17821
#18 0x000000000066144d in do_select (join=0x7ffda3a26640, fields=0x7ffd9b216b70, table=0x0, procedure=0x0) at /home/prohaska/ma5534/mariadb-5.5.34/sql/sql_select.cc:16340
#19 0x0000000000640bbe in JOIN::exec (this=0x7ffda3a26640) at /home/prohaska/ma5534/mariadb-5.5.34/sql/sql_select.cc:2859
#20 0x00000000006413c5 in mysql_select (thd=0x7ffd9b213060, rref_pointer_array=0x7ffd9b216cc8, tables=0x7ffda3a26078, wild_num=0, fields=..., conds=0x0, og_num=0, order=0x0, group=0x0, having=0x0, proc_p\
aram=0x0, select_options=2147748608, result=0x7ffda38d5c10, unit=0x7ffd9b216388, select_lex=0x7ffd9b216a60) at /home/prohaska/ma5534/mariadb-5.5.34/sql/sql_select.cc:3079
|
|
So, you're saying, TokuDB gets confused, because MariaDB enables keyread, but then uses the tables scan (with rnd_init()) and not the index scan (with index_init()) ?
I admit, this is a bit weird, but I still don't see why that could be a problem.
|
|
if key_read is set, rnd_next on a tokudb table without a PK will not unpack any of the column data.
|
|
Rich, we'll try to make sure that keyread is only requested for index scans, not for table scans.
But I'd still suggest you to fix TokuDB to ignore key_read flag in rnd_next. It is simply not applicable there.
|
|
What about a query in which the PK is a covering key?
|
|
When MariaDB (or MySQL, fwiw) is doing rnd_init/rnd_next it means a "table scan". Like, not using index, just read rows one by one in no specific order. So, a concept of "key read" is simply not applicable here.
Now many engines, InnoDB, NDB, and, as far as I understand, TokuDB too, implement a "table scan" via an index scan by the primary key. Which is fine, but the optimizer doesn't really care how the "table scan" is implemented, the optimizer doesn't specifically ask for a PK scan. From the optimizer point of view no index is used, and "key read" does not apply.
If the optimizer would want you to scan the PK, it would do index_init(idx=0), index_first, index_next, etc. And if there's a query where the PK is a covering key, optimizer usually prefers that.
|
|
in ha_tokudb::rnd_init, we now set key_read = 0 and the problem is fixed.
|
|
Rich,
Please note that during the execution of the same query, the query engine may alternatively access the same table with different access methods. Specifically for the subquery test in this bug, if the outer record contains NULL we use a table scan, if there are no NULLs, then we use an index lookup. So when we initialize the next index access, if key_read was chosen initially it has to be restored again.
I extended the test case as follows to cover this situation. Please check that all results for the same query against InnoDB and TokuDB are the same, as well as all results with materialization on/off are the same:
CREATE TABLE t1 (a1 char(2), a2 char(2)) ENGINE=InnoDB;
INSERT INTO t1 VALUES (NULL, 'xx');
INSERT INTO t1 VALUES ('yy', 'xx');
INSERT INTO t1 VALUES ('yy', NULL);
INSERT INTO t1 VALUES ('zz', 'zz');
CREATE TABLE t2t (b char(2), INDEX(b)) ENGINE=TokuDB;
CREATE TABLE t2i (b char(2), INDEX(b)) ENGINE=InnoDB;
INSERT INTO t2t VALUES ('yy'),('zz');
INSERT INTO t2i VALUES ('yy'),('zz');
SET optimizer_switch = 'materialization=on';
explain extended
SELECT * FROM t1 WHERE (a1, a2) NOT IN ( SELECT b, b FROM t2t);
SELECT * FROM t1 WHERE (a1, a2) NOT IN ( SELECT b, b FROM t2t);
explain extended
SELECT * FROM t1 WHERE (a1, a2) NOT IN ( SELECT b, b FROM t2i);
SELECT * FROM t1 WHERE (a1, a2) NOT IN ( SELECT b, b FROM t2i);
explain extended
select a1, a2, (a1, a2) IN ( SELECT b, b FROM t2t) as sq from t1;
select a1, a2, (a1, a2) IN ( SELECT b, b FROM t2t) as sq from t1;
explain extended
select a1, a2, (a1, a2) IN ( SELECT b, b FROM t2i) as sq from t1;
select a1, a2, (a1, a2) IN ( SELECT b, b FROM t2i) as sq from t1;
SET optimizer_switch = 'materialization=off';
explain extended
SELECT * FROM t1 WHERE (a1, a2) NOT IN ( SELECT b, b FROM t2t);
SELECT * FROM t1 WHERE (a1, a2) NOT IN ( SELECT b, b FROM t2t);
explain extended
SELECT * FROM t1 WHERE (a1, a2) NOT IN ( SELECT b, b FROM t2i);
SELECT * FROM t1 WHERE (a1, a2) NOT IN ( SELECT b, b FROM t2i);
explain extended
select a1, a2, (a1, a2) IN ( SELECT b, b FROM t2t) as sq from t1;
select a1, a2, (a1, a2) IN ( SELECT b, b FROM t2t) as sq from t1;
explain extended
select a1, a2, (a1, a2) IN ( SELECT b, b FROM t2i) as sq from t1;
select a1, a2, (a1, a2) IN ( SELECT b, b FROM t2i) as sq from t1;
|
|
bug fix is here:
https://github.com/Tokutek/ft-engine/commit/a16c8ef8caa9f5b14267c3599fdd6bb9395e6749
|
|
Rich,
I don't see any test case in the mentioned commit. I can add a test case, but only after we merge in your fix. Alternatively I could send you a test case that you can just copy/paste in a .test file (e.g. subselect4.test, or some TokuDB-specific test file).
Let me know how do you prefer to proceed.
|
|
I am using your test case. See this commit: https://github.com/Tokutek/mariadb/commit/219f3382426ca84eb80fa23d70719ebd46736dab. I would be interested in any additional tests.
|
|
I don't have additional tests, these cover the cases I am aware of. Perhaps I could discover other execution strategies with the same problem, and make tests for them, but that would be a rather time-consuming exercise. Since you added the test case as well, I will close this bug.
|
|
Fixed in TokuDB. Test case added to TokuDB:
https://github.com/Tokutek/mariadb/commit/219f3382426ca84eb80fa23d70719ebd46736dab
|