Uploaded image for project: 'MariaDB Server'
  1. MariaDB Server
  2. MDEV-25576

The statement EXPLAIN running as regular statement and as prepared statement produces different results for UPDATE with subquery

Details

    Description

      Both EXPLAIN and EXPLAIN EXTENDED produce different results set in case it is run in normal way and in PS mode for the statement UPDATE with subquery.

      MariaDB [test]> CREATE TABLE t1 (c1 INT KEY) ENGINE=MyISAM;
      Query OK, 0 rows affected (0,128 sec)
       
      MariaDB [test]> CREATE TABLE t2 (c2 INT) ENGINE=MyISAM;
      Query OK, 0 rows affected (0,023 sec)
       
      MariaDB [test]> CREATE TABLE t3 (c3 INT) ENGINE=MyISAM;
      Query OK, 0 rows affected (0,021 sec)
      

      MariaDB [test]> EXPLAIN EXTENDED UPDATE t3 SET c3 = ( SELECT COUNT(d1.c1) FROM ( SELECT a11.c1 FROM t1 AS a11 STRAIGHT_JOIN t2 AS a21 ON a21.c2 = a11.c1 JOIN t1 AS a12 ON a12.c1 = a11.c1 ) d1 );
      +------+-------------+-------+------+---------------+------+---------+------+------+----------+-----------------------------------------------------+
      | id   | select_type | table | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra                                               |
      +------+-------------+-------+------+---------------+------+---------+------+------+----------+-----------------------------------------------------+
      |    1 | PRIMARY     | t3    | ALL  | NULL          | NULL | NULL    | NULL |    0 |   100.00 |                                                     |
      |    2 | SUBQUERY    | NULL  | NULL | NULL          | NULL | NULL    | NULL | NULL |     NULL | Impossible WHERE noticed after reading const tables |
      +------+-------------+-------+------+---------------+------+---------+------+------+----------+-----------------------------------------------------+
      2 rows in set (0,002 sec)
      

      MariaDB [test]> PREPARE stmt FROM "EXPLAIN EXTENDED UPDATE t3 SET c3 = ( SELECT COUNT(d1.c1) FROM ( SELECT a11.c1 FROM t1 AS a11 STRAIGHT_JOIN t2 AS a21 ON a21.c2 = a11.c1 JOIN t1 AS a12 ON a12.c1 = a11.c1 ) d1 )";
      Query OK, 0 rows affected (0,000 sec)
      Statement prepared
       
      MariaDB [test]>  EXECUTE stmt;
      +------+-------------+-------+------+---------------+------+---------+------+------+----------+--------------------------------+
      | id   | select_type | table | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra                          |
      +------+-------------+-------+------+---------------+------+---------+------+------+----------+--------------------------------+
      |    1 | PRIMARY     | t3    | ALL  | NULL          | NULL | NULL    | NULL |    0 |   100.00 |                                |
      |    2 | SUBQUERY    | NULL  | NULL | NULL          | NULL | NULL    | NULL | NULL |     NULL | no matching row in const table |
      +------+-------------+-------+------+---------------+------+---------+------+------+----------+--------------------------------+
      2 rows in set (0,000 sec)
      

      Different results are also produced by the statement 'DELETE FROM with subquery' in case it is run in normal way and in PS mode.
      See test case below:

      MariaDB [test]> PREPARE stmt FROM "EXPLAIN DELETE FROM t3 WHERE c3 = ( SELECT COUNT(d1.c1) FROM ( SELECT a11.c1 FROM t1 AS a11 STRAIGHT_JOIN t2 AS a21 ON a21.c2 = a11.c1 JOIN t1 AS a12 ON a12.c1 = a11.c1 ) d1 )";
      Query OK, 0 rows affected (0,001 sec)
      Statement prepared
       
      MariaDB [test]> EXECUTE stmt;
      +------+-------------+-------+------+---------------+------+---------+------+------+--------------------------------+
      | id   | select_type | table | type | possible_keys | key  | key_len | ref  | rows | Extra                          |
      +------+-------------+-------+------+---------------+------+---------+------+------+--------------------------------+
      |    1 | PRIMARY     | t3    | ALL  | NULL          | NULL | NULL    | NULL |    1 | Using where                    |
      |    2 | SUBQUERY    | NULL  | NULL | NULL          | NULL | NULL    | NULL | NULL | no matching row in const table |
      +------+-------------+-------+------+---------------+------+---------+------+------+--------------------------------+
      2 rows in set (0,001 sec)
      
      

      MariaDB [test]> EXPLAIN DELETE FROM t3 WHERE c3 = ( SELECT COUNT(d1.c1) FROM ( SELECT a11.c1 FROM t1 AS a11 STRAIGHT_JOIN t2 AS a21 ON a21.c2 = a11.c1 JOIN t1 AS a12 ON a12.c1 = a11.c1 ) d1 );
      +------+-------------+-------+------+---------------+------+---------+------+------+-----------------------------------------------------+
      | id   | select_type | table | type | possible_keys | key  | key_len | ref  | rows | Extra                                               |
      +------+-------------+-------+------+---------------+------+---------+------+------+-----------------------------------------------------+
      |    1 | PRIMARY     | t3    | ALL  | NULL          | NULL | NULL    | NULL |    1 | Using where                                         |
      |    2 | SUBQUERY    | NULL  | NULL | NULL          | NULL | NULL    | NULL | NULL | Impossible WHERE noticed after reading const tables |
      +------+-------------+-------+------+---------------+------+---------+------+------+-----------------------------------------------------+
      2 rows in set (0,001 sec)
      

      Attachments

        Issue Links

          Activity

            This piece looks like some hack:

              if (conds && const_table_map != found_const_table_map &&
                  (select_options & SELECT_DESCRIBE))
              {
                conds=new (thd->mem_root) Item_int(thd, (longlong) 0, 1); // Always false
              }
            

            It was introduced by https://github.com/mariadb/server/commit/8762539d477d949a5bd4c2df8b03b28727e5ee98.

            psergei Sergei Petrunia added a comment - This piece looks like some hack: if (conds && const_table_map != found_const_table_map && (select_options & SELECT_DESCRIBE)) { conds= new (thd->mem_root) Item_int(thd, (longlong) 0, 1); // Always false } It was introduced by https://github.com/mariadb/server/commit/8762539d477d949a5bd4c2df8b03b28727e5ee98 .
            psergei Sergei Petrunia added a comment - - edited

            There are two approaches to addressing the issue (= making the EXPLAIN output the same):

            A. Unify PS and non-PS execution paths: make sure they both either pass or don't pass the SELECT_DESCRIBE flag when invoking child select's JOIN::optimize

            B. Make JOIN::optimize produce the same output regardless of whether SELECT_DESCRIBE flag is set.

            My comments above have been mostly about B.
            As far as I understand, Dmitry's efforts were about A.

            psergei Sergei Petrunia added a comment - - edited There are two approaches to addressing the issue (= making the EXPLAIN output the same): A. Unify PS and non-PS execution paths: make sure they both either pass or don't pass the SELECT_DESCRIBE flag when invoking child select's JOIN::optimize B. Make JOIN::optimize produce the same output regardless of whether SELECT_DESCRIBE flag is set. My comments above have been mostly about B. As far as I understand, Dmitry's efforts were about A.
            psergei Sergei Petrunia added a comment - - edited

            This patch fixes the test failures in bb-10.2-MDEV-25576 :

            diff --git a/sql/sql_base.cc b/sql/sql_base.cc
            index f11027546c1..01ec985227c 100644
            --- a/sql/sql_base.cc
            +++ b/sql/sql_base.cc
            @@ -8806,9 +8806,7 @@ void promote_select_describe_flag_if_needed(LEX *lex)
             {
               if (lex->describe)
               {
            -    SELECT_LEX *sl= lex->all_selects_list;
            -    for (; sl; sl= sl->next_select_in_list())
            -      sl->options|= SELECT_DESCRIBE;
            +    lex->select_lex.options |= SELECT_DESCRIBE;
               }
             }
             
            

            psergei Sergei Petrunia added a comment - - edited This patch fixes the test failures in bb-10.2- MDEV-25576 : diff --git a/sql/sql_base.cc b/sql/sql_base.cc index f11027546c1..01ec985227c 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -8806,9 +8806,7 @@ void promote_select_describe_flag_if_needed(LEX *lex) { if (lex->describe) { - SELECT_LEX *sl= lex->all_selects_list; - for (; sl; sl= sl->next_select_in_list()) - sl->options|= SELECT_DESCRIBE; + lex->select_lex.options |= SELECT_DESCRIBE; } }
            shulga Dmitry Shulga added a comment -

            For 10.4 and upper the following changeset should applied instead the original one from commit.

            diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc
            index 9fa1e015274..ff3e48f53ed 100644
            --- a/sql/sql_delete.cc
            +++ b/sql/sql_delete.cc
            @@ -378,6 +378,8 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
               query_plan.table= table;
               query_plan.updating_a_view= MY_TEST(table_list->view);
             
            +  thd->lex->promote_select_describe_flag_if_needed();
            +
               if (mysql_prepare_delete(thd, table_list, select_lex->with_wild,
                                        select_lex->item_list, &conds,
                                        &delete_while_scanning))
            diff --git a/sql/sql_lex.h b/sql/sql_lex.h
            index eb21419c7af..16bdf580317 100644
            --- a/sql/sql_lex.h
            +++ b/sql/sql_lex.h
            @@ -4595,6 +4595,16 @@ struct LEX: public Query_tables_list
               bool resolve_references_to_cte(TABLE_LIST *tables,
                                              TABLE_LIST **tables_last);
             
            +  /**
            +    Turn on the SELECT_DESCRIBE flag for the primary SELECT_LEX of
            +    the EXPLAIN UPDATE/DELETE statement.
            +  */
            +
            +  void promote_select_describe_flag_if_needed()
            +  {
            +    if (describe)
            +      builtin_select.options |= SELECT_DESCRIBE;
            +  }
             };
             
             
            diff --git a/sql/sql_update.cc b/sql/sql_update.cc
            index 85db99f3a62..5d72dd88e7f 100644
            --- a/sql/sql_update.cc
            +++ b/sql/sql_update.cc
            @@ -470,6 +470,8 @@ int mysql_update(THD *thd,
               want_privilege= (table_list->view ? UPDATE_ACL :
                                table_list->grant.want_privilege);
             #endif
            +  thd->lex->promote_select_describe_flag_if_needed();
            +
               if (mysql_prepare_update(thd, table_list, &conds, order_num, order))
                 DBUG_RETURN(1);
            

            shulga Dmitry Shulga added a comment - For 10.4 and upper the following changeset should applied instead the original one from commit. diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index 9fa1e015274..ff3e48f53ed 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -378,6 +378,8 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, query_plan.table= table; query_plan.updating_a_view= MY_TEST(table_list->view);   + thd->lex->promote_select_describe_flag_if_needed(); + if (mysql_prepare_delete(thd, table_list, select_lex->with_wild, select_lex->item_list, &conds, &delete_while_scanning)) diff --git a/sql/sql_lex.h b/sql/sql_lex.h index eb21419c7af..16bdf580317 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -4595,6 +4595,16 @@ struct LEX: public Query_tables_list bool resolve_references_to_cte(TABLE_LIST *tables, TABLE_LIST **tables_last);   + /** + Turn on the SELECT_DESCRIBE flag for the primary SELECT_LEX of + the EXPLAIN UPDATE/DELETE statement. + */ + + void promote_select_describe_flag_if_needed() + { + if (describe) + builtin_select.options |= SELECT_DESCRIBE; + } };     diff --git a/sql/sql_update.cc b/sql/sql_update.cc index 85db99f3a62..5d72dd88e7f 100644 --- a/sql/sql_update.cc +++ b/sql/sql_update.cc @@ -470,6 +470,8 @@ int mysql_update(THD *thd, want_privilege= (table_list->view ? UPDATE_ACL : table_list->grant.want_privilege); #endif + thd->lex->promote_select_describe_flag_if_needed(); + if (mysql_prepare_update(thd, table_list, &conds, order_num, order)) DBUG_RETURN(1);

            Input from the last optimizer call:

            • There's lack of agreement about the desired/acceptable level of verbosity in
              commit comments. Will follow up on this.
            • For this particular patch: if some code has cleared the SELECT_DESCRIBE,
              maybe it should be also responsible for setting it back (if I understood this
              correctly)
            • The attempt to remove "if SELECT_DESCRIBE.. " should be followed up on in a
              separate patch, for 10.7.
            psergei Sergei Petrunia added a comment - Input from the last optimizer call: There's lack of agreement about the desired/acceptable level of verbosity in commit comments. Will follow up on this. For this particular patch: if some code has cleared the SELECT_DESCRIBE, maybe it should be also responsible for setting it back (if I understood this correctly) The attempt to remove "if SELECT_DESCRIBE.. " should be followed up on in a separate patch, for 10.7.

            People

              shulga Dmitry Shulga
              shulga Dmitry Shulga
              Votes:
              0 Vote for this issue
              Watchers:
              3 Start watching this issue

              Dates

                Created:
                Updated:
                Resolved:

                Git Integration

                  Error rendering 'com.xiplink.jira.git.jira_git_plugin:git-issue-webpanel'. Please contact your Jira administrators.