[Commits] Rev 4077: MDEV-4856 SQL_ERROR_LOG shows 1146 errors which didnt appear in mysql client. in file:///home/hf/wmar/mdev-4856/

holyfoot at askmonty.org holyfoot at askmonty.org
Sat Feb 22 20:52:52 EET 2014


At file:///home/hf/wmar/mdev-4856/

------------------------------------------------------------
revno: 4077
revision-id: holyfoot at askmonty.org-20140222173543-xkzlp5rpga41evur
parent: svoj at mariadb.org-20140218065405-vw3bfhvhrfjsc6hh
committer: Alexey Botchkov <holyfoot at askmonty.org>
branch nick: mdev-4856
timestamp: Sat 2014-02-22 21:35:43 +0400
message:
  MDEV-4856 SQL_ERROR_LOG shows 1146 errors which didnt appear in mysql client.
      The fill_schema_table() function used to call get_table_share() for a table name in WHERE
      then clear the error list. That way plugins receive the superfluous error notification if it
      happens in it. Also the problem was that error handler didn't prevent the suppressed
      error message from logging anyway as the logging happens in THD::raise_condition
      before the handler call.
      Fixed by adding the Warnings_only_error_handler before the call. raise_condition() also fixed.
      The Internal_error_handler class was exteded with virtual functions to handle
      storing the error message.
-------------- next part --------------
=== modified file 'mysql-test/suite/plugins/r/sql_error_log.result'
--- a/mysql-test/suite/plugins/r/sql_error_log.result	2014-01-23 18:21:02 +0000
+++ b/mysql-test/suite/plugins/r/sql_error_log.result	2014-02-22 17:35:43 +0000
@@ -33,6 +33,8 @@ insert into t1 values ('aa');
 ERROR 22007: Incorrect integer value: 'aa' for column 'id' at row 1
 SET SQL_MODE = '';
 drop table t1;
+SELECT TABLE_NAME FROM information_schema.TABLES WHERE TABLE_SCHEMA = 'not_exists' AND TABLE_NAME = 'not_exists';
+TABLE_NAME
 uninstall plugin SQL_ERROR_LOG;
 Warnings:
 Warning 1620    Plugin is busy and will be uninstalled on shutdown

=== modified file 'mysql-test/suite/plugins/t/sql_error_log.test'
--- a/mysql-test/suite/plugins/t/sql_error_log.test	2014-01-24 02:07:22 +0000
+++ b/mysql-test/suite/plugins/t/sql_error_log.test	2014-02-22 17:35:43 +0000
@@ -46,6 +46,8 @@ insert into t1 values ('aa');
 SET SQL_MODE = '';
 drop table t1;
 
+SELECT TABLE_NAME FROM information_schema.TABLES WHERE TABLE_SCHEMA = 'not_exists' AND TABLE_NAME = 'not_exists';
+
 uninstall plugin SQL_ERROR_LOG;
 
 let $MYSQLD_DATADIR= `SELECT @@datadir`;

=== modified file 'sql/sql_base.cc'
--- a/sql/sql_base.cc	2014-01-28 09:58:18 +0000
+++ b/sql/sql_base.cc	2014-02-22 17:35:43 +0000
@@ -719,8 +719,10 @@ get_table_share_with_discover(THD *thd, 
     @todo Rework alternative ways to deal with ER_NO_SUCH TABLE.
   */
   if (share ||
-      (thd->is_error() && thd->stmt_da->sql_errno() != ER_NO_SUCH_TABLE &&
-       thd->stmt_da->sql_errno() != ER_NO_SUCH_TABLE_IN_ENGINE))
+      (thd->get_internal_handler() &&
+       thd->get_internal_handler()->is_error() &&
+       thd->get_internal_handler()->first_errno() != ER_NO_SUCH_TABLE &&
+       thd->get_internal_handler()->first_errno() != ER_NO_SUCH_TABLE_IN_ENGINE))
     DBUG_RETURN(share);
 
   *error= 0;
