List:Commits« Previous MessageNext Message »
From:Petr Chardin Date:January 19 2006 2:56am
Subject:bk commit into 5.1 tree (cps:1.2075)
View as plain text  
Below is the list of changes that have just been committed into a local
5.1 repository of cps. When cps does a push these changes will
be propagated to the main repository and, within 24 hours after the
push, to the public repository.
For information on how to access the public repository
see http://dev.mysql.com/doc/mysql/en/installing-source-tree.html

ChangeSet
  1.2075 06/01/19 05:56:06 cps@stripped +46 -0
  WL1019: complete patch. Reapplied patch to the clean
  tree to get rid of multiple typos in CS comments and
  unify the patch.

  mysql-test/t/log_tables.test
    1.1 06/01/19 05:56:01 cps@stripped +146 -0
    New BitKeeper file ``mysql-test/t/log_tables.test''

  mysql-test/r/log_tables.result
    1.1 06/01/19 05:56:01 cps@stripped +54 -0
    New BitKeeper file ``mysql-test/r/log_tables.result''

  storage/csv/ha_tina.h
    1.11 06/01/19 05:56:01 cps@stripped +21 -1
    enable concurrent insert for CSV, add log table flag

  storage/csv/ha_tina.cc
    1.30 06/01/19 05:56:01 cps@stripped +170 -6
    add support for concurrent insert (see bk commit - 5.1 tree
    (petr:1.1910) for standalone patch), add log tables-specific
    csv table handling.

  sql/table.h
    1.125 06/01/19 05:56:01 cps@stripped +6 -0
    add log-related info

  sql/table.cc
    1.201 06/01/19 05:56:01 cps@stripped +22 -9
    mark log tables as such during the open

  sql/sql_table.cc
    1.297 06/01/19 05:56:01 cps@stripped +4 -2
    don't reoped the log tables for admin purposes

  sql/sql_show.cc
    1.293 06/01/19 05:56:01 cps@stripped +3 -3
    convert old logging routines calls to use new API

  sql/sql_prepare.cc
    1.158 06/01/19 05:56:01 cps@stripped +3 -3
    convert old logging routines calls to use new API

  sql/sql_parse.cc
    1.510 06/01/19 05:56:01 cps@stripped +53 -41
    command_name is now an array of LEX_STRINGs

  sql/sql_delete.cc
    1.171 06/01/19 05:56:01 cps@stripped +36 -3
    fix TRUNCATE to work with log tables

  mysql-test/t/log_tables.test
    1.0 06/01/19 05:56:01 cps@stripped +0 -0
    BitKeeper file /home/cps/mysql/devel/mysql-5.1-logs-prepush/mysql-test/t/log_tables.test

  mysql-test/r/log_tables.result
    1.0 06/01/19 05:56:01 cps@stripped +0 -0
    BitKeeper file /home/cps/mysql/devel/mysql-5.1-logs-prepush/mysql-test/r/log_tables.result

  sql/sql_db.cc
    1.125 06/01/19 05:56:00 cps@stripped +2 -2
    convert old logging routines calls to use new API

  sql/sql_base.cc
    1.298 06/01/19 05:56:00 cps@stripped +27 -12
    TABLE objects used by the logger should be skipped
    during refreshes (as log tables are always opened
    and locked). fix table_is_used to skip them.  This
    is needed for FLUSH LOGS to work

  sql/slave.cc
    1.262 06/01/19 05:56:00 cps@stripped +2 -2
    convert old logging routines calls to use new API

  sql/share/errmsg.txt
    1.74 06/01/19 05:56:00 cps@stripped +4 -0
    add new error messages for the log tables

  sql/mysqld.cc
    1.520 06/01/19 05:56:00 cps@stripped +87 -34
    Add support for the log tables. Their initalization, cleanup
    and specific options.

  sql/mysql_priv.h
    1.370 06/01/19 05:56:00 cps@stripped +17 -3
    define common log routines and objects

  sql/log_event.cc
    1.199 06/01/19 05:56:00 cps@stripped +3 -2
    convert old logging routines calls to use new API

  sql/log.h
    1.2 06/01/19 05:56:00 cps@stripped +174 -4
    declare new log classes

  sql/log.cc
    1.186 06/01/19 05:56:00 cps@stripped +1084 -151
    Add log tables support, refactoring: there are log event
    handlers with common interface. They are used by the LOGGER
    class, which is responsible for their initialization, cleanup
    and managment. Logging to the tables provided by one of the
    log event handler types.

  sql/lock.cc
    1.85 06/01/19 05:56:00 cps@stripped +5 -11
    from now on we check for handler-related locking issues
    in the handler itself rather then in lock.cc

  sql/handler.h
    1.182 06/01/19 05:56:00 cps@stripped +24 -0
    new virtual function is added: we should check for handler-related
    locking issues in the handler

  sql/ha_myisam.h
    1.71 06/01/19 05:56:00 cps@stripped +1 -0
    new function declared

  sql/ha_myisam.cc
    1.169 06/01/19 05:56:00 cps@stripped +19 -0
    move locking-related checks to the hanlder

  scripts/mysql_fix_privilege_tables.sql
    1.33 06/01/19 05:56:00 cps@stripped +36 -0
    add new log tables: use an SP to create them for
    non-csv build to work fine.

  scripts/mysql_create_system_tables.sh
    1.31 06/01/19 05:56:00 cps@stripped +25 -0
    new system tables: slow_log and general_log

  mysql-test/t/system_mysql_db_fix.test
    1.21 06/01/19 05:56:00 cps@stripped +8 -1
    disable test if CSV engine is not in: result depends on the
    presence of CSV-based log tables

  mysql-test/t/system_mysql_db.test
    1.9 06/01/19 05:56:00 cps@stripped +4 -0
    disable test if CSV engine is not in: result depends on the
    presence of CSV-based log tables

  mysql-test/t/show_check.test
    1.61 06/01/19 05:56:00 cps@stripped +4 -0
    disable test if CSV engine is not in: result depends on the
    presence of CSV-based log tables

  mysql-test/t/mysqlcheck.test
    1.2 06/01/19 05:56:00 cps@stripped +4 -0
    disable test if CSV engine is not in: result depends on the
    presence of CSV-based log tables

  mysql-test/t/information_schema.test
    1.68 06/01/19 05:56:00 cps@stripped +4 -0
    disable test if CSV engine is not in: result depends on the
    presence of CSV-based log tables

  mysql-test/t/csv.test
    1.6 06/01/19 05:56:00 cps@stripped +35 -1
    add tests for concurrent insert (the functionality is added
    to CSV in this patch)

  mysql-test/t/connect.test
    1.20 06/01/19 05:55:59 cps@stripped +4 -0
    disable test if CSV engine is not in: result depends on the
    presence of CSV-based log tables

  mysql-test/r/system_mysql_db.result
    1.33 06/01/19 05:55:59 cps@stripped +27 -0
    update result

  mysql-test/r/show_check.result
    1.89 06/01/19 05:55:59 cps@stripped +18 -6
    update result

  mysql-test/r/mysqlcheck.result
    1.5 06/01/19 05:55:59 cps@stripped +8 -0
    update result

  mysql-test/r/information_schema.result
    1.103 06/01/19 05:55:59 cps@stripped +4 -2
    update result

  mysql-test/r/im_utils.result
    1.2 06/01/19 05:55:59 cps@stripped +2 -0
    update result

  mysql-test/r/csv.result
    1.5 06/01/19 05:55:59 cps@stripped +17 -0
    update result

  mysql-test/r/connect.result
    1.23 06/01/19 05:55:59 cps@stripped +6 -0
    update result

  mysql-test/mysql-test-run.pl
    1.54 06/01/19 05:55:59 cps@stripped +3 -0
    Add old logs flag to IM tests. As IM could only deal with
    old logs (this feature is not needed with log tables)

  mysql-test/lib/init_db.sql
    1.20 06/01/19 05:55:59 cps@stripped +4 -0
    create log tables when running tests.

  mysql-test/include/system_db_struct.inc
    1.7 06/01/19 05:55:59 cps@stripped +2 -0
    check log tables structure

  mysql-test/include/im_check_os.inc
    1.2 06/01/19 05:55:59 cps@stripped +7 -0
    we should only run im tests if csv is on for now: im relies
    on mysqld options available only in csv build.

  include/my_base.h
    1.81 06/01/19 05:55:59 cps@stripped +7 -2
    add new ha_extra flag for the log tables

  configure.in
    1.332 06/01/19 05:55:59 cps@stripped +2 -1
    CSV is compiled in by default now

# This is a BitKeeper patch.  What follows are the unified diffs for the
# set of deltas contained in the patch.  The rest of the patch, the part
# that BitKeeper cares about, is below these diffs.
# User:	cps
# Host:	outpost.site
# Root:	/home/cps/mysql/devel/mysql-5.1-logs-prepush

--- 1.331/configure.in	2006-01-09 19:26:11 +03:00
+++ 1.332/configure.in	2006-01-19 05:55:59 +03:00
@@ -2437,7 +2437,8 @@
  \$(top_builddir)/storage/archive/libarchive.a, [
   AC_CONFIG_FILES(storage/archive/Makefile)
 ])
-MYSQL_STORAGE_ENGINE(csv,,,,,no,storage/csv,,,[
+MYSQL_STORAGE_ENGINE(csv,,,"yes",,tina_hton,storage/csv,
+  ../storage/csv/ha_tina.o,,[
   AC_CONFIG_FILES(storage/csv/Makefile)
 ])
 MYSQL_STORAGE_ENGINE(blackhole)

--- 1.80/include/my_base.h	2006-01-13 05:15:39 +03:00
+++ 1.81/include/my_base.h	2006-01-19 05:55:59 +03:00
@@ -155,13 +155,18 @@
   */
   HA_EXTRA_KEYREAD_PRESERVE_FIELDS,
   HA_EXTRA_MMAP,
-  /* 
+  /*
     Ignore if the a tuple is not found, continue processing the
     transaction and ignore that 'row'.  Needed for idempotency
     handling on the slave
   */
   HA_EXTRA_IGNORE_NO_KEY,
-  HA_EXTRA_NO_IGNORE_NO_KEY
+  HA_EXTRA_NO_IGNORE_NO_KEY,
+  /*
+    Mark the table as a log table. For some handlers (e.g. CSV) this results
+    in a special locking for the table.
+  */
+  HA_EXTRA_MARK_AS_LOG_TABLE
 };
 
 	/* The following is parameter to ha_panic() */

--- 1.168/sql/ha_myisam.cc	2006-01-17 10:37:23 +03:00
+++ 1.169/sql/ha_myisam.cc	2006-01-19 05:56:00 +03:00
@@ -295,6 +295,25 @@
 }
 #endif /* HAVE_REPLICATION */
 
+
+bool ha_myisam::check_if_locking_is_allowed(THD *thd, TABLE *table, uint count)
+{
+  /*
+    To be able to open and lock for reading system tables like 'mysql.proc',
+    when we already have some tables opened and locked, and avoid deadlocks
+    we have to disallow write-locking of these tables with any other tables.
+  */
+  if (table->s->system_table &&
+      table->reginfo.lock_type >= TL_WRITE_ALLOW_WRITE &&
+      count != 1)
+  {
+    my_error(ER_WRONG_LOCK_OF_SYSTEM_TABLE, MYF(0), table->s->db.str,
+             table->s->table_name.str);
+    return FALSE;
+  }
+  return TRUE;
+}
+
 	/* Name is here without an extension */
 
 int ha_myisam::open(const char *name, int mode, uint test_if_locked)

--- 1.70/sql/ha_myisam.h	2005-11-23 23:44:55 +03:00
+++ 1.71/sql/ha_myisam.h	2006-01-19 05:56:00 +03:00
@@ -60,6 +60,7 @@
   uint max_supported_key_part_length() const { return MI_MAX_KEY_LENGTH; }
   uint checksum() const;
 
+  virtual bool check_if_locking_is_allowed(THD *thd, TABLE *table, uint count);
   int open(const char *name, int mode, uint test_if_locked);
   int close(void);
   int write_row(byte * buf);

--- 1.181/sql/handler.h	2006-01-17 11:24:49 +03:00
+++ 1.182/sql/handler.h	2006-01-19 05:56:00 +03:00
@@ -1089,6 +1089,30 @@
   {
     /* TODO: DBUG_ASSERT(inited == NONE); */
   }
+  /*
+    Check whether a handler allows to lock the table.
+
+    SYNOPSIS
+      check_if_locking_is_allowed()
+        thd     Handler of the thread, trying to lock the table
+        table   Table handler to check
+        count   Number of locks already granted to the table
+
+    DESCRIPTION
+      Check whether a handler allows to lock the table. For instance,
+      MyISAM does not allow to lock mysql.proc along with other tables.
+      This limitation stems from the fact that MyISAM does not support
+      row-level locking and we have to add this limitation to avoid
+      deadlocks.
+
+    RETURN
+      TRUE      Locking is allowed
+      FALSE     Locking is not allowed. The error was thrown.
+  */
+  virtual bool check_if_locking_is_allowed(THD *thd, TABLE *table, uint count)
+  {
+    return TRUE;
+  }
   virtual int ha_initialise();
   int ha_open(TABLE *table, const char *name, int mode, int test_if_locked);
   bool update_auto_increment();

--- 1.84/sql/lock.cc	2006-01-17 10:37:25 +03:00
+++ 1.85/sql/lock.cc	2006-01-19 05:56:00 +03:00
@@ -614,18 +614,12 @@
       lock_count++;
     }
     /*
-      To be able to open and lock for reading system tables like 'mysql.proc',
-      when we already have some tables opened and locked, and avoid deadlocks
-      we have to disallow write-locking of these tables with any other tables.
+      Check if we can lock the table. For some tables we cannot do that
+      beacause of handler-specific locking issues.
     */
