From 39636f48f088d3c92a7493c5aa046be11a48c6a0 Mon Sep 17 00:00:00 2001
From: "msvensson@neptunus.(none)[msvensson]" <>
Date: Tue, 21 Jun 2005 14:19:56 +0200
Subject: [PATCH] patch

---
 client/mysqldump.c            | 156 +++++++++++++++++++++-------------
 client/mysqltest.c            |  35 +++++++-
 mysql-test/mysql-test-run.sh  |   3 +
 mysql-test/r/mysqldump.result |  27 ++++++
 mysql-test/t/mysqldump.test   |  78 +++++++++++++++++
 5 files changed, 238 insertions(+), 61 deletions(-)

diff --git a/client/mysqldump.c b/client/mysqldump.c
index 7b18b1d92d..207235983d 100644
--- a/client/mysqldump.c
+++ b/client/mysqldump.c
@@ -57,6 +57,7 @@
 #define EX_CONSCHECK 3
 #define EX_EOM 4
 #define EX_EOF 5 /* ferror for output file was got */
+#define EX_ILLEGAL_TABLE 6
 
 /* index into 'show fields from table' */
 
@@ -140,14 +141,6 @@ const char *compatible_mode_names[]=
 TYPELIB compatible_mode_typelib= {array_elements(compatible_mode_names) - 1,
 				  "", compatible_mode_names, NULL};
 
-#define TABLE_RULE_HASH_SIZE   16
-
-typedef struct st_table_rule_ent
-{
-  char* key;    /* dbname.tablename */
-  uint key_len;
-} TABLE_RULE_ENT;
-
 HASH ignore_table;
 
 static struct my_option my_long_options[] =
@@ -538,29 +531,21 @@ static void write_footer(FILE *sql_file)
 } /* write_footer */
 
 
-static void free_table_ent(TABLE_RULE_ENT* e)
-{
-  my_free((gptr) e, MYF(0));
-}
-
-
-static byte* get_table_key(TABLE_RULE_ENT* e, uint* len,
-			   my_bool not_used __attribute__((unused)))
+byte* get_table_key(const char *entry, uint *length,
+				my_bool not_used __attribute__((unused)))
 {
-  *len= e->key_len;
-  return (byte*)e->key;
+  *length= strlen(entry);
+  return (byte*) entry;
 }
 
 
 void init_table_rule_hash(HASH* h)
 {
-  if(hash_init(h, charset_info, TABLE_RULE_HASH_SIZE, 0, 0,
-	       (hash_get_key) get_table_key,
-	       (hash_free_key) free_table_ent, 0))
+  if(hash_init(h, charset_info, 16, 0, 0,
+	       (hash_get_key) get_table_key, 0, 0))
     exit(EX_EOM);
 }
 
-
 static my_bool
 get_one_option(int optid, const struct my_option *opt __attribute__((unused)),
 	       char *argument)
@@ -633,25 +618,15 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)),
     break;
   case (int) OPT_IGNORE_TABLE:
   {
-    uint len= (uint)strlen(argument);
-    TABLE_RULE_ENT* e;
     if (!strchr(argument, '.'))
     {
       fprintf(stderr, "Illegal use of option --ignore-table=<database>.<table>\n");
       exit(1);
     }
-    /* len is always > 0 because we know the there exists a '.' */
-    e= (TABLE_RULE_ENT*)my_malloc(sizeof(TABLE_RULE_ENT) + len, MYF(MY_WME));
-    if (!e)
-      exit(EX_EOM);
-    e->key= (char*)e + sizeof(TABLE_RULE_ENT);
-    e->key_len= len;
-    memcpy(e->key, argument, len);
-
     if (!hash_inited(&ignore_table))
       init_table_rule_hash(&ignore_table);
 
-    if(my_hash_insert(&ignore_table, (byte*)e))
+    if (my_hash_insert(&ignore_table, (byte*)my_strdup(argument, MYF(0))))
       exit(EX_EOM);
     break;
   }
@@ -955,7 +930,28 @@ static char *quote_name(const char *name, char *buff, my_bool force)
   return buff;
 } /* quote_name */
 