@@ -742,17 +744,17 @@ get_table_share_with_discover(THD *thd, 
       unwanted error in case the error was already silenced.
       @todo Rework the alternative ways to deal with ER_NO_SUCH TABLE.
     */
-    if (thd->is_error())
+    if (thd->is_any_error())
     {
       if (table_list->parent_l)
       {
-        thd->clear_error();
+        thd->clear_any_error();
         my_error(ER_WRONG_MRG_TABLE, MYF(0));
       }
       else if (table_list->belong_to_view)
       {
         TABLE_LIST *view= table_list->belong_to_view;
-        thd->clear_error();
+        thd->clear_any_error();
         my_error(ER_VIEW_INVALID, MYF(0),
                  view->view_db.str, view->view_name.str);
       }

=== modified file 'sql/sql_class.cc'
--- a/sql/sql_class.cc	2014-01-28 09:00:50 +0000
+++ b/sql/sql_class.cc	2014-02-22 17:35:43 +0000
@@ -1016,6 +1016,18 @@ Internal_error_handler *THD::pop_interna
 }
 
 
+void THD::clear_any_error()
+{
+  clear_error();
+  for (Internal_error_handler *error_handler= m_internal_handler;
+       error_handler;
+       error_handler= error_handler->m_prev_internal_handler)
+  {
+    error_handler->reset();
+  }
+}
+
+
 void THD::raise_error(uint sql_errno)
 {
   const char* msg= ER(sql_errno);
@@ -1145,7 +1157,6 @@ MYSQL_ERROR* THD::raise_condition(uint s
     got_warning= 1;
     break;
   case MYSQL_ERROR::WARN_LEVEL_ERROR:
-    mysql_audit_general(this, MYSQL_AUDIT_GENERAL_ERROR, sql_errno, msg);
     break;
   default:
     DBUG_ASSERT(FALSE);
@@ -1156,6 +1167,8 @@ MYSQL_ERROR* THD::raise_condition(uint s
 
   if (level == MYSQL_ERROR::WARN_LEVEL_ERROR)
   {
+    mysql_audit_general(this, MYSQL_AUDIT_GENERAL_ERROR, sql_errno, msg);
+
     is_slave_error=  1; // needed to catch query errors during replication
 
     if (! stmt_da->is_error())

=== modified file 'sql/sql_class.h'
--- a/sql/sql_class.h	2014-01-26 20:48:42 +0000
+++ b/sql/sql_class.h	2014-02-22 17:35:43 +0000
@@ -1326,6 +1326,22 @@ class Internal_error_handler
                                 MYSQL_ERROR::enum_warning_level level,
                                 const char* msg,
                                 MYSQL_ERROR ** cond_hdl) = 0;
+  virtual bool is_error() const
+  {
+    return m_prev_internal_handler ?
+      m_prev_internal_handler->is_error() : FALSE;
+  }
+  virtual uint first_errno() const
+  {
+    return m_prev_internal_handler ?
+      m_prev_internal_handler->first_errno() : 0;
+  }
+  virtual const char *first_message() const
+  {
+    return m_prev_internal_handler ?
+      m_prev_internal_handler->first_message() : "";
+  }
+  virtual void reset() {}
 
 private:
   Internal_error_handler *m_prev_internal_handler;
@@ -2614,6 +2630,8 @@ class THD :public Statement,
     is_slave_error= 0;
     DBUG_VOID_RETURN;
   }
+  void clear_any_error();
+
 #ifndef EMBEDDED_LIBRARY
   inline bool vio_ok() const { return net.vio != 0; }
   /** Return FALSE if connection to client is broken. */
@@ -2655,6 +2673,15 @@ class THD :public Statement,
     To raise this flag, use my_error().
   */
   inline bool is_error() const { return stmt_da->is_error(); }
+  /*
+    Some errors can be silenced by the Error_handler.
+    This method signal the silended errors as well.
+  */
+  inline bool is_any_error() const
+  {
+    return stmt_da->is_error() ||
+           (m_internal_handler && m_internal_handler->is_error());
+  }
   inline CHARSET_INFO *charset() { return variables.character_set_client; }
   void update_charset();
 

=== modified file 'sql/sql_show.cc'
--- a/sql/sql_show.cc	2014-02-17 10:09:40 +0000
+++ b/sql/sql_show.cc	2014-02-22 17:35:43 +0000
@@ -3721,7 +3721,7 @@ make_table_name_list(THD *thd, List<LEX_
     {
       if (sql_command_flags[lex->sql_command] & CF_STATUS_COMMAND)
         return 1;
-      thd->clear_error();
+      thd->clear_any_error();
       return 2;
     }
     return 1;
@@ -3873,8 +3873,9 @@ fill_schema_table_by_open(THD *thd, bool
     Again we don't do this for SHOW COLUMNS/KEYS because
     of backward compatibility.
   */
-  if (!is_show_fields_or_keys && result && thd->is_error() &&
-      thd->stmt_da->sql_errno() == ER_NO_SUCH_TABLE)
+  if (!is_show_fields_or_keys && result &&
+      thd->get_internal_handler()->is_error() &&
+      thd->get_internal_handler()->first_errno() == ER_NO_SUCH_TABLE)
   {
     /*
       Hide error for a non-existing table.
@@ -3882,7 +3883,7 @@ fill_schema_table_by_open(THD *thd, bool
       with a db name and table, but the table does not exist.
     */
     result= false;
-    thd->clear_error();
+    thd->clear_any_error();
   }
   else
   {
@@ -4282,7 +4283,7 @@ static int fill_schema_table_from_frm(TH
   */
   DBUG_ASSERT(thd->open_tables == NULL);
   thd->mdl_context.rollback_to_savepoint(open_tables_state_backup->mdl_system_tables_svp);
-  thd->clear_error();
+  thd->clear_any_error();
   return res;
 }
 
@@ -4325,6 +4326,41 @@ class Trigger_error_handler : public Int
 };
 
 
+class Warnings_only_error_handler : public Internal_error_handler
+{
+protected:
+  bool error_flag;
+  uint saved_first_errno;
+  char saved_first_msg[MYSQL_ERRMSG_SIZE];
+public:
+  char saved_sqlstate[SQLSTATE_LENGTH+1];
+  Warnings_only_error_handler() : error_flag(FALSE) {}
+  bool handle_condition(THD *thd,
+                        uint sql_errno,
+                        const char* sqlstate,
+                        MYSQL_ERROR::enum_warning_level level,
+                        const char* msg,
+                        MYSQL_ERROR ** cond_hdl)
+  {
+    if (level != MYSQL_ERROR::WARN_LEVEL_ERROR)
+      return FALSE;
+
+    if (!error_flag)
+    {
+      error_flag= TRUE;
+      saved_first_errno= sql_errno;
+      strncpy(saved_first_msg, msg, sizeof(saved_first_msg));
+      strncpy(saved_sqlstate, sqlstate, sizeof(saved_sqlstate));
+    }
+
+    return TRUE;
+  }
+  bool is_error() const { return error_flag; }
+  uint first_errno() const { return saved_first_errno; }
+  const char *first_message() const { return saved_first_msg; }
+  virtual void reset() { error_flag= FALSE; }
+};
+
 
 /**
   @brief          Fill I_S tables whose data are retrieved
@@ -4472,9 +4508,10 @@ int get_all_tables(THD *thd, TABLE_LIST 
 #endif
     {
       List<LEX_STRING> table_names;
-      int res= make_table_name_list(thd, &table_names, lex,
-                                    &lookup_field_vals,
-                                    with_i_schema, db_name);
+      int res;
+      res= make_table_name_list(thd, &table_names, lex,
+                                &lookup_field_vals,
+                                with_i_schema, db_name);
       if (res == 2)   /* Not fatal error, continue */
         continue;
       if (res)
@@ -4520,37 +4557,39 @@ int get_all_tables(THD *thd, TABLE_LIST 
           }
           else
           {
+            Trigger_error_handler err_handler;
+            int res;
+            /*
+               Here we need to filter out warnings, which can happen
+               during loading of triggers in fill_schema_table_from_frm(),
+               because we don't need those warnings to pollute output of
+               SELECT from I_S / SHOW-statements.
+            */
+            thd->push_internal_handler(&err_handler);
+
             if (!(table_open_method & ~OPEN_FRM_ONLY) &&
                 !with_i_schema)
             {
-              /*
-                Here we need to filter out warnings, which can happen
-                during loading of triggers in fill_schema_table_from_frm(),
-                because we don't need those warnings to pollute output of
-                SELECT from I_S / SHOW-statements.
-              */
-
-              Trigger_error_handler err_handler;
-              thd->push_internal_handler(&err_handler);
-
-              int res= fill_schema_table_from_frm(thd, tables, schema_table,
-                                                  db_name, table_name,
-                                                  schema_table_idx,
-                                                  &open_tables_state_backup,
-                                                  can_deadlock);
-
-              thd->pop_internal_handler();
-
-              if (!res)
+              if (!fill_schema_table_from_frm(thd, tables, schema_table,
+                                              db_name, table_name,
+                                              schema_table_idx,
+                                              &open_tables_state_backup,
+                                              can_deadlock))
+              {
+                thd->pop_internal_handler();
                 continue;
+              }
+              err_handler.reset();
             }
 
             DEBUG_SYNC(thd, "before_open_in_get_all_tables");
-            if (fill_schema_table_by_open(thd, FALSE,
-                                          table, schema_table,
-                                          db_name, table_name,
-                                          &open_tables_state_backup,
-                                          can_deadlock))
+            res= fill_schema_table_by_open(thd, FALSE,
+                                           table, schema_table,
+                                           db_name, table_name,
+                                           &open_tables_state_backup,
+                                           can_deadlock);
+            thd->pop_internal_handler();
+            if (res)
               goto err;
           }
         }
@@ -4903,14 +4942,17 @@ static int get_schema_tables_record(THD 
       column with the error text, and clear the error so that the operation
       can continue.
     */
-    const char *error= thd->is_error() ? thd->stmt_da->message() : "";
+    bool is_error= thd->get_internal_handler() &&
+                   thd->get_internal_handler()->is_error();
+    const char *error= is_error ?
+                       thd->get_internal_handler()->first_message() : "";
     table->field[20]->store(error, strlen(error), cs);
 
-    if (thd->is_error())
+    if (is_error)
     {
       push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
-                   thd->stmt_da->sql_errno(), thd->stmt_da->message());
-      thd->clear_error();
+                   thd->get_internal_handler()->first_errno(), error);
+      thd->clear_any_error();
     }
   }
 
@@ -5072,10 +5114,14 @@ static int get_schema_column_record(THD 
         I.e. we are in SELECT FROM INFORMATION_SCHEMA.COLUMS
         rather than in SHOW COLUMNS
       */
-      if (thd->is_error())
+      if (thd->get_internal_handler() &&
+          thd->get_internal_handler()->is_error())
+      {
         push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
-                     thd->stmt_da->sql_errno(), thd->stmt_da->message());
-      thd->clear_error();
+                     thd->get_internal_handler()->first_errno(),
+                     thd->get_internal_handler()->first_message());
+        thd->clear_any_error();
+      }
       res= 0;
     }
     DBUG_RETURN(res);