-    if (table_ptr[i]->s->system_table &&
-        table_ptr[i]->reginfo.lock_type >= TL_WRITE_ALLOW_WRITE &&
-        count != 1)
-    {
-      my_error(ER_WRONG_LOCK_OF_SYSTEM_TABLE, MYF(0), table_ptr[i]->s->db.str,
-               table_ptr[i]->s->table_name.str);
-      DBUG_RETURN(0);
-    }
+    if (!table_ptr[i]->file->check_if_locking_is_allowed(thd, table_ptr[i],
+                                                        count))
+      return 0;
   }
 
   if (!(sql_lock= (MYSQL_LOCK*)

--- 1.185/sql/log.cc	2006-01-17 10:37:26 +03:00
+++ 1.186/sql/log.cc	2006-01-19 05:56:00 +03:00
@@ -34,7 +34,17 @@
 #include "message.h"
 #endif
 
-MYSQL_LOG mysql_log, mysql_slow_log, mysql_bin_log;
+/* max size of the log message */
+#define MAX_LOG_BUFFER_SIZE 1024
+#define MAX_USER_HOST_SIZE 512
+#define MAX_TIME_SIZE 32
+
+/* we need this for log files intialization */
+extern char *opt_logname, *opt_slow_logname;
+
+LOGGER logger;
+
+MYSQL_LOG mysql_bin_log;
 ulong sync_binlog_counter= 0;
 
 static bool test_if_number(const char *str,
@@ -95,6 +105,882 @@
 };
 
 
+
+/*
+  Open log table of a given type (general or slow log)
+
+  SYNOPSIS
+    open_log_table()
+
+    log_type   type of the log table to open: LOG_GENERAL or LOG_SLOW
+
+  DESCRIPTION
+
+    The function opens a log table and marks it as such. Log tables are open
+    during the whole time, while server is running. Except for the moments
+    when they have to be reopened: during FLUSH LOGS and TRUNCATE. This
+    function is invoked directly only once during startup. All subsequent
+    calls happen through reopen_log_table(), which performs additional check.
+
+  RETURN
+    FALSE - OK
+    TRUE - error occured
+*/
+
+bool Log_to_csv_event_handler::open_log_table(uint log_type)
+{
+  THD *log_thd, *curr= current_thd;
+  TABLE_LIST *table;
+  bool error= FALSE;
+  DBUG_ENTER("open_log_table");
+
+  switch (log_type) {
+  case LOG_GENERAL:
+    log_thd= general_log_thd;
+    table= &general_log;
+    /* clean up table before reuse/initial usage */
+    bzero((char*) table, sizeof(TABLE_LIST));
+    table->alias= table->table_name= (char*) "general_log";
+    table->table_name_length= 11;
+    break;
+  case LOG_SLOW:
+    log_thd= slow_log_thd;
+    table= &slow_log;
+    bzero((char*) table, sizeof(TABLE_LIST));
+    table->alias= table->table_name= (char*) "slow_log";
+    table->table_name_length= 8;
+    break;
+  default:
+    DBUG_ASSERT(0);
+  }
+
+  /*
+    This way we check that appropriate log thd was created ok during
+    initialization. We cannot check "is_log_tables_initialized" var, as
+    the very initialization is not finished until this function is
+    completed in the very first time.
+  */
+  if (!log_thd)
+  {
+    DBUG_PRINT("error",("Cannot initialize log tables"));
+    DBUG_RETURN(TRUE);
+  }
+
+  /*
+    Set THD's thread_stack. This is needed to perform stack overrun
+    check, which is done by some routines (e.g. open_table()).
+    In the case we are called by thread, which already has this parameter
+    set, we use this value. Otherwise we do a wild guess. This won't help
+    to correctly track the stack overrun in these exceptional cases (which
+    could probably happen only during startup and shutdown) but at least
+    lets us to pass asserts.
+    The problem stems from the fact that logger THDs are not real threads.
+  */
+  if (curr)
+    log_thd->thread_stack= curr->thread_stack;
+  else
+    log_thd->thread_stack= (char*) &log_thd;
+
+  log_thd->store_globals();
+
+  table->lock_type= TL_WRITE_CONCURRENT_INSERT;
+  table->db= log_thd->db;
+  table->db_length= log_thd->db_length;
+
+  if (simple_open_n_lock_tables(log_thd, table) ||
+      table->table->file->extra(HA_EXTRA_MARK_AS_LOG_TABLE) ||
+      table->table->file->ha_rnd_init(0))
+    error= TRUE;
+  else
+    table->table->locked_by_logger= TRUE;
+
+  /* restore thread settings */
+  if (curr)
+    curr->store_globals();
+  else
+  {
+    my_pthread_setspecific_ptr(THR_THD,  0);
+    my_pthread_setspecific_ptr(THR_MALLOC, 0);
+  }
+
+  DBUG_RETURN(error);
+}
+
+
+Log_to_csv_event_handler::Log_to_csv_event_handler()
+{
+  /* init artificial THD's */
+  general_log_thd= new THD;
+  /* logger thread always works with mysql database */
+  general_log_thd->db= my_strdup("mysql", MYF(0));
+  general_log_thd->db_length= 5;
+
+  slow_log_thd= new THD;
+  /* logger thread always works with mysql database */
+  slow_log_thd->db= my_strdup("mysql", MYF(0));;
+  slow_log_thd->db_length= 5;
+}
+
+
+Log_to_csv_event_handler::~Log_to_csv_event_handler()
+{
+  /* now cleanup the tables */
+  if (general_log_thd)
+  {
+    delete general_log_thd;
+    general_log_thd= NULL;
+  }
+
+  if (slow_log_thd)
+  {
+    delete slow_log_thd;
+    slow_log_thd= NULL;
+  }
+}
+
+
+/*
+  Reopen log table of a given type
+
+  SYNOPSIS
+    reopen_log_table()
+
+    log_type   type of the log table to open: LOG_GENERAL or LOG_SLOW
+
+  DESCRIPTION
+
+    The function is a wrapper around open_log_table(). It is used during
+    FLUSH LOGS and TRUNCATE of the log tables (i.e. when we need to close
+    and reopen them). The difference is in the check of the
+    logger.is_log_tables_initialized var, which can't be done in
+    open_log_table(), as it makes no sense during startup.
+
+    NOTE: this code assumes that we have logger mutex locked
+
+  RETURN
+    FALSE - ok
+    TRUE - open_log_table() returned an error
+*/
+
+bool Log_to_csv_event_handler::reopen_log_table(uint log_type)
+{
+  /* don't open the log table, if it wasn't enabled during startup */
+  if (!logger.is_log_tables_initialized)
+    return FALSE;
+  return open_log_table(log_type);
+}
+
+void Log_to_csv_event_handler::cleanup()
+{
+  close_log_table(LOG_GENERAL, FALSE);
+  close_log_table(LOG_SLOW, FALSE);
+  logger.is_log_tables_initialized= FALSE;
+}
+
+/* log event handlers */
+
+/*
+  Log command to the general log table
+
+  SYNOPSIS
+    log_general_to_csv()
+
+    event_time        command start timestamp
+    user_host         the pointer to the string with user@host info
+    user_host_len     length of the user_host string. this is computed once
+                      and passed to all general log event handlers
+    thread_id         Id of the thread, issued a query
+    command_type      the type of the command being logged
+    command_type_len  the length of the string above
+    sql_text          the very text of the query being executed
+    sql_text_len      the length of sql_text string
+
+  DESCRIPTION
+
+   Log given command to the general log table
+
+  RETURN
+    FALSE - OK
+    TRUE - error occured
+*/
+
+bool Log_to_csv_event_handler::
+  log_general(time_t event_time, const char *user_host,
+              uint user_host_len, int thread_id,
+              const char *command_type, uint command_type_len,
+              const char *sql_text, uint sql_text_len)
+{
+  TABLE *table= general_log.table;
+
+  /* below should never happen */
+  if (unlikely(!logger.is_log_tables_initialized))
+    return FALSE;
+
+  /* log table entries are not replicated at the moment */
+  tmp_disable_binlog(current_thd);
+
+  general_log_thd->start_time= event_time;
+  /* set default value (which is CURRENT_TIMESTAMP) */
+  table->field[0]->set_null();
+
+  table->field[1]->store(user_host, user_host_len, &my_charset_latin1);
+  table->field[2]->store((longlong) thread_id);
+  table->field[3]->store((longlong) server_id);
+  table->field[4]->store(command_type, command_type_len, &my_charset_latin1);
+  table->field[5]->store(sql_text, sql_text_len, &my_charset_latin1);
+  table->file->ha_write_row(table->record[0]);
+
+  reenable_binlog(current_thd);
+
+  return FALSE;
+}
+
+
+/*
+  Log a query to the slow log table
+
+  SYNOPSIS
+    log_slow_to_csv()
+    thd               THD of the query
+    current_time      current timestamp
+    query_start_arg   command start timestamp
+    user_host         the pointer to the string with user@host info
+    user_host_len     length of the user_host string. this is computed once
+                      and passed to all general log event handlers
+    query_time        Amount of time the query took to execute (in seconds)
+    lock_time         Amount of time the query was locked (in seconds)
+    is_command        The flag, which determines, whether the sql_text is a
+                      query or an administrator command (these are treated
+                      differently by the old logging routines)
+    sql_text          the very text of the query or administrator command
+                      processed
+    sql_text_len      the length of sql_text string
+
+  DESCRIPTION
+
+   Log a query to the slow log table
+
+  RETURN
+    FALSE - OK
+    TRUE - error occured
+*/
+
+bool Log_to_csv_event_handler::
+  log_slow(THD *thd, time_t current_time, time_t query_start_arg,
+           const char *user_host, uint user_host_len,
+           longlong query_time, longlong lock_time, bool is_command,
+           const char *sql_text, uint sql_text_len)
+{
+  /* table variables */
+  TABLE *table= slow_log.table;
+
+  DBUG_ENTER("log_slow_to_csv");
+
+  /* below should never happen */
+  if (unlikely(!logger.is_log_tables_initialized))
+    return FALSE;
+
+  /* log table entries are not replicated at the moment */
+  tmp_disable_binlog(current_thd);
+
+  /*
+     Set start time for CURRENT_TIMESTAMP to the start of the query.
+     This will be default value for the field
+  */
+  slow_log_thd->start_time= query_start_arg;
+
+  /* set default value (which is CURRENT_TIMESTAMP) */
+  table->field[0]->set_null();
+
+  /* store the value */
+  table->field[1]->store(user_host, user_host_len, &my_charset_latin1);
+
+  if (query_start_arg)
+  {
+    /* fill in query_time field */
+    table->field[2]->store(query_time);
+    /* lock_time */
+    table->field[3]->store(lock_time);
+    /* rows_sent */
+    table->field[4]->store((longlong) thd->sent_row_count);
+    /* rows_examined */
+    table->field[5]->store((longlong) thd->examined_row_count);
+  }
+  else
+  {
+    table->field[2]->set_null();
+    table->field[3]->set_null();
+    table->field[4]->set_null();
+    table->field[5]->set_null();
+  }
+
+  if (thd->db)
+    /* fill database field */
+    table->field[6]->store(thd->db, thd->db_length, &my_charset_latin1);
+  else
+    table->field[6]->set_null();
+
+  if (thd->last_insert_id_used)
+    table->field[7]->store((longlong) thd->current_insert_id);
+  else
+    table->field[7]->set_null();
+
+  /* set value if we do an insert on autoincrement column */
+  if (thd->insert_id_used)
+    table->field[8]->store((longlong) thd->last_insert_id);
+  else
+    table->field[8]->set_null();
+
+  table->field[9]->store((longlong) server_id);
+
+  /* sql_text */
+  table->field[10]->store(sql_text,sql_text_len,
+                          &my_charset_latin1);
+
+  /* write the row */
+  table->file->ha_write_row(table->record[0]);
+
+  reenable_binlog(current_thd);
+
+  DBUG_RETURN(0);
+}
+
+bool Log_to_csv_event_handler::
+  log_error(enum loglevel level, const char *format, va_list args)
+{
+  /* No log table is implemented */
+  DBUG_ASSERT(0);
+  return FALSE;
+}
+
+bool Log_to_file_event_handler::
+  log_error(enum loglevel level, const char *format,
+            va_list args)
+{
+  return vprint_msg_to_log(level, format, args);
+}
+
+void Log_to_file_event_handler::init_pthread_objects()
+{
+  mysql_log.init_pthread_objects();
+  mysql_slow_log.init_pthread_objects();
+}
+
+
+/* Wrapper around MYSQL_LOG::write() for slow log */
+
+bool Log_to_file_event_handler::
+  log_slow(THD *thd, time_t current_time, time_t query_start_arg,
+           const char *user_host, uint user_host_len,
+           longlong query_time, longlong lock_time, bool is_command,
+           const char *sql_text, uint sql_text_len)
+{
+  return mysql_slow_log.write(thd, current_time, query_start_arg,
+                              user_host, user_host_len,
+                              query_time, lock_time, is_command,
+                              sql_text, sql_text_len);
+}
+
+
+/*
+   Wrapper around MYSQL_LOG::write() for general log. We need it since we
+   want all log event handlers to have the same signature.
+*/
+
+bool Log_to_file_event_handler::
+  log_general(time_t event_time, const char *user_host,
+              uint user_host_len, int thread_id,
+              const char *command_type, uint command_type_len,
+              const char *sql_text, uint sql_text_len)
+{
+  return mysql_log.write(event_time, user_host, user_host_len,
+                         thread_id, command_type, command_type_len,
+                         sql_text, sql_text_len);
+}
+
+
+bool Log_to_file_event_handler::init()
+{
+  if (!is_initialized)
+  {
+    if (opt_slow_log)
+      mysql_slow_log.open_slow_log(opt_slow_logname);
+
+    if (opt_log)
+      mysql_log.open_query_log(opt_logname);
+
+    is_initialized= TRUE;
+  }
+
+  return FALSE;
+}
+
+
+void Log_to_file_event_handler::cleanup()
+{
+  mysql_log.cleanup();
+  mysql_slow_log.cleanup();
+}
+
+void Log_to_file_event_handler::flush()
+{
+  /* reopen log files */
+  mysql_log.new_file(1);
+  mysql_slow_log.new_file(1);
+}
+
+/*
+  Log error with all enabled log event handlers
+
+  SYNOPSIS
+    error_log_print()
+
+    level             The level of the error significance: NOTE,
+                      WARNING or ERROR.
+    format            format string for the error message
+    args              list of arguments for the format string
+
+  RETURN
+    FALSE - OK
+    TRUE - error occured
+*/
+
+bool LOGGER::error_log_print(enum loglevel level, const char *format,
+                             va_list args)
+{
+  bool error= FALSE;
+  Log_event_handler **current_handler= error_log_handler_list;
+
+  /* currently we don't need locking here as there is no error_log table */
+  while (*current_handler)
+    error= (*current_handler++)->log_error(level, format, args) || error;
+
+  return error;
+}
+
+
+void LOGGER::cleanup()
+{
+  DBUG_ASSERT(inited == 1);
+  (void) pthread_mutex_destroy(&LOCK_logger);
+  if (table_log_handler)
+    table_log_handler->cleanup();
+  if (file_log_handler)
+    file_log_handler->cleanup();
+}
+
+
+void LOGGER::close_log_table(uint log_type, bool lock_in_use)
+{
+  table_log_handler->close_log_table(log_type, lock_in_use);
+}
+
+
+/*
+  Perform basic log initialization: create file-based log handler and
+  init error log.
+*/
+void LOGGER::init_base()
+{
+  DBUG_ASSERT(inited == 0);
+  inited= 1;
+
+  /*
+    Here we create file log handler. We don't do it for the table log handler
+    here as it cannot be created so early. The reason is THD initialization,
+    which depends on the system variables (parsed later).
+  */
+  if (!file_log_handler)
+    file_log_handler= new Log_to_file_event_handler;
+
+  /* by default we use traditional error log */
+  init_error_log(LEGACY);
+
+  file_log_handler->init_pthread_objects();
+  (void) pthread_mutex_init(&LOCK_logger, MY_MUTEX_INIT_SLOW);
+}
+
+
+void LOGGER::init_log_tables()
+{
+  if (!table_log_handler)
+    table_log_handler= new Log_to_csv_event_handler;
+
+  if (!is_log_tables_initialized &&
+      !table_log_handler->init() && !file_log_handler->init())
+    is_log_tables_initialized= TRUE;
+}
+
+
+bool LOGGER::reopen_log_table(uint log_type)
+{
+  return table_log_handler->reopen_log_table(log_type);
+}
+
+
+bool LOGGER::flush_logs(THD *thd)
+{
+  TABLE_LIST close_slow_log, close_general_log;
+
+  /* reopen log tables */
+  bzero((char*) &close_slow_log, sizeof(TABLE_LIST));
+  close_slow_log.alias= close_slow_log.table_name=(char*) "slow_log";
+  close_slow_log.table_name_length= 8;
+  close_slow_log.db= (char*) "mysql";
+  close_slow_log.db_length= 5;
+
+  bzero((char*) &close_general_log, sizeof(TABLE_LIST));
+  close_general_log.alias= close_general_log.table_name=(char*) "general_log";
+  close_general_log.table_name_length= 11;
+  close_general_log.db= (char*) "mysql";
+  close_general_log.db_length= 5;
+
+  /* reopen log files */
+  file_log_handler->flush();
+
+  /*
+    this will lock and wait for all but the logger thread to release the
+    tables. Then we could reopen log tables. Then release the name locks.
+  */
+  lock_and_wait_for_table_name(thd, &close_slow_log);
+  lock_and_wait_for_table_name(thd, &close_general_log);
+
+  /* deny others from logging to general and slow log, while reopening tables */
+  logger.lock();
+
+  table_log_handler->flush(thd, &close_slow_log, &close_general_log);
+
+  /* end of log tables flush */
+  logger.unlock();
+  return FALSE;
+}
+
+
+/*
+  Log slow query with all enabled log event handlers
+
+  SYNOPSIS
+    slow_log_print()
+
+    thd               THD of the query being logged
+    query             The query being logged
+    query_length      The length of the query string
+    query_start_arg   Query start timestamp
+
+  RETURN
+    FALSE - OK
+    TRUE - error occured
+*/
+
+bool LOGGER::slow_log_print(THD *thd, const char *query, uint query_length,
+                            time_t query_start_arg)
+{
+  bool error= FALSE;
+  Log_event_handler **current_handler= slow_log_handler_list;
+  bool is_command= FALSE;
+
+  char message_buff[MAX_LOG_BUFFER_SIZE];
+  char user_host_buff[MAX_USER_HOST_SIZE];
+
+  my_time_t current_time;
+  Security_context *sctx= thd->security_ctx;
+  uint message_buff_len= 0, user_host_len= 0;
+  longlong query_time= 0, lock_time= 0;
+  longlong last_insert_id= 0, insert_id= 0;
+
+  /*
+    Print the message to the buffer if we have slow log enabled
+  */
+
+  if (*slow_log_handler_list)
+  {
+    current_time= time(NULL);
+
+    if (!(thd->options & OPTION_UPDATE_LOG))
+      return 0;
+
+    lock();
+
+    /* fill in user_host value: the format is "%s[%s] @ %s [%s]" */
+    user_host_len= strxnmov(user_host_buff, MAX_USER_HOST_SIZE,
+                            sctx->priv_user ? sctx->priv_user : "", "[",
+                            sctx->user ? sctx->user : "", "] @ ",
+                            sctx->host ? sctx->host : "", " [",
+                            sctx->ip ? sctx->ip : "", "]", NullS) -
+      user_host_buff;
+
+    if (query_start_arg)
+    {
+      query_time= (longlong) (current_time - query_start_arg);
+      lock_time= (longlong) (thd->time_after_lock - query_start_arg);
+    }
+
+    if (thd->last_insert_id_used)
+      last_insert_id= (longlong) thd->current_insert_id;
+
+    /* set value if we do an insert on autoincrement column */
+    if (thd->insert_id_used)
+      insert_id= (longlong) thd->last_insert_id;
+
+    if (!query)
+    {
+      is_command= TRUE;
+      query= command_name[thd->command].str;
+      query_length= command_name[thd->command].length;
+    }
+
+    while (*current_handler)
+      error= (*current_handler++)->log_slow(thd, current_time, query_start_arg,
+                                            user_host_buff, user_host_len,
+                                            query_time, lock_time, is_command,
+                                            query, query_length) || error;
+
+    unlock();
+  }
+  return error;
+}
+
+bool LOGGER::general_log_print(THD *thd, enum enum_server_command command,
+                               const char *format, va_list args)
+{
+  bool error= FALSE;
+  Log_event_handler **current_handler= general_log_handler_list;
+
+  /*
+    Print the message to the buffer if we have at least one log event handler
+    enabled and want to log this king of commands
+  */
+  if (*general_log_handler_list && (what_to_log & (1L << (uint) command)))
+  {
+    char message_buff[MAX_LOG_BUFFER_SIZE];
+    char user_host_buff[MAX_USER_HOST_SIZE];
+    Security_context *sctx= thd->security_ctx;
+    ulong id;
+    uint message_buff_len= 0, user_host_len= 0;
+
+    if (thd)
+    {                                           /* Normal thread */
+      if ((thd->options & OPTION_LOG_OFF)
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+          && (sctx->master_access & SUPER_ACL)
+#endif
+         )
+      {
+        return 0;                         /* No logging */
+      }
+      id= thd->thread_id;
+    }
+    else
+      id=0;                                     /* Log from connect handler */
+
+    lock();
+    time_t current_time= time(NULL);
+
+    user_host_len= strxnmov(user_host_buff, MAX_USER_HOST_SIZE,
+                            sctx->priv_user ? sctx->priv_user : "", "[",
+                            sctx->user ? sctx->user : "", "] @ ",
+                            sctx->host ? sctx->host : "", " [",
+                            sctx->ip ? sctx->ip : "", "]", NullS) -
+                                                            user_host_buff;
+
+    /* prepare message */
+    if (format)
+      message_buff_len= my_vsnprintf(message_buff,
+                   sizeof(message_buff), format, args);
+    else
+      message_buff[0]= '\0';
+
+    while (*current_handler)
+      error+= (*current_handler++)->
+        log_general(current_time, user_host_buff,
+                   user_host_len, id,
+                   command_name[(uint) command].str,
+                   command_name[(uint) command].length,
+                   message_buff, message_buff_len) || error;
+    unlock();
+  }
+  return error;
+}
+
+void LOGGER::init_error_log(enum enum_printer error_log_printer)
+{
+  switch (error_log_printer) {
+  case NONE:
+    error_log_handler_list[0]= 0;
+    break;
+  case LEGACY:
+    error_log_handler_list[0]= file_log_handler;
+    error_log_handler_list[1]= 0;
+    break;
+    /* these two are disabled for now */
+  case CSV:
+    DBUG_ASSERT(0);
+    break;
+  case LEGACY_AND_CSV:
+    DBUG_ASSERT(0);
+    break;
+  }
+}
+
+void LOGGER::init_slow_log(enum enum_printer slow_log_printer)
+{
+  switch (slow_log_printer) {
+  case NONE:
+    slow_log_handler_list[0]= 0;
+    break;
+  case LEGACY:
+    slow_log_handler_list[0]= file_log_handler;
+    slow_log_handler_list[1]= 0;
+    break;
+  case CSV:
+    slow_log_handler_list[0]= table_log_handler;
+    slow_log_handler_list[1]= 0;
+    break;
+  case LEGACY_AND_CSV:
+    slow_log_handler_list[0]= file_log_handler;
+    slow_log_handler_list[1]= table_log_handler;
+    slow_log_handler_list[2]= 0;
+    break;
+  }
+}
+
+void LOGGER::init_general_log(enum enum_printer general_log_printer)
+{
+  switch (general_log_printer) {
+  case NONE:
+    general_log_handler_list[0]= 0;
+    break;
+  case LEGACY:
+    general_log_handler_list[0]= file_log_handler;
+    general_log_handler_list[1]= 0;
+    break;
+  case CSV:
+    general_log_handler_list[0]= table_log_handler;
+    general_log_handler_list[1]= 0;
+    break;
+  case LEGACY_AND_CSV:
+    general_log_handler_list[0]= file_log_handler;
+    general_log_handler_list[1]= table_log_handler;
+    general_log_handler_list[2]= 0;
+    break;
+  }
+}
+
+
+bool Log_to_csv_event_handler::flush(THD *thd, TABLE_LIST *close_slow_log,
+                                     TABLE_LIST *close_general_log)
+{
+  VOID(pthread_mutex_lock(&LOCK_open));
+  close_log_table(LOG_GENERAL, TRUE);
+  close_log_table(LOG_SLOW, TRUE);
+  close_general_log->next_local= close_slow_log;
+  query_cache_invalidate3(thd, close_general_log, 0);
+  unlock_table_name(thd, close_slow_log);
+  unlock_table_name(thd, close_general_log);
+  VOID(pthread_mutex_unlock(&LOCK_open));
+  return reopen_log_table(LOG_SLOW) || reopen_log_table(LOG_GENERAL);
+}
+
+/* the parameters are unused for the log tables */
+bool Log_to_csv_event_handler::init()
+{
+  /* we always open log tables. even if the logging is disabled */
+  return (open_log_table(LOG_GENERAL) || open_log_table(LOG_SLOW));
+}
+
+int LOGGER::set_handlers(enum enum_printer error_log_printer,
+                         enum enum_printer slow_log_printer,
+                         enum enum_printer general_log_printer)
+{
+  /* error log table is not supported yet */
+  DBUG_ASSERT(error_log_printer < CSV);
+
+  lock();
+
+  if ((slow_log_printer >= CSV || general_log_printer >= CSV) &&
+      !is_log_tables_initialized)
+  {
+    slow_log_printer= LEGACY;
+    general_log_printer= LEGACY;
+
+    sql_print_error("Failed to initialize log tables. "
+                    "Falling back to the old-fashioned logs");
+  }
+
+  init_error_log(error_log_printer);
+  init_slow_log(slow_log_printer);
+  init_general_log(general_log_printer);
+
+  unlock();
+
+  return 0;
+}
+
+
+/*
+  Close log table of a given type (general or slow log)
+
+  SYNOPSIS
+    close_log_table()
+
+    log_type       type of the log table to close: LOG_GENERAL or LOG_SLOW
+    lock_in_use    Set to TRUE if the caller owns LOCK_open. FALSE otherwise.
+
+  DESCRIPTION
+
+    The function closes a log table. It is invoked (1) when we need to reopen
+    log tables (e.g. FLUSH LOGS or TRUNCATE on the log table is being
+    executed) or (2) during shutdown.
+*/
+
+void Log_to_csv_event_handler::
+  close_log_table(uint log_type, bool lock_in_use)
+{
+  THD *log_thd, *curr= current_thd;
+  TABLE_LIST *table;
+
+  if (!logger.is_log_tables_initialized)
+    return;                                     /* do nothing */
+
+  switch (log_type) {
+  case LOG_GENERAL:
+    log_thd= general_log_thd;
+    table= &general_log;
+    break;
+  case LOG_SLOW:
+    log_thd= slow_log_thd;
+    table= &slow_log;
+    break;
+  default:
+    DBUG_ASSERT(0);
+  }
+
+  /*
+    Set thread stack start for the logger thread. See comment in
+    open_log_table() for details.
+  */
+  if (curr)
+    log_thd->thread_stack= curr->thread_stack;
+  else
+    log_thd->thread_stack= (char*) &log_thd;
+
+  /* close the table */
+  log_thd->store_globals();
+  table->table->file->ha_rnd_end();
+  /* discard logger mark before unlock*/
+  table->table->locked_by_logger= FALSE;
+  close_thread_tables(log_thd, lock_in_use);
+
+  if (curr)
+    curr->store_globals();
+  else
+  {
+    my_pthread_setspecific_ptr(THR_THD,  0);
+    my_pthread_setspecific_ptr(THR_MALLOC, 0);
+  }
+}
+
+
 /*
   this function is mostly a placeholder.
   conceptually, binlog initialization (now mostly done in MYSQL_LOG::open)
@@ -1527,95 +2413,97 @@
 
 
 /*
-  Write to normal (not rotable) log
-  This is the format for the 'normal' log.
+  Write a command to traditional general log file
+
+  SYNOPSIS
+    write()
+
+    event_time        command start timestamp
+    user_host         the pointer to the string with user@host info
+    user_host_len     length of the user_host string. this is computed once
+                      and passed to all general log  event handlers
+    thread_id         Id of the thread, issued a query
+    command_type      the type of the command being logged
+    command_type_len  the length of the string above
+    sql_text          the very text of the query being executed
+    sql_text_len      the length of sql_text string
+
+  DESCRIPTION
+
+   Log given command to to normal (not rotable) log file
+
+  RETURN
+    FASE - OK
+    TRUE - error occured
 */
 
-bool MYSQL_LOG::write(THD *thd,enum enum_server_command command,
-		      const char *format,...)
+bool MYSQL_LOG::write(time_t event_time, const char *user_host,
+                      uint user_host_len, int thread_id,
+                      const char *command_type, uint command_type_len,
+                      const char *sql_text, uint sql_text_len)
 {
-  if (is_open() && (what_to_log & (1L << (uint) command)))
+  char buff[32];
+  uint length= 0;
+  char time_buff[MAX_TIME_SIZE];
+  struct tm start;
+  uint time_buff_len= 0;
+
+  /* Test if someone closed between the is_open test and lock */
+  if (is_open())
   {
-    uint length;
-    int error= 0;
-    VOID(pthread_mutex_lock(&LOCK_log));
+    /* Note that my_b_write() assumes it knows the length for this */
+      if (event_time != last_time)
+      {
+        last_time= event_time;
 
-    /* Test if someone closed between the is_open test and lock */
-    if (is_open())
-    {
-      time_t skr;
-      ulong id;
-      va_list args;
-      va_start(args,format);
-      char buff[32];
-
-      if (thd)
-      {						// Normal thread
-	if ((thd->options & OPTION_LOG_OFF)
-#ifndef NO_EMBEDDED_ACCESS_CHECKS
-	    && (thd->security_ctx->master_access & SUPER_ACL)
-#endif
-)
-	{
-	  VOID(pthread_mutex_unlock(&LOCK_log));
-	  return 0;				// No logging
-	}
-	id=thd->thread_id;
-	if (thd->user_time || !(skr=thd->query_start()))
-	  skr=time(NULL);			// Connected
+        localtime_r(&event_time, &start);
+
+        time_buff_len= my_snprintf(time_buff, MAX_TIME_SIZE,
+                                   "%02d%02d%02d %2d:%02d:%02d",
+                                   start.tm_year % 100, start.tm_mon + 1,
+                                   start.tm_mday, start.tm_hour,
+                                   start.tm_min, start.tm_sec);
+
+        if (my_b_write(&log_file, (byte*) &time_buff, time_buff_len))
+          goto err;
       }
       else
-      {						// Log from connect handler
-	skr=time(NULL);
-	id=0;
-      }
-      if (skr != last_time)
-      {
-	last_time=skr;
-	struct tm tm_tmp;
-	struct tm *start;
-	localtime_r(&skr,&tm_tmp);
-	start=&tm_tmp;
-	/* Note that my_b_write() assumes it knows the length for this */
-	sprintf(buff,"%02d%02d%02d %2d:%02d:%02d\t",
-		start->tm_year % 100,
-		start->tm_mon+1,
-		start->tm_mday,
-		start->tm_hour,
-		start->tm_min,
-		start->tm_sec);
-	if (my_b_write(&log_file, (byte*) buff,16))
-	  error=errno;
-      }
-      else if (my_b_write(&log_file, (byte*) "\t\t",2) < 0)
-	error=errno;
-      length=my_sprintf(buff,
-			(buff, "%7ld %-11.11s", id,
-			 command_name[(uint) command]));
-      if (my_b_write(&log_file, (byte*) buff,length))
-	error=errno;
-      if (format)
-      {
-	if (my_b_write(&log_file, (byte*) " ",1) ||
-	    my_b_vprintf(&log_file,format,args) == (uint) -1)
-	  error=errno;
-      }
-      if (my_b_write(&log_file, (byte*) "\n",1) ||
-	  flush_io_cache(&log_file))
-	error=errno;
-      if (error && ! write_error)
-      {
-	write_error=1;
-	sql_print_error(ER(ER_ERROR_ON_WRITE),name,error);
-      }
-      va_end(args);
-    }
-    VOID(pthread_mutex_unlock(&LOCK_log));
-    return error != 0;
+        if (my_b_write(&log_file, (byte*) "\t\t" ,2) < 0)
+          goto err;
+
+    /* command_type, thread_id */
+    length= my_snprintf(buff, 32, "%5ld ", thread_id);
+
+    if (my_b_write(&log_file, (byte*) buff, length))
+      goto err;
+
+    if (my_b_write(&log_file, (byte*) command_type, command_type_len))
+      goto err;
+
+    if (my_b_write(&log_file, (byte*) "\t", 1))
+      goto err;
+
+    /* sql_text */
+    if (my_b_write(&log_file, (byte*) sql_text, sql_text_len))
+      goto err;
+
+    if (my_b_write(&log_file, (byte*) "\n", 1) ||
+        flush_io_cache(&log_file))
+      goto err;
   }
-  return 0;
+
+  return FALSE;
+err:
+
+  if (!write_error)
+  {
+    write_error= 1;
+    sql_print_error(ER(ER_ERROR_ON_WRITE), name, errno);
+  }
+  return TRUE;
 }
 
+
 bool MYSQL_LOG::flush_and_sync()
 {
   int err=0, fd=log_file.file;
@@ -2003,6 +2891,34 @@
   DBUG_RETURN(error);
 }
 
+
+int error_log_print(enum loglevel level, const char *format,
+                    va_list args)
+{
+  return logger.error_log_print(level, format, args);
+}
+
+
+bool slow_log_print(THD *thd, const char *query, uint query_length,
+                    time_t query_start_arg)
+{
+  return logger.slow_log_print(thd, query, query_length, query_start_arg);
+}
+
+
+bool general_log_print(THD *thd, enum enum_server_command command,
+                       const char *format, ...)
+{
+  va_list args;
+  uint error= 0;
+
+  va_start(args, format);
+  error= logger.general_log_print(thd, command, format, args);
+  va_end(args);
+
+  return error;
+}
+
 void MYSQL_LOG::rotate_and_purge(uint flags)
 {
   if (!(flags & RP_LOCK_LOG_IS_ALREADY_LOCKED))
@@ -2147,71 +3063,86 @@
 
 
 /*
-  Write to the slow query log.
+  Log a query to the traditional slow log file
+
+  SYNOPSIS
+    write()
+
+    thd               THD of the query
+    current_time      current timestamp
+    query_start_arg   command start timestamp
+    user_host         the pointer to the string with user@host info
+    user_host_len     length of the user_host string. this is computed once
+                      and passed to all general log event handlers
+    query_time        Amount of time the query took to execute (in seconds)
+    lock_time         Amount of time the query was locked (in seconds)
+    is_command        The flag, which determines, whether the sql_text is a
+                      query or an administrator command.
+    sql_text          the very text of the query or administrator command
+                      processed
+    sql_text_len      the length of sql_text string
+
+  DESCRIPTION
+
+   Log a query to the slow log file.
+
+  RETURN
+    FALSE - OK
+    TRUE - error occured
 */
 
-bool MYSQL_LOG::write(THD *thd,const char *query, uint query_length,
-		      time_t query_start_arg)
+bool MYSQL_LOG::write(THD *thd, time_t current_time, time_t query_start_arg,
+                      const char *user_host, uint user_host_len,
+                      longlong query_time, longlong lock_time, bool is_command,
+                      const char *sql_text, uint sql_text_len)
 {
-  bool error=0;
-  time_t current_time;
-  if (!is_open())
-    return 0;
+  bool error= 0;
   DBUG_ENTER("MYSQL_LOG::write");
 
-  VOID(pthread_mutex_lock(&LOCK_log));
+  if (!is_open())
+    DBUG_RETURN(0);
+
   if (is_open())
   {						// Safety agains reopen
-    int tmp_errno=0;
-    char buff[80],*end;
-    end=buff;
-    if (!(thd->options & OPTION_UPDATE_LOG))
-    {
-      VOID(pthread_mutex_unlock(&LOCK_log));
-      DBUG_RETURN(0);
-    }
-    if (!(specialflag & SPECIAL_SHORT_LOG_FORMAT) || query_start_arg)
+    int tmp_errno= 0;
+    char buff[80], *end;
+    uint buff_len;
+    end= buff;
+
+    if (!(specialflag & SPECIAL_SHORT_LOG_FORMAT))
     {
       Security_context *sctx= thd->security_ctx;
-      current_time=time(NULL);
       if (current_time != last_time)
       {
-        last_time=current_time;
-        struct tm tm_tmp;
-        struct tm *start;
-        localtime_r(&current_time,&tm_tmp);
-        start=&tm_tmp;
+        last_time= current_time;
+        struct tm start;
+        localtime_r(&current_time, &start);
+
+        buff_len= my_snprintf(buff, sizeof buff,
+                              "# Time: %02d%02d%02d %2d:%02d:%02d\n",
+                              start.tm_year % 100, start.tm_mon + 1,
+                              start.tm_mday, start.tm_hour,
+                              start.tm_min, start.tm_sec);
+
         /* Note that my_b_write() assumes it knows the length for this */
-        sprintf(buff,"# Time: %02d%02d%02d %2d:%02d:%02d\n",
-                start->tm_year % 100,
-                start->tm_mon+1,
-                start->tm_mday,
-                start->tm_hour,
-                start->tm_min,
-                start->tm_sec);
-        if (my_b_write(&log_file, (byte*) buff,24))
+        if (my_b_write(&log_file, (byte*) buff, buff_len))
           tmp_errno=errno;
       }
-      if (my_b_printf(&log_file, "# User@Host: %s[%s] @ %s [%s]\n",
-                      sctx->priv_user ?
-                      sctx->priv_user : "",
-                      sctx->user ? sctx->user : "",
-                      sctx->host ? sctx->host : "",
-                      sctx->ip ? sctx->ip : "") ==
-          (uint) -1)
+      if (my_b_printf(&log_file, "# User@Host: ", sizeof("# User@Host: ") - 1))
         tmp_errno=errno;
-    }
-    if (query_start_arg)
-    {
-      /* For slow query log */
-      if (my_b_printf(&log_file,
-                      "# Query_time: %lu  Lock_time: %lu  Rows_sent: %lu  Rows_examined: %lu\n",
-                      (ulong) (current_time - query_start_arg),
-                      (ulong) (thd->time_after_lock - query_start_arg),
-                      (ulong) thd->sent_row_count,
-                      (ulong) thd->examined_row_count) == (uint) -1)
+      if (my_b_printf(&log_file, user_host, user_host_len))
+        tmp_errno=errno;
+      if (my_b_write(&log_file, (byte*) "\n", 1))
         tmp_errno=errno;
     }
+    /* For slow query log */
+    if (my_b_printf(&log_file,
+                    "# Query_time: %lu  Lock_time: %lu"
+                    " Rows_sent: %lu  Rows_examined: %lu\n",
+                    (ulong) query_time, (ulong) lock_time,
+                    (ulong) thd->sent_row_count,
+                    (ulong) thd->examined_row_count) == (uint) -1)
+      tmp_errno=errno;
     if (thd->db && strcmp(thd->db,db))
     {						// Database changed
       if (my_b_printf(&log_file,"use %s;\n",thd->db) == (uint) -1)
@@ -2232,15 +3163,15 @@
         end=longlong10_to_str((longlong) thd->last_insert_id,end,-10);
       }
     }
-    if (thd->query_start_used)
-    {
-      if (query_start_arg != thd->query_start())
-      {
-        query_start_arg=thd->query_start();
-        end=strmov(end,",timestamp=");
-        end=int10_to_str((long) query_start_arg,end,10);
-      }
-    }
+
+    /*
+      This info used to show up randomly, depending on whether the query
+      checked the query start time or not. now we always write current
+      timestamp to the slow log
+    */
+    end= strmov(end, ",timestamp=");
+    end= int10_to_str((long) current_time, end, 10);
+
     if (end != buff)
     {
       *end++=';';
@@ -2249,14 +3180,13 @@
           my_b_write(&log_file, (byte*) buff+1,(uint) (end-buff)))
         tmp_errno=errno;
     }
-    if (!query)
+    if (is_command)
     {
-      end=strxmov(buff, "# administrator command: ",
-                  command_name[thd->command], NullS);
-      query_length=(ulong) (end-buff);
-      query=buff;
+      end= strxmov(buff, "# administrator command: ", NullS);
+      buff_len= (ulong) (end - buff);
+      my_b_write(&log_file, (byte*) buff, buff_len);
     }
-    if (my_b_write(&log_file, (byte*) query,query_length) ||
+    if (my_b_write(&log_file, (byte*) sql_text, sql_text_len) ||
         my_b_write(&log_file, (byte*) ";\n",2) ||
         flush_io_cache(&log_file))
       tmp_errno=errno;
@@ -2270,7 +3200,6 @@
       }
     }
   }
-  VOID(pthread_mutex_unlock(&LOCK_log));
   DBUG_RETURN(error);
 }
 
@@ -2463,6 +3392,7 @@
   skr=time(NULL);
   localtime_r(&skr, &tm_tmp);
   start=&tm_tmp;
+
   fprintf(stderr, "%02d%02d%02d %2d:%02d:%02d [%s] %s\n",
           start->tm_year % 100,
           start->tm_mon+1,
@@ -2649,23 +3579,26 @@
     to other functions to write that message to other logging sources.
 
   RETURN VALUES
-    void
+    The function always returns 0. The return value is present in the
+    signature to be compatible with other logging routines, which could
+    return an error (e.g. logging to the log tables)
 */
 
-void vprint_msg_to_log(enum loglevel level, const char *format, va_list args)
+int vprint_msg_to_log(enum loglevel level, const char *format, va_list args)
 {
   char   buff[1024];
   uint length;
   DBUG_ENTER("vprint_msg_to_log");
 
-  length= my_vsnprintf(buff, sizeof(buff)-5, format, args);
+  /* "- 5" is because of print_buffer_to_nt_eventlog() */
+  length= my_vsnprintf(buff, sizeof(buff) - 5, format, args);
   print_buffer_to_file(level, buff);
 
 #ifdef __NT__
   print_buffer_to_nt_eventlog(level, buff, length, sizeof(buff));
 #endif
 
-  DBUG_VOID_RETURN;
+  DBUG_RETURN(0);
 }
 
 
@@ -2675,7 +3608,7 @@
   DBUG_ENTER("sql_print_error");
 
   va_start(args, format);
-  vprint_msg_to_log(ERROR_LEVEL, format, args);
+  error_log_print(ERROR_LEVEL, format, args);
   va_end(args);
 
   DBUG_VOID_RETURN;
@@ -2688,7 +3621,7 @@
   DBUG_ENTER("sql_print_warning");
 
   va_start(args, format);
-  vprint_msg_to_log(WARNING_LEVEL, format, args);
+  error_log_print(WARNING_LEVEL, format, args);
   va_end(args);
 
   DBUG_VOID_RETURN;
@@ -2701,7 +3634,7 @@
   DBUG_ENTER("sql_print_information");
 
   va_start(args, format);
-  vprint_msg_to_log(INFORMATION_LEVEL, format, args);
+  error_log_print(INFORMATION_LEVEL, format, args);
   va_end(args);
 
   DBUG_VOID_RETURN;

--- 1.198/sql/log_event.cc	2006-01-17 14:43:32 +03:00
+++ 1.199/sql/log_event.cc	2006-01-19 05:56:00 +03:00
@@ -1795,7 +1795,7 @@
 
     /* If the query was not ignored, it is printed to the general log */
     if (thd->net.last_errno != ER_SLAVE_IGNORED_TABLE)
-      mysql_log.write(thd,COM_QUERY,"%s",thd->query);
+      general_log_print(thd, COM_QUERY, "%s", thd->query);
 
 compare_errors:
 
@@ -3513,7 +3513,8 @@
 int Xid_log_event::exec_event(struct st_relay_log_info* rli)
 {
   /* For a slave Xid_log_event is COMMIT */
-  mysql_log.write(thd,COM_QUERY,"COMMIT /* implicit, from Xid_log_event */");
+  general_log_print(thd, COM_QUERY,
+                    "COMMIT /* implicit, from Xid_log_event */");
   return end_trans(thd, COMMIT) || Log_event::exec_event(rli);
 }
 #endif /* !MYSQL_CLIENT */

--- 1.369/sql/mysql_priv.h	2006-01-18 11:38:51 +03:00
+++ 1.370/sql/mysql_priv.h	2006-01-19 05:56:00 +03:00
@@ -1171,12 +1171,23 @@
 bool init_errmessage(void);
 void sql_perror(const char *message);
 
-void vprint_msg_to_log(enum loglevel level, const char *format, va_list args);
+int vprint_msg_to_log(enum loglevel level, const char *format, va_list args);
 void sql_print_error(const char *format, ...);
 void sql_print_warning(const char *format, ...);
 void sql_print_information(const char *format, ...);
 
+/* type of the log table */
+#define LOG_SLOW 1
+#define LOG_GENERAL 2
 
+int error_log_print(enum loglevel level, const char *format,
+                    va_list args);
+
+bool slow_log_print(THD *thd, const char *query, uint query_length,
+                    time_t query_start_arg);
+
+bool general_log_print(THD *thd, enum enum_server_command command,
+                       const char *format,...);
 
 bool fn_format_relative_to_data_home(my_string to, const char *name,
 				     const char *dir, const char *extension);
@@ -1217,7 +1228,7 @@
             def_ft_boolean_syntax[sizeof(ft_boolean_syntax)];
 #define mysql_tmpdir (my_tmpdir(&mysql_tmpdir_list))
 extern MY_TMPDIR mysql_tmpdir_list;
-extern const char *command_name[];
+extern LEX_STRING command_name[];
 extern const char *first_keyword, *my_localhost, *delayed_user, *binary_keyword;
 extern const char **errmesg;			/* Error messages */
 extern const char *myisam_recover_options_str;
@@ -1279,6 +1290,7 @@
 extern bool opt_using_transactions, mysqld_embedded;
 extern bool using_update_log, opt_large_files, server_id_supplied;
 extern bool opt_log, opt_update_log, opt_bin_log, opt_slow_log, opt_error_log;
+extern bool opt_old_log_format;
 extern bool opt_disable_networking, opt_skip_show_db;
 extern my_bool opt_character_set_client_handshake;
 extern bool volatile abort_loop, shutdown_in_progress, grant_option;
@@ -1300,7 +1312,9 @@
 extern my_bool opt_large_pages;
 extern uint opt_large_page_size;
 
-extern MYSQL_LOG mysql_log,mysql_slow_log,mysql_bin_log;
+extern MYSQL_LOG mysql_bin_log;
+extern LOGGER logger;
+extern TABLE_LIST general_log, slow_log;
 extern FILE *bootstrap_file;
 extern int bootstrap_error;
 extern FILE *stderror_file;

--- 1.519/sql/mysqld.cc	2006-01-17 11:24:49 +03:00
+++ 1.520/sql/mysqld.cc	2006-01-19 05:56:00 +03:00
@@ -331,6 +331,9 @@
 
 bool opt_log, opt_update_log, opt_bin_log, opt_slow_log;
 bool opt_error_log= IF_WIN(1,0);
+#ifdef WITH_CSV_STORAGE_ENGINE
+bool opt_old_log_format, opt_both_log_formats;
+#endif
 bool opt_disable_networking=0, opt_skip_show_db=0;
 my_bool opt_character_set_client_handshake= 1;
 bool server_id_supplied = 0;
@@ -603,6 +606,7 @@
 my_bool master_ssl;
 char *master_ssl_key, *master_ssl_cert;
 char *master_ssl_ca, *master_ssl_capath, *master_ssl_cipher;
+char *opt_logname, *opt_slow_logname;
 
 /* Static variables */
 
@@ -610,8 +614,8 @@
 static my_bool opt_do_pstack, opt_bootstrap, opt_myisam_log;
 static int cleanup_done;
 static ulong opt_specialflag, opt_myisam_block_size;
-static char *opt_logname, *opt_update_logname, *opt_binlog_index_name;
-static char *opt_slow_logname, *opt_tc_heuristic_recover;
+static char *opt_update_logname, *opt_binlog_index_name;
+static char *opt_tc_heuristic_recover;
 static char *mysql_home_ptr, *pidfile_name_ptr;
 static char **defaults_argv;
 static char *opt_bin_logname;
@@ -1138,8 +1142,7 @@
   if (cleanup_done++)
     return; /* purecov: inspected */
 
-  mysql_log.cleanup();
-  mysql_slow_log.cleanup();
+  logger.cleanup();
   /*
     make sure that handlers finish up
     what they have that is dependent on the binlog
@@ -2389,6 +2392,9 @@
 #ifdef EXTRA_DEBUG
       sql_print_information("Got signal %d to shutdown mysqld",sig);
 #endif
+      /* switch to the old log message processing */
+      logger.set_handlers(LEGACY, opt_slow_log ? LEGACY:NONE,
+                          opt_log ? LEGACY:NONE);
       DBUG_PRINT("info",("Got signal: %d  abort_loop: %d",sig,abort_loop));
       if (!abort_loop)
       {
@@ -2416,6 +2422,9 @@
 			      REFRESH_THREADS | REFRESH_HOSTS),
 			     (TABLE_LIST*) 0, &not_used); // Flush logs
       }
