[MDEV-12564] Server crashes in Item_func_trig_cond::val_int Created: 2017-04-23  Updated: 2017-04-25  Resolved: 2017-04-25

Status: Closed
Project: MariaDB Server
Component/s: Optimizer
Affects Version/s: 10.2
Fix Version/s: 10.2.6

Type: Bug Priority: Major
Reporter: Elena Stepanova Assignee: Igor Babaev
Resolution: Fixed Votes: 0
Labels: 10.2-ga, regression


 Description   

CREATE TABLE t (pk INT PRIMARY KEY, i INT NOT NULL, c VARCHAR(8), KEY(c)) ENGINE=MyISAM;
INSERT INTO t VALUES (1,10,'foo'),(2,20,'bar');
 
SELECT * FROM t t1 RIGHT JOIN t t2 ON (t2.pk = t1.pk)
WHERE (t2.i, t2.pk) NOT IN ( SELECT t3.i, t3.i FROM t t3, t t4 ) AND t1.c = 'foo';

10.2 54a995cd2206995f6dd675cabdce12a4b7ff7540

#3  <signal handler called>
#4  0x00007f0296837a4d in Item_func_trig_cond::val_int (this=0x7f027c03d758) at /data/src/10.2/sql/item_cmpfunc.h:609
#5  0x00007f0296a30c9b in Item::val_bool (this=0x7f027c03d758) at /data/src/10.2/sql/item.cc:112
#6  0x00007f0296a68562 in Item_cond_and::val_int (this=0x7f027c040120) at /data/src/10.2/sql/item_cmpfunc.cc:4972
#7  0x00007f0296820f65 in evaluate_join_record (join=0x7f027c017180, join_tab=0x7f027c03f210, error=0) at /data/src/10.2/sql/sql_select.cc:18553
#8  0x00007f0296820b9f in sub_select (join=0x7f027c017180, join_tab=0x7f027c03f210, end_of_records=false) at /data/src/10.2/sql/sql_select.cc:18458
#9  0x00007f0296820137 in do_select (join=0x7f027c017180, procedure=0x0) at /data/src/10.2/sql/sql_select.cc:18002
#10 0x00007f02967fad61 in JOIN::exec_inner (this=0x7f027c017180) at /data/src/10.2/sql/sql_select.cc:3464
#11 0x00007f02967fa266 in JOIN::exec (this=0x7f027c017180) at /data/src/10.2/sql/sql_select.cc:3267
#12 0x00007f0296aeb9a9 in subselect_single_select_engine::exec (this=0x7f027c015c28) at /data/src/10.2/sql/item_subselect.cc:3832
#13 0x00007f0296ae0b89 in Item_subselect::exec (this=0x7f027c015a28) at /data/src/10.2/sql/item_subselect.cc:716
#14 0x00007f0296ae119e in Item_in_subselect::exec (this=0x7f027c015a28) at /data/src/10.2/sql/item_subselect.cc:896
#15 0x00007f0296ae3cc9 in Item_in_subselect::val_bool (this=0x7f027c015a28) at /data/src/10.2/sql/item_subselect.cc:1743
#16 0x00007f02966ef723 in Item::val_bool_result (this=0x7f027c015a28) at /data/src/10.2/sql/item.h:1187
#17 0x00007f0296a5da50 in Item_in_optimizer::val_int (this=0x7f027c0177e0) at /data/src/10.2/sql/item_cmpfunc.cc:1621
#18 0x00007f02966ef697 in Item::val_int_result (this=0x7f027c0177e0) at /data/src/10.2/sql/item.h:1183
#19 0x00007f0296a4b7cf in Item_cache_int::cache_value (this=0x7f027c041428) at /data/src/10.2/sql/item.cc:9463
#20 0x00007f0296a557e4 in Item_cache_wrapper::cache (this=0x7f027c041370) at /data/src/10.2/sql/item.cc:8209
#21 0x00007f0296a48226 in Item_cache_wrapper::val_bool (this=0x7f027c041370) at /data/src/10.2/sql/item.cc:8373
#22 0x00007f0296a59aa0 in Item_func_not::val_int (this=0x7f027c015c68) at /data/src/10.2/sql/item_cmpfunc.cc:284
#23 0x00007f0296820f65 in evaluate_join_record (join=0x7f027c0162b8, join_tab=0x7f027c03b990, error=0) at /data/src/10.2/sql/sql_select.cc:18553
#24 0x00007f0296820b9f in sub_select (join=0x7f027c0162b8, join_tab=0x7f027c03b990, end_of_records=false) at /data/src/10.2/sql/sql_select.cc:18458
#25 0x00007f029682128f in evaluate_join_record (join=0x7f027c0162b8, join_tab=0x7f027c03b5e0, error=0) at /data/src/10.2/sql/sql_select.cc:18678
#26 0x00007f0296820b9f in sub_select (join=0x7f027c0162b8, join_tab=0x7f027c03b5e0, end_of_records=false) at /data/src/10.2/sql/sql_select.cc:18458
#27 0x00007f0296820137 in do_select (join=0x7f027c0162b8, procedure=0x0) at /data/src/10.2/sql/sql_select.cc:18002
#28 0x00007f02967fad61 in JOIN::exec_inner (this=0x7f027c0162b8) at /data/src/10.2/sql/sql_select.cc:3464
#29 0x00007f02967fa266 in JOIN::exec (this=0x7f027c0162b8) at /data/src/10.2/sql/sql_select.cc:3267
#30 0x00007f02967fb3d9 in mysql_select (thd=0x7f027c000b00, tables=0x7f027c012618, wild_num=1, fields=..., conds=0x7f027c016040, og_num=0, order=0x0, group=0x0, having=0x0, proc_param=0x0, select_options=2147748608, result=0x7f027c016298, unit=0x7f027c0045e0, select_lex=0x7f027c004d10) at /data/src/10.2/sql/sql_select.cc:3659
#31 0x00007f02967efea2 in handle_select (thd=0x7f027c000b00, lex=0x7f027c004518, result=0x7f027c016298, setup_tables_done_option=0) at /data/src/10.2/sql/sql_select.cc:373
#32 0x00007f02967bc223 in execute_sqlcom_select (thd=0x7f027c000b00, all_tables=0x7f027c012618) at /data/src/10.2/sql/sql_parse.cc:6431
#33 0x00007f02967b2262 in mysql_execute_command (thd=0x7f027c000b00) at /data/src/10.2/sql/sql_parse.cc:3448
#34 0x00007f02967bfbf6 in mysql_parse (thd=0x7f027c000b00, rawbuf=0x7f027c012348 "SELECT * FROM t t1 RIGHT JOIN t t2 ON (t2.pk = t1.pk)\nWHERE (t2.i, t2.pk) NOT IN ( SELECT t3.i, t3.i FROM t t3, t t4 ) AND t1.c = 'foo'", length=135, parser_state=0x7f028e5cc210, is_com_multi=false, is_next_command=false) at /data/src/10.2/sql/sql_parse.cc:7874
#35 0x00007f02967adcce in dispatch_command (command=COM_QUERY, thd=0x7f027c000b00, packet=0x7f027c0087a1 "", packet_length=135, is_com_multi=false, is_next_command=false) at /data/src/10.2/sql/sql_parse.cc:1812
#36 0x00007f02967ac63e in do_command (thd=0x7f027c000b00) at /data/src/10.2/sql/sql_parse.cc:1362
#37 0x00007f02968f6e05 in do_handle_one_connection (connect=0x7f0299d43260) at /data/src/10.2/sql/sql_connect.cc:1354
#38 0x00007f02968f6b92 in handle_one_connection (arg=0x7f0299d43260) at /data/src/10.2/sql/sql_connect.cc:1260
#39 0x00007f0296d0feb8 in pfs_spawn_thread (arg=0x7f0299d1db10) at /data/src/10.2/storage/perfschema/pfs.cc:1862
#40 0x00007f0295dd2064 in start_thread (arg=0x7f028e5cd700) at pthread_create.c:309
#41 0x00007f02941c262d in clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:111

