From 287661e66cc1fea6730b357bb56d48c6d065ef43 Mon Sep 17 00:00:00 2001
From: unknown <monty@mysql.com>
Date: Thu, 11 Dec 2003 06:24:08 +0200
Subject: [PATCH] Fixed a possible memory leak on MacOSX when using the shared
 libmysql.so library (Bug #2061) mysql_server_init() now returns error code if
 something went wrong (Bug #2062) Don't use my_fopen() when reading symlink
 information as this may cause problems when a lot of files are opened. Free
 thread keys with pthread_key_delete() instead of relying on automatic free.
 (Bug #2062) Fixed bug in UNION statement with alias '*'. (Bug #1249) Fixed a
 bug in DELETE ... ORDER BY ... LIMIT where the rows where not deleted in the
 proper order. (Bug #1024). FOUND_ROWS() could return incorrect number of rows
 after a query with an impossible WHERE condition. HOW DATABASES doesn't
 anymore show .sym files (on windows) that doesn't point to a valid directory.
 (Bug #1385)

include/config-win.h:
  Ensure that USE_SYMDIR is set for all windows versions
  (This is set in makefiles, so this is just an extra safety measure)
include/my_pthread.h:
  Fixed a possible memory leak on MacOSX when using the shared libmysql.so library (Bug #2061)
include/my_sys.h:
  my_init() now returns error code if something went wrong
include/mysql.h:
  mysql_once_init() now returns error code if something went wrong
include/mysql_com.h:
  my_init() now returns error code if something went wrong
libmysql/libmysql.c:
  mysql_server_init() and mysql_once_init() now returns error code if something went wrong (Bug #2062)
mysql-test/r/limit.result:
  Update results
mysql-test/r/select_found.result:
  Update results
mysql-test/r/union.result:
  Update results
mysql-test/t/limit.test:
  Added test for DELETE ... ORDER BY ... LIMIT (bug #1024)
mysql-test/t/select_found.test:
  Added test for problem with impossible WHERE (Bug #1468)
mysql-test/t/union.test:
  Added test for problem with alias '*' (Bug #1249)
mysys/mf_pack.c:
  Don't use my_fopen() when reading symlink information as this may cause problems when a lot of files are opened.
mysys/my_init.c:
  my_init() now returns error code if something went wrong
mysys/my_lib.c:
  More debug information
mysys/my_thr_init.c:
  Free thread keys with pthread_key_delete() instead of relying on automatic free. (Bug #2062)
sql/sql_base.cc:
  Fixed bug in UNION statement with alias '*'. (Bug #1249)
sql/sql_delete.cc:
  Fixed a bug in DELETE ... ORDER BY ... LIMIT where the rows where not deleted in the proper order. (Bug #1024).
sql/sql_select.cc:
  FOUND_ROWS() could return incorrect number of rows after a query with an impossible WHERE condition.
sql/sql_show.cc:
  SHOW DATABASES doesn't anymore show .sym files (on windows) that doesn't point to a valid directory. (Bug #1385)
sql/sql_yacc.yy:
  Allow syntax UNION DISTINCT
---
 include/config-win.h             |  4 ++++
 include/my_pthread.h             |  3 +++
 include/my_sys.h                 |  2 +-
 include/mysql.h                  |  2 +-
 include/mysql_com.h              |  2 +-
 libmysql/libmysql.c              | 22 ++++++++++++++++------
 mysql-test/r/limit.result        | 23 ++++++++++++++++++++---
 mysql-test/r/select_found.result | 19 +++++++++++++++++++
 mysql-test/r/union.result        |  6 +++++-
 mysql-test/t/limit.test          | 22 +++++++++++++++++++---
 mysql-test/t/select_found.test   | 14 ++++++++++++++
 mysql-test/t/union.test          |  9 ++++++++-
 mysys/mf_pack.c                  | 27 ++++++++++++++-------------
 mysys/my_init.c                  | 20 +++++++++++++++-----
 mysys/my_lib.c                   |  6 ++++--
 mysys/my_thr_init.c              | 26 ++++++++++++++++++++------
 sql/sql_base.cc                  |  7 ++++++-
 sql/sql_delete.cc                | 12 +++++++++---
 sql/sql_select.cc                |  2 ++
 sql/sql_show.cc                  | 12 ++++++++++++
 sql/sql_yacc.yy                  |  1 +
 21 files changed, 194 insertions(+), 47 deletions(-)

diff --git a/include/config-win.h b/include/config-win.h
index b30f50f0ed..e6f03a10af 100644
--- a/include/config-win.h
+++ b/include/config-win.h
@@ -151,6 +151,10 @@ typedef uint rf_SetTimer;
 #define USE_MB_IDENT 1
 #define USE_STRCOLL 1
 
+/* All windows servers should support .sym files */
+#undef USE_SYMDIR
+#define USE_SYMDIR
+
 /* If LOAD DATA LOCAL INFILE should be enabled by default */
 #define ENABLED_LOCAL_INFILE 1
 
diff --git a/include/my_pthread.h b/include/my_pthread.h
index 16a14ac503..59367ee903 100644
--- a/include/my_pthread.h
+++ b/include/my_pthread.h
@@ -115,6 +115,7 @@ void pthread_exit(void *a);	 /* was #define pthread_exit(A) ExitThread(A)*/
 #undef SAFE_MUTEX				/* This will cause conflicts */
 #define pthread_key(T,V)  DWORD V
 #define pthread_key_create(A,B) ((*A=TlsAlloc())==0xFFFFFFFF)
+#define pthread_key_delete(A) TlsFree(A)
 #define pthread_getspecific(A) (TlsGetValue(A))
 #define my_pthread_getspecific(T,A) ((T) TlsGetValue(A))
 #define my_pthread_getspecific_ptr(T,V) ((T) TlsGetValue(V))
@@ -123,6 +124,7 @@ void pthread_exit(void *a);	 /* was #define pthread_exit(A) ExitThread(A)*/
 #else
 #define pthread_key(T,V) __declspec(thread) T V
 #define pthread_key_create(A,B) pthread_dummy(0)
+#define pthread_key_delete(A) pthread_dummy(0)
 #define pthread_getspecific(A) (&(A))
 #define my_pthread_getspecific(T,A) (&(A))
 #define my_pthread_getspecific_ptr(T,V) (V)
@@ -178,6 +180,7 @@ extern int pthread_mutex_destroy (pthread_mutex_t *);
 typedef int pthread_attr_t;			/* Needed by Unixware 7.0.0 */
 
 #define pthread_key_create(A,B) thr_keycreate((A),(B))
+#define pthread_key_delete(A) thr_keydelete(A)
 
 #define pthread_handler_decl(A,B) void *A(void *B)
 #define pthread_key(T,V) pthread_key_t V
diff --git a/include/my_sys.h b/include/my_sys.h
index df8b9759e5..acb4c01b4f 100644
--- a/include/my_sys.h
+++ b/include/my_sys.h
@@ -577,7 +577,7 @@ extern int my_snprintf(char* to, size_t n, const char* fmt, ...);
 extern int my_message(uint my_err, const char *str,myf MyFlags);
 extern int my_message_no_curses(uint my_err, const char *str,myf MyFlags);
 extern int my_message_curses(uint my_err, const char *str,myf MyFlags);
-extern void my_init(void);
+extern my_bool my_init(void);
 extern void my_end(int infoflag);
 extern int my_redel(const char *from, const char *to, int MyFlags);
 extern int my_copystat(const char *from, const char *to, int MyFlags);
diff --git a/include/mysql.h b/include/mysql.h
index 0c91266e19..3ffc014c44 100644
--- a/include/mysql.h
+++ b/include/mysql.h
@@ -426,7 +426,7 @@ int		STDCALL mysql_drop_db(MYSQL *mysql, const char *DB);
 int simple_command(MYSQL *mysql,enum enum_server_command command,
 		   const char *arg, unsigned long length, my_bool skipp_check);
 unsigned long net_safe_read(MYSQL* mysql);
-void mysql_once_init(void);
+int mysql_once_init(void);
 
 extern my_bool server_inited;
 
diff --git a/include/mysql_com.h b/include/mysql_com.h
index a874034ba4..e183a0ed42 100644
--- a/include/mysql_com.h
+++ b/include/mysql_com.h
@@ -241,7 +241,7 @@ void hash_password(unsigned long *result, const char *password);
 
 /* Some other useful functions */
 
-void my_init(void);
+my_bool my_init(void);
 int load_defaults(const char *conf_file, const char **groups,
 		  int *argc, char ***argv);
 my_bool my_thread_init(void);
diff --git a/libmysql/libmysql.c b/libmysql/libmysql.c
index 5fcc3ba5ee..607d8af6e5 100644
--- a/libmysql/libmysql.c
+++ b/libmysql/libmysql.c
@@ -102,8 +102,7 @@ int STDCALL mysql_server_init(int argc __attribute__((unused)),
 			      char **argv __attribute__((unused)),
 			      char **groups __attribute__((unused)))
 {
-  mysql_once_init();
-  return 0;
+  return (int) mysql_once_init();
 }
 
 void STDCALL mysql_server_end()
@@ -1436,7 +1435,8 @@ STDCALL mysql_rpl_query_type(const char* q, int len)
 MYSQL * STDCALL
 mysql_init(MYSQL *mysql)
 {
-  mysql_once_init();
+  if (mysql_once_init())
+    return 0;
   if (!mysql)
   {
     if (!(mysql=(MYSQL*) my_malloc(sizeof(*mysql),MYF(MY_WME | MY_ZEROFILL))))
@@ -1476,15 +1476,20 @@ mysql_init(MYSQL *mysql)
     This function is called by mysql_init() and indirectly called
     by mysql_query(), so one should never have to call this from an
     outside program.
+
+  RETURN
+    0  ok
+    1  could not initialize environment (out of memory or thread keys)
 */
 
-void mysql_once_init(void)
+int mysql_once_init(void)
 {
   if (!mysql_client_init)
   {
     mysql_client_init=1;
     org_my_init_done=my_init_done;
-    my_init();					/* Will init threads */
+    if (my_init())				/* Will init threads */
+      return 1;
     init_client_errs();
     if (!mysql_port)
     {
@@ -1518,10 +1523,15 @@ void mysql_once_init(void)
   }
 #ifdef THREAD
   else
-    my_thread_init();         /* Init if new thread */
+  {
+    if (my_thread_init())         /* Init if new thread */
+      return 1;
+  }
 #endif
+  return 0;
 }
 
+
 /**************************************************************************
   Fill in SSL part of MYSQL structure and set 'use_ssl' flag.
   NB! Errors are not reported until you do mysql_real_connect.
diff --git a/mysql-test/r/limit.result b/mysql-test/r/limit.result
index 5a8edc99c1..c82105e6a4 100644
--- a/mysql-test/r/limit.result
+++ b/mysql-test/r/limit.result
@@ -36,9 +36,7 @@ a	b
 3	4
 drop table t1;
 create table t1 (i int);
-insert into t1 (i) values(1);
-insert into t1 (i) values(1);
-insert into t1 (i) values(1);
+insert into t1 (i) values(1),(1),(1);
 delete from t1 limit 1;
 update t1 set i=2 limit 1;
 delete from t1 limit 0;
@@ -50,3 +48,22 @@ i
 drop table t1;
 select 0 limit 0;
 0
+CREATE TABLE t1(id int auto_increment primary key, id2 int, index(id2));
+INSERT INTO t1 (id2) values (0),(0),(0);
+DELETE FROM t1 WHERE id=1;
+INSERT INTO t1 SET id2=0;
+SELECT * FROM t1;
+id	id2
+4	0
+2	0
+3	0
+DELETE FROM t1 WHERE id2 = 0 ORDER BY id LIMIT 1;
+SELECT * FROM t1;
+id	id2
+4	0
+3	0
+DELETE FROM t1 WHERE id2 = 0 ORDER BY id desc LIMIT 1;
+SELECT * FROM t1;
+id	id2
+3	0
+DROP TABLE t1;
diff --git a/mysql-test/r/select_found.result b/mysql-test/r/select_found.result
index 0a7f464cf7..419ffb73d5 100644
--- a/mysql-test/r/select_found.result
+++ b/mysql-test/r/select_found.result
@@ -169,3 +169,22 @@ SELECT FOUND_ROWS();
 FOUND_ROWS()
 2
 drop table t1;
+create table t1 (id int, primary key (id));
+insert into t1 values (1), (2), (3), (4), (5);
+select SQL_CALC_FOUND_ROWS * from t1 where id > 3 limit 0, 1;
+id
+4
+select FOUND_ROWS();
+FOUND_ROWS()
+2
+select SQL_CALC_FOUND_ROWS * from t1 where id > 3 AND 1=2 limit 0, 1;
+id
+select FOUND_ROWS();
+FOUND_ROWS()
+0
+select SQL_CALC_FOUND_ROWS * from t1 where id > 6 limit 0, 1;
+id
+select FOUND_ROWS();
+FOUND_ROWS()
+0
+drop table t1;
diff --git a/mysql-test/r/union.result b/mysql-test/r/union.result
index cd5d600edf..fc5aa1ad0c 100644
--- a/mysql-test/r/union.result
+++ b/mysql-test/r/union.result
@@ -3,7 +3,7 @@ CREATE TABLE t1 (a int not null, b char (10) not null);
 insert into t1 values(1,'a'),(2,'b'),(3,'c'),(3,'c');
 CREATE TABLE t2 (a int not null, b char (10) not null);
 insert into t2 values (3,'c'),(4,'d'),(5,'f'),(6,'e');
-select a,b from t1 union select a,b from t2;
+select a,b from t1 union distinct select a,b from t2;
 a	b
 1	a
 2	b
@@ -423,3 +423,7 @@ create temporary table t1 select a from t1 union select a from t2;
 create table t1 select a from t1 union select a from t2;
 INSERT TABLE 't1' isn't allowed in FROM table list
 drop table t1,t2;
+select length(version()) > 1 as `*` UNION select 2;
+*
+1
+2
diff --git a/mysql-test/t/limit.test b/mysql-test/t/limit.test
index d4f6ce186c..32962073ee 100644
--- a/mysql-test/t/limit.test
+++ b/mysql-test/t/limit.test
@@ -19,9 +19,7 @@ select * from t1;
 drop table t1;
 
 create table t1 (i int);
-insert into t1 (i) values(1);
-insert into t1 (i) values(1);
-insert into t1 (i) values(1);
+insert into t1 (i) values(1),(1),(1);
 delete from t1 limit 1;
 update t1 set i=2 limit 1;
 delete from t1 limit 0;
@@ -29,4 +27,22 @@ update t1 set i=3 limit 0;
 select * from t1;
 drop table t1;
 
+# LIMIT 0
+
 select 0 limit 0;
+
+#
+# Test with DELETE, ORDER BY and limit (bug #1024)
+#
+
+CREATE TABLE t1(id int auto_increment primary key, id2 int, index(id2)); 
+INSERT INTO t1 (id2) values (0),(0),(0);
+DELETE FROM t1 WHERE id=1;
+INSERT INTO t1 SET id2=0;
+SELECT * FROM t1; 
+DELETE FROM t1 WHERE id2 = 0 ORDER BY id LIMIT 1; 
+# should have deleted WHERE id=2 
+SELECT * FROM t1; 
+DELETE FROM t1 WHERE id2 = 0 ORDER BY id desc LIMIT 1; 
+SELECT * FROM t1;
+DROP TABLE t1;
diff --git a/mysql-test/t/select_found.test b/mysql-test/t/select_found.test
index 0a483c860c..f5ee4d5c01 100644
--- a/mysql-test/t/select_found.test
+++ b/mysql-test/t/select_found.test
@@ -85,3 +85,17 @@ INSERT INTO t1 (titre,maxnumrep) VALUES
 SELECT SQL_CALC_FOUND_ROWS titre,numeropost,maxnumrep FROM t1 WHERE numeropost IN (1,2) ORDER BY maxnumrep DESC LIMIT 0, 1;
 SELECT FOUND_ROWS();
 drop table t1;
+
+#
+# Test problem with impossible WHERE (Bug #1468)
+#
+
+create table t1 (id int, primary key (id));
+insert into t1 values (1), (2), (3), (4), (5);
+select SQL_CALC_FOUND_ROWS * from t1 where id > 3 limit 0, 1;
+select FOUND_ROWS();
+select SQL_CALC_FOUND_ROWS * from t1 where id > 3 AND 1=2 limit 0, 1;
+select FOUND_ROWS();
+select SQL_CALC_FOUND_ROWS * from t1 where id > 6 limit 0, 1;
+select FOUND_ROWS();
+drop table t1;
diff --git a/mysql-test/t/union.test b/mysql-test/t/union.test
index ba4673e9a8..badfe4b9a3 100644
--- a/mysql-test/t/union.test
+++ b/mysql-test/t/union.test
@@ -8,7 +8,7 @@ insert into t1 values(1,'a'),(2,'b'),(3,'c'),(3,'c');
 CREATE TABLE t2 (a int not null, b char (10) not null);
 insert into t2 values (3,'c'),(4,'d'),(5,'f'),(6,'e');
 
-select a,b from t1 union select a,b from t2;
+select a,b from t1 union distinct select a,b from t2;
 select a,b from t1 union all select a,b from t2;
 select a,b from t1 union all select a,b from t2 order by b;
 select a,b from t1 union all select a,b from t2 union select 7,'g';
@@ -227,3 +227,10 @@ create temporary table t1 select a from t1 union select a from t2;
 --error 1093
 create table t1 select a from t1 union select a from t2;
 drop table t1,t2;
+
+#
+# Problem with alias '*' (BUG #1249)
+#
+
+select length(version()) > 1 as `*` UNION select 2;
+
diff --git a/mysys/mf_pack.c b/mysys/mf_pack.c
index b3aa347006..e2e811fe89 100644
--- a/mysys/mf_pack.c
+++ b/mysys/mf_pack.c
@@ -210,13 +210,13 @@ uint cleanup_dirname(register my_string to, const char *from)
 } /* cleanup_dirname */
 
 
-	/*
-	  On system where you don't have symbolic links, the following
-	  code will allow you to create a file: 
-	  directory-name.lnk that should contain the real path
-	  to the directory.  This will be used if the directory name
-	  doesn't exists
-	*/
+/*
+  On system where you don't have symbolic links, the following
+  code will allow you to create a file: 
+  directory-name.sym that should contain the real path
+  to the directory.  This will be used if the directory name
+  doesn't exists
+*/
 
 
 my_bool my_use_symdir=0;	/* Set this if you want to use symdirs */
@@ -228,16 +228,17 @@ void symdirget(char *dir)
   char *pos=strend(dir);
   if (dir[0] && pos[-1] != FN_DEVCHAR && access(dir, F_OK))
   {
-    FILE *fp;
+    File file;
+    uint length;
     char temp= *(--pos);            /* May be "/" or "\" */
     strmov(pos,".sym");
-    fp = my_fopen(dir, O_RDONLY,MYF(0));
+    file= my_open(dir, O_RDONLY, MYF(0));
     *pos++=temp; *pos=0;	  /* Restore old filename */
-    if (fp)
+    if (file >= 0)
     {
-      if (fgets(buff, sizeof(buff)-1, fp))
+      if ((length= my_read(file, buff, sizeof(buff), MYF(0))) > 0)
       {
-	for (pos=strend(buff);
+	for (pos= buff + length ;
 	     pos > buff && (iscntrl(pos[-1]) || isspace(pos[-1])) ;
 	     pos --);
 
@@ -247,7 +248,7 @@ void symdirget(char *dir)
 
 	strmake(dir,buff, (uint) (pos-buff));
       }
-      my_fclose(fp,MYF(0));
+      my_close(file, MYF(0));
     }
   }
 }
diff --git a/mysys/my_init.c b/mysys/my_init.c
index a8a184a2cb..8d4ba2c97b 100644
--- a/mysys/my_init.c
+++ b/mysys/my_init.c
@@ -62,13 +62,22 @@ static ulong atoi_octal(const char *str)
 }
 
 
-	/* Init my_sys functions and my_sys variabels */
+/*
+  Init my_sys functions and my_sys variabels
+
+  SYNOPSIS
+    my_init()
 
-void my_init(void)
+  RETURN
+    0  ok
+    1  Couldn't initialize environment
+*/
+
+my_bool my_init(void)
 {
   my_string str;
   if (my_init_done)
-    return;
+    return 0;
   my_init_done=1;
 #if defined(THREAD) && defined(SAFE_MUTEX)
   safe_mutex_global_init();		/* Must be called early */
@@ -78,7 +87,8 @@ void my_init(void)
 #if defined(HAVE_PTHREAD_INIT)
   pthread_init();			/* Must be called before DBUG_ENTER */
 #endif
-  my_thread_global_init();
+  if (my_thread_global_init())
+    return 1;
 #if !defined( __WIN__) && !defined(OS2) && !defined(__NETWARE__)
   sigfillset(&my_signals);		/* signals blocked by mf_brkhant */
 #endif
@@ -110,7 +120,7 @@ void my_init(void)
 #ifdef __WIN__
     win32_init_tcp_ip();
 #endif
-    DBUG_VOID_RETURN;
+    DBUG_RETURN(0);
   }
 } /* my_init */
 
diff --git a/mysys/my_lib.c b/mysys/my_lib.c
index 035bafd07b..426acedc64 100644
--- a/mysys/my_lib.c
+++ b/mysys/my_lib.c
@@ -602,9 +602,11 @@ MY_STAT *my_stat(const char *path, MY_STAT *stat_area, myf my_flags)
   if ((m_used= (stat_area == NULL)))
     if (!(stat_area = (MY_STAT *) my_malloc(sizeof(MY_STAT), my_flags)))
       goto error;
-  if ( ! stat((my_string) path, (struct stat *) stat_area) )
+  if (! stat((my_string) path, (struct stat *) stat_area) )
     DBUG_RETURN(stat_area);
-  my_errno=errno;
+
+  DBUG_PRINT("error",("Got errno: %d from stat", errno));
+  my_errno= errno;
   if (m_used)					/* Free if new area */
     my_free((gptr) stat_area,MYF(0));
 
diff --git a/mysys/my_thr_init.c b/mysys/my_thr_init.c
index 9f64e9dcb6..3827b24ac3 100644
--- a/mysys/my_thr_init.c
+++ b/mysys/my_thr_init.c
@@ -44,12 +44,23 @@ pthread_mutexattr_t my_fast_mutexattr;
 pthread_mutexattr_t my_errchk_mutexattr;
 #endif
 
+/*
+  initialize thread environment
+
+  SYNOPSIS
+    my_thread_global_init()
+
+  RETURN
+    0  ok
+    1  error (Couldn't create THR_KEY_mysys)
+*/
+
 my_bool my_thread_global_init(void)
 {
-  if (pthread_key_create(&THR_KEY_mysys,free))
+  if (pthread_key_create(&THR_KEY_mysys,0))
   {
     fprintf(stderr,"Can't initialize threads: error %d\n",errno);
-    exit(1);
+    return 1;
   }
 #ifdef PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP
   pthread_mutexattr_init(&my_fast_mutexattr);
@@ -79,15 +90,18 @@ my_bool my_thread_global_init(void)
 #ifndef HAVE_GETHOSTBYNAME_R
   pthread_mutex_init(&LOCK_gethostbyname_r,MY_MUTEX_INIT_SLOW);
 #endif
-  return my_thread_init();
+  if (my_thread_init())
+  {
+    my_thread_global_end();			/* Clean up */
+    return 1;
+  }
+  return 0;
 }
 
 
 void my_thread_global_end(void)
 {
-#if defined(USE_TLS)
-  (void) TlsFree(THR_KEY_mysys);
-#endif
+  pthread_key_delete(THR_KEY_mysys);
 #ifdef PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP
   pthread_mutexattr_destroy(&my_fast_mutexattr);
 #endif
diff --git a/sql/sql_base.cc b/sql/sql_base.cc
index e4694adb9a..dd80062d6e 100644
--- a/sql/sql_base.cc
+++ b/sql/sql_base.cc
@@ -1868,8 +1868,13 @@ int setup_fields(THD *thd, TABLE_LIST *tables, List<Item> &fields,
 
   while ((item=it++))
   {
+    /*
+      Expand * to all fields if this is not the temporary table for an
+      a UNION result
+    */
     if (item->type() == Item::FIELD_ITEM &&
-	((Item_field*) item)->field_name[0] == '*')
+	((Item_field*) item)->field_name[0] == '*' &&
+	!((Item_field*) item)->field)
     {
       uint elem=fields.elements;
       if (insert_fields(thd,tables,((Item_field*) item)->db_name,
diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc
index 34a79ecd78..ee2b720907 100644
--- a/sql/sql_delete.cc
+++ b/sql/sql_delete.cc
@@ -78,7 +78,7 @@ int mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, ORDER *order,
     DBUG_RETURN(-1);
   if ((select && select->check_quick(thd,
 				     test(thd->options & OPTION_SAFE_UPDATES),
-				     limit)) || 
+				     limit)) ||
       !limit)
   {
     delete select;
@@ -117,13 +117,19 @@ int mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, ORDER *order,
     if (setup_order(thd, &tables, fields, all_fields, order) ||
         !(sortorder=make_unireg_sortorder(order, &length)) ||
         (table->found_records = filesort(table, sortorder, length,
-                                        (SQL_SELECT *) 0, 0L, HA_POS_ERROR,
+					 select, 0L, HA_POS_ERROR,
 					 &examined_rows))
         == HA_POS_ERROR)
     {
       delete select;
-      DBUG_RETURN(-1);		// This will force out message
+      DBUG_RETURN(-1);			// This will force out message
     }
+    /*
+      Filesort has already found and selected the rows we want to delete,
+      so we don't need the where clause
+    */
+    delete select;
+    select= 0;
   }
 
   init_read_record(&info,thd,table,select,1,1);
diff --git a/sql/sql_select.cc b/sql/sql_select.cc
index df4b0226ff..892ad02bb5 100644
--- a/sql/sql_select.cc
+++ b/sql/sql_select.cc
@@ -3273,6 +3273,8 @@ return_zero_rows(JOIN *join, select_result *result,TABLE_LIST *tables,
     }
     result->send_eof();				// Should be safe
   }
+  /* Update results for FOUND_ROWS */
+  join->thd->limit_found_rows= join->thd->examined_row_count= 0;
   DBUG_RETURN(0);
 }
 
diff --git a/sql/sql_show.cc b/sql/sql_show.cc
index a7de04d0bf..cb6cd18b7c 100644
--- a/sql/sql_show.cc
+++ b/sql/sql_show.cc
@@ -197,7 +197,19 @@ mysql_find_files(THD *thd,List<char> *files, const char *db,const char *path,
 #ifdef USE_SYMDIR
       char *ext;
       if (my_use_symdir && !strcmp(ext=fn_ext(file->name), ".sym"))
+      {
+	/* Only show the sym file if it points to a directory */
+	char buff[FN_REFLEN], *end;
+	MY_STAT status;
         *ext=0;                                 /* Remove extension */
+	unpack_dirname(buff, file->name);
+	end= strend(buff);
+	if (end != buff && end[-1] == FN_LIBCHAR)
+	  end[-1]= 0;				// Remove end FN_LIBCHAR
+	if (!my_stat(buff, &status, MYF(0)) ||
+	    !MY_S_ISDIR(status.st_mode))
+	  continue;
+      }
       else
 #endif
       {
diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy
index 0383730090..b5b9a4cdfb 100644
--- a/sql/sql_yacc.yy
+++ b/sql/sql_yacc.yy
@@ -4014,5 +4014,6 @@ optional_order_or_limit:
 
 union_option:
 	/* empty */ {}
+	| DISTINCT {}
 	| ALL { Lex->union_option=1; }
 	;
-- 
2.30.9