+      /* reenable logs after the options were reloaded */
+      logger.set_handlers(LEGACY, opt_slow_log ? CSV:NONE,
+                          opt_log ? CSV:NONE);
       break;
 #ifdef USE_ONE_SIGNAL_HAND
     case THR_SERVER_ALARM:
@@ -2680,8 +2689,6 @@
     global MYSQL_LOGs in their constructors, because then they would be inited
     before MY_INIT(). So we do it here.
   */
-  mysql_log.init_pthread_objects();
-  mysql_slow_log.init_pthread_objects();
   mysql_bin_log.init_pthread_objects();
 
   if (gethostname(glob_hostname,sizeof(glob_hostname)-4) < 0)
@@ -3047,9 +3054,48 @@
 #ifdef HAVE_REPLICATION
   init_slave_list();
 #endif
-  /* Setup log files */
-  if (opt_log)
-    mysql_log.open_query_log(opt_logname);
+  /* Setup logs */
+
+  /* enable old-fashioned error log */
+  if (opt_error_log)
+  {
+    if (!log_error_file_ptr[0])
+      fn_format(log_error_file, glob_hostname, mysql_data_home, ".err",
+                MY_REPLACE_EXT); /* replace '.<domain>' by '.err', bug#4997 */
+    else
+      fn_format(log_error_file, log_error_file_ptr, mysql_data_home, ".err",
+                MY_UNPACK_FILENAME | MY_SAFE_PATH);
+    if (!log_error_file[0])
+      opt_error_log= 1;				// Too long file name
+    else
+    {
+#ifndef EMBEDDED_LIBRARY
+      if (freopen(log_error_file, "a+", stdout))
+#endif
+        freopen(log_error_file, "a+", stderr);
+    }
+  }
+
+#ifdef WITH_CSV_STORAGE_ENGINE
+  logger.init_log_tables();
+
+  if (opt_old_log_format || (have_csv_db != SHOW_OPTION_YES))
+    logger.set_handlers(LEGACY, opt_slow_log ? LEGACY:NONE,
+                        opt_log ? LEGACY:NONE);
+  else
+    if (opt_both_log_formats)
+      logger.set_handlers(LEGACY,
+                          opt_slow_log ? LEGACY_AND_CSV:NONE,
+                          opt_log ? LEGACY_AND_CSV:NONE);
+    else
+      /* the default is CSV log tables */
+      logger.set_handlers(LEGACY, opt_slow_log ? CSV:NONE,
+                          opt_log ? CSV:NONE);
+#else
+  logger.set_handlers(LEGACY, opt_slow_log ? LEGACY:NONE,
+                      opt_log ? LEGACY:NONE);
+#endif
+
   if (opt_update_log)
   {
     /*
@@ -3146,9 +3192,6 @@
               array_elements(binlog_format_names)-1);
   opt_binlog_format= binlog_format_names[opt_binlog_format_id];
 
-  if (opt_slow_log)
-    mysql_slow_log.open_slow_log(opt_slow_logname);
-
 #ifdef HAVE_REPLICATION
   if (opt_log_slave_updates && replicate_same_server_id)
   {
@@ -3160,25 +3203,6 @@
   }
 #endif
 
-  if (opt_error_log)
-  {
-    if (!log_error_file_ptr[0])
-      fn_format(log_error_file, glob_hostname, mysql_data_home, ".err",
-                MY_REPLACE_EXT); /* replace '.<domain>' by '.err', bug#4997 */
-    else
-      fn_format(log_error_file, log_error_file_ptr, mysql_data_home, ".err",
-		MY_UNPACK_FILENAME | MY_SAFE_PATH);
-    if (!log_error_file[0])
-      opt_error_log= 1;				// Too long file name
-    else
-    {
-#ifndef EMBEDDED_LIBRARY
-      if (freopen(log_error_file, "a+", stdout))
-#endif
-	stderror_file= freopen(log_error_file, "a+", stderr);
-    }
-  }
-
   if (opt_bin_log)
   {
     char buf[FN_REFLEN];
@@ -3432,6 +3456,12 @@
 
   MY_INIT(argv[0]);		// init my_sys library & pthreads
 
+  /*
+    Perform basic logger initialization logger. Should be called after
+    MY_INIT, as it initializes mutexes. Log tables are inited later.
+  */
+  logger.init_base();
+
 #ifdef _CUSTOMSTARTUPCONFIG_
   if (_cust_check_startup())
   {
@@ -3577,6 +3607,7 @@
   */
   error_handler_hook= my_message_sql;
   start_signal_handler();				// Creates pidfile
+
   if (acl_init(opt_noacl) ||
       my_tz_init((THD *)0, default_tz_name, opt_bootstrap))
   {
@@ -3701,7 +3732,7 @@
   clean_up_mutexes();
   shutdown_events();
   my_end(opt_endinfo ? MY_CHECK_ERROR | MY_GIVE_INFO : 0);
- 
+
   exit(0);
   return(0);					/* purecov: deadcode */
 }
@@ -4639,7 +4670,7 @@
   OPT_REPLICATE_IGNORE_TABLE,  OPT_REPLICATE_WILD_DO_TABLE,
   OPT_REPLICATE_WILD_IGNORE_TABLE, OPT_REPLICATE_SAME_SERVER_ID,
   OPT_DISCONNECT_SLAVE_EVENT_COUNT, OPT_TC_HEURISTIC_RECOVER,
-  OPT_ABORT_SLAVE_EVENT_COUNT,
+  OPT_ABORT_SLAVE_EVENT_COUNT, OPT_OLD_LOG_FORMAT, OPT_BOTH_LOG_FORMATS,
   OPT_INNODB_DATA_HOME_DIR,
   OPT_INNODB_DATA_FILE_PATH,
   OPT_INNODB_LOG_GROUP_HOME_DIR,
@@ -5195,6 +5226,16 @@
     "Log slow queries to this log file. Defaults logging to hostname-slow.log file. Must be enabled to activate other slow log options.",
    (gptr*) &opt_slow_logname, (gptr*) &opt_slow_logname, 0, GET_STR, OPT_ARG,
    0, 0, 0, 0, 0, 0},
+#ifdef WITH_CSV_STORAGE_ENGINE
+  {"old-log-format", OPT_OLD_LOG_FORMAT,
+   "Enable old log file format. (No SELECT * FROM logs)",
+   (gptr*) &opt_old_log_format, 0, 0, GET_BOOL, OPT_ARG,
+   0, 0, 0, 0, 0, 0},
+  {"both-log-formats", OPT_BOTH_LOG_FORMATS,
+   "Enable old log file format along with log tables",
+   (gptr*) &opt_both_log_formats, 0, 0, GET_BOOL, OPT_ARG,
+   0, 0, 0, 0, 0, 0},
+#endif
   {"log-tc", OPT_LOG_TC,
    "Path to transaction coordinator log (used for transactions that affect "
    "more than one storage engine, when binary log is disabled)",
@@ -6886,6 +6927,10 @@
   opt_skip_slave_start= opt_reckless_slave = 0;
   mysql_home[0]= pidfile_name[0]= log_error_file[0]= 0;
   opt_log= opt_update_log= opt_slow_log= 0;
+#ifdef WITH_CSV_STORAGE_ENGINE
+  opt_old_log_format= 0;
+  opt_both_log_formats= 0;
+#endif
   opt_bin_log= 0;
   opt_disable_networking= opt_skip_show_db=0;
   opt_logname= opt_update_logname= opt_binlog_index_name= opt_slow_logname= 0;
@@ -7294,8 +7339,16 @@
   }
 #endif /* HAVE_REPLICATION */
   case (int) OPT_SLOW_QUERY_LOG:
-    opt_slow_log=1;
+    opt_slow_log= 1;
     break;
+#ifdef WITH_CSV_STORAGE_ENGINE
+  case (int) OPT_OLD_LOG_FORMAT:
+    opt_old_log_format= 1;
+    break;
+  case (int) OPT_BOTH_LOG_FORMATS:
+    opt_both_log_formats= 1;
+    break;
+#endif
   case (int) OPT_SKIP_NEW:
     opt_specialflag|= SPECIAL_NO_NEW_FUNC;
     delay_key_write_options= (uint) DELAY_KEY_WRITE_NONE;

--- 1.261/sql/slave.cc	2006-01-12 21:50:31 +03:00
+++ 1.262/sql/slave.cc	2006-01-19 05:56:00 +03:00
@@ -4354,8 +4354,8 @@
     else
     {
       change_rpl_status(RPL_IDLE_SLAVE,RPL_ACTIVE_SLAVE);
-      mysql_log.write(thd, COM_CONNECT_OUT, "%s@%s:%d",
-		      mi->user, mi->host, mi->port);
+      general_log_print(thd, COM_CONNECT_OUT, "%s@%s:%d",
+                        mi->user, mi->host, mi->port);
     }
 #ifdef SIGNAL_WITH_VIO_CLOSE
     thd->set_active_vio(mysql->net.vio);

--- 1.297/sql/sql_base.cc	2006-01-17 10:37:27 +03:00
+++ 1.298/sql/sql_base.cc	2006-01-19 05:56:00 +03:00
@@ -835,7 +835,8 @@
     bool found=0;
     for (TABLE_LIST *table= tables; table; table= table->next_local)
     {
-      if (remove_table_from_cache(thd, table->db, table->table_name,
+      if ((!table->table || !table->table->s->log_table) &&
+          remove_table_from_cache(thd, table->db, table->table_name,
                                   RTFC_OWNED_BY_THD_FLAG))
 	found=1;
     }
@@ -869,7 +870,8 @@
       for (uint idx=0 ; idx < open_cache.records ; idx++)
       {
 	TABLE *table=(TABLE*) hash_element(&open_cache,idx);
-	if ((table->s->version) < refresh_version && table->db_stat)
+	if (!table->s->log_table &&
+            ((table->s->version) < refresh_version && table->db_stat))
 	{
 	  found=1;
 	  pthread_cond_wait(&COND_refresh,&LOCK_open);
@@ -1852,7 +1854,7 @@
   if (!thd->open_tables)
     thd->version=refresh_version;
   else if ((thd->version != refresh_version) &&
-           ! (flags & MYSQL_LOCK_IGNORE_FLUSH))
+           ! (flags & MYSQL_LOCK_IGNORE_FLUSH) && !table->s->log_table)
   {
     /* Someone did a refresh while thread was opening tables */
     if (refresh)
@@ -1873,7 +1875,11 @@
   {
     if (table->s->version != refresh_version)
     {
-      if (flags & MYSQL_LOCK_IGNORE_FLUSH)
+      /*
+        Don't close tables if we are working with a log table or were
+        asked not to close the table explicitly
+      */
+      if (flags & MYSQL_LOCK_IGNORE_FLUSH || table->s->log_table)
       {
         /* Force close at once after usage */
         thd->version= table->s->version;
@@ -2236,6 +2242,10 @@
   Wait until all threads has closed the tables in the list
   We have also to wait if there is thread that has a lock on this table even
   if the table is closed
+  NOTE: log tables are handled differently by the logging routines.
+        E.g. general_log is always opened and locked by the logger
+        and the table handler used by the logger, will be skipped by
+        this check.
 */
 
 bool table_is_used(TABLE *table, bool wait_for_name_lock)
@@ -2254,9 +2264,10 @@
          search= (TABLE*) hash_next(&open_cache, (byte*) key,
                                     key_length, &state))
     {
-      DBUG_PRINT("info", ("share: 0x%lx  locked_by_flush: %d  "
-                          "locked_by_name: %d  db_stat: %u  version: %u",
-                          (ulong) search->s,
+      DBUG_PRINT("info", ("share: 0x%lx  locked_by_logger: %d "
+                          "locked_by_flush: %d  locked_by_name: %d "
+                          "db_stat: %u  version: %u",
+                          (ulong) search->s, search->locked_by_logger,
                           search->locked_by_flush, search->locked_by_name,
                           search->db_stat,
                           search->s->version));
@@ -2267,12 +2278,15 @@
         - There is an name lock on it (Table is to be deleted or altered)
         - If we are in flush table and we didn't execute the flush
         - If the table engine is open and it's an old version
-          (We must wait until all engines are shut down to use the table)
+        (We must wait until all engines are shut down to use the table)
+        However we fo not wait if we encountered a table, locked by the logger.
+        Log tables are managed separately by logging routines.
       */
-      if (search->locked_by_name && wait_for_name_lock ||
-          search->locked_by_flush ||
-          (search->db_stat && search->s->version < refresh_version))
-	return 1;
+      if (!search->locked_by_logger &&
+          (search->locked_by_name && wait_for_name_lock ||
+           search->locked_by_flush ||
+           (search->db_stat && search->s->version < refresh_version)))
+        return 1;
     }
   } while ((table=table->next));
   DBUG_RETURN(0);
@@ -5867,6 +5881,7 @@
                                    &state))
     {
       THD *in_use;
+
       table->s->version=0L;		/* Free when thread is ready */
       if (!(in_use=table->in_use))
       {

--- 1.124/sql/sql_db.cc	2006-01-12 21:50:32 +03:00
+++ 1.125/sql/sql_db.cc	2006-01-19 05:56:00 +03:00
@@ -1158,8 +1158,8 @@
                sctx->priv_user,
                sctx->priv_host,
                dbname);
-      mysql_log.write(thd, COM_INIT_DB, ER(ER_DBACCESS_DENIED_ERROR),
-                      sctx->priv_user, sctx->priv_host, dbname);
+      general_log_print(thd, COM_INIT_DB, ER(ER_DBACCESS_DENIED_ERROR),
+                        sctx->priv_user, sctx->priv_host, dbname);
       my_free(dbname,MYF(0));
       DBUG_RETURN(1);
     }

--- 1.170/sql/sql_delete.cc	2006-01-09 13:22:55 +03:00
+++ 1.171/sql/sql_delete.cc	2006-01-19 05:56:01 +03:00
@@ -857,6 +857,8 @@
   char path[FN_REFLEN];
   TABLE *table;
   bool error;
+  uint closed_log_tables= 0, lock_logger= 0;
+  TABLE_LIST *tmp_table_list;
   uint path_length;
   DBUG_ENTER("mysql_truncate");
 
@@ -905,13 +907,36 @@
                                       HTON_CAN_RECREATE)
         || thd->lex->sphead)
       goto trunc_by_del;
+
     if (lock_and_wait_for_table_name(thd, table_list))
       DBUG_RETURN(TRUE);
   }
 
-  // Remove the .frm extension
-  // AIX 5.2 64-bit compiler bug (BUG#16155): this crashes, replacement works.
-  //   *(path + path_length - reg_ext_length)= '\0';
+  /* close log tables in use */
+  if (!my_strcasecmp(system_charset_info, table_list->db, "mysql"))
+  {
+    if (!my_strcasecmp(system_charset_info, table_list->table_name,
+                       "general_log"))
+    {
+      lock_logger= 1;
+      logger.lock();
+      logger.close_log_table(LOG_GENERAL, FALSE);
+      closed_log_tables= closed_log_tables | LOG_GENERAL;
+    }
+    else
+      if (!my_strcasecmp(system_charset_info, table_list->table_name,
+                         "slow_log"))
+      {
+        lock_logger= 1;
+        logger.lock();
+        logger.close_log_table(LOG_SLOW, FALSE);
+        closed_log_tables= closed_log_tables | LOG_SLOW;
+      }
+  }
+
+  // Remove the .frm extension AIX 5.2 64-bit compiler bug (BUG#16155): this
+  // crashes, replacement works.  *(path + path_length - reg_ext_length)=
+  // '\0';
   path[path_length - reg_ext_length] = 0;
   error= ha_create_table(thd, path, table_list->db, table_list->table_name,
                          &create_info, 1);
@@ -937,6 +962,14 @@
     VOID(pthread_mutex_lock(&LOCK_open));
     unlock_table_name(thd, table_list);
     VOID(pthread_mutex_unlock(&LOCK_open));
+
+    if (closed_log_tables & LOG_SLOW)
+      logger.reopen_log_table(LOG_SLOW);
+
+    if (closed_log_tables & LOG_GENERAL)
+      logger.reopen_log_table(LOG_GENERAL);
+    if (lock_logger)
+      logger.unlock();
   }
   else if (error)
   {

--- 1.509/sql/sql_parse.cc	2006-01-13 19:09:23 +03:00
+++ 1.510/sql/sql_parse.cc	2006-01-19 05:56:01 +03:00
@@ -73,14 +73,23 @@
 
 const char *any_db="*any*";	// Special symbol for check_access
 
-const char *command_name[]={
-  "Sleep", "Quit", "Init DB", "Query", "Field List", "Create DB",
-  "Drop DB", "Refresh", "Shutdown", "Statistics", "Processlist",
-  "Connect","Kill","Debug","Ping","Time","Delayed insert","Change user",
-  "Binlog Dump","Table Dump",  "Connect Out", "Register Slave",
-  "Prepare", "Execute", "Long Data", "Close stmt",
-  "Reset stmt", "Set option", "Fetch", "Daemon",
-  "Error"					// Last command number
+LEX_STRING command_name[]={
+  STRING_WITH_LEN("Sleep"), STRING_WITH_LEN("Quit"),
+  STRING_WITH_LEN("Init DB"), STRING_WITH_LEN("Query"),
+  STRING_WITH_LEN("Field List"), STRING_WITH_LEN("Create DB"),
+  STRING_WITH_LEN("Drop DB"), STRING_WITH_LEN("Refresh"),
+  STRING_WITH_LEN("Shutdown"), STRING_WITH_LEN("Statistics"),
+  STRING_WITH_LEN("Processlist"), STRING_WITH_LEN("Connect"),
+  STRING_WITH_LEN("Kill"), STRING_WITH_LEN("Debug"),
+  STRING_WITH_LEN("Ping"), STRING_WITH_LEN("Time"),
+  STRING_WITH_LEN("Delayed insert"), STRING_WITH_LEN("Change user"),
+  STRING_WITH_LEN("Binlog Dump"), STRING_WITH_LEN("Table Dump"),
+  STRING_WITH_LEN("Connect Out"), STRING_WITH_LEN("Register Slave"),
+  STRING_WITH_LEN("Prepare"), STRING_WITH_LEN("Execute"),
+  STRING_WITH_LEN("Long Data"), STRING_WITH_LEN("Close stmt"),
+  STRING_WITH_LEN("Reset stmt"), STRING_WITH_LEN("Set option"),
+  STRING_WITH_LEN("Fetch"), STRING_WITH_LEN("Daemon"),
+  STRING_WITH_LEN("Error")  // Last command number
 };
 
 const char *xa_state_names[]={
@@ -322,7 +331,7 @@
   if (opt_secure_auth_local && passwd_len == SCRAMBLE_LENGTH_323)
   {
     net_printf_error(thd, ER_NOT_SUPPORTED_AUTH_MODE);
-    mysql_log.write(thd, COM_CONNECT, ER(ER_NOT_SUPPORTED_AUTH_MODE));
+    general_log_print(thd, COM_CONNECT, ER(ER_NOT_SUPPORTED_AUTH_MODE));
     DBUG_RETURN(-1);
   }
   if (passwd_len != 0 &&
@@ -356,9 +365,9 @@
       net_printf_error(thd, ER_SERVER_IS_IN_SECURE_AUTH_MODE,
                        thd->main_security_ctx.user,
                        thd->main_security_ctx.host_or_ip);
-      mysql_log.write(thd, COM_CONNECT, ER(ER_SERVER_IS_IN_SECURE_AUTH_MODE),
-                      thd->main_security_ctx.user,
-                      thd->main_security_ctx.host_or_ip);
+      general_log_print(thd, COM_CONNECT, ER(ER_SERVER_IS_IN_SECURE_AUTH_MODE),
+                        thd->main_security_ctx.user,
+                        thd->main_security_ctx.host_or_ip);
       DBUG_RETURN(-1);
     }
     /* We have to read very specific packet size */
@@ -406,14 +415,14 @@
       }
 
       /* Why logging is performed before all checks've passed? */
-      mysql_log.write(thd, command,
-                      (thd->main_security_ctx.priv_user ==
-                       thd->main_security_ctx.user ?
-                       (char*) "%s@%s on %s" :
-                       (char*) "%s@%s as anonymous on %s"),
-                      thd->main_security_ctx.user,
-                      thd->main_security_ctx.host_or_ip,
-                      db ? db : (char*) "");
+      general_log_print(thd, command,
+                        (thd->main_security_ctx.priv_user ==
+                         thd->main_security_ctx.user ?
+                         (char*) "%s@%s on %s" :
+                         (char*) "%s@%s as anonymous on %s"),
+                        thd->main_security_ctx.user,
+                        thd->main_security_ctx.host_or_ip,
+                        db ? db : (char*) "");
 
       /*
         This is the default access rights for the current database.  It's
@@ -460,17 +469,17 @@
   else if (res == 2) // client gave short hash, server has long hash
   {
     net_printf_error(thd, ER_NOT_SUPPORTED_AUTH_MODE);
-    mysql_log.write(thd,COM_CONNECT,ER(ER_NOT_SUPPORTED_AUTH_MODE));
+    general_log_print(thd, COM_CONNECT, ER(ER_NOT_SUPPORTED_AUTH_MODE));
     DBUG_RETURN(-1);
   }
   net_printf_error(thd, ER_ACCESS_DENIED_ERROR,
                    thd->main_security_ctx.user,
                    thd->main_security_ctx.host_or_ip,
                    passwd_len ? ER(ER_YES) : ER(ER_NO));
-  mysql_log.write(thd, COM_CONNECT, ER(ER_ACCESS_DENIED_ERROR),
-                  thd->main_security_ctx.user,
-                  thd->main_security_ctx.host_or_ip,
-                  passwd_len ? ER(ER_YES) : ER(ER_NO));
+  general_log_print(thd, COM_CONNECT, ER(ER_ACCESS_DENIED_ERROR),
+                    thd->main_security_ctx.user,
+                    thd->main_security_ctx.host_or_ip,
+                    passwd_len ? ER(ER_YES) : ER(ER_NO));
   DBUG_RETURN(-1);
 #endif /* NO_EMBEDDED_ACCESS_CHECKS */
 }
@@ -1570,7 +1579,7 @@
 			packet, strlen(packet), thd->charset());
     if (!mysql_change_db(thd, tmp.str, FALSE))
     {
-      mysql_log.write(thd,command,"%s",thd->db);
+      general_log_print(thd, command, "%s",thd->db);
       send_ok(thd);
     }
     break;
@@ -1703,7 +1712,7 @@
     if (alloc_query(thd, packet, packet_length))
       break;					// fatal error is set
     char *packet_end= thd->query + thd->query_length;
-    mysql_log.write(thd,command,"%s",thd->query);
+    general_log_print(thd, command, "%s", thd->query);
     DBUG_PRINT("query",("%-.4096s",thd->query));
 
     if (!(specialflag & SPECIAL_NO_PRIOR))
@@ -1812,7 +1821,7 @@
     thd->query_length= strlen(packet);       // for simplicity: don't optimize
     if (!(thd->query=fields=thd->memdup(packet,thd->query_length+1)))
       break;
-    mysql_log.write(thd,command,"%s %s",table_list.table_name, fields);
+    general_log_print(thd, command, "%s %s", table_list.table_name, fields);
     if (lower_case_table_names)
       my_casedn_str(files_charset_info, table_list.table_name);
     remove_escape(table_list.table_name);	// This can't have wildcards
@@ -1841,7 +1850,7 @@
 #endif
   case COM_QUIT:
     /* We don't calculate statistics for this command */
-    mysql_log.write(thd,command,NullS);
+    general_log_print(thd, command, NullS);
     net->error=0;				// Don't give 'abort' message
     error=TRUE;					// End server
     break;
@@ -1861,7 +1870,7 @@
       }
       if (check_access(thd,CREATE_ACL,db,0,1,0,is_schema_db(db)))
 	break;
-      mysql_log.write(thd,command,packet);
+      general_log_print(thd, command, packet);
       bzero(&create_info, sizeof(create_info));
       mysql_create_db(thd, (lower_case_table_names == 2 ? alias : db),
                       &create_info, 0);
@@ -1886,7 +1895,7 @@
                    ER(ER_LOCK_OR_ACTIVE_TRANSACTION), MYF(0));
 	break;
       }