The crash seems to have appeared in 10.2 tree with this commit:

commit 00ab154d49853e20f48a516897e14bf67c58671e
Author: Igor Babaev <igor@askmonty.org>
Date:   Mon Apr 3 15:59:38 2017 -0700
 
    Fixed bug mdev-10454.



 Comments   
Comment by Igor Babaev [ 2017-04-23 ]

Started parallel execution with 10.0 where the test case works fine:
In Item_in_subselect::create_row_in_to_exists_cond()
10.0 line 2399:

if (!abort_on_null && left_expr->element_index(i)->maybe_null)
(gdb) p dbug_print_item((Item*)left_expr)
$8 = 0x186e4e0 <dbug_item_print_buf> "(`test`.`t2`.`i`,`test`.`t2`.`pk`)"

10.2 line 2482 (the same):

if (!abort_on_null && left_expr->element_index(i)->maybe_null)
(gdb) p dbug_print_item((Item*)left_expr)
$24 = 0x1cf2a20 <dbug_item_print_buf> "(t2.i,t1.pk)"

The original expression is (t2.i, t2.pk).
This raises the first question: who makes the substitution in 10.2 and why?
Formally the substitution is ok as RIGHT JOIN is converted into INNER JOIN and
ON condition (t2.pk = t1.pk) is added WHERE.
The problem appears here:
10.2

