Description:
Description
When SET SESSION debug='+d,ast' is enabled, executing a UNION query with an aggregate
function triggers assert(false) in dumpAccessPath(), causing the mysqld process to abort.
Root Cause
The dumpAccessPath() function in sql/query_term.cc (lines 249–335) is an AST
debug-printing helper that iterates over the AccessPath tree via a switch-case statement.
Currently, it only handles the following AccessPath types:
- LIMIT_OFFSET, TABLE_SCAN, SORT, MATERIALIZE, FAKE_SINGLE_ROW
- TABLE_VALUE_CONSTRUCTOR, AGGREGATE, FILTER, HASH_JOIN
- FOLLOW_TAIL, MATERIALIZED_TABLE_FUNCTION, INDEX_SCAN
Not all AccessPath enum values are covered. When the optimizer produces an AccessPath type
not listed above (e.g., REMOVE_DUPLICATES, STREAM, etc.) for a UNION query, execution
falls through to the default: assert(false) branch at line 319–320, which terminates the
process.
// sql/query_term.cc:319-321
default:
assert(false); // <-- crash: unhandled AccessPath type
Call Stack
Query_expression::optimize()
→ Query_term_union::debugPrint() (query_term.cc:229)
→ Query_term_set_op::print()
→ Query_block::debugPrint()
→ Query_block::qbPrint()
→ dumpAccessPath() (query_term.cc:249)
→ assert(false) (query_term.cc:320)
Reproduction
-- 1. Create table and insert data
CREATE DATABASE IF NOT EXISTS test_repro;
USE test_repro;
DROP TABLE IF EXISTS t3;
CREATE TABLE t3 (a INT);
INSERT INTO t3 VALUES (1),(2),(3),(1),(2);
-- 2. Enable AST debug and execute UNION query
SET SESSION debug='+d,ast';
SELECT COUNT(*) FROM t3 UNION SELECT COUNT(*) FROM t3;
Result: ERROR 2013 (HY000): Lost connection to MySQL server during query
Crash Log
mysqld: /data/mysql-server/sql/query_term.cc:320:
void dumpAccessPath(int, AccessPath *, std::ostringstream &):
Assertion `false' failed.
Query: SELECT COUNT(*) FROM t3 UNION SELECT COUNT(*) FROM t3
Full Backtrace
dumpAccessPath(int, AccessPath*, std::ostringstream&)
Query_block::qbPrint(int, std::ostringstream&) const
Query_block::debugPrint(int, std::ostringstream&) const
Query_term_set_op::print(int, std::ostringstream&, char const*) const
Query_term_union::debugPrint(int, std::ostringstream&) const
Query_expression::optimize(THD*, TABLE*, bool, bool)
Sql_cmd_dml::execute_inner(THD*)
Sql_cmd_dml::execute(THD*)
mysql_execute_command(THD*, bool)
dispatch_sql_command(THD*, Parser_state*)
dispatch_command(THD*, COM_DATA const*, enum_server_command)
do_command(THD*)
How to repeat:
-- 1. create table
CREATE DATABASE IF NOT EXISTS test_repro;
USE test_repro;
DROP TABLE IF EXISTS t3;
CREATE TABLE t3 (a INT);
INSERT INTO t3 VALUES (1),(2),(3),(1),(2);
-- 2. core
SET SESSION debug='+d,ast';
SELECT COUNT(*) FROM t3 UNION SELECT COUNT(*) FROM t3;
Description: Description When SET SESSION debug='+d,ast' is enabled, executing a UNION query with an aggregate function triggers assert(false) in dumpAccessPath(), causing the mysqld process to abort. Root Cause The dumpAccessPath() function in sql/query_term.cc (lines 249–335) is an AST debug-printing helper that iterates over the AccessPath tree via a switch-case statement. Currently, it only handles the following AccessPath types: - LIMIT_OFFSET, TABLE_SCAN, SORT, MATERIALIZE, FAKE_SINGLE_ROW - TABLE_VALUE_CONSTRUCTOR, AGGREGATE, FILTER, HASH_JOIN - FOLLOW_TAIL, MATERIALIZED_TABLE_FUNCTION, INDEX_SCAN Not all AccessPath enum values are covered. When the optimizer produces an AccessPath type not listed above (e.g., REMOVE_DUPLICATES, STREAM, etc.) for a UNION query, execution falls through to the default: assert(false) branch at line 319–320, which terminates the process. // sql/query_term.cc:319-321 default: assert(false); // <-- crash: unhandled AccessPath type Call Stack Query_expression::optimize() → Query_term_union::debugPrint() (query_term.cc:229) → Query_term_set_op::print() → Query_block::debugPrint() → Query_block::qbPrint() → dumpAccessPath() (query_term.cc:249) → assert(false) (query_term.cc:320) Reproduction -- 1. Create table and insert data CREATE DATABASE IF NOT EXISTS test_repro; USE test_repro; DROP TABLE IF EXISTS t3; CREATE TABLE t3 (a INT); INSERT INTO t3 VALUES (1),(2),(3),(1),(2); -- 2. Enable AST debug and execute UNION query SET SESSION debug='+d,ast'; SELECT COUNT(*) FROM t3 UNION SELECT COUNT(*) FROM t3; Result: ERROR 2013 (HY000): Lost connection to MySQL server during query Crash Log mysqld: /data/mysql-server/sql/query_term.cc:320: void dumpAccessPath(int, AccessPath *, std::ostringstream &): Assertion `false' failed. Query: SELECT COUNT(*) FROM t3 UNION SELECT COUNT(*) FROM t3 Full Backtrace dumpAccessPath(int, AccessPath*, std::ostringstream&) Query_block::qbPrint(int, std::ostringstream&) const Query_block::debugPrint(int, std::ostringstream&) const Query_term_set_op::print(int, std::ostringstream&, char const*) const Query_term_union::debugPrint(int, std::ostringstream&) const Query_expression::optimize(THD*, TABLE*, bool, bool) Sql_cmd_dml::execute_inner(THD*) Sql_cmd_dml::execute(THD*) mysql_execute_command(THD*, bool) dispatch_sql_command(THD*, Parser_state*) dispatch_command(THD*, COM_DATA const*, enum_server_command) do_command(THD*) How to repeat: -- 1. create table CREATE DATABASE IF NOT EXISTS test_repro; USE test_repro; DROP TABLE IF EXISTS t3; CREATE TABLE t3 (a INT); INSERT INTO t3 VALUES (1),(2),(3),(1),(2); -- 2. core SET SESSION debug='+d,ast'; SELECT COUNT(*) FROM t3 UNION SELECT COUNT(*) FROM t3;