-      mysql_log.write(thd,command,db);
+      general_log_print(thd, command, db);
       mysql_rm_db(thd, db, 0, 0);
       break;
     }
@@ -1910,7 +1919,7 @@
 	kill_zombie_dump_threads(slave_server_id);
       thd->server_id = slave_server_id;
 
-      mysql_log.write(thd, command, "Log: '%s'  Pos: %ld", packet+10,
+      general_log_print(thd, command, "Log: '%s'  Pos: %ld", packet+10,
                       (long) pos);
       mysql_binlog_send(thd, thd->strdup(packet + 10), (my_off_t) pos, flags);
       unregister_slave(thd,1,1);
@@ -1928,7 +1937,7 @@
     ulong options= (ulong) (uchar) packet[0];
     if (check_global_access(thd,RELOAD_ACL))
       break;
-    mysql_log.write(thd,command,NullS);
+    general_log_print(thd, command, NullS);
     if (!reload_acl_and_cache(thd, options, (TABLE_LIST*) 0, &not_used))
       send_ok(thd);
     break;
@@ -1956,7 +1965,7 @@
       break;
     }
     DBUG_PRINT("quit",("Got shutdown command for level %u", level));
-    mysql_log.write(thd,command,NullS);
+    general_log_print(thd, command, NullS);
     send_eof(thd);
 #ifdef __WIN__
     sleep(1);					// must wait after eof()
