Bug #120297 query_term.cc:320 assert(false) crash — UNION query triggers unhandled AccessPath
Submitted: 20 Apr 8:44 Modified: 20 Apr 10:04
Reporter: Chunling Qin Email Updates:
Status: Verified Impact on me:
None 
Category:MySQL Server: Optimizer Severity:S2 (Serious)
Version: 8.0.32 OS:Any
Assigned to: CPU Architecture:Any

[20 Apr 8:44] Chunling Qin
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;
[20 Apr 10:04] Roy Lyseng
Thank you for the bug report.
Verified as described.