+/*
+  Quote a table name so it can be used in "SHOW TABLES LIKE <tabname>"
 
+  SYNOPSIS
+    quote_for_like
+    name     - name of the table
+    buff     - quoted name of the table
+
+  DESCRIPTION
+    Quote \, _, ' and % characters
+
+    Note: Because MySQL uses the C escape syntax in strings
+    (for example, '\n' to represent newline), you must double
+    any '\' that you use in your LIKE  strings. For example, to
+    search for '\n', specify it as '\\n'. To search for '\', specify
+    it as '\\\\' (the backslashes are stripped once by the parser
+    and another time when the pattern match is done, leaving a
+    single backslash to be matched).
+
+    Example: "t\1" => "t\\\\1"
+
+*/
 
 static char *quote_for_like(const char *name, char *buff)
 {
@@ -963,7 +959,13 @@ static char *quote_for_like(const char *name, char *buff)
   *to++= '\'';
   while (*name)
   {
-    if (*name == '\'' || *name == '_' || *name == '\\' || *name == '%')
+    if (*name == '\\')
+    {
+      *to++='\\';
+      *to++='\\';
+      *to++='\\';
+    }
+    else if (*name == '\'' || *name == '_'  || *name == '%')
       *to++= '\\';
     *to++= *name++;
   }
@@ -1114,6 +1116,7 @@ static uint getTableStructure(char *table, char* db)
   FILE       *sql_file = md_result_file;
   int        len;
   DBUG_ENTER("getTableStructure");
+  DBUG_PRINT("enter", ("db: %s, table: %s", db, table));
 
   if (!insert_pat_inited)
   {
@@ -2165,6 +2168,7 @@ static int get_actual_table_name(const char *old_table_name,
   char query[50 + 2*NAME_LEN];
   char show_name_buff[FN_REFLEN];
   DBUG_ENTER("get_actual_table_name");
+  DBUG_PRINT("enter", ("old_table_name: %s", old_table_name));
 
   /* Check memory for quote_for_like() */
   DBUG_ASSERT(2*sizeof(old_table_name) < sizeof(show_name_buff));
@@ -2186,36 +2190,72 @@ static int get_actual_table_name(const char *old_table_name,
 	  	row= mysql_fetch_row( tableRes );
 	  	strmake(new_table_name, row[0], buf_size-1);
 		retval = 0;
+                DBUG_PRINT("info", ("new_table_name: %s", new_table_name));
 	}
   	mysql_free_result(tableRes);
   }
-  return retval;
+    DBUG_PRINT("exit", ("retval: %d", retval));
+  DBUG_RETURN(retval);
 }
 
 
 static int dump_selected_tables(char *db, char **table_names, int tables)
 {
-  uint numrows;
+  uint numrows, i;
   char table_buff[NAME_LEN*+3];
+  char new_table_name[NAME_LEN];
+  DYNAMIC_STRING lock_tables_query;
+  HASH dump_tables;
+
+  DBUG_ENTER("dump_selected_tables");
 
   if (init_dumping(db))
     return 1;
-  if (lock_tables)
+
+  /* Init hash table for storing the actual name of tables to dump */
+  if (hash_init(&dump_tables, charset_info, 16, 0, 0,
+                (hash_get_key) get_table_key, 0, 0))
+    exit(EX_EOM);
+
+  init_dynamic_string(&lock_tables_query, "LOCK TABLES ", 256, 1024);
+  for (; tables > 0 ; tables-- , table_names++)
   {
-    DYNAMIC_STRING query;
-    int i;
 
-    init_dynamic_string(&query, "LOCK TABLES ", 256, 1024);
-    for (i=0 ; i < tables ; i++)
+    /* the table name passed on commandline may be wrong case */
+    if (!get_actual_table_name( *table_names,
+                                new_table_name, sizeof(new_table_name) ))
     {
-      dynstr_append(&query, quote_name(table_names[i], table_buff, 1));
-      dynstr_append(&query, " READ /*!32311 LOCAL */,");
+      /* Add found table name to lock_tables_query */
+      if (lock_tables)
+      {
+        dynstr_append(&lock_tables_query,
+                      quote_name(new_table_name, table_buff, 1));
+        dynstr_append(&lock_tables_query, " READ /*!32311 LOCAL */,");
+      }
+
+      /* Add found table name to dump_tables list */
+      if (my_hash_insert(&dump_tables,
+                         (byte*)my_strdup(new_table_name, MYF(0))))
+        exit(EX_EOM);
+
     }
-    if (mysql_real_query(sock, query.str, query.length-1))
+    else
+    {
+       my_printf_error(0,"Couldn't find table: \"%s\"\n", MYF(0),
+                       *table_names);
+       safe_exit(EX_ILLEGAL_TABLE);
+       /* We shall countinue here, if --force was given */
+    }
+  }
+
+  if (lock_tables)
+  {
+    if (mysql_real_query(sock, lock_tables_query.str,
+                         lock_tables_query.length-1))
       DBerror(sock, "when doing LOCK TABLES");
        /* We shall countinue here, if --force was given */
-    dynstr_free(&query);
   }
+  dynstr_free(&lock_tables_query);
   if (flush_logs)
   {
     if (mysql_refresh(sock, REFRESH_LOG))
@@ -2224,20 +2264,20 @@ static int dump_selected_tables(char *db, char **table_names, int tables)
   }
   if (opt_xml)
     print_xml_tag1(md_result_file, "", "database name=", db, "\n");
-  for (; tables > 0 ; tables-- , table_names++)
+
+  /* Dump each selected table */
+  const char *table_name;
+  for (i= 0 ; i < dump_tables.records ; i++)
   {
-     char new_table_name[NAME_LEN];
-
-     /* the table name passed on commandline may be wrong case */
-     if (!get_actual_table_name( *table_names, new_table_name, sizeof(new_table_name) ))
-     {
-	    numrows = getTableStructure(new_table_name, db);
-	    if (!dFlag && numrows > 0)
-	      dumpTable(numrows, new_table_name);
-     }
-    my_free(order_by, MYF(MY_ALLOW_ZERO_PTR));
-    order_by= 0;
+    table_name= hash_element(&dump_tables, i);
+    DBUG_PRINT("info",("Dumping table %s", table_name));
+    numrows = getTableStructure(table_name, db);
+    if (!dFlag && numrows > 0)
+      dumpTable(numrows, table_name);
   }
+  hash_free(&dump_tables);
+  my_free(order_by, MYF(MY_ALLOW_ZERO_PTR));
+  order_by= 0;
   if (opt_xml)
   {
     fputs("</database>\n", md_result_file);
@@ -2245,7 +2285,7 @@ static int dump_selected_tables(char *db, char **table_names, int tables)
   }
   if (lock_tables)
     mysql_query_with_error_report(sock, 0, "UNLOCK TABLES");
-  return 0;
+  DBUG_RETURN(0);
 } /* dump_selected_tables */
 
 
diff --git a/client/mysqltest.c b/client/mysqltest.c
index e60d9ecd1c..3c238b57a0 100644
--- a/client/mysqltest.c
+++ b/client/mysqltest.c
@@ -781,7 +781,7 @@ int var_set(const char *var_name, const char *var_name_end,
   }
   else
     v = var_reg + digit;
-  return eval_expr(v, var_val, (const char**)&var_val_end);
+  DBUG_RETURN(eval_expr(v, var_val, (const char**)&var_val_end));
 }
 
 