@@ -1973,7 +1982,7 @@
 #endif
   case COM_STATISTICS:
   {
-    mysql_log.write(thd,command,NullS);
+    general_log_print(thd, command, NullS);
     statistic_increment(thd->status_var.com_stat[SQLCOM_SHOW_STATUS],
 			&LOCK_status);
 #ifndef EMBEDDED_LIBRARY
@@ -2013,7 +2022,7 @@
     if (!thd->security_ctx->priv_user[0] &&
         check_global_access(thd, PROCESS_ACL))
       break;
-    mysql_log.write(thd,command,NullS);
+    general_log_print(thd, command, NullS);
     mysqld_list_processes(thd,
 			  thd->security_ctx->master_access & PROCESS_ACL ? 
 			  NullS : thd->security_ctx->priv_user, 0);
@@ -2050,7 +2059,7 @@
     if (check_global_access(thd, SUPER_ACL))
       break;					/* purecov: inspected */
     mysql_print_status();
-    mysql_log.write(thd,command,NullS);
+    general_log_print(thd, command, NullS);
     send_eof(thd);
     break;
   case COM_SLEEP:
@@ -2132,7 +2141,7 @@
 	 (specialflag & SPECIAL_LOG_QUERIES_NOT_USING_INDEXES)))
     {
       thd->status_var.long_query_count++;
-      mysql_slow_log.write(thd, thd->query, thd->query_length, start_of_query);
+      slow_log_print(thd, thd->query, thd->query_length, start_of_query);
     }
   }
 }
