Details

    • Bug
    • Status: In Review (View Workflow)
    • Major
    • Resolution: Unresolved
    • 11.4, 11.7(EOL), 11.8
    • 11.4
    • None
    • macOS 15.1.1 with clang 19. Fedora 41 x86_64 with clang 19 and clang 18.

    Description

      As of git sha a35f744d787b89c8ef8abcad1762e17b910baf2b , ASAN builds crash during the bootstrap step given the environment described in the "Environment" field on this ticket:

      =================================================================
      ==5629==ERROR: AddressSanitizer: use-after-poison on address 0x62900003ef30 at pc 0x000103e47248 bp 0x00016d3949b0 sp 0x00016d3949a8
      READ of size 8 at 0x62900003ef30 thread T0
          #0 0x103e47244 in multi_update::num_found() const sql_class.h:7649
          #1 0x103e10e70 in MYSQL_DML_DONE(THD*, int) sql_select.cc:34149
          #2 0x103e108b0 in Sql_cmd_dml::execute(THD*) sql_select.cc:34324
          #3 0x103b3ed94 in mysql_execute_command(THD*, bool) sql_parse.cc:4415
          #4 0x103b1f2d4 in mysql_parse(THD*, char*, unsigned int, Parser_state*) sql_parse.cc:7901
          #5 0x103b1dfcc in bootstrap(st_mysql_file*) sql_parse.cc:1091
          #6 0x1034dddec in mysqld_main(int, char**) mysqld.cc:6104
          #7 0x102a66e3c in main main.cc:34
          #8 0x18a384270  (<unknown module>)
       
      0x62900003ef30 is located 11568 bytes inside of 16516-byte region [0x62900003c200,0x629000040284)
      allocated by thread T0 here:
          #0 0x10adc8c04 in malloc+0x94 (libclang_rt.asan_osx_dynamic.dylib:arm64e+0x54c04)
          #1 0x105ec5438 in sf_malloc safemalloc.c:126
          #2 0x105e79480 in my_malloc my_malloc.c:93
          #3 0x105e30880 in root_alloc my_alloc.c:66
          #4 0x105e3109c in reset_root_defaults my_alloc.c:244
          #5 0x103933890 in THD::init_for_queries() sql_class.cc:1509
          #6 0x103b1d458 in bootstrap(st_mysql_file*) sql_parse.cc:1017
          #7 0x1034dddec in mysqld_main(int, char**) mysqld.cc:6104
          #8 0x102a66e3c in main main.cc:34
          #9 0x18a384270  (<unknown module>)
       
      SUMMARY: AddressSanitizer: use-after-poison sql_class.h:7649 in multi_update::num_found() const
      Shadow bytes around the buggy address:
        0x62900003ec80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
        0x62900003ed00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
        0x62900003ed80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
        0x62900003ee00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
        0x62900003ee80: 00 00 f7 00 00 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7
      =>0x62900003ef00: f7 f7 f7 f7 f7 f7[f7]f7 f7 f7 f7 f7 f7 f7 f7 f7
        0x62900003ef80: f7 f7 f7 f7 f7 00 00 f7 00 00 f7 00 00 00 00 00
        0x62900003f000: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
        0x62900003f080: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
        0x62900003f100: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
        0x62900003f180: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
      Shadow byte legend (one shadow byte represents 8 application bytes):
        Addressable:           00
        Partially addressable: 01 02 03 04 05 06 07
        Heap left redzone:       fa
        Freed heap region:       fd
        Stack left redzone:      f1
        Stack mid redzone:       f2
        Stack right redzone:     f3
        Stack after return:      f5
        Stack use after scope:   f8
        Global redzone:          f9
        Global init order:       f6
        Poisoned by user:        f7
        Container overflow:      fc
        Array cookie:            ac
        Intra object redzone:    bb
        ASan internal:           fe
        Left alloca redzone:     ca
        Right alloca redzone:    cb
      ==5629==ABORTING
      241126 10:31:39 [ERROR] mysqld got signal 6 ;
      Sorry, we probably made a mistake, and this is a bug.
       
      Your assistance in bug reporting will enable us to fix this for the next release.
      To report this bug, see https://mariadb.com/kb/en/reporting-bugs
       
      We will try our best to scrape up some info that will hopefully help
      diagnose the problem, but since we have already crashed,
      something is definitely wrong and this may fail.
       
      Server version: 11.8.0-MariaDB-debug source revision: a35f744d787b89c8ef8abcad1762e17b910baf2b
      key_buffer_size=134217728
      read_buffer_size=131072
      max_used_connections=0
      max_threads=153
      thread_count=1
      It is possible that mysqld could use up to
      key_buffer_size + (read_buffer_size + sort_buffer_size)*max_threads = 468250 K  bytes of memory
      Hope that's ok; if not, decrease some variables in the equation.
       
      Thread pointer: 0x62c000120288
      Attempting backtrace. You can use the following information to find out
      where mysqld died. If you see no messages after this, something went
      terribly wrong...
      stack_bottom = 0x16d39a180 thread_stack 0xb00000
      0   mariadbd                            0x0000000105e9174c my_print_stacktrace + 360
      Printing to addr2line failed
      0   mariadbd                            0x0000000102d7d440 handle_fatal_signal + 4100
      0   libsystem_platform.dylib            0x000000018a73c184 _sigtramp + 56
      0   libsystem_pthread.dylib             0x000000018a706f70 pthread_kill + 288
      0   libsystem_c.dylib                   0x000000018a613908 abort + 128
      0   libclang_rt.asan_osx_dynamic.dylib  0x000000010adef0bc _ZN11__sanitizer6AtexitEPFvvE + 0
      0   libclang_rt.asan_osx_dynamic.dylib  0x000000010adee7b4 _ZN11__sanitizer22SetCheckUnwindCallbackEPFvvE + 0
      0   libclang_rt.asan_osx_dynamic.dylib  0x000000010add109c _ZN6__asan16ErrorDescription5PrintEv + 0
      0   libclang_rt.asan_osx_dynamic.dylib  0x000000010add03d8 _ZN6__asan18ReportGenericErrorEmmmmbmjb + 1456
      0   libclang_rt.asan_osx_dynamic.dylib  0x000000010add18b0 __asan_report_load8 + 56
      0   mariadbd                            0x0000000103e47248 _ZNK12multi_update9num_foundEv + 76
      0   mariadbd                            0x0000000103e10e74 _ZL14MYSQL_DML_DONEP3THDi + 460
      0   mariadbd                            0x0000000103e108b4 _ZN11Sql_cmd_dml7executeEP3THD + 1652
      0   mariadbd                            0x0000000103b3ed98 _Z21mysql_execute_commandP3THDb + 39300
      0   mariadbd                            0x0000000103b1f2d8 _Z11mysql_parseP3THDPcjP12Parser_state + 2692
      0   mariadbd                            0x0000000103b1dfd0 _Z9bootstrapP13st_mysql_file + 5076
      0   mariadbd                            0x00000001034dddf0 _Z11mysqld_mainiPPc + 6640
      0   mariadbd                            0x0000000102a66e40 main + 36
      0   dyld                                0x000000018a384274 start + 2840
       
      Trying to get some variables.
      Some pointers may be invalid and cause the dump to abort.
      Query (0x62900003c2a8): update help_topic set description = CONCAT(description, '\n| FORCE                                 | Enables the plugin. If the plugin  |\n|                                       | cannot be initialized, then the    |\n|                                       | server will fail to start with an  |\n|                                       | error.                             |\n+---------------------------------------+------------------------------------+\n| FORCE_PLUS_PERMANENT                  | Enables the plugin. If the plugin  |\n|                                       | cannot be initialized, then the    |\n|                                       | server will fail to start with an  |\n|                                       | error. In addition, the plugin     |\n|                                       | cannot be uninstalled with         |\n|                                       | UNINSTALL SONAME or UNINSTALL      |\n|                                       | PLUGIN while the server is         |\n|                                       | running.                           |\n+---------------------------------------+------------------------------------+\n\nA plugin\'s status can be found by looking at the PLUGIN_STATUS column of the\ninformation_schema.PLUGINS table.\n\nUninstalling Plugins\n--------------------\n\nPlugins that are found in the mysql.plugin table, that is those that were\ninstalled with INSTALL SONAME, INSTALL PLUGIN or mariadb-plugin can be\nuninstalled in one of two ways:\n\n* The UNINSTALL SONAME or the UNINSTALL PLUGIN statement while the server is\nrunning\n* With mariadb-plugin while the server is offline.\n\nPlugins that were enabled as a --plugin-load option do not need to be\nuninstalled. If --plugin-load is omitted the next time the server starts, or\nthe plugin is not listed as one of the --plugin-load entries, the plugin will\nnot be loaded.\n\nUNINSTALL PLUGIN uninstalls a single installed plugin, while UNINSTALL SONAME\nuninstalls all plugins belonging to a given library.\n\nURL: https://mariadb.com/kb/en/plugin-overview/') WHERE help_topic_id = 79;
       
      Connection ID (thread ID): 1
      Status: NOT_KILLED
       
      Optimizer switch: index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_merge_sort_intersection=off,index_condition_pushdown=on,derived_merge=on,derived_with_keys=on,firstmatch=on,loosescan=on,materialization=on,in_to_exists=on,semijoin=on,partial_match_rowid_merge=on,partial_match_table_scan=on,subquery_cache=on,mrr=off,mrr_cost_based=off,mrr_sort_keys=off,outer_join_with_cache=on,semijoin_with_cache=on,join_cache_incremental=on,join_cache_hashed=on,join_cache_bka=on,optimize_join_buffer_size=on,table_elimination=on,extended_keys=on,exists_to_in=on,orderby_uses_equalities=on,condition_pushdown_for_derived=on,split_materialized=on,condition_pushdown_for_subquery=on,rowid_filter=on,condition_pushdown_from_having=on,not_null_range_scan=off,hash_join_cardinality=on,cset_narrowing=on,sargable_casefold=on
      

      Attachments

        Activity

          Have you considered specifying cmake -DWITH_SAFEMALLOC=OFF when specifying -DWITH_ASAN=ON? Safemalloc is duplicating part of what AddressSanitizer does. I don’t think it makes sense to enable both at the same time.

          marko Marko Mäkelä added a comment - Have you considered specifying cmake -DWITH_SAFEMALLOC=OFF when specifying -DWITH_ASAN=ON ? Safemalloc is duplicating part of what AddressSanitizer does. I don’t think it makes sense to enable both at the same time.
          Gosselin Dave Gosselin added a comment -

          Under -DWITH_ASAN=ON, I've tried running -DWITH_SAFEMALLOC=OFF and -DWITH_SAFEMALLOC=ON yet regardless we see the use-after-poison result with the same traces as given in the description.

          Gosselin Dave Gosselin added a comment - Under -DWITH_ASAN=ON , I've tried running -DWITH_SAFEMALLOC=OFF and -DWITH_SAFEMALLOC=ON yet regardless we see the use-after-poison result with the same traces as given in the description.

          I hope that there was no sf_malloc() in the reported stack trace when you were building with cmake -DWITH_SAFEMALLOC=OFF.

          I don’t know if it could be related, butwhen I build MariaDB Server 10.6 or 10.11 for AMD64 on Ubuntu 24.04 with clang-19 and -stdlib=libc++, I will get SIGSEGV crashes after almost every test. I initially thought that it could be related to PLUGIN_PERFSCHEMA or WITH_SAFEMALLOC, but it keeps happening even if I disable both. When I build with the GNU/Linux default GCC standard library using either clang 19 or GCC 13.2.0, all tests pass.

          This could be a recent regression, because I don’t remember observing this in the past. But then again, most times when I use the clang libc++ is for the -fsanitize=memory build, which is different again.

          marko Marko Mäkelä added a comment - I hope that there was no sf_malloc() in the reported stack trace when you were building with cmake -DWITH_SAFEMALLOC=OFF . I don’t know if it could be related, butwhen I build MariaDB Server 10.6 or 10.11 for AMD64 on Ubuntu 24.04 with clang-19 and -stdlib=libc++ , I will get SIGSEGV crashes after almost every test. I initially thought that it could be related to PLUGIN_PERFSCHEMA or WITH_SAFEMALLOC , but it keeps happening even if I disable both. When I build with the GNU/Linux default GCC standard library using either clang 19 or GCC 13.2.0, all tests pass. This could be a recent regression, because I don’t remember observing this in the past. But then again, most times when I use the clang libc++ is for the -fsanitize=memory build, which is different again.

          danblack reminded of something that I had observed in MDEV-20377: there are redundant calls to pthread_exit(0) that will cause clang-19 -stdlib=libc++ to crash at least WITH_MSAN. They will need to be removed from 10.5, 10.6, 10.11 still.

          marko Marko Mäkelä added a comment - danblack reminded of something that I had observed in MDEV-20377 : there are redundant calls to pthread_exit(0) that will cause clang-19 -stdlib=libc++ to crash at least WITH_MSAN . They will need to be removed from 10.5, 10.6, 10.11 still.
          Gosselin Dave Gosselin added a comment -

          10.5 appears to be unaffected by this issue as the bootstrap is successful (-DWITH_ASAN=ON and -DWITH_SAFEMALLOC=OFF, I have not tried the latter with ON).

          Gosselin Dave Gosselin added a comment - 10.5 appears to be unaffected by this issue as the bootstrap is successful (-DWITH_ASAN=ON and -DWITH_SAFEMALLOC=OFF, I have not tried the latter with ON).

          So in short Igor collect statistics about found/changed rows from results after deleting the results explicitly in the command code.

          The statistics needed only for dtrace (I do not know if it is used now, so I am close eyes in the fact it is incorrect in case of non-transactional tables after an error (always 0)).

          So rewriting cleanup/delete for this I found 1) overkill 2) collecting statistics after cleanup() also a bad idea (can be removed) 3) cleanup() we use mostly fro SP/PS.

          I propose simple way: collect statistics just before removing the result. Here is my fast and dirty patch for it (maybe also building method hierarchy is overkill but maybe all hould be rewritten on methods call (as you like)) also main.delete_returning should be fixed in this patch, but it is proof of concept

          diff --git a/sql/sql_cmd.h b/sql/sql_cmd.h
          index baa779e9610..8f01482c670 100644
          --- a/sql/sql_cmd.h
          +++ b/sql/sql_cmd.h
          @@ -20,6 +20,8 @@
           #ifndef SQL_CMD_INCLUDED
           #define SQL_CMD_INCLUDED
           
          +#include <my_base.h>
          +
           /*
             When a command is added here, be sure it's also added in mysqld.cc
             in "struct show_var_st status_vars[]= {" ...
          @@ -224,6 +226,11 @@ class Sql_cmd : public Sql_alloc
             */
             virtual bool is_dml() const { return false; }
           
          +  virtual void get_dml_stat (ha_rows &found, ha_rows &changed)
          +  {
          +    found= changed= 0;
          +  }
          +
             /**
               @brief Unprepare prepared statement for the command
               @param thd global context of the processed statement
          diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc
          index dd842f0f1c0..1e3441cdc59 100644
          --- a/sql/sql_delete.cc
          +++ b/sql/sql_delete.cc
          @@ -1851,6 +1851,13 @@ bool Sql_cmd_delete::execute_inner(THD *thd)
               }
             }
           
          +  if (res || !result)
          +  {
          +     deleted= ((multi_delete*)(get_result()))->num_deleted();
          +  }
          +  else
          +    deleted= 0;
          +
             if (result)
             {
               res= false;
          diff --git a/sql/sql_delete.h b/sql/sql_delete.h
          index 1842c5c04c7..eb59a7fa77e 100644
          --- a/sql/sql_delete.h
          +++ b/sql/sql_delete.h
          @@ -43,6 +43,7 @@ template <typename T> class SQL_I_List;
           class Sql_cmd_delete final : public Sql_cmd_dml
           {
           public:
          +  ha_rows deleted;
             Sql_cmd_delete(bool multitable_arg)
               : orig_multitable(multitable_arg), multitable(multitable_arg),
                 save_protocol(NULL)
          @@ -66,6 +67,12 @@ class Sql_cmd_delete final : public Sql_cmd_dml
           
             void remove_order_by_without_limit(THD *thd);
           
          +  void get_dml_stat (ha_rows &found, ha_rows &changed) override
          +  {
          +     found= 0;
          +     changed= deleted;
          +  }
          +
           protected:
             /**
               @brief Perform precheck of table privileges for delete statements
          diff --git a/sql/sql_select.cc b/sql/sql_select.cc
          index c3f50989e06..7c11d34a271 100644
          --- a/sql/sql_select.cc
          +++ b/sql/sql_select.cc
          @@ -34139,39 +34139,38 @@ static void MYSQL_DML_START(THD *thd)
           }
           
           
          -static void MYSQL_DML_DONE(THD *thd, int rc)
          +static void MYSQL_DML_GET_STAT(THD * thd, ha_rows &found, ha_rows &changed)
           {
             switch (thd->lex->sql_command) {
          +  case SQLCOM_UPDATE:
          +  case SQLCOM_UPDATE_MULTI:
          +  case SQLCOM_DELETE_MULTI:
          +    thd->lex->m_sql_cmd->get_dml_stat(found, changed);
          +    break;
          +  case SQLCOM_DELETE:
          +    found= 0;
          +    changed= (thd->get_row_count_func());
          +    break;
          +  default:
          +    DBUG_ASSERT(0);
          +  }
          +}
          +
           
          +static void MYSQL_DML_DONE(THD *thd, int rc, ha_rows found, ha_rows changed)
          +{
          +  switch (thd->lex->sql_command) {
             case SQLCOM_UPDATE:
          -    MYSQL_UPDATE_DONE(
          -    rc,
          -    (rc ? 0 :
          -     ((multi_update*)(((Sql_cmd_dml*)(thd->lex->m_sql_cmd))->get_result()))
          -     ->num_found()),
          -    (rc ? 0 :
          -     ((multi_update*)(((Sql_cmd_dml*)(thd->lex->m_sql_cmd))->get_result()))
          -     ->num_updated()));
          +    MYSQL_UPDATE_DONE(rc, found, changed);
               break;
             case SQLCOM_UPDATE_MULTI:
          -    MYSQL_MULTI_UPDATE_DONE(
          -    rc,
          -    (rc ? 0 :
          -     ((multi_update*)(((Sql_cmd_dml*)(thd->lex->m_sql_cmd))->get_result()))
          -     ->num_found()),
          -    (rc ? 0 :
          -     ((multi_update*)(((Sql_cmd_dml*)(thd->lex->m_sql_cmd))->get_result()))
          -     ->num_updated()));
          +    MYSQL_MULTI_UPDATE_DONE(rc, found, changed);
               break;
             case SQLCOM_DELETE:
          -    MYSQL_DELETE_DONE(rc, (rc ? 0 : (ulong) (thd->get_row_count_func())));
          +    MYSQL_DELETE_DONE(rc, changed);
               break;
             case SQLCOM_DELETE_MULTI:
          -    MYSQL_MULTI_DELETE_DONE(
          -    rc,
          -    (rc ? 0 :
          -     ((multi_delete*)(((Sql_cmd_dml*)(thd->lex->m_sql_cmd))->get_result()))
          -     ->num_deleted()));
          +    MYSQL_MULTI_DELETE_DONE(rc, changed);
               break;
             default:
               DBUG_ASSERT(0);
          @@ -34260,6 +34259,7 @@ bool Sql_cmd_dml::prepare(THD *thd)
           bool Sql_cmd_dml::execute(THD *thd)
           {
             lex = thd->lex;
          +  ha_rows found= 0, changed= 0;
             bool res;
           
             SELECT_LEX_UNIT *unit = &lex->unit;
          @@ -34310,6 +34310,8 @@ bool Sql_cmd_dml::execute(THD *thd)
           
             if (res)
               goto err;
          +  else
          +    MYSQL_DML_GET_STAT(thd, found, changed);
           
             res= unit->cleanup();
           
          @@ -34318,13 +34320,13 @@ bool Sql_cmd_dml::execute(THD *thd)
           
             THD_STAGE_INFO(thd, stage_end);
           
          -  MYSQL_DML_DONE(thd, res);
          +  MYSQL_DML_DONE(thd, 0, found, changed);
           
             return res;
           
           err:
             DBUG_ASSERT(thd->is_error() || thd->killed);
          -  MYSQL_DML_DONE(thd, 1);
          +  MYSQL_DML_DONE(thd, 1, 0, 0);
             THD_STAGE_INFO(thd, stage_end);
             (void)unit->cleanup();
             if (is_prepared())
          diff --git a/sql/sql_update.cc b/sql/sql_update.cc
          index 1e425911de0..e467b308df9 100644
          --- a/sql/sql_update.cc
          +++ b/sql/sql_update.cc
          @@ -3123,6 +3123,13 @@ bool Sql_cmd_update::execute_inner(THD *thd)
                 }
               }
             }
          +  if (res || !result)
          +  {
          +     this->found= ((multi_update*)(get_result()))->num_found();
          +     this->updated= ((multi_update*)(get_result()))->num_updated();
          +  }
          +  else
          +    this->found= this->updated= 0;
           
             if (result)
             {
          diff --git a/sql/sql_update.h b/sql/sql_update.h
          index d01ecb7354a..4dc93a98ab2 100644
          --- a/sql/sql_update.h
          +++ b/sql/sql_update.h
          @@ -45,6 +45,7 @@ bool compare_record(const TABLE *table);
           class Sql_cmd_update final : public Sql_cmd_dml
           {
           public:
          +  ha_rows found, updated;
             Sql_cmd_update(bool multitable_arg)
               : orig_multitable(multitable_arg), multitable(multitable_arg)
             {}
          @@ -65,6 +66,13 @@ class Sql_cmd_update final : public Sql_cmd_dml
           
             void set_as_multitable() { multitable= true; }
           
          +  void get_dml_stat (ha_rows &found, ha_rows &changed) override
          +  {
          +
          +     found= this->found;
          +     changed= this->updated;
          +  }
          +
           protected:
             /**
               @brief Perform precheck of table privileges for update statements
          

          sanja Oleksandr Byelkin added a comment - So in short Igor collect statistics about found/changed rows from results after deleting the results explicitly in the command code. The statistics needed only for dtrace (I do not know if it is used now, so I am close eyes in the fact it is incorrect in case of non-transactional tables after an error (always 0)). So rewriting cleanup/delete for this I found 1) overkill 2) collecting statistics after cleanup() also a bad idea (can be removed) 3) cleanup() we use mostly fro SP/PS. I propose simple way: collect statistics just before removing the result. Here is my fast and dirty patch for it (maybe also building method hierarchy is overkill but maybe all hould be rewritten on methods call (as you like)) also main.delete_returning should be fixed in this patch, but it is proof of concept diff --git a/sql/sql_cmd.h b/sql/sql_cmd.h index baa779e9610..8f01482c670 100644 --- a/sql/sql_cmd.h +++ b/sql/sql_cmd.h @@ -20,6 +20,8 @@ #ifndef SQL_CMD_INCLUDED #define SQL_CMD_INCLUDED +#include <my_base.h> + /* When a command is added here, be sure it's also added in mysqld.cc in "struct show_var_st status_vars[]= {" ... @@ -224,6 +226,11 @@ class Sql_cmd : public Sql_alloc */ virtual bool is_dml() const { return false; } + virtual void get_dml_stat (ha_rows &found, ha_rows &changed) + { + found= changed= 0; + } + /** @brief Unprepare prepared statement for the command @param thd global context of the processed statement diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index dd842f0f1c0..1e3441cdc59 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -1851,6 +1851,13 @@ bool Sql_cmd_delete::execute_inner(THD *thd) } } + if (res || !result) + { + deleted= ((multi_delete*)(get_result()))->num_deleted(); + } + else + deleted= 0; + if (result) { res= false; diff --git a/sql/sql_delete.h b/sql/sql_delete.h index 1842c5c04c7..eb59a7fa77e 100644 --- a/sql/sql_delete.h +++ b/sql/sql_delete.h @@ -43,6 +43,7 @@ template <typename T> class SQL_I_List; class Sql_cmd_delete final : public Sql_cmd_dml { public: + ha_rows deleted; Sql_cmd_delete(bool multitable_arg) : orig_multitable(multitable_arg), multitable(multitable_arg), save_protocol(NULL) @@ -66,6 +67,12 @@ class Sql_cmd_delete final : public Sql_cmd_dml void remove_order_by_without_limit(THD *thd); + void get_dml_stat (ha_rows &found, ha_rows &changed) override + { + found= 0; + changed= deleted; + } + protected: /** @brief Perform precheck of table privileges for delete statements diff --git a/sql/sql_select.cc b/sql/sql_select.cc index c3f50989e06..7c11d34a271 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -34139,39 +34139,38 @@ static void MYSQL_DML_START(THD *thd) } -static void MYSQL_DML_DONE(THD *thd, int rc) +static void MYSQL_DML_GET_STAT(THD * thd, ha_rows &found, ha_rows &changed) { switch (thd->lex->sql_command) { + case SQLCOM_UPDATE: + case SQLCOM_UPDATE_MULTI: + case SQLCOM_DELETE_MULTI: + thd->lex->m_sql_cmd->get_dml_stat(found, changed); + break; + case SQLCOM_DELETE: + found= 0; + changed= (thd->get_row_count_func()); + break; + default: + DBUG_ASSERT(0); + } +} + +static void MYSQL_DML_DONE(THD *thd, int rc, ha_rows found, ha_rows changed) +{ + switch (thd->lex->sql_command) { case SQLCOM_UPDATE: - MYSQL_UPDATE_DONE( - rc, - (rc ? 0 : - ((multi_update*)(((Sql_cmd_dml*)(thd->lex->m_sql_cmd))->get_result())) - ->num_found()), - (rc ? 0 : - ((multi_update*)(((Sql_cmd_dml*)(thd->lex->m_sql_cmd))->get_result())) - ->num_updated())); + MYSQL_UPDATE_DONE(rc, found, changed); break; case SQLCOM_UPDATE_MULTI: - MYSQL_MULTI_UPDATE_DONE( - rc, - (rc ? 0 : - ((multi_update*)(((Sql_cmd_dml*)(thd->lex->m_sql_cmd))->get_result())) - ->num_found()), - (rc ? 0 : - ((multi_update*)(((Sql_cmd_dml*)(thd->lex->m_sql_cmd))->get_result())) - ->num_updated())); + MYSQL_MULTI_UPDATE_DONE(rc, found, changed); break; case SQLCOM_DELETE: - MYSQL_DELETE_DONE(rc, (rc ? 0 : (ulong) (thd->get_row_count_func()))); + MYSQL_DELETE_DONE(rc, changed); break; case SQLCOM_DELETE_MULTI: - MYSQL_MULTI_DELETE_DONE( - rc, - (rc ? 0 : - ((multi_delete*)(((Sql_cmd_dml*)(thd->lex->m_sql_cmd))->get_result())) - ->num_deleted())); + MYSQL_MULTI_DELETE_DONE(rc, changed); break; default: DBUG_ASSERT(0); @@ -34260,6 +34259,7 @@ bool Sql_cmd_dml::prepare(THD *thd) bool Sql_cmd_dml::execute(THD *thd) { lex = thd->lex; + ha_rows found= 0, changed= 0; bool res; SELECT_LEX_UNIT *unit = &lex->unit; @@ -34310,6 +34310,8 @@ bool Sql_cmd_dml::execute(THD *thd) if (res) goto err; + else + MYSQL_DML_GET_STAT(thd, found, changed); res= unit->cleanup(); @@ -34318,13 +34320,13 @@ bool Sql_cmd_dml::execute(THD *thd) THD_STAGE_INFO(thd, stage_end); - MYSQL_DML_DONE(thd, res); + MYSQL_DML_DONE(thd, 0, found, changed); return res; err: DBUG_ASSERT(thd->is_error() || thd->killed); - MYSQL_DML_DONE(thd, 1); + MYSQL_DML_DONE(thd, 1, 0, 0); THD_STAGE_INFO(thd, stage_end); (void)unit->cleanup(); if (is_prepared()) diff --git a/sql/sql_update.cc b/sql/sql_update.cc index 1e425911de0..e467b308df9 100644 --- a/sql/sql_update.cc +++ b/sql/sql_update.cc @@ -3123,6 +3123,13 @@ bool Sql_cmd_update::execute_inner(THD *thd) } } } + if (res || !result) + { + this->found= ((multi_update*)(get_result()))->num_found(); + this->updated= ((multi_update*)(get_result()))->num_updated(); + } + else + this->found= this->updated= 0; if (result) { diff --git a/sql/sql_update.h b/sql/sql_update.h index d01ecb7354a..4dc93a98ab2 100644 --- a/sql/sql_update.h +++ b/sql/sql_update.h @@ -45,6 +45,7 @@ bool compare_record(const TABLE *table); class Sql_cmd_update final : public Sql_cmd_dml { public: + ha_rows found, updated; Sql_cmd_update(bool multitable_arg) : orig_multitable(multitable_arg), multitable(multitable_arg) {} @@ -65,6 +66,13 @@ class Sql_cmd_update final : public Sql_cmd_dml void set_as_multitable() { multitable= true; } + void get_dml_stat (ha_rows &found, ha_rows &changed) override + { + + found= this->found; + changed= this->updated; + } + protected: /** @brief Perform precheck of table privileges for update statements
          Gosselin Dave Gosselin added a comment - - edited

          sanja thanks, I took your patch and made some updates: for single table cases, we had local variables declared in update_single_table and delete_from_single_table which shadowed class member variables of the same name, preventing statistics counting from persisting after those functions returned. I removed the locals so that the members on the Sql_cmd_update and Sql_cmd_delete classes would be correct in both multitable and single table cases. The casting to (multi_delete*) and (multi_update*) was unsafe in the case that multitable was FALSE because the result variable would be replaced with an instance of another type which could not be cast to either of those two types. I check the value of multitable before casting, which in fact is part of the fix really for the single table case as when multitable == false, we are assuming single table. Please see review here: https://github.com/MariaDB/server/pull/3813

          Gosselin Dave Gosselin added a comment - - edited sanja thanks, I took your patch and made some updates: for single table cases, we had local variables declared in update_single_table and delete_from_single_table which shadowed class member variables of the same name, preventing statistics counting from persisting after those functions returned. I removed the locals so that the members on the Sql_cmd_update and Sql_cmd_delete classes would be correct in both multitable and single table cases. The casting to (multi_delete*) and (multi_update*) was unsafe in the case that multitable was FALSE because the result variable would be replaced with an instance of another type which could not be cast to either of those two types. I check the value of multitable before casting, which in fact is part of the fix really for the single table case as when multitable == false, we are assuming single table. Please see review here: https://github.com/MariaDB/server/pull/3813

          People

            sanja Oleksandr Byelkin
            Gosselin Dave Gosselin
            Votes:
            0 Vote for this issue
            Watchers:
            5 Start watching this issue

            Dates

              Created:
              Updated:

              Git Integration

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