• Dmitry Shulga's avatar
    MDEV-16128: Server crash in Item_func::print_op on 2nd execution of PS · bd9274fa
    Dmitry Shulga authored
    For some queries that involve tables with different but convertible
    character sets for columns taking part in the query, repeatable
    execution of such queries in PS mode or as part of a stored routine
    would result in server abnormal termination.
    
    For example,
      CREATE TABLE t1 (a2 varchar(10));
      CREATE TABLE t2 (u1 varchar(10) CHARACTER SET utf8);
      CREATE TABLE t3 (u2 varchar(10) CHARACTER SET utf8);
      PREPARE stmt FROM
        "SELECT t1.* FROM (t1 JOIN t2 ON (t2.u1 = t1.a2))
         WHERE (EXISTS (SELECT 1 FROM t3 WHERE t3.u2 = t1.a2))";
    
      EXECUTE stmt;
      EXECUTE stmt; <== Running this prepared statement the second time
                        results in server crash.
    
    The reason of server crash is that an instance of the class
    Item_func_conv_charset, that created for conversion of a column
    from one character set to another, is allocated on execution
    memory root but pointer to this instance is stored in an item
    placed on prepared statement memory root. Below is calls trace to
    the place where an instance of the class Item_func_conv_charset
    is created.
    
    setup_conds
     Item_func::fix_fields
      Item_bool_rowready_func2::fix_length_and_dec
       Item_func::setup_args_and_comparator
        Item_func_or_sum::agg_arg_charsets_for_comparison
         Item_func_or_sum::agg_arg_charsets
          Item_func_or_sum::agg_item_set_converter
           Item::safe_charset_converter
    
    And the following trace shows the place where a pointer to
    the instance of the class Item_func_conv_charset is passed
    to the class Item_func_eq, that is created on a memory root of
    the prepared statement.
    
    Prepared_statement::execute
     mysql_execute_command
      execute_sqlcom_select
       handle_select
        mysql_select
         JOIN::optimize
          JOIN::optimize_inner
           convert_join_subqueries_to_semijoins
            convert_subq_to_sj
    
    To fix the issue, switch to the Prepared Statement memory root
    before calling the method Item_func::setup_args_and_comparator
    in order to place any created Items on permanent memory root.
    It may seem that such approach would result in a memory
    leakage in case the parameter marker '?' is used in the query
    as in the following example
      PREPARE stmt FROM
        "SELECT t1.* FROM (t1 JOIN t2 ON (t2.u1 = t1.a2))
         WHERE (EXISTS (SELECT 1 FROM t3 WHERE t3.u2 = ?))";
      EXECUTE stmt USING convert('A' using latin1);
    but it wouldn't since for such case any of the parameter markers
    is treated as a constant and no subquery to semijoin optimization
    is performed.
    bd9274fa
ps.test 130 KB