@@ -6541,7 +6550,8 @@
   {
     /*
       Flush the normal query log, the update log, the binary log,
-      the slow query log, and the relay log (if it exists).
+      the slow query log, the relay log (if it exists) and the log
+      tables.
     */
 
     /*
@@ -6551,14 +6561,16 @@
       than it would help them)
     */
     tmp_write_to_binlog= 0;
-    mysql_log.new_file(1);
-    mysql_slow_log.new_file(1);
     mysql_bin_log.rotate_and_purge(RP_FORCE_ROTATE);
 #ifdef HAVE_REPLICATION
     pthread_mutex_lock(&LOCK_active_mi);
     rotate_relay_log(active_mi);
     pthread_mutex_unlock(&LOCK_active_mi);
 #endif
+
+    /* flush slow and general logs */
+    logger.flush_logs(thd);
+
     if (ha_flush_logs(NULL))
       result=1;
     if (flush_error_log())

--- 1.292/sql/sql_show.cc	2006-01-18 11:24:35 +03:00
+++ 1.293/sql/sql_show.cc	2006-01-19 05:56:01 +03:00
@@ -605,8 +605,8 @@
   {
     my_error(ER_DBACCESS_DENIED_ERROR, MYF(0),
              sctx->priv_user, sctx->host_or_ip, dbname);
-    mysql_log.write(thd,COM_INIT_DB,ER(ER_DBACCESS_DENIED_ERROR),
-		    sctx->priv_user, sctx->host_or_ip, dbname);
+    general_log_print(thd,COM_INIT_DB,ER(ER_DBACCESS_DENIED_ERROR),
+                      sctx->priv_user, sctx->host_or_ip, dbname);
     DBUG_RETURN(TRUE);
   }
 #endif
@@ -1502,7 +1502,7 @@
     if (thd_info->proc_info)
       protocol->store(thd_info->proc_info, system_charset_info);
     else
-      protocol->store(command_name[thd_info->command], system_charset_info);
+      protocol->store(command_name[thd_info->command].str, system_charset_info);
     if (thd_info->start_time)
       protocol->store((uint32) (now - thd_info->start_time));
     else

--- 1.296/sql/sql_table.cc	2006-01-17 16:39:59 +03:00
+++ 1.297/sql/sql_table.cc	2006-01-19 05:56:01 +03:00
@@ -2928,7 +2928,8 @@
     }
 
     /* Close all instances of the table to allow repair to rename files */
-    if (lock_type == TL_WRITE && table->table->s->version)
+    if (lock_type == TL_WRITE && table->table->s->version &&
+        !table->table->s->log_table)
     {
       pthread_mutex_lock(&LOCK_open);
       const char *old_message=thd->enter_cond(&COND_refresh, &LOCK_open,
@@ -3098,9 +3099,10 @@
     }
     if (table->table)
     {
+      /* in the below check we do not refresh the log tables */
       if (fatal_error)
         table->table->s->version=0;               // Force close of table
-      else if (open_for_modify)
+      else if (open_for_modify && !table->table->s->log_table)
       {
         pthread_mutex_lock(&LOCK_open);
         remove_table_from_cache(thd, table->table->s->db.str,

--- 1.200/sql/table.cc	2006-01-17 10:37:29 +03:00
+++ 1.201/sql/table.cc	2006-01-19 05:56:01 +03:00
@@ -310,16 +310,29 @@
     error= open_binary_frm(thd, share, head, file);
     *root_ptr= old_root;
 
-    /*
-      We can't mark all tables in 'mysql' database as system since we don't
-      allow to lock such tables for writing with any other tables (even with
-      other system tables) and some privilege tables need this.
-    */
     if (share->db.length == 5 &&
-        !my_strcasecmp(system_charset_info, share->db.str, "mysql") &&
-        (!my_strcasecmp(system_charset_info, share->table_name.str, "proc") ||
-         !my_strcasecmp(system_charset_info, share->table_name.str, "event")))
-      share->system_table= 1;
+        !my_strcasecmp(system_charset_info, share->db.str, "mysql"))
+    {
+      /*
+        We can't mark all tables in 'mysql' database as system since we don't
+        allow to lock such tables for writing with any other tables (even with
+        other system tables) and some privilege tables need this.
+      */
+      if (!my_strcasecmp(system_charset_info, share->table_name.str, "proc")
+          || !my_strcasecmp(system_charset_info, share->table_name.str,
+                            "event"))
+        share->system_table= 1;
+      else
+      {
+        if (!my_strcasecmp(system_charset_info, share->table_name.str,
+                           "general_log"))
+          share->log_table= LOG_GENERAL;
+        else
+          if (!my_strcasecmp(system_charset_info, share->table_name.str,
+                             "slow_log"))
+            share->log_table= LOG_SLOW;
+      }
+    }
     error_given= 1;
   }
 

--- 1.124/sql/table.h	2006-01-17 10:37:29 +03:00
+++ 1.125/sql/table.h	2006-01-19 05:56:01 +03:00
@@ -198,6 +198,11 @@
     locking of this table for writing. FALSE - otherwise.
   */
   bool system_table;
+  /*
+    This flag is set for the log tables. Used during FLUSH instances to skip
+    log tables, while closing tables (since logs must be always available)
+  */
+  bool log_table;
 #ifdef WITH_PARTITION_STORAGE_ENGINE
   const uchar *partition_info;
   uint  partition_info_len;
@@ -286,6 +291,7 @@
   my_bool distinct,const_table,no_rows;
   my_bool key_read, no_keyread;
   my_bool locked_by_flush;
+  my_bool locked_by_logger;
   my_bool locked_by_name;
   my_bool fulltext_searched;
   my_bool no_cache;

--- 1.73/sql/share/errmsg.txt	2006-01-18 18:25:48 +03:00
+++ 1.74/sql/share/errmsg.txt	2006-01-19 05:56:00 +03:00
@@ -5794,3 +5794,7 @@
 ER_DROP_INDEX_FK
         eng "Cannot drop index '%-.64s': needed in a foreign key constraint"
+ER_CANT_WRITE_LOCK_LOG_TABLE
+        eng "You can't write-lock a log table. Only read access is possible."
+ER_CANT_READ_LOCK_LOG_TABLE
+        eng "You can't use usual read lock with log tables. Try READ LOCAL instead."

--- 1.1/mysql-test/include/im_check_os.inc	2005-10-01 01:14:35 +04:00
+++ 1.2/mysql-test/include/im_check_os.inc	2006-01-19 05:55:59 +03:00
@@ -3,5 +3,12 @@
 
 --source include/not_windows.inc
 
+# check that CSV engine was compiled in, as IM the test suite uses
+# logs tables-specific option and the option is not present if CSV
+# (and => the log tables) are not in.
+# NOTE: In future we should remove this check and make the test suite
+# to pass correct opyions to IM depending on the CSV presence
+--source include/have_csv.inc
+
 --connection default
 --disconnect dflt_server_con

--- 1.1/mysql-test/r/im_utils.result	2005-10-01 01:14:38 +04:00
+++ 1.2/mysql-test/r/im_utils.result	2006-01-19 05:55:59 +03:00
@@ -21,6 +21,7 @@
 skip-innodb	VALUE
 skip-bdb	VALUE
 skip-ndbcluster	VALUE
+old-log-format	VALUE
 SHOW INSTANCE OPTIONS mysqld2;
 option_name	value
 instance_name	VALUE
@@ -41,6 +42,7 @@
 skip-innodb	VALUE
 skip-bdb	VALUE
 skip-ndbcluster	VALUE
+old-log-format	VALUE
 START INSTANCE mysqld2;
 STOP INSTANCE mysqld2;
 SHOW mysqld1 LOG FILES;

--- 1.4/mysql-test/r/csv.result	2005-11-18 00:52:19 +03:00
+++ 1.5/mysql-test/r/csv.result	2006-01-19 05:55:59 +03:00
@@ -4976,6 +4976,23 @@
 4
 5
 DROP TABLE bug14672;
+CREATE TABLE test_concurrent_insert ( val integer ) ENGINE = CSV;
+LOCK TABLES test_concurrent_insert READ LOCAL;
+INSERT INTO test_concurrent_insert VALUES (1);
+SELECT * FROM test_concurrent_insert;
+val
+1
+SELECT * FROM test_concurrent_insert;
+val
+UNLOCK TABLES;
+LOCK TABLES test_concurrent_insert WRITE;
+INSERT INTO test_concurrent_insert VALUES (2);
+SELECT * FROM test_concurrent_insert;
+val
+1
+2
+UNLOCK TABLES;
+DROP TABLE test_concurrent_insert;
 create table t1 (a int) engine=csv;
 insert t1 values (1);
 delete from t1;

--- 1.5/mysql-test/t/csv.test	2005-11-18 00:52:19 +03:00
+++ 1.6/mysql-test/t/csv.test	2006-01-19 05:56:00 +03:00
@@ -2,7 +2,7 @@
 # Test for the CSV engine
 #
 
--- source include/have_csv.inc
+--source include/have_csv.inc
 
 #
 # Simple select test
@@ -1352,6 +1352,40 @@
 DROP TABLE bug14672;
 
 # End of 4.1 tests
+
+#
+# Test CONCURRENT INSERT (5.1)
+#
+
+CREATE TABLE test_concurrent_insert ( val integer ) ENGINE = CSV;
+
+connect (con1,localhost,root,,);
+connect (con2,localhost,root,,);
+
+connection con1;
+# obtain TL_READ lock on the table
+LOCK TABLES test_concurrent_insert READ LOCAL;
+
+connection con2;
+# should pass despite of the lock
+INSERT INTO test_concurrent_insert VALUES (1);
+SELECT * FROM test_concurrent_insert;
+
+connection con1;
+# first connection should not notice the changes
+SELECT * FROM test_concurrent_insert;
+
+UNLOCK TABLES;
+
+# Now check that we see our own changes
+
+LOCK TABLES test_concurrent_insert WRITE;
+INSERT INTO test_concurrent_insert VALUES (2);
+SELECT * FROM test_concurrent_insert;
+UNLOCK TABLES;
+
+# cleanup
+DROP TABLE test_concurrent_insert;
 
 #
 # BUG#13406 - incorrect amount of "records deleted"

--- 1.29/storage/csv/ha_tina.cc	2006-01-17 10:37:30 +03:00
+++ 1.30/storage/csv/ha_tina.cc	2006-01-19 05:56:01 +03:00
@@ -197,8 +197,16 @@
   char *tmp_name;
   uint length;
 
+  if (!tina_init)
+     tina_init_func();
+
   pthread_mutex_lock(&tina_mutex);
   length=(uint) strlen(table_name);
+
+  /*
+    If share is not present in the hash, create a new share and
+    initialize its members.
+  */
   if (!(share=(TINA_SHARE*) hash_search(&tina_open_tables,
                                         (byte*) table_name,
                                         length)))
@@ -214,6 +222,7 @@
     }
 
     share->use_count= 0;
+    share->is_log_table= FALSE;
     share->table_name_length= length;
     share->table_name= tmp_name;
     strmov(share->table_name, table_name);
@@ -238,6 +247,9 @@
     share->mapped_file= NULL; // We don't know the state as we just allocated it
     if (get_mmap(share, 0) > 0)
       goto error3;
+
+    /* init file length value used by readers */
+    share->saved_data_file_length= share->file_stat.st_size;
   }
   share->use_count++;
   pthread_mutex_unlock(&tina_mutex);
@@ -311,14 +323,16 @@
     These definitions are found in handler.h
     They are not probably completely right.
   */
-  current_position(0), next_position(0), chain_alloced(0),
-  chain_size(DEFAULT_CHAIN_LENGTH), records_is_known(0)
+  current_position(0), next_position(0), local_saved_data_file_length(0),
+  chain_alloced(0), chain_size(DEFAULT_CHAIN_LENGTH),
+  records_is_known(0)
 {
   /* Set our original buffers from pre-allocated memory */
   buffer.set(byte_buffer, IO_SIZE, system_charset_info);
   chain= chain_buffer;
 }
 
+
 /*
   Encode a buffer into the quoted format.
 */
@@ -427,13 +441,18 @@
 */
 int ha_tina::find_current_row(byte *buf)
 {
-  byte *mapped_ptr= (byte *)share->mapped_file + current_position;
+  byte *mapped_ptr;
   byte *end_ptr;
   DBUG_ENTER("ha_tina::find_current_row");
 
-  /* EOF should be counted as new line */
+  mapped_ptr= (byte *)share->mapped_file + current_position;
+
+  /*
+    We do not read further then local_saved_data_file_length in order
+    not to conflict with undergoing concurrent insert.
+  */
   if ((end_ptr=  find_eoln(share->mapped_file, current_position,
-                           share->file_stat.st_size)) == 0)
+                           local_saved_data_file_length)) == 0)
     DBUG_RETURN(HA_ERR_END_OF_FILE);
 
   for (Field **field=table->field ; *field ; field++)
@@ -491,6 +510,112 @@
   return ha_tina_exts;
 }
 
+/*
+  Three functions below are needed to enable concurrent insert functionality
+  for CSV engine. For more details see mysys/thr_lock.c
+*/
+
+void tina_get_status(void* param, int concurrent_insert)
+{
+  ha_tina *tina= (ha_tina*) param;
+  tina->get_status();
+}
+
+void tina_update_status(void* param)
+{
+  ha_tina *tina= (ha_tina*) param;
+  tina->update_status();
+}
+
+/* this should exist and return 0 for concurrent insert to work */
+my_bool tina_check_status(void* param)
+{
+  return 0;
+}
+
+/*
+  Save the state of the table
+
+  SYNOPSIS
+    get_status()
+
+  DESCRIPTION
+    This function is used to retrieve the file length. During the lock
+    phase of concurrent insert. For more details see comment to
+    ha_tina::update_status below.
+*/
+
+void ha_tina::get_status()
+{
+  if (share->is_log_table)
+  {
+    /*
+      We have to use mutex to follow pthreads memory visibility
+      rules for share->saved_data_file_length
+    */
+    pthread_mutex_lock(&share->mutex);
+    local_saved_data_file_length= share->saved_data_file_length;
+    pthread_mutex_unlock(&share->mutex);
+    return;
+  }
+  local_saved_data_file_length= share->saved_data_file_length;
+}
+
+
+/*
+  Correct the state of the table. Called by unlock routines
+  before the write lock is released.
+
+  SYNOPSIS
+    update_status()
+
+  DESCRIPTION
+    When we employ concurrent insert lock, we save current length of the file
+    during the lock phase. We do not read further saved value, as we don't
+    want to interfere with undergoing concurrent insert. Writers update file
+    length info during unlock with update_status().
+
+  NOTE
+    For log tables concurrent insert works different. The reason is that
+    log tables are always opened and locked. And as they do not unlock
+    tables, the file length after writes should be updated in a different
+    way. For this purpose we need is_log_table flag. When this flag is set
+    we call update_status() explicitly after each row write.
+*/
+
+void ha_tina::update_status()
+{
+  /* correct local_saved_data_file_length for writers */
+  share->saved_data_file_length= share->file_stat.st_size;
+}
+
+
+bool ha_tina::check_if_locking_is_allowed(THD *thd, TABLE *table, uint count)
+{
+  /*
+    Deny locking of the log tables, which is incompatible with
+    concurrent insert. Unless called from a logger THD:
+    general_log_thd or slow_log_thd.
+  */
+  if (table->s->log_table &&
+      thd->lex->sql_command != SQLCOM_TRUNCATE &&
+      !(thd->lex->sql_command == SQLCOM_FLUSH &&
+        thd->lex->type & REFRESH_LOG) &&
+      (thd != logger.get_general_log_thd()) &&
+      (thd != logger.get_slow_log_thd()) &&
+      (table->reginfo.lock_type >= TL_READ_NO_INSERT))
+  {
+    /*
+      The check  >= TL_READ_NO_INSERT denies all write locks
+      plus the only read lock (TL_READ_NO_INSERT itself)
+    */
+    table->reginfo.lock_type == TL_READ_NO_INSERT ?
+      my_error(ER_CANT_READ_LOCK_LOG_TABLE, MYF(0)) :
+        my_error(ER_CANT_WRITE_LOCK_LOG_TABLE, MYF(0));
+    return FALSE;
+  }
+  return TRUE;
+}
 
 /*
   Open a database file. Keep in mind that tables are caches, so
@@ -503,9 +628,19 @@
 
   if (!(share= get_share(name, table)))
     DBUG_RETURN(1);
-  thr_lock_data_init(&share->lock,&lock,NULL);
+
+  /*
+    Init locking. Pass handler object to the locking routines,
+    so that they could save/update local_saved_data_file_length value
+    during locking. This is needed to enable concurrent inserts.
+  */
+  thr_lock_data_init(&share->lock, &lock, (void*) this);
   ref_length=sizeof(off_t);
 
+  share->lock.get_status= tina_get_status;
+  share->lock.update_status= tina_update_status;
+  share->lock.check_status= tina_check_status;
+
   DBUG_RETURN(0);
 }
 
@@ -549,6 +684,18 @@
   */
   if (get_mmap(share, 0) > 0)
     DBUG_RETURN(-1);
+
+  /* update local copy of the max position to see our own changes */
+  local_saved_data_file_length= share->file_stat.st_size;
+
+  /* update status for the log tables */
+  if (share->is_log_table)
+  {
+    pthread_mutex_lock(&share->mutex);
+    update_status();
+    pthread_mutex_unlock(&share->mutex);
+  }
+
   records++;
   DBUG_RETURN(0);
 }
@@ -567,6 +714,7 @@
   int size;
   DBUG_ENTER("ha_tina::update_row");
 
+
   statistic_increment(table->in_use->status_var.ha_read_rnd_next_count,
 		      &LOCK_status);
 
@@ -580,6 +728,13 @@
 
   if (my_write(share->data_file, buffer.ptr(), size, MYF(MY_WME | MY_NABP)))
     DBUG_RETURN(-1);
+
+  /* UPDATE should never happen on the log tables */
+  DBUG_ASSERT(!share->is_log_table);
+
+  /* update local copy of the max position to see our own changes */
+  local_saved_data_file_length= share->file_stat.st_size;
+
   DBUG_RETURN(0);
 }
 
@@ -604,6 +759,9 @@
 
   --records;
 
+  /* DELETE should never happen on the log table */
+  DBUG_ASSERT(!share->is_log_table);
+
   DBUG_RETURN(0);
 }
 
@@ -811,6 +969,12 @@
 int ha_tina::extra(enum ha_extra_function operation)
 {
   DBUG_ENTER("ha_tina::extra");
+ if (operation == HA_EXTRA_MARK_AS_LOG_TABLE)
+ {
+   pthread_mutex_lock(&share->mutex);
+   share->is_log_table= TRUE;
+   pthread_mutex_unlock(&share->mutex);
+ }
   DBUG_RETURN(0);
 }
 