@@ -955,9 +955,38 @@ static void do_exec(struct st_query* q)
       replace_dynstr_append_mem(ds, buf, strlen(buf));
   }
   error= pclose(res_file);
-
   if (error != 0)
-    die("command \"%s\" failed", cmd);
+  {
+    uint status= WEXITSTATUS(error);
+    if(q->abort_on_error)
+      die("At line %u: command \"%s\" failed", start_lineno, cmd);
+    else
+    {
+      DBUG_PRINT("info",
+                 ("error: %d, status: %d", error, status));
+      bool ok= 0;
+      uint i;
+      for (i=0 ; (uint) i < q->expected_errors ; i++)
+      {
+        DBUG_PRINT("info", ("expected error: %d", q->expected_errno[i].code.errnum));
+        if ((q->expected_errno[i].type == ERR_ERRNO) &&
+            (q->expected_errno[i].code.errnum == status))
+          ok= 1;
+        verbose_msg("At line %u: command \"%s\" failed with expected error: %d",
+                    start_lineno, cmd, status);
+      }
+      if (!ok)
+        die("At line: %u: command \"%s\" failed with wrong error: %d",
+            start_lineno, cmd, status);
+    }
+  }
+  else if (q->expected_errno[0].type == ERR_ERRNO &&
+           q->expected_errno[0].code.errnum != 0)
+  {
+    /* Error code we wanted was != 0, i.e. not an expected success */
+    die("At line: %u: command \"%s\" succeeded - should have failed with errno %d...",
+        start_lineno, cmd, q->expected_errno[0].code.errnum);
+  }
 
   if (!disable_result_log)
   {
diff --git a/mysql-test/mysql-test-run.sh b/mysql-test/mysql-test-run.sh
index 3f7e7d2220..6071a753c8 100644
--- a/mysql-test/mysql-test-run.sh
+++ b/mysql-test/mysql-test-run.sh
@@ -688,6 +688,9 @@ MYSQL_CLIENT_TEST="$MYSQL_CLIENT_TEST --no-defaults --testcase --user=root --soc
 if [ "x$USE_EMBEDDED_SERVER" = "x1" ]; then
   MYSQL_CLIENT_TEST="$MYSQL_CLIENT_TEST -A --language=$LANGUAGE -A --datadir=$SLAVE_MYDDIR -A --character-sets-dir=$CHARSETSDIR"
 fi
+# Save path and name of mysqldump
+MYSQL_DUMP_DIR="$MYSQL_DUMP"
+export MYSQL_DUMP_DIR
 MYSQL_DUMP="$MYSQL_DUMP --no-defaults -uroot --socket=$MASTER_MYSOCK --password=$DBPASSWD $EXTRA_MYSQLDUMP_OPT"
 MYSQL_BINLOG="$MYSQL_BINLOG --no-defaults --local-load=$MYSQL_TMP_DIR $EXTRA_MYSQLBINLOG_OPT"
 MYSQL_FIX_SYSTEM_TABLES="$MYSQL_FIX_SYSTEM_TABLES --no-defaults --host=localhost --port=$MASTER_MYPORT --socket=$MASTER_MYSOCK --user=root --password=$DBPASSWD --basedir=$BASEDIR --bindir=$CLIENT_BINDIR --verbose"
diff --git a/mysql-test/r/mysqldump.result b/mysql-test/r/mysqldump.result
index f66647bdbf..50399c91ec 100644
--- a/mysql-test/r/mysqldump.result
+++ b/mysql-test/r/mysqldump.result
@@ -1409,3 +1409,30 @@ CREATE TABLE `t2` (
 
 DROP TABLE t1, t2;
 DROP DATABASE mysqldump_test_db;
+create database mysqldump_test_db;
+use mysqldump_test_db;
+create table t1(a varchar(30) primary key, b int not null);
+create table t2(a varchar(30) primary key, b int not null);
+create table t3(a varchar(30) primary key, b int not null);
+test_sequence
+------ Testing with illegal table names ------
+MYSQL_DUMP_DIR: Couldn't find table: "\d-2-1.sql"
+
+MYSQL_DUMP_DIR: Couldn't find table: "\t1"
+
+MYSQL_DUMP_DIR: Couldn't find table: "\t1"
+
+MYSQL_DUMP_DIR: Couldn't find table: "\\t1"
+
+MYSQL_DUMP_DIR: Couldn't find table: "t\1"
+
+MYSQL_DUMP_DIR: Couldn't find table: "t\1"
+
+MYSQL_DUMP_DIR: Couldn't find table: "t/1"
+
+test_sequence
+------ Testing with illegal database names ------
+MYSQL_DUMP_DIR: Got error: 1049: Unknown database 'mysqldump_test_d' when selecting the database
+MYSQL_DUMP_DIR: Got error: 1102: Incorrect database name 'mysqld\ump_test_db' when selecting the database
+drop table t1, t2, t3;
+drop database mysqldump_test_db;
diff --git a/mysql-test/t/mysqldump.test b/mysql-test/t/mysqldump.test
index 5a35c32831..349b1e9623 100644
--- a/mysql-test/t/mysqldump.test
+++ b/mysql-test/t/mysqldump.test
@@ -558,3 +558,81 @@ INSERT INTO t2 VALUES (1), (2);
 --exec $MYSQL_DUMP --skip-comments --no-data mysqldump_test_db t1 t2
 DROP TABLE t1, t2;
 DROP DATABASE mysqldump_test_db;
+
+#
+#  Testing with tables and databases that don't exists
+#  or contains illegal characters
+# (Bug #9358 mysqldump crashes if tablename starts with \)
+#
+create database mysqldump_test_db;
+use mysqldump_test_db;
+create table t1(a varchar(30) primary key, b int not null);
+create table t2(a varchar(30) primary key, b int not null);
+create table t3(a varchar(30) primary key, b int not null);
+
+--disable_query_log
+select '------ Testing with illegal table names ------' as test_sequence ;
+--enable_query_log
+--replace_result $MYSQL_DUMP_DIR MYSQL_DUMP_DIR
+--error 6
+--exec $MYSQL_DUMP --compact --skip-comments mysqldump_test_db "\d-2-1.sql" 2>&1
+
+--replace_result $MYSQL_DUMP_DIR MYSQL_DUMP_DIR
+--error 6
+--exec $MYSQL_DUMP --compact --skip-comments mysqldump_test_db  "\t1" 2>&1
+
+--replace_result $MYSQL_DUMP_DIR MYSQL_DUMP_DIR
+--error 6
+--exec $MYSQL_DUMP --compact --skip-comments mysqldump_test_db  "\\t1" 2>&1
+ 
+--replace_result $MYSQL_DUMP_DIR MYSQL_DUMP_DIR
+--error 6
+--exec $MYSQL_DUMP --compact --skip-comments mysqldump_test_db  "\\\\t1" 2>&1
+
+--replace_result $MYSQL_DUMP_DIR MYSQL_DUMP_DIR
+--error 6
+--exec $MYSQL_DUMP --compact --skip-comments mysqldump_test_db  "t\1" 2>&1
+
+--replace_result $MYSQL_DUMP_DIR MYSQL_DUMP_DIR
+--error 6
+--exec $MYSQL_DUMP --compact --skip-comments  mysqldump_test_db  "t\\1" 2>&1
+
+--replace_result $MYSQL_DUMP_DIR MYSQL_DUMP_DIR
+--error 6
+--exec $MYSQL_DUMP --compact --skip-comments mysqldump_test_db  "t/1" 2>&1
+
+--replace_result $MYSQL_DUMP_DIR MYSQL_DUMP_DIR
+--error 6
+--exec $MYSQL_DUMP --compact --skip-comments "mysqldump_test_db" "T_1"
+
+--replace_result $MYSQL_DUMP_DIR MYSQL_DUMP_DIR
+--error 6
+--exec $MYSQL_DUMP --compact --skip-comments "mysqldump_test_db" "T%1"
+
+--replace_result $MYSQL_DUMP_DIR MYSQL_DUMP_DIR
+--error 6
+--exec $MYSQL_DUMP --compact --skip-comments "mysqldump_test_db" "T'1"
+
+--replace_result $MYSQL_DUMP_DIR MYSQL_DUMP_DIR
+--error 6
+--exec $MYSQL_DUMP --compact --skip-comments "mysqldump_test_db" "T_1"
+
+--replace_result $MYSQL_DUMP_DIR MYSQL_DUMP_DIR
+--error 6
+--exec $MYSQL_DUMP --compact --skip-comments "mysqldump_test_db" "T_"
+
+--disable_query_log
+select '------ Testing with illegal database names ------' as test_sequence ;
+--enable_query_log
+--replace_result $MYSQL_DUMP_DIR MYSQL_DUMP_DIR
+--error 2
+--exec $MYSQL_DUMP --compact --skip-comments mysqldump_test_d 2>&1
+
+--replace_result $MYSQL_DUMP_DIR MYSQL_DUMP_DIR
+--error 2
+--exec $MYSQL_DUMP --compact --skip-comments "mysqld\ump_test_db" 2>&1
+
+drop table t1, t2, t3;
+drop database mysqldump_test_db;
+
+
-- 
2.30.9