(gdb) p/x ((Item*)(((Item*)left_expr)->element_index(1)))->maybe_null
$25 = 0x1

It means that maybe_null flag hasn't been corrected after the conversion.
Let's change the left operand of NOT IN predicate for (t2.pk = t1.pk) on 10.0:

SELECT * FROM t t1 RIGHT JOIN t t2 ON (t2.pk = t1.pk) WHERE (t2.i, t1.pk) NOT IN ( SELECT t3.i, t3.i FROM t t3, t t4 ) AND t1.c = 'foo';

Then we have in 10.0:

(gdb) p dbug_print_item((Item*)left_expr)
$13 = 0x186e4e0 <dbug_item_print_buf> "(`test`.`t2`.`i`,`test`.`t1`.`pk`)"
(gdb) p/x ((Item*)(((Item*)left_expr)->element_index(1)))->maybe_null
$14 = 0x1

Now we can continue comparison:
We came to the next statement :

if (!(item= new (thd->mem_root)
  Item_func_trig_cond(thd, item, get_cond_guard(i))))

and after stepping into get_cond_guard() we see:

return pushed_cond_guards ? pushed_cond_guards + i : NULL

with pushed_cond_guards == NULL in 10.2 and pushed_cond_guards != NULL in 10.0
pushed_cond_guards is set up in the caller Item_in_subselect::create_in_to_exists_cond() in the line

init_cond_guards();

We have to return to this line.
In function Item_in_subselect::init_cond_guards() in line

if (!abort_on_null && left_expr->maybe_null && !pushed_cond_guards)

we have:
in 10.2

(gdb) p ((Item*) left_expr)->maybe_null
$26 = false

this is correct, but here pushed_cond_guards remains NULL
In 10.0 we have

(gdb) p ((Item*) left_expr)->maybe_null
$15 = true

This is incorrect, but this forces creation of pushed_cond_guards.
So in 10.2 we have two bugs with maybe_null flags that leads to creation of unnecessary, but valid Item _trig_cond:

MariaDB [test]> explain extended SELECT * FROM t t1 RIGHT JOIN t t2 ON (t2.pk = t1.pk) WHERE (t2.i, t1.pk) NOT IN ( SELECT t3.i, t3.i FROM t t3, t t4 ) AND t1.c = 'foo';
+------+--------------------+-------+--------+---------------+---------+---------+------------+------+----------+-------------------------------------------------+
| id   | select_type        | table | type   | possible_keys | key     | key_len | ref        | rows | filtered | Extra                                           |
+------+--------------------+-------+--------+---------------+---------+---------+------------+------+----------+-------------------------------------------------+
|    1 | PRIMARY            | t1    | ref    | PRIMARY,c     | c       | 11      | const      |    1 |   100.00 | Using index condition                           |
|    1 | PRIMARY            | t2    | eq_ref | PRIMARY       | PRIMARY | 4       | test.t1.pk |    1 |   100.00 | Using where                                     |
|    2 | DEPENDENT SUBQUERY | t3    | ALL    | NULL          | NULL    | NULL    | NULL       |    2 |   100.00 | Using where                                     |
|    2 | DEPENDENT SUBQUERY | t4    | index  | NULL          | PRIMARY | 4       | NULL       |    2 |   100.00 | Using index; Using join buffer (flat, BNL join) |
+------+--------------------+-------+--------+---------------+---------+---------+------------+------+----------+-------------------------------------------------+
4 rows in set, 1 warning (16.48 sec)
 
MariaDB [test]> show warnings;
+-------+------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Level | Code | Message                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              |
+-------+------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Note  | 1003 | select `test`.`t1`.`pk` AS `pk`,`test`.`t1`.`i` AS `i`,`test`.`t1`.`c` AS `c`,`test`.`t2`.`pk` AS `pk`,`test`.`t2`.`i` AS `i`,`test`.`t2`.`c` AS `c` from `test`.`t` `t2` join `test`.`t` `t1` where ((`test`.`t1`.`c` = 'foo') and (`test`.`t2`.`pk` = `test`.`t1`.`pk`) and (not(<expr_cache><`test`.`t2`.`i`,`test`.`t1`.`pk`>(<in_optimizer>((`test`.`t2`.`i`,`test`.`t1`.`pk`),<exists>(select `test`.`t3`.`i`,`test`.`t3`.`i` from `test`.`t` `t3` join `test`.`t` `t4` where ((<cache>(`test`.`t2`.`i`) = `test`.`t3`.`i`) and trigcond((<cache>(`test`.`t1`.`pk`) = `test`.`t3`.`i`))))))))) |
+-------+------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+

In 10.2 we have only one bug with the maybe_null flag, but as a result an Invalid Item_trig_cond is created and this causes a crash.

Comment by Igor Babaev [ 2017-04-24 ]

Here's the answer for the above question why we see (t2.i, t1.pk) instead of original (t2.i, t2.pk) in 10.2 and don't see this substitution in 10.0 when optimizing the subquery.
The expression (t2.i, t1.pk) is the result of invocation of substitute_for_best_equal_field().
In 10.0 this substitution does not happen because t2.pk lacks the reference to the equality t1.pk=t2.pk.
It lacks it because there is no specific implementation of equal_fields_propagator for the class Item_row.
In 10.2 the code that sets such references were rewritten and now such references can be set for
row elements.
To avoid this difference in substitution we'll be comparing the behaviour of 10.0 and 10.2 for the query

SELECT * FROM t t1 right JOIN t t2 ON (t2.pk = t1.pk) WHERE (t2.i, t1.pk) NOT IN ( SELECT t3.i, t3.i FROM t t3, t t4 ) AND t1.c = 'foo';

Comment by Igor Babaev [ 2017-04-24 ]

Ok, the query

SELECT * FROM t t1 right JOIN t t2 ON (t2.pk = t1.pk) WHERE (t2.i, t1.pk) NOT IN ( SELECT t3.i, t3.i FROM t t3, t t4 ) AND t1.c = 'foo';

works fine in 10.2. So the substitution of t2.pk/t1.pk is critical to demostrate the bug.

Here's what started happening after the patch that fixed the bug mdev-10454.
The patch added an implementation of propagate_equal_fields() for the class Item_row and thus opened the possibility of the above substitution.
At the prepare stage after setup_conds() called for WHERE condition had completed the flag of maybe_null of the Item_row object created for (t2.i, t2.pk) was set to false because the maybe_null flags of both elements were set to false. However the flag of maybe_null for t1.pk from ON condition were set to true because t1 was an inner table of an outer join.
At the optimization stage the outer join was converted to inner join, but the maybe_null flags were not corrected and remained the same.
So after the substitution t2.pk/t1.pk. the maybe_null flag for the row remained false while the maybe_flag for the second element of the row was true. As a result, the guards variables were not created for the elements of the row, but a guard object for the second element was created. The object were not valid because it referred to NULL as a guard variable. This ultimately caused a crash when the expression with the guard was evaluated at the execution stage.

The patch made sure that the guard objects are not created without guard variables.

Yet it does not resolve the problem of inconsistent maybe_null flags. and it might be that the problem will pop op in other pieces of code.

Comment by Igor Babaev [ 2017-04-25 ]

The fix for this bug was pushed into the 10.2 tree.

Generated at Thu Feb 08 07:58:41 UTC 2024 using Jira 8.20.16#820016-sha1:9d11dbea5f4be3d4cc21f03a88dd11d8c8687422.