--- 1.10/storage/csv/ha_tina.h	2005-12-02 03:11:14 +03:00
+++ 1.11/storage/csv/ha_tina.h	2006-01-19 05:56:01 +03:00
@@ -23,9 +23,20 @@
 typedef struct st_tina_share {
   char *table_name;
   byte *mapped_file;                /* mapped region of file */
-  uint table_name_length,use_count;
+  uint table_name_length, use_count;
+  /*
+    Below flag is needed to make log tables work with concurrent insert.
+    For more details see comment to ha_tina::update_status.
+  */
+  my_bool is_log_table;
   MY_STAT file_stat;                /* Stat information for the data file */
   File data_file;                   /* Current open data file */
+  /*
+    Here we save the length of the file for readers. This is updated by
+    inserts, updates and deletes. The var is initialized along with the
+    share initialization.
+  */
+  off_t saved_data_file_length;
   pthread_mutex_t mutex;
   THR_LOCK lock;
 } TINA_SHARE;
@@ -41,6 +52,7 @@
   TINA_SHARE *share;       /* Shared lock info */
   off_t current_position;  /* Current position in the file during a file scan */
   off_t next_position;     /* Next position in the file scan */
+  off_t local_saved_data_file_length; /* save position for reads */
   byte byte_buffer[IO_SIZE];
   String buffer;
   /*
@@ -92,6 +104,7 @@
   */
   ha_rows estimate_rows_upper_bound() { return HA_POS_ERROR; }
 
+  virtual bool check_if_locking_is_allowed(THD *thd, TABLE *table, uint count);
   int open(const char *name, int mode, uint test_if_locked);
   int close(void);
   int write_row(byte * buf);
@@ -119,6 +132,13 @@
 
   THR_LOCK_DATA **store_lock(THD *thd, THR_LOCK_DATA **to,
       enum thr_lock_type lock_type);
+
+  /*
+    These functions used to get/update status of the handler.
+    Needed to enable concurrent inserts.
+  */
+  void get_status();
+  void update_status();
 
   /* The following methods were added just for TINA */
   int encode_quote(byte *buf);
--- New file ---
+++ mysql-test/r/log_tables.result	06/01/19 05:56:01
use mysql;
truncate table general_log;
select * from general_log;
event_time	user_host	thread_id	server_id	command_type	argument
TIMESTAMP	root[root] @ localhost []	1	1	Query	select * from general_log
truncate table slow_log;
select * from slow_log;
start_time	user_host	query_time	lock_time	rows_sent	rows_examined	db	last_insert_id	insert_id	server_id	sql_text
truncate table general_log;
select * from general_log where argument like '%general_log%';
event_time	user_host	thread_id	server_id	command_type	argument
TIMESTAMP	root[root] @ localhost []	1	1	Query	select * from general_log where argument like '%general_log%'
create table join_test (verbose_comment varchar (80), command_type varchar(64));
insert into join_test values ("User performed a usual SQL query", "Query");
insert into join_test values ("New DB connection was registered", "Connect");
insert into join_test values ("Get the table info", "Field List");
select verbose_comment, user_host, argument
from  mysql.general_log join join_test
on (mysql.general_log.command_type = join_test.command_type);
verbose_comment	user_host	argument
User performed a usual SQL query	root[root] @ localhost []	select * from general_log where argument like '%general_log%'
User performed a usual SQL query	root[root] @ localhost []	create table join_test (verbose_comment varchar (80), command_type varchar(64))
User performed a usual SQL query	root[root] @ localhost []	insert into join_test values ("User performed a usual SQL query", "Query")
User performed a usual SQL query	root[root] @ localhost []	insert into join_test values ("New DB connection was registered", "Connect")
User performed a usual SQL query	root[root] @ localhost []	insert into join_test values ("Get the table info", "Field List")
User performed a usual SQL query	root[root] @ localhost []	select verbose_comment, user_host, argument
from  mysql.general_log join join_test
on (mysql.general_log.command_type = join_test.command_type)
drop table join_test;
flush logs;
lock tables mysql.general_log WRITE;
ERROR HY000: You can't write-lock a log table. Only read access is possible.
lock tables mysql.slow_log WRITE;
ERROR HY000: You can't write-lock a log table. Only read access is possible.
lock tables mysql.general_log READ;
ERROR HY000: You can't use usual read lock with log tables. Try READ LOCAL instead.
lock tables mysql.slow_log READ;
ERROR HY000: You can't use usual read lock with log tables. Try READ LOCAL instead.
lock tables mysql.slow_log READ LOCAL, mysql.general_log READ LOCAL;
unlock tables;
lock tables mysql.general_log READ LOCAL;
 flush logs;
unlock tables;
select "Mark that we woke up from flush logs in the test"
       as "test passed";
test passed
Mark that we woke up from flush logs in the test
lock tables mysql.general_log READ LOCAL;
 truncate mysql.general_log;
unlock tables;
select "Mark that we woke up from TRUNCATE in the test"
       as "test passed";
test passed
Mark that we woke up from TRUNCATE in the test

--- New file ---
+++ mysql-test/t/log_tables.test	06/01/19 05:56:01
#
# Basic log tables test
#

# check that CSV engine was compiled in
--source include/have_csv.inc

use mysql;

#
# Check that log tables work and we can do basic selects. This also
# tests truncate, which works in a special mode with the log tables
#

truncate table general_log;
--replace_column 1 TIMESTAMP
select * from general_log;
truncate table slow_log;
--replace_column 1 TIMESTAMP
select * from slow_log;

#
# We want to check that a record newly written to a log table shows up for
# the query: since log tables use concurrent insert machinery and log tables
# are always locked by artificial THD, this feature requires additional
# check in ha_tina::write_row. This simple test should prove that the
# log table flag in the table handler is triggered and working.
#

truncate table general_log;
--replace_column 1 TIMESTAMP
select * from general_log where argument like '%general_log%';


#
# Check some basic queries interfering with the log tables.
# In our test we'll use a tbale with verbose comments to the short
# command type names, used in the tables
#

create table join_test (verbose_comment varchar (80), command_type varchar(64));

insert into join_test values ("User performed a usual SQL query", "Query");
insert into join_test values ("New DB connection was registered", "Connect");
insert into join_test values ("Get the table info", "Field List");

select verbose_comment, user_host, argument
  from  mysql.general_log join join_test
    on (mysql.general_log.command_type = join_test.command_type);

drop table join_test;

#
# check that flush of the log table work fine
#

flush logs;

#
# check locking of the log tables
#

--error 1532
lock tables mysql.general_log WRITE;

--error 1532
lock tables mysql.slow_log WRITE;

#
# This attemts to get TL_READ_NO_INSERT lock, which is incompatible with
# TL_WRITE_CONCURRENT_INSERT. This should fail. We issue this error as log
# tables are always opened and locked by the logger.
#

--error 1533
lock tables mysql.general_log READ;

--error 1533
lock tables mysql.slow_log READ;

#
# This call should result in TL_READ lock on the log table. This is ok and
# should pass.
#

lock tables mysql.slow_log READ LOCAL, mysql.general_log READ LOCAL;

unlock tables;

#
# check that FLUSH LOGS waits for all readers of the log table to vanish
#

connect (con1,localhost,root,,);
connect (con2,localhost,root,,);

connection con1;

lock tables mysql.general_log READ LOCAL;

connection con2;

# this should wait for log tables to unlock
send flush logs;

connection con1;

unlock tables;

# this connection should be alive by the time
connection con2;

reap;

select "Mark that we woke up from flush logs in the test"
       as "test passed";

#
# perform the same check for TRUNCATE: it should also wait for readers
# to disappear
#

connection con1;

lock tables mysql.general_log READ LOCAL;

connection con2;

# this should wait for log tables to unlock
send truncate mysql.general_log;

connection con1;

unlock tables;

# this connection should be alive by the time
connection con2;

reap;

select "Mark that we woke up from TRUNCATE in the test"
       as "test passed";

disconnect con2;
disconnect con1;



--- 1.102/mysql-test/r/information_schema.result	2006-01-13 19:44:44 +03:00
+++ 1.103/mysql-test/r/information_schema.result	2006-01-19 05:55:59 +03:00
@@ -62,6 +62,7 @@
 db
 event
 func
+general_log
 help_category
 help_keyword
 help_relation
@@ -70,6 +71,7 @@
 plugin
 proc
 procs_priv
+slow_log
 tables_priv
 time_zone
 time_zone_leap_second
@@ -732,7 +734,7 @@
 CREATE VIEW a1 (t_CRASHME) AS SELECT f1 FROM t_crashme GROUP BY f1;
 CREATE VIEW a2 AS SELECT t_CRASHME FROM a1;
 count(*)
-107
+109
 drop view a2, a1;
 drop table t_crashme;
 select table_schema,table_name, column_name from
@@ -816,7 +818,7 @@
 table_schema	count(*)
 cluster_replication	1
 information_schema	19
-mysql	19
+mysql	21
 create table t1 (i int, j int);
 create trigger trg1 before insert on t1 for each row
 begin

--- 1.67/mysql-test/t/information_schema.test	2006-01-05 00:38:46 +03:00
+++ 1.68/mysql-test/t/information_schema.test	2006-01-19 05:56:00 +03:00
@@ -1,6 +1,10 @@
 # This test  uses grants, which can't get tested for embedded server
 -- source include/not_embedded.inc
 
+# check that CSV engine was compiled in, as the result of the test
+# depends on the presence of the log tables (which are CSV-based).
+--source include/have_csv.inc
+
 # Test for information_schema.schemata &
 # show databases
 

--- 1.4/mysql-test/r/mysqlcheck.result	2006-01-12 21:50:30 +03:00
+++ 1.5/mysql-test/r/mysqlcheck.result	2006-01-19 05:55:59 +03:00
@@ -3,6 +3,8 @@
 mysql.db                                           OK
 mysql.event                                        OK
 mysql.func                                         OK
+mysql.general_log
+note     : The storage engine for the table doesn't support optimize
 mysql.help_category                                OK
 mysql.help_keyword                                 OK
 mysql.help_relation                                OK
@@ -11,6 +13,8 @@
 mysql.plugin                                       OK
 mysql.proc                                         OK
 mysql.procs_priv                                   OK
+mysql.slow_log
+note     : The storage engine for the table doesn't support optimize
 mysql.tables_priv                                  OK
 mysql.time_zone                                    OK
 mysql.time_zone_leap_second                        OK
@@ -22,6 +26,8 @@
 mysql.db                                           OK
 mysql.event                                        OK
 mysql.func                                         OK
+mysql.general_log
+note     : The storage engine for the table doesn't support optimize
 mysql.help_category                                OK
 mysql.help_keyword                                 OK
 mysql.help_relation                                OK
@@ -30,6 +36,8 @@
 mysql.plugin                                       OK
 mysql.proc                                         OK
 mysql.procs_priv                                   OK
+mysql.slow_log
+note     : The storage engine for the table doesn't support optimize
 mysql.tables_priv                                  OK
 mysql.time_zone                                    OK
 mysql.time_zone_leap_second                        OK

--- 1.1/mysql-test/t/mysqlcheck.test	2005-10-18 13:24:54 +04:00
+++ 1.2/mysql-test/t/mysqlcheck.test	2006-01-19 05:56:00 +03:00
@@ -1,6 +1,10 @@
 # Embedded server doesn't support external clients
 --source include/not_embedded.inc
 
+# check that CSV engine was compiled in, as the result of the test
+# depends on the presence of the log tables (which are CSV-based).
+--source include/have_csv.inc
+
 #
 # Bug #13783  mysqlcheck tries to optimize and analyze information_schema
 #

--- 1.19/mysql-test/lib/init_db.sql	2006-01-12 21:50:29 +03:00
+++ 1.20/mysql-test/lib/init_db.sql	2006-01-19 05:55:59 +03:00
@@ -570,6 +570,10 @@
 ) character set utf8 comment='Stored Procedures';
 
 
+CREATE PROCEDURE create_log_tables() BEGIN DECLARE is_csv_enabled int DEFAULT 0; SELECT @@have_csv = 'YES' INTO is_csv_enabled; IF (is_csv_enabled) THEN CREATE TABLE general_log (event_time TIMESTAMP NOT NULL, user_host MEDIUMTEXT, thread_id INTEGER, server_id INTEGER, command_type VARCHAR(64), argument MEDIUMTEXT) engine=CSV CHARACTER SET utf8 comment='General log'; CREATE TABLE slow_log (start_time TIMESTAMP NOT NULL, user_host MEDIUMTEXT NOT NULL, query_time TIME NOT NULL, lock_time TIME NOT NULL, rows_sent INTEGER NOT NULL, rows_examined INTEGER NOT NULL, db VARCHAR(512), last_insert_id INTEGER, insert_id INTEGER, server_id INTEGER, sql_text MEDIUMTEXT NOT NULL) engine=CSV CHARACTER SET utf8 comment='Slow log'; END IF; END;
+CALL create_log_tables();
+DROP PROCEDURE create_log_tables;
+
 CREATE TABLE event (
   db char(64) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL default '',
   name char(64) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL default '',

--- 1.53/mysql-test/mysql-test-run.pl	2006-01-17 12:48:38 +03:00
+++ 1.54/mysql-test/mysql-test-run.pl	2006-01-19 05:55:59 +03:00
@@ -909,6 +909,7 @@
    path_datadir => "$opt_vardir/im_mysqld_1.data",
    path_sock    => "$sockdir/mysqld_1.sock",
    path_pid     => "$opt_vardir/run/mysqld_1.pid",
+   old_log_format => 1
   };
 
   $instance_manager->{'instances'}->[1]=
@@ -919,6 +920,7 @@
    path_sock    => "$sockdir/mysqld_2.sock",
    path_pid     => "$opt_vardir/run/mysqld_2.pid",
    nonguarded   => 1,
+   old_log_format => 1
   };
 
   if ( $opt_extern )
@@ -1825,6 +1827,7 @@
 ;
 
     print OUT "nonguarded\n" if $instance->{'nonguarded'};
+    print OUT "old-log-format\n" if $instance->{'old_log_format'};
     print OUT "\n";
   }
 

--- 1.1/sql/log.h	2005-12-22 08:34:46 +03:00
+++ 1.2/sql/log.h	2006-01-19 05:56:00 +03:00
@@ -132,6 +132,21 @@
   ~st_log_info() { pthread_mutex_destroy(&lock);}
 } LOG_INFO;
 
+/*
+  Currently we have only 3 kinds of logging functions: old-fashioned
+  logs, stdout and csv logging routines.
+*/
+#define MAX_LOG_HANDLERS_NUM 3
+
+enum enum_printer
+{
+  NONE,
+  LEGACY,
+  CSV,
+  LEGACY_AND_CSV
+};
+
+
 class Log_event;
 class Rows_log_event;
 
@@ -276,10 +291,18 @@
   bool open_index_file(const char *index_file_name_arg,
                        const char *log_name);
   void new_file(bool need_lock);
-  bool write(THD *thd, enum enum_server_command command,
-	     const char *format,...);
-  bool write(THD *thd, const char *query, uint query_length,
-	     time_t query_start=0);
+  /* log a command to the old-fashioned general log */
+  bool write(time_t event_time, const char *user_host,
+             uint user_host_len, int thread_id,
+             const char *command_type, uint command_type_len,
+             const char *sql_text, uint sql_text_len);
+
+  /* log a query to the old-fashioned slow query log */
+  bool write(THD *thd, time_t current_time, time_t query_start_arg,
+             const char *user_host, uint user_host_len,
+             longlong query_time, longlong lock_time, bool is_command,
+             const char *sql_text, uint sql_text_len);
+
   bool write(Log_event* event_info); // binary log write
   bool write(THD *thd, IO_CACHE *cache, Log_event *commit_event);
 
@@ -328,5 +351,152 @@
   inline IO_CACHE *get_index_file() { return &index_file;}
   inline uint32 get_open_count() { return open_count; }
 };
+
+class Log_event_handler
+{
+public:
+  virtual bool init()= 0;
+  virtual void cleanup()= 0;
+
+  virtual bool log_slow(THD *thd, time_t current_time,
+                        time_t query_start_arg, const char *user_host,
+                        uint user_host_len, longlong query_time,
+                        longlong lock_time, bool is_command,
+                        const char *sql_text, uint sql_text_len)= 0;
+  virtual bool log_error(enum loglevel level, const char *format,
+                         va_list args)= 0;
+  virtual bool log_general(time_t event_time, const char *user_host,
+                           uint user_host_len, int thread_id,
+                           const char *command_type, uint command_type_len,
+                           const char *sql_text, uint sql_text_len)= 0;
+  virtual ~Log_event_handler() {}
+};
+
+
+class Log_to_csv_event_handler: public Log_event_handler
+{
+  /*
+    We create artificial THD for each of the logs. This is to avoid
+    locking issues: we don't want locks on the log tables reside in the
+    THD's of the query. The reason is the locking order and duration.
+  */
+  THD *general_log_thd, *slow_log_thd;
+  friend class LOGGER;
+  TABLE_LIST general_log, slow_log;
+
+private:
+  bool open_log_table(uint log_type);
+
+public:
+  Log_to_csv_event_handler();
+  ~Log_to_csv_event_handler();
+  virtual bool init();
+  virtual void cleanup();
+
+  virtual bool log_slow(THD *thd, time_t current_time,
+                        time_t query_start_arg, const char *user_host,
+                        uint user_host_len, longlong query_time,
+                        longlong lock_time, bool is_command,
+                        const char *sql_text, uint sql_text_len);
+  virtual bool log_error(enum loglevel level, const char *format,
+                         va_list args);
+  virtual bool log_general(time_t event_time, const char *user_host,
+                           uint user_host_len, int thread_id,
+                           const char *command_type, uint command_type_len,
+                           const char *sql_text, uint sql_text_len);
+  bool flush(THD *thd, TABLE_LIST *close_slow_Log,
+             TABLE_LIST* close_general_log);
+  void close_log_table(uint log_type, bool lock_in_use);
+  bool reopen_log_table(uint log_type);
+};
+
+
+class Log_to_file_event_handler: public Log_event_handler
+{
+  MYSQL_LOG mysql_log, mysql_slow_log;
+  bool is_initialized;
+public:
+  Log_to_file_event_handler(): is_initialized(FALSE)
+  {}
+  virtual bool init();
+  virtual void cleanup();
+
+  virtual bool log_slow(THD *thd, time_t current_time,
+                        time_t query_start_arg, const char *user_host,
+                        uint user_host_len, longlong query_time,
+                        longlong lock_time, bool is_command,
+                        const char *sql_text, uint sql_text_len);
+  virtual bool log_error(enum loglevel level, const char *format,
+                         va_list args);
+  virtual bool log_general(time_t event_time, const char *user_host,
+                           uint user_host_len, int thread_id,
+                           const char *command_type, uint command_type_len,
+                           const char *sql_text, uint sql_text_len);
+  void flush();
+  void init_pthread_objects();
+};
+
+
+/* Class which manages slow, general and error log event handlers */
+class LOGGER
+{
+  pthread_mutex_t LOCK_logger;
+  /* flag to check whether logger mutex is initialized */
+  uint inited;
+
+  /* available log handlers */
+  Log_to_csv_event_handler *table_log_handler;
+  Log_to_file_event_handler *file_log_handler;
+
+  /* NULL-terminated arrays of log handlers */
+  Log_event_handler *error_log_handler_list[MAX_LOG_HANDLERS_NUM + 1];
+  Log_event_handler *slow_log_handler_list[MAX_LOG_HANDLERS_NUM + 1];
+  Log_event_handler *general_log_handler_list[MAX_LOG_HANDLERS_NUM + 1];
+
+public:
+
+  bool is_log_tables_initialized;
+
+  LOGGER() : inited(0), table_log_handler(NULL),
+             file_log_handler(NULL), is_log_tables_initialized(FALSE)
+  {}
+  void lock() { (void) pthread_mutex_lock(&LOCK_logger); }
+  void unlock() { (void) pthread_mutex_unlock(&LOCK_logger); }
+  /*
+    We want to initialize all log mutexes as soon as possible,
+    but we cannot do it in constructor, as safe_mutex relies on
+    initialization, performed by MY_INIT(). This why this is done in
+    this function.
+  */
+  void init_base();
+  void init_log_tables();
+  bool flush_logs(THD *thd);
+  THD *get_general_log_thd()
+  {
+    return (THD *) table_log_handler->general_log_thd;
+  }
+  THD *get_slow_log_thd()
+  {
+    return (THD *) table_log_handler->slow_log_thd;
+  }
+  void cleanup();
+  bool error_log_print(enum loglevel level, const char *format,
+                      va_list args);
+  bool slow_log_print(THD *thd, const char *query, uint query_length,
+                      time_t query_start_arg);
+  bool general_log_print(THD *thd,enum enum_server_command command,
+                         const char *format, va_list args);
+
+  void close_log_table(uint log_type, bool lock_in_use);
+  bool reopen_log_table(uint log_type);
+
+  /* we use this function to setup all enabled log event handlers */
+  int set_handlers(enum enum_printer error_log_printer,
+                   enum enum_printer slow_log_printer,
+                   enum enum_printer general_log_printer);
+  void init_error_log(enum enum_printer error_log_printer);
+  void init_slow_log(enum enum_printer slow_log_printer);
+  void init_general_log(enum enum_printer general_log_printer);
+ };
 
 #endif /* LOG_H */