@@ -5965,12 +6011,13 @@ static int get_schema_views_record(THD *
 
     if (schema_table_store_record(thd, table))
       DBUG_RETURN(1);
-    if (res && thd->is_error())
+    if (res && thd->get_internal_handler()->is_error())
       push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
-                   thd->stmt_da->sql_errno(), thd->stmt_da->message());
+                   thd->get_internal_handler()->first_errno(),
+                   thd->get_internal_handler()->first_message());
   }
   if (res)
-    thd->clear_error();
+    thd->clear_any_error();
   DBUG_RETURN(0);
 }
 
@@ -7666,92 +7713,6 @@ int make_schema_select(THD *thd, SELECT_
 }
 
 
-/**
-  Fill INFORMATION_SCHEMA-table, leave correct Diagnostics_area /
-  Warning_info state after itself.
-
-  This function is a wrapper around ST_SCHEMA_TABLE::fill_table(), which
-  may "partially silence" some errors. The thing is that during
-  fill_table() many errors might be emitted. These errors stem from the
-  nature of fill_table().
-
-  For example, SELECT ... FROM INFORMATION_SCHEMA.xxx WHERE TABLE_NAME = 'xxx'
-  results in a number of 'Table <db name>.xxx does not exist' errors,
-  because fill_table() tries to open the 'xxx' table in every possible
-  database.
-
-  Those errors are cleared (the error status is cleared from
-  Diagnostics_area) inside fill_table(), but they remain in Warning_info
-  (Warning_info is not cleared because it may contain useful warnings).
-
-  This function is responsible for making sure that Warning_info does not
-  contain warnings corresponding to the cleared errors.
-
-  @note: THD::no_warnings_for_error used to be set before calling
-  fill_table(), thus those errors didn't go to Warning_info. This is not
-  the case now (THD::no_warnings_for_error was eliminated as a hack), so we
-  need to take care of those warnings here.
-
-  @param thd            Thread context.
-  @param table_list     I_S table.
-  @param join_table     JOIN/SELECT table.
-
-  @return Error status.
-  @retval TRUE Error.
-  @retval FALSE Success.
-*/
-static bool do_fill_table(THD *thd,
-                          TABLE_LIST *table_list,
-                          JOIN_TAB *join_table)
-{
-  // NOTE: fill_table() may generate many "useless" warnings, which will be
-  // ignored afterwards. On the other hand, there might be "useful"
-  // warnings, which should be presented to the user. Warning_info usually
-  // stores no more than THD::variables.max_error_count warnings.
-  // The problem is that "useless warnings" may occupy all the slots in the
-  // Warning_info, so "useful warnings" get rejected. In order to avoid
-  // that problem we create a Warning_info instance, which is capable of
-  // storing "unlimited" number of warnings.
-  Warning_info wi(thd->query_id, true);
-  Warning_info *wi_saved= thd->warning_info;
-
-  thd->warning_info= &wi;
-
-  bool res= table_list->schema_table->fill_table(
-    thd, table_list, join_table->select_cond);
-
-  thd->warning_info= wi_saved;
-
-  // Pass an error if any.
-
-  if (thd->stmt_da->is_error())
-  {
-    thd->warning_info->push_warning(thd,
-                                    thd->stmt_da->sql_errno(),
-                                    thd->stmt_da->get_sqlstate(),
-                                    MYSQL_ERROR::WARN_LEVEL_ERROR,
-                                    thd->stmt_da->message());
-  }
-
-  // Pass warnings (if any).
-  //
-  // Filter out warnings with WARN_LEVEL_ERROR level, because they
-  // correspond to the errors which were filtered out in fill_table().
-
-
-  List_iterator_fast<MYSQL_ERROR> it(wi.warn_list());
-  MYSQL_ERROR *err;
-
-  while ((err= it++))
-  {
-    if (err->get_level() != MYSQL_ERROR::WARN_LEVEL_ERROR)
-      thd->warning_info->push_warning(thd, err);
-  }
-
-  return res;
-}
-
-
 /*
   Fill temporary schema tables before SELECT
 
@@ -7825,9 +7786,20 @@ bool get_schema_tables_result(JOIN *join
       else
         table_list->table->file->stats.records= 0;
 
-      if (do_fill_table(thd, table_list, tab))
       {
-        result= 1;
+        Warnings_only_error_handler err_handler;
+        thd->push_internal_handler(&err_handler);
+        result= table_list->schema_table->fill_table(thd, table_list,
+                                                     tab->select_cond);
+        thd->pop_internal_handler();
+        if (err_handler.is_error())
+        {
+          my_message(err_handler.first_errno(),
+              err_handler.first_message(), MYF(0));
+        }
+      }
+      if (result)
+      {
         join->error= 1;
         tab->read_record.table->file= table_list->table->file;
         table_list->schema_table_state= executed_place;

=== modified file 'sql/table.cc'
--- a/sql/table.cc	2014-02-17 10:00:51 +0000
+++ b/sql/table.cc	2014-02-22 17:35:43 +0000
@@ -4443,28 +4443,40 @@ bool TABLE_LIST::prep_check_option(THD *
 
 void TABLE_LIST::hide_view_error(THD *thd)
 {
-  if (thd->killed || thd->get_internal_handler())
+  uint sql_errno;
+
+  if (thd->killed)
     return;
+  if (thd->get_internal_handler())
+  {
+    if (!thd->get_internal_handler()->is_error())
+      return;
+    sql_errno= thd->get_internal_handler()->first_errno();
+  }
+  else
+  {
+    DBUG_ASSERT(thd->is_error());
+    sql_errno= thd->stmt_da->sql_errno();
+  }
   /* Hide "Unknown column" or "Unknown function" error */
