Bug #116669 Existing connection cannot be reused to run multiple queries in Command Services
Submitted: 14 Nov 15:36 Modified: 15 Nov 8:06
Reporter: Yura Sorokin (OCA) Email Updates:
Status: Verified Impact on me:
None 
Category:MySQL Server: Connection Handling Severity:S3 (Non-critical)
Version:8.0.40 8.4.3 9.1.0 OS:Any
Assigned to: CPU Architecture:Any
Tags: Contribution

[14 Nov 15:36] Yura Sorokin
Description:
Developers who are building a new component that uses mysql_command_xxx services (mysql_command_factory, mysql_command_query, etc.) cannot reuse the same connection to run multiple queries (tested with 'SELECT's but may also be true for 'INSERT' / 'UPDATE' / 'DELETE').
Basically, the following sequence of calls will crash the server

cmd_factory_srv->init(&mysql_h);
cmd_factory_srv->connect(mysql_h);
cmd_query_srv->query(mysql_h, "SELECT 1");
cmd_query_srv->query(mysql_h, "SELECT 1");

5   mysqld-debug                        0x0000000112ba9d98 Callback_command_delegate::field_metadata(st_send_field*, CHARSET_INFO const*) + 232
6   mysqld-debug                        0x0000000112bab241 Command_delegate::call_field_metadata(void*, st_send_field*, CHARSET_INFO const*) + 97
7   mysqld-debug                        0x000000011083fffc Protocol_callback::send_field_metadata(Send_field*, CHARSET_INFO const*) + 172
8   mysqld-debug                        0x0000000110410149 THD::send_result_metadata(mem_root_deque<Item*> const&, unsigned int) + 713
9   mysqld-debug                        0x00000001102b7f1f Query_result_send::send_result_set_metadata(THD*, mem_root_deque<Item*> const&, unsigned int) + 47
10  mysqld-debug                        0x00000001106c17d6 Query_expression::ExecuteIteratorQuery(THD*) + 806
11  mysqld-debug                        0x00000001106c2294 Query_expression::execute(THD*) + 324
12  mysqld-debug                        0x00000001105dcc40 Sql_cmd_dml::execute_inner(THD*) + 224
13  mysqld-debug                        0x00000001105dbba6 Sql_cmd_dml::execute(THD*) + 2470
14  mysqld-debug                        0x000000011052d0b0 mysql_execute_command(THD*, bool) + 30640
15  mysqld-debug                        0x00000001105235d0 dispatch_sql_command(THD*, Parser_state*) + 2784
16  mysqld-debug                        0x000000011051eb3d dispatch_command(THD*, COM_DATA const*, enum_server_command) + 9005
17  mysqld-debug                        0x000000011084b062 Srv_session::execute_command(enum_server_command, COM_DATA const*, CHARSET_INFO const*, st_command_service_cbs const*, cs_text_or_binary, void*) + 1362
18  mysqld-debug                        0x000000011080eb9f command_service_run_command(Srv_session*, enum_server_command, COM_DATA const*, CHARSET_INFO const*, st_command_service_cbs const*, cs_text_or_binary, void*) + 111
19  mysqld-debug                        0x0000000112baa545 cs::csi_advanced_command(MYSQL*, enum_server_command, unsigned char const*, unsigned long, unsigned char const*, unsigned long, bool, MYSQL_STMT*) + 709
20  mysqld-debug                        0x00000001107ba3f5 mysql_send_query + 773
21  mysqld-debug                        0x00000001107bb1da mysql_real_query + 378
22  mysqld-debug                        0x0000000112ba6f07 mysql_command_services_imp::query(MYSQL_H_imp*, char const*, unsigned long) + 71
23  component_test_mysql_command_servic 0x000000011ba6f0dd test_mysql_command_services_udf(UDF_INIT*, UDF_ARGS*, char*, unsigned long*, unsigned char*, unsigned char*) + 813
24  mysqld-debug                        0x000000010fee9e13 udf_handler::val_str(String*, String*) + 499
25  mysqld-debug                        0x000000010feead87 Item_func_udf_str::val_str(String*) + 119
26  mysqld-debug                        0x000000010fe29b9b Item::send(Protocol*, String*) + 107
27  mysqld-debug                        0x00000001104104b8 THD::send_result_set_row(mem_root_deque<Item*> const&) + 360
28  mysqld-debug                        0x00000001102b802f Query_result_send::send_data(THD*, mem_root_deque<Item*> const&) + 95
29  mysqld-debug                        0x00000001106c1e42 Query_expression::ExecuteIteratorQuery(THD*) + 2450
30  mysqld-debug                        0x00000001106c2294 Query_expression::execute(THD*) + 324
31  mysqld-debug                        0x00000001105dcc40 Sql_cmd_dml::execute_inner(THD*) + 224
32  mysqld-debug                        0x00000001105dbba6 Sql_cmd_dml::execute(THD*) + 2470
33  mysqld-debug                        0x000000011052d0b0 mysql_execute_command(THD*, bool) + 30640
34  mysqld-debug                        0x00000001105235d0 dispatch_sql_command(THD*, Parser_state*) + 2784
35  mysqld-debug                        0x000000011051eb3d dispatch_command(THD*, COM_DATA const*, enum_server_command) + 9005
36  mysqld-debug                        0x0000000110521ba7 do_command(THD*) + 2087
37  mysqld-debug                        0x0000000110810264 handle_connection(void*) + 500
38  mysqld-debug                        0x0000000112a432a4 pfs_spawn_thread(void*) + 324
39  libsystem_pthread.dylib             0x00007ff809557253 _pthread_start + 99
40  libsystem_pthread.dylib             0x00007ff809552bef thread_start + 15

Most probably the issue was introduced by this commit
https://github.com/mysql/mysql-server/commit/5dc1a146fc5e61e35fd7861f3cf2fc28f3d655da
(in the 'csi_advanced_command()' part). Although it fixes the memory leak, 'mcs_extn->consumer_srv_data' when is not nullptr cannot be simply re-used here as its 'mcs_extn->data' member has already been set to nullptr by 'std::exchange()' inside 'csi_read_rows()'.

How to repeat:
Apply the attached "steps_to_reproduce.diff" patch. It modifies the "test_mysql_command_services" test component so that its "test_mysql_command_services_udf" UDF now takes one more parameter - the number of query executions.

It also extends the 'test_sevices.test_mysql_command_services_component' MTR test case with 2 more calls

SELECT test_mysql_command_services_udf("SELECT 1");
SELECT test_mysql_command_services_udf("SELECT 1", 2);

Suggested fix:
Instead of simply reusing 'mcs_extn->consumer_srv_data', call factory_srv->end() and 'factory_srv->start()' in case when this value is not nullptr.
See the attached "suggested_fix.diff" patch.
[14 Nov 15:44] Yura Sorokin
Bug#116669 steps to reprodice

(*) I confirm the code being submitted is offered under the terms of the OCA, and that I am authorized to contribute it.

Contribution: bug116669_steps_to_reproduce.diff (application/octet-stream, text), 8.66 KiB.

[14 Nov 15:45] Yura Sorokin
Bug#116669 suggested fix

(*) I confirm the code being submitted is offered under the terms of the OCA, and that I am authorized to contribute it.

Contribution: bug116669_suggested_fix.diff (application/octet-stream, text), 1.11 KiB.

[14 Nov 15:54] Yura Sorokin
The patches should also work for 8.4.3 and 9.1.0.
[15 Nov 8:06] MySQL Verification Team
Hello Yura Sorokin,

Thank you for the report and contribution.

regards,
Umesh