--- 1.88/mysql-test/r/show_check.result	2006-01-12 21:50:30 +03:00
+++ 1.89/mysql-test/r/show_check.result	2006-01-19 05:55:59 +03:00
@@ -147,10 +147,14 @@
 flush tables;
 show open tables;
 Database	Table	In_use	Name_locked
+mysql	general_log	1	0
+mysql	slow_log	1	0
 create table t1(n int);
 insert into t1 values (1);
 show open tables;
 Database	Table	In_use	Name_locked
+mysql	general_log	1	0
+mysql	slow_log	1	0
 test	t1	0	0
 drop table t1;
 create table t1 (a int not null, b VARCHAR(10), INDEX (b) ) AVG_ROW_LENGTH=10 CHECKSUM=1 COMMENT="test" ENGINE=MYISAM MIN_ROWS=10 MAX_ROWS=100 PACK_KEYS=1 DELAY_KEY_WRITE=1 ROW_FORMAT=fixed;
@@ -564,20 +568,24 @@
 1
 SHOW OPEN TABLES;
 Database	Table	In_use	Name_locked
-mysql	db	0	0
+mysql	proc	0	0
 test	urkunde	0	0
 mysql	time_zone	0	0
-mysql	user	0	0
+mysql	db	0	0
 test	txt1	0	0
-mysql	proc	0	0
+mysql	slow_log	1	0
 test	tyt2	0	0
+mysql	general_log	1	0
+mysql	user	0	0
 mysql	time_zone_name	0	0
 SHOW OPEN TABLES FROM mysql;
 Database	Table	In_use	Name_locked
-mysql	db	0	0
+mysql	proc	0	0
 mysql	time_zone	0	0
+mysql	db	0	0
+mysql	slow_log	1	0
+mysql	general_log	1	0
 mysql	user	0	0
-mysql	proc	0	0
 mysql	time_zone_name	0	0
 SHOW OPEN TABLES FROM mysql LIKE 'u%';
 Database	Table	In_use	Name_locked
@@ -590,12 +598,16 @@
 mysql	time_zone_name	0	0
 SHOW OPEN TABLES LIKE '%o%';
 Database	Table	In_use	Name_locked
-mysql	time_zone	0	0
 mysql	proc	0	0
+mysql	time_zone	0	0
+mysql	slow_log	1	0
+mysql	general_log	1	0
 mysql	time_zone_name	0	0
 FLUSH TABLES;
 SHOW OPEN TABLES;
 Database	Table	In_use	Name_locked
+mysql	general_log	1	0
+mysql	slow_log	1	0
 DROP TABLE txt1;
 DROP TABLE tyt2;
 DROP TABLE urkunde;

--- 1.60/mysql-test/t/show_check.test	2005-12-31 07:49:53 +03:00
+++ 1.61/mysql-test/t/show_check.test	2006-01-19 05:56:00 +03:00
@@ -2,6 +2,10 @@
 # embedded server testing
 -- source include/not_embedded.inc
 
+# check that CSV engine was compiled in, as the result of the test
+# depends on the presence of the log tables (which are CSV-based).
+--source include/have_csv.inc
+
 #
 # Test of some show commands
 #

--- 1.32/scripts/mysql_fix_privilege_tables.sql	2006-01-11 20:08:53 +03:00
+++ 1.33/scripts/mysql_fix_privilege_tables.sql	2006-01-19 05:56:00 +03:00
@@ -527,6 +527,42 @@
                   MODIFY comment
                          char(64) collate utf8_bin DEFAULT '' NOT NULL;
 
+--
+-- Create missing log tables (5.1)
+--
+
+delimiter //
+CREATE PROCEDURE create_log_tables()
+BEGIN
+  DECLARE is_csv_enabled int DEFAULT 0;
+  SELECT @@have_csv = 'YES' INTO is_csv_enabled;
+  IF (is_csv_enabled) THEN
+    CREATE TABLE IF NOT EXISTS general_log (
+      event_time TIMESTAMP NOT NULL,
+      user_host    MEDIUMTEXT,
+      thread_id INTEGER,
+      server_id INTEGER,
+      command_type VARCHAR(64),
+      argument     MEDIUMTEXT
+    ) engine=CSV CHARACTER SET utf8 comment='General log';
+    CREATE TABLE IF NOT EXISTS slow_log (
+      start_time TIMESTAMP NOT NULL,
+      user_host MEDIUMTEXT NOT NULL,
+      query_time   TIME NOT NULL,
+      lock_time    TIME NOT NULL,
+      rows_sent    INTEGER NOT NULL,
+      rows_examined INTEGER NOT NULL,
+      db VARCHAR(512),
+      last_insert_id INTEGER,
+      insert_id INTEGER,
+      server_id INTEGER,
+      sql_text MEDIUMTEXT NOT NULL
+    ) engine=CSV CHARACTER SET utf8 comment='Slow log';
+  END IF;
+END//
+delimiter ;
+CALL create_log_tables();
+DROP PROCEDURE create_log_tables;
 #
 # EVENT table
 #

--- 1.22/mysql-test/r/connect.result	2006-01-10 21:16:41 +03:00
+++ 1.23/mysql-test/r/connect.result	2006-01-19 05:55:59 +03:00
@@ -5,6 +5,7 @@
 db
 event
 func
+general_log
 help_category
 help_keyword
 help_relation
@@ -13,6 +14,7 @@
 plugin
 proc
 procs_priv
+slow_log
 tables_priv
 time_zone
 time_zone_leap_second
@@ -34,6 +36,7 @@
 db
 event
 func
+general_log
 help_category
 help_keyword
 help_relation
@@ -42,6 +45,7 @@
 plugin
 proc
 procs_priv
+slow_log
 tables_priv
 time_zone
 time_zone_leap_second
@@ -71,6 +75,7 @@
 db
 event
 func
+general_log
 help_category
 help_keyword
 help_relation
@@ -79,6 +84,7 @@
 plugin
 proc
 procs_priv
+slow_log
 tables_priv
 time_zone
 time_zone_leap_second

--- 1.19/mysql-test/t/connect.test	2005-11-08 00:30:38 +03:00
+++ 1.20/mysql-test/t/connect.test	2006-01-19 05:55:59 +03:00
@@ -5,6 +5,10 @@
 # This test makes no sense with the embedded server
 --source include/not_embedded.inc
 
+# check that CSV engine was compiled in, as the test relies on the presence
+# of the log tables (which are CSV-based). By connect mysql; show tables;
+--source include/have_csv.inc
+
 --disable_warnings
 drop table if exists t1,t2;
 --enable_warnings

--- 1.157/sql/sql_prepare.cc	2005-12-12 22:55:13 +03:00
+++ 1.158/sql/sql_prepare.cc	2006-01-19 05:56:01 +03:00
@@ -1866,7 +1866,7 @@
     thd->stmt_map.erase(stmt);
   }
   else
-    mysql_log.write(thd, COM_STMT_PREPARE, "[%lu] %s", stmt->id, packet);
+    general_log_print(thd, COM_STMT_PREPARE, "[%lu] %s", stmt->id, packet);
 
   /* check_prepared_statemnt sends the metadata packet in case of success */
   DBUG_VOID_RETURN;
@@ -2228,7 +2228,7 @@
   if (!(specialflag & SPECIAL_NO_PRIOR))
     my_pthread_setprio(pthread_self(), WAIT_PRIOR);
   if (error == 0)
-    mysql_log.write(thd, COM_STMT_EXECUTE, "[%lu] %s", stmt->id, thd->query);
+    general_log_print(thd, COM_STMT_EXECUTE, "[%lu] %s", stmt->id, thd->query);
 
   DBUG_VOID_RETURN;
 
@@ -2607,7 +2607,7 @@
 {
   /* Setup binary logging */
   if (mysql_bin_log.is_open() && is_update_query(lex->sql_command) ||
-      mysql_log.is_open() || mysql_slow_log.is_open())
+      opt_log || opt_slow_log)
   {
     set_params_from_vars= insert_params_from_vars_with_log;
 #ifndef EMBEDDED_LIBRARY

--- 1.6/mysql-test/include/system_db_struct.inc	2006-01-11 20:06:25 +03:00
+++ 1.7/mysql-test/include/system_db_struct.inc	2006-01-19 05:55:59 +03:00
@@ -13,3 +13,5 @@
 show create table procs_priv;
 show create table proc;
 show create table event;
+show create table general_log;
+show create table slow_log;

--- 1.32/mysql-test/r/system_mysql_db.result	2006-01-11 20:06:26 +03:00
+++ 1.33/mysql-test/r/system_mysql_db.result	2006-01-19 05:55:59 +03:00
@@ -5,6 +5,7 @@
 db
 event
 func
+general_log
 help_category
 help_keyword
 help_relation
@@ -13,6 +14,7 @@
 plugin
 proc
 procs_priv
+slow_log
 tables_priv
 time_zone
 time_zone_leap_second
@@ -184,6 +186,31 @@
   `comment` char(64) character set utf8 collate utf8_bin NOT NULL default '',
   PRIMARY KEY  (`db`,`name`,`type`)
 ) ENGINE=MyISAM DEFAULT CHARSET=utf8 COMMENT='Stored Procedures'
+show create table general_log;
+Table	Create Table
+general_log	CREATE TABLE `general_log` (
+  `event_time` timestamp NOT NULL default CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP,
+  `user_host` mediumtext,
+  `thread_id` int(11) default NULL,
+  `server_id` int(11) default NULL,
+  `command_type` varchar(64) default NULL,
+  `argument` mediumtext
+) ENGINE=CSV DEFAULT CHARSET=utf8 COMMENT='General log'
+show create table slow_log;
+Table	Create Table
+slow_log	CREATE TABLE `slow_log` (
+  `start_time` timestamp NOT NULL default CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP,
+  `user_host` mediumtext NOT NULL,
+  `query_time` time NOT NULL,
+  `lock_time` time NOT NULL,
+  `rows_sent` int(11) NOT NULL,
+  `rows_examined` int(11) NOT NULL,
+  `db` varchar(512) default NULL,
+  `last_insert_id` int(11) default NULL,
+  `insert_id` int(11) default NULL,
+  `server_id` int(11) default NULL,
+  `sql_text` mediumtext NOT NULL
+) ENGINE=CSV DEFAULT CHARSET=utf8 COMMENT='Slow log'
 show create table event;
 Table	Create Table
 event	CREATE TABLE `event` (

--- 1.8/mysql-test/t/system_mysql_db.test	2005-09-15 23:17:37 +04:00
+++ 1.9/mysql-test/t/system_mysql_db.test	2006-01-19 05:56:00 +03:00
@@ -2,6 +2,10 @@
 # This test must examine integrity of system database "mysql"
 #
 
+# check that CSV engine was compiled in, as the result of the test
+# depends on the presence of the log tables (which are CSV-based).
+--source include/have_csv.inc
+
 # First delete some tables maybe left over from previous tests
 --disable_warnings
 drop table if exists t1,t1aa,t2aa;

--- 1.20/mysql-test/t/system_mysql_db_fix.test	2006-01-11 20:08:53 +03:00
+++ 1.21/mysql-test/t/system_mysql_db_fix.test	2006-01-19 05:56:00 +03:00
@@ -1,6 +1,10 @@
 # Embedded server doesn't support external clients
 --source include/not_embedded.inc
 
+# check that CSV engine was compiled in, as the test relies on the presence
+# of the log tables (which are CSV-based)
+--source include/have_csv.inc
+
 #
 # This is the test for mysql_fix_privilege_tables
 #
@@ -85,7 +89,10 @@
 
 -- disable_query_log
 
-DROP TABLE db, host, user, func, plugin, tables_priv, columns_priv, procs_priv, help_category, help_keyword, help_relation, help_topic, proc, time_zone, time_zone_leap_second, time_zone_name, time_zone_transition, time_zone_transition_type, event;
+DROP TABLE db, host, user, func, plugin, tables_priv, columns_priv,
+procs_priv, help_category, help_keyword, help_relation, help_topic, proc,
+time_zone, time_zone_leap_second, time_zone_name, time_zone_transition,
+time_zone_transition_type, general_log, slow_log, event;
 
 -- enable_query_log
 

--- 1.30/scripts/mysql_create_system_tables.sh	2006-01-12 21:50:30 +03:00
+++ 1.31/scripts/mysql_create_system_tables.sh	2006-01-19 05:56:00 +03:00
@@ -42,6 +42,7 @@
 c_tzn="" c_tz="" c_tzt="" c_tztt="" c_tzls="" c_pl=""
 i_tzn="" i_tz="" i_tzt="" i_tztt="" i_tzls="" i_pl=""
 c_p="" c_pp=""
+c_gl="" c_sl=""
 
 # Check for old tables
 if test ! -f $mdata/db.frm
@@ -354,6 +355,7 @@
   c_hr="$c_hr   comment='keyword-topic relation';"
 fi
 
+
 if test ! -f $mdata/time_zone_name.frm
 then
   if test "$1" = "verbose" ; then
@@ -744,6 +746,27 @@
 fi
 
 
+if test ! -f $mdata/general_log.frm
+then
+  if test "$1" = "verbose" ; then
+   echo "Preparing general_log table" 1>&2;
+  fi
+    c_gl="$c_gl CREATE PROCEDURE create_general_log_table() BEGIN DECLARE is_csv_enabled int DEFAULT 0; SELECT @@have_csv = 'YES' INTO is_csv_enabled; IF (is_csv_enabled) THEN CREATE TABLE general_log (event_time TIMESTAMP NOT NULL, user_host MEDIUMTEXT, thread_id INTEGER, server_id INTEGER, command_type VARCHAR(64), argument MEDIUMTEXT) engine=CSV CHARACTER SET utf8 comment='General log'; END IF; END;
+CALL create_general_log_table();
+DROP PROCEDURE create_general_log_table;"
+fi
+
+
+if test ! -f $mdata/slow_log.frm
+then
+  if test "$1" = "verbose" ; then
+   echo "Preparing slow_log table" 1>&2;
+  fi
+    c_sl="$c_sl CREATE PROCEDURE create_slow_log_table() BEGIN DECLARE is_csv_enabled int DEFAULT 0; SELECT @@have_csv = 'YES' INTO is_csv_enabled; IF (is_csv_enabled) THEN CREATE TABLE slow_log (start_time TIMESTAMP NOT NULL, user_host MEDIUMTEXT NOT NULL, query_time TIME NOT NULL, lock_time TIME NOT NULL, rows_sent INTEGER NOT NULL, rows_examined INTEGER NOT NULL, db VARCHAR(512), last_insert_id INTEGER, insert_id INTEGER, server_id INTEGER, sql_text MEDIUMTEXT NOT NULL) engine=CSV CHARACTER SET utf8 comment='Slow log'; END IF; END;
+CALL create_slow_log_table();
+DROP PROCEDURE create_slow_log_table;"
+fi
+
 if test ! -f $mdata/event.frm
 then
   c_ev="$c_ev CREATE TABLE event ("
@@ -812,6 +835,8 @@
 $c_p
 $c_pp
 
+$c_gl
+$c_sl
 $c_ev
 CREATE DATABASE IF NOT EXISTS cluster_replication;
 CREATE TABLE IF NOT EXISTS cluster_replication.binlog_index (Position BIGINT UNSIGNED NOT NULL, File VARCHAR(255) NOT NULL, epoch BIGINT UNSIGNED NOT NULL, inserts BIGINT UNSIGNED NOT NULL, updates BIGINT UNSIGNED NOT NULL, deletes BIGINT UNSIGNED NOT NULL, schemaops BIGINT UNSIGNED NOT NULL, PRIMARY KEY(epoch)) ENGINE=MYISAM;
Thread
bk commit into 5.1 tree (cps:1.2075)Petr Chardin19 Jan