-  DBUG_ASSERT(thd->is_error());
 
-  if (thd->stmt_da->sql_errno() == ER_BAD_FIELD_ERROR ||
-      thd->stmt_da->sql_errno() == ER_SP_DOES_NOT_EXIST ||
-      thd->stmt_da->sql_errno() == ER_FUNC_INEXISTENT_NAME_COLLISION ||
-      thd->stmt_da->sql_errno() == ER_PROCACCESS_DENIED_ERROR ||
-      thd->stmt_da->sql_errno() == ER_COLUMNACCESS_DENIED_ERROR ||
-      thd->stmt_da->sql_errno() == ER_TABLEACCESS_DENIED_ERROR ||
-      thd->stmt_da->sql_errno() == ER_TABLE_NOT_LOCKED ||
-      thd->stmt_da->sql_errno() == ER_NO_SUCH_TABLE)
+  if (sql_errno == ER_BAD_FIELD_ERROR ||
+      sql_errno == ER_SP_DOES_NOT_EXIST ||
+      sql_errno == ER_FUNC_INEXISTENT_NAME_COLLISION ||
+      sql_errno == ER_PROCACCESS_DENIED_ERROR ||
+      sql_errno == ER_COLUMNACCESS_DENIED_ERROR ||
+      sql_errno == ER_TABLEACCESS_DENIED_ERROR ||
+      sql_errno == ER_TABLE_NOT_LOCKED ||
+      sql_errno == ER_NO_SUCH_TABLE)
   {
     TABLE_LIST *top= top_table();
-    thd->clear_error();
+    thd->clear_any_error();
     my_error(ER_VIEW_INVALID, MYF(0), top->view_db.str, top->view_name.str);
   }
-  else if (thd->stmt_da->sql_errno() == ER_NO_DEFAULT_FOR_FIELD)
+  else if (sql_errno == ER_NO_DEFAULT_FOR_FIELD)
   {
     TABLE_LIST *top= top_table();
-    thd->clear_error();
+    thd->clear_any_error();
     // TODO: make correct error message
     my_error(ER_NO_DEFAULT_FOR_VIEW_FIELD, MYF(0),
              top->view_db.str, top->view_name.str);



More information about the commits mailing list