[Pkg-mysql-commits] r880 - branches/etch-5.0/debian/patches

Sean Finney seanius at alioth.debian.org
Mon May 28 17:16:24 UTC 2007


Author: seanius
Date: 2007-05-28 17:16:23 +0000 (Mon, 28 May 2007)
New Revision: 880

Added:
   branches/etch-5.0/debian/patches/92_SECURITY_CVE-2007-2691_thd_privs.dpatch
Modified:
   branches/etch-5.0/debian/patches/00list
Log:
CVE-2007-2691

Modified: branches/etch-5.0/debian/patches/00list
===================================================================
--- branches/etch-5.0/debian/patches/00list	2007-05-28 17:15:56 UTC (rev 879)
+++ branches/etch-5.0/debian/patches/00list	2007-05-28 17:16:23 UTC (rev 880)
@@ -20,3 +20,4 @@
 89_ndb__staticlib.dpatch
 90_tmp__limit_comma_bug.dpatch
 91_SECURITY_CVE-2007-2691_alter-drop.dpatch
+92_SECURITY_CVE-2007-2691_thd_privs.dpatch

Added: branches/etch-5.0/debian/patches/92_SECURITY_CVE-2007-2691_thd_privs.dpatch
===================================================================
--- branches/etch-5.0/debian/patches/92_SECURITY_CVE-2007-2691_thd_privs.dpatch	                        (rev 0)
+++ branches/etch-5.0/debian/patches/92_SECURITY_CVE-2007-2691_thd_privs.dpatch	2007-05-28 17:16:23 UTC (rev 880)
@@ -0,0 +1,13877 @@
+#! /bin/sh /usr/share/dpatch/dpatch-run
+## 92_SECURITY_CVE-2007-2691_thd_privs.dpatch by  <seanius at debian.org>
+##
+## DP: taken from http://lists.mysql.com/commits/23293
+
+ at DPATCH@
+diff -urNad mysql-5.0-etch~/sql/mysql_priv.h mysql-5.0-etch/sql/mysql_priv.h
+--- mysql-5.0-etch~/sql/mysql_priv.h	2007-05-28 18:56:15.000000000 +0200
++++ mysql-5.0-etch/sql/mysql_priv.h	2007-05-28 19:12:52.000000000 +0200
+@@ -930,6 +930,8 @@
+ int fill_schema_column_privileges(THD *thd, TABLE_LIST *tables, COND *cond);
+ bool get_schema_tables_result(JOIN *join,
+                               enum enum_schema_table_state executed_place);
++enum enum_schema_tables get_schema_table_idx(ST_SCHEMA_TABLE *schema_table);
++
+ #define is_schema_db(X) \
+   !my_strcasecmp(system_charset_info, information_schema_name.str, (X))
+ 
+diff -urNad mysql-5.0-etch~/sql/mysql_priv.h.orig mysql-5.0-etch/sql/mysql_priv.h.orig
+--- mysql-5.0-etch~/sql/mysql_priv.h.orig	1970-01-01 01:00:00.000000000 +0100
++++ mysql-5.0-etch/sql/mysql_priv.h.orig	2007-05-28 18:56:15.000000000 +0200
+@@ -0,0 +1,1685 @@
++/* Copyright (C) 2000-2003 MySQL AB
++
++   This program is free software; you can redistribute it and/or modify
++   it under the terms of the GNU General Public License as published by
++   the Free Software Foundation; either version 2 of the License, or
++   (at your option) any later version.
++
++   This program is distributed in the hope that it will be useful,
++   but WITHOUT ANY WARRANTY; without even the implied warranty of
++   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++   GNU General Public License for more details.
++
++   You should have received a copy of the GNU General Public License
++   along with this program; if not, write to the Free Software
++   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */
++
++/*
++  Mostly this file is used in the server. But a little part of it is used in
++  mysqlbinlog too (definition of SELECT_DISTINCT and others).
++  The consequence is that 90% of the file is wrapped in #ifndef MYSQL_CLIENT,
++  except the part which must be in the server and in the client.
++*/
++
++#ifndef MYSQL_CLIENT
++
++#include <my_global.h>
++#include <mysql_version.h>
++#include <mysql_embed.h>
++#include <my_sys.h>
++#include <my_time.h>
++#include <m_string.h>
++#include <hash.h>
++#include <signal.h>
++#include <thr_lock.h>
++#include <my_base.h>			/* Needed by field.h */
++#include "sql_bitmap.h"
++#include "sql_array.h"
++
++#ifdef __EMX__
++#undef write  /* remove pthread.h macro definition for EMX */
++#endif
++
++/* TODO convert all these three maps to Bitmap classes */
++typedef ulonglong table_map;          /* Used for table bits in join */
++#if MAX_INDEXES <= 64
++typedef Bitmap<64>  key_map;          /* Used for finding keys */
++#else
++typedef Bitmap<((MAX_INDEXES+7)/8*8)> key_map; /* Used for finding keys */
++#endif
++typedef ulong key_part_map;           /* Used for finding key parts */
++typedef ulong nesting_map;  /* Used for flags of nesting constructs */
++/*
++  Used to identify NESTED_JOIN structures within a join (applicable only to
++  structures that have not been simplified away and embed more the one
++  element)
++*/
++typedef ulonglong nested_join_map;
++
++/* query_id */
++typedef ulonglong query_id_t;
++extern query_id_t query_id;
++
++/* increment query_id and return it.  */
++inline query_id_t next_query_id() { return query_id++; }
++
++/* useful constants */
++extern const key_map key_map_empty;
++extern key_map key_map_full;          /* Should be threaded as const */
++extern const char *primary_key_name;
++
++#include "mysql_com.h"
++#include <violite.h>
++#include "unireg.h"
++
++void init_sql_alloc(MEM_ROOT *root, uint block_size, uint pre_alloc_size);
++gptr sql_alloc(unsigned size);
++gptr sql_calloc(unsigned size);
++char *sql_strdup(const char *str);
++char *sql_strmake(const char *str,uint len);
++gptr sql_memdup(const void * ptr,unsigned size);
++void sql_element_free(void *ptr);
++char *sql_strmake_with_convert(const char *str, uint32 arg_length,
++			       CHARSET_INFO *from_cs,
++			       uint32 max_res_length,
++			       CHARSET_INFO *to_cs, uint32 *result_length);
++void kill_one_thread(THD *thd, ulong id, bool only_kill_query);
++bool net_request_file(NET* net, const char* fname);
++char* query_table_status(THD *thd,const char *db,const char *table_name);
++
++#define x_free(A)	{ my_free((gptr) (A),MYF(MY_WME | MY_FAE | MY_ALLOW_ZERO_PTR)); }
++#define safeFree(x)	{ if(x) { my_free((gptr) x,MYF(0)); x = NULL; } }
++#define PREV_BITS(type,A)	((type) (((type) 1 << (A)) -1))
++#define all_bits_set(A,B) ((A) & (B) != (B))
++
++extern CHARSET_INFO *system_charset_info, *files_charset_info ;
++extern CHARSET_INFO *national_charset_info, *table_alias_charset;
++
++
++enum Derivation
++{
++  DERIVATION_IGNORABLE= 5,
++  DERIVATION_COERCIBLE= 4,
++  DERIVATION_SYSCONST= 3,
++  DERIVATION_IMPLICIT= 2,
++  DERIVATION_NONE= 1,
++  DERIVATION_EXPLICIT= 0
++};
++
++
++typedef struct my_locale_st
++{
++  const char *name;
++  const char *description;
++  const bool is_ascii;
++  TYPELIB *month_names;
++  TYPELIB *ab_month_names;
++  TYPELIB *day_names;
++  TYPELIB *ab_day_names;
++#ifdef __cplusplus 
++  my_locale_st(const char *name_par, const char *descr_par, bool is_ascii_par,
++               TYPELIB *month_names_par, TYPELIB *ab_month_names_par,
++               TYPELIB *day_names_par, TYPELIB *ab_day_names_par) : 
++    name(name_par), description(descr_par), is_ascii(is_ascii_par),
++    month_names(month_names_par), ab_month_names(ab_month_names_par),
++    day_names(day_names_par), ab_day_names(ab_day_names_par)
++  {}
++#endif
++} MY_LOCALE;
++
++extern MY_LOCALE my_locale_en_US;
++extern MY_LOCALE *my_locales[];
++
++MY_LOCALE *my_locale_by_name(const char *name);
++
++/***************************************************************************
++  Configuration parameters
++****************************************************************************/
++
++#define ACL_CACHE_SIZE		256
++#define MAX_PASSWORD_LENGTH	32
++#define HOST_CACHE_SIZE		128
++#define MAX_ACCEPT_RETRY	10	// Test accept this many times
++#define MAX_FIELDS_BEFORE_HASH	32
++#define USER_VARS_HASH_SIZE     16
++#define TABLE_OPEN_CACHE_MIN    64
++#define TABLE_OPEN_CACHE_DEFAULT 64
++
++/* 
++ Value of 9236 discovered through binary search 2006-09-26 on Ubuntu Dapper
++ Drake, libc6 2.3.6-0ubuntu2, Linux kernel 2.6.15-27-686, on x86.  (Added 
++ 100 bytes as reasonable buffer against growth and other environments'
++ requirements.)
++
++ Feel free to raise this by the smallest amount you can to get the
++ "execution_constants" test to pass.
++ */
++#define STACK_MIN_SIZE          10788   // Abort if less stack during eval.
++
++#define STACK_MIN_SIZE_FOR_OPEN 1024*80
++#define STACK_BUFF_ALLOC	256	// For stack overrun checks
++#ifndef MYSQLD_NET_RETRY_COUNT
++#define MYSQLD_NET_RETRY_COUNT  10	// Abort read after this many int.
++#endif
++#define TEMP_POOL_SIZE          128
++
++#define QUERY_ALLOC_BLOCK_SIZE		8192
++#define QUERY_ALLOC_PREALLOC_SIZE   	8192
++#define TRANS_ALLOC_BLOCK_SIZE		4096
++#define TRANS_ALLOC_PREALLOC_SIZE	4096
++#define RANGE_ALLOC_BLOCK_SIZE		2048
++#define ACL_ALLOC_BLOCK_SIZE		1024
++#define UDF_ALLOC_BLOCK_SIZE		1024
++#define TABLE_ALLOC_BLOCK_SIZE		1024
++#define BDB_LOG_ALLOC_BLOCK_SIZE	1024
++#define WARN_ALLOC_BLOCK_SIZE		2048
++#define WARN_ALLOC_PREALLOC_SIZE	1024
++
++/*
++  The following parameters is to decide when to use an extra cache to
++  optimise seeks when reading a big table in sorted order
++*/
++#define MIN_FILE_LENGTH_TO_USE_ROW_CACHE (10L*1024*1024)
++#define MIN_ROWS_TO_USE_TABLE_CACHE	 100
++#define MIN_ROWS_TO_USE_BULK_INSERT	 100
++
++/*
++  The following is used to decide if MySQL should use table scanning
++  instead of reading with keys.  The number says how many evaluation of the
++  WHERE clause is comparable to reading one extra row from a table.
++*/
++#define TIME_FOR_COMPARE   5	// 5 compares == one read
++
++/*
++  Number of comparisons of table rowids equivalent to reading one row from a 
++  table.
++*/
++#define TIME_FOR_COMPARE_ROWID  (TIME_FOR_COMPARE*2)
++
++/*
++  For sequential disk seeks the cost formula is:
++    DISK_SEEK_BASE_COST + DISK_SEEK_PROP_COST * #blocks_to_skip  
++  
++  The cost of average seek 
++    DISK_SEEK_BASE_COST + DISK_SEEK_PROP_COST*BLOCKS_IN_AVG_SEEK =1.0.
++*/
++#define DISK_SEEK_BASE_COST ((double)0.5)
++
++#define BLOCKS_IN_AVG_SEEK  128
++
++#define DISK_SEEK_PROP_COST ((double)0.5/BLOCKS_IN_AVG_SEEK)
++
++
++/*
++  Number of rows in a reference table when refereed through a not unique key.
++  This value is only used when we don't know anything about the key
++  distribution.
++*/
++#define MATCHING_ROWS_IN_OTHER_TABLE 10
++
++/* Don't pack string keys shorter than this (if PACK_KEYS=1 isn't used) */
++#define KEY_DEFAULT_PACK_LENGTH 8
++
++/* Characters shown for the command in 'show processlist' */
++#define PROCESS_LIST_WIDTH 100
++
++#define PRECISION_FOR_DOUBLE 53
++#define PRECISION_FOR_FLOAT  24
++
++/* The following can also be changed from the command line */
++#define CONNECT_TIMEOUT		5		// Do not wait long for connect
++#define DEFAULT_CONCURRENCY	10
++#define DELAYED_LIMIT		100		/* pause after xxx inserts */
++#define DELAYED_QUEUE_SIZE	1000
++#define DELAYED_WAIT_TIMEOUT	5*60		/* Wait for delayed insert */
++#define FLUSH_TIME		0		/* Don't flush tables */
++#define MAX_CONNECT_ERRORS	10		// errors before disabling host
++
++#ifdef HAVE_INNOBASE_DB
++#define IF_INNOBASE_DB(A, B) (A)
++#else
++#define IF_INNOBASE_DB(A, B) (B)
++#endif
++#ifdef __NETWARE__
++#define IF_NETWARE(A,B) (A)
++#else
++#define IF_NETWARE(A,B) (B)
++#endif
++
++#if defined(__WIN__) || defined(OS2)
++#define IF_WIN(A,B) (A)
++#undef	FLUSH_TIME
++#define FLUSH_TIME	1800			/* Flush every half hour */
++
++#define INTERRUPT_PRIOR -2
++#define CONNECT_PRIOR	-1
++#define WAIT_PRIOR	0
++#define QUERY_PRIOR	2
++#else
++#define IF_WIN(A,B) (B)
++#define INTERRUPT_PRIOR 10
++#define CONNECT_PRIOR	9
++#define WAIT_PRIOR	8
++#define QUERY_PRIOR	6
++#endif /* __WIN92__ */
++
++	/* Bits from testflag */
++#define TEST_PRINT_CACHED_TABLES 1
++#define TEST_NO_KEY_GROUP	 2
++#define TEST_MIT_THREAD		4
++#define TEST_BLOCKING		8
++#define TEST_KEEP_TMP_TABLES	16
++#define TEST_NO_THREADS		32	/* For debugging under Linux */
++#define TEST_READCHECK		64	/* Force use of readcheck */
++#define TEST_NO_EXTRA		128
++#define TEST_CORE_ON_SIGNAL	256	/* Give core if signal */
++#define TEST_NO_STACKTRACE	512
++#define TEST_SIGINT		1024	/* Allow sigint on threads */
++#define TEST_SYNCHRONIZATION    2048    /* get server to do sleep in
++                                           some places */
++#endif
++
++/*
++   This is included in the server and in the client.
++   Options for select set by the yacc parser (stored in lex->options).
++
++   XXX:
++   log_event.h defines OPTIONS_WRITTEN_TO_BIN_LOG to specify what THD
++   options list are written into binlog. These options can NOT change their
++   values, or it will break replication between version.
++
++   context is encoded as following:
++   SELECT - SELECT_LEX_NODE::options
++   THD    - THD::options
++   intern - neither. used only as
++            func(..., select_node->options | thd->options | OPTION_XXX, ...)
++
++   TODO: separate three contexts above, move them to separate bitfields.
++*/
++
++#define SELECT_DISTINCT         (1L << 0)       // SELECT, user
++#define SELECT_STRAIGHT_JOIN    (1L << 1)       // SELECT, user
++#define SELECT_DESCRIBE         (1L << 2)       // SELECT, user
++#define SELECT_SMALL_RESULT     (1L << 3)       // SELECT, user
++#define SELECT_BIG_RESULT       (1L << 4)       // SELECT, user
++#define OPTION_FOUND_ROWS       (1L << 5)       // SELECT, user
++#define OPTION_TO_QUERY_CACHE   (1L << 6)       // SELECT, user
++#define SELECT_NO_JOIN_CACHE    (1L << 7)       // intern
++#define OPTION_BIG_TABLES       (1L << 8)       // THD, user
++#define OPTION_BIG_SELECTS      (1L << 9)       // THD, user
++#define OPTION_LOG_OFF          (1L << 10)      // THD, user
++#define OPTION_UPDATE_LOG       (1L << 11)      // THD, user, unused
++#define TMP_TABLE_ALL_COLUMNS   (1L << 12)      // SELECT, intern
++#define OPTION_WARNINGS         (1L << 13)      // THD, user
++#define OPTION_AUTO_IS_NULL     (1L << 14)      // THD, user, binlog
++#define OPTION_FOUND_COMMENT    (1L << 15)      // SELECT, intern, parser
++#define OPTION_SAFE_UPDATES     (1L << 16)      // THD, user
++#define OPTION_BUFFER_RESULT    (1L << 17)      // SELECT, user
++#define OPTION_BIN_LOG          (1L << 18)      // THD, user
++#define OPTION_NOT_AUTOCOMMIT   (1L << 19)      // THD, user
++#define OPTION_BEGIN            (1L << 20)      // THD, intern
++#define OPTION_TABLE_LOCK       (1L << 21)      // THD, intern
++#define OPTION_QUICK            (1L << 22)      // SELECT (for DELETE)
++#define OPTION_QUOTE_SHOW_CREATE (1L << 23)     // THD, user
++
++/* Thr following is used to detect a conflict with DISTINCT
++   in the user query has requested */
++#define SELECT_ALL              (1L << 24)      // SELECT, user, parser
++
++/* Set if we are updating a non-transaction safe table */
++#define OPTION_STATUS_NO_TRANS_UPDATE   (1L << 25) // THD, intern
++
++/* The following can be set when importing tables in a 'wrong order'
++   to suppress foreign key checks */
++#define OPTION_NO_FOREIGN_KEY_CHECKS    (1L << 26) // THD, user, binlog
++/* The following speeds up inserts to InnoDB tables by suppressing unique
++   key checks in some cases */
++#define OPTION_RELAXED_UNIQUE_CHECKS    (1L << 27) // THD, user, binlog
++#define SELECT_NO_UNLOCK                (1L << 28) // SELECT, intern
++#define OPTION_SCHEMA_TABLE             (1L << 29) // SELECT, intern
++/* Flag set if setup_tables already done */
++#define OPTION_SETUP_TABLES_DONE        (1L << 30) // intern
++/* If not set then the thread will ignore all warnings with level notes. */
++#define OPTION_SQL_NOTES                (1UL << 31) // THD, user
++/* 
++  Force the used temporary table to be a MyISAM table (because we will use
++  fulltext functions when reading from it.
++*/
++#define TMP_TABLE_FORCE_MYISAM          (LL(1) << 32)
++
++/*
++  Maximum length of time zone name that we support
++  (Time zone name is char(64) in db). mysqlbinlog needs it.
++*/
++#define MAX_TIME_ZONE_NAME_LENGTH 72
++
++/* The rest of the file is included in the server only */
++#ifndef MYSQL_CLIENT
++
++/* Bits for different SQL modes modes (including ANSI mode) */
++#define MODE_REAL_AS_FLOAT              1
++#define MODE_PIPES_AS_CONCAT            2
++#define MODE_ANSI_QUOTES                4
++#define MODE_IGNORE_SPACE		8
++#define MODE_NOT_USED			16
++#define MODE_ONLY_FULL_GROUP_BY		32
++#define MODE_NO_UNSIGNED_SUBTRACTION	64
++#define MODE_NO_DIR_IN_CREATE		128
++#define MODE_POSTGRESQL			256
++#define MODE_ORACLE			512
++#define MODE_MSSQL			1024
++#define MODE_DB2			2048
++#define MODE_MAXDB			4096
++#define MODE_NO_KEY_OPTIONS             8192
++#define MODE_NO_TABLE_OPTIONS           16384 
++#define MODE_NO_FIELD_OPTIONS           32768
++#define MODE_MYSQL323                   65536
++#define MODE_MYSQL40                    (MODE_MYSQL323*2)
++#define MODE_ANSI	                (MODE_MYSQL40*2)
++#define MODE_NO_AUTO_VALUE_ON_ZERO      (MODE_ANSI*2)
++#define MODE_NO_BACKSLASH_ESCAPES       (MODE_NO_AUTO_VALUE_ON_ZERO*2)
++#define MODE_STRICT_TRANS_TABLES	(MODE_NO_BACKSLASH_ESCAPES*2)
++#define MODE_STRICT_ALL_TABLES		(MODE_STRICT_TRANS_TABLES*2)
++#define MODE_NO_ZERO_IN_DATE		(MODE_STRICT_ALL_TABLES*2)
++#define MODE_NO_ZERO_DATE		(MODE_NO_ZERO_IN_DATE*2)
++#define MODE_INVALID_DATES		(MODE_NO_ZERO_DATE*2)
++#define MODE_ERROR_FOR_DIVISION_BY_ZERO (MODE_INVALID_DATES*2)
++#define MODE_TRADITIONAL		(MODE_ERROR_FOR_DIVISION_BY_ZERO*2)
++#define MODE_NO_AUTO_CREATE_USER	(MODE_TRADITIONAL*2)
++#define MODE_HIGH_NOT_PRECEDENCE	(MODE_NO_AUTO_CREATE_USER*2)
++#define MODE_NO_ENGINE_SUBSTITUTION     (MODE_HIGH_NOT_PRECEDENCE*2)
++/*
++  Replication uses 8 bytes to store SQL_MODE in the binary log. The day you
++  use strictly more than 64 bits by adding one more define above, you should
++  contact the replication team because the replication code should then be
++  updated (to store more bytes on disk).
++
++  NOTE: When adding new SQL_MODE types, make sure to also add them to
++  ../scripts/mysql_create_system_tables.sh and
++  ../scripts/mysql_fix_privilege_tables.sql
++*/
++
++#define RAID_BLOCK_SIZE 1024
++
++#define MY_CHARSET_BIN_MB_MAXLEN 1
++
++// uncachable cause
++#define UNCACHEABLE_DEPENDENT   1
++#define UNCACHEABLE_RAND        2
++#define UNCACHEABLE_SIDEEFFECT	4
++// forcing to save JOIN for explain
++#define UNCACHEABLE_EXPLAIN     8
++/* Don't evaluate subqueries in prepare even if they're not correlated */
++#define UNCACHEABLE_PREPARE    16
++
++#ifdef EXTRA_DEBUG
++/*
++  Sync points allow us to force the server to reach a certain line of code
++  and block there until the client tells the server it is ok to go on.
++  The client tells the server to block with SELECT GET_LOCK()
++  and unblocks it with SELECT RELEASE_LOCK(). Used for debugging difficult
++  concurrency problems
++*/
++#define DBUG_SYNC_POINT(lock_name,lock_timeout) \
++ debug_sync_point(lock_name,lock_timeout)
++void debug_sync_point(const char* lock_name, uint lock_timeout);
++#else
++#define DBUG_SYNC_POINT(lock_name,lock_timeout)
++#endif /* EXTRA_DEBUG */
++
++/* BINLOG_DUMP options */
++
++#define BINLOG_DUMP_NON_BLOCK   1
++
++/* sql_show.cc:show_log_files() */
++#define SHOW_LOG_STATUS_FREE "FREE"
++#define SHOW_LOG_STATUS_INUSE "IN USE"
++
++struct st_table_list;
++class String;
++void view_store_options(THD *thd, st_table_list *table, String *buff);
++
++/* Options to add_table_to_list() */
++#define TL_OPTION_UPDATING	1
++#define TL_OPTION_FORCE_INDEX	2
++#define TL_OPTION_IGNORE_LEAVES 4
++#define TL_OPTION_ALIAS         8
++
++/* Some portable defines */
++
++#define portable_sizeof_char_ptr 8
++
++#define tmp_file_prefix "#sql"			/* Prefix for tmp tables */
++#define tmp_file_prefix_length 4
++
++/* Flags for calc_week() function.  */
++#define WEEK_MONDAY_FIRST    1
++#define WEEK_YEAR            2
++#define WEEK_FIRST_WEEKDAY   4
++
++#define STRING_BUFFER_USUAL_SIZE 80
++
++enum enum_parsing_place
++{
++  NO_MATTER,
++  IN_HAVING,
++  SELECT_LIST,
++  IN_WHERE,
++  IN_ON
++};
++
++struct st_table;
++class THD;
++
++/* Struct to handle simple linked lists */
++
++typedef struct st_sql_list {
++  uint elements;
++  byte *first;
++  byte **next;
++
++  st_sql_list() {}                              /* Remove gcc warning */
++  inline void empty()
++  {
++    elements=0;
++    first=0;
++    next= &first;
++  }
++  inline void link_in_list(byte *element,byte **next_ptr)
++  {
++    elements++;
++    (*next)=element;
++    next= next_ptr;
++    *next=0;
++  }
++  inline void save_and_clear(struct st_sql_list *save)
++  {
++    *save= *this;
++    empty();
++  }
++  inline void push_front(struct st_sql_list *save)
++  {
++    *save->next= first;				/* link current list last */
++    first= save->first;
++    elements+= save->elements;
++  }
++  inline void push_back(struct st_sql_list *save)
++  {
++    if (save->first)
++    {
++      *next= save->first;
++      next= save->next;
++      elements+= save->elements;
++    }
++  }
++} SQL_LIST;
++
++
++extern pthread_key(THD*, THR_THD);
++inline THD *_current_thd(void)
++{
++  return my_pthread_getspecific_ptr(THD*,THR_THD);
++}
++#define current_thd _current_thd()
++
++/*
++  External variables
++*/
++extern ulong server_id, concurrency;
++
++
++typedef my_bool (*qc_engine_callback)(THD *thd, char *table_key,
++                                      uint key_length,
++                                      ulonglong *engine_data);
++#include "sql_string.h"
++#include "sql_list.h"
++#include "sql_map.h"
++#include "my_decimal.h"
++#include "handler.h"
++#include "parse_file.h"
++#include "table.h"
++#include "sql_error.h"
++#include "field.h"				/* Field definitions */
++#include "protocol.h"
++#include "sql_udf.h"
++class user_var_entry;
++class Security_context;
++enum enum_var_type
++{
++  OPT_DEFAULT= 0, OPT_SESSION, OPT_GLOBAL
++};
++class sys_var;
++class Comp_creator;
++typedef Comp_creator* (*chooser_compare_func_creator)(bool invert);
++#include "item.h"
++extern my_decimal decimal_zero;
++
++/* sql_parse.cc */
++void free_items(Item *item);
++void cleanup_items(Item *item);
++class THD;
++void close_thread_tables(THD *thd, bool locked=0, bool skip_derived=0);
++bool check_one_table_access(THD *thd, ulong privilege,
++			   TABLE_LIST *tables);
++bool check_single_table_access(THD *thd, ulong privilege,
++			   TABLE_LIST *tables);
++bool check_routine_access(THD *thd,ulong want_access,char *db,char *name,
++			  bool is_proc, bool no_errors);
++bool check_some_access(THD *thd, ulong want_access, TABLE_LIST *table);
++bool check_merge_table_access(THD *thd, char *db,
++			      TABLE_LIST *table_list);
++bool check_some_routine_access(THD *thd, const char *db, const char *name, bool is_proc);
++bool multi_update_precheck(THD *thd, TABLE_LIST *tables);
++bool multi_delete_precheck(THD *thd, TABLE_LIST *tables);
++bool mysql_multi_update_prepare(THD *thd);
++bool mysql_multi_delete_prepare(THD *thd);
++bool mysql_insert_select_prepare(THD *thd);
++bool update_precheck(THD *thd, TABLE_LIST *tables);
++bool delete_precheck(THD *thd, TABLE_LIST *tables);
++bool insert_precheck(THD *thd, TABLE_LIST *tables);
++bool create_table_precheck(THD *thd, TABLE_LIST *tables,
++                           TABLE_LIST *create_table);
++int append_query_string(CHARSET_INFO *csinfo,
++                        String const *from, String *to);
++
++void get_default_definer(THD *thd, LEX_USER *definer);
++LEX_USER *create_default_definer(THD *thd);
++LEX_USER *create_definer(THD *thd, LEX_STRING *user_name, LEX_STRING *host_name);
++LEX_USER *get_current_user(THD *thd, LEX_USER *user);
++bool check_string_length(LEX_STRING *str,
++                         const char *err_msg, uint max_length);
++
++enum enum_mysql_completiontype {
++  ROLLBACK_RELEASE=-2, ROLLBACK=1,  ROLLBACK_AND_CHAIN=7,
++  COMMIT_RELEASE=-1,   COMMIT=0,    COMMIT_AND_CHAIN=6
++};
++
++int end_trans(THD *thd, enum enum_mysql_completiontype completion);
++
++Item *negate_expression(THD *thd, Item *expr);
++#include "sql_class.h"
++#include "sql_acl.h"
++#include "tztime.h"
++#include "opt_range.h"
++
++#ifdef HAVE_QUERY_CACHE
++struct Query_cache_query_flags
++{
++  unsigned int client_long_flag:1;
++  unsigned int client_protocol_41:1;
++  unsigned int more_results_exists:1;
++  unsigned int pkt_nr;
++  uint character_set_client_num;
++  uint character_set_results_num;
++  uint collation_connection_num;
++  ha_rows limit;
++  Time_zone *time_zone;
++  ulong sql_mode;
++  ulong max_sort_length;
++  ulong group_concat_max_len;
++  MY_LOCALE *lc_time_names;
++};
++#define QUERY_CACHE_FLAGS_SIZE sizeof(Query_cache_query_flags)
++#include "sql_cache.h"
++#define query_cache_store_query(A, B) query_cache.store_query(A, B)
++#define query_cache_destroy() query_cache.destroy()
++#define query_cache_result_size_limit(A) query_cache.result_size_limit(A)
++#define query_cache_init() query_cache.init()
++#define query_cache_resize(A) query_cache.resize(A)
++#define query_cache_set_min_res_unit(A) query_cache.set_min_res_unit(A)
++#define query_cache_invalidate3(A, B, C) query_cache.invalidate(A, B, C)
++#define query_cache_invalidate1(A) query_cache.invalidate(A)
++#define query_cache_send_result_to_client(A, B, C) \
++  query_cache.send_result_to_client(A, B, C)
++#define query_cache_invalidate_by_MyISAM_filename_ref \
++  &query_cache_invalidate_by_MyISAM_filename
++#else
++#define QUERY_CACHE_FLAGS_SIZE 0
++#define query_cache_store_query(A, B)
++#define query_cache_destroy()
++#define query_cache_result_size_limit(A)
++#define query_cache_init()
++#define query_cache_resize(A)
++#define query_cache_set_min_res_unit(A)
++#define query_cache_invalidate3(A, B, C)
++#define query_cache_invalidate1(A)
++#define query_cache_send_result_to_client(A, B, C) 0
++#define query_cache_invalidate_by_MyISAM_filename_ref NULL
++
++#define query_cache_abort(A)
++#define query_cache_end_of_result(A)
++#define query_cache_invalidate_by_MyISAM_filename_ref NULL
++#endif /*HAVE_QUERY_CACHE*/
++
++bool mysql_create_db(THD *thd, char *db, HA_CREATE_INFO *create, bool silent);
++bool mysql_alter_db(THD *thd, const char *db, HA_CREATE_INFO *create);
++bool mysql_rm_db(THD *thd,char *db,bool if_exists, bool silent);
++void mysql_binlog_send(THD* thd, char* log_ident, my_off_t pos, ushort flags);
++bool mysql_rm_table(THD *thd,TABLE_LIST *tables, my_bool if_exists,
++                    my_bool drop_temporary);
++int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists,
++			 bool drop_temporary, bool drop_view, bool log_query);
++int mysql_rm_table_part2_with_lock(THD *thd, TABLE_LIST *tables,
++				   bool if_exists, bool drop_temporary,
++				   bool log_query);
++int quick_rm_table(enum db_type base,const char *db,
++		   const char *table_name);
++void close_cached_table(THD *thd, TABLE *table);
++bool mysql_rename_tables(THD *thd, TABLE_LIST *table_list);
++bool do_rename(THD *thd, TABLE_LIST *ren_table, char *new_db,
++                      char *new_table_name, char *new_table_alias,
++                      bool skip_error);
++bool mysql_change_db(THD *thd,const char *name,bool no_access_check);
++void mysql_parse(THD *thd,char *inBuf,uint length);
++bool mysql_test_parse_for_slave(THD *thd,char *inBuf,uint length);
++bool is_update_query(enum enum_sql_command command);
++bool alloc_query(THD *thd, const char *packet, uint packet_length);
++void mysql_init_select(LEX *lex);
++void mysql_reset_thd_for_next_command(THD *thd);
++void mysql_init_query(THD *thd, uchar *buf, uint length);
++bool mysql_new_select(LEX *lex, bool move_down);
++void create_select_for_variable(const char *var_name);
++void mysql_init_multi_delete(LEX *lex);
++bool multi_delete_set_locks_and_link_aux_tables(LEX *lex);
++void init_max_user_conn(void);
++void init_update_queries(void);
++void free_max_user_conn(void);
++pthread_handler_t handle_one_connection(void *arg);
++pthread_handler_t handle_bootstrap(void *arg);
++void end_thread(THD *thd,bool put_in_cache);
++void flush_thread_cache();
++bool mysql_execute_command(THD *thd);
++bool do_command(THD *thd);
++bool dispatch_command(enum enum_server_command command, THD *thd,
++		      char* packet, uint packet_length);
++void log_slow_statement(THD *thd);
++bool check_dup(const char *db, const char *name, TABLE_LIST *tables);
++
++bool table_cache_init(void);
++void table_cache_free(void);
++uint cached_tables(void);
++void kill_mysql(void);
++void close_connection(THD *thd, uint errcode, bool lock);
++bool reload_acl_and_cache(THD *thd, ulong options, TABLE_LIST *tables, 
++                          bool *write_to_binlog);
++bool check_access(THD *thd, ulong access, const char *db, ulong *save_priv,
++		  bool no_grant, bool no_errors, bool schema_db);
++bool check_table_access(THD *thd, ulong want_access, TABLE_LIST *tables,
++			bool no_errors);
++bool check_global_access(THD *thd, ulong want_access);
++
++bool mysql_backup_table(THD* thd, TABLE_LIST* table_list);
++bool mysql_restore_table(THD* thd, TABLE_LIST* table_list);
++
++bool mysql_checksum_table(THD* thd, TABLE_LIST* table_list,
++                          HA_CHECK_OPT* check_opt);
++bool mysql_check_table(THD* thd, TABLE_LIST* table_list,
++                       HA_CHECK_OPT* check_opt);
++bool mysql_repair_table(THD* thd, TABLE_LIST* table_list,
++                        HA_CHECK_OPT* check_opt);
++bool mysql_analyze_table(THD* thd, TABLE_LIST* table_list,
++                         HA_CHECK_OPT* check_opt);
++bool mysql_optimize_table(THD* thd, TABLE_LIST* table_list,
++                          HA_CHECK_OPT* check_opt);
++bool mysql_assign_to_keycache(THD* thd, TABLE_LIST* table_list,
++                              LEX_STRING *key_cache_name);
++bool mysql_preload_keys(THD* thd, TABLE_LIST* table_list);
++int reassign_keycache_tables(THD* thd, KEY_CACHE *src_cache,
++                             KEY_CACHE *dst_cache);
++TABLE *create_virtual_tmp_table(THD *thd, List<create_field> &field_list);
++
++bool mysql_xa_recover(THD *thd);
++
++bool check_simple_select();
++
++SORT_FIELD * make_unireg_sortorder(ORDER *order, uint *length,
++                                  SORT_FIELD *sortorder);
++int setup_order(THD *thd, Item **ref_pointer_array, TABLE_LIST *tables,
++		List<Item> &fields, List <Item> &all_fields, ORDER *order);
++int setup_group(THD *thd, Item **ref_pointer_array, TABLE_LIST *tables,
++		List<Item> &fields, List<Item> &all_fields, ORDER *order,
++		bool *hidden_group_fields);
++
++bool handle_select(THD *thd, LEX *lex, select_result *result,
++                   ulong setup_tables_done_option);
++bool mysql_select(THD *thd, Item ***rref_pointer_array,
++                  TABLE_LIST *tables, uint wild_num,  List<Item> &list,
++                  COND *conds, uint og_num, ORDER *order, ORDER *group,
++                  Item *having, ORDER *proc_param, ulonglong select_type, 
++                  select_result *result, SELECT_LEX_UNIT *unit, 
++                  SELECT_LEX *select_lex);
++void free_underlaid_joins(THD *thd, SELECT_LEX *select);
++bool mysql_explain_union(THD *thd, SELECT_LEX_UNIT *unit,
++                         select_result *result);
++int mysql_explain_select(THD *thd, SELECT_LEX *sl, char const *type,
++			 select_result *result);
++bool mysql_union(THD *thd, LEX *lex, select_result *result,
++                 SELECT_LEX_UNIT *unit, ulong setup_tables_done_option);
++bool mysql_handle_derived(LEX *lex, bool (*processor)(THD *thd,
++                                                      LEX *lex,
++                                                      TABLE_LIST *table));
++bool mysql_derived_prepare(THD *thd, LEX *lex, TABLE_LIST *t);
++bool mysql_derived_filling(THD *thd, LEX *lex, TABLE_LIST *t);
++Field *create_tmp_field(THD *thd, TABLE *table,Item *item, Item::Type type,
++			Item ***copy_func, Field **from_field,
++                        Field **def_field,
++			bool group, bool modify_item,
++			bool table_cant_handle_bit_fields,
++                        bool make_copy_field,
++                        uint convert_blob_length);
++void sp_prepare_create_field(THD *thd, create_field *sql_field);
++int prepare_create_field(create_field *sql_field, 
++			 uint *blob_columns, 
++			 int *timestamps, int *timestamps_with_niladic,
++			 uint table_flags);
++bool mysql_create_table(THD *thd,const char *db, const char *table_name,
++                        HA_CREATE_INFO *create_info,
++                        List<create_field> &fields, List<Key> &keys,
++                        bool tmp_table, uint select_field_count);
++
++bool mysql_alter_table(THD *thd, char *new_db, char *new_name,
++                       HA_CREATE_INFO *create_info,
++                       TABLE_LIST *table_list,
++                       List<create_field> &fields,
++                       List<Key> &keys,
++                       uint order_num, ORDER *order, bool ignore,
++                       ALTER_INFO *alter_info, bool do_send_ok);
++bool mysql_recreate_table(THD *thd, TABLE_LIST *table_list, bool do_send_ok);
++bool mysql_create_like_table(THD *thd, TABLE_LIST *table,
++                             HA_CREATE_INFO *create_info,
++                             Table_ident *src_table);
++bool mysql_rename_table(enum db_type base,
++			const char *old_db,
++			const char * old_name,
++			const char *new_db,
++			const char * new_name);
++bool mysql_create_index(THD *thd, TABLE_LIST *table_list, List<Key> &keys);
++bool mysql_drop_index(THD *thd, TABLE_LIST *table_list,
++                      ALTER_INFO *alter_info);
++bool mysql_prepare_update(THD *thd, TABLE_LIST *table_list,
++                          Item **conds, uint order_num, ORDER *order);
++int mysql_update(THD *thd,TABLE_LIST *tables,List<Item> &fields,
++		 List<Item> &values,COND *conds,
++		 uint order_num, ORDER *order, ha_rows limit,
++		 enum enum_duplicates handle_duplicates, bool ignore);
++bool mysql_multi_update(THD *thd, TABLE_LIST *table_list,
++                        List<Item> *fields, List<Item> *values,
++                        COND *conds, ulonglong options,
++                        enum enum_duplicates handle_duplicates, bool ignore,
++                        SELECT_LEX_UNIT *unit, SELECT_LEX *select_lex);
++bool mysql_prepare_insert(THD *thd, TABLE_LIST *table_list, TABLE *table,
++                          List<Item> &fields, List_item *values,
++                          List<Item> &update_fields,
++                          List<Item> &update_values, enum_duplicates duplic,
++                          COND **where, bool select_insert);
++bool mysql_insert(THD *thd,TABLE_LIST *table,List<Item> &fields,
++                  List<List_item> &values, List<Item> &update_fields,
++                  List<Item> &update_values, enum_duplicates flag,
++                  bool ignore);
++int check_that_all_fields_are_given_values(THD *thd, TABLE *entry,
++                                           TABLE_LIST *table_list);
++void mark_fields_used_by_triggers_for_insert_stmt(THD *thd, TABLE *table,
++                                                  enum_duplicates duplic);
++bool mysql_prepare_delete(THD *thd, TABLE_LIST *table_list, Item **conds);
++bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
++                  SQL_LIST *order, ha_rows rows, ulonglong options,
++                  bool reset_auto_increment);
++bool mysql_truncate(THD *thd, TABLE_LIST *table_list, bool dont_send_ok);
++bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create);
++TABLE *open_ltable(THD *thd, TABLE_LIST *table_list, thr_lock_type update);
++TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT* mem,
++		  bool *refresh, uint flags);
++bool reopen_name_locked_table(THD* thd, TABLE_LIST* table);
++TABLE *find_locked_table(THD *thd, const char *db,const char *table_name);
++bool reopen_table(TABLE *table,bool locked);
++bool reopen_tables(THD *thd,bool get_locks,bool in_refresh);
++void close_old_data_files(THD *thd, TABLE *table, bool abort_locks,
++			  bool send_refresh);
++bool close_data_tables(THD *thd,const char *db, const char *table_name);
++bool wait_for_tables(THD *thd);
++bool table_is_used(TABLE *table, bool wait_for_name_lock);
++bool drop_locked_tables(THD *thd,const char *db, const char *table_name);
++void abort_locked_tables(THD *thd,const char *db, const char *table_name);
++void execute_init_command(THD *thd, sys_var_str *init_command_var,
++			  rw_lock_t *var_mutex);
++extern Field *not_found_field;
++extern Field *view_ref_found;
++
++enum find_item_error_report_type {REPORT_ALL_ERRORS, REPORT_EXCEPT_NOT_FOUND,
++				  IGNORE_ERRORS, REPORT_EXCEPT_NON_UNIQUE,
++                                  IGNORE_EXCEPT_NON_UNIQUE};
++Field *
++find_field_in_tables(THD *thd, Item_ident *item,
++                     TABLE_LIST *first_table, TABLE_LIST *last_table,
++                     Item **ref, find_item_error_report_type report_error,
++                     bool check_privileges, bool register_tree_change);
++Field *
++find_field_in_table_ref(THD *thd, TABLE_LIST *table_list,
++                        const char *name, uint length,
++                        const char *item_name, const char *db_name,
++                        const char *table_name, Item **ref,
++                        bool check_privileges, bool allow_rowid,
++                        uint *cached_field_index_ptr,
++                        bool register_tree_change, TABLE_LIST **actual_table);
++Field *
++find_field_in_table(THD *thd, TABLE *table, const char *name, uint length,
++                    bool allow_rowid, uint *cached_field_index_ptr);
++
++#ifdef HAVE_OPENSSL
++#include <openssl/des.h>
++struct st_des_keyblock
++{
++  DES_cblock key1, key2, key3;
++};
++struct st_des_keyschedule
++{
++  DES_key_schedule ks1, ks2, ks3;
++};
++extern char *des_key_file;
++extern struct st_des_keyschedule des_keyschedule[10];
++extern uint des_default_key;
++extern pthread_mutex_t LOCK_des_key_file;
++bool load_des_key_file(const char *file_name);
++#endif /* HAVE_OPENSSL */
++
++/* sql_do.cc */
++bool mysql_do(THD *thd, List<Item> &values);
++
++/* sql_analyse.h */
++bool append_escaped(String *to_str, String *from_str);
++
++/* sql_show.cc */
++bool mysqld_show_open_tables(THD *thd,const char *wild);
++bool mysqld_show_logs(THD *thd);
++void append_identifier(THD *thd, String *packet, const char *name,
++		       uint length);
++int get_quote_char_for_identifier(THD *thd, const char *name, uint length);
++void mysqld_list_fields(THD *thd,TABLE_LIST *table, const char *wild);
++int mysqld_dump_create_info(THD *thd, TABLE_LIST *table_list, int fd);
++bool mysqld_show_create(THD *thd, TABLE_LIST *table_list);
++bool mysqld_show_create_db(THD *thd, char *dbname, HA_CREATE_INFO *create);
++
++void mysqld_list_processes(THD *thd,const char *user,bool verbose);
++int mysqld_show_status(THD *thd);
++int mysqld_show_variables(THD *thd,const char *wild);
++bool mysqld_show_storage_engines(THD *thd);
++bool mysqld_show_privileges(THD *thd);
++bool mysqld_show_column_types(THD *thd);
++bool mysqld_help (THD *thd, const char *text);
++void calc_sum_of_all_status(STATUS_VAR *to);
++
++void append_definer(THD *thd, String *buffer, const LEX_STRING *definer_user,
++                    const LEX_STRING *definer_host);
++
++
++/* information schema */
++extern LEX_STRING information_schema_name;
++LEX_STRING *make_lex_string(THD *thd, LEX_STRING *lex_str,
++                            const char* str, uint length,
++                            bool allocate_lex_string);
++ST_SCHEMA_TABLE *find_schema_table(THD *thd, const char* table_name);
++ST_SCHEMA_TABLE *get_schema_table(enum enum_schema_tables schema_table_idx);
++int prepare_schema_table(THD *thd, LEX *lex, Table_ident *table_ident,
++                         enum enum_schema_tables schema_table_idx);
++int make_schema_select(THD *thd,  SELECT_LEX *sel,
++                       enum enum_schema_tables schema_table_idx);
++int mysql_schema_table(THD *thd, LEX *lex, TABLE_LIST *table_list);
++int fill_schema_user_privileges(THD *thd, TABLE_LIST *tables, COND *cond);
++int fill_schema_schema_privileges(THD *thd, TABLE_LIST *tables, COND *cond);
++int fill_schema_table_privileges(THD *thd, TABLE_LIST *tables, COND *cond);
++int fill_schema_column_privileges(THD *thd, TABLE_LIST *tables, COND *cond);
++bool get_schema_tables_result(JOIN *join,
++                              enum enum_schema_table_state executed_place);
++#define is_schema_db(X) \
++  !my_strcasecmp(system_charset_info, information_schema_name.str, (X))
++
++/* sql_prepare.cc */
++
++void mysql_stmt_prepare(THD *thd, const char *packet, uint packet_length);
++void mysql_stmt_execute(THD *thd, char *packet, uint packet_length);
++void mysql_stmt_close(THD *thd, char *packet);
++void mysql_sql_stmt_prepare(THD *thd);
++void mysql_sql_stmt_execute(THD *thd);
++void mysql_sql_stmt_close(THD *thd);
++void mysql_stmt_fetch(THD *thd, char *packet, uint packet_length);
++void mysql_stmt_reset(THD *thd, char *packet);
++void mysql_stmt_get_longdata(THD *thd, char *pos, ulong packet_length);
++void reinit_stmt_before_use(THD *thd, LEX *lex);
++
++/* sql_handler.cc */
++bool mysql_ha_open(THD *thd, TABLE_LIST *tables, bool reopen);
++bool mysql_ha_close(THD *thd, TABLE_LIST *tables);
++bool mysql_ha_read(THD *, TABLE_LIST *,enum enum_ha_read_modes,char *,
++                   List<Item> *,enum ha_rkey_function,Item *,ha_rows,ha_rows);
++int mysql_ha_flush(THD *thd, TABLE_LIST *tables, uint mode_flags,
++                   bool is_locked);
++void mysql_ha_mark_tables_for_reopen(THD *thd, TABLE *table);
++/* mysql_ha_flush mode_flags bits */
++#define MYSQL_HA_CLOSE_FINAL        0x00
++#define MYSQL_HA_REOPEN_ON_USAGE    0x01
++#define MYSQL_HA_FLUSH_ALL          0x02
++
++/* sql_base.cc */
++#define TMP_TABLE_KEY_EXTRA 8
++void set_item_name(Item *item,char *pos,uint length);
++bool add_field_to_list(THD *thd, char *field_name, enum enum_field_types type,
++		       char *length, char *decimal,
++		       uint type_modifier,
++		       Item *default_value, Item *on_update_value,
++		       LEX_STRING *comment,
++		       char *change, List<String> *interval_list,
++		       CHARSET_INFO *cs,
++		       uint uint_geom_type);
++create_field * new_create_field(THD *thd, char *field_name, enum_field_types type,
++				char *length, char *decimals,
++				uint type_modifier, 
++				Item *default_value, Item *on_update_value,
++				LEX_STRING *comment, char *change, 
++				List<String> *interval_list, CHARSET_INFO *cs,
++				uint uint_geom_type);
++void store_position_for_column(const char *name);
++bool add_to_list(THD *thd, SQL_LIST &list,Item *group,bool asc);
++bool push_new_name_resolution_context(THD *thd,
++                                      TABLE_LIST *left_op,
++                                      TABLE_LIST *right_op);
++void add_join_on(TABLE_LIST *b,Item *expr);
++void add_join_natural(TABLE_LIST *a,TABLE_LIST *b,List<String> *using_fields);
++bool add_proc_to_list(THD *thd, Item *item);
++TABLE *unlink_open_table(THD *thd,TABLE *list,TABLE *find);
++void update_non_unique_table_error(TABLE_LIST *update,
++                                   const char *operation,
++                                   TABLE_LIST *duplicate);
++
++SQL_SELECT *make_select(TABLE *head, table_map const_tables,
++			table_map read_tables, COND *conds,
++                        bool allow_null_cond,  int *error);
++extern Item **not_found_item;
++Item ** find_item_in_list(Item *item, List<Item> &items, uint *counter,
++                          find_item_error_report_type report_error,
++                          bool *unaliased);
++bool get_key_map_from_key_list(key_map *map, TABLE *table,
++                               List<String> *index_list);
++bool insert_fields(THD *thd, Name_resolution_context *context,
++		   const char *db_name, const char *table_name,
++                   List_iterator<Item> *it, bool any_privileges);
++bool setup_tables(THD *thd, Name_resolution_context *context,
++                  List<TABLE_LIST> *from_clause, TABLE_LIST *tables,
++                  Item **conds, TABLE_LIST **leaves, bool select_insert);
++bool setup_tables_and_check_access (THD *thd, 
++                                    Name_resolution_context *context,
++                                    List<TABLE_LIST> *from_clause, 
++                                    TABLE_LIST *tables, Item **conds, 
++                                    TABLE_LIST **leaves, 
++                                    bool select_insert,
++                                    ulong want_access_first,
++                                    ulong want_access);
++int setup_wild(THD *thd, TABLE_LIST *tables, List<Item> &fields,
++	       List<Item> *sum_func_list, uint wild_num);
++bool setup_fields(THD *thd, Item** ref_pointer_array,
++                  List<Item> &item, bool set_query_id,
++                  List<Item> *sum_func_list, bool allow_sum_func);
++inline bool setup_fields_with_no_wrap(THD *thd, Item **ref_pointer_array,
++                                     List<Item> &item, bool set_query_id,
++                                     List<Item> *sum_func_list,
++                                     bool allow_sum_func)
++{
++  bool res;
++  thd->lex->select_lex.no_wrap_view_item= TRUE;
++  res= setup_fields(thd, ref_pointer_array, item, set_query_id, sum_func_list,
++                    allow_sum_func);
++  thd->lex->select_lex.no_wrap_view_item= FALSE;
++  return res;
++}
++int setup_conds(THD *thd, TABLE_LIST *tables, TABLE_LIST *leaves,
++		COND **conds);
++int setup_ftfuncs(SELECT_LEX* select);
++int init_ftfuncs(THD *thd, SELECT_LEX* select, bool no_order);
++void wait_for_refresh(THD *thd);
++int open_tables(THD *thd, TABLE_LIST **tables, uint *counter, uint flags);
++int simple_open_n_lock_tables(THD *thd,TABLE_LIST *tables);
++bool open_and_lock_tables(THD *thd,TABLE_LIST *tables);
++bool open_normal_and_derived_tables(THD *thd, TABLE_LIST *tables, uint flags);
++int lock_tables(THD *thd, TABLE_LIST *tables, uint counter, bool *need_reopen);
++TABLE *open_temporary_table(THD *thd, const char *path, const char *db,
++			    const char *table_name, bool link_in_list);
++bool rm_temporary_table(enum db_type base, char *path);
++void free_io_cache(TABLE *entry);
++void intern_close_table(TABLE *entry);
++bool close_thread_table(THD *thd, TABLE **table_ptr);
++void close_temporary_tables(THD *thd);
++void close_tables_for_reopen(THD *thd, TABLE_LIST **tables);
++TABLE_LIST *find_table_in_list(TABLE_LIST *table,
++                               st_table_list *TABLE_LIST::*link,
++                               const char *db_name,
++                               const char *table_name);
++TABLE_LIST *unique_table(THD *thd, TABLE_LIST *table, TABLE_LIST *table_list);
++TABLE **find_temporary_table(THD *thd, const char *db, const char *table_name);
++bool close_temporary_table(THD *thd, const char *db, const char *table_name);
++void close_temporary(TABLE *table, bool delete_table);
++bool rename_temporary_table(THD* thd, TABLE *table, const char *new_db,
++			    const char *table_name);
++void remove_db_from_cache(const char *db);
++void flush_tables();
++bool is_equal(const LEX_STRING *a, const LEX_STRING *b);
++
++/* bits for last argument to remove_table_from_cache() */
++#define RTFC_NO_FLAG                0x0000
++#define RTFC_OWNED_BY_THD_FLAG      0x0001
++#define RTFC_WAIT_OTHER_THREAD_FLAG 0x0002
++#define RTFC_CHECK_KILLED_FLAG      0x0004
++bool remove_table_from_cache(THD *thd, const char *db, const char *table,
++                             uint flags);
++
++bool close_cached_tables(THD *thd, bool wait_for_refresh, TABLE_LIST *tables);
++void copy_field_from_tmp_record(Field *field,int offset);
++bool fill_record(THD *thd, Field **field, List<Item> &values,
++                 bool ignore_errors);
++bool fill_record_n_invoke_before_triggers(THD *thd, List<Item> &fields,
++                                          List<Item> &values,
++                                          bool ignore_errors,
++                                          Table_triggers_list *triggers,
++                                          enum trg_event_type event);
++bool fill_record_n_invoke_before_triggers(THD *thd, Field **field,
++                                          List<Item> &values,
++                                          bool ignore_errors,
++                                          Table_triggers_list *triggers,
++                                          enum trg_event_type event);
++OPEN_TABLE_LIST *list_open_tables(THD *thd, const char *db, const char *wild);
++
++inline TABLE_LIST *find_table_in_global_list(TABLE_LIST *table,
++                                             const char *db_name,
++                                             const char *table_name)
++{
++  return find_table_in_list(table, &TABLE_LIST::next_global,
++                            db_name, table_name);
++}
++
++inline TABLE_LIST *find_table_in_local_list(TABLE_LIST *table,
++                                            const char *db_name,
++                                            const char *table_name)
++{
++  return find_table_in_list(table, &TABLE_LIST::next_local,
++                            db_name, table_name);
++}
++
++
++/* sql_calc.cc */
++bool eval_const_cond(COND *cond);
++
++/* sql_load.cc */
++bool mysql_load(THD *thd, sql_exchange *ex, TABLE_LIST *table_list,
++	        List<Item> &fields_vars, List<Item> &set_fields,
++                List<Item> &set_values_list,
++                enum enum_duplicates handle_duplicates, bool ignore,
++                bool local_file);
++int write_record(THD *thd, TABLE *table, COPY_INFO *info);
++
++/* sql_manager.cc */
++/* bits set in manager_status */
++#define MANAGER_BERKELEY_LOG_CLEANUP    (1L << 0)
++extern ulong volatile manager_status;
++extern bool volatile manager_thread_in_use, mqh_used;
++extern pthread_t manager_thread;
++pthread_handler_t handle_manager(void *arg);
++
++/* sql_test.cc */
++#ifndef DBUG_OFF
++void print_where(COND *cond,const char *info);
++void print_cached_tables(void);
++void TEST_filesort(SORT_FIELD *sortorder,uint s_length);
++void print_plan(JOIN* join,uint idx, double record_count, double read_time,
++                double current_read_time, const char *info);
++#endif
++void mysql_print_status();
++/* key.cc */
++int find_ref_key(TABLE *form,Field *field, uint *offset);
++void key_copy(byte *to_key, byte *from_record, KEY *key_info, uint key_length);
++void key_restore(byte *to_record, byte *from_key, KEY *key_info,
++                 uint key_length);
++bool key_cmp_if_same(TABLE *form,const byte *key,uint index,uint key_length);
++void key_unpack(String *to,TABLE *form,uint index);
++bool is_key_used(TABLE *table, uint idx, List<Item> &fields);
++int key_cmp(KEY_PART_INFO *key_part, const byte *key, uint key_length);
++
++bool init_errmessage(void);
++void sql_perror(const char *message);
++
++void vprint_msg_to_log(enum loglevel level, const char *format, va_list args);
++void sql_print_error(const char *format, ...) ATTRIBUTE_FORMAT(printf, 1, 2);
++void sql_print_warning(const char *format, ...) ATTRIBUTE_FORMAT(printf, 1, 2);
++void sql_print_information(const char *format, ...)
++  ATTRIBUTE_FORMAT(printf, 1, 2);
++
++
++bool fn_format_relative_to_data_home(my_string to, const char *name,
++				     const char *dir, const char *extension);
++File open_binlog(IO_CACHE *log, const char *log_file_name,
++                 const char **errmsg);
++
++/* mysqld.cc */
++extern void MYSQLerror(const char*);
++void refresh_status(THD *thd);
++
++/* item_func.cc */
++extern bool check_reserved_words(LEX_STRING *name);
++
++/* strfunc.cc */
++ulonglong find_set(TYPELIB *lib, const char *x, uint length, CHARSET_INFO *cs,
++		   char **err_pos, uint *err_len, bool *set_warning);
++uint find_type(TYPELIB *lib, const char *find, uint length, bool part_match);
++uint find_type2(TYPELIB *lib, const char *find, uint length, CHARSET_INFO *cs);
++void unhex_type2(TYPELIB *lib);
++uint check_word(TYPELIB *lib, const char *val, const char *end,
++		const char **end_of_word);
++
++
++bool is_keyword(const char *name, uint len);
++
++#define MY_DB_OPT_FILE "db.opt"
++bool check_db_dir_existence(const char *db_name);
++bool load_db_opt(THD *thd, const char *path, HA_CREATE_INFO *create);
++bool load_db_opt_by_name(THD *thd, const char *db_name,
++                         HA_CREATE_INFO *db_create_info);
++bool my_dbopt_init(void);
++void my_dbopt_cleanup(void);
++void my_dbopt_free(void);
++
++/*
++  External variables
++*/
++
++extern time_t start_time;
++extern char *mysql_data_home,server_version[SERVER_VERSION_LENGTH],
++	    mysql_real_data_home[], *opt_mysql_tmpdir, mysql_charsets_dir[],
++            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 const char *first_keyword, *my_localhost, *delayed_user, *binary_keyword;
++extern const char **errmesg;			/* Error messages */
++extern const char *myisam_recover_options_str;
++extern const char *in_left_expr_name, *in_additional_cond;
++extern const char * const triggers_file_ext;
++extern const char * const trigname_file_ext;
++extern Eq_creator eq_creator;
++extern Ne_creator ne_creator;
++extern Gt_creator gt_creator;
++extern Lt_creator lt_creator;
++extern Ge_creator ge_creator;
++extern Le_creator le_creator;
++extern char language[FN_REFLEN], reg_ext[FN_EXTLEN];
++extern char glob_hostname[FN_REFLEN], mysql_home[FN_REFLEN];
++extern char pidfile_name[FN_REFLEN], system_time_zone[30], *opt_init_file;
++extern char log_error_file[FN_REFLEN], *opt_tc_log_file;
++extern double log_10[32];
++extern ulonglong log_10_int[20];
++extern ulonglong keybuff_size;
++extern ulonglong thd_startup_options;
++extern ulong refresh_version,flush_version, thread_id;
++extern ulong binlog_cache_use, binlog_cache_disk_use;
++extern ulong aborted_threads,aborted_connects;
++extern ulong delayed_insert_timeout;
++extern ulong delayed_insert_limit, delayed_queue_size;
++extern ulong delayed_insert_threads, delayed_insert_writes;
++extern ulong delayed_rows_in_use,delayed_insert_errors;
++extern ulong slave_open_temp_tables;
++extern ulong query_cache_size, query_cache_min_res_unit;
++extern ulong slow_launch_threads, slow_launch_time;
++extern ulong table_cache_size;
++extern ulong max_connections,max_connect_errors, connect_timeout;
++extern ulong slave_net_timeout, slave_trans_retries;
++extern uint max_user_connections;
++extern ulong what_to_log,flush_time;
++extern ulong query_buff_size, thread_stack;
++extern ulong max_prepared_stmt_count, prepared_stmt_count;
++extern ulong binlog_cache_size, max_binlog_cache_size, open_files_limit;
++extern ulong max_binlog_size, max_relay_log_size;
++extern ulong rpl_recovery_rank, thread_cache_size;
++extern ulong back_log;
++extern ulong specialflag, current_pid;
++extern ulong expire_logs_days, sync_binlog_period, sync_binlog_counter;
++extern ulong opt_tc_log_size, tc_log_max_pages_used, tc_log_page_size;
++extern ulong tc_log_page_waits;
++extern my_bool relay_log_purge, opt_innodb_safe_binlog, opt_innodb;
++extern uint test_flags,select_errors,ha_open_options;
++extern uint protocol_version, mysqld_port, dropping_tables;
++extern uint delay_key_write_options, lower_case_table_names;
++extern bool opt_endinfo, using_udf_functions;
++extern my_bool locked_in_memory;
++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 my_bool opt_log_queries_not_using_indexes;
++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;
++extern uint volatile thread_count, thread_running, global_read_lock;
++extern my_bool opt_sql_bin_update, opt_safe_user_create, opt_no_mix_types;
++extern my_bool opt_safe_show_db, opt_local_infile;
++extern my_bool opt_slave_compressed_protocol, use_temp_pool;
++extern my_bool opt_readonly, lower_case_file_system;
++extern my_bool opt_enable_named_pipe, opt_sync_frm, opt_allow_suspicious_udfs;
++extern my_bool opt_secure_auth;
++extern my_bool opt_log_slow_admin_statements;
++extern my_bool sp_automatic_privileges, opt_noacl;
++extern my_bool opt_old_style_user_limits, trust_function_creators;
++extern uint opt_crash_binlog_innodb;
++extern char *shared_memory_base_name, *mysqld_unix_port;
++extern my_bool opt_enable_shared_memory;
++extern char *default_tz_name;
++extern my_bool opt_large_pages;
++extern uint opt_large_page_size;
++
++extern MYSQL_LOG mysql_log,mysql_slow_log,mysql_bin_log;
++extern FILE *bootstrap_file;
++extern int bootstrap_error;
++extern FILE *stderror_file;
++extern pthread_key(MEM_ROOT**,THR_MALLOC);
++extern pthread_mutex_t LOCK_mysql_create_db,LOCK_Acl,LOCK_open,
++       LOCK_thread_count,LOCK_mapped_file,LOCK_user_locks, LOCK_status,
++       LOCK_error_log, LOCK_delayed_insert, LOCK_uuid_generator,
++       LOCK_delayed_status, LOCK_delayed_create, LOCK_crypt, LOCK_timezone,
++       LOCK_slave_list, LOCK_active_mi, LOCK_manager, LOCK_global_read_lock,
++       LOCK_global_system_variables, LOCK_user_conn,
++       LOCK_prepared_stmt_count,
++       LOCK_bytes_sent, LOCK_bytes_received;
++#ifdef HAVE_OPENSSL
++extern pthread_mutex_t LOCK_des_key_file;
++#endif
++extern rw_lock_t LOCK_grant, LOCK_sys_init_connect, LOCK_sys_init_slave;
++extern pthread_cond_t COND_refresh, COND_thread_count, COND_manager;
++extern pthread_cond_t COND_global_read_lock;
++extern pthread_attr_t connection_attrib;
++extern I_List<THD> threads;
++extern I_List<NAMED_LIST> key_caches;
++extern MY_BITMAP temp_pool;
++extern String my_empty_string;
++extern const String my_null_string;
++extern SHOW_VAR init_vars[],status_vars[], internal_vars[];
++extern struct system_variables global_system_variables;
++extern struct system_variables max_system_variables;
++extern struct system_status_var global_status_var;
++extern struct rand_struct sql_rand;
++
++extern const char *opt_date_time_formats[];
++extern KNOWN_DATE_TIME_FORMAT known_date_time_formats[];
++
++extern String null_string;
++extern HASH open_cache;
++extern TABLE *unused_tables;
++extern I_List<i_string> binlog_do_db, binlog_ignore_db;
++extern const char* any_db;
++extern struct my_option my_long_options[];
++extern const LEX_STRING view_type;
++
++/* optional things, have_* variables */
++
++#ifdef HAVE_INNOBASE_DB
++extern handlerton innobase_hton;
++#define have_innodb innobase_hton.state
++#else
++extern SHOW_COMP_OPTION have_innodb;
++#endif
++#ifdef HAVE_BERKELEY_DB
++extern handlerton berkeley_hton;
++#define have_berkeley_db berkeley_hton.state
++#else
++extern SHOW_COMP_OPTION have_berkeley_db;
++#endif
++#ifdef HAVE_EXAMPLE_DB
++extern handlerton example_hton;
++#define have_example_db example_hton.state
++#else
++extern SHOW_COMP_OPTION have_example_db;
++#endif
++#ifdef HAVE_ARCHIVE_DB
++extern handlerton archive_hton;
++#define have_archive_db archive_hton.state
++#else
++extern SHOW_COMP_OPTION have_archive_db;
++#endif
++#ifdef HAVE_CSV_DB
++extern handlerton tina_hton;
++#define have_csv_db tina_hton.state
++#else
++extern SHOW_COMP_OPTION have_csv_db;
++#endif
++#ifdef HAVE_FEDERATED_DB
++extern handlerton federated_hton;
++#define have_federated_db federated_hton.state
++#else
++extern SHOW_COMP_OPTION have_federated_db;
++#endif
++#ifdef HAVE_BLACKHOLE_DB
++extern handlerton blackhole_hton;
++#define have_blackhole_db blackhole_hton.state
++#else
++extern SHOW_COMP_OPTION have_blackhole_db;
++#endif
++#ifdef HAVE_NDBCLUSTER_DB
++extern handlerton ndbcluster_hton;
++#define have_ndbcluster ndbcluster_hton.state
++#else
++extern SHOW_COMP_OPTION have_ndbcluster;
++#endif
++
++/* MRG_MYISAM handler is always built, but may be skipped */
++extern handlerton myisammrg_hton;
++#define have_merge_db myisammrg_hton.state
++
++extern SHOW_COMP_OPTION have_isam;
++extern SHOW_COMP_OPTION have_raid, have_openssl, have_symlink, have_dlopen;
++extern SHOW_COMP_OPTION have_query_cache;
++extern SHOW_COMP_OPTION have_geometry, have_rtree_keys;
++extern SHOW_COMP_OPTION have_crypt;
++extern SHOW_COMP_OPTION have_compress;
++
++#ifndef __WIN__
++extern pthread_t signal_thread;
++#endif
++
++#ifdef HAVE_OPENSSL
++extern struct st_VioSSLFd * ssl_acceptor_fd;
++#endif /* HAVE_OPENSSL */
++
++MYSQL_LOCK *mysql_lock_tables(THD *thd, TABLE **table, uint count,
++                              uint flags, bool *need_reopen);
++/* mysql_lock_tables() and open_table() flags bits */
++#define MYSQL_LOCK_IGNORE_GLOBAL_READ_LOCK      0x0001
++#define MYSQL_LOCK_IGNORE_FLUSH                 0x0002
++#define MYSQL_LOCK_NOTIFY_IF_NEED_REOPEN        0x0004
++#define MYSQL_OPEN_IGNORE_LOCKED_TABLES         0x0008
++
++void mysql_unlock_tables(THD *thd, MYSQL_LOCK *sql_lock);
++void mysql_unlock_read_tables(THD *thd, MYSQL_LOCK *sql_lock);
++void mysql_unlock_some_tables(THD *thd, TABLE **table,uint count);
++void mysql_lock_remove(THD *thd, MYSQL_LOCK *locked,TABLE *table);
++void mysql_lock_abort(THD *thd, TABLE *table);
++bool mysql_lock_abort_for_thread(THD *thd, TABLE *table);
++MYSQL_LOCK *mysql_lock_merge(MYSQL_LOCK *a,MYSQL_LOCK *b);
++TABLE_LIST *mysql_lock_have_duplicate(THD *thd, TABLE_LIST *needle,
++                                      TABLE_LIST *haystack);
++bool lock_global_read_lock(THD *thd);
++void unlock_global_read_lock(THD *thd);
++bool wait_if_global_read_lock(THD *thd, bool abort_on_refresh,
++                              bool is_not_commit);
++void start_waiting_global_read_lock(THD *thd);
++bool make_global_read_lock_block_commit(THD *thd);
++bool set_protect_against_global_read_lock(void);
++void unset_protect_against_global_read_lock(void);
++void broadcast_refresh(void);
++
++/* Lock based on name */
++int lock_and_wait_for_table_name(THD *thd, TABLE_LIST *table_list);
++int lock_table_name(THD *thd, TABLE_LIST *table_list);
++void unlock_table_name(THD *thd, TABLE_LIST *table_list);
++bool wait_for_locked_table_names(THD *thd, TABLE_LIST *table_list);
++bool lock_table_names(THD *thd, TABLE_LIST *table_list);
++void unlock_table_names(THD *thd, TABLE_LIST *table_list,
++			TABLE_LIST *last_table);
++
++
++/* old unireg functions */
++
++void unireg_init(ulong options);
++void unireg_end(void);
++bool mysql_create_frm(THD *thd, my_string file_name,
++                      const char *db, const char *table,
++		      HA_CREATE_INFO *create_info,
++		      List<create_field> &create_field,
++		      uint key_count,KEY *key_info,handler *db_type);
++int rea_create_table(THD *thd, my_string file_name,
++                     const char *db, const char *table,
++                     HA_CREATE_INFO *create_info,
++		     List<create_field> &create_field,
++		     uint key_count,KEY *key_info);
++int format_number(uint inputflag,uint max_length,my_string pos,uint length,
++		  my_string *errpos);
++int openfrm(THD *thd, const char *name,const char *alias,uint filestat,
++            uint prgflag, uint ha_open_flags, TABLE *outparam);
++int readfrm(const char *name, const void** data, uint* length);
++int writefrm(const char* name, const void* data, uint len);
++int closefrm(TABLE *table);
++int read_string(File file, gptr *to, uint length);
++void free_blobs(TABLE *table);
++int set_zone(int nr,int min_zone,int max_zone);
++ulong convert_period_to_month(ulong period);
++ulong convert_month_to_period(ulong month);
++void get_date_from_daynr(long daynr,uint *year, uint *month,
++			 uint *day);
++my_time_t TIME_to_timestamp(THD *thd, const TIME *t, my_bool *not_exist);
++bool str_to_time_with_warn(const char *str,uint length,TIME *l_time);
++timestamp_type str_to_datetime_with_warn(const char *str, uint length,
++                                         TIME *l_time, uint flags);
++void localtime_to_TIME(TIME *to, struct tm *from);
++void calc_time_from_sec(TIME *to, long seconds, long microseconds);
++
++void make_truncated_value_warning(THD *thd, const char *str_val,
++				  uint str_length, timestamp_type time_type,
++                                  const char *field_name);
++extern DATE_TIME_FORMAT *date_time_format_make(timestamp_type format_type,
++					       const char *format_str,
++					       uint format_length);
++extern DATE_TIME_FORMAT *date_time_format_copy(THD *thd,
++					       DATE_TIME_FORMAT *format);
++const char *get_date_time_format_str(KNOWN_DATE_TIME_FORMAT *format,
++				     timestamp_type type);
++extern bool make_date_time(DATE_TIME_FORMAT *format, TIME *l_time,
++			   timestamp_type type, String *str);
++void make_datetime(const DATE_TIME_FORMAT *format, const TIME *l_time,
++                   String *str);
++void make_date(const DATE_TIME_FORMAT *format, const TIME *l_time,
++               String *str);
++void make_time(const DATE_TIME_FORMAT *format, const TIME *l_time,
++               String *str);
++
++int test_if_number(char *str,int *res,bool allow_wildcards);
++void change_byte(byte *,uint,char,char);
++void init_read_record(READ_RECORD *info, THD *thd, TABLE *reg_form,
++		      SQL_SELECT *select,
++		      int use_record_cache, bool print_errors);
++void init_read_record_idx(READ_RECORD *info, THD *thd, TABLE *table, 
++                          bool print_error, uint idx);
++void end_read_record(READ_RECORD *info);
++ha_rows filesort(THD *thd, TABLE *form,struct st_sort_field *sortorder,
++		 uint s_length, SQL_SELECT *select,
++		 ha_rows max_rows, ha_rows *examined_rows);
++void filesort_free_buffers(TABLE *table, bool full);
++void change_double_for_sort(double nr,byte *to);
++double my_double_round(double value, int dec, bool truncate);
++int get_quick_record(SQL_SELECT *select);
++int calc_weekday(long daynr,bool sunday_first_day_of_week);
++uint calc_week(TIME *l_time, uint week_behaviour, uint *year);
++void find_date(char *pos,uint *vek,uint flag);
++TYPELIB *convert_strings_to_array_type(my_string *typelibs, my_string *end);
++TYPELIB *typelib(MEM_ROOT *mem_root, List<String> &strings);
++ulong get_form_pos(File file, uchar *head, TYPELIB *save_names);
++ulong make_new_entry(File file,uchar *fileinfo,TYPELIB *formnames,
++		     const char *newname);
++ulong next_io_size(ulong pos);
++void append_unescaped(String *res, const char *pos, uint length);
++int create_frm(THD *thd, char *name, const char *db, const char *table,
++               uint reclength,uchar *fileinfo,
++	       HA_CREATE_INFO *create_info, uint keys);
++void update_create_info_from_table(HA_CREATE_INFO *info, TABLE *form);
++int rename_file_ext(const char * from,const char * to,const char * ext);
++bool check_db_name(char *db);
++bool check_column_name(const char *name);
++bool check_table_name(const char *name, uint length);
++char *get_field(MEM_ROOT *mem, Field *field);
++bool get_field(MEM_ROOT *mem, Field *field, class String *res);
++int wild_case_compare(CHARSET_INFO *cs, const char *str,const char *wildstr);
++
++/* from hostname.cc */
++struct in_addr;
++my_string ip_to_hostname(struct in_addr *in,uint *errors);
++void inc_host_errors(struct in_addr *in);
++void reset_host_errors(struct in_addr *in);
++bool hostname_cache_init();
++void hostname_cache_free();
++void hostname_cache_refresh(void);
++
++/* sql_cache.cc */
++extern bool sql_cache_init();
++extern void sql_cache_free();
++extern int sql_cache_hit(THD *thd, char *inBuf, uint length);
++
++/* item_func.cc */
++Item *get_system_var(THD *thd, enum_var_type var_type, LEX_STRING name,
++		     LEX_STRING component);
++int get_var_with_binlog(THD *thd, enum_sql_command sql_command,
++                        LEX_STRING &name, user_var_entry **out_entry);
++/* log.cc */
++bool flush_error_log(void);
++
++/* sql_list.cc */
++void free_list(I_List <i_string_pair> *list);
++void free_list(I_List <i_string> *list);
++
++/* sql_yacc.cc */
++extern int MYSQLparse(void *thd);
++#ifndef DBUG_OFF
++extern void turn_parser_debug_on();
++#endif
++
++/* frm_crypt.cc */
++#ifdef HAVE_CRYPTED_FRM
++SQL_CRYPT *get_crypt_for_frm(void);
++#endif
++
++#include "sql_view.h"
++
++/* Some inline functions for more speed */
++
++inline bool add_item_to_list(THD *thd, Item *item)
++{
++  return thd->lex->current_select->add_item_to_list(thd, item);
++}
++
++inline bool add_value_to_list(THD *thd, Item *value)
++{
++  return thd->lex->value_list.push_back(value);
++}
++
++inline bool add_order_to_list(THD *thd, Item *item, bool asc)
++{
++  return thd->lex->current_select->add_order_to_list(thd, item, asc);
++}
++
++inline bool add_group_to_list(THD *thd, Item *item, bool asc)
++{
++  return thd->lex->current_select->add_group_to_list(thd, item, asc);
++}
++
++inline void mark_as_null_row(TABLE *table)
++{
++  table->null_row=1;
++  table->status|=STATUS_NULL_ROW;
++  bfill(table->null_flags,table->s->null_bytes,255);
++}
++
++inline void table_case_convert(char * name, uint length)
++{
++  if (lower_case_table_names)
++    files_charset_info->cset->casedn(files_charset_info,
++                                     name, length, name, length);
++}
++
++inline const char *table_case_name(HA_CREATE_INFO *info, const char *name)
++{
++  return ((lower_case_table_names == 2 && info->alias) ? info->alias : name);
++}
++
++inline ulong sql_rnd_with_mutex()
++{
++  pthread_mutex_lock(&LOCK_thread_count);
++  ulong tmp=(ulong) (my_rnd(&sql_rand) * 0xffffffff); /* make all bits random */
++  pthread_mutex_unlock(&LOCK_thread_count);
++  return tmp;
++}
++
++Comp_creator *comp_eq_creator(bool invert);
++Comp_creator *comp_ge_creator(bool invert);
++Comp_creator *comp_gt_creator(bool invert);
++Comp_creator *comp_le_creator(bool invert);
++Comp_creator *comp_lt_creator(bool invert);
++Comp_creator *comp_ne_creator(bool invert);
++
++Item * all_any_subquery_creator(Item *left_expr,
++				chooser_compare_func_creator cmp,
++				bool all,
++				SELECT_LEX *select_lex);
++
++/*
++  clean/setup table fields and map
++
++  SYNOPSYS
++    setup_table_map()
++    table - TABLE structure pointer (which should be setup)
++    table_list TABLE_LIST structure pointer (owner of TABLE)
++    tablenr - table number
++*/
++
++inline void setup_table_map(TABLE *table, TABLE_LIST *table_list, uint tablenr)
++{
++  table->used_fields= 0;
++  table->const_table= 0;
++  table->null_row= 0;
++  table->status= STATUS_NO_RECORD;
++  table->keys_in_use_for_query= table->s->keys_in_use;
++  table->maybe_null= table_list->outer_join;
++  TABLE_LIST *embedding= table_list->embedding;
++  while (!table->maybe_null && embedding)
++  {
++    table->maybe_null= embedding->outer_join;
++    embedding= embedding->embedding;
++  }
++  table->tablenr= tablenr;
++  table->map= (table_map) 1 << tablenr;
++  table->force_index= table_list->force_index;
++}
++
++
++/*
++  SYNOPSYS
++    hexchar_to_int()
++    convert a hex digit into number
++*/
++
++inline int hexchar_to_int(char c)
++{
++  if (c <= '9' && c >= '0')
++    return c-'0';
++  c|=32;
++  if (c <= 'f' && c >= 'a')
++    return c-'a'+10;
++  return -1;
++}
++
++/*
++  is_user_table()
++  return true if the table was created explicitly
++*/
++
++inline bool is_user_table(TABLE * table)
++{
++  const char *name= table->s->table_name;
++  return strncmp(name, tmp_file_prefix, tmp_file_prefix_length);
++}
++
++/*
++  Some functions that are different in the embedded library and the normal
++  server
++*/
++
++#ifndef EMBEDDED_LIBRARY
++extern "C" void unireg_abort(int exit_code);
++void kill_delayed_threads(void);
++bool check_stack_overrun(THD *thd, long margin, char *dummy);
++#else
++#define unireg_abort(exit_code) DBUG_RETURN(exit_code)
++inline void kill_delayed_threads(void) {}
++#define check_stack_overrun(A, B, C) 0
++#endif
++
++#endif /* MYSQL_CLIENT */
+diff -urNad mysql-5.0-etch~/sql/sql_parse.cc mysql-5.0-etch/sql/sql_parse.cc
+--- mysql-5.0-etch~/sql/sql_parse.cc	2007-05-28 18:56:15.000000000 +0200
++++ mysql-5.0-etch/sql/sql_parse.cc	2007-05-28 19:12:52.000000000 +0200
+@@ -2213,7 +2213,8 @@
+                          enum enum_schema_tables schema_table_idx)
+ {
+   DBUG_ENTER("prepare_schema_table");
+-  SELECT_LEX *sel= 0;
++  SELECT_LEX *schema_select_lex= NULL;
++
+   switch (schema_table_idx) {
+   case SCH_SCHEMATA:
+ #if defined(DONT_ALLOW_SHOW_COMMANDS)
+@@ -2221,11 +2222,9 @@
+                ER(ER_NOT_ALLOWED_COMMAND), MYF(0));   /* purecov: inspected */
+     DBUG_RETURN(1);
+ #else
+-    if ((specialflag & SPECIAL_SKIP_SHOW_DB) &&
+-	check_global_access(thd, SHOW_DB_ACL))
+-      DBUG_RETURN(1);
+     break;
+ #endif
++
+   case SCH_TABLE_NAMES:
+   case SCH_TABLES:
+   case SCH_VIEWS:
+@@ -2235,32 +2234,25 @@
+                ER(ER_NOT_ALLOWED_COMMAND), MYF(0)); /* purecov: inspected */
+     DBUG_RETURN(1);
+ #else
++    if (lex->select_lex.db == NULL &&
++        thd->copy_db_to(&lex->select_lex.db, NULL))
+     {
+-      char *db;
+-      if (lex->select_lex.db == NULL &&
+-          thd->copy_db_to(&lex->select_lex.db, 0))
+-      {
+-        DBUG_RETURN(1);
+-      }
+-      db= lex->select_lex.db;
+-      remove_escape(db);				// Fix escaped '_'
+-      if (check_db_name(db))
+-      {
+-        my_error(ER_WRONG_DB_NAME, MYF(0), db);
+-        DBUG_RETURN(1);
+-      }
+-      if (check_access(thd, SELECT_ACL, db, &thd->col_access, 0, 0,
+-                       is_schema_db(db)))
+-        DBUG_RETURN(1);			        /* purecov: inspected */
+-      if (!thd->col_access && check_grant_db(thd,db))
+-      {
+-	my_error(ER_DBACCESS_DENIED_ERROR, MYF(0),
+-                 thd->security_ctx->priv_user, thd->security_ctx->priv_host,
+-                 db);
+-	DBUG_RETURN(1);
+-      }
+-      break;
++      DBUG_RETURN(1);
++    }
++
++    schema_select_lex= new SELECT_LEX();
++    schema_select_lex->db= lex->select_lex.db;
++    schema_select_lex->table_list.first= NULL;
++    remove_escape(schema_select_lex->db); // Fix escaped '_'
++
++    if (check_db_name(schema_select_lex->db))
++    {
++      my_error(ER_WRONG_DB_NAME, MYF(0), schema_select_lex->db);
++      DBUG_RETURN(1);
+     }
++
++
++    break;
+ #endif
+   case SCH_COLUMNS:
+   case SCH_STATISTICS:
+@@ -2269,28 +2261,23 @@
+                ER(ER_NOT_ALLOWED_COMMAND), MYF(0)); /* purecov: inspected */
+     DBUG_RETURN(1);
+ #else
+-    if (table_ident)
+     {
++      DBUG_ASSERT(table_ident);
++
+       TABLE_LIST **query_tables_last= lex->query_tables_last;
+-      sel= new SELECT_LEX();
++      schema_select_lex= new SELECT_LEX();
+       /* 'parent_lex' is used in init_query() so it must be before it. */
+-      sel->parent_lex= lex;
+-      sel->init_query();
+-      if (!sel->add_table_to_list(thd, table_ident, 0, 0, TL_READ, 
+-                                 (List<String> *) 0, (List<String> *) 0))
++      schema_select_lex->parent_lex= lex;
++      schema_select_lex->init_query();
++      if (!schema_select_lex->add_table_to_list(thd, table_ident, 0, 0, TL_READ,
++                                  (List<String> *) 0, (List<String> *) 0))
+         DBUG_RETURN(1);
+       lex->query_tables_last= query_tables_last;
+-      TABLE_LIST *table_list= (TABLE_LIST*) sel->table_list.first;
+-      char *db= table_list->db;
+-      remove_escape(db);			// Fix escaped '_'
+-      remove_escape(table_list->table_name);
+-      if (check_access(thd,SELECT_ACL | EXTRA_ACL,db,
+-                       &table_list->grant.privilege, 0, 0,
+-                       test(table_list->schema_table)))
+-        DBUG_RETURN(1);				/* purecov: inspected */
+-      if (grant_option && check_grant(thd, SELECT_ACL, table_list, 2,
+-                                      UINT_MAX, 0))
+-        DBUG_RETURN(1);
++
++      TABLE_LIST *dst_table= (TABLE_LIST*) schema_select_lex->table_list.first;
++      remove_escape(dst_table->db);			// Fix escaped '_'
++      remove_escape(dst_table->table_name);
++
+       break;
+     }
+ #endif
+@@ -2317,7 +2304,7 @@
+     DBUG_RETURN(1);
+   }
+   TABLE_LIST *table_list= (TABLE_LIST*) select_lex->table_list.first;
+-  table_list->schema_select_lex= sel;
++  table_list->schema_select_lex= schema_select_lex;
+   table_list->schema_table_reformed= 1;
+   statistic_increment(thd->status_var.com_stat[lex->orig_sql_command],
+                       &LOCK_status);
+@@ -5272,6 +5259,83 @@
+ }
+ 
+ 
++static bool check_show_access(THD *thd, TABLE_LIST *table)
++{
++  switch (get_schema_table_idx(table->schema_table))
++  {
++  case SCH_SCHEMATA:
++    return (specialflag & SPECIAL_SKIP_SHOW_DB) &&
++           check_global_access(thd, SHOW_DB_ACL);
++
++  case SCH_TABLE_NAMES:
++  case SCH_TABLES:
++  case SCH_VIEWS:
++  case SCH_TRIGGERS:
++    {
++      const char *dst_db_name= table->schema_select_lex->db;
++
++      DBUG_ASSERT(dst_db_name);
++
++      if (check_access(thd, SELECT_ACL, dst_db_name,
++                       &thd->col_access, FALSE, FALSE,
++                       is_schema_db(dst_db_name)))
++      {
++        return TRUE;
++      }
++
++      if (!thd->col_access && check_grant_db(thd, dst_db_name))
++      {
++        my_error(ER_DBACCESS_DENIED_ERROR, MYF(0),
++                 thd->security_ctx->priv_user,
++                 thd->security_ctx->priv_host,
++                 dst_db_name);
++        return TRUE;
++      }
++
++      return FALSE;
++    }
++
++  case SCH_COLUMNS:
++  case SCH_STATISTICS:
++    {
++      TABLE_LIST *dst_table=
++        (TABLE_LIST *) table->schema_select_lex->table_list.first;
++
++      DBUG_ASSERT(dst_table);
++
++      if (check_access(thd, SELECT_ACL | EXTRA_ACL,
++                       dst_table->db,
++                       &dst_table->grant.privilege,
++                       FALSE, FALSE,
++                       test(dst_table->schema_table)))
++      {
++        return FALSE;
++      }
++
++      return grant_option &&
++             check_grant(thd, SELECT_ACL, dst_table, 2, UINT_MAX, FALSE);
++    }
++
++  case SCH_OPEN_TABLES:
++  case SCH_VARIABLES:
++  case SCH_STATUS:
++  case SCH_PROCEDURES:
++  case SCH_CHARSETS:
++  case SCH_COLLATIONS:
++  case SCH_COLLATION_CHARACTER_SET_APPLICABILITY:
++  case SCH_USER_PRIVILEGES:
++  case SCH_SCHEMA_PRIVILEGES:
++  case SCH_TABLE_PRIVILEGES:
++  case SCH_COLUMN_PRIVILEGES:
++  case SCH_TABLE_CONSTRAINTS:
++  case SCH_KEY_COLUMN_USAGE:
++    break;
++  }
++
++  return FALSE;
++}
++
++
+ /*
+   Check the privilege for all used tables.
+ 
+@@ -5330,7 +5394,16 @@
+        Remove SHOW_VIEW_ACL, because it will be checked during making view
+      */
+     tables->grant.orig_want_privilege= (want_access & ~SHOW_VIEW_ACL);
+-    if (tables->derived || tables->schema_table ||
++
++    if (tables->schema_table_reformed)
++    {
++      if (check_show_access(thd, tables))
++        goto deny;
++
++      continue;
++    }
++
++    if (tables->derived ||
+         (tables->table && (int)tables->table->s->tmp_table) ||
+         my_tz_check_n_skip_implicit_tables(&tables,
+                                            thd->lex->time_zone_tables_used))
+diff -urNad mysql-5.0-etch~/sql/sql_parse.cc.orig mysql-5.0-etch/sql/sql_parse.cc.orig
+--- mysql-5.0-etch~/sql/sql_parse.cc.orig	1970-01-01 01:00:00.000000000 +0100
++++ mysql-5.0-etch/sql/sql_parse.cc.orig	2007-05-28 18:56:15.000000000 +0200
+@@ -0,0 +1,7572 @@
++/* Copyright (C) 2000-2003 MySQL AB
++
++   This program is free software; you can redistribute it and/or modify
++   it under the terms of the GNU General Public License as published by
++   the Free Software Foundation; either version 2 of the License, or
++   (at your option) any later version.
++
++   This program is distributed in the hope that it will be useful,
++   but WITHOUT ANY WARRANTY; without even the implied warranty of
++   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++   GNU General Public License for more details.
++
++   You should have received a copy of the GNU General Public License
++   along with this program; if not, write to the Free Software
++   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */
++
++#define MYSQL_LEX 1
++#include "mysql_priv.h"
++#include "sql_repl.h"
++#include "repl_failsafe.h"
++#include <m_ctype.h>
++#include <myisam.h>
++#include <my_dir.h>
++
++#ifdef HAVE_INNOBASE_DB
++#include "ha_innodb.h"
++#endif
++
++#ifdef HAVE_NDBCLUSTER_DB
++#include "ha_ndbcluster.h"
++#endif
++
++#include "sp_head.h"
++#include "sp.h"
++#include "sp_cache.h"
++
++#ifdef HAVE_OPENSSL
++/*
++  Without SSL the handshake consists of one packet. This packet
++  has both client capabilites and scrambled password.
++  With SSL the handshake might consist of two packets. If the first
++  packet (client capabilities) has CLIENT_SSL flag set, we have to
++  switch to SSL and read the second packet. The scrambled password
++  is in the second packet and client_capabilites field will be ignored.
++  Maybe it is better to accept flags other than CLIENT_SSL from the
++  second packet?
++*/
++#define SSL_HANDSHAKE_SIZE      2
++#define NORMAL_HANDSHAKE_SIZE   6
++#define MIN_HANDSHAKE_SIZE      2
++#else
++#define MIN_HANDSHAKE_SIZE      6
++#endif /* HAVE_OPENSSL */
++
++/* Used in error handling only */
++#define SP_TYPE_STRING(LP) \
++  ((LP)->sphead->m_type == TYPE_ENUM_FUNCTION ? "FUNCTION" : "PROCEDURE")
++#define SP_COM_STRING(LP) \
++  ((LP)->sql_command == SQLCOM_CREATE_SPFUNCTION || \
++   (LP)->sql_command == SQLCOM_ALTER_FUNCTION || \
++   (LP)->sql_command == SQLCOM_SHOW_CREATE_FUNC || \
++   (LP)->sql_command == SQLCOM_DROP_FUNCTION ? \
++   "FUNCTION" : "PROCEDURE")
++
++#ifdef SOLARIS
++extern "C" int gethostname(char *name, int namelen);
++#endif
++
++static void time_out_user_resource_limits(THD *thd, USER_CONN *uc);
++#ifndef NO_EMBEDDED_ACCESS_CHECKS
++static int check_for_max_user_connections(THD *thd, USER_CONN *uc);
++static void decrease_user_connections(USER_CONN *uc);
++#endif /* NO_EMBEDDED_ACCESS_CHECKS */
++static bool check_db_used(THD *thd,TABLE_LIST *tables);
++static bool check_multi_update_lock(THD *thd);
++static void remove_escape(char *name);
++static bool append_file_to_dir(THD *thd, const char **filename_ptr,
++			       const char *table_name);
++
++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",
++  "Error"					// Last command number
++};
++
++const char *xa_state_names[]={
++  "NON-EXISTING", "ACTIVE", "IDLE", "PREPARED"
++};
++
++#ifdef __WIN__
++static void  test_signal(int sig_ptr)
++{
++#if !defined( DBUG_OFF)
++  MessageBox(NULL,"Test signal","DBUG",MB_OK);
++#endif
++#if defined(OS2)
++  fprintf(stderr, "Test signal %d\n", sig_ptr);
++  fflush(stderr);
++#endif
++}
++static void init_signals(void)
++{
++  int signals[7] = {SIGINT,SIGILL,SIGFPE,SIGSEGV,SIGTERM,SIGBREAK,SIGABRT } ;
++  for (int i=0 ; i < 7 ; i++)
++    signal( signals[i], test_signal) ;
++}
++#endif
++
++static void unlock_locked_tables(THD *thd)
++{
++  if (thd->locked_tables)
++  {
++    thd->lock=thd->locked_tables;
++    thd->locked_tables=0;			// Will be automatically closed
++    close_thread_tables(thd);			// Free tables
++  }
++}
++
++
++static bool end_active_trans(THD *thd)
++{
++  int error=0;
++  DBUG_ENTER("end_active_trans");
++  if (unlikely(thd->in_sub_stmt))
++  {
++    my_error(ER_COMMIT_NOT_ALLOWED_IN_SF_OR_TRG, MYF(0));
++    DBUG_RETURN(1);
++  }
++  if (thd->transaction.xid_state.xa_state != XA_NOTR)
++  {
++    my_error(ER_XAER_RMFAIL, MYF(0),
++             xa_state_names[thd->transaction.xid_state.xa_state]);
++    DBUG_RETURN(1);
++  }
++  if (thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN |
++		      OPTION_TABLE_LOCK))
++  {
++    DBUG_PRINT("info",("options: 0x%lx", (ulong) thd->options));
++    /* Safety if one did "drop table" on locked tables */
++    if (!thd->locked_tables)
++      thd->options&= ~OPTION_TABLE_LOCK;
++    thd->server_status&= ~SERVER_STATUS_IN_TRANS;
++    if (ha_commit(thd))
++      error=1;
++    thd->options&= ~(ulong) (OPTION_BEGIN | OPTION_STATUS_NO_TRANS_UPDATE);
++  }
++  DBUG_RETURN(error);
++}
++
++static bool begin_trans(THD *thd)
++{
++  int error=0;
++  if (unlikely(thd->in_sub_stmt))
++  {
++    my_error(ER_COMMIT_NOT_ALLOWED_IN_SF_OR_TRG, MYF(0));
++    return 1;
++  }
++  if (thd->locked_tables)
++  {
++    thd->lock=thd->locked_tables;
++    thd->locked_tables=0;			// Will be automatically closed
++    close_thread_tables(thd);			// Free tables
++  }
++  if (end_active_trans(thd))
++    error= -1;
++  else
++  {
++    LEX *lex= thd->lex;
++    thd->options= ((thd->options & (ulong) ~(OPTION_STATUS_NO_TRANS_UPDATE)) |
++		   OPTION_BEGIN);
++    thd->server_status|= SERVER_STATUS_IN_TRANS;
++    if (lex->start_transaction_opt & MYSQL_START_TRANS_OPT_WITH_CONS_SNAPSHOT)
++      error= ha_start_consistent_snapshot(thd);
++  }
++  return error;
++}
++
++#ifdef HAVE_REPLICATION
++/*
++  Returns true if all tables should be ignored
++*/
++inline bool all_tables_not_ok(THD *thd, TABLE_LIST *tables)
++{
++  return table_rules_on && tables && !tables_ok(thd,tables);
++}
++#endif
++
++
++static bool some_non_temp_table_to_be_updated(THD *thd, TABLE_LIST *tables)
++{
++  for (TABLE_LIST *table= tables; table; table= table->next_global)
++  {
++    DBUG_ASSERT(table->db && table->table_name);
++    if (table->updating &&
++        !find_temporary_table(thd, table->db, table->table_name))
++      return 1;
++  }
++  return 0;
++}
++
++#ifndef NO_EMBEDDED_ACCESS_CHECKS
++static HASH hash_user_connections;
++
++static int get_or_create_user_conn(THD *thd, const char *user,
++				   const char *host,
++				   USER_RESOURCES *mqh)
++{
++  int return_val= 0;
++  uint temp_len, user_len;
++  char temp_user[USER_HOST_BUFF_SIZE];
++  struct  user_conn *uc;
++
++  DBUG_ASSERT(user != 0);
++  DBUG_ASSERT(host != 0);
++
++  user_len= strlen(user);
++  temp_len= (strmov(strmov(temp_user, user)+1, host) - temp_user)+1;
++  (void) pthread_mutex_lock(&LOCK_user_conn);
++  if (!(uc = (struct  user_conn *) hash_search(&hash_user_connections,
++					       (byte*) temp_user, temp_len)))
++  {
++    /* First connection for user; Create a user connection object */
++    if (!(uc= ((struct user_conn*)
++	       my_malloc(sizeof(struct user_conn) + temp_len+1,
++			 MYF(MY_WME)))))
++    {
++      net_send_error(thd, 0, NullS);		// Out of memory
++      return_val= 1;
++      goto end;
++    }
++    uc->user=(char*) (uc+1);
++    memcpy(uc->user,temp_user,temp_len+1);
++    uc->host= uc->user + user_len +  1;
++    uc->len= temp_len;
++    uc->connections= uc->questions= uc->updates= uc->conn_per_hour= 0;
++    uc->user_resources= *mqh;
++    uc->intime= thd->thr_create_time;
++    if (my_hash_insert(&hash_user_connections, (byte*) uc))
++    {
++      my_free((char*) uc,0);
++      net_send_error(thd, 0, NullS);		// Out of memory
++      return_val= 1;
++      goto end;
++    }
++  }
++  thd->user_connect=uc;
++  uc->connections++;
++end:
++  (void) pthread_mutex_unlock(&LOCK_user_conn);
++  return return_val;
++
++}
++#endif /* !NO_EMBEDDED_ACCESS_CHECKS */
++
++
++/*
++  Check if user exist and password supplied is correct. 
++
++  SYNOPSIS
++    check_user()
++    thd          thread handle, thd->security_ctx->{host,user,ip} are used
++    command      originator of the check: now check_user is called
++                 during connect and change user procedures; used for 
++                 logging.
++    passwd       scrambled password received from client
++    passwd_len   length of scrambled password
++    db           database name to connect to, may be NULL
++    check_count  dont know exactly
++
++    Note, that host, user and passwd may point to communication buffer.
++    Current implementation does not depend on that, but future changes
++    should be done with this in mind; 'thd' is INOUT, all other params
++    are 'IN'.
++
++  RETURN VALUE
++    0  OK; thd->security_ctx->user/master_access/priv_user/db_access and
++       thd->db are updated; OK is sent to client;
++   -1  access denied or handshake error; error is sent to client;
++   >0  error, not sent to client
++*/
++
++int check_user(THD *thd, enum enum_server_command command, 
++	       const char *passwd, uint passwd_len, const char *db,
++	       bool check_count)
++{
++  DBUG_ENTER("check_user");
++  
++#ifdef NO_EMBEDDED_ACCESS_CHECKS
++  thd->main_security_ctx.master_access= GLOBAL_ACLS;       // Full rights
++  /* Change database if necessary */
++  if (db && db[0])
++  {
++    /*
++      thd->db is saved in caller and needs to be freed by caller if this
++      function returns 0
++    */
++    thd->reset_db(NULL, 0);
++    if (mysql_change_db(thd, db, FALSE))
++    {
++      /* Send the error to the client */
++      net_send_error(thd);
++      DBUG_RETURN(-1);
++    }
++  }
++  send_ok(thd);
++  DBUG_RETURN(0);
++#else
++
++  my_bool opt_secure_auth_local;
++  pthread_mutex_lock(&LOCK_global_system_variables);
++  opt_secure_auth_local= opt_secure_auth;
++  pthread_mutex_unlock(&LOCK_global_system_variables);
++  
++  /*
++    If the server is running in secure auth mode, short scrambles are 
++    forbidden.
++  */
++  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));
++    DBUG_RETURN(-1);
++  }
++  if (passwd_len != 0 &&
++      passwd_len != SCRAMBLE_LENGTH &&
++      passwd_len != SCRAMBLE_LENGTH_323)
++    DBUG_RETURN(ER_HANDSHAKE_ERROR);
++
++  /*
++    Clear thd->db as it points to something, that will be freed when 
++    connection is closed. We don't want to accidentally free a wrong pointer
++    if connect failed. Also in case of 'CHANGE USER' failure, current
++    database will be switched to 'no database selected'.
++  */
++  thd->reset_db(NULL, 0);
++
++  USER_RESOURCES ur;
++  int res= acl_getroot(thd, &ur, passwd, passwd_len);
++#ifndef EMBEDDED_LIBRARY
++  if (res == -1)
++  {
++    /*
++      This happens when client (new) sends password scrambled with
++      scramble(), but database holds old value (scrambled with
++      scramble_323()). Here we please client to send scrambled_password
++      in old format.
++    */
++    NET *net= &thd->net;
++    if (opt_secure_auth_local)
++    {
++      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);
++      DBUG_RETURN(-1);
++    }
++    /* We have to read very specific packet size */
++    if (send_old_password_request(thd) ||
++        my_net_read(net) != SCRAMBLE_LENGTH_323 + 1)
++    {
++      inc_host_errors(&thd->remote.sin_addr);
++      DBUG_RETURN(ER_HANDSHAKE_ERROR);
++    }
++    /* Final attempt to check the user based on reply */
++    /* So as passwd is short, errcode is always >= 0 */
++    res= acl_getroot(thd, &ur, (char *) net->read_pos, SCRAMBLE_LENGTH_323);
++  }
++#endif /*EMBEDDED_LIBRARY*/
++  /* here res is always >= 0 */
++  if (res == 0)
++  {
++    if (!(thd->main_security_ctx.master_access &
++          NO_ACCESS)) // authentication is OK
++    {
++      DBUG_PRINT("info",
++                 ("Capabilities: %lu  packet_length: %ld  Host: '%s'  "
++                  "Login user: '%s' Priv_user: '%s'  Using password: %s "
++                  "Access: %lu  db: '%s'",
++                  thd->client_capabilities,
++                  thd->max_client_packet_length,
++                  thd->main_security_ctx.host_or_ip,
++                  thd->main_security_ctx.user,
++                  thd->main_security_ctx.priv_user,
++                  passwd_len ? "yes": "no",
++                  thd->main_security_ctx.master_access,
++                  (thd->db ? thd->db : "*none*")));
++
++      if (check_count)
++      {
++        VOID(pthread_mutex_lock(&LOCK_thread_count));
++        bool count_ok= thread_count <= max_connections + delayed_insert_threads
++                       || (thd->main_security_ctx.master_access & SUPER_ACL);
++        VOID(pthread_mutex_unlock(&LOCK_thread_count));
++        if (!count_ok)
++        {                                         // too many connections
++          net_send_error(thd, ER_CON_COUNT_ERROR);
++          DBUG_RETURN(-1);
++        }
++      }
++
++      /* 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*) "");
++
++      /*
++        This is the default access rights for the current database.  It's
++        set to 0 here because we don't have an active database yet (and we
++        may not have an active database to set.
++      */
++      thd->main_security_ctx.db_access=0;
++
++      /* Don't allow user to connect if he has done too many queries */
++      if ((ur.questions || ur.updates || ur.conn_per_hour || ur.user_conn ||
++	   max_user_connections) &&
++	  get_or_create_user_conn(thd,
++            (opt_old_style_user_limits ? thd->main_security_ctx.user :
++             thd->main_security_ctx.priv_user),
++            (opt_old_style_user_limits ? thd->main_security_ctx.host_or_ip :
++             thd->main_security_ctx.priv_host),
++            &ur))
++	DBUG_RETURN(-1);
++      if (thd->user_connect &&
++	  (thd->user_connect->user_resources.conn_per_hour ||
++	   thd->user_connect->user_resources.user_conn ||
++	   max_user_connections) &&
++	  check_for_max_user_connections(thd, thd->user_connect))
++	DBUG_RETURN(-1);
++
++      /* Change database if necessary */
++      if (db && db[0])
++      {
++        if (mysql_change_db(thd, db, FALSE))
++        {
++          /* Send error to the client */
++          net_send_error(thd);
++          if (thd->user_connect)
++            decrease_user_connections(thd->user_connect);
++          DBUG_RETURN(-1);
++        }
++      }
++      send_ok(thd);
++      thd->password= test(passwd_len);          // remember for error messages 
++      /* Ready to handle queries */
++      DBUG_RETURN(0);
++    }
++  }
++  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));
++    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));
++  DBUG_RETURN(-1);
++#endif /* NO_EMBEDDED_ACCESS_CHECKS */
++}
++
++/*
++  Check for maximum allowable user connections, if the mysqld server is
++  started with corresponding variable that is greater then 0.
++*/
++
++extern "C" byte *get_key_conn(user_conn *buff, uint *length,
++			      my_bool not_used __attribute__((unused)))
++{
++  *length=buff->len;
++  return (byte*) buff->user;
++}
++
++extern "C" void free_user(struct user_conn *uc)
++{
++  my_free((char*) uc,MYF(0));
++}
++
++void init_max_user_conn(void)
++{
++#ifndef NO_EMBEDDED_ACCESS_CHECKS
++  (void) hash_init(&hash_user_connections,system_charset_info,max_connections,
++		   0,0,
++		   (hash_get_key) get_key_conn, (hash_free_key) free_user,
++		   0);
++#endif
++}
++
++
++/*
++  check if user has already too many connections
++  
++  SYNOPSIS
++  check_for_max_user_connections()
++  thd			Thread handle
++  uc			User connect object
++
++  NOTES
++    If check fails, we decrease user connection count, which means one
++    shouldn't call decrease_user_connections() after this function.
++
++  RETURN
++    0	ok
++    1	error
++*/
++
++#ifndef NO_EMBEDDED_ACCESS_CHECKS
++
++static int check_for_max_user_connections(THD *thd, USER_CONN *uc)
++{
++  int error=0;
++  DBUG_ENTER("check_for_max_user_connections");
++
++  (void) pthread_mutex_lock(&LOCK_user_conn);
++  if (max_user_connections && !uc->user_resources.user_conn &&
++      max_user_connections < (uint) uc->connections)
++  {
++    net_printf_error(thd, ER_TOO_MANY_USER_CONNECTIONS, uc->user);
++    error=1;
++    goto end;
++  }
++  time_out_user_resource_limits(thd, uc);
++  if (uc->user_resources.user_conn &&
++      uc->user_resources.user_conn < uc->connections)
++  {
++    net_printf_error(thd, ER_USER_LIMIT_REACHED, uc->user,
++                     "max_user_connections",
++                     (long) uc->user_resources.user_conn);
++    error= 1;
++    goto end;
++  }
++  if (uc->user_resources.conn_per_hour &&
++      uc->user_resources.conn_per_hour <= uc->conn_per_hour)
++  {
++    net_printf_error(thd, ER_USER_LIMIT_REACHED, uc->user,
++                     "max_connections_per_hour",
++                     (long) uc->user_resources.conn_per_hour);
++    error=1;
++    goto end;
++  }
++  uc->conn_per_hour++;
++
++  end:
++  if (error)
++    uc->connections--; // no need for decrease_user_connections() here
++  (void) pthread_mutex_unlock(&LOCK_user_conn);
++  DBUG_RETURN(error);
++}
++
++/*
++  Decrease user connection count
++
++  SYNOPSIS
++    decrease_user_connections()
++    uc			User connection object
++
++  NOTES
++    If there is a n user connection object for a connection
++    (which only happens if 'max_user_connections' is defined or
++    if someone has created a resource grant for a user), then
++    the connection count is always incremented on connect.
++
++    The user connect object is not freed if some users has
++    'max connections per hour' defined as we need to be able to hold
++    count over the lifetime of the connection.
++*/
++
++static void decrease_user_connections(USER_CONN *uc)
++{
++  DBUG_ENTER("decrease_user_connections");
++  (void) pthread_mutex_lock(&LOCK_user_conn);
++  DBUG_ASSERT(uc->connections);
++  if (!--uc->connections && !mqh_used)
++  {
++    /* Last connection for user; Delete it */
++    (void) hash_delete(&hash_user_connections,(byte*) uc);
++  }
++  (void) pthread_mutex_unlock(&LOCK_user_conn);
++  DBUG_VOID_RETURN;
++}
++
++#endif /* NO_EMBEDDED_ACCESS_CHECKS */
++
++
++void free_max_user_conn(void)
++{
++#ifndef NO_EMBEDDED_ACCESS_CHECKS
++  hash_free(&hash_user_connections);
++#endif /* NO_EMBEDDED_ACCESS_CHECKS */
++}
++
++
++
++/*
++  Mark all commands that somehow changes a table
++  This is used to check number of updates / hour
++
++  sql_command is actually set to SQLCOM_END sometimes
++  so we need the +1 to include it in the array.
++
++  numbers are:
++     0  - read-only query
++  != 0  - query that may change a table
++     2  - query that returns meaningful ROW_COUNT() -
++          a number of modified rows
++*/
++
++char  uc_update_queries[SQLCOM_END+1];
++
++void init_update_queries(void)
++{
++  bzero((gptr) &uc_update_queries, sizeof(uc_update_queries));
++
++  uc_update_queries[SQLCOM_CREATE_TABLE]=1;
++  uc_update_queries[SQLCOM_CREATE_INDEX]=1;
++  uc_update_queries[SQLCOM_ALTER_TABLE]=1;
++  uc_update_queries[SQLCOM_UPDATE]=2;
++  uc_update_queries[SQLCOM_UPDATE_MULTI]=2;
++  uc_update_queries[SQLCOM_INSERT]=2;
++  uc_update_queries[SQLCOM_INSERT_SELECT]=2;
++  uc_update_queries[SQLCOM_DELETE]=2;
++  uc_update_queries[SQLCOM_DELETE_MULTI]=2;
++  uc_update_queries[SQLCOM_TRUNCATE]=1;
++  uc_update_queries[SQLCOM_DROP_TABLE]=1;
++  uc_update_queries[SQLCOM_LOAD]=1;
++  uc_update_queries[SQLCOM_CREATE_DB]=1;
++  uc_update_queries[SQLCOM_DROP_DB]=1;
++  uc_update_queries[SQLCOM_REPLACE]=2;
++  uc_update_queries[SQLCOM_REPLACE_SELECT]=2;
++  uc_update_queries[SQLCOM_RENAME_TABLE]=1;
++  uc_update_queries[SQLCOM_BACKUP_TABLE]=1;
++  uc_update_queries[SQLCOM_RESTORE_TABLE]=1;
++  uc_update_queries[SQLCOM_DROP_INDEX]=1;
++  uc_update_queries[SQLCOM_CREATE_VIEW]=1;
++  uc_update_queries[SQLCOM_DROP_VIEW]=1;
++}
++
++bool is_update_query(enum enum_sql_command command)
++{
++  DBUG_ASSERT(command >= 0 && command <= SQLCOM_END);
++  return uc_update_queries[command] != 0;
++}
++
++/*
++  Reset per-hour user resource limits when it has been more than
++  an hour since they were last checked
++
++  SYNOPSIS:
++    time_out_user_resource_limits()
++    thd			Thread handler
++    uc			User connection details
++
++  NOTE:
++    This assumes that the LOCK_user_conn mutex has been acquired, so it is
++    safe to test and modify members of the USER_CONN structure.
++*/
++
++static void time_out_user_resource_limits(THD *thd, USER_CONN *uc)
++{
++  time_t check_time = thd->start_time ?  thd->start_time : time(NULL);
++  DBUG_ENTER("time_out_user_resource_limits");
++
++  /* If more than a hour since last check, reset resource checking */
++  if (check_time  - uc->intime >= 3600)
++  {
++    uc->questions=1;
++    uc->updates=0;
++    uc->conn_per_hour=0;
++    uc->intime=check_time;
++  }
++
++  DBUG_VOID_RETURN;
++}
++
++
++/*
++  Check if maximum queries per hour limit has been reached
++  returns 0 if OK.
++*/
++
++static bool check_mqh(THD *thd, uint check_command)
++{
++#ifndef NO_EMBEDDED_ACCESS_CHECKS
++  bool error= 0;
++  USER_CONN *uc=thd->user_connect;
++  DBUG_ENTER("check_mqh");
++  DBUG_ASSERT(uc != 0);
++
++  (void) pthread_mutex_lock(&LOCK_user_conn);
++
++  time_out_user_resource_limits(thd, uc);
++
++  /* Check that we have not done too many questions / hour */
++  if (uc->user_resources.questions &&
++      uc->questions++ >= uc->user_resources.questions)
++  {
++    net_printf_error(thd, ER_USER_LIMIT_REACHED, uc->user, "max_questions",
++                     (long) uc->user_resources.questions);
++    error=1;
++    goto end;
++  }
++  if (check_command < (uint) SQLCOM_END)
++  {
++    /* Check that we have not done too many updates / hour */
++    if (uc->user_resources.updates && uc_update_queries[check_command] &&
++	uc->updates++ >= uc->user_resources.updates)
++    {
++      net_printf_error(thd, ER_USER_LIMIT_REACHED, uc->user, "max_updates",
++                       (long) uc->user_resources.updates);
++      error=1;
++      goto end;
++    }
++  }
++end:
++  (void) pthread_mutex_unlock(&LOCK_user_conn);
++  DBUG_RETURN(error);
++#else
++  return (0);
++#endif /* NO_EMBEDDED_ACCESS_CHECKS */
++}
++
++
++static void reset_mqh(LEX_USER *lu, bool get_them= 0)
++{
++#ifndef NO_EMBEDDED_ACCESS_CHECKS
++  (void) pthread_mutex_lock(&LOCK_user_conn);
++  if (lu)  // for GRANT
++  {
++    USER_CONN *uc;
++    uint temp_len=lu->user.length+lu->host.length+2;
++    char temp_user[USER_HOST_BUFF_SIZE];
++
++    memcpy(temp_user,lu->user.str,lu->user.length);
++    memcpy(temp_user+lu->user.length+1,lu->host.str,lu->host.length);
++    temp_user[lu->user.length]='\0'; temp_user[temp_len-1]=0;
++    if ((uc = (struct  user_conn *) hash_search(&hash_user_connections,
++						(byte*) temp_user, temp_len)))
++    {
++      uc->questions=0;
++      get_mqh(temp_user,&temp_user[lu->user.length+1],uc);
++      uc->updates=0;
++      uc->conn_per_hour=0;
++    }
++  }
++  else
++  {
++    /* for FLUSH PRIVILEGES and FLUSH USER_RESOURCES */
++    for (uint idx=0;idx < hash_user_connections.records; idx++)
++    {
++      USER_CONN *uc=(struct user_conn *) hash_element(&hash_user_connections,
++						      idx);
++      if (get_them)
++	get_mqh(uc->user,uc->host,uc);
++      uc->questions=0;
++      uc->updates=0;
++      uc->conn_per_hour=0;
++    }
++  }
++  (void) pthread_mutex_unlock(&LOCK_user_conn);
++#endif /* NO_EMBEDDED_ACCESS_CHECKS */
++}
++
++void thd_init_client_charset(THD *thd, uint cs_number)
++{
++  /*
++   Use server character set and collation if
++   - opt_character_set_client_handshake is not set
++   - client has not specified a character set
++   - client character set is the same as the servers
++   - client character set doesn't exists in server
++  */
++  if (!opt_character_set_client_handshake ||
++      !(thd->variables.character_set_client= get_charset(cs_number, MYF(0))) ||
++      !my_strcasecmp(&my_charset_latin1,
++                     global_system_variables.character_set_client->name,
++                     thd->variables.character_set_client->name))
++  {
++    thd->variables.character_set_client=
++      global_system_variables.character_set_client;
++    thd->variables.collation_connection=
++      global_system_variables.collation_connection;
++    thd->variables.character_set_results=
++      global_system_variables.character_set_results;
++  }
++  else
++  {
++    thd->variables.character_set_results=
++      thd->variables.collation_connection= 
++      thd->variables.character_set_client;
++  }
++}
++
++
++/*
++    Perform handshake, authorize client and update thd ACL variables.
++  SYNOPSIS
++    check_connection()
++    thd  thread handle
++
++  RETURN
++     0  success, OK is sent to user, thd is updated.
++    -1  error, which is sent to user
++   > 0  error code (not sent to user)
++*/
++
++#ifndef EMBEDDED_LIBRARY
++static int check_connection(THD *thd)
++{
++  uint connect_errors= 0;
++  NET *net= &thd->net;
++  ulong pkt_len= 0;
++  char *end;
++
++  DBUG_PRINT("info",
++             ("New connection received on %s", vio_description(net->vio)));
++#ifdef SIGNAL_WITH_VIO_CLOSE
++  thd->set_active_vio(net->vio);
++#endif
++
++  if (!thd->main_security_ctx.host)         // If TCP/IP connection
++  {
++    char ip[30];
++
++    if (vio_peer_addr(net->vio, ip, &thd->peer_port))
++      return (ER_BAD_HOST_ERROR);
++    if (!(thd->main_security_ctx.ip= my_strdup(ip,MYF(0))))
++      return (ER_OUT_OF_RESOURCES);
++    thd->main_security_ctx.host_or_ip= thd->main_security_ctx.ip;
++    vio_in_addr(net->vio,&thd->remote.sin_addr);
++    if (!(specialflag & SPECIAL_NO_RESOLVE))
++    {
++      vio_in_addr(net->vio,&thd->remote.sin_addr);
++      thd->main_security_ctx.host=
++        ip_to_hostname(&thd->remote.sin_addr, &connect_errors);
++      /* Cut very long hostnames to avoid possible overflows */
++      if (thd->main_security_ctx.host)
++      {
++        if (thd->main_security_ctx.host != my_localhost)
++          thd->main_security_ctx.host[min(strlen(thd->main_security_ctx.host),
++                                          HOSTNAME_LENGTH)]= 0;
++        thd->main_security_ctx.host_or_ip= thd->main_security_ctx.host;
++      }
++      if (connect_errors > max_connect_errors)
++        return(ER_HOST_IS_BLOCKED);
++    }
++    DBUG_PRINT("info",("Host: %s  ip: %s",
++		       (thd->main_security_ctx.host ?
++                        thd->main_security_ctx.host : "unknown host"),
++		       (thd->main_security_ctx.ip ?
++                        thd->main_security_ctx.ip : "unknown ip")));
++    if (acl_check_host(thd->main_security_ctx.host, thd->main_security_ctx.ip))
++      return(ER_HOST_NOT_PRIVILEGED);
++  }
++  else /* Hostname given means that the connection was on a socket */
++  {
++    DBUG_PRINT("info",("Host: %s", thd->main_security_ctx.host));
++    thd->main_security_ctx.host_or_ip= thd->main_security_ctx.host;
++    thd->main_security_ctx.ip= 0;
++    /* Reset sin_addr */
++    bzero((char*) &thd->remote, sizeof(thd->remote));
++  }
++  vio_keepalive(net->vio, TRUE);
++  {
++    /* buff[] needs to big enough to hold the server_version variable */
++    char buff[SERVER_VERSION_LENGTH + SCRAMBLE_LENGTH + 64];
++    ulong client_flags = (CLIENT_LONG_FLAG | CLIENT_CONNECT_WITH_DB |
++			  CLIENT_PROTOCOL_41 | CLIENT_SECURE_CONNECTION);
++
++    if (opt_using_transactions)
++      client_flags|=CLIENT_TRANSACTIONS;
++#ifdef HAVE_COMPRESS
++    client_flags |= CLIENT_COMPRESS;
++#endif /* HAVE_COMPRESS */
++#ifdef HAVE_OPENSSL
++    if (ssl_acceptor_fd)
++      client_flags |= CLIENT_SSL;       /* Wow, SSL is available! */
++#endif /* HAVE_OPENSSL */
++
++    end= strnmov(buff, server_version, SERVER_VERSION_LENGTH) + 1;
++    int4store((uchar*) end, thd->thread_id);
++    end+= 4;
++    /*
++      So as check_connection is the only entry point to authorization
++      procedure, scramble is set here. This gives us new scramble for
++      each handshake.
++    */
++    create_random_string(thd->scramble, SCRAMBLE_LENGTH, &thd->rand);
++    /*
++      Old clients does not understand long scrambles, but can ignore packet
++      tail: that's why first part of the scramble is placed here, and second
++      part at the end of packet.
++    */
++    end= strmake(end, thd->scramble, SCRAMBLE_LENGTH_323) + 1;
++   
++    int2store(end, client_flags);
++    /* write server characteristics: up to 16 bytes allowed */
++    end[2]=(char) default_charset_info->number;
++    int2store(end+3, thd->server_status);
++    bzero(end+5, 13);
++    end+= 18;
++    /* write scramble tail */
++    end= strmake(end, thd->scramble + SCRAMBLE_LENGTH_323, 
++                 SCRAMBLE_LENGTH - SCRAMBLE_LENGTH_323) + 1;
++
++    /* At this point we write connection message and read reply */
++    if (net_write_command(net, (uchar) protocol_version, "", 0, buff,
++			  (uint) (end-buff)) ||
++	(pkt_len= my_net_read(net)) == packet_error ||
++	pkt_len < MIN_HANDSHAKE_SIZE)
++    {
++      inc_host_errors(&thd->remote.sin_addr);
++      return(ER_HANDSHAKE_ERROR);
++    }
++  }
++#ifdef _CUSTOMCONFIG_
++#include "_cust_sql_parse.h"
++#endif
++  if (connect_errors)
++    reset_host_errors(&thd->remote.sin_addr);
++  if (thd->packet.alloc(thd->variables.net_buffer_length))
++    return(ER_OUT_OF_RESOURCES);
++
++  thd->client_capabilities=uint2korr(net->read_pos);
++  if (thd->client_capabilities & CLIENT_PROTOCOL_41)
++  {
++    thd->client_capabilities|= ((ulong) uint2korr(net->read_pos+2)) << 16;
++    thd->max_client_packet_length= uint4korr(net->read_pos+4);
++    DBUG_PRINT("info", ("client_character_set: %d", (uint) net->read_pos[8]));
++    thd_init_client_charset(thd, (uint) net->read_pos[8]);
++    thd->update_charset();
++    end= (char*) net->read_pos+32;
++  }
++  else
++  {
++    thd->max_client_packet_length= uint3korr(net->read_pos+2);
++    end= (char*) net->read_pos+5;
++  }
++
++  if (thd->client_capabilities & CLIENT_IGNORE_SPACE)
++    thd->variables.sql_mode|= MODE_IGNORE_SPACE;
++#ifdef HAVE_OPENSSL
++  DBUG_PRINT("info", ("client capabilities: %lu", thd->client_capabilities));
++  if (thd->client_capabilities & CLIENT_SSL)
++  {
++    /* Do the SSL layering. */
++    if (!ssl_acceptor_fd)
++    {
++      inc_host_errors(&thd->remote.sin_addr);
++      return(ER_HANDSHAKE_ERROR);
++    }
++    DBUG_PRINT("info", ("IO layer change in progress..."));
++    if (sslaccept(ssl_acceptor_fd, net->vio, thd->variables.net_wait_timeout))
++    {
++      DBUG_PRINT("error", ("Failed to accept new SSL connection"));
++      inc_host_errors(&thd->remote.sin_addr);
++      return(ER_HANDSHAKE_ERROR);
++    }
++    DBUG_PRINT("info", ("Reading user information over SSL layer"));
++    if ((pkt_len= my_net_read(net)) == packet_error ||
++	pkt_len < NORMAL_HANDSHAKE_SIZE)
++    {
++      DBUG_PRINT("error", ("Failed to read user information (pkt_len= %lu)",
++			   pkt_len));
++      inc_host_errors(&thd->remote.sin_addr);
++      return(ER_HANDSHAKE_ERROR);
++    }
++  }
++#endif
++
++  if (end >= (char*) net->read_pos+ pkt_len +2)
++  {
++    inc_host_errors(&thd->remote.sin_addr);
++    return(ER_HANDSHAKE_ERROR);
++  }
++
++  if (thd->client_capabilities & CLIENT_INTERACTIVE)
++    thd->variables.net_wait_timeout= thd->variables.net_interactive_timeout;
++  if ((thd->client_capabilities & CLIENT_TRANSACTIONS) &&
++      opt_using_transactions)
++    net->return_status= &thd->server_status;
++  net->read_timeout=(uint) thd->variables.net_read_timeout;
++
++  char *user= end;
++  char *passwd= strend(user)+1;
++  uint user_len= passwd - user - 1;
++  char *db= passwd;
++  char db_buff[NAME_LEN + 1];           // buffer to store db in utf8
++  char user_buff[USERNAME_LENGTH + 1];	// buffer to store user in utf8
++  uint dummy_errors;
++
++  /*
++    Old clients send null-terminated string as password; new clients send
++    the size (1 byte) + string (not null-terminated). Hence in case of empty
++    password both send '\0'.
++  */
++  uint passwd_len= thd->client_capabilities & CLIENT_SECURE_CONNECTION ?
++    *passwd++ : strlen(passwd);
++  db= thd->client_capabilities & CLIENT_CONNECT_WITH_DB ?
++    db + passwd_len + 1 : 0;
++  uint db_len= db ? strlen(db) : 0;
++
++  if (passwd + passwd_len + db_len > (char *)net->read_pos + pkt_len)
++  {
++    inc_host_errors(&thd->remote.sin_addr);
++    return ER_HANDSHAKE_ERROR;
++  }
++
++  /* Since 4.1 all database names are stored in utf8 */
++  if (db)
++  {
++    db_buff[copy_and_convert(db_buff, sizeof(db_buff)-1,
++                             system_charset_info,
++                             db, db_len,
++                             thd->charset(), &dummy_errors)]= 0;
++    db= db_buff;
++  }
++
++  user_buff[user_len= copy_and_convert(user_buff, sizeof(user_buff)-1,
++                                       system_charset_info, user, user_len,
++                                       thd->charset(), &dummy_errors)]= '\0';
++  user= user_buff;
++
++  /* If username starts and ends in "'", chop them off */
++  if (user_len > 1 && user[0] == '\'' && user[user_len - 1] == '\'')
++  {
++    user[user_len-1]= 0;
++    user++;
++    user_len-= 2;
++  }
++
++  if (thd->main_security_ctx.user)
++    x_free(thd->main_security_ctx.user);
++  if (!(thd->main_security_ctx.user= my_strdup(user, MYF(0))))
++    return (ER_OUT_OF_RESOURCES);
++  return check_user(thd, COM_CONNECT, passwd, passwd_len, db, TRUE);
++}
++
++
++void execute_init_command(THD *thd, sys_var_str *init_command_var,
++			  rw_lock_t *var_mutex)
++{
++  Vio* save_vio;
++  ulong save_client_capabilities;
++
++  thd->proc_info= "Execution of init_command";
++  /*
++    We need to lock init_command_var because
++    during execution of init_command_var query
++    values of init_command_var can't be changed
++  */
++  rw_rdlock(var_mutex);
++  thd->query= init_command_var->value;
++  thd->query_length= init_command_var->value_length;
++  save_client_capabilities= thd->client_capabilities;
++  thd->client_capabilities|= CLIENT_MULTI_QUERIES;
++  /*
++    We don't need return result of execution to client side.
++    To forbid this we should set thd->net.vio to 0.
++  */
++  save_vio= thd->net.vio;
++  thd->net.vio= 0;
++  thd->net.no_send_error= 0;
++  dispatch_command(COM_QUERY, thd, thd->query, thd->query_length+1);
++  rw_unlock(var_mutex);
++  thd->client_capabilities= save_client_capabilities;
++  thd->net.vio= save_vio;
++}
++
++
++pthread_handler_t handle_one_connection(void *arg)
++{
++  THD *thd=(THD*) arg;
++  uint launch_time  =
++    (uint) ((thd->thr_create_time = time(NULL)) - thd->connect_time);
++  if (launch_time >= slow_launch_time)
++    statistic_increment(slow_launch_threads,&LOCK_status );
++
++  pthread_detach_this_thread();
++
++#if !defined( __WIN__) && !defined(OS2)	// Win32 calls this in pthread_create
++  /* The following calls needs to be done before we call DBUG_ macros */
++  if (!(test_flags & TEST_NO_THREADS) & my_thread_init())
++  {
++    close_connection(thd, ER_OUT_OF_RESOURCES, 1);
++    statistic_increment(aborted_connects,&LOCK_status);
++    end_thread(thd,0);
++    return 0;
++  }
++#endif
++
++  /*
++    handle_one_connection() is the only way a thread would start
++    and would always be on top of the stack, therefore, the thread
++    stack always starts at the address of the first local variable
++    of handle_one_connection, which is thd. We need to know the
++    start of the stack so that we could check for stack overruns.
++  */
++  DBUG_PRINT("info", ("handle_one_connection called by thread %lu\n",
++		      thd->thread_id));
++  /* now that we've called my_thread_init(), it is safe to call DBUG_* */
++
++#if defined(__WIN__)
++  init_signals();
++#elif !defined(OS2) && !defined(__NETWARE__)
++  sigset_t set;
++  VOID(sigemptyset(&set));			// Get mask in use
++  VOID(pthread_sigmask(SIG_UNBLOCK,&set,&thd->block_signals));
++#endif
++  thd->thread_stack= (char*) &thd;
++  if (thd->store_globals())
++  {
++    close_connection(thd, ER_OUT_OF_RESOURCES, 1);
++    statistic_increment(aborted_connects,&LOCK_status);
++    end_thread(thd,0);
++    return 0;
++  }
++
++  do
++  {
++    int error;
++    NET *net= &thd->net;
++    Security_context *sctx= thd->security_ctx;
++    net->no_send_error= 0;
++
++    if ((error=check_connection(thd)))
++    {						// Wrong permissions
++      if (error > 0)
++	net_printf_error(thd, error, sctx->host_or_ip);
++#ifdef __NT__
++      if (vio_type(net->vio) == VIO_TYPE_NAMEDPIPE)
++	my_sleep(1000);				/* must wait after eof() */
++#endif
++      statistic_increment(aborted_connects,&LOCK_status);
++      goto end_thread;
++    }
++#ifdef __NETWARE__
++    netware_reg_user(sctx->ip, sctx->user, "MySQL");
++#endif
++    if (thd->variables.max_join_size == HA_POS_ERROR)
++      thd->options |= OPTION_BIG_SELECTS;
++    if (thd->client_capabilities & CLIENT_COMPRESS)
++      net->compress=1;				// Use compression
++
++    thd->version= refresh_version;
++    thd->proc_info= 0;
++    thd->command= COM_SLEEP;
++    thd->set_time();
++    thd->init_for_queries();
++
++    if (sys_init_connect.value_length && !(sctx->master_access & SUPER_ACL))
++    {
++      execute_init_command(thd, &sys_init_connect, &LOCK_sys_init_connect);
++      if (thd->query_error)
++      {
++	thd->killed= THD::KILL_CONNECTION;
++        sql_print_warning(ER(ER_NEW_ABORTING_CONNECTION),
++                          thd->thread_id,(thd->db ? thd->db : "unconnected"),
++                          sctx->user ? sctx->user : "unauthenticated",
++                          sctx->host_or_ip, "init_connect command failed");
++        sql_print_warning("%s", net->last_error);
++      }
++      thd->proc_info=0;
++      thd->set_time();
++      thd->init_for_queries();
++    }
++
++    while (!net->error && net->vio != 0 &&
++           !(thd->killed == THD::KILL_CONNECTION))
++    {
++      net->no_send_error= 0;
++      if (do_command(thd))
++	break;
++    }
++    if (thd->user_connect)
++      decrease_user_connections(thd->user_connect);
++    if (net->error && net->vio != 0 && net->report_error)
++    {
++      if (!thd->killed && thd->variables.log_warnings > 1)
++	sql_print_warning(ER(ER_NEW_ABORTING_CONNECTION),
++                          thd->thread_id,(thd->db ? thd->db : "unconnected"),
++                          sctx->user ? sctx->user : "unauthenticated",
++                          sctx->host_or_ip,
++                          (net->last_errno ? ER(net->last_errno) :
++                           ER(ER_UNKNOWN_ERROR)));
++      net_send_error(thd, net->last_errno, NullS);
++      statistic_increment(aborted_threads,&LOCK_status);
++    }
++    else if (thd->killed)
++    {
++      statistic_increment(aborted_threads,&LOCK_status);
++    }
++    
++end_thread:
++    close_connection(thd, 0, 1);
++    end_thread(thd,1);
++    /*
++      If end_thread returns, we are either running with --one-thread
++      or this thread has been schedule to handle the next query
++    */
++    thd= current_thd;
++    thd->thread_stack= (char*) &thd;
++  } while (!(test_flags & TEST_NO_THREADS));
++  /* The following is only executed if we are not using --one-thread */
++  return(0);					/* purecov: deadcode */
++}
++
++#endif /* EMBEDDED_LIBRARY */
++
++/*
++  Execute commands from bootstrap_file.
++  Used when creating the initial grant tables
++*/
++
++pthread_handler_t handle_bootstrap(void *arg)
++{
++  THD *thd=(THD*) arg;
++  FILE *file=bootstrap_file;
++  char *buff;
++
++  /* The following must be called before DBUG_ENTER */
++  thd->thread_stack= (char*) &thd;
++  if (my_thread_init() || thd->store_globals())
++  {
++#ifndef EMBEDDED_LIBRARY
++    close_connection(thd, ER_OUT_OF_RESOURCES, 1);
++#endif
++    thd->fatal_error();
++    goto end;
++  }
++  DBUG_ENTER("handle_bootstrap");
++
++#ifndef EMBEDDED_LIBRARY
++  pthread_detach_this_thread();
++  thd->thread_stack= (char*) &thd;
++#if !defined(__WIN__) && !defined(OS2) && !defined(__NETWARE__)
++  sigset_t set;
++  VOID(sigemptyset(&set));			// Get mask in use
++  VOID(pthread_sigmask(SIG_UNBLOCK,&set,&thd->block_signals));
++#endif
++#endif /* EMBEDDED_LIBRARY */
++
++  if (thd->variables.max_join_size == HA_POS_ERROR)
++    thd->options |= OPTION_BIG_SELECTS;
++
++  thd->proc_info=0;
++  thd->version=refresh_version;
++  thd->security_ctx->priv_user=
++    thd->security_ctx->user= (char*) my_strdup("boot", MYF(MY_WME));
++  /*
++    Make the "client" handle multiple results. This is necessary
++    to enable stored procedures with SELECTs and Dynamic SQL
++    in init-file.
++  */
++  thd->client_capabilities|= CLIENT_MULTI_RESULTS;
++
++  buff= (char*) thd->net.buff;
++  thd->init_for_queries();
++  while (fgets(buff, thd->net.max_packet, file))
++  {
++   ulong length= (ulong) strlen(buff);
++   while (buff[length-1] != '\n' && !feof(file))
++   {
++     /*
++       We got only a part of the current string. Will try to increase
++       net buffer then read the rest of the current string.
++     */
++     if (net_realloc(&(thd->net), 2 * thd->net.max_packet))
++     {
++       net_send_error(thd, ER_NET_PACKET_TOO_LARGE, NullS);
++       thd->fatal_error();
++       break;
++     }
++     buff= (char*) thd->net.buff;
++     fgets(buff + length, thd->net.max_packet - length, file);
++     length+= (ulong) strlen(buff + length);
++   }
++   if (thd->is_fatal_error)
++     break;
++
++    while (length && (my_isspace(thd->charset(), buff[length-1]) ||
++           buff[length-1] == ';'))
++      length--;
++    buff[length]=0;
++    thd->query_length=length;
++    thd->query= thd->memdup_w_gap(buff, length+1, 
++				  thd->db_length+1+QUERY_CACHE_FLAGS_SIZE);
++    thd->query[length] = '\0';
++    /*
++      We don't need to obtain LOCK_thread_count here because in bootstrap
++      mode we have only one thread.
++    */
++    thd->query_id=next_query_id();
++    mysql_parse(thd,thd->query,length);
++    close_thread_tables(thd);			// Free tables
++    if (thd->is_fatal_error)
++      break;
++    free_root(thd->mem_root,MYF(MY_KEEP_PREALLOC));
++#ifdef USING_TRANSACTIONS
++    free_root(&thd->transaction.mem_root,MYF(MY_KEEP_PREALLOC));
++#endif
++  }
++
++  /* thd->fatal_error should be set in case something went wrong */
++end:
++  bootstrap_error= thd->is_fatal_error;
++
++  net_end(&thd->net);
++  thd->cleanup();
++  delete thd;
++
++#ifndef EMBEDDED_LIBRARY
++  (void) pthread_mutex_lock(&LOCK_thread_count);
++  thread_count--;
++  (void) pthread_mutex_unlock(&LOCK_thread_count);
++  (void) pthread_cond_broadcast(&COND_thread_count);
++  my_thread_end();
++  pthread_exit(0);
++#endif
++  DBUG_RETURN(0);
++}
++
++
++    /* This works because items are allocated with sql_alloc() */
++
++void cleanup_items(Item *item)
++{
++  DBUG_ENTER("cleanup_items");  
++  for (; item ; item=item->next)
++    item->cleanup();
++  DBUG_VOID_RETURN;
++}
++
++/*
++  Handle COM_TABLE_DUMP command
++
++  SYNOPSIS
++    mysql_table_dump
++      thd           thread handle
++      db            database name or an empty string. If empty,
++                    the current database of the connection is used
++      tbl_name      name of the table to dump
++
++  NOTES
++    This function is written to handle one specific command only.
++
++  RETURN VALUE
++    0               success
++    1               error, the error message is set in THD
++*/
++
++static
++int mysql_table_dump(THD* thd, char* db, char* tbl_name)
++{
++  TABLE* table;
++  TABLE_LIST* table_list;
++  int error = 0;
++  DBUG_ENTER("mysql_table_dump");
++  db = (db && db[0]) ? db : thd->db;
++  if (!(table_list = (TABLE_LIST*) thd->calloc(sizeof(TABLE_LIST))))
++    DBUG_RETURN(1); // out of memory
++  table_list->db= db;
++  table_list->table_name= table_list->alias= tbl_name;
++  table_list->lock_type= TL_READ_NO_INSERT;
++  table_list->prev_global= &table_list;	// can be removed after merge with 4.1
++
++  if (!db || check_db_name(db))
++  {
++    my_error(ER_WRONG_DB_NAME ,MYF(0), db ? db : "NULL");
++    goto err;
++  }
++  if (lower_case_table_names)
++    my_casedn_str(files_charset_info, tbl_name);
++  remove_escape(table_list->table_name);
++
++  if (!(table=open_ltable(thd, table_list, TL_READ_NO_INSERT)))
++    DBUG_RETURN(1);
++
++  if (check_one_table_access(thd, SELECT_ACL, table_list))
++    goto err;
++  thd->free_list = 0;
++  thd->query_length=(uint) strlen(tbl_name);
++  thd->query = tbl_name;
++  if ((error = mysqld_dump_create_info(thd, table_list, -1)))
++  {
++    my_error(ER_GET_ERRNO, MYF(0), my_errno);
++    goto err;
++  }
++  net_flush(&thd->net);
++  if ((error= table->file->dump(thd,-1)))
++    my_error(ER_GET_ERRNO, MYF(0), error);
++
++err:
++  DBUG_RETURN(error);
++}
++
++/*
++  Ends the current transaction and (maybe) begin the next
++
++  SYNOPSIS
++    end_trans()
++      thd            Current thread
++      completion     Completion type
++
++  RETURN
++    0 - OK
++*/
++
++int end_trans(THD *thd, enum enum_mysql_completiontype completion)
++{
++  bool do_release= 0;
++  int res= 0;
++  DBUG_ENTER("end_trans");
++
++  if (unlikely(thd->in_sub_stmt))
++  {
++    my_error(ER_COMMIT_NOT_ALLOWED_IN_SF_OR_TRG, MYF(0));
++    DBUG_RETURN(1);
++  }
++  if (thd->transaction.xid_state.xa_state != XA_NOTR)
++  {
++    my_error(ER_XAER_RMFAIL, MYF(0),
++             xa_state_names[thd->transaction.xid_state.xa_state]);
++    DBUG_RETURN(1);
++  }
++  switch (completion) {
++  case COMMIT:
++    /*
++     We don't use end_active_trans() here to ensure that this works
++     even if there is a problem with the OPTION_AUTO_COMMIT flag
++     (Which of course should never happen...)
++    */
++    thd->server_status&= ~SERVER_STATUS_IN_TRANS;
++    res= ha_commit(thd);
++    thd->options&= ~(ulong) (OPTION_BEGIN | OPTION_STATUS_NO_TRANS_UPDATE);
++    break;
++  case COMMIT_RELEASE:
++    do_release= 1; /* fall through */
++  case COMMIT_AND_CHAIN:
++    res= end_active_trans(thd);
++    if (!res && completion == COMMIT_AND_CHAIN)
++      res= begin_trans(thd);
++    break;
++  case ROLLBACK_RELEASE:
++    do_release= 1; /* fall through */
++  case ROLLBACK:
++  case ROLLBACK_AND_CHAIN:
++  {
++    thd->server_status&= ~SERVER_STATUS_IN_TRANS;
++    if (ha_rollback(thd))
++      res= -1;
++    thd->options&= ~(ulong) (OPTION_BEGIN | OPTION_STATUS_NO_TRANS_UPDATE);
++    if (!res && (completion == ROLLBACK_AND_CHAIN))
++      res= begin_trans(thd);
++    break;
++  }
++  default:
++    res= -1;
++    my_error(ER_UNKNOWN_COM_ERROR, MYF(0));
++    DBUG_RETURN(-1);
++  }
++
++  if (res < 0)
++    my_error(thd->killed_errno(), MYF(0));
++  else if ((res == 0) && do_release)
++    thd->killed= THD::KILL_CONNECTION;
++
++  DBUG_RETURN(res);
++}
++
++#ifndef EMBEDDED_LIBRARY
++
++/*
++  Read one command from socket and execute it (query or simple command).
++  This function is called in loop from thread function.
++  SYNOPSIS
++    do_command()
++  RETURN VALUE
++    0  success
++    1  request of thread shutdown (see dispatch_command() description)
++*/
++
++bool do_command(THD *thd)
++{
++  char *packet;
++  uint old_timeout;
++  ulong packet_length;
++  NET *net;
++  enum enum_server_command command;
++  DBUG_ENTER("do_command");
++
++  net= &thd->net;
++  /*
++    indicator of uninitialized lex => normal flow of errors handling
++    (see my_message_sql)
++  */
++  thd->lex->current_select= 0;
++
++  packet=0;
++  old_timeout=net->read_timeout;
++  /* Wait max for 8 hours */
++  net->read_timeout=(uint) thd->variables.net_wait_timeout;
++  thd->clear_error();				// Clear error message
++
++  net_new_transaction(net);
++  if ((packet_length=my_net_read(net)) == packet_error)
++  {
++    DBUG_PRINT("info",("Got error %d reading command from socket %s",
++		       net->error,
++		       vio_description(net->vio)));
++    /* Check if we can continue without closing the connection */
++    if (net->error != 3)
++    {
++      statistic_increment(aborted_threads,&LOCK_status);
++      DBUG_RETURN(TRUE);			// We have to close it.
++    }
++    net_send_error(thd, net->last_errno, NullS);
++    net->error= 0;
++    DBUG_RETURN(FALSE);
++  }
++  else
++  {
++    packet=(char*) net->read_pos;
++    command = (enum enum_server_command) (uchar) packet[0];
++    if (command >= COM_END)
++      command= COM_END;				// Wrong command
++    DBUG_PRINT("info",("Command on %s = %d (%s)",
++		       vio_description(net->vio), command,
++		       command_name[command]));
++  }
++  net->read_timeout=old_timeout;		// restore it
++  /*
++    packet_length contains length of data, as it was stored in packet
++    header. In case of malformed header, packet_length can be zero.
++    If packet_length is not zero, my_net_read ensures that this number
++    of bytes was actually read from network. Additionally my_net_read
++    sets packet[packet_length]= 0 (thus if packet_length == 0,
++    command == packet[0] == COM_SLEEP).
++    In dispatch_command packet[packet_length] points beyond the end of packet.
++  */
++  DBUG_RETURN(dispatch_command(command,thd, packet+1, (uint) packet_length));
++}
++#endif  /* EMBEDDED_LIBRARY */
++
++
++/*
++   Perform one connection-level (COM_XXXX) command.
++
++  SYNOPSIS
++    dispatch_command()
++    thd             connection handle
++    command         type of command to perform 
++    packet          data for the command, packet is always null-terminated
++    packet_length   length of packet + 1 (to show that data is
++                    null-terminated) except for COM_SLEEP, where it
++                    can be zero.
++  RETURN VALUE
++    0   ok
++    1   request of thread shutdown, i. e. if command is
++        COM_QUIT/COM_SHUTDOWN
++*/
++
++bool dispatch_command(enum enum_server_command command, THD *thd,
++		      char* packet, uint packet_length)
++{
++  NET *net= &thd->net;
++  bool error= 0;
++  DBUG_ENTER("dispatch_command");
++
++  if (thd->killed == THD::KILL_QUERY || thd->killed == THD::KILL_BAD_DATA)
++    thd->killed= THD::NOT_KILLED;
++
++  thd->command=command;
++  /*
++    Commands which always take a long time are logged into
++    the slow log only if opt_log_slow_admin_statements is set.
++  */
++  thd->enable_slow_log= TRUE;
++  thd->lex->sql_command= SQLCOM_END; /* to avoid confusing VIEW detectors */
++  thd->set_time();
++  VOID(pthread_mutex_lock(&LOCK_thread_count));
++  thd->query_id=query_id;
++  if (command != COM_STATISTICS && command != COM_PING)
++    next_query_id();
++  thread_running++;
++  /* TODO: set thd->lex->sql_command to SQLCOM_END here */
++  VOID(pthread_mutex_unlock(&LOCK_thread_count));
++
++  thd->server_status&=
++           ~(SERVER_QUERY_NO_INDEX_USED | SERVER_QUERY_NO_GOOD_INDEX_USED);
++  switch (command) {
++  case COM_INIT_DB:
++  {
++    LEX_STRING tmp;
++    statistic_increment(thd->status_var.com_stat[SQLCOM_CHANGE_DB],
++			&LOCK_status);
++    thd->convert_string(&tmp, system_charset_info,
++			packet, strlen(packet), thd->charset());
++    if (!mysql_change_db(thd, tmp.str, FALSE))
++    {
++      mysql_log.write(thd,command,"%s",thd->db);
++      send_ok(thd);
++    }
++    break;
++  }
++#ifdef HAVE_REPLICATION
++  case COM_REGISTER_SLAVE:
++  {
++    if (!register_slave(thd, (uchar*)packet, packet_length))
++      send_ok(thd);
++    break;
++  }
++#endif
++  case COM_TABLE_DUMP:
++  {
++    char *db, *tbl_name;
++    uint db_len= *(uchar*) packet;
++    if (db_len >= packet_length || db_len > NAME_LEN)
++    {
++      my_message(ER_UNKNOWN_COM_ERROR, ER(ER_UNKNOWN_COM_ERROR), MYF(0));
++      break;
++    }
++    uint tbl_len= *(uchar*) (packet + db_len + 1);
++    if (db_len+tbl_len+2 > packet_length || tbl_len > NAME_LEN)
++    {
++      my_message(ER_UNKNOWN_COM_ERROR, ER(ER_UNKNOWN_COM_ERROR), MYF(0));
++      break;
++    }
++
++    statistic_increment(thd->status_var.com_other, &LOCK_status);
++    thd->enable_slow_log= opt_log_slow_admin_statements;
++    db= thd->alloc(db_len + tbl_len + 2);
++    if (!db)
++    {
++      my_message(ER_OUT_OF_RESOURCES, ER(ER_OUT_OF_RESOURCES), MYF(0));
++      break;
++    }
++    tbl_name= strmake(db, packet + 1, db_len)+1;
++    strmake(tbl_name, packet + db_len + 2, tbl_len);
++    mysql_table_dump(thd, db, tbl_name);
++    break;
++  }
++  case COM_CHANGE_USER:
++  {
++    thd->change_user();
++    thd->clear_error();                         // if errors from rollback
++
++    statistic_increment(thd->status_var.com_other, &LOCK_status);
++    char *user= (char*) packet;
++    char *passwd= strend(user)+1;
++    /*
++      Old clients send null-terminated string ('\0' for empty string) for
++      password.  New clients send the size (1 byte) + string (not null
++      terminated, so also '\0' for empty string).
++    */
++    char db_buff[NAME_LEN+1];               // buffer to store db in utf8
++    char *db= passwd;
++    uint passwd_len= thd->client_capabilities & CLIENT_SECURE_CONNECTION ?
++      *passwd++ : strlen(passwd);
++    db+= passwd_len + 1;
++#ifndef EMBEDDED_LIBRARY
++    /* Small check for incoming packet */
++    if ((uint) ((uchar*) db - net->read_pos) > packet_length)
++    {
++      my_message(ER_UNKNOWN_COM_ERROR, ER(ER_UNKNOWN_COM_ERROR), MYF(0));
++      break;
++    }
++#endif
++    /* Convert database name to utf8 */
++    uint dummy_errors;
++    db_buff[copy_and_convert(db_buff, sizeof(db_buff)-1,
++                             system_charset_info, db, strlen(db),
++                             thd->charset(), &dummy_errors)]= 0;
++    db= db_buff;
++
++    /* Save user and privileges */
++    uint save_db_length= thd->db_length;
++    char *save_db= thd->db;
++    Security_context save_security_ctx= *thd->security_ctx;
++    USER_CONN *save_user_connect= thd->user_connect;
++
++    if (!(thd->security_ctx->user= my_strdup(user, MYF(0))))
++    {
++      thd->security_ctx->user= save_security_ctx.user;
++      my_message(ER_OUT_OF_RESOURCES, ER(ER_OUT_OF_RESOURCES), MYF(0));
++      break;
++    }
++
++    /* Clear variables that are allocated */
++    thd->user_connect= 0;
++    int res= check_user(thd, COM_CHANGE_USER, passwd, passwd_len, db, FALSE);
++
++    if (res)
++    {
++      /* authentication failure, we shall restore old user */
++      if (res > 0)
++        my_message(ER_UNKNOWN_COM_ERROR, ER(ER_UNKNOWN_COM_ERROR), MYF(0));
++      x_free(thd->security_ctx->user);
++      *thd->security_ctx= save_security_ctx;
++      thd->user_connect= save_user_connect;
++      thd->db= save_db;
++      thd->db_length= save_db_length;
++    }
++    else
++    {
++#ifndef NO_EMBEDDED_ACCESS_CHECKS
++      /* we've authenticated new user */
++      if (save_user_connect)
++	decrease_user_connections(save_user_connect);
++#endif /* NO_EMBEDDED_ACCESS_CHECKS */
++      x_free((gptr) save_db);
++      x_free((gptr)  save_security_ctx.user);
++    }
++    break;
++  }
++  case COM_STMT_EXECUTE:
++  {
++    mysql_stmt_execute(thd, packet, packet_length);
++    break;
++  }
++  case COM_STMT_FETCH:
++  {
++    mysql_stmt_fetch(thd, packet, packet_length);
++    break;
++  }
++  case COM_STMT_SEND_LONG_DATA:
++  {
++    mysql_stmt_get_longdata(thd, packet, packet_length);
++    break;
++  }
++  case COM_STMT_PREPARE:
++  {
++    mysql_stmt_prepare(thd, packet, packet_length);
++    break;
++  }
++  case COM_STMT_CLOSE:
++  {
++    mysql_stmt_close(thd, packet);
++    break;
++  }
++  case COM_STMT_RESET:
++  {
++    mysql_stmt_reset(thd, packet);
++    break;
++  }
++  case COM_QUERY:
++  {
++    if (alloc_query(thd, packet, packet_length))
++      break;					// fatal error is set
++    char *packet_end= thd->query + thd->query_length;
++    /* 'b' stands for 'buffer' parameter', special for 'my_snprintf' */
++    const char *format= "%.*b";
++    mysql_log.write(thd,command, format, thd->query_length, thd->query);
++    DBUG_PRINT("query",("%-.4096s",thd->query));
++
++    if (!(specialflag & SPECIAL_NO_PRIOR))
++      my_pthread_setprio(pthread_self(),QUERY_PRIOR);
++
++    mysql_parse(thd,thd->query, thd->query_length);
++
++    while (!thd->killed && thd->lex->found_semicolon && !thd->net.report_error)
++    {
++      char *packet= thd->lex->found_semicolon;
++      net->no_send_error= 0;
++      /*
++        Multiple queries exits, execute them individually
++      */
++      if (thd->lock || thd->open_tables || thd->derived_tables ||
++          thd->prelocked_mode)
++        close_thread_tables(thd);
++      ulong length= (ulong)(packet_end-packet);
++
++      log_slow_statement(thd);
++
++      /* Remove garbage at start of query */
++      while (my_isspace(thd->charset(), *packet) && length > 0)
++      {
++        packet++;
++        length--;
++      }
++      VOID(pthread_mutex_lock(&LOCK_thread_count));
++      thd->query_length= length;
++      thd->query= packet;
++      thd->query_id= next_query_id();
++      thd->set_time(); /* Reset the query start time. */
++      /* TODO: set thd->lex->sql_command to SQLCOM_END here */
++      VOID(pthread_mutex_unlock(&LOCK_thread_count));
++      mysql_parse(thd, packet, length);
++    }
++
++    if (!(specialflag & SPECIAL_NO_PRIOR))
++      my_pthread_setprio(pthread_self(),WAIT_PRIOR);
++    DBUG_PRINT("info",("query ready"));
++    break;
++  }
++  case COM_FIELD_LIST:				// This isn't actually needed
++#ifdef DONT_ALLOW_SHOW_COMMANDS
++    my_message(ER_NOT_ALLOWED_COMMAND, ER(ER_NOT_ALLOWED_COMMAND),
++               MYF(0));	/* purecov: inspected */
++    break;
++#else
++  {
++    char *fields, *pend;
++    /* Locked closure of all tables */
++    TABLE_LIST *locked_tables= NULL;
++    TABLE_LIST table_list;
++    LEX_STRING conv_name;
++    /* Saved variable value */
++    my_bool old_innodb_table_locks= 
++              IF_INNOBASE_DB(thd->variables.innodb_table_locks, FALSE);
++    /* used as fields initializator */
++    lex_start(thd, 0, 0);
++
++
++    statistic_increment(thd->status_var.com_stat[SQLCOM_SHOW_FIELDS],
++			&LOCK_status);
++    bzero((char*) &table_list,sizeof(table_list));
++    if (thd->copy_db_to(&table_list.db, 0))
++      break;
++    pend= strend(packet);
++    thd->convert_string(&conv_name, system_charset_info,
++			packet, (uint) (pend-packet), thd->charset());
++    table_list.alias= table_list.table_name= conv_name.str;
++    packet= pend+1;
++
++    if (!my_strcasecmp(system_charset_info, table_list.db,
++                       information_schema_name.str))
++    {
++      ST_SCHEMA_TABLE *schema_table= find_schema_table(thd, table_list.alias);
++      if (schema_table)
++        table_list.schema_table= schema_table;
++    }
++
++    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);
++    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
++
++    if (check_access(thd,SELECT_ACL,table_list.db,&table_list.grant.privilege,
++		     0, 0, test(table_list.schema_table)))
++      break;
++    if (grant_option &&
++	check_grant(thd, SELECT_ACL, &table_list, 2, UINT_MAX, 0))
++      break;
++    /* init structures for VIEW processing */
++    table_list.select_lex= &(thd->lex->select_lex);
++    mysql_init_query(thd, (uchar*)"", 0);
++    thd->lex->
++      select_lex.table_list.link_in_list((byte*) &table_list,
++                                         (byte**) &table_list.next_local);
++    thd->lex->add_to_query_tables(&table_list);
++
++    /* switch on VIEW optimisation: do not fill temporary tables */
++    thd->lex->sql_command= SQLCOM_SHOW_FIELDS;
++    mysqld_list_fields(thd,&table_list,fields);
++    thd->lex->unit.cleanup();
++    thd->cleanup_after_query();
++    break;
++  }
++#endif
++  case COM_QUIT:
++    /* We don't calculate statistics for this command */
++    mysql_log.write(thd,command,NullS);
++    net->error=0;				// Don't give 'abort' message
++    error=TRUE;					// End server
++    break;
++
++  case COM_CREATE_DB:				// QQ: To be removed
++    {
++      char *db=thd->strdup(packet), *alias;
++      HA_CREATE_INFO create_info;
++
++      statistic_increment(thd->status_var.com_stat[SQLCOM_CREATE_DB],
++			  &LOCK_status);
++      // null test to handle EOM
++      if (!db || !(alias= thd->strdup(db)) || check_db_name(db))
++      {
++	my_error(ER_WRONG_DB_NAME, MYF(0), db ? db : "NULL");
++	break;
++      }
++      if (check_access(thd,CREATE_ACL,db,0,1,0,is_schema_db(db)))
++	break;
++      mysql_log.write(thd,command,packet);
++      bzero(&create_info, sizeof(create_info));
++      mysql_create_db(thd, (lower_case_table_names == 2 ? alias : db),
++                      &create_info, 0);
++      break;
++    }
++  case COM_DROP_DB:				// QQ: To be removed
++    {
++      statistic_increment(thd->status_var.com_stat[SQLCOM_DROP_DB],
++			  &LOCK_status);
++      char *db=thd->strdup(packet);
++      /*  null test to handle EOM */
++      if (!db || check_db_name(db))
++      {
++	my_error(ER_WRONG_DB_NAME, MYF(0), db ? db : "NULL");
++	break;
++      }
++      if (check_access(thd,DROP_ACL,db,0,1,0,is_schema_db(db)))
++	break;
++      if (thd->locked_tables || thd->active_transaction())
++      {
++	my_message(ER_LOCK_OR_ACTIVE_TRANSACTION,
++                   ER(ER_LOCK_OR_ACTIVE_TRANSACTION), MYF(0));
++	break;
++      }
++      mysql_log.write(thd,command,db);
++      mysql_rm_db(thd, db, 0, 0);
++      break;
++    }
++#ifndef EMBEDDED_LIBRARY
++  case COM_BINLOG_DUMP:
++    {
++      ulong pos;
++      ushort flags;
++      uint32 slave_server_id;
++
++      statistic_increment(thd->status_var.com_other,&LOCK_status);
++      thd->enable_slow_log= opt_log_slow_admin_statements;
++      if (check_global_access(thd, REPL_SLAVE_ACL))
++	break;
++
++      /* TODO: The following has to be changed to an 8 byte integer */
++      pos = uint4korr(packet);
++      flags = uint2korr(packet + 4);
++      thd->server_id=0; /* avoid suicide */
++      if ((slave_server_id= uint4korr(packet+6))) // mysqlbinlog.server_id==0
++	kill_zombie_dump_threads(slave_server_id);
++      thd->server_id = slave_server_id;
++
++      mysql_log.write(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);
++      /*  fake COM_QUIT -- if we get here, the thread needs to terminate */
++      error = TRUE;
++      net->error = 0;
++      break;
++    }
++#endif
++  case COM_REFRESH:
++  {
++    bool not_used;
++    statistic_increment(thd->status_var.com_stat[SQLCOM_FLUSH],
++                        &LOCK_status);
++    ulong options= (ulong) (uchar) packet[0];
++    if (check_global_access(thd,RELOAD_ACL))
++      break;
++    mysql_log.write(thd,command,NullS);
++    if (!reload_acl_and_cache(thd, options, (TABLE_LIST*) 0, &not_used))
++      send_ok(thd);
++    break;
++  }
++#ifndef EMBEDDED_LIBRARY
++  case COM_SHUTDOWN:
++  {
++    statistic_increment(thd->status_var.com_other, &LOCK_status);
++    if (check_global_access(thd,SHUTDOWN_ACL))
++      break; /* purecov: inspected */
++    /*
++      If the client is < 4.1.3, it is going to send us no argument; then
++      packet_length is 1, packet[0] is the end 0 of the packet. Note that
++      SHUTDOWN_DEFAULT is 0. If client is >= 4.1.3, the shutdown level is in
++      packet[0].
++    */
++    enum mysql_enum_shutdown_level level=
++      (enum mysql_enum_shutdown_level) (uchar) packet[0];
++    DBUG_PRINT("quit",("Got shutdown command for level %u", level));
++    if (level == SHUTDOWN_DEFAULT)
++      level= SHUTDOWN_WAIT_ALL_BUFFERS; // soon default will be configurable
++    else if (level != SHUTDOWN_WAIT_ALL_BUFFERS)
++    {
++      my_error(ER_NOT_SUPPORTED_YET, MYF(0), "this shutdown level");
++      break;
++    }
++    DBUG_PRINT("quit",("Got shutdown command for level %u", level));
++    mysql_log.write(thd,command,NullS);
++    send_eof(thd);
++#ifdef __WIN__
++    sleep(1);					// must wait after eof()
++#endif
++#ifndef OS2
++    send_eof(thd);				// This is for 'quit request'
++#endif
++    close_connection(thd, 0, 1);
++    close_thread_tables(thd);			// Free before kill
++    kill_mysql();
++    error=TRUE;
++    break;
++  }
++#endif
++  case COM_STATISTICS:
++  {
++    mysql_log.write(thd,command,NullS);
++    statistic_increment(thd->status_var.com_stat[SQLCOM_SHOW_STATUS],
++			&LOCK_status);
++#ifndef EMBEDDED_LIBRARY
++    char buff[200];
++#else
++    char *buff= thd->net.last_error;
++#endif
++
++    STATUS_VAR current_global_status_var;
++    calc_sum_of_all_status(&current_global_status_var);
++
++    ulong uptime = (ulong) (thd->start_time - start_time);
++    sprintf((char*) buff,
++	    "Uptime: %lu  Threads: %d  Questions: %lu  Slow queries: %lu  Opens: %lu  Flush tables: %lu  Open tables: %u  Queries per second avg: %.3f",
++	    uptime,
++	    (int) thread_count, (ulong) thd->query_id,
++	    current_global_status_var.long_query_count,
++	    current_global_status_var.opened_tables, refresh_version, cached_tables(),
++	    (uptime ? (ulonglong2double(thd->query_id) / (double) uptime) :
++	     (double) 0));
++#ifdef SAFEMALLOC
++    if (sf_malloc_cur_memory)				// Using SAFEMALLOC
++      sprintf(strend(buff), "  Memory in use: %ldK  Max memory used: %ldK",
++	      (sf_malloc_cur_memory+1023L)/1024L,
++	      (sf_malloc_max_memory+1023L)/1024L);
++#endif
++#ifndef EMBEDDED_LIBRARY
++    VOID(my_net_write(net, buff,(uint) strlen(buff)));
++    VOID(net_flush(net));
++#endif
++    break;
++  }
++  case COM_PING:
++    statistic_increment(thd->status_var.com_other, &LOCK_status);
++    send_ok(thd);				// Tell client we are alive
++    break;
++  case COM_PROCESS_INFO:
++    statistic_increment(thd->status_var.com_stat[SQLCOM_SHOW_PROCESSLIST],
++			&LOCK_status);
++    if (!thd->security_ctx->priv_user[0] &&
++        check_global_access(thd, PROCESS_ACL))
++      break;
++    mysql_log.write(thd,command,NullS);
++    mysqld_list_processes(thd,
++			  thd->security_ctx->master_access & PROCESS_ACL ? 
++			  NullS : thd->security_ctx->priv_user, 0);
++    break;
++  case COM_PROCESS_KILL:
++  {
++    statistic_increment(thd->status_var.com_stat[SQLCOM_KILL], &LOCK_status);
++    ulong id=(ulong) uint4korr(packet);
++    kill_one_thread(thd,id,false);
++    break;
++  }
++  case COM_SET_OPTION:
++  {
++    statistic_increment(thd->status_var.com_stat[SQLCOM_SET_OPTION],
++			&LOCK_status);
++    enum_mysql_set_option command= (enum_mysql_set_option) uint2korr(packet);
++    switch (command) {
++    case MYSQL_OPTION_MULTI_STATEMENTS_ON:
++      thd->client_capabilities|= CLIENT_MULTI_STATEMENTS;
++      send_eof(thd);
++      break;
++    case MYSQL_OPTION_MULTI_STATEMENTS_OFF:
++      thd->client_capabilities&= ~CLIENT_MULTI_STATEMENTS;
++      send_eof(thd);
++      break;
++    default:
++      my_message(ER_UNKNOWN_COM_ERROR, ER(ER_UNKNOWN_COM_ERROR), MYF(0));
++      break;
++    }
++    break;
++  }
++  case COM_DEBUG:
++    statistic_increment(thd->status_var.com_other, &LOCK_status);
++    if (check_global_access(thd, SUPER_ACL))
++      break;					/* purecov: inspected */
++    mysql_print_status();
++    mysql_log.write(thd,command,NullS);
++    send_eof(thd);
++    break;
++  case COM_SLEEP:
++  case COM_CONNECT:				// Impossible here
++  case COM_TIME:				// Impossible from client
++  case COM_DELAYED_INSERT:
++  case COM_END:
++  default:
++    my_message(ER_UNKNOWN_COM_ERROR, ER(ER_UNKNOWN_COM_ERROR), MYF(0));
++    break;
++  }
++  if (thd->lock || thd->open_tables || thd->derived_tables ||
++      thd->prelocked_mode)
++  {
++    thd->proc_info="closing tables";
++    close_thread_tables(thd);			/* Free tables */
++  }
++  /*
++    assume handlers auto-commit (if some doesn't - transaction handling
++    in MySQL should be redesigned to support it; it's a big change,
++    and it's not worth it - better to commit explicitly only writing
++    transactions, read-only ones should better take care of themselves.
++    saves some work in 2pc too)
++    see also sql_base.cc - close_thread_tables()
++  */
++  bzero(&thd->transaction.stmt, sizeof(thd->transaction.stmt));
++  if (!thd->active_transaction())
++    thd->transaction.xid_state.xid.null();
++
++  /* report error issued during command execution */
++  if (thd->killed_errno() && !thd->net.report_error)
++    thd->send_kill_message();
++  if (thd->net.report_error)
++    net_send_error(thd);
++
++  log_slow_statement(thd);
++
++  thd->proc_info="cleaning up";
++  VOID(pthread_mutex_lock(&LOCK_thread_count)); // For process list
++  thd->proc_info=0;
++  thd->command=COM_SLEEP;
++  thd->query=0;
++  thd->query_length=0;
++  thread_running--;
++  VOID(pthread_mutex_unlock(&LOCK_thread_count));
++  thd->packet.shrink(thd->variables.net_buffer_length);	// Reclaim some memory
++  free_root(thd->mem_root,MYF(MY_KEEP_PREALLOC));
++  DBUG_RETURN(error);
++}
++
++
++void log_slow_statement(THD *thd)
++{
++  time_t start_of_query;
++
++  /*
++    The following should never be true with our current code base,
++    but better to keep this here so we don't accidently try to log a
++    statement in a trigger or stored function
++  */
++  if (unlikely(thd->in_sub_stmt))
++    return;                                     // Don't set time for sub stmt
++
++  start_of_query= thd->start_time;
++  thd->end_time();				// Set start time
++
++  /*
++    Do not log administrative statements unless the appropriate option is
++    set; do not log into slow log if reading from backup.
++  */
++  if (thd->enable_slow_log && !thd->user_time)
++  {
++    thd->proc_info="logging slow query";
++
++    if ((ulong) (thd->start_time - thd->time_after_lock) >
++	thd->variables.long_query_time ||
++        (thd->server_status &
++	  (SERVER_QUERY_NO_INDEX_USED | SERVER_QUERY_NO_GOOD_INDEX_USED)) &&
++        (specialflag & SPECIAL_LOG_QUERIES_NOT_USING_INDEXES) &&
++        /* == SQLCOM_END unless this is a SHOW command */
++        thd->lex->orig_sql_command == SQLCOM_END)
++    {
++      thd->status_var.long_query_count++;
++      mysql_slow_log.write(thd, thd->query, thd->query_length, start_of_query);
++    }
++  }
++}
++
++
++/*
++  Create a TABLE_LIST object for an INFORMATION_SCHEMA table.
++
++  SYNOPSIS
++    prepare_schema_table()
++      thd              thread handle
++      lex              current lex
++      table_ident      table alias if it's used
++      schema_table_idx the type of the INFORMATION_SCHEMA table to be
++                       created
++
++  DESCRIPTION
++    This function is used in the parser to convert a SHOW or DESCRIBE
++    table_name command to a SELECT from INFORMATION_SCHEMA.
++    It prepares a SELECT_LEX and a TABLE_LIST object to represent the
++    given command as a SELECT parse tree.
++
++  NOTES
++    Due to the way this function works with memory and LEX it cannot
++    be used outside the parser (parse tree transformations outside
++    the parser break PS and SP).
++
++  RETURN VALUE
++    0                 success
++    1                 out of memory or SHOW commands are not allowed
++                      in this version of the server.
++*/
++
++int prepare_schema_table(THD *thd, LEX *lex, Table_ident *table_ident,
++                         enum enum_schema_tables schema_table_idx)
++{
++  DBUG_ENTER("prepare_schema_table");
++  SELECT_LEX *sel= 0;
++  switch (schema_table_idx) {
++  case SCH_SCHEMATA:
++#if defined(DONT_ALLOW_SHOW_COMMANDS)
++    my_message(ER_NOT_ALLOWED_COMMAND,
++               ER(ER_NOT_ALLOWED_COMMAND), MYF(0));   /* purecov: inspected */
++    DBUG_RETURN(1);
++#else
++    if ((specialflag & SPECIAL_SKIP_SHOW_DB) &&
++	check_global_access(thd, SHOW_DB_ACL))
++      DBUG_RETURN(1);
++    break;
++#endif
++  case SCH_TABLE_NAMES:
++  case SCH_TABLES:
++  case SCH_VIEWS:
++  case SCH_TRIGGERS:
++#ifdef DONT_ALLOW_SHOW_COMMANDS
++    my_message(ER_NOT_ALLOWED_COMMAND,
++               ER(ER_NOT_ALLOWED_COMMAND), MYF(0)); /* purecov: inspected */
++    DBUG_RETURN(1);
++#else
++    {
++      char *db;
++      if (lex->select_lex.db == NULL &&
++          thd->copy_db_to(&lex->select_lex.db, 0))
++      {
++        DBUG_RETURN(1);
++      }
++      db= lex->select_lex.db;
++      remove_escape(db);				// Fix escaped '_'
++      if (check_db_name(db))
++      {
++        my_error(ER_WRONG_DB_NAME, MYF(0), db);
++        DBUG_RETURN(1);
++      }
++      if (check_access(thd, SELECT_ACL, db, &thd->col_access, 0, 0,
++                       is_schema_db(db)))
++        DBUG_RETURN(1);			        /* purecov: inspected */
++      if (!thd->col_access && check_grant_db(thd,db))
++      {
++	my_error(ER_DBACCESS_DENIED_ERROR, MYF(0),
++                 thd->security_ctx->priv_user, thd->security_ctx->priv_host,
++                 db);
++	DBUG_RETURN(1);
++      }
++      break;
++    }
++#endif
++  case SCH_COLUMNS:
++  case SCH_STATISTICS:
++#ifdef DONT_ALLOW_SHOW_COMMANDS
++    my_message(ER_NOT_ALLOWED_COMMAND,
++               ER(ER_NOT_ALLOWED_COMMAND), MYF(0)); /* purecov: inspected */
++    DBUG_RETURN(1);
++#else
++    if (table_ident)
++    {
++      TABLE_LIST **query_tables_last= lex->query_tables_last;
++      sel= new SELECT_LEX();
++      /* 'parent_lex' is used in init_query() so it must be before it. */
++      sel->parent_lex= lex;
++      sel->init_query();
++      if (!sel->add_table_to_list(thd, table_ident, 0, 0, TL_READ, 
++                                 (List<String> *) 0, (List<String> *) 0))
++        DBUG_RETURN(1);
++      lex->query_tables_last= query_tables_last;
++      TABLE_LIST *table_list= (TABLE_LIST*) sel->table_list.first;
++      char *db= table_list->db;
++      remove_escape(db);			// Fix escaped '_'
++      remove_escape(table_list->table_name);
++      if (check_access(thd,SELECT_ACL | EXTRA_ACL,db,
++                       &table_list->grant.privilege, 0, 0,
++                       test(table_list->schema_table)))
++        DBUG_RETURN(1);				/* purecov: inspected */
++      if (grant_option && check_grant(thd, SELECT_ACL, table_list, 2,
++                                      UINT_MAX, 0))
++        DBUG_RETURN(1);
++      break;
++    }
++#endif
++  case SCH_OPEN_TABLES:
++  case SCH_VARIABLES:
++  case SCH_STATUS:
++  case SCH_PROCEDURES:
++  case SCH_CHARSETS:
++  case SCH_COLLATIONS:
++  case SCH_COLLATION_CHARACTER_SET_APPLICABILITY:
++  case SCH_USER_PRIVILEGES:
++  case SCH_SCHEMA_PRIVILEGES:
++  case SCH_TABLE_PRIVILEGES:
++  case SCH_COLUMN_PRIVILEGES:
++  case SCH_TABLE_CONSTRAINTS:
++  case SCH_KEY_COLUMN_USAGE:
++  default:
++    break;
++  }
++  
++  SELECT_LEX *select_lex= lex->current_select;
++  if (make_schema_select(thd, select_lex, schema_table_idx))
++  {
++    DBUG_RETURN(1);
++  }
++  TABLE_LIST *table_list= (TABLE_LIST*) select_lex->table_list.first;
++  table_list->schema_select_lex= sel;
++  table_list->schema_table_reformed= 1;
++  statistic_increment(thd->status_var.com_stat[lex->orig_sql_command],
++                      &LOCK_status);
++  DBUG_RETURN(0);
++}
++
++
++/*
++  Read query from packet and store in thd->query
++  Used in COM_QUERY and COM_STMT_PREPARE
++
++  DESCRIPTION
++    Sets the following THD variables:
++      query
++      query_length
++
++  RETURN VALUES
++    FALSE ok
++    TRUE  error;  In this case thd->fatal_error is set
++*/
++
++bool alloc_query(THD *thd, const char *packet, uint packet_length)
++{
++  packet_length--;				// Remove end null
++  /* Remove garbage at start and end of query */
++  while (my_isspace(thd->charset(),packet[0]) && packet_length > 0)
++  {
++    packet++;
++    packet_length--;
++  }
++  const char *pos= packet + packet_length;     // Point at end null
++  while (packet_length > 0 &&
++	 (pos[-1] == ';' || my_isspace(thd->charset() ,pos[-1])))
++  {
++    pos--;
++    packet_length--;
++  }
++  /* We must allocate some extra memory for query cache */
++  thd->query_length= 0;                        // Extra safety: Avoid races
++  if (!(thd->query= (char*) thd->memdup_w_gap((gptr) (packet),
++					      packet_length,
++					      thd->db_length+ 1 +
++					      QUERY_CACHE_FLAGS_SIZE)))
++    return TRUE;
++  thd->query[packet_length]=0;
++  thd->query_length= packet_length;
++
++  /* Reclaim some memory */
++  thd->packet.shrink(thd->variables.net_buffer_length);
++  thd->convert_buffer.shrink(thd->variables.net_buffer_length);
++
++  return FALSE;
++}
++
++static void reset_one_shot_variables(THD *thd) 
++{
++  thd->variables.character_set_client=
++    global_system_variables.character_set_client;
++  thd->variables.collation_connection=
++    global_system_variables.collation_connection;
++  thd->variables.collation_database=
++    global_system_variables.collation_database;
++  thd->variables.collation_server=
++    global_system_variables.collation_server;
++  thd->update_charset();
++  thd->variables.time_zone=
++    global_system_variables.time_zone;
++  thd->one_shot_set= 0;
++}
++
++
++/*
++  Execute command saved in thd and lex->sql_command
++
++  SYNOPSIS
++    mysql_execute_command()
++      thd                       Thread handle
++
++  IMPLEMENTATION
++
++    Before every operation that can request a write lock for a table
++    wait if a global read lock exists. However do not wait if this
++    thread has locked tables already. No new locks can be requested
++    until the other locks are released. The thread that requests the
++    global read lock waits for write locked tables to become unlocked.
++
++    Note that wait_if_global_read_lock() sets a protection against a new
++    global read lock when it succeeds. This needs to be released by
++    start_waiting_global_read_lock() after the operation.
++
++  RETURN
++    FALSE       OK
++    TRUE        Error
++*/
++
++bool
++mysql_execute_command(THD *thd)
++{
++  bool res= FALSE;
++  bool need_start_waiting= FALSE; // have protection against global read lock
++  int  result= 0;
++  LEX  *lex= thd->lex;
++  /* first SELECT_LEX (have special meaning for many of non-SELECTcommands) */
++  SELECT_LEX *select_lex= &lex->select_lex;
++  /* first table of first SELECT_LEX */
++  TABLE_LIST *first_table= (TABLE_LIST*) select_lex->table_list.first;
++  /* list of all tables in query */
++  TABLE_LIST *all_tables;
++  /* most outer SELECT_LEX_UNIT of query */
++  SELECT_LEX_UNIT *unit= &lex->unit;
++  /* Saved variable value */
++  DBUG_ENTER("mysql_execute_command");
++  thd->net.no_send_error= 0;
++
++  /*
++    Remember first generated insert id value of the previous
++    statement.  We remember it here at the beginning of the statement,
++    and also in Item_func_last_insert_id::fix_fields() and
++    sys_var_last_insert_id::value_ptr().  Last two places are required
++    because LAST_INSERT_ID() and @@LAST_INSERT_ID may also be used in
++    expression that is not executed with mysql_execute_command().
++
++    And we remember it here because some statements read
++    @@LAST_INSERT_ID indirectly, like "SELECT * FROM t1 WHERE id IS
++    NULL", that may replace "id IS NULL" with "id = <LAST_INSERT_ID>".
++  */
++  thd->current_insert_id= thd->last_insert_id;
++
++  /*
++    In many cases first table of main SELECT_LEX have special meaning =>
++    check that it is first table in global list and relink it first in 
++    queries_tables list if it is necessary (we need such relinking only
++    for queries with subqueries in select list, in this case tables of
++    subqueries will go to global list first)
++
++    all_tables will differ from first_table only if most upper SELECT_LEX
++    do not contain tables.
++
++    Because of above in place where should be at least one table in most
++    outer SELECT_LEX we have following check:
++    DBUG_ASSERT(first_table == all_tables);
++    DBUG_ASSERT(first_table == all_tables && first_table != 0);
++  */
++  lex->first_lists_tables_same();
++  /* should be assigned after making first tables same */
++  all_tables= lex->query_tables;
++  /* set context for commands which do not use setup_tables */
++  select_lex->
++    context.resolve_in_table_list_only((TABLE_LIST*)select_lex->
++                                       table_list.first);
++
++  /*
++    Reset warning count for each query that uses tables
++    A better approach would be to reset this for any commands
++    that is not a SHOW command or a select that only access local
++    variables, but for now this is probably good enough.
++    Don't reset warnings when executing a stored routine.
++  */
++  if ((all_tables || &lex->select_lex != lex->all_selects_list ||
++       lex->sroutines.records) && !thd->spcont ||
++      lex->time_zone_tables_used)
++    mysql_reset_errors(thd, 0);
++
++#ifdef HAVE_REPLICATION
++  if (unlikely(thd->slave_thread))
++  {
++    /*
++      Check if statment should be skipped because of slave filtering
++      rules
++
++      Exceptions are:
++      - UPDATE MULTI: For this statement, we want to check the filtering
++        rules later in the code
++      - SET: we always execute it (Not that many SET commands exists in
++        the binary log anyway -- only 4.1 masters write SET statements,
++	in 5.0 there are no SET statements in the binary log)
++      - DROP TEMPORARY TABLE IF EXISTS: we always execute it (otherwise we
++        have stale files on slave caused by exclusion of one tmp table).
++    */
++    if (!(lex->sql_command == SQLCOM_UPDATE_MULTI) &&
++	!(lex->sql_command == SQLCOM_SET_OPTION) &&
++	!(lex->sql_command == SQLCOM_DROP_TABLE &&
++          lex->drop_temporary && lex->drop_if_exists) &&
++        all_tables_not_ok(thd, all_tables))
++    {
++      /* we warn the slave SQL thread */
++      my_message(ER_SLAVE_IGNORED_TABLE, ER(ER_SLAVE_IGNORED_TABLE), MYF(0));
++      if (thd->one_shot_set)
++      {
++        /*
++          It's ok to check thd->one_shot_set here:
++
++          The charsets in a MySQL 5.0 slave can change by both a binlogged
++          SET ONE_SHOT statement and the event-internal charset setting, 
++          and these two ways to change charsets do not seems to work
++          together.
++
++          At least there seems to be problems in the rli cache for
++          charsets if we are using ONE_SHOT.  Note that this is normally no
++          problem because either the >= 5.0 slave reads a 4.1 binlog (with
++          ONE_SHOT) *or* or 5.0 binlog (without ONE_SHOT) but never both."
++        */
++        reset_one_shot_variables(thd);
++      }
++      DBUG_RETURN(0);
++    }
++  }
++  else
++  {
++#endif /* HAVE_REPLICATION */
++    /*
++      When option readonly is set deny operations which change non-temporary
++      tables. Except for the replication thread and the 'super' users.
++    */
++    if (opt_readonly &&
++        !(thd->security_ctx->master_access & SUPER_ACL) &&
++        uc_update_queries[lex->sql_command] &&
++        !((lex->sql_command == SQLCOM_CREATE_TABLE) &&
++          (lex->create_info.options & HA_LEX_CREATE_TMP_TABLE)) &&
++        !((lex->sql_command == SQLCOM_DROP_TABLE) && lex->drop_temporary) &&
++        ((lex->sql_command != SQLCOM_UPDATE_MULTI) &&
++          some_non_temp_table_to_be_updated(thd, all_tables)))
++    {
++      my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--read-only");
++      DBUG_RETURN(-1);
++    }
++#ifdef HAVE_REPLICATION
++  } /* endif unlikely slave */
++#endif
++  if(lex->orig_sql_command == SQLCOM_END)
++    statistic_increment(thd->status_var.com_stat[lex->sql_command],
++                        &LOCK_status);
++
++  switch (lex->sql_command) {
++  case SQLCOM_SELECT:
++  {
++    /* assign global limit variable if limit is not given */
++    {
++      SELECT_LEX *param= lex->unit.global_parameters;
++      if (!param->explicit_limit)
++	param->select_limit=
++          new Item_int((ulonglong)thd->variables.select_limit);
++    }
++
++    select_result *result=lex->result;
++    if (all_tables)
++    {
++      if (lex->orig_sql_command != SQLCOM_SHOW_STATUS_PROC &&
++          lex->orig_sql_command != SQLCOM_SHOW_STATUS_FUNC)
++        res= check_table_access(thd,
++                                lex->exchange ? SELECT_ACL | FILE_ACL :
++                                SELECT_ACL,
++                                all_tables, 0);
++    }
++    else
++      res= check_access(thd,
++			lex->exchange ? SELECT_ACL | FILE_ACL : SELECT_ACL,
++			any_db, 0, 0, 0, 0);
++    if (res)
++      goto error;
++
++    if (!(res= open_and_lock_tables(thd, all_tables)))
++    {
++      if (lex->describe)
++      {
++        /*
++          We always use select_send for EXPLAIN, even if it's an EXPLAIN
++          for SELECT ... INTO OUTFILE: a user application should be able
++          to prepend EXPLAIN to any query and receive output for it,
++          even if the query itself redirects the output.
++        */
++	if (!(result= new select_send()))
++	  goto error;
++	else
++	  thd->send_explain_fields(result);
++	res= mysql_explain_union(thd, &thd->lex->unit, result);
++	if (lex->describe & DESCRIBE_EXTENDED)
++	{
++	  char buff[1024];
++	  String str(buff,(uint32) sizeof(buff), system_charset_info);
++	  str.length(0);
++	  thd->lex->unit.print(&str);
++	  str.append('\0');
++	  push_warning(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
++		       ER_YES, str.ptr());
++	}
++	result->send_eof();
++        delete result;
++      }
++      else
++      {
++	if (!result && !(result= new select_send()))
++          goto error;
++	query_cache_store_query(thd, all_tables);
++	res= handle_select(thd, lex, result, 0);
++        if (result != lex->result)
++          delete result;
++      }
++    }
++    break;
++  }
++  case SQLCOM_PREPARE:
++  {
++    mysql_sql_stmt_prepare(thd);
++    break;
++  }
++  case SQLCOM_EXECUTE:
++  {
++    mysql_sql_stmt_execute(thd);
++    break;
++  }
++  case SQLCOM_DEALLOCATE_PREPARE:
++  {
++    mysql_sql_stmt_close(thd);
++    break;
++  }
++  case SQLCOM_DO:
++    if (check_table_access(thd, SELECT_ACL, all_tables, 0) ||
++        open_and_lock_tables(thd, all_tables))
++      goto error;
++
++    res= mysql_do(thd, *lex->insert_list);
++    break;
++
++  case SQLCOM_EMPTY_QUERY:
++    send_ok(thd);
++    break;
++
++  case SQLCOM_HELP:
++    res= mysqld_help(thd,lex->help_arg);
++    break;
++
++#ifndef EMBEDDED_LIBRARY
++  case SQLCOM_PURGE:
++  {
++    if (check_global_access(thd, SUPER_ACL))
++      goto error;
++    /* PURGE MASTER LOGS TO 'file' */
++    res = purge_master_logs(thd, lex->to_log);
++    break;
++  }
++  case SQLCOM_PURGE_BEFORE:
++  {
++    Item *it;
++
++    if (check_global_access(thd, SUPER_ACL))
++      goto error;
++    /* PURGE MASTER LOGS BEFORE 'data' */
++    it= (Item *)lex->value_list.head();
++    if ((!it->fixed && it->fix_fields(lex->thd, &it)) ||
++        it->check_cols(1))
++    {
++      my_error(ER_WRONG_ARGUMENTS, MYF(0), "PURGE LOGS BEFORE");
++      goto error;
++    }
++    it= new Item_func_unix_timestamp(it);
++    /*
++      it is OK only emulate fix_fieds, because we need only
++      value of constant
++    */
++    it->quick_fix_field();
++    res = purge_master_logs_before_date(thd, (ulong)it->val_int());
++    break;
++  }
++#endif
++  case SQLCOM_SHOW_WARNS:
++  {
++    res= mysqld_show_warnings(thd, (ulong)
++			      ((1L << (uint) MYSQL_ERROR::WARN_LEVEL_NOTE) |
++			       (1L << (uint) MYSQL_ERROR::WARN_LEVEL_WARN) |
++			       (1L << (uint) MYSQL_ERROR::WARN_LEVEL_ERROR)
++			       ));
++    break;
++  }
++  case SQLCOM_SHOW_ERRORS:
++  {
++    res= mysqld_show_warnings(thd, (ulong)
++			      (1L << (uint) MYSQL_ERROR::WARN_LEVEL_ERROR));
++    break;
++  }
++  case SQLCOM_SHOW_NEW_MASTER:
++  {
++    if (check_global_access(thd, REPL_SLAVE_ACL))
++      goto error;
++    /* This query don't work now. See comment in repl_failsafe.cc */
++#ifndef WORKING_NEW_MASTER
++    my_error(ER_NOT_SUPPORTED_YET, MYF(0), "SHOW NEW MASTER");
++    goto error;
++#else
++    res = show_new_master(thd);
++    break;
++#endif
++  }
++
++#ifdef HAVE_REPLICATION
++  case SQLCOM_SHOW_SLAVE_HOSTS:
++  {
++    if (check_global_access(thd, REPL_SLAVE_ACL))
++      goto error;
++    res = show_slave_hosts(thd);
++    break;
++  }
++  case SQLCOM_SHOW_BINLOG_EVENTS:
++  {
++    if (check_global_access(thd, REPL_SLAVE_ACL))
++      goto error;
++    res = mysql_show_binlog_events(thd);
++    break;
++  }
++#endif
++
++  case SQLCOM_BACKUP_TABLE:
++  {
++    DBUG_ASSERT(first_table == all_tables && first_table != 0);
++    if (check_db_used(thd, all_tables) ||
++	check_table_access(thd, SELECT_ACL, all_tables, 0) ||
++	check_global_access(thd, FILE_ACL))
++      goto error; /* purecov: inspected */
++    thd->enable_slow_log= opt_log_slow_admin_statements;
++    res = mysql_backup_table(thd, first_table);
++    select_lex->table_list.first= (byte*) first_table;
++    lex->query_tables=all_tables;
++    break;
++  }
++  case SQLCOM_RESTORE_TABLE:
++  {
++    DBUG_ASSERT(first_table == all_tables && first_table != 0);
++    if (check_db_used(thd, all_tables) ||
++	check_table_access(thd, INSERT_ACL, all_tables, 0) ||
++	check_global_access(thd, FILE_ACL))
++      goto error; /* purecov: inspected */
++    thd->enable_slow_log= opt_log_slow_admin_statements;
++    res = mysql_restore_table(thd, first_table);
++    select_lex->table_list.first= (byte*) first_table;
++    lex->query_tables=all_tables;
++    break;
++  }
++  case SQLCOM_ASSIGN_TO_KEYCACHE:
++  {
++    DBUG_ASSERT(first_table == all_tables && first_table != 0);
++    if (check_db_used(thd, all_tables) ||
++        check_access(thd, INDEX_ACL, first_table->db,
++                     &first_table->grant.privilege, 0, 0,
++                     test(first_table->schema_table)))
++      goto error;
++    res= mysql_assign_to_keycache(thd, first_table, &lex->ident);
++    break;
++  }
++  case SQLCOM_PRELOAD_KEYS:
++  {
++    DBUG_ASSERT(first_table == all_tables && first_table != 0);
++    if (check_db_used(thd, all_tables) ||
++	check_access(thd, INDEX_ACL, first_table->db,
++                     &first_table->grant.privilege, 0, 0,
++                     test(first_table->schema_table)))
++      goto error;
++    res = mysql_preload_keys(thd, first_table);
++    break;
++  }
++#ifdef HAVE_REPLICATION
++  case SQLCOM_CHANGE_MASTER:
++  {
++    if (check_global_access(thd, SUPER_ACL))
++      goto error;
++    pthread_mutex_lock(&LOCK_active_mi);
++    res = change_master(thd,active_mi);
++    pthread_mutex_unlock(&LOCK_active_mi);
++    break;
++  }
++  case SQLCOM_SHOW_SLAVE_STAT:
++  {
++    /* Accept one of two privileges */
++    if (check_global_access(thd, SUPER_ACL | REPL_CLIENT_ACL))
++      goto error;
++    pthread_mutex_lock(&LOCK_active_mi);
++    res = show_master_info(thd,active_mi);
++    pthread_mutex_unlock(&LOCK_active_mi);
++    break;
++  }
++  case SQLCOM_SHOW_MASTER_STAT:
++  {
++    /* Accept one of two privileges */
++    if (check_global_access(thd, SUPER_ACL | REPL_CLIENT_ACL))
++      goto error;
++    res = show_binlog_info(thd);
++    break;
++  }
++
++  case SQLCOM_LOAD_MASTER_DATA: // sync with master
++    if (check_global_access(thd, SUPER_ACL))
++      goto error;
++    if (end_active_trans(thd))
++      goto error;
++    else
++      res = load_master_data(thd);
++    break;
++#endif /* HAVE_REPLICATION */
++#ifdef HAVE_NDBCLUSTER_DB
++  case SQLCOM_SHOW_NDBCLUSTER_STATUS:
++    {
++      res = ndbcluster_show_status(thd);
++      break;
++    }
++#endif
++#ifdef HAVE_INNOBASE_DB
++  case SQLCOM_SHOW_INNODB_STATUS:
++    {
++      if (check_global_access(thd, SUPER_ACL))
++	goto error;
++      res = innodb_show_status(thd);
++      break;
++    }
++  case SQLCOM_SHOW_MUTEX_STATUS:
++    {
++      if (check_global_access(thd, SUPER_ACL))
++        goto error;
++      res = innodb_mutex_show_status(thd);
++      break;
++    }
++#endif
++#ifdef HAVE_REPLICATION
++  case SQLCOM_LOAD_MASTER_TABLE:
++  {
++    DBUG_ASSERT(first_table == all_tables && first_table != 0);
++    DBUG_ASSERT(first_table->db); /* Must be set in the parser */
++
++    if (check_access(thd, CREATE_ACL, first_table->db,
++		     &first_table->grant.privilege, 0, 0,
++                     test(first_table->schema_table)))
++      goto error;				/* purecov: inspected */
++    if (grant_option)
++    {
++      /* Check that the first table has CREATE privilege */
++      if (check_grant(thd, CREATE_ACL, all_tables, 0, 1, 0))
++	goto error;
++    }
++    if (strlen(first_table->table_name) > NAME_LEN)
++    {
++      my_error(ER_WRONG_TABLE_NAME, MYF(0), first_table->table_name);
++      break;
++    }
++    pthread_mutex_lock(&LOCK_active_mi);
++    /*
++      fetch_master_table will send the error to the client on failure.
++      Give error if the table already exists.
++    */
++    if (!fetch_master_table(thd, first_table->db, first_table->table_name,
++			    active_mi, 0, 0))
++    {
++      send_ok(thd);
++    }
++    pthread_mutex_unlock(&LOCK_active_mi);
++    break;
++  }
++#endif /* HAVE_REPLICATION */
++
++  case SQLCOM_CREATE_TABLE:
++  {
++    /* If CREATE TABLE of non-temporary table, do implicit commit */
++    if (!(lex->create_info.options & HA_LEX_CREATE_TMP_TABLE))
++    {
++      if (end_active_trans(thd))
++      {
++	res= -1;
++	break;
++      }
++    }
++    else 
++    {
++      /* So that CREATE TEMPORARY TABLE gets to binlog at commit/rollback */
++      thd->options|= OPTION_STATUS_NO_TRANS_UPDATE;
++    }
++    DBUG_ASSERT(first_table == all_tables && first_table != 0);
++    bool link_to_local;
++    // Skip first table, which is the table we are creating
++    TABLE_LIST *create_table= lex->unlink_first_table(&link_to_local);
++    TABLE_LIST *select_tables= lex->query_tables;
++
++    if ((res= create_table_precheck(thd, select_tables, create_table)))
++      goto end_with_restore_list;
++
++#ifndef HAVE_READLINK
++    lex->create_info.data_file_name=lex->create_info.index_file_name=0;
++#else
++    /* Fix names if symlinked tables */
++    if (append_file_to_dir(thd, &lex->create_info.data_file_name,
++			   create_table->table_name) ||
++	append_file_to_dir(thd, &lex->create_info.index_file_name,
++			   create_table->table_name))
++      goto end_with_restore_list;
++#endif
++    /*
++      If we are using SET CHARSET without DEFAULT, add an implicit
++      DEFAULT to not confuse old users. (This may change).
++    */
++    if ((lex->create_info.used_fields & 
++	 (HA_CREATE_USED_DEFAULT_CHARSET | HA_CREATE_USED_CHARSET)) ==
++	HA_CREATE_USED_CHARSET)
++    {
++      lex->create_info.used_fields&= ~HA_CREATE_USED_CHARSET;
++      lex->create_info.used_fields|= HA_CREATE_USED_DEFAULT_CHARSET;
++      lex->create_info.default_table_charset= lex->create_info.table_charset;
++      lex->create_info.table_charset= 0;
++    }
++    /*
++      The create-select command will open and read-lock the select table
++      and then create, open and write-lock the new table. If a global
++      read lock steps in, we get a deadlock. The write lock waits for
++      the global read lock, while the global read lock waits for the
++      select table to be closed. So we wait until the global readlock is
++      gone before starting both steps. Note that
++      wait_if_global_read_lock() sets a protection against a new global
++      read lock when it succeeds. This needs to be released by
++      start_waiting_global_read_lock(). We protect the normal CREATE
++      TABLE in the same way. That way we avoid that a new table is
++      created during a gobal read lock.
++    */
++    if (!thd->locked_tables &&
++        !(need_start_waiting= !wait_if_global_read_lock(thd, 0, 1)))
++    {
++      res= 1;
++      goto end_with_restore_list;
++    }
++    if (select_lex->item_list.elements)		// With select
++    {
++      select_result *result;
++
++      select_lex->options|= SELECT_NO_UNLOCK;
++      unit->set_limit(select_lex);
++
++      if (!(res= open_and_lock_tables(thd, select_tables)))
++      {
++        /*
++          Is table which we are changing used somewhere in other parts
++          of query
++        */
++        if (!(lex->create_info.options & HA_LEX_CREATE_TMP_TABLE))
++        {
++          TABLE_LIST *duplicate;
++          if ((duplicate= unique_table(thd, create_table, select_tables)))
++          {
++            update_non_unique_table_error(create_table, "CREATE", duplicate);
++            res= 1;
++            goto end_with_restore_list;
++          }
++        }
++        /* If we create merge table, we have to test tables in merge, too */
++        if (lex->create_info.used_fields & HA_CREATE_USED_UNION)
++        {
++          TABLE_LIST *tab;
++          for (tab= (TABLE_LIST*) lex->create_info.merge_list.first;
++               tab;
++               tab= tab->next_local)
++          {
++            TABLE_LIST *duplicate;
++            if ((duplicate= unique_table(thd, tab, select_tables)))
++            {
++              update_non_unique_table_error(tab, "CREATE", duplicate);
++              res= 1;
++              goto end_with_restore_list;
++            }
++          }
++        }
++
++        if ((result= new select_create(create_table,
++				       &lex->create_info,
++				       lex->create_list,
++				       lex->key_list,
++				       select_lex->item_list,
++				       lex->duplicates,
++				       lex->ignore)))
++        {
++          /*
++            CREATE from SELECT give its SELECT_LEX for SELECT,
++            and item_list belong to SELECT
++          */
++          res= handle_select(thd, lex, result, 0);
++          delete result;
++        }
++	/* reset for PS */
++	lex->create_list.empty();
++	lex->key_list.empty();
++      }
++    }
++    else
++    {
++      /* regular create */
++      if (lex->name)
++        res= mysql_create_like_table(thd, create_table, &lex->create_info, 
++                                     (Table_ident *)lex->name); 
++      else
++      {
++        res= mysql_create_table(thd, create_table->db,
++				create_table->table_name, &lex->create_info,
++				lex->create_list,
++				lex->key_list, 0, 0);
++      }
++      if (!res)
++	send_ok(thd);
++    }
++
++    /* put tables back for PS rexecuting */
++end_with_restore_list:
++    lex->link_first_table_back(create_table, link_to_local);
++    break;
++  }
++  case SQLCOM_CREATE_INDEX:
++    DBUG_ASSERT(first_table == all_tables && first_table != 0);
++    if (check_one_table_access(thd, INDEX_ACL, all_tables))
++      goto error; /* purecov: inspected */
++    thd->enable_slow_log= opt_log_slow_admin_statements;
++    if (end_active_trans(thd))
++      goto error;
++    else
++      res = mysql_create_index(thd, first_table, lex->key_list);
++    break;
++
++#ifdef HAVE_REPLICATION
++  case SQLCOM_SLAVE_START:
++  {
++    pthread_mutex_lock(&LOCK_active_mi);
++    start_slave(thd,active_mi,1 /* net report*/);
++    pthread_mutex_unlock(&LOCK_active_mi);
++    break;
++  }
++  case SQLCOM_SLAVE_STOP:
++  /*
++    If the client thread has locked tables, a deadlock is possible.
++    Assume that
++    - the client thread does LOCK TABLE t READ.
++    - then the master updates t.
++    - then the SQL slave thread wants to update t,
++      so it waits for the client thread because t is locked by it.
++    - then the client thread does SLAVE STOP.
++      SLAVE STOP waits for the SQL slave thread to terminate its
++      update t, which waits for the client thread because t is locked by it.
++    To prevent that, refuse SLAVE STOP if the
++    client thread has locked tables
++  */
++  if (thd->locked_tables || thd->active_transaction() || thd->global_read_lock)
++  {
++    my_message(ER_LOCK_OR_ACTIVE_TRANSACTION,
++               ER(ER_LOCK_OR_ACTIVE_TRANSACTION), MYF(0));
++    goto error;
++  }
++  {
++    pthread_mutex_lock(&LOCK_active_mi);
++    stop_slave(thd,active_mi,1/* net report*/);
++    pthread_mutex_unlock(&LOCK_active_mi);
++    break;
++  }
++#endif /* HAVE_REPLICATION */
++
++  case SQLCOM_ALTER_TABLE:
++    DBUG_ASSERT(first_table == all_tables && first_table != 0);
++    {
++      ulong priv=0;
++      if (lex->name && (!lex->name[0] || strlen(lex->name) > NAME_LEN))
++      {
++	my_error(ER_WRONG_TABLE_NAME, MYF(0), lex->name);
++        goto error;
++      }
++      /* Must be set in the parser */
++      DBUG_ASSERT(select_lex->db);
++      if (check_access(thd, ALTER_ACL, first_table->db,
++		       &first_table->grant.privilege, 0, 0,
++                       test(first_table->schema_table)) ||
++	  check_access(thd,INSERT_ACL | CREATE_ACL,select_lex->db,&priv,0,0,
++                       is_schema_db(select_lex->db))||
++	  check_merge_table_access(thd, first_table->db,
++				   (TABLE_LIST *)
++				   lex->create_info.merge_list.first))
++	goto error;				/* purecov: inspected */
++      if (grant_option)
++      {
++	if (check_grant(thd, ALTER_ACL, all_tables, 0, UINT_MAX, 0))
++	  goto error;
++	if (lex->name && !test_all_bits(priv,INSERT_ACL | CREATE_ACL))
++	{					// Rename of table
++	  TABLE_LIST tmp_table;
++	  bzero((char*) &tmp_table,sizeof(tmp_table));
++	  tmp_table.table_name=lex->name;
++	  tmp_table.db=select_lex->db;
++	  tmp_table.grant.privilege=priv;
++	  if (check_grant(thd, INSERT_ACL | CREATE_ACL, &tmp_table, 0,
++			  UINT_MAX, 0))
++	    goto error;
++	}
++      }
++      /* Don't yet allow changing of symlinks with ALTER TABLE */
++      if (lex->create_info.data_file_name)
++        push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, 0,
++                     "DATA DIRECTORY option ignored");
++      if (lex->create_info.index_file_name)
++        push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, 0,
++                     "INDEX DIRECTORY option ignored");
++      lex->create_info.data_file_name=lex->create_info.index_file_name=0;
++      /* ALTER TABLE ends previous transaction */
++      if (end_active_trans(thd))
++	goto error;
++      else
++      {
++        if (!thd->locked_tables &&
++            !(need_start_waiting= !wait_if_global_read_lock(thd, 0, 1)))
++        {
++          res= 1;
++          break;
++        }
++
++        thd->enable_slow_log= opt_log_slow_admin_statements;
++	res= mysql_alter_table(thd, select_lex->db, lex->name,
++			       &lex->create_info,
++			       first_table, lex->create_list,
++			       lex->key_list,
++			       select_lex->order_list.elements,
++                               (ORDER *) select_lex->order_list.first,
++			       lex->ignore, &lex->alter_info, 1);
++      }
++      break;
++    }
++  case SQLCOM_RENAME_TABLE:
++  {
++    DBUG_ASSERT(first_table == all_tables && first_table != 0);
++    TABLE_LIST *table;
++    if (check_db_used(thd, all_tables))
++      goto error;
++    for (table= first_table; table; table= table->next_local->next_local)
++    {
++      if (check_access(thd, ALTER_ACL | DROP_ACL, table->db,
++		       &table->grant.privilege,0,0, test(table->schema_table)) ||
++	  check_access(thd, INSERT_ACL | CREATE_ACL, table->next_local->db,
++		       &table->next_local->grant.privilege, 0, 0,
++                       test(table->next_local->schema_table)))
++	goto error;
++      if (grant_option)
++      {
++	TABLE_LIST old_list, new_list;
++	/*
++	  we do not need initialize old_list and new_list because we will
++	  come table[0] and table->next[0] there
++	*/
++	old_list= table[0];
++	new_list= table->next_local[0];
++	if (check_grant(thd, ALTER_ACL | DROP_ACL, &old_list, 0, 1, 0) ||
++	    (!test_all_bits(table->next_local->grant.privilege,
++			    INSERT_ACL | CREATE_ACL) &&
++	     check_grant(thd, INSERT_ACL | CREATE_ACL, &new_list, 0, 1, 0)))
++	  goto error;
++      }
++    }
++    query_cache_invalidate3(thd, first_table, 0);
++    if (end_active_trans(thd) || mysql_rename_tables(thd, first_table))
++      goto error;
++    break;
++  }
++#ifndef EMBEDDED_LIBRARY
++  case SQLCOM_SHOW_BINLOGS:
++#ifdef DONT_ALLOW_SHOW_COMMANDS
++    my_message(ER_NOT_ALLOWED_COMMAND, ER(ER_NOT_ALLOWED_COMMAND),
++               MYF(0)); /* purecov: inspected */
++    goto error;
++#else
++    {
++      if (check_global_access(thd, SUPER_ACL))
++	goto error;
++      res = show_binlogs(thd);
++      break;
++    }
++#endif
++#endif /* EMBEDDED_LIBRARY */
++  case SQLCOM_SHOW_CREATE:
++    DBUG_ASSERT(first_table == all_tables && first_table != 0);
++#ifdef DONT_ALLOW_SHOW_COMMANDS
++    my_message(ER_NOT_ALLOWED_COMMAND, ER(ER_NOT_ALLOWED_COMMAND),
++               MYF(0)); /* purecov: inspected */
++    goto error;
++#else
++    {
++      /* Ignore temporary tables if this is "SHOW CREATE VIEW" */
++      if (lex->only_view)
++        first_table->skip_temporary= 1;
++
++      if (check_db_used(thd, all_tables) ||
++	  check_access(thd, SELECT_ACL | EXTRA_ACL, first_table->db,
++		       &first_table->grant.privilege, 0, 0, 
++                       test(first_table->schema_table)))
++	goto error;
++      if (grant_option && check_grant(thd, SELECT_ACL, all_tables, 2, UINT_MAX, 0))
++	goto error;
++      res= mysqld_show_create(thd, first_table);
++      break;
++    }
++#endif
++  case SQLCOM_CHECKSUM:
++  {
++    DBUG_ASSERT(first_table == all_tables && first_table != 0);
++    if (check_db_used(thd, all_tables) ||
++	check_table_access(thd, SELECT_ACL | EXTRA_ACL, all_tables, 0))
++      goto error; /* purecov: inspected */
++    res = mysql_checksum_table(thd, first_table, &lex->check_opt);
++    break;
++  }
++  case SQLCOM_REPAIR:
++  {
++    DBUG_ASSERT(first_table == all_tables && first_table != 0);
++    if (check_db_used(thd, all_tables) ||
++	check_table_access(thd, SELECT_ACL | INSERT_ACL, all_tables, 0))
++      goto error; /* purecov: inspected */
++    thd->enable_slow_log= opt_log_slow_admin_statements;
++    res= mysql_repair_table(thd, first_table, &lex->check_opt);
++    /* ! we write after unlocking the table */
++    if (!res && !lex->no_write_to_binlog)
++    {
++      /* Presumably, REPAIR and binlog writing doesn't require synchronization */
++      if (mysql_bin_log.is_open())
++      {
++	thd->clear_error(); // No binlog error generated
++        Query_log_event qinfo(thd, thd->query, thd->query_length, 0, FALSE);
++        mysql_bin_log.write(&qinfo);
++      }
++    }
++    select_lex->table_list.first= (byte*) first_table;
++    lex->query_tables=all_tables;
++    break;
++  }
++  case SQLCOM_CHECK:
++  {
++    DBUG_ASSERT(first_table == all_tables && first_table != 0);
++    if (check_db_used(thd, all_tables) ||
++	check_table_access(thd, SELECT_ACL | EXTRA_ACL , all_tables, 0))
++      goto error; /* purecov: inspected */
++    thd->enable_slow_log= opt_log_slow_admin_statements;
++    res = mysql_check_table(thd, first_table, &lex->check_opt);
++    select_lex->table_list.first= (byte*) first_table;
++    lex->query_tables=all_tables;
++    break;
++  }
++  case SQLCOM_ANALYZE:
++  {
++    DBUG_ASSERT(first_table == all_tables && first_table != 0);
++    if (check_db_used(thd, all_tables) ||
++	check_table_access(thd, SELECT_ACL | INSERT_ACL, all_tables, 0))
++      goto error; /* purecov: inspected */
++    thd->enable_slow_log= opt_log_slow_admin_statements;
++    res = mysql_analyze_table(thd, first_table, &lex->check_opt);
++    /* ! we write after unlocking the table */
++    if (!res && !lex->no_write_to_binlog)
++    {
++      /* Presumably, ANALYZE and binlog writing doesn't require synchronization */
++      if (mysql_bin_log.is_open())
++      {
++	thd->clear_error(); // No binlog error generated
++        Query_log_event qinfo(thd, thd->query, thd->query_length, 0, FALSE);
++        mysql_bin_log.write(&qinfo);
++      }
++    }
++    select_lex->table_list.first= (byte*) first_table;
++    lex->query_tables=all_tables;
++    break;
++  }
++
++  case SQLCOM_OPTIMIZE:
++  {
++    DBUG_ASSERT(first_table == all_tables && first_table != 0);
++    if (check_db_used(thd, all_tables) ||
++	check_table_access(thd, SELECT_ACL | INSERT_ACL, all_tables, 0))
++      goto error; /* purecov: inspected */
++    thd->enable_slow_log= opt_log_slow_admin_statements;
++    res= (specialflag & (SPECIAL_SAFE_MODE | SPECIAL_NO_NEW_FUNC)) ?
++      mysql_recreate_table(thd, first_table, 1) :
++      mysql_optimize_table(thd, first_table, &lex->check_opt);
++    /* ! we write after unlocking the table */
++    if (!res && !lex->no_write_to_binlog)
++    {
++      /* Presumably, OPTIMIZE and binlog writing doesn't require synchronization */
++      if (mysql_bin_log.is_open())
++      {
++	thd->clear_error(); // No binlog error generated
++        Query_log_event qinfo(thd, thd->query, thd->query_length, 0, FALSE);
++        mysql_bin_log.write(&qinfo);
++      }
++    }
++    select_lex->table_list.first= (byte*) first_table;
++    lex->query_tables=all_tables;
++    break;
++  }
++  case SQLCOM_UPDATE:
++    DBUG_ASSERT(first_table == all_tables && first_table != 0);
++    if (update_precheck(thd, all_tables))
++      break;
++    DBUG_ASSERT(select_lex->offset_limit == 0);
++    unit->set_limit(select_lex);
++    res= (result= mysql_update(thd, all_tables,
++                               select_lex->item_list,
++                               lex->value_list,
++                               select_lex->where,
++                               select_lex->order_list.elements,
++                               (ORDER *) select_lex->order_list.first,
++                               unit->select_limit_cnt,
++                               lex->duplicates, lex->ignore));
++    /* mysql_update return 2 if we need to switch to multi-update */
++    if (result != 2)
++      break;
++  case SQLCOM_UPDATE_MULTI:
++  {
++    DBUG_ASSERT(first_table == all_tables && first_table != 0);
++    /* if we switched from normal update, rights are checked */
++    if (result != 2)
++    {
++      if ((res= multi_update_precheck(thd, all_tables)))
++        break;
++    }
++    else
++      res= 0;
++
++    res= mysql_multi_update_prepare(thd);
++
++#ifdef HAVE_REPLICATION
++    /* Check slave filtering rules */
++    if (unlikely(thd->slave_thread))
++    {
++      if (all_tables_not_ok(thd, all_tables))
++      {
++        if (res!= 0)
++        {
++          res= 0;             /* don't care of prev failure  */
++          thd->clear_error(); /* filters are of highest prior */
++        }
++        /* we warn the slave SQL thread */
++        my_error(ER_SLAVE_IGNORED_TABLE, MYF(0));
++        break;
++      }
++      if (res)
++        break;
++    }
++    else
++    {
++#endif /* HAVE_REPLICATION */
++      if (res)
++        break;
++      if (opt_readonly &&
++	  !(thd->security_ctx->master_access & SUPER_ACL) &&
++	  some_non_temp_table_to_be_updated(thd, all_tables))
++      {
++	my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--read-only");
++	break;
++      }
++#ifdef HAVE_REPLICATION
++    }  /* unlikely */
++#endif
++
++    res= mysql_multi_update(thd, all_tables,
++                            &select_lex->item_list,
++                            &lex->value_list,
++                            select_lex->where,
++                            select_lex->options,
++                            lex->duplicates, lex->ignore, unit, select_lex);
++    break;
++  }
++  case SQLCOM_REPLACE:
++  case SQLCOM_INSERT:
++  {
++    DBUG_ASSERT(first_table == all_tables && first_table != 0);
++    if ((res= insert_precheck(thd, all_tables)))
++      break;
++
++    if (!thd->locked_tables &&
++        !(need_start_waiting= !wait_if_global_read_lock(thd, 0, 1)))
++    {
++      res= 1;
++      break;
++    }
++
++    res= mysql_insert(thd, all_tables, lex->field_list, lex->many_values,
++		      lex->update_list, lex->value_list,
++                      lex->duplicates, lex->ignore);
++
++    /*
++      If we have inserted into a VIEW, and the base table has
++      AUTO_INCREMENT column, but this column is not accessible through
++      a view, then we should restore LAST_INSERT_ID to the value it
++      had before the statement.
++    */
++    if (first_table->view && !first_table->contain_auto_increment)
++      thd->last_insert_id= thd->current_insert_id;
++
++    break;
++  }
++  case SQLCOM_REPLACE_SELECT:
++  case SQLCOM_INSERT_SELECT:
++  {
++    select_result *result;
++    DBUG_ASSERT(first_table == all_tables && first_table != 0);
++    if ((res= insert_precheck(thd, all_tables)))
++      break;
++
++    /* Fix lock for first table */
++    if (first_table->lock_type == TL_WRITE_DELAYED)
++      first_table->lock_type= TL_WRITE;
++
++    /* Don't unlock tables until command is written to binary log */
++    select_lex->options|= SELECT_NO_UNLOCK;
++
++    unit->set_limit(select_lex);
++
++    if (! thd->locked_tables &&
++        ! (need_start_waiting= ! wait_if_global_read_lock(thd, 0, 1)))
++    {
++      res= 1;
++      break;
++    }
++
++    if (!(res= open_and_lock_tables(thd, all_tables)))
++    {
++      /* Skip first table, which is the table we are inserting in */
++      TABLE_LIST *second_table= first_table->next_local;
++      select_lex->table_list.first= (byte*) second_table;
++      select_lex->context.table_list= 
++        select_lex->context.first_name_resolution_table= second_table;
++      res= mysql_insert_select_prepare(thd);
++      if (!res && (result= new select_insert(first_table, first_table->table,
++                                             &lex->field_list,
++                                             &lex->update_list,
++                                             &lex->value_list,
++                                             lex->duplicates, lex->ignore)))
++      {
++	res= handle_select(thd, lex, result, OPTION_SETUP_TABLES_DONE);
++        /*
++          Invalidate the table in the query cache if something changed
++          after unlocking when changes become visible.
++          TODO: this is workaround. right way will be move invalidating in
++          the unlock procedure.
++        */
++        if (first_table->lock_type ==  TL_WRITE_CONCURRENT_INSERT &&
++            thd->lock)
++        {
++          /* INSERT ... SELECT should invalidate only the very first table */
++          TABLE_LIST *save_table= first_table->next_local;
++          first_table->next_local= 0;
++          mysql_unlock_tables(thd, thd->lock);
++          query_cache_invalidate3(thd, first_table, 1);
++          first_table->next_local= save_table;
++          thd->lock=0;
++        }
++        delete result;
++      }
++      /* revert changes for SP */
++      select_lex->table_list.first= (byte*) first_table;
++    }
++
++    /*
++      If we have inserted into a VIEW, and the base table has
++      AUTO_INCREMENT column, but this column is not accessible through
++      a view, then we should restore LAST_INSERT_ID to the value it
++      had before the statement.
++    */
++    if (first_table->view && !first_table->contain_auto_increment)
++      thd->last_insert_id= thd->current_insert_id;
++
++    break;
++  }
++  case SQLCOM_TRUNCATE:
++    if (end_active_trans(thd))
++    {
++      res= -1;
++      break;
++    }
++    DBUG_ASSERT(first_table == all_tables && first_table != 0);
++    if (check_one_table_access(thd, DELETE_ACL, all_tables))
++      goto error;
++    /*
++      Don't allow this within a transaction because we want to use
++      re-generate table
++    */
++    if (thd->locked_tables || thd->active_transaction())
++    {
++      my_message(ER_LOCK_OR_ACTIVE_TRANSACTION,
++                 ER(ER_LOCK_OR_ACTIVE_TRANSACTION), MYF(0));
++      goto error;
++    }
++
++    res= mysql_truncate(thd, first_table, 0);
++    break;
++  case SQLCOM_DELETE:
++  {
++    DBUG_ASSERT(first_table == all_tables && first_table != 0);
++    if ((res= delete_precheck(thd, all_tables)))
++      break;
++    DBUG_ASSERT(select_lex->offset_limit == 0);
++    unit->set_limit(select_lex);
++
++    if (!thd->locked_tables &&
++        !(need_start_waiting= !wait_if_global_read_lock(thd, 0, 1)))
++    {
++      res= 1;
++      break;
++    }
++
++    res = mysql_delete(thd, all_tables, select_lex->where,
++                       &select_lex->order_list,
++                       unit->select_limit_cnt, select_lex->options,
++                       FALSE);
++    break;
++  }
++  case SQLCOM_DELETE_MULTI:
++  {
++    DBUG_ASSERT(first_table == all_tables && first_table != 0);
++    TABLE_LIST *aux_tables=
++      (TABLE_LIST *)thd->lex->auxiliary_table_list.first;
++    multi_delete *result;
++
++    if (!thd->locked_tables &&
++        !(need_start_waiting= !wait_if_global_read_lock(thd, 0, 1)))
++    {
++      res= 1;
++      break;
++    }
++
++    if ((res= multi_delete_precheck(thd, all_tables)))
++      break;
++
++    /* condition will be TRUE on SP re-excuting */
++    if (select_lex->item_list.elements != 0)
++      select_lex->item_list.empty();
++    if (add_item_to_list(thd, new Item_null()))
++      goto error;
++
++    thd->proc_info="init";
++    if ((res= open_and_lock_tables(thd, all_tables)))
++      break;
++
++    if ((res= mysql_multi_delete_prepare(thd)))
++      goto error;
++
++    if (!thd->is_fatal_error && (result= new multi_delete(aux_tables,
++							  lex->table_count)))
++    {
++      res= mysql_select(thd, &select_lex->ref_pointer_array,
++			select_lex->get_table_list(),
++			select_lex->with_wild,
++			select_lex->item_list,
++			select_lex->where,
++			0, (ORDER *)NULL, (ORDER *)NULL, (Item *)NULL,
++			(ORDER *)NULL,
++			select_lex->options | thd->options |
++			SELECT_NO_JOIN_CACHE | SELECT_NO_UNLOCK |
++                        OPTION_SETUP_TABLES_DONE,
++			result, unit, select_lex);
++      delete result;
++    }
++    else
++      res= TRUE;                                // Error
++    break;
++  }
++  case SQLCOM_DROP_TABLE:
++  {
++    DBUG_ASSERT(first_table == all_tables && first_table != 0);
++    if (!lex->drop_temporary)
++    {
++      if (check_table_access(thd, DROP_ACL, all_tables, 0))
++	goto error;				/* purecov: inspected */
++      if (end_active_trans(thd))
++        goto error;
++    }
++    else
++    {
++      /*
++	If this is a slave thread, we may sometimes execute some 
++	DROP / * 40005 TEMPORARY * / TABLE
++	that come from parts of binlogs (likely if we use RESET SLAVE or CHANGE
++	MASTER TO), while the temporary table has already been dropped.
++	To not generate such irrelevant "table does not exist errors",
++	we silently add IF EXISTS if TEMPORARY was used.
++      */
++      if (thd->slave_thread)
++	lex->drop_if_exists= 1;
++
++      /* So that DROP TEMPORARY TABLE gets to binlog at commit/rollback */
++      thd->options|= OPTION_STATUS_NO_TRANS_UPDATE;
++    }
++    /* DDL and binlog write order protected by LOCK_open */
++    res= mysql_rm_table(thd, first_table, lex->drop_if_exists,
++			lex->drop_temporary);
++  }
++  break;
++  case SQLCOM_DROP_INDEX:
++    DBUG_ASSERT(first_table == all_tables && first_table != 0);
++    if (check_one_table_access(thd, INDEX_ACL, all_tables))
++      goto error;				/* purecov: inspected */
++    if (end_active_trans(thd))
++      goto error;
++    else
++      res = mysql_drop_index(thd, first_table, &lex->alter_info);
++    break;
++  case SQLCOM_SHOW_PROCESSLIST:
++    if (!thd->security_ctx->priv_user[0] &&
++        check_global_access(thd,PROCESS_ACL))
++      break;
++    mysqld_list_processes(thd,
++			  (thd->security_ctx->master_access & PROCESS_ACL ?
++                           NullS :
++                           thd->security_ctx->priv_user),
++                          lex->verbose);
++    break;
++  case SQLCOM_SHOW_STORAGE_ENGINES:
++    res= mysqld_show_storage_engines(thd);
++    break;
++  case SQLCOM_SHOW_PRIVILEGES:
++    res= mysqld_show_privileges(thd);
++    break;
++  case SQLCOM_SHOW_COLUMN_TYPES:
++    res= mysqld_show_column_types(thd);
++    break;
++  case SQLCOM_SHOW_LOGS:
++#ifdef DONT_ALLOW_SHOW_COMMANDS
++    my_message(ER_NOT_ALLOWED_COMMAND, ER(ER_NOT_ALLOWED_COMMAND),
++               MYF(0));	/* purecov: inspected */
++    goto error;
++#else
++    {
++      if (grant_option && check_access(thd, FILE_ACL, any_db,0,0,0,0))
++	goto error;
++      res= mysqld_show_logs(thd);
++      break;
++    }
++#endif
++  case SQLCOM_CHANGE_DB:
++    if (!mysql_change_db(thd,select_lex->db,FALSE))
++      send_ok(thd);
++    break;
++
++  case SQLCOM_LOAD:
++  {
++    DBUG_ASSERT(first_table == all_tables && first_table != 0);
++    uint privilege= (lex->duplicates == DUP_REPLACE ?
++		     INSERT_ACL | DELETE_ACL : INSERT_ACL) |
++                    (lex->local_file ? 0 : FILE_ACL);
++
++    if (lex->local_file)
++    {
++      if (!(thd->client_capabilities & CLIENT_LOCAL_FILES) ||
++          !opt_local_infile)
++      {
++	my_message(ER_NOT_ALLOWED_COMMAND, ER(ER_NOT_ALLOWED_COMMAND), MYF(0));
++	goto error;
++      }
++    }
++
++    if (check_one_table_access(thd, privilege, all_tables))
++      goto error;
++
++    res= mysql_load(thd, lex->exchange, first_table, lex->field_list,
++                    lex->update_list, lex->value_list, lex->duplicates,
++                    lex->ignore, (bool) lex->local_file);
++    break;
++  }
++
++  case SQLCOM_SET_OPTION:
++  {
++    List<set_var_base> *lex_var_list= &lex->var_list;
++    if ((check_table_access(thd, SELECT_ACL, all_tables, 0) ||
++	 open_and_lock_tables(thd, all_tables)))
++      goto error;
++    if (lex->one_shot_set && not_all_support_one_shot(lex_var_list))
++    {
++      my_error(ER_RESERVED_SYNTAX, MYF(0), "SET ONE_SHOT");
++      goto error;
++    }
++    if (!(res= sql_set_variables(thd, lex_var_list)))
++    {
++      /*
++        If the previous command was a SET ONE_SHOT, we don't want to forget
++        about the ONE_SHOT property of that SET. So we use a |= instead of = .
++      */
++      thd->one_shot_set|= lex->one_shot_set;
++      send_ok(thd);
++    }
++    break;
++  }
++
++  case SQLCOM_UNLOCK_TABLES:
++    /*
++      It is critical for mysqldump --single-transaction --master-data that
++      UNLOCK TABLES does not implicitely commit a connection which has only
++      done FLUSH TABLES WITH READ LOCK + BEGIN. If this assumption becomes
++      false, mysqldump will not work.
++    */
++    unlock_locked_tables(thd);
++    if (thd->options & OPTION_TABLE_LOCK)
++    {
++      end_active_trans(thd);
++      thd->options&= ~(ulong) (OPTION_TABLE_LOCK);
++    }
++    if (thd->global_read_lock)
++      unlock_global_read_lock(thd);
++    send_ok(thd);
++    break;
++  case SQLCOM_LOCK_TABLES:
++    unlock_locked_tables(thd);
++    if (check_db_used(thd, all_tables) || end_active_trans(thd))
++      goto error;
++    if (check_table_access(thd, LOCK_TABLES_ACL | SELECT_ACL, all_tables, 0))
++      goto error;
++    thd->in_lock_tables=1;
++    thd->options|= OPTION_TABLE_LOCK;
++
++    if (!(res= simple_open_n_lock_tables(thd, all_tables)))
++    {
++#ifdef HAVE_QUERY_CACHE
++      if (thd->variables.query_cache_wlock_invalidate)
++	query_cache.invalidate_locked_for_write(first_table);
++#endif /*HAVE_QUERY_CACHE*/
++      thd->locked_tables=thd->lock;
++      thd->lock=0;
++      send_ok(thd);
++    }
++    else
++      thd->options&= ~(ulong) (OPTION_TABLE_LOCK);
++    thd->in_lock_tables=0;
++    break;
++  case SQLCOM_CREATE_DB:
++  {
++    if (end_active_trans(thd))
++    {
++      res= -1;
++      break;
++    }
++    char *alias;
++    if (!(alias=thd->strdup(lex->name)) || check_db_name(lex->name))
++    {
++      my_error(ER_WRONG_DB_NAME, MYF(0), lex->name);
++      break;
++    }
++    /*
++      If in a slave thread :
++      CREATE DATABASE DB was certainly not preceded by USE DB.
++      For that reason, db_ok() in sql/slave.cc did not check the
++      do_db/ignore_db. And as this query involves no tables, tables_ok()
++      above was not called. So we have to check rules again here.
++    */
++#ifdef HAVE_REPLICATION
++    if (thd->slave_thread &&
++	(!db_ok(lex->name, replicate_do_db, replicate_ignore_db) ||
++	 !db_ok_with_wild_table(lex->name)))
++    {
++      my_message(ER_SLAVE_IGNORED_TABLE, ER(ER_SLAVE_IGNORED_TABLE), MYF(0));
++      break;
++    }
++#endif
++    if (check_access(thd,CREATE_ACL,lex->name,0,1,0,is_schema_db(lex->name)))
++      break;
++    res= mysql_create_db(thd,(lower_case_table_names == 2 ? alias : lex->name),
++			 &lex->create_info, 0);
++    break;
++  }
++  case SQLCOM_DROP_DB:
++  {
++    if (end_active_trans(thd))
++    {
++      res= -1;
++      break;
++    }
++    if (check_db_name(lex->name))
++    {
++      my_error(ER_WRONG_DB_NAME, MYF(0), lex->name);
++      break;
++    }
++    /*
++      If in a slave thread :
++      DROP DATABASE DB may not be preceded by USE DB.
++      For that reason, maybe db_ok() in sql/slave.cc did not check the 
++      do_db/ignore_db. And as this query involves no tables, tables_ok()
++      above was not called. So we have to check rules again here.
++    */
++#ifdef HAVE_REPLICATION
++    if (thd->slave_thread && 
++	(!db_ok(lex->name, replicate_do_db, replicate_ignore_db) ||
++	 !db_ok_with_wild_table(lex->name)))
++    {
++      my_message(ER_SLAVE_IGNORED_TABLE, ER(ER_SLAVE_IGNORED_TABLE), MYF(0));
++      break;
++    }
++#endif
++    if (check_access(thd,DROP_ACL,lex->name,0,1,0,is_schema_db(lex->name)))
++      break;
++    if (thd->locked_tables || thd->active_transaction())
++    {
++      my_message(ER_LOCK_OR_ACTIVE_TRANSACTION,
++                 ER(ER_LOCK_OR_ACTIVE_TRANSACTION), MYF(0));
++      goto error;
++    }
++    res= mysql_rm_db(thd, lex->name, lex->drop_if_exists, 0);
++    break;
++  }
++  case SQLCOM_ALTER_DB:
++  {
++    char *db= lex->name;
++    DBUG_ASSERT(db); /* Must be set in the parser */
++    if (!strip_sp(db) || check_db_name(db))
++    {
++      my_error(ER_WRONG_DB_NAME, MYF(0), lex->name);
++      break;
++    }
++    /*
++      If in a slave thread :
++      ALTER DATABASE DB may not be preceded by USE DB.
++      For that reason, maybe db_ok() in sql/slave.cc did not check the
++      do_db/ignore_db. And as this query involves no tables, tables_ok()
++      above was not called. So we have to check rules again here.
++    */
++#ifdef HAVE_REPLICATION
++    if (thd->slave_thread &&
++	(!db_ok(db, replicate_do_db, replicate_ignore_db) ||
++	 !db_ok_with_wild_table(db)))
++    {
++      my_message(ER_SLAVE_IGNORED_TABLE, ER(ER_SLAVE_IGNORED_TABLE), MYF(0));
++      break;
++    }
++#endif
++    if (check_access(thd, ALTER_ACL, db, 0, 1, 0, is_schema_db(db)))
++      break;
++    if (thd->locked_tables || thd->active_transaction())
++    {
++      my_message(ER_LOCK_OR_ACTIVE_TRANSACTION,
++                 ER(ER_LOCK_OR_ACTIVE_TRANSACTION), MYF(0));
++      goto error;
++    }
++    res= mysql_alter_db(thd, db, &lex->create_info);
++    break;
++  }
++  case SQLCOM_SHOW_CREATE_DB:
++  {
++    if (!strip_sp(lex->name) || check_db_name(lex->name))
++    {
++      my_error(ER_WRONG_DB_NAME, MYF(0), lex->name);
++      break;
++    }
++    if (check_access(thd,SELECT_ACL,lex->name,0,1,0,is_schema_db(lex->name)))
++      break;
++    res=mysqld_show_create_db(thd,lex->name,&lex->create_info);
++    break;
++  }
++  case SQLCOM_CREATE_FUNCTION:                  // UDF function
++  {
++    if (check_access(thd,INSERT_ACL,"mysql",0,1,0,0))
++      break;
++#ifdef HAVE_DLOPEN
++    if (sp_find_routine(thd, TYPE_ENUM_FUNCTION, lex->spname,
++                        &thd->sp_func_cache, FALSE))
++    {
++      my_error(ER_UDF_EXISTS, MYF(0), lex->spname->m_name.str);
++      goto error;
++    }
++    if (!(res = mysql_create_function(thd, &lex->udf)))
++      send_ok(thd);
++#else
++    my_error(ER_CANT_OPEN_LIBRARY, MYF(0), lex->udf.dl, 0, "feature disabled");
++    res= TRUE;
++#endif
++    break;
++  }
++#ifndef NO_EMBEDDED_ACCESS_CHECKS
++  case SQLCOM_CREATE_USER:
++  {
++    if (check_access(thd, INSERT_ACL, "mysql", 0, 1, 1, 0) &&
++        check_global_access(thd,CREATE_USER_ACL))
++      break;
++    if (end_active_trans(thd))
++      goto error;
++    /* Conditionally writes to binlog */
++    if (!(res= mysql_create_user(thd, lex->users_list)))
++      send_ok(thd);
++    break;
++  }
++  case SQLCOM_DROP_USER:
++  {
++    if (check_access(thd, DELETE_ACL, "mysql", 0, 1, 1, 0) &&
++        check_global_access(thd,CREATE_USER_ACL))
++      break;
++    if (end_active_trans(thd))
++      goto error;
++    /* Conditionally writes to binlog */
++    if (!(res= mysql_drop_user(thd, lex->users_list)))
++      send_ok(thd);
++    break;
++  }
++  case SQLCOM_RENAME_USER:
++  {
++    if (check_access(thd, UPDATE_ACL, "mysql", 0, 1, 1, 0) &&
++        check_global_access(thd,CREATE_USER_ACL))
++      break;
++    if (end_active_trans(thd))
++      goto error;
++    /* Conditionally writes to binlog */
++    if (!(res= mysql_rename_user(thd, lex->users_list)))
++      send_ok(thd);
++    break;
++  }
++  case SQLCOM_REVOKE_ALL:
++  {
++    if (check_access(thd, UPDATE_ACL, "mysql", 0, 1, 1, 0) &&
++        check_global_access(thd,CREATE_USER_ACL))
++      break;
++    /* Conditionally writes to binlog */
++    if (!(res = mysql_revoke_all(thd, lex->users_list)))
++      send_ok(thd);
++    break;
++  }
++  case SQLCOM_REVOKE:
++  case SQLCOM_GRANT:
++  {
++    if (check_access(thd, lex->grant | lex->grant_tot_col | GRANT_ACL,
++		     first_table ?  first_table->db : select_lex->db,
++		     first_table ? &first_table->grant.privilege : 0,
++		     first_table ? 0 : 1, 0,
++                     first_table ? (bool) first_table->schema_table :
++                     select_lex->db ? is_schema_db(select_lex->db) : 0))
++      goto error;
++
++    if (thd->security_ctx->user)              // If not replication
++    {
++      LEX_USER *user, *tmp_user;
++
++      List_iterator <LEX_USER> user_list(lex->users_list);
++      while ((tmp_user= user_list++))
++      {
++        if (!(user= get_current_user(thd, tmp_user)))
++          goto error;
++        if (specialflag & SPECIAL_NO_RESOLVE &&
++            hostname_requires_resolving(user->host.str))
++          push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
++                              ER_WARN_HOSTNAME_WONT_WORK,
++                              ER(ER_WARN_HOSTNAME_WONT_WORK),
++                              user->host.str);
++        // Are we trying to change a password of another user
++        DBUG_ASSERT(user->host.str != 0);
++        if (strcmp(thd->security_ctx->user, user->user.str) ||
++            my_strcasecmp(system_charset_info,
++                          user->host.str, thd->security_ctx->host_or_ip))
++        {
++          // TODO: use check_change_password()
++          if (is_acl_user(user->host.str, user->user.str) &&
++              user->password.str &&
++              check_access(thd, UPDATE_ACL,"mysql",0,1,1,0))
++          {
++            my_message(ER_PASSWORD_NOT_ALLOWED,
++                       ER(ER_PASSWORD_NOT_ALLOWED), MYF(0));
++            goto error;
++          }
++        }
++      }
++    }
++    if (first_table)
++    {
++      if (lex->type == TYPE_ENUM_PROCEDURE ||
++          lex->type == TYPE_ENUM_FUNCTION)
++      {
++        uint grants= lex->all_privileges 
++		   ? (PROC_ACLS & ~GRANT_ACL) | (lex->grant & GRANT_ACL)
++		   : lex->grant;
++        if (grant_option && 
++	    check_grant_routine(thd, grants | GRANT_ACL, all_tables,
++                                lex->type == TYPE_ENUM_PROCEDURE, 0))
++	  goto error;
++        /* Conditionally writes to binlog */
++        res= mysql_routine_grant(thd, all_tables,
++                                 lex->type == TYPE_ENUM_PROCEDURE, 
++                                 lex->users_list, grants,
++                                 lex->sql_command == SQLCOM_REVOKE, 0);
++      }
++      else
++      {
++	if (grant_option && check_grant(thd,
++					(lex->grant | lex->grant_tot_col |
++					 GRANT_ACL),
++					all_tables, 0, UINT_MAX, 0))
++	  goto error;
++        /* Conditionally writes to binlog */
++        res= mysql_table_grant(thd, all_tables, lex->users_list,
++			       lex->columns, lex->grant,
++			       lex->sql_command == SQLCOM_REVOKE);
++      }
++    }
++    else
++    {
++      if (lex->columns.elements || lex->type)
++      {
++	my_message(ER_ILLEGAL_GRANT_FOR_TABLE, ER(ER_ILLEGAL_GRANT_FOR_TABLE),
++                   MYF(0));
++        goto error;
++      }
++      else
++	/* Conditionally writes to binlog */
++	res = mysql_grant(thd, select_lex->db, lex->users_list, lex->grant,
++			  lex->sql_command == SQLCOM_REVOKE);
++      if (!res)
++      {
++	if (lex->sql_command == SQLCOM_GRANT)
++	{
++	  List_iterator <LEX_USER> str_list(lex->users_list);
++	  LEX_USER *user, *tmp_user;
++	  while ((tmp_user=str_list++))
++          {
++            if (!(user= get_current_user(thd, tmp_user)))
++              goto error;
++	    reset_mqh(user);
++          }
++	}
++      }
++    }
++    break;
++  }
++#endif /*!NO_EMBEDDED_ACCESS_CHECKS*/
++  case SQLCOM_RESET:
++    /*
++      RESET commands are never written to the binary log, so we have to
++      initialize this variable because RESET shares the same code as FLUSH
++    */
++    lex->no_write_to_binlog= 1;
++  case SQLCOM_FLUSH:
++  {
++    bool write_to_binlog;
++    if (check_global_access(thd,RELOAD_ACL))
++      goto error;
++
++    /*
++      reload_acl_and_cache() will tell us if we are allowed to write to the
++      binlog or not.
++    */
++    if (!reload_acl_and_cache(thd, lex->type, first_table, &write_to_binlog))
++    {
++      /*
++        We WANT to write and we CAN write.
++        ! we write after unlocking the table.
++      */
++      /* Presumably, RESET and binlog writing doesn't require synchronization */
++      if (!lex->no_write_to_binlog && write_to_binlog)
++      {
++        if (mysql_bin_log.is_open())
++        {
++          Query_log_event qinfo(thd, thd->query, thd->query_length, 0, FALSE);
++          mysql_bin_log.write(&qinfo);
++        }
++      }
++      send_ok(thd);
++    } 
++    
++    break;
++  }
++  case SQLCOM_KILL:
++  {
++    Item *it= (Item *)lex->value_list.head();
++
++    if ((!it->fixed && it->fix_fields(lex->thd, &it)) || it->check_cols(1))
++    {
++      my_message(ER_SET_CONSTANTS_ONLY, ER(ER_SET_CONSTANTS_ONLY),
++		 MYF(0));
++      goto error;
++    }
++    kill_one_thread(thd, (ulong)it->val_int(), lex->type & ONLY_KILL_QUERY);
++    break;
++  }
++#ifndef NO_EMBEDDED_ACCESS_CHECKS
++  case SQLCOM_SHOW_GRANTS:
++  {
++    LEX_USER *grant_user= get_current_user(thd, lex->grant_user);
++    if (!grant_user)
++      goto error;
++    if ((thd->security_ctx->priv_user &&
++	 !strcmp(thd->security_ctx->priv_user, grant_user->user.str)) ||
++	!check_access(thd, SELECT_ACL, "mysql",0,1,0,0))
++    {
++      res = mysql_show_grants(thd, grant_user);
++    }
++    break;
++  }
++#endif
++  case SQLCOM_HA_OPEN:
++    DBUG_ASSERT(first_table == all_tables && first_table != 0);
++    if (check_db_used(thd, all_tables) ||
++	check_table_access(thd, SELECT_ACL, all_tables, 0))
++      goto error;
++    res= mysql_ha_open(thd, first_table, 0);
++    break;
++  case SQLCOM_HA_CLOSE:
++    DBUG_ASSERT(first_table == all_tables && first_table != 0);
++    if (check_db_used(thd, all_tables))
++      goto error;
++    res= mysql_ha_close(thd, first_table);
++    break;
++  case SQLCOM_HA_READ:
++    DBUG_ASSERT(first_table == all_tables && first_table != 0);
++    /*
++      There is no need to check for table permissions here, because
++      if a user has no permissions to read a table, he won't be
++      able to open it (with SQLCOM_HA_OPEN) in the first place.
++    */
++    if (check_db_used(thd, all_tables))
++      goto error;
++    unit->set_limit(select_lex);
++    res= mysql_ha_read(thd, first_table, lex->ha_read_mode, lex->ident.str,
++                       lex->insert_list, lex->ha_rkey_mode, select_lex->where,
++                       unit->select_limit_cnt, unit->offset_limit_cnt);
++    break;
++
++  case SQLCOM_BEGIN:
++    if (thd->transaction.xid_state.xa_state != XA_NOTR)
++    {
++      my_error(ER_XAER_RMFAIL, MYF(0),
++               xa_state_names[thd->transaction.xid_state.xa_state]);
++      break;
++    }
++    if (begin_trans(thd))
++      goto error;
++    send_ok(thd);
++    break;
++  case SQLCOM_COMMIT:
++    if (end_trans(thd, lex->tx_release ? COMMIT_RELEASE :
++                              lex->tx_chain ? COMMIT_AND_CHAIN : COMMIT))
++      goto error;
++    send_ok(thd);
++    break;
++  case SQLCOM_ROLLBACK:
++    if (end_trans(thd, lex->tx_release ? ROLLBACK_RELEASE :
++                              lex->tx_chain ? ROLLBACK_AND_CHAIN : ROLLBACK))
++      goto error;
++    send_ok(thd);
++    break;
++  case SQLCOM_RELEASE_SAVEPOINT:
++  {
++    SAVEPOINT *sv;
++    for (sv=thd->transaction.savepoints; sv; sv=sv->prev)
++    {
++      if (my_strnncoll(system_charset_info,
++                       (uchar *)lex->ident.str, lex->ident.length,
++                       (uchar *)sv->name, sv->length) == 0)
++        break;
++    }
++    if (sv)
++    {
++      if (ha_release_savepoint(thd, sv))
++        res= TRUE; // cannot happen
++      else
++        send_ok(thd);
++      thd->transaction.savepoints=sv->prev;
++    }
++    else
++      my_error(ER_SP_DOES_NOT_EXIST, MYF(0), "SAVEPOINT", lex->ident.str);
++    break;
++  }
++  case SQLCOM_ROLLBACK_TO_SAVEPOINT:
++  {
++    SAVEPOINT *sv;
++    for (sv=thd->transaction.savepoints; sv; sv=sv->prev)
++    {
++      if (my_strnncoll(system_charset_info,
++                       (uchar *)lex->ident.str, lex->ident.length,
++                       (uchar *)sv->name, sv->length) == 0)
++        break;
++    }
++    if (sv)
++    {
++      if (ha_rollback_to_savepoint(thd, sv))
++        res= TRUE; // cannot happen
++      else
++      {
++        if ((thd->options & OPTION_STATUS_NO_TRANS_UPDATE) &&
++            !thd->slave_thread)
++          push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
++                       ER_WARNING_NOT_COMPLETE_ROLLBACK,
++                       ER(ER_WARNING_NOT_COMPLETE_ROLLBACK));
++        send_ok(thd);
++      }
++      thd->transaction.savepoints=sv;
++    }
++    else
++      my_error(ER_SP_DOES_NOT_EXIST, MYF(0), "SAVEPOINT", lex->ident.str);
++    break;
++  }
++  case SQLCOM_SAVEPOINT:
++    if (!(thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN) ||
++          thd->in_sub_stmt) || !opt_using_transactions)
++      send_ok(thd);
++    else
++    {
++      SAVEPOINT **sv, *newsv;
++      for (sv=&thd->transaction.savepoints; *sv; sv=&(*sv)->prev)
++      {
++        if (my_strnncoll(system_charset_info,
++                         (uchar *)lex->ident.str, lex->ident.length,
++                         (uchar *)(*sv)->name, (*sv)->length) == 0)
++          break;
++      }
++      if (*sv) /* old savepoint of the same name exists */
++      {
++        newsv=*sv;
++        ha_release_savepoint(thd, *sv); // it cannot fail
++        *sv=(*sv)->prev;
++      }
++      else if ((newsv=(SAVEPOINT *) alloc_root(&thd->transaction.mem_root,
++                                               savepoint_alloc_size)) == 0)
++      {
++        my_error(ER_OUT_OF_RESOURCES, MYF(0));
++        break;
++      }
++      newsv->name=strmake_root(&thd->transaction.mem_root,
++                               lex->ident.str, lex->ident.length);
++      newsv->length=lex->ident.length;
++      /*
++        if we'll get an error here, don't add new savepoint to the list.
++        we'll lose a little bit of memory in transaction mem_root, but it'll
++        be free'd when transaction ends anyway
++      */
++      if (ha_savepoint(thd, newsv))
++        res= TRUE;
++      else
++      {
++        newsv->prev=thd->transaction.savepoints;
++        thd->transaction.savepoints=newsv;
++        send_ok(thd);
++      }
++    }
++    break;
++  case SQLCOM_CREATE_PROCEDURE:
++  case SQLCOM_CREATE_SPFUNCTION:
++  {
++    uint namelen;
++    char *name;
++    int result= SP_INTERNAL_ERROR;
++
++    DBUG_ASSERT(lex->sphead != 0);
++    DBUG_ASSERT(lex->sphead->m_db.str); /* Must be initialized in the parser */
++    /*
++      Verify that the database name is allowed, optionally
++      lowercase it.
++    */
++    if (check_db_name(lex->sphead->m_db.str))
++    {
++      my_error(ER_WRONG_DB_NAME, MYF(0), lex->sphead->m_db.str);
++      goto create_sp_error;
++    }
++
++    /*
++      Check that a database directory with this name
++      exists. Design note: This won't work on virtual databases
++      like information_schema.
++    */
++    if (check_db_dir_existence(lex->sphead->m_db.str))
++    {
++      my_error(ER_BAD_DB_ERROR, MYF(0), lex->sphead->m_db.str);
++      goto create_sp_error;
++    }
++
++    if (check_access(thd, CREATE_PROC_ACL, lex->sphead->m_db.str, 0, 0, 0,
++                     is_schema_db(lex->sphead->m_db.str)))
++      goto create_sp_error;
++
++    if (end_active_trans(thd))
++      goto create_sp_error;
++
++    name= lex->sphead->name(&namelen);
++#ifdef HAVE_DLOPEN
++    if (lex->sphead->m_type == TYPE_ENUM_FUNCTION)
++    {
++      udf_func *udf = find_udf(name, namelen);
++
++      if (udf)
++      {
++        my_error(ER_UDF_EXISTS, MYF(0), name);
++        goto create_sp_error;
++      }
++    }
++#endif
++
++    /*
++      If the definer is not specified, this means that CREATE-statement missed
++      DEFINER-clause. DEFINER-clause can be missed in two cases:
++
++        - The user submitted a statement w/o the clause. This is a normal
++          case, we should assign CURRENT_USER as definer.
++
++        - Our slave received an updated from the master, that does not
++          replicate definer for stored rountines. We should also assign
++          CURRENT_USER as definer here, but also we should mark this routine
++          as NON-SUID. This is essential for the sake of backward
++          compatibility.
++
++          The problem is the slave thread is running under "special" user (@),
++          that actually does not exist. In the older versions we do not fail
++          execution of a stored routine if its definer does not exist and
++          continue the execution under the authorization of the invoker
++          (BUG#13198). And now if we try to switch to slave-current-user (@),
++          we will fail.
++
++          Actually, this leads to the inconsistent state of master and
++          slave (different definers, different SUID behaviour), but it seems,
++          this is the best we can do.
++    */
++
++    if (!lex->definer)
++    {
++      bool res= FALSE;
++      Query_arena original_arena;
++      Query_arena *ps_arena = thd->activate_stmt_arena_if_needed(&original_arena);
++
++      if (!(lex->definer= create_default_definer(thd)))
++        res= TRUE;
++
++      if (ps_arena)
++        thd->restore_active_arena(ps_arena, &original_arena);
++
++      /* Error has been already reported. */
++      if (res)
++        goto create_sp_error;
++
++      if (thd->slave_thread)
++        lex->sphead->m_chistics->suid= SP_IS_NOT_SUID;
++    }
++
++    /*
++      If the specified definer differs from the current user, we should check
++      that the current user has SUPER privilege (in order to create a stored
++      routine under another user one must have SUPER privilege).
++    */
++
++    else if (strcmp(lex->definer->user.str, thd->security_ctx->priv_user) ||
++        my_strcasecmp(system_charset_info,
++                      lex->definer->host.str,
++                      thd->security_ctx->priv_host))
++    {
++      if (check_global_access(thd, SUPER_ACL))
++      {
++        my_error(ER_SPECIFIC_ACCESS_DENIED_ERROR, MYF(0), "SUPER");
++        goto create_sp_error;
++      }
++    }
++
++    /* Check that the specified definer exists. Emit a warning if not. */
++
++#ifndef NO_EMBEDDED_ACCESS_CHECKS
++    if (!is_acl_user(lex->definer->host.str,
++                     lex->definer->user.str))
++    {
++      push_warning_printf(thd,
++                          MYSQL_ERROR::WARN_LEVEL_NOTE,
++                          ER_NO_SUCH_USER,
++                          ER(ER_NO_SUCH_USER),
++                          lex->definer->user.str,
++                          lex->definer->host.str);
++    }
++#endif /* NO_EMBEDDED_ACCESS_CHECKS */
++
++    res= (result= lex->sphead->create(thd));
++    switch (result) {
++    case SP_OK:
++#ifndef NO_EMBEDDED_ACCESS_CHECKS
++      /* only add privileges if really neccessary */
++      if (sp_automatic_privileges && !opt_noacl &&
++          check_routine_access(thd, DEFAULT_CREATE_PROC_ACLS,
++                               lex->sphead->m_db.str, name,
++                               lex->sql_command == SQLCOM_CREATE_PROCEDURE, 1))
++      {
++        if (sp_grant_privileges(thd, lex->sphead->m_db.str, name,
++                                lex->sql_command == SQLCOM_CREATE_PROCEDURE))
++          push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
++                       ER_PROC_AUTO_GRANT_FAIL,
++                       ER(ER_PROC_AUTO_GRANT_FAIL));
++        close_thread_tables(thd);
++      }
++#endif
++    break;
++    case SP_WRITE_ROW_FAILED:
++      my_error(ER_SP_ALREADY_EXISTS, MYF(0), SP_TYPE_STRING(lex), name);
++    break;
++    case SP_BAD_IDENTIFIER:
++      my_error(ER_TOO_LONG_IDENT, MYF(0), name);
++    break;
++    case SP_BODY_TOO_LONG:
++      my_error(ER_TOO_LONG_BODY, MYF(0), name);
++    break;
++    default:
++      my_error(ER_SP_STORE_FAILED, MYF(0), SP_TYPE_STRING(lex), name);
++    break;
++    } /* end switch */
++
++    /*
++      Capture all errors within this CASE and
++      clean up the environment.
++    */
++create_sp_error:
++    lex->unit.cleanup();
++    delete lex->sphead;
++    lex->sphead= 0;
++    if (result != SP_OK )
++      goto error;
++    send_ok(thd);
++    break; /* break super switch */
++  } /* end case group bracket */
++  case SQLCOM_CALL:
++    {
++      sp_head *sp;
++
++      /*
++        This will cache all SP and SF and open and lock all tables
++        required for execution.
++      */
++      if (check_table_access(thd, SELECT_ACL, all_tables, 0) ||
++	  open_and_lock_tables(thd, all_tables))
++       goto error;
++
++      /*
++        By this moment all needed SPs should be in cache so no need to look 
++        into DB. 
++      */
++      if (!(sp= sp_find_routine(thd, TYPE_ENUM_PROCEDURE, lex->spname,
++                                &thd->sp_proc_cache, TRUE)))
++      {
++	my_error(ER_SP_DOES_NOT_EXIST, MYF(0), "PROCEDURE",
++                 lex->spname->m_qname.str);
++	goto error;
++      }
++      else
++      {
++	ha_rows select_limit;
++        /* bits that should be cleared in thd->server_status */
++	uint bits_to_be_cleared= 0;
++        /*
++          Check that the stored procedure doesn't contain Dynamic SQL
++          and doesn't return result sets: such stored procedures can't
++          be called from a function or trigger.
++        */
++        if (thd->in_sub_stmt)
++        {
++          const char *where= (thd->in_sub_stmt & SUB_STMT_TRIGGER ?
++                              "trigger" : "function");
++          if (sp->is_not_allowed_in_function(where))
++            goto error;
++        }
++
++	my_bool nsok= thd->net.no_send_ok;
++	thd->net.no_send_ok= TRUE;
++	if (sp->m_flags & sp_head::MULTI_RESULTS)
++	{
++	  if (! (thd->client_capabilities & CLIENT_MULTI_RESULTS))
++	  {
++            /*
++              The client does not support multiple result sets being sent
++              back
++            */
++	    my_error(ER_SP_BADSELECT, MYF(0), sp->m_qname.str);
++	    thd->net.no_send_ok= nsok;
++	    goto error;
++	  }
++          /*
++            If SERVER_MORE_RESULTS_EXISTS is not set,
++            then remember that it should be cleared
++          */
++	  bits_to_be_cleared= (~thd->server_status &
++                               SERVER_MORE_RESULTS_EXISTS);
++	  thd->server_status|= SERVER_MORE_RESULTS_EXISTS;
++	}
++
++#ifndef NO_EMBEDDED_ACCESS_CHECKS
++	if (check_routine_access(thd, EXECUTE_ACL,
++				 sp->m_db.str, sp->m_name.str, TRUE, FALSE))
++	{
++	  thd->net.no_send_ok= nsok;
++	  goto error;
++	}
++#endif
++	select_limit= thd->variables.select_limit;
++	thd->variables.select_limit= HA_POS_ERROR;
++
++        /* 
++          We never write CALL statements into binlog:
++           - If the mode is non-prelocked, each statement will be logged
++             separately.
++           - If the mode is prelocked, the invoking statement will care
++             about writing into binlog.
++          So just execute the statement.
++        */
++	res= sp->execute_procedure(thd, &lex->value_list);
++	/*
++          If warnings have been cleared, we have to clear total_warn_count
++          too, otherwise the clients get confused.
++	 */
++	if (thd->warn_list.is_empty())
++	  thd->total_warn_count= 0;
++
++	thd->variables.select_limit= select_limit;
++
++	thd->net.no_send_ok= nsok;
++        thd->server_status&= ~bits_to_be_cleared;
++
++	if (!res)
++	  send_ok(thd, (ulong) (thd->row_count_func < 0 ? 0 :
++                                thd->row_count_func));
++	else
++	  goto error;		// Substatement should already have sent error
++      }
++      break;
++    }
++  case SQLCOM_ALTER_PROCEDURE:
++  case SQLCOM_ALTER_FUNCTION:
++    {
++      int result;
++      sp_head *sp;
++      st_sp_chistics chistics;
++
++      memcpy(&chistics, &lex->sp_chistics, sizeof(chistics));
++      if (lex->sql_command == SQLCOM_ALTER_PROCEDURE)
++        sp= sp_find_routine(thd, TYPE_ENUM_PROCEDURE, lex->spname,
++                            &thd->sp_proc_cache, FALSE);
++      else
++        sp= sp_find_routine(thd, TYPE_ENUM_FUNCTION, lex->spname,
++                            &thd->sp_func_cache, FALSE);
++      mysql_reset_errors(thd, 0);
++      if (! sp)
++      {
++	if (lex->spname->m_db.str)
++	  result= SP_KEY_NOT_FOUND;
++	else
++	{
++	  my_message(ER_NO_DB_ERROR, ER(ER_NO_DB_ERROR), MYF(0));
++	  goto error;
++	}
++      }
++      else
++      {
++        if (check_routine_access(thd, ALTER_PROC_ACL, sp->m_db.str, 
++				 sp->m_name.str,
++                                 lex->sql_command == SQLCOM_ALTER_PROCEDURE, 0))
++	  goto error;
++
++        if (end_active_trans(thd)) 
++          goto error;
++	memcpy(&lex->sp_chistics, &chistics, sizeof(lex->sp_chistics));
++        if ((sp->m_type == TYPE_ENUM_FUNCTION) &&
++            !trust_function_creators &&  mysql_bin_log.is_open() &&
++            !sp->m_chistics->detistic &&
++            (chistics.daccess == SP_CONTAINS_SQL ||
++             chistics.daccess == SP_MODIFIES_SQL_DATA))
++        {
++          my_message(ER_BINLOG_UNSAFE_ROUTINE,
++		     ER(ER_BINLOG_UNSAFE_ROUTINE), MYF(0));
++          result= SP_INTERNAL_ERROR;
++        }
++        else
++        {
++          /*
++            Note that if you implement the capability of ALTER FUNCTION to
++            alter the body of the function, this command should be made to
++            follow the restrictions that log-bin-trust-function-creators=0
++            already puts on CREATE FUNCTION.
++          */
++          if (lex->sql_command == SQLCOM_ALTER_PROCEDURE)
++            /* Conditionally writes to binlog */
++            result= sp_update_procedure(thd, lex->spname, &lex->sp_chistics);
++          else
++            /* Conditionally writes to binlog */
++            result= sp_update_function(thd, lex->spname, &lex->sp_chistics);
++        }
++      }
++      switch (result)
++      {
++      case SP_OK:
++	send_ok(thd);
++	break;
++      case SP_KEY_NOT_FOUND:
++	my_error(ER_SP_DOES_NOT_EXIST, MYF(0),
++                 SP_COM_STRING(lex), lex->spname->m_qname.str);
++	goto error;
++      default:
++	my_error(ER_SP_CANT_ALTER, MYF(0),
++                 SP_COM_STRING(lex), lex->spname->m_qname.str);
++	goto error;
++      }
++      break;
++    }
++  case SQLCOM_DROP_PROCEDURE:
++  case SQLCOM_DROP_FUNCTION:
++    {
++      int result;
++      int type= (lex->sql_command == SQLCOM_DROP_PROCEDURE ?
++                 TYPE_ENUM_PROCEDURE : TYPE_ENUM_FUNCTION);
++
++      result= sp_routine_exists_in_table(thd, type, lex->spname);
++      mysql_reset_errors(thd, 0);
++      if (result == SP_OK)
++      {
++        char *db= lex->spname->m_db.str;
++	char *name= lex->spname->m_name.str;
++
++	if (check_routine_access(thd, ALTER_PROC_ACL, db, name,
++                                 lex->sql_command == SQLCOM_DROP_PROCEDURE, 0))
++          goto error;
++
++        if (end_active_trans(thd)) 
++          goto error;
++#ifndef NO_EMBEDDED_ACCESS_CHECKS
++	if (sp_automatic_privileges && !opt_noacl &&
++	    sp_revoke_privileges(thd, db, name, 
++                                 lex->sql_command == SQLCOM_DROP_PROCEDURE))
++	{
++	  push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, 
++		       ER_PROC_AUTO_REVOKE_FAIL,
++		       ER(ER_PROC_AUTO_REVOKE_FAIL));
++	}
++#endif
++	if (lex->sql_command == SQLCOM_DROP_PROCEDURE)
++          /* Conditionally writes to binlog */
++	  result= sp_drop_procedure(thd, lex->spname); /* Conditionally writes to binlog */
++	else
++          /* Conditionally writes to binlog */
++	  result= sp_drop_function(thd, lex->spname); /* Conditionally writes to binlog */
++      }
++      else
++      {
++#ifdef HAVE_DLOPEN
++	if (lex->sql_command == SQLCOM_DROP_FUNCTION)
++	{
++          udf_func *udf = find_udf(lex->spname->m_name.str,
++                                   lex->spname->m_name.length);
++          if (udf)
++          {
++	    if (check_access(thd, DELETE_ACL, "mysql", 0, 1, 0, 0))
++	      goto error;
++
++	    /* Does NOT write to binlog */
++	    if (!(res = mysql_drop_function(thd, &lex->spname->m_name)))
++	    {
++	      send_ok(thd);
++	      break;
++	    }
++	  }
++	}
++#endif
++	if (lex->spname->m_db.str)
++	  result= SP_KEY_NOT_FOUND;
++	else
++	{
++	  my_message(ER_NO_DB_ERROR, ER(ER_NO_DB_ERROR), MYF(0));
++	  goto error;
++	}
++      }
++      res= result;
++      switch (result)
++      {
++      case SP_OK:
++	send_ok(thd);
++	break;
++      case SP_KEY_NOT_FOUND:
++	if (lex->drop_if_exists)
++	{
++	  push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
++			      ER_SP_DOES_NOT_EXIST, ER(ER_SP_DOES_NOT_EXIST),
++			      SP_COM_STRING(lex), lex->spname->m_name.str);
++	  res= FALSE;
++	  send_ok(thd);
++	  break;
++	}
++	my_error(ER_SP_DOES_NOT_EXIST, MYF(0),
++                 SP_COM_STRING(lex), lex->spname->m_qname.str);
++	goto error;
++      default:
++	my_error(ER_SP_DROP_FAILED, MYF(0),
++                 SP_COM_STRING(lex), lex->spname->m_qname.str);
++	goto error;
++      }
++      break;
++    }
++  case SQLCOM_SHOW_CREATE_PROC:
++    {
++      if (lex->spname->m_name.length > NAME_LEN)
++      {
++	my_error(ER_TOO_LONG_IDENT, MYF(0), lex->spname->m_name.str);
++	goto error;
++      }
++      if (sp_show_create_procedure(thd, lex->spname) != SP_OK)
++      {			/* We don't distinguish between errors for now */
++	my_error(ER_SP_DOES_NOT_EXIST, MYF(0),
++                 SP_COM_STRING(lex), lex->spname->m_name.str);
++	goto error;
++      }
++      break;
++    }
++  case SQLCOM_SHOW_CREATE_FUNC:
++    {
++      if (lex->spname->m_name.length > NAME_LEN)
++      {
++	my_error(ER_TOO_LONG_IDENT, MYF(0), lex->spname->m_name.str);
++	goto error;
++      }
++      if (sp_show_create_function(thd, lex->spname) != SP_OK)
++      {			/* We don't distinguish between errors for now */
++	my_error(ER_SP_DOES_NOT_EXIST, MYF(0),
++                 SP_COM_STRING(lex), lex->spname->m_name.str);
++	goto error;
++      }
++      break;
++    }
++  case SQLCOM_SHOW_STATUS_PROC:
++    {
++      res= sp_show_status_procedure(thd, (lex->wild ?
++					  lex->wild->ptr() : NullS));
++      break;
++    }
++  case SQLCOM_SHOW_STATUS_FUNC:
++    {
++      res= sp_show_status_function(thd, (lex->wild ? 
++					 lex->wild->ptr() : NullS));
++      break;
++    }
++#ifndef DBUG_OFF
++  case SQLCOM_SHOW_PROC_CODE:
++  case SQLCOM_SHOW_FUNC_CODE:
++    {
++      sp_head *sp;
++
++      if (lex->spname->m_name.length > NAME_LEN)
++      {
++	my_error(ER_TOO_LONG_IDENT, MYF(0), lex->spname->m_name.str);
++	goto error;
++      }
++      if (lex->sql_command == SQLCOM_SHOW_PROC_CODE)
++        sp= sp_find_routine(thd, TYPE_ENUM_PROCEDURE, lex->spname,
++                            &thd->sp_proc_cache, FALSE);
++      else
++        sp= sp_find_routine(thd, TYPE_ENUM_FUNCTION, lex->spname,
++                            &thd->sp_func_cache, FALSE);
++      if (!sp || sp->show_routine_code(thd))
++      {
++        /* We don't distinguish between errors for now */
++        my_error(ER_SP_DOES_NOT_EXIST, MYF(0),
++                 SP_COM_STRING(lex), lex->spname->m_name.str);
++        goto error;
++      }
++      break;
++    }
++#endif // ifndef DBUG_OFF
++  case SQLCOM_CREATE_VIEW:
++    {
++      if (end_active_trans(thd))
++        goto error;
++
++      res= mysql_create_view(thd, first_table, thd->lex->create_view_mode);
++      break;
++    }
++  case SQLCOM_DROP_VIEW:
++    {
++      if (check_table_access(thd, DROP_ACL, all_tables, 0) ||
++          end_active_trans(thd))
++        goto error;
++      /* Conditionally writes to binlog. */
++      res= mysql_drop_view(thd, first_table, thd->lex->drop_mode);
++      break;
++    }
++  case SQLCOM_CREATE_TRIGGER:
++  {
++    if (end_active_trans(thd))
++      goto error;
++
++    /* Conditionally writes to binlog. */
++    res= mysql_create_or_drop_trigger(thd, all_tables, 1);
++
++    /* We don't care about trigger body after this point */
++    delete lex->sphead;
++    lex->sphead= 0;
++    break;
++  }
++  case SQLCOM_DROP_TRIGGER:
++  {
++    if (end_active_trans(thd))
++      goto error;
++
++    /* Conditionally writes to binlog. */
++    res= mysql_create_or_drop_trigger(thd, all_tables, 0);
++    break;
++  }
++  case SQLCOM_XA_START:
++    if (thd->transaction.xid_state.xa_state == XA_IDLE &&
++        thd->lex->xa_opt == XA_RESUME)
++    {
++      if (! thd->transaction.xid_state.xid.eq(thd->lex->xid))
++      {
++        my_error(ER_XAER_NOTA, MYF(0));
++        break;
++      }
++      thd->transaction.xid_state.xa_state=XA_ACTIVE;
++      send_ok(thd);
++      break;
++    }
++    if (thd->lex->xa_opt != XA_NONE)
++    { // JOIN is not supported yet. TODO
++      my_error(ER_XAER_INVAL, MYF(0));
++      break;
++    }
++    if (thd->transaction.xid_state.xa_state != XA_NOTR)
++    {
++      my_error(ER_XAER_RMFAIL, MYF(0),
++               xa_state_names[thd->transaction.xid_state.xa_state]);
++      break;
++    }
++    if (thd->active_transaction() || thd->locked_tables)
++    {
++      my_error(ER_XAER_OUTSIDE, MYF(0));
++      break;
++    }
++    if (xid_cache_search(thd->lex->xid))
++    {
++      my_error(ER_XAER_DUPID, MYF(0));
++      break;
++    }
++    DBUG_ASSERT(thd->transaction.xid_state.xid.is_null());
++    thd->transaction.xid_state.xa_state=XA_ACTIVE;
++    thd->transaction.xid_state.xid.set(thd->lex->xid);
++    xid_cache_insert(&thd->transaction.xid_state);
++    thd->options= ((thd->options & (ulong) ~(OPTION_STATUS_NO_TRANS_UPDATE)) |
++                   OPTION_BEGIN);
++    thd->server_status|= SERVER_STATUS_IN_TRANS;
++    send_ok(thd);
++    break;
++  case SQLCOM_XA_END:
++    /* fake it */
++    if (thd->lex->xa_opt != XA_NONE)
++    { // SUSPEND and FOR MIGRATE are not supported yet. TODO
++      my_error(ER_XAER_INVAL, MYF(0));
++      break;
++    }
++    if (thd->transaction.xid_state.xa_state != XA_ACTIVE)
++    {
++      my_error(ER_XAER_RMFAIL, MYF(0),
++               xa_state_names[thd->transaction.xid_state.xa_state]);
++      break;
++    }
++    if (!thd->transaction.xid_state.xid.eq(thd->lex->xid))
++    {
++      my_error(ER_XAER_NOTA, MYF(0));
++      break;
++    }
++    thd->transaction.xid_state.xa_state=XA_IDLE;
++    send_ok(thd);
++    break;
++  case SQLCOM_XA_PREPARE:
++    if (thd->transaction.xid_state.xa_state != XA_IDLE)
++    {
++      my_error(ER_XAER_RMFAIL, MYF(0),
++               xa_state_names[thd->transaction.xid_state.xa_state]);
++      break;
++    }
++    if (!thd->transaction.xid_state.xid.eq(thd->lex->xid))
++    {
++      my_error(ER_XAER_NOTA, MYF(0));
++      break;
++    }
++    if (ha_prepare(thd))
++    {
++      my_error(ER_XA_RBROLLBACK, MYF(0));
++      xid_cache_delete(&thd->transaction.xid_state);
++      thd->transaction.xid_state.xa_state=XA_NOTR;
++      break;
++    }
++    thd->transaction.xid_state.xa_state=XA_PREPARED;
++    send_ok(thd);
++    break;
++  case SQLCOM_XA_COMMIT:
++    if (!thd->transaction.xid_state.xid.eq(thd->lex->xid))
++    {
++      XID_STATE *xs=xid_cache_search(thd->lex->xid);
++      if (!xs || xs->in_thd)
++        my_error(ER_XAER_NOTA, MYF(0));
++      else
++      {
++        ha_commit_or_rollback_by_xid(thd->lex->xid, 1);
++        xid_cache_delete(xs);
++        send_ok(thd);
++      }
++      break;
++    }
++    if (thd->transaction.xid_state.xa_state == XA_IDLE &&
++        thd->lex->xa_opt == XA_ONE_PHASE)
++    {
++      int r;
++      if ((r= ha_commit(thd)))
++        my_error(r == 1 ? ER_XA_RBROLLBACK : ER_XAER_RMERR, MYF(0));
++      else
++        send_ok(thd);
++    }
++    else if (thd->transaction.xid_state.xa_state == XA_PREPARED &&
++             thd->lex->xa_opt == XA_NONE)
++    {
++      if (wait_if_global_read_lock(thd, 0, 0))
++      {
++        ha_rollback(thd);
++        my_error(ER_XAER_RMERR, MYF(0));
++      }
++      else
++      {
++        if (ha_commit_one_phase(thd, 1))
++          my_error(ER_XAER_RMERR, MYF(0));
++        else
++          send_ok(thd);
++        start_waiting_global_read_lock(thd);
++      }
++    }
++    else
++    {
++      my_error(ER_XAER_RMFAIL, MYF(0),
++               xa_state_names[thd->transaction.xid_state.xa_state]);
++      break;
++    }
++    thd->options&= ~(ulong) (OPTION_BEGIN | OPTION_STATUS_NO_TRANS_UPDATE);
++    thd->server_status&= ~SERVER_STATUS_IN_TRANS;
++    xid_cache_delete(&thd->transaction.xid_state);
++    thd->transaction.xid_state.xa_state=XA_NOTR;
++    break;
++  case SQLCOM_XA_ROLLBACK:
++    if (!thd->transaction.xid_state.xid.eq(thd->lex->xid))
++    {
++      XID_STATE *xs=xid_cache_search(thd->lex->xid);
++      if (!xs || xs->in_thd)
++        my_error(ER_XAER_NOTA, MYF(0));
++      else
++      {
++        ha_commit_or_rollback_by_xid(thd->lex->xid, 0);
++        xid_cache_delete(xs);
++        send_ok(thd);
++      }
++      break;
++    }
++    if (thd->transaction.xid_state.xa_state != XA_IDLE &&
++        thd->transaction.xid_state.xa_state != XA_PREPARED)
++    {
++      my_error(ER_XAER_RMFAIL, MYF(0),
++               xa_state_names[thd->transaction.xid_state.xa_state]);
++      break;
++    }
++    if (ha_rollback(thd))
++      my_error(ER_XAER_RMERR, MYF(0));
++    else
++      send_ok(thd);
++    thd->options&= ~(ulong) (OPTION_BEGIN | OPTION_STATUS_NO_TRANS_UPDATE);
++    thd->server_status&= ~SERVER_STATUS_IN_TRANS;
++    xid_cache_delete(&thd->transaction.xid_state);
++    thd->transaction.xid_state.xa_state=XA_NOTR;
++    break;
++  case SQLCOM_XA_RECOVER:
++    res= mysql_xa_recover(thd);
++    break;
++  default:
++#ifndef EMBEDDED_LIBRARY
++    DBUG_ASSERT(0);                             /* Impossible */
++#endif
++    send_ok(thd);
++    break;
++  }
++  thd->proc_info="query end";
++  /* Two binlog-related cleanups: */
++
++  /*
++    Reset system variables temporarily modified by SET ONE SHOT.
++
++    Exception: If this is a SET, do nothing. This is to allow
++    mysqlbinlog to print many SET commands (in this case we want the
++    charset temp setting to live until the real query). This is also
++    needed so that SET CHARACTER_SET_CLIENT... does not cancel itself
++    immediately.
++  */
++  if (thd->one_shot_set && lex->sql_command != SQLCOM_SET_OPTION)
++    reset_one_shot_variables(thd);
++
++  /*
++    The return value for ROW_COUNT() is "implementation dependent" if the
++    statement is not DELETE, INSERT or UPDATE, but -1 is what JDBC and ODBC
++    wants.
++
++    We do not change the value for a CALL or EXECUTE statement, so the value
++    generated by the last called (or executed) statement is preserved.
++   */
++  if (lex->sql_command != SQLCOM_CALL && lex->sql_command != SQLCOM_EXECUTE &&
++      uc_update_queries[lex->sql_command]<2)
++    thd->row_count_func= -1;
++
++  goto end;
++
++error:
++  res= TRUE;
++
++end:
++  if (need_start_waiting)
++  {
++    /*
++      Release the protection against the global read lock and wake
++      everyone, who might want to set a global read lock.
++    */
++    start_waiting_global_read_lock(thd);
++  }
++  DBUG_RETURN(res || thd->net.report_error);
++}
++
++
++/*
++  Check grants for commands which work only with one table.
++
++  SYNOPSIS
++    check_single_table_access()
++    thd			Thread handler
++    privilege		requested privilege
++    all_tables		global table list of query
++
++  RETURN
++    0 - OK
++    1 - access denied, error is sent to client
++*/
++
++bool check_single_table_access(THD *thd, ulong privilege, 
++                               TABLE_LIST *all_tables)
++{
++  Security_context * backup_ctx= thd->security_ctx;
++
++  /* we need to switch to the saved context (if any) */
++  if (all_tables->security_ctx)
++    thd->security_ctx= all_tables->security_ctx;
++
++  const char *db_name;
++  if ((all_tables->view || all_tables->field_translation) &&
++      !all_tables->schema_table)
++    db_name= all_tables->view_db.str;
++  else
++    db_name= all_tables->db;
++
++  if (check_access(thd, privilege, db_name,
++		   &all_tables->grant.privilege, 0, 0,
++                   test(all_tables->schema_table)))
++    goto deny;
++
++  /* Show only 1 table for check_grant */
++  if (grant_option && check_grant(thd, privilege, all_tables, 0, 1, 0))
++    goto deny;
++
++  thd->security_ctx= backup_ctx;
++  return 0;
++
++deny:
++  thd->security_ctx= backup_ctx;
++  return 1;
++}
++
++/*
++  Check grants for commands which work only with one table and all other
++  tables belonging to subselects or implicitly opened tables.
++
++  SYNOPSIS
++    check_one_table_access()
++    thd			Thread handler
++    privilege		requested privilege
++    all_tables		global table list of query
++
++  RETURN
++    0 - OK
++    1 - access denied, error is sent to client
++*/
++
++bool check_one_table_access(THD *thd, ulong privilege, TABLE_LIST *all_tables)
++{
++  if (check_single_table_access (thd,privilege,all_tables))
++    return 1;
++
++  /* Check rights on tables of subselects and implictly opened tables */
++  TABLE_LIST *subselects_tables, *view= all_tables->view ? all_tables : 0;
++  if ((subselects_tables= all_tables->next_global))
++  {
++    /*
++      Access rights asked for the first table of a view should be the same
++      as for the view
++    */
++    if (view && subselects_tables->belong_to_view == view)
++    {
++      if (check_single_table_access (thd, privilege, subselects_tables))
++        return 1;
++      subselects_tables= subselects_tables->next_global;
++    }
++    if (subselects_tables &&
++        (check_table_access(thd, SELECT_ACL, subselects_tables, 0)))
++      return 1;
++  }
++  return 0;
++}
++
++
++/****************************************************************************
++  Get the user (global) and database privileges for all used tables
++
++  NOTES
++    The idea of EXTRA_ACL is that one will be granted access to the table if
++    one has the asked privilege on any column combination of the table; For
++    example to be able to check a table one needs to have SELECT privilege on
++    any column of the table.
++
++  RETURN
++    0  ok
++    1  If we can't get the privileges and we don't use table/column grants.
++
++    save_priv	In this we store global and db level grants for the table
++		Note that we don't store db level grants if the global grants
++                is enough to satisfy the request and the global grants contains
++                a SELECT grant.
++****************************************************************************/
++
++bool
++check_access(THD *thd, ulong want_access, const char *db, ulong *save_priv,
++	     bool dont_check_global_grants, bool no_errors, bool schema_db)
++{
++  Security_context *sctx= thd->security_ctx;
++#ifndef NO_EMBEDDED_ACCESS_CHECKS
++  ulong db_access;
++  bool  db_is_pattern= test(want_access & GRANT_ACL);
++#endif
++  ulong dummy;
++  DBUG_ENTER("check_access");
++  DBUG_PRINT("enter",("db: %s  want_access: %lu  master_access: %lu",
++                      db ? db : "", want_access, sctx->master_access));
++  if (save_priv)
++    *save_priv=0;
++  else
++    save_priv= &dummy;
++
++  if ((!db || !db[0]) && !thd->db && !dont_check_global_grants)
++  {
++    DBUG_PRINT("error",("No database"));
++    if (!no_errors)
++      my_message(ER_NO_DB_ERROR, ER(ER_NO_DB_ERROR),
++                 MYF(0));                       /* purecov: tested */
++    DBUG_RETURN(TRUE);				/* purecov: tested */
++  }
++
++  if (schema_db)
++  {
++    if (want_access & ~(SELECT_ACL | EXTRA_ACL))
++    {
++      if (!no_errors)
++      {
++        const char *db_name= db ? db : thd->db;
++        my_error(ER_DBACCESS_DENIED_ERROR, MYF(0),
++                 sctx->priv_user, sctx->priv_host, db_name);
++      }
++      DBUG_RETURN(TRUE);
++    }
++    else
++    {
++      *save_priv= SELECT_ACL;
++      DBUG_RETURN(FALSE);
++    }
++  }
++
++#ifdef NO_EMBEDDED_ACCESS_CHECKS
++  DBUG_RETURN(0);
++#else
++  if ((sctx->master_access & want_access) == want_access)
++  {
++    /*
++      If we don't have a global SELECT privilege, we have to get the database
++      specific access rights to be able to handle queries of type
++      UPDATE t1 SET a=1 WHERE b > 0
++    */
++    db_access= sctx->db_access;
++    if (!(sctx->master_access & SELECT_ACL) &&
++	(db && (!thd->db || db_is_pattern || strcmp(db,thd->db))))
++      db_access=acl_get(sctx->host, sctx->ip, sctx->priv_user, db,
++                        db_is_pattern);
++    *save_priv=sctx->master_access | db_access;
++    DBUG_RETURN(FALSE);
++  }
++  if (((want_access & ~sctx->master_access) & ~(DB_ACLS | EXTRA_ACL)) ||
++      ! db && dont_check_global_grants)
++  {						// We can never grant this
++    DBUG_PRINT("error",("No possible access"));
++    if (!no_errors)
++      my_error(ER_ACCESS_DENIED_ERROR, MYF(0),
++               sctx->priv_user,
++               sctx->priv_host,
++               (thd->password ?
++                ER(ER_YES) :
++                ER(ER_NO)));                    /* purecov: tested */
++    DBUG_RETURN(TRUE);				/* purecov: tested */
++  }
++
++  if (db == any_db)
++    DBUG_RETURN(FALSE);				// Allow select on anything
++
++  if (db && (!thd->db || db_is_pattern || strcmp(db,thd->db)))
++    db_access= acl_get(sctx->host, sctx->ip, sctx->priv_user, db,
++                       db_is_pattern);
++  else
++    db_access= sctx->db_access;
++  DBUG_PRINT("info",("db_access: %lu", db_access));
++  /* Remove SHOW attribute and access rights we already have */
++  want_access &= ~(sctx->master_access | EXTRA_ACL);
++  DBUG_PRINT("info",("db_access: %lu  want_access: %lu",
++                     db_access, want_access));
++  db_access= ((*save_priv=(db_access | sctx->master_access)) & want_access);
++
++  /* grant_option is set if there exists a single table or column grant */
++  if (db_access == want_access ||
++      (grant_option && !dont_check_global_grants &&
++       !(want_access & ~(db_access | TABLE_ACLS | PROC_ACLS))))
++    DBUG_RETURN(FALSE);				/* Ok */
++
++  DBUG_PRINT("error",("Access denied"));
++  if (!no_errors)
++    my_error(ER_DBACCESS_DENIED_ERROR, MYF(0),
++             sctx->priv_user, sctx->priv_host,
++             (db ? db : (thd->db ?
++                         thd->db :
++                         "unknown")));          /* purecov: tested */
++  DBUG_RETURN(TRUE);				/* purecov: tested */
++#endif /* NO_EMBEDDED_ACCESS_CHECKS */
++}
++
++
++/*
++  check for global access and give descriptive error message if it fails
++
++  SYNOPSIS
++    check_global_access()
++    thd			Thread handler
++    want_access		Use should have any of these global rights
++
++  WARNING
++    One gets access right if one has ANY of the rights in want_access
++    This is useful as one in most cases only need one global right,
++    but in some case we want to check if the user has SUPER or
++    REPL_CLIENT_ACL rights.
++
++  RETURN
++    0	ok
++    1	Access denied.  In this case an error is sent to the client
++*/
++
++bool check_global_access(THD *thd, ulong want_access)
++{
++#ifdef NO_EMBEDDED_ACCESS_CHECKS
++  return 0;
++#else
++  char command[128];
++  if ((thd->security_ctx->master_access & want_access))
++    return 0;
++  get_privilege_desc(command, sizeof(command), want_access);
++  my_error(ER_SPECIFIC_ACCESS_DENIED_ERROR, MYF(0), command);
++  return 1;
++#endif /* NO_EMBEDDED_ACCESS_CHECKS */
++}
++
++
++/*
++  Check the privilege for all used tables.
++
++  SYNOPSYS
++    check_table_access()
++      thd          Thread context
++      want_access  Privileges requested
++      tables       List of tables to be checked
++      no_errors    FALSE/TRUE - report/don't report error to
++                   the client (using my_error() call).
++
++  NOTES
++    Table privileges are cached in the table list for GRANT checking.
++    This functions assumes that table list used and
++    thd->lex->query_tables_own_last value correspond to each other
++    (the latter should be either 0 or point to next_global member
++    of one of elements of this table list).
++
++  RETURN VALUE
++    FALSE - OK
++    TRUE  - Access denied
++*/
++
++bool
++check_table_access(THD *thd, ulong want_access,TABLE_LIST *tables,
++		   bool no_errors)
++{
++  uint found=0;
++  ulong found_access=0;
++  TABLE_LIST *org_tables= tables;
++  TABLE_LIST *first_not_own_table= thd->lex->first_not_own_table();
++  Security_context *sctx= thd->security_ctx, *backup_ctx= thd->security_ctx;
++  /*
++    The check that first_not_own_table is not reached is for the case when
++    the given table list refers to the list for prelocking (contains tables
++    of other queries). For simple queries first_not_own_table is 0.
++  */
++  for (; tables != first_not_own_table; tables= tables->next_global)
++  {
++    if (tables->security_ctx)
++      sctx= tables->security_ctx;
++    else
++      sctx= backup_ctx;
++
++    if (tables->schema_table && 
++        (want_access & ~(SELECT_ACL | EXTRA_ACL | FILE_ACL)))
++    {
++      if (!no_errors)
++        my_error(ER_DBACCESS_DENIED_ERROR, MYF(0),
++                 sctx->priv_user, sctx->priv_host,
++                 information_schema_name.str);
++      return TRUE;
++    }
++    /*
++       Register access for view underlying table.
++       Remove SHOW_VIEW_ACL, because it will be checked during making view
++     */
++    tables->grant.orig_want_privilege= (want_access & ~SHOW_VIEW_ACL);
++    if (tables->derived || tables->schema_table ||
++        (tables->table && (int)tables->table->s->tmp_table) ||
++        my_tz_check_n_skip_implicit_tables(&tables,
++                                           thd->lex->time_zone_tables_used))
++      continue;
++    thd->security_ctx= sctx;
++    if ((sctx->master_access & want_access) ==
++        (want_access & ~EXTRA_ACL) &&
++	thd->db)
++      tables->grant.privilege= want_access;
++    else if (tables->db && thd->db && strcmp(tables->db, thd->db) == 0)
++    {
++      if (found && !grant_option)		// db already checked
++	tables->grant.privilege=found_access;
++      else
++      {
++	if (check_access(thd,want_access,tables->db,&tables->grant.privilege,
++			 0, no_errors, test(tables->schema_table)))
++	  goto deny;                            // Access denied
++	found_access=tables->grant.privilege;
++	found=1;
++      }
++    }
++    else if (check_access(thd,want_access,tables->db,&tables->grant.privilege,
++			  0, no_errors, test(tables->schema_table)))
++      goto deny;
++  }
++  thd->security_ctx= backup_ctx;
++  if (grant_option)
++    return check_grant(thd,want_access & ~EXTRA_ACL,org_tables,
++		       test(want_access & EXTRA_ACL), UINT_MAX, no_errors);
++  return FALSE;
++deny:
++  thd->security_ctx= backup_ctx;
++  return TRUE;
++}
++
++
++bool
++check_routine_access(THD *thd, ulong want_access,char *db, char *name,
++		     bool is_proc, bool no_errors)
++{
++  TABLE_LIST tables[1];
++  
++  bzero((char *)tables, sizeof(TABLE_LIST));
++  tables->db= db;
++  tables->table_name= tables->alias= name;
++  
++  /*
++    The following test is just a shortcut for check_access() (to avoid
++    calculating db_access) under the assumption that it's common to
++    give persons global right to execute all stored SP (but not
++    necessary to create them).
++  */
++  if ((thd->security_ctx->master_access & want_access) == want_access)
++    tables->grant.privilege= want_access;
++  else if (check_access(thd,want_access,db,&tables->grant.privilege,
++			0, no_errors, 0))
++    return TRUE;
++  
++#ifndef NO_EMBEDDED_ACCESS_CHECKS
++  if (grant_option)
++    return check_grant_routine(thd, want_access, tables, is_proc, no_errors);
++#endif
++
++  return FALSE;
++}
++
++
++/*
++  Check if the routine has any of the routine privileges
++
++  SYNOPSIS
++    check_some_routine_access()
++    thd		 Thread handler
++    db           Database name
++    name         Routine name
++
++  RETURN
++    0            ok
++    1            error
++*/
++
++bool check_some_routine_access(THD *thd, const char *db, const char *name,
++                               bool is_proc)
++{
++  ulong save_priv;
++  if (thd->security_ctx->master_access & SHOW_PROC_ACLS)
++    return FALSE;
++  /*
++    There are no routines in information_schema db. So we can safely
++    pass zero to last paramter of check_access function
++  */
++  if (!check_access(thd, SHOW_PROC_ACLS, db, &save_priv, 0, 1, 0) ||
++      (save_priv & SHOW_PROC_ACLS))
++    return FALSE;
++  return check_routine_level_acl(thd, db, name, is_proc);
++}
++
++
++/*
++  Check if the given table has any of the asked privileges
++
++  SYNOPSIS
++    check_some_access()
++    thd		 Thread handler
++    want_access	 Bitmap of possible privileges to check for
++
++  RETURN
++    0  ok
++    1  error
++*/
++
++
++bool check_some_access(THD *thd, ulong want_access, TABLE_LIST *table)
++{
++  ulong access;
++  DBUG_ENTER("check_some_access");
++
++  /* This loop will work as long as we have less than 32 privileges */
++  for (access= 1; access < want_access ; access<<= 1)
++  {
++    if (access & want_access)
++    {
++      if (!check_access(thd, access, table->db,
++                        &table->grant.privilege, 0, 1,
++                        test(table->schema_table)) &&
++          !grant_option || !check_grant(thd, access, table, 0, 1, 1))
++        DBUG_RETURN(0);
++    }
++  }
++  DBUG_PRINT("exit",("no matching access rights"));
++  DBUG_RETURN(1);
++}
++
++
++bool check_merge_table_access(THD *thd, char *db,
++			      TABLE_LIST *table_list)
++{
++  int error=0;
++  if (table_list)
++  {
++    /* Check that all tables use the current database */
++    TABLE_LIST *tmp;
++    for (tmp= table_list; tmp; tmp= tmp->next_local)
++    {
++      if (!tmp->db || !tmp->db[0])
++	tmp->db=db;
++    }
++    error=check_table_access(thd, SELECT_ACL | UPDATE_ACL | DELETE_ACL,
++			     table_list,0);
++  }
++  return error;
++}
++
++
++static bool check_db_used(THD *thd,TABLE_LIST *tables)
++{
++  char *current_db= NULL;
++  for (; tables; tables= tables->next_global)
++  {
++    if (tables->db == NULL)
++    {
++      /*
++        This code never works and should be removed in 5.1.  All tables
++        that are added to the list of tables should already have its
++        database field initialized properly (see st_lex::add_table_to_list).
++      */
++      DBUG_ASSERT(0);
++      if (thd->copy_db_to(&current_db, 0))
++        return TRUE;
++      tables->db= current_db;
++    }
++  }
++  return FALSE;
++}
++
++/****************************************************************************
++	Check stack size; Send error if there isn't enough stack to continue
++****************************************************************************/
++
++#if STACK_DIRECTION < 0
++#define used_stack(A,B) (long) (A - B)
++#else
++#define used_stack(A,B) (long) (B - A)
++#endif
++
++#ifndef DBUG_OFF
++long max_stack_used;
++#endif
++
++#ifndef EMBEDDED_LIBRARY
++/*
++  Note: The 'buf' parameter is necessary, even if it is unused here.
++  - fix_fields functions has a "dummy" buffer large enough for the
++    corresponding exec. (Thus we only have to check in fix_fields.)
++  - Passing to check_stack_overrun() prevents the compiler from removing it.
++ */
++bool check_stack_overrun(THD *thd, long margin,
++			 char *buf __attribute__((unused)))
++{
++  long stack_used;
++  DBUG_ASSERT(thd == current_thd);
++  if ((stack_used=used_stack(thd->thread_stack,(char*) &stack_used)) >=
++      (long) (thread_stack - margin))
++  {
++    sprintf(errbuff[0],ER(ER_STACK_OVERRUN_NEED_MORE),
++            stack_used,thread_stack,margin);
++    my_message(ER_STACK_OVERRUN_NEED_MORE,errbuff[0],MYF(0));
++    thd->fatal_error();
++    return 1;
++  }
++#ifndef DBUG_OFF
++  max_stack_used= max(max_stack_used, stack_used);
++#endif
++  return 0;
++}
++#endif /* EMBEDDED_LIBRARY */
++
++#define MY_YACC_INIT 1000			// Start with big alloc
++#define MY_YACC_MAX  32000			// Because of 'short'
++
++bool my_yyoverflow(short **yyss, YYSTYPE **yyvs, ulong *yystacksize)
++{
++  LEX	*lex= current_thd->lex;
++  ulong old_info=0;
++  if ((uint) *yystacksize >= MY_YACC_MAX)
++    return 1;
++  if (!lex->yacc_yyvs)
++    old_info= *yystacksize;
++  *yystacksize= set_zone((*yystacksize)*2,MY_YACC_INIT,MY_YACC_MAX);
++  if (!(lex->yacc_yyvs= (char*)
++	my_realloc((gptr) lex->yacc_yyvs,
++		   *yystacksize*sizeof(**yyvs),
++		   MYF(MY_ALLOW_ZERO_PTR | MY_FREE_ON_ERROR))) ||
++      !(lex->yacc_yyss= (char*)
++	my_realloc((gptr) lex->yacc_yyss,
++		   *yystacksize*sizeof(**yyss),
++		   MYF(MY_ALLOW_ZERO_PTR | MY_FREE_ON_ERROR))))
++    return 1;
++  if (old_info)
++  {						// Copy old info from stack
++    memcpy(lex->yacc_yyss, (gptr) *yyss, old_info*sizeof(**yyss));
++    memcpy(lex->yacc_yyvs, (gptr) *yyvs, old_info*sizeof(**yyvs));
++  }
++  *yyss=(short*) lex->yacc_yyss;
++  *yyvs=(YYSTYPE*) lex->yacc_yyvs;
++  return 0;
++}
++
++
++/****************************************************************************
++  Initialize global thd variables needed for query
++****************************************************************************/
++
++void
++mysql_init_query(THD *thd, uchar *buf, uint length)
++{
++  DBUG_ENTER("mysql_init_query");
++  lex_start(thd, buf, length);
++  mysql_reset_thd_for_next_command(thd);
++  DBUG_VOID_RETURN;
++}
++
++
++/*
++ Reset THD part responsible for command processing state.
++
++ DESCRIPTION
++   This needs to be called before execution of every statement
++   (prepared or conventional).
++
++ TODO
++   Make it a method of THD and align its name with the rest of
++   reset/end/start/init methods.
++   Call it after we use THD for queries, not before.
++*/
++
++void mysql_reset_thd_for_next_command(THD *thd)
++{
++  DBUG_ENTER("mysql_reset_thd_for_next_command");
++  thd->free_list= 0;
++  thd->select_number= 1;
++  thd->query_start_used= thd->insert_id_used=0;
++  thd->last_insert_id_used_bin_log= FALSE;
++  thd->is_fatal_error= thd->time_zone_used= 0;
++  thd->server_status&= ~ (SERVER_MORE_RESULTS_EXISTS | 
++                          SERVER_QUERY_NO_INDEX_USED |
++                          SERVER_QUERY_NO_GOOD_INDEX_USED);
++  DBUG_ASSERT(thd->security_ctx== &thd->main_security_ctx);
++  thd->tmp_table_used= 0;
++  if (!thd->in_sub_stmt)
++  {
++    if (opt_bin_log)
++    {
++      reset_dynamic(&thd->user_var_events);
++      thd->user_var_events_alloc= thd->mem_root;
++    }
++    thd->clear_error();
++    thd->total_warn_count=0;			// Warnings for this query
++    thd->rand_used= 0;
++    thd->sent_row_count= thd->examined_row_count= 0;
++  }
++  DBUG_VOID_RETURN;
++}
++
++
++void
++mysql_init_select(LEX *lex)
++{
++  SELECT_LEX *select_lex= lex->current_select;
++  select_lex->init_select();
++  lex->wild= 0;
++  if (select_lex == &lex->select_lex)
++  {
++    DBUG_ASSERT(lex->result == 0);
++    lex->exchange= 0;
++  }
++}
++
++
++bool
++mysql_new_select(LEX *lex, bool move_down)
++{
++  SELECT_LEX *select_lex;
++  THD *thd= lex->thd;
++  DBUG_ENTER("mysql_new_select");
++
++  if (!(select_lex= new (thd->mem_root) SELECT_LEX()))
++    DBUG_RETURN(1);
++  select_lex->select_number= ++thd->select_number;
++  select_lex->parent_lex= lex; /* Used in init_query. */
++  select_lex->init_query();
++  select_lex->init_select();
++  lex->nest_level++;
++  select_lex->nest_level= lex->nest_level;
++  /*
++    Don't evaluate this subquery during statement prepare even if
++    it's a constant one. The flag is switched off in the end of
++    mysql_stmt_prepare.
++  */
++  if (thd->stmt_arena->is_stmt_prepare())
++    select_lex->uncacheable|= UNCACHEABLE_PREPARE;
++  if (move_down)
++  {
++    SELECT_LEX_UNIT *unit;
++    lex->subqueries= TRUE;
++    /* first select_lex of subselect or derived table */
++    if (!(unit= new (thd->mem_root) SELECT_LEX_UNIT()))
++      DBUG_RETURN(1);
++
++    unit->init_query();
++    unit->init_select();
++    unit->thd= thd;
++    unit->include_down(lex->current_select);
++    unit->link_next= 0;
++    unit->link_prev= 0;
++    unit->return_to= lex->current_select;
++    select_lex->include_down(unit);
++    /*
++      By default we assume that it is usual subselect and we have outer name
++      resolution context, if no we will assign it to 0 later
++    */
++    select_lex->context.outer_context= &select_lex->outer_select()->context;
++  }
++  else
++  {
++    if (lex->current_select->order_list.first && !lex->current_select->braces)
++    {
++      my_error(ER_WRONG_USAGE, MYF(0), "UNION", "ORDER BY");
++      DBUG_RETURN(1);
++    }
++    select_lex->include_neighbour(lex->current_select);
++    SELECT_LEX_UNIT *unit= select_lex->master_unit();                              
++    if (!unit->fake_select_lex && unit->add_fake_select_lex(lex->thd))
++      DBUG_RETURN(1);
++    select_lex->context.outer_context= 
++                unit->first_select()->context.outer_context;
++  }
++
++  select_lex->master_unit()->global_parameters= select_lex;
++  select_lex->include_global((st_select_lex_node**)&lex->all_selects_list);
++  lex->current_select= select_lex;
++  /*
++    in subquery is SELECT query and we allow resolution of names in SELECT
++    list
++  */
++  select_lex->context.resolve_in_select_list= TRUE;
++  DBUG_RETURN(0);
++}
++
++/*
++  Create a select to return the same output as 'SELECT @@var_name'.
++
++  SYNOPSIS
++    create_select_for_variable()
++    var_name		Variable name
++
++  DESCRIPTION
++    Used for SHOW COUNT(*) [ WARNINGS | ERROR]
++
++    This will crash with a core dump if the variable doesn't exists
++*/
++
++void create_select_for_variable(const char *var_name)
++{
++  THD *thd;
++  LEX *lex;
++  LEX_STRING tmp, null_lex_string;
++  Item *var;
++  char buff[MAX_SYS_VAR_LENGTH*2+4+8], *end;
++  DBUG_ENTER("create_select_for_variable");
++
++  thd= current_thd;
++  lex= thd->lex;
++  mysql_init_select(lex);
++  lex->sql_command= SQLCOM_SELECT;
++  tmp.str= (char*) var_name;
++  tmp.length=strlen(var_name);
++  bzero((char*) &null_lex_string.str, sizeof(null_lex_string));
++  /*
++    We set the name of Item to @@session.var_name because that then is used
++    as the column name in the output.
++  */
++  if ((var= get_system_var(thd, OPT_SESSION, tmp, null_lex_string)))
++  {
++    end= strxmov(buff, "@@session.", var_name, NullS);
++    var->set_name(buff, end-buff, system_charset_info);
++    add_item_to_list(thd, var);
++  }
++  DBUG_VOID_RETURN;
++}
++
++
++void mysql_init_multi_delete(LEX *lex)
++{
++  lex->sql_command=  SQLCOM_DELETE_MULTI;
++  mysql_init_select(lex);
++  lex->select_lex.select_limit= 0;
++  lex->unit.select_limit_cnt= HA_POS_ERROR;
++  lex->select_lex.table_list.save_and_clear(&lex->auxiliary_table_list);
++  lex->lock_option= using_update_log ? TL_READ_NO_INSERT : TL_READ;
++  lex->query_tables= 0;
++  lex->query_tables_last= &lex->query_tables;
++}
++
++/*
++  When you modify mysql_parse(), you may need to mofify
++  mysql_test_parse_for_slave() in this same file.
++*/
++
++void mysql_parse(THD *thd, char *inBuf, uint length)
++{
++  DBUG_ENTER("mysql_parse");
++
++  DBUG_EXECUTE_IF("parser_debug", turn_parser_debug_on(););
++
++  mysql_init_query(thd, (uchar*) inBuf, length);
++  if (query_cache_send_result_to_client(thd, inBuf, length) <= 0)
++  {
++    LEX *lex= thd->lex;
++    
++    sp_cache_flush_obsolete(&thd->sp_proc_cache);
++    sp_cache_flush_obsolete(&thd->sp_func_cache);
++    
++    if (!MYSQLparse((void *)thd) && ! thd->is_fatal_error)
++    {
++#ifndef NO_EMBEDDED_ACCESS_CHECKS
++      if (mqh_used && thd->user_connect &&
++	  check_mqh(thd, lex->sql_command))
++      {
++	thd->net.error = 0;
++      }
++      else
++#endif
++      {
++	if (thd->net.report_error)
++	{
++	  if (thd->lex->sphead)
++	  {
++	    delete thd->lex->sphead;
++	    thd->lex->sphead= NULL;
++	  }
++	}
++	else
++	{
++          /*
++            Binlog logs a string starting from thd->query and having length
++            thd->query_length; so we set thd->query_length correctly (to not
++            log several statements in one event, when we executed only first).
++            We set it to not see the ';' (otherwise it would get into binlog
++            and Query_log_event::print() would give ';;' output).
++            This also helps display only the current query in SHOW
++            PROCESSLIST.
++            Note that we don't need LOCK_thread_count to modify query_length.
++          */
++          if (lex->found_semicolon &&
++              (thd->query_length= (ulong)(lex->found_semicolon - thd->query)))
++            thd->query_length--;
++          /* Actually execute the query */
++	  mysql_execute_command(thd);
++	  query_cache_end_of_result(thd);
++	}
++      }
++      lex->unit.cleanup();
++    }
++    else
++    {
++      DBUG_ASSERT(thd->net.report_error);
++      DBUG_PRINT("info",("Command aborted. Fatal_error: %d",
++			 thd->is_fatal_error));
++
++      /*
++        The first thing we do after parse error is freeing sp_head to
++        ensure that we have restored original memroot.
++      */
++      if (thd->lex->sphead)
++      {
++	/* Clean up after failed stored procedure/function */
++	delete thd->lex->sphead;
++	thd->lex->sphead= NULL;
++      }
++      query_cache_abort(&thd->net);
++      lex->unit.cleanup();
++    }
++    thd->proc_info="freeing items";
++    thd->end_statement();
++    thd->cleanup_after_query();
++    DBUG_ASSERT(thd->change_list.is_empty());
++  }
++  DBUG_VOID_RETURN;
++}
++
++
++#ifdef HAVE_REPLICATION
++/*
++  Usable by the replication SQL thread only: just parse a query to know if it
++  can be ignored because of replicate-*-table rules.
++
++  RETURN VALUES
++    0	cannot be ignored
++    1	can be ignored
++*/
++
++bool mysql_test_parse_for_slave(THD *thd, char *inBuf, uint length)
++{
++  LEX *lex= thd->lex;
++  bool error= 0;
++  DBUG_ENTER("mysql_test_parse_for_slave");
++
++  mysql_init_query(thd, (uchar*) inBuf, length);
++  if (!MYSQLparse((void*) thd) && ! thd->is_fatal_error &&
++      all_tables_not_ok(thd,(TABLE_LIST*) lex->select_lex.table_list.first))
++    error= 1;                  /* Ignore question */
++  thd->end_statement();
++  thd->cleanup_after_query();
++  DBUG_RETURN(error);
++}
++#endif
++
++
++
++/*****************************************************************************
++** Store field definition for create
++** Return 0 if ok
++******************************************************************************/
++
++bool add_field_to_list(THD *thd, char *field_name, enum_field_types type,
++		       char *length, char *decimals,
++		       uint type_modifier,
++		       Item *default_value, Item *on_update_value,
++                       LEX_STRING *comment,
++		       char *change,
++                       List<String> *interval_list, CHARSET_INFO *cs,
++		       uint uint_geom_type)
++{
++  register create_field *new_field;
++  LEX  *lex= thd->lex;
++  DBUG_ENTER("add_field_to_list");
++
++  if (strlen(field_name) > NAME_LEN)
++  {
++    my_error(ER_TOO_LONG_IDENT, MYF(0), field_name); /* purecov: inspected */
++    DBUG_RETURN(1);				/* purecov: inspected */
++  }
++  if (type_modifier & PRI_KEY_FLAG)
++  {
++    lex->col_list.push_back(new key_part_spec(field_name,0));
++    lex->key_list.push_back(new Key(Key::PRIMARY, NullS, HA_KEY_ALG_UNDEF,
++				    0, lex->col_list));
++    lex->col_list.empty();
++  }
++  if (type_modifier & (UNIQUE_FLAG | UNIQUE_KEY_FLAG))
++  {
++    lex->col_list.push_back(new key_part_spec(field_name,0));
++    lex->key_list.push_back(new Key(Key::UNIQUE, NullS, HA_KEY_ALG_UNDEF, 0,
++				    lex->col_list));
++    lex->col_list.empty();
++  }
++
++  if (default_value)
++  {
++    /* 
++      Default value should be literal => basic constants =>
++      no need fix_fields()
++      
++      We allow only one function as part of default value - 
++      NOW() as default for TIMESTAMP type.
++    */
++    if (default_value->type() == Item::FUNC_ITEM && 
++        !(((Item_func*)default_value)->functype() == Item_func::NOW_FUNC &&
++         type == FIELD_TYPE_TIMESTAMP))
++    {
++      my_error(ER_INVALID_DEFAULT, MYF(0), field_name);
++      DBUG_RETURN(1);
++    }
++    else if (default_value->type() == Item::NULL_ITEM)
++    {
++      default_value= 0;
++      if ((type_modifier & (NOT_NULL_FLAG | AUTO_INCREMENT_FLAG)) ==
++	  NOT_NULL_FLAG)
++      {
++	my_error(ER_INVALID_DEFAULT, MYF(0), field_name);
++	DBUG_RETURN(1);
++      }
++    }
++    else if (type_modifier & AUTO_INCREMENT_FLAG)
++    {
++      my_error(ER_INVALID_DEFAULT, MYF(0), field_name);
++      DBUG_RETURN(1);
++    }
++  }
++
++  if (on_update_value && type != FIELD_TYPE_TIMESTAMP)
++  {
++    my_error(ER_INVALID_ON_UPDATE, MYF(0), field_name);
++    DBUG_RETURN(1);
++  }
++
++  if (type == FIELD_TYPE_TIMESTAMP && length)
++  {
++    /* Display widths are no longer supported for TIMSTAMP as of MySQL 4.1.
++       In other words, for declarations such as TIMESTAMP(2), TIMESTAMP(4),
++       and so on, the display width is ignored.
++    */
++    char buf[32];
++    my_snprintf(buf, sizeof(buf), "TIMESTAMP(%s)", length);
++    push_warning_printf(thd,MYSQL_ERROR::WARN_LEVEL_WARN,
++                        ER_WARN_DEPRECATED_SYNTAX,
++                        ER(ER_WARN_DEPRECATED_SYNTAX),
++                        buf, "TIMESTAMP");
++  }
++
++  if (!(new_field= new create_field()) ||
++      new_field->init(thd, field_name, type, length, decimals, type_modifier,
++                      default_value, on_update_value, comment, change,
++                      interval_list, cs, uint_geom_type))
++    DBUG_RETURN(1);
++
++  lex->create_list.push_back(new_field);
++  lex->last_field=new_field;
++  DBUG_RETURN(0);
++}
++
++
++/* Store position for column in ALTER TABLE .. ADD column */
++
++void store_position_for_column(const char *name)
++{
++  current_thd->lex->last_field->after=my_const_cast(char*) (name);
++}
++
++bool
++add_proc_to_list(THD* thd, Item *item)
++{
++  ORDER *order;
++  Item	**item_ptr;
++
++  if (!(order = (ORDER *) thd->alloc(sizeof(ORDER)+sizeof(Item*))))
++    return 1;
++  item_ptr = (Item**) (order+1);
++  *item_ptr= item;
++  order->item=item_ptr;
++  order->free_me=0;
++  thd->lex->proc_list.link_in_list((byte*) order,(byte**) &order->next);
++  return 0;
++}
++
++
++/* Fix escaping of _, % and \ in database and table names (for ODBC) */
++
++static void remove_escape(char *name)
++{
++  if (!*name)					// For empty DB names
++    return;
++  char *to;
++#ifdef USE_MB
++  char *strend=name+(uint) strlen(name);
++#endif
++  for (to=name; *name ; name++)
++  {
++#ifdef USE_MB
++    int l;
++    if (use_mb(system_charset_info) &&
++        (l = my_ismbchar(system_charset_info, name, strend)))
++    {
++	while (l--)
++	    *to++ = *name++;
++	name--;
++	continue;
++    }
++#endif
++    if (*name == '\\' && name[1])
++      name++;					// Skip '\\'
++    *to++= *name;
++  }
++  *to=0;
++}
++
++/****************************************************************************
++** save order by and tables in own lists
++****************************************************************************/
++
++
++bool add_to_list(THD *thd, SQL_LIST &list,Item *item,bool asc)
++{
++  ORDER *order;
++  DBUG_ENTER("add_to_list");
++  if (!(order = (ORDER *) thd->alloc(sizeof(ORDER))))
++    DBUG_RETURN(1);
++  order->item_ptr= item;
++  order->item= &order->item_ptr;
++  order->asc = asc;
++  order->free_me=0;
++  order->used=0;
++  order->counter_used= 0;
++  list.link_in_list((byte*) order,(byte**) &order->next);
++  DBUG_RETURN(0);
++}
++
++
++/*
++  Add a table to list of used tables
++
++  SYNOPSIS
++    add_table_to_list()
++    table		Table to add
++    alias		alias for table (or null if no alias)
++    table_options	A set of the following bits:
++			TL_OPTION_UPDATING	Table will be updated
++			TL_OPTION_FORCE_INDEX	Force usage of index
++			TL_OPTION_ALIAS	        an alias in multi table DELETE
++    lock_type		How table should be locked
++    use_index		List of indexed used in USE INDEX
++    ignore_index	List of indexed used in IGNORE INDEX
++
++    RETURN
++      0		Error
++      #		Pointer to TABLE_LIST element added to the total table list
++*/
++
++TABLE_LIST *st_select_lex::add_table_to_list(THD *thd,
++					     Table_ident *table,
++					     LEX_STRING *alias,
++					     ulong table_options,
++					     thr_lock_type lock_type,
++					     List<String> *use_index_arg,
++					     List<String> *ignore_index_arg,
++                                             LEX_STRING *option)
++{
++  register TABLE_LIST *ptr;
++  TABLE_LIST *previous_table_ref; /* The table preceding the current one. */
++  char *alias_str;
++  LEX *lex= thd->lex;
++  DBUG_ENTER("add_table_to_list");
++  LINT_INIT(previous_table_ref);
++
++  if (!table)
++    DBUG_RETURN(0);				// End of memory
++  alias_str= alias ? alias->str : table->table.str;
++  if (!test(table_options & TL_OPTION_ALIAS) && 
++      check_table_name(table->table.str, table->table.length))
++  {
++    my_error(ER_WRONG_TABLE_NAME, MYF(0), table->table.str);
++    DBUG_RETURN(0);
++  }
++
++  if (!alias)					/* Alias is case sensitive */
++  {
++    if (table->sel)
++    {
++      my_message(ER_DERIVED_MUST_HAVE_ALIAS,
++                 ER(ER_DERIVED_MUST_HAVE_ALIAS), MYF(0));
++      DBUG_RETURN(0);
++    }
++    if (!(alias_str=thd->memdup(alias_str,table->table.length+1)))
++      DBUG_RETURN(0);
++  }
++  if (!(ptr = (TABLE_LIST *) thd->calloc(sizeof(TABLE_LIST))))
++    DBUG_RETURN(0);				/* purecov: inspected */
++  if (table->db.str)
++  {
++    if (table->is_derived_table() == FALSE && check_db_name(table->db.str))
++    {
++      my_error(ER_WRONG_DB_NAME, MYF(0), table->db.str);
++      DBUG_RETURN(0);
++    }
++    ptr->db= table->db.str;
++    ptr->db_length= table->db.length;
++  }
++  else if (thd->copy_db_to(&ptr->db, &ptr->db_length))
++    DBUG_RETURN(0);
++
++  ptr->alias= alias_str;
++  if (lower_case_table_names && table->table.length)
++    table->table.length= my_casedn_str(files_charset_info, table->table.str);
++  ptr->table_name=table->table.str;
++  ptr->table_name_length=table->table.length;
++  ptr->lock_type=   lock_type;
++  ptr->updating=    test(table_options & TL_OPTION_UPDATING);
++  ptr->force_index= test(table_options & TL_OPTION_FORCE_INDEX);
++  ptr->ignore_leaves= test(table_options & TL_OPTION_IGNORE_LEAVES);
++  ptr->derived=	    table->sel;
++  if (!ptr->derived && !my_strcasecmp(system_charset_info, ptr->db,
++                                      information_schema_name.str))
++  {
++    ST_SCHEMA_TABLE *schema_table= find_schema_table(thd, ptr->table_name);
++    if (!schema_table ||
++        (schema_table->hidden && 
++         lex->orig_sql_command == SQLCOM_END))  // not a 'show' command
++    {
++      my_error(ER_UNKNOWN_TABLE, MYF(0),
++               ptr->table_name, information_schema_name.str);
++      DBUG_RETURN(0);
++    }
++    ptr->schema_table_name= ptr->table_name;
++    ptr->schema_table= schema_table;
++  }
++  ptr->select_lex=  lex->current_select;
++  ptr->cacheable_table= 1;
++  if (use_index_arg)
++    ptr->use_index=(List<String> *) thd->memdup((gptr) use_index_arg,
++						sizeof(*use_index_arg));
++  if (ignore_index_arg)
++    ptr->ignore_index=(List<String> *) thd->memdup((gptr) ignore_index_arg,
++						   sizeof(*ignore_index_arg));
++  ptr->option= option ? option->str : 0;
++  /* check that used name is unique */
++  if (lock_type != TL_IGNORE)
++  {
++    TABLE_LIST *first_table= (TABLE_LIST*) table_list.first;
++    if (lex->sql_command == SQLCOM_CREATE_VIEW)
++      first_table= first_table ? first_table->next_local : NULL;
++    for (TABLE_LIST *tables= first_table ;
++	 tables ;
++	 tables=tables->next_local)
++    {
++      if (!my_strcasecmp(table_alias_charset, alias_str, tables->alias) &&
++	  !strcmp(ptr->db, tables->db))
++      {
++	my_error(ER_NONUNIQ_TABLE, MYF(0), alias_str); /* purecov: tested */
++	DBUG_RETURN(0);				/* purecov: tested */
++      }
++    }
++  }
++  /* Store the table reference preceding the current one. */
++  if (table_list.elements > 0)
++  {
++    /*
++      table_list.next points to the last inserted TABLE_LIST->next_local'
++      element
++      We don't use the offsetof() macro here to avoid warnings from gcc
++    */
++    previous_table_ref= (TABLE_LIST*) ((char*) table_list.next -
++                                       ((char*) &(ptr->next_local) -
++                                        (char*) ptr));
++    /*
++      Set next_name_resolution_table of the previous table reference to point
++      to the current table reference. In effect the list
++      TABLE_LIST::next_name_resolution_table coincides with
++      TABLE_LIST::next_local. Later this may be changed in
++      store_top_level_join_columns() for NATURAL/USING joins.
++    */
++    previous_table_ref->next_name_resolution_table= ptr;
++  }
++
++  /*
++    Link the current table reference in a local list (list for current select).
++    Notice that as a side effect here we set the next_local field of the
++    previous table reference to 'ptr'. Here we also add one element to the
++    list 'table_list'.
++  */
++  table_list.link_in_list((byte*) ptr, (byte**) &ptr->next_local);
++  ptr->next_name_resolution_table= NULL;
++  /* Link table in global list (all used tables) */
++  lex->add_to_query_tables(ptr);
++  DBUG_RETURN(ptr);
++}
++
++
++/*
++  Initialize a new table list for a nested join
++
++  SYNOPSIS
++    init_nested_join()
++    thd         current thread
++
++  DESCRIPTION
++    The function initializes a structure of the TABLE_LIST type
++    for a nested join. It sets up its nested join list as empty.
++    The created structure is added to the front of the current
++    join list in the st_select_lex object. Then the function
++    changes the current nest level for joins to refer to the newly
++    created empty list after having saved the info on the old level
++    in the initialized structure.
++
++  RETURN VALUE
++    0,  if success
++    1,  otherwise
++*/
++
++bool st_select_lex::init_nested_join(THD *thd)
++{
++  TABLE_LIST *ptr;
++  NESTED_JOIN *nested_join;
++  DBUG_ENTER("init_nested_join");
++
++  if (!(ptr= (TABLE_LIST*) thd->calloc(ALIGN_SIZE(sizeof(TABLE_LIST))+
++                                       sizeof(NESTED_JOIN))))
++    DBUG_RETURN(1);
++  nested_join= ptr->nested_join=
++    ((NESTED_JOIN*) ((byte*) ptr + ALIGN_SIZE(sizeof(TABLE_LIST))));
++
++  join_list->push_front(ptr);
++  ptr->embedding= embedding;
++  ptr->join_list= join_list;
++  embedding= ptr;
++  join_list= &nested_join->join_list;
++  join_list->empty();
++  DBUG_RETURN(0);
++}
++
++
++/*
++  End a nested join table list
++
++  SYNOPSIS
++    end_nested_join()
++    thd         current thread
++
++  DESCRIPTION
++    The function returns to the previous join nest level.
++    If the current level contains only one member, the function
++    moves it one level up, eliminating the nest.
++
++  RETURN VALUE
++    Pointer to TABLE_LIST element added to the total table list, if success
++    0, otherwise
++*/
++
++TABLE_LIST *st_select_lex::end_nested_join(THD *thd)
++{
++  TABLE_LIST *ptr;
++  NESTED_JOIN *nested_join;
++  DBUG_ENTER("end_nested_join");
++
++  DBUG_ASSERT(embedding);
++  ptr= embedding;
++  join_list= ptr->join_list;
++  embedding= ptr->embedding;
++  nested_join= ptr->nested_join;
++  if (nested_join->join_list.elements == 1)
++  {
++    TABLE_LIST *embedded= nested_join->join_list.head();
++    join_list->pop();
++    embedded->join_list= join_list;
++    embedded->embedding= embedding;
++    join_list->push_front(embedded);
++    ptr= embedded;
++  }
++  else if (nested_join->join_list.elements == 0)
++  {
++    join_list->pop();
++    ptr= 0;                                     // return value
++  }
++  DBUG_RETURN(ptr);
++}
++
++
++/*
++  Nest last join operation
++
++  SYNOPSIS
++    nest_last_join()
++    thd         current thread
++
++  DESCRIPTION
++    The function nest last join operation as if it was enclosed in braces.
++
++  RETURN VALUE
++    0  Error
++    #  Pointer to TABLE_LIST element created for the new nested join
++
++*/
++
++TABLE_LIST *st_select_lex::nest_last_join(THD *thd)
++{
++  TABLE_LIST *ptr;
++  NESTED_JOIN *nested_join;
++  List<TABLE_LIST> *embedded_list;
++  DBUG_ENTER("nest_last_join");
++
++  if (!(ptr= (TABLE_LIST*) thd->calloc(ALIGN_SIZE(sizeof(TABLE_LIST))+
++                                       sizeof(NESTED_JOIN))))
++    DBUG_RETURN(0);
++  nested_join= ptr->nested_join=
++    ((NESTED_JOIN*) ((byte*) ptr + ALIGN_SIZE(sizeof(TABLE_LIST))));
++
++  ptr->embedding= embedding;
++  ptr->join_list= join_list;
++  embedded_list= &nested_join->join_list;
++  embedded_list->empty();
++
++  for (uint i=0; i < 2; i++)
++  {
++    TABLE_LIST *table= join_list->pop();
++    table->join_list= embedded_list;
++    table->embedding= ptr;
++    embedded_list->push_back(table);
++    if (table->natural_join)
++    {
++      ptr->is_natural_join= TRUE;
++      /*
++        If this is a JOIN ... USING, move the list of joined fields to the
++        table reference that describes the join.
++      */
++      if (table->join_using_fields)
++      {
++        ptr->join_using_fields= table->join_using_fields;
++        table->join_using_fields= NULL;
++      }
++    }
++  }
++  join_list->push_front(ptr);
++  nested_join->used_tables= nested_join->not_null_tables= (table_map) 0;
++  DBUG_RETURN(ptr);
++}
++
++
++/*
++  Add a table to the current join list
++
++  SYNOPSIS
++    add_joined_table()
++    table       the table to add
++
++  DESCRIPTION
++    The function puts a table in front of the current join list
++    of st_select_lex object.
++    Thus, joined tables are put into this list in the reverse order
++    (the most outer join operation follows first).
++
++  RETURN VALUE
++    None
++*/
++
++void st_select_lex::add_joined_table(TABLE_LIST *table)
++{
++  DBUG_ENTER("add_joined_table");
++  join_list->push_front(table);
++  table->join_list= join_list;
++  table->embedding= embedding;
++  DBUG_VOID_RETURN;
++}
++
++
++/*
++  Convert a right join into equivalent left join
++
++  SYNOPSIS
++    convert_right_join()
++    thd         current thread
++
++  DESCRIPTION
++    The function takes the current join list t[0],t[1] ... and
++    effectively converts it into the list t[1],t[0] ...
++    Although the outer_join flag for the new nested table contains
++    JOIN_TYPE_RIGHT, it will be handled as the inner table of a left join
++    operation.
++
++  EXAMPLES
++    SELECT * FROM t1 RIGHT JOIN t2 ON on_expr =>
++      SELECT * FROM t2 LEFT JOIN t1 ON on_expr
++
++    SELECT * FROM t1,t2 RIGHT JOIN t3 ON on_expr =>
++      SELECT * FROM t1,t3 LEFT JOIN t2 ON on_expr
++
++    SELECT * FROM t1,t2 RIGHT JOIN (t3,t4) ON on_expr =>
++      SELECT * FROM t1,(t3,t4) LEFT JOIN t2 ON on_expr
++
++    SELECT * FROM t1 LEFT JOIN t2 ON on_expr1 RIGHT JOIN t3  ON on_expr2 =>
++      SELECT * FROM t3 LEFT JOIN (t1 LEFT JOIN t2 ON on_expr2) ON on_expr1
++
++  RETURN
++    Pointer to the table representing the inner table, if success
++    0, otherwise
++*/
++
++TABLE_LIST *st_select_lex::convert_right_join()
++{
++  TABLE_LIST *tab2= join_list->pop();
++  TABLE_LIST *tab1= join_list->pop();
++  DBUG_ENTER("convert_right_join");
++
++  join_list->push_front(tab2);
++  join_list->push_front(tab1);
++  tab1->outer_join|= JOIN_TYPE_RIGHT;
++
++  DBUG_RETURN(tab1);
++}
++
++/*
++  Set lock for all tables in current select level
++
++  SYNOPSIS:
++    set_lock_for_tables()
++    lock_type			Lock to set for tables
++
++  NOTE:
++    If lock is a write lock, then tables->updating is set 1
++    This is to get tables_ok to know that the table is updated by the
++    query
++*/
++
++void st_select_lex::set_lock_for_tables(thr_lock_type lock_type)
++{
++  bool for_update= lock_type >= TL_READ_NO_INSERT;
++  DBUG_ENTER("set_lock_for_tables");
++  DBUG_PRINT("enter", ("lock_type: %d  for_update: %d", lock_type,
++		       for_update));
++
++  for (TABLE_LIST *tables= (TABLE_LIST*) table_list.first;
++       tables;
++       tables= tables->next_local)
++  {
++    tables->lock_type= lock_type;
++    tables->updating=  for_update;
++  }
++  DBUG_VOID_RETURN;
++}
++
++
++/*
++  Create a fake SELECT_LEX for a unit
++
++  SYNOPSIS:
++    add_fake_select_lex()
++    thd			   thread handle
++
++  DESCRIPTION
++    The method create a fake SELECT_LEX object for a unit.
++    This object is created for any union construct containing a union
++    operation and also for any single select union construct of the form
++    (SELECT ... ORDER BY order_list [LIMIT n]) ORDER BY ... 
++    or of the form
++    (SELECT ... ORDER BY LIMIT n) ORDER BY ...
++  
++  NOTES
++    The object is used to retrieve rows from the temporary table
++    where the result on the union is obtained.
++
++  RETURN VALUES
++    1     on failure to create the object
++    0     on success
++*/
++
++bool st_select_lex_unit::add_fake_select_lex(THD *thd)
++{
++  SELECT_LEX *first_sl= first_select();
++  DBUG_ENTER("add_fake_select_lex");
++  DBUG_ASSERT(!fake_select_lex);
++
++  if (!(fake_select_lex= new (thd->mem_root) SELECT_LEX()))
++      DBUG_RETURN(1);
++  fake_select_lex->include_standalone(this, 
++                                      (SELECT_LEX_NODE**)&fake_select_lex);
++  fake_select_lex->select_number= INT_MAX;
++  fake_select_lex->parent_lex= thd->lex; /* Used in init_query. */
++  fake_select_lex->make_empty_select();
++  fake_select_lex->linkage= GLOBAL_OPTIONS_TYPE;
++  fake_select_lex->select_limit= 0;
++
++  fake_select_lex->context.outer_context=first_sl->context.outer_context;
++  /* allow item list resolving in fake select for ORDER BY */
++  fake_select_lex->context.resolve_in_select_list= TRUE;
++  fake_select_lex->context.select_lex= fake_select_lex;
++
++  if (!first_sl->next_select())
++  {
++    /* 
++      This works only for 
++      (SELECT ... ORDER BY list [LIMIT n]) ORDER BY order_list [LIMIT m],
++      (SELECT ... LIMIT n) ORDER BY order_list [LIMIT m]
++      just before the parser starts processing order_list
++    */ 
++    global_parameters= fake_select_lex;
++    fake_select_lex->no_table_names_allowed= 1;
++    thd->lex->current_select= fake_select_lex;
++  }
++  thd->lex->pop_context();
++  DBUG_RETURN(0);
++}
++
++
++/*
++  Push a new name resolution context for a JOIN ... ON clause to the
++  context stack of a query block.
++
++  SYNOPSIS
++    push_new_name_resolution_context()
++    thd       pointer to current thread
++    left_op   left  operand of the JOIN
++    right_op  rigth operand of the JOIN
++
++  DESCRIPTION
++    Create a new name resolution context for a JOIN ... ON clause,
++    set the first and last leaves of the list of table references
++    to be used for name resolution, and push the newly created
++    context to the stack of contexts of the query.
++
++  RETURN
++    FALSE  if all is OK
++    TRUE   if a memory allocation error occured
++*/
++
++bool
++push_new_name_resolution_context(THD *thd,
++                                 TABLE_LIST *left_op, TABLE_LIST *right_op)
++{
++  Name_resolution_context *on_context;
++  if (!(on_context= new (thd->mem_root) Name_resolution_context))
++    return TRUE;
++  on_context->init();
++  on_context->first_name_resolution_table=
++    left_op->first_leaf_for_name_resolution();
++  on_context->last_name_resolution_table=
++    right_op->last_leaf_for_name_resolution();
++  return thd->lex->push_context(on_context);
++}
++
++
++/*
++  Add an ON condition to the second operand of a JOIN ... ON.
++
++  SYNOPSIS
++    add_join_on
++    b     the second operand of a JOIN ... ON
++    expr  the condition to be added to the ON clause
++
++  DESCRIPTION
++    Add an ON condition to the right operand of a JOIN ... ON clause.
++
++  RETURN
++    FALSE  if there was some error
++    TRUE   if all is OK
++*/
++
++void add_join_on(TABLE_LIST *b, Item *expr)
++{
++  if (expr)
++  {
++    if (!b->on_expr)
++      b->on_expr= expr;
++    else
++    {
++      /*
++        If called from the parser, this happens if you have both a
++        right and left join. If called later, it happens if we add more
++        than one condition to the ON clause.
++      */
++      b->on_expr= new Item_cond_and(b->on_expr,expr);
++    }
++    b->on_expr->top_level_item();
++  }
++}
++
++
++/*
++  Mark that there is a NATURAL JOIN or JOIN ... USING between two
++  tables.
++
++  SYNOPSIS
++    add_join_natural()
++    a			Left join argument
++    b			Right join argument
++    using_fields        Field names from USING clause
++  
++  IMPLEMENTATION
++    This function marks that table b should be joined with a either via
++    a NATURAL JOIN or via JOIN ... USING. Both join types are special
++    cases of each other, so we treat them together. The function
++    setup_conds() creates a list of equal condition between all fields
++    of the same name for NATURAL JOIN or the fields in 'using_fields'
++    for JOIN ... USING. The list of equality conditions is stored
++    either in b->on_expr, or in JOIN::conds, depending on whether there
++    was an outer join.
++
++  EXAMPLE
++    SELECT * FROM t1 NATURAL LEFT JOIN t2
++     <=>
++    SELECT * FROM t1 LEFT JOIN t2 ON (t1.i=t2.i and t1.j=t2.j ... )
++
++    SELECT * FROM t1 NATURAL JOIN t2 WHERE <some_cond>
++     <=>
++    SELECT * FROM t1, t2 WHERE (t1.i=t2.i and t1.j=t2.j and <some_cond>)
++
++    SELECT * FROM t1 JOIN t2 USING(j) WHERE <some_cond>
++     <=>
++    SELECT * FROM t1, t2 WHERE (t1.j=t2.j and <some_cond>)
++
++  RETURN
++    None
++*/
++
++void add_join_natural(TABLE_LIST *a, TABLE_LIST *b, List<String> *using_fields)
++{
++  b->natural_join= a;
++  b->join_using_fields= using_fields;
++}
++
++
++/*
++  Reload/resets privileges and the different caches.
++
++  SYNOPSIS
++    reload_acl_and_cache()
++    thd			Thread handler (can be NULL!)
++    options             What should be reset/reloaded (tables, privileges,
++    slave...)
++    tables              Tables to flush (if any)
++    write_to_binlog     Depending on 'options', it may be very bad to write the
++                        query to the binlog (e.g. FLUSH SLAVE); this is a
++                        pointer where reload_acl_and_cache() will put 0 if
++                        it thinks we really should not write to the binlog.
++                        Otherwise it will put 1.
++
++  RETURN
++    0	 ok
++    !=0  error.  thd->killed or thd->net.report_error is set
++*/
++
++bool reload_acl_and_cache(THD *thd, ulong options, TABLE_LIST *tables,
++                          bool *write_to_binlog)
++{
++  bool result=0;
++  select_errors=0;				/* Write if more errors */
++  bool tmp_write_to_binlog= 1;
++
++  DBUG_ASSERT(!thd || !thd->in_sub_stmt);
++
++#ifndef NO_EMBEDDED_ACCESS_CHECKS
++  if (options & REFRESH_GRANT)
++  {
++    THD *tmp_thd= 0;
++    /*
++      If reload_acl_and_cache() is called from SIGHUP handler we have to
++      allocate temporary THD for execution of acl_reload()/grant_reload().
++    */
++    if (!thd && (thd= (tmp_thd= new THD)))
++    {
++      thd->thread_stack= (char*) &tmp_thd;
++      thd->store_globals();
++    }
++    if (thd)
++    {
++      (void)acl_reload(thd);
++      (void)grant_reload(thd);
++    }
++    if (tmp_thd)
++    {
++      delete tmp_thd;
++      /* Remember that we don't have a THD */
++      my_pthread_setspecific_ptr(THR_THD,  0);
++      thd= 0;
++    }
++    reset_mqh((LEX_USER *)NULL, TRUE);
++  }
++#endif
++  if (options & REFRESH_LOG)
++  {
++    /*
++      Flush the normal query log, the update log, the binary log,
++      the slow query log, and the relay log (if it exists).
++    */
++
++    /*
++      Writing this command to the binlog may result in infinite loops
++      when doing mysqlbinlog|mysql, and anyway it does not really make
++      sense to log it automatically (would cause more trouble to users
++      than it would help them)
++    */
++    tmp_write_to_binlog= 0;
++    mysql_log.new_file(1);
++    mysql_slow_log.new_file(1);
++    if( mysql_bin_log.is_open() )
++    {
++      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
++    if (ha_flush_logs())
++      result=1;
++    if (flush_error_log())
++      result=1;
++  }
++#ifdef HAVE_QUERY_CACHE
++  if (options & REFRESH_QUERY_CACHE_FREE)
++  {
++    query_cache.pack();				// FLUSH QUERY CACHE
++    options &= ~REFRESH_QUERY_CACHE;    // Don't flush cache, just free memory
++  }
++  if (options & (REFRESH_TABLES | REFRESH_QUERY_CACHE))
++  {
++    query_cache.flush();			// RESET QUERY CACHE
++  }
++#endif /*HAVE_QUERY_CACHE*/
++  /*
++    Note that if REFRESH_READ_LOCK bit is set then REFRESH_TABLES is set too
++    (see sql_yacc.yy)
++  */
++  if (options & (REFRESH_TABLES | REFRESH_READ_LOCK)) 
++  {
++    if ((options & REFRESH_READ_LOCK) && thd)
++    {
++      /*
++        We must not try to aspire a global read lock if we have a write
++        locked table. This would lead to a deadlock when trying to
++        reopen (and re-lock) the table after the flush.
++      */
++      if (thd->locked_tables)
++      {
++        THR_LOCK_DATA **lock_p= thd->locked_tables->locks;
++        THR_LOCK_DATA **end_p= lock_p + thd->locked_tables->lock_count;
++
++        for (; lock_p < end_p; lock_p++)
++        {
++          if ((*lock_p)->type == TL_WRITE)
++          {
++            my_error(ER_LOCK_OR_ACTIVE_TRANSACTION, MYF(0));
++            return 1;
++          }
++        }
++      }
++      /*
++	Writing to the binlog could cause deadlocks, as we don't log
++	UNLOCK TABLES
++      */
++      tmp_write_to_binlog= 0;
++      if (lock_global_read_lock(thd))
++	return 1;                               // Killed
++      result=close_cached_tables(thd,(options & REFRESH_FAST) ? 0 : 1,
++                                 tables);
++      if (make_global_read_lock_block_commit(thd)) // Killed
++      {
++        /* Don't leave things in a half-locked state */
++        unlock_global_read_lock(thd);
++        return 1;
++      }
++    }
++    else
++      result=close_cached_tables(thd,(options & REFRESH_FAST) ? 0 : 1, tables);
++    my_dbopt_cleanup();
++  }
++  if (options & REFRESH_HOSTS)
++    hostname_cache_refresh();
++  if (thd && (options & REFRESH_STATUS))
++    refresh_status(thd);
++  if (options & REFRESH_THREADS)
++    flush_thread_cache();
++#ifdef HAVE_REPLICATION
++  if (options & REFRESH_MASTER)
++  {
++    DBUG_ASSERT(thd);
++    tmp_write_to_binlog= 0;
++    if (reset_master(thd))
++    {
++      result=1;
++      thd->fatal_error();                       // Ensure client get error
++    }
++  }
++#endif
++#ifdef OPENSSL
++   if (options & REFRESH_DES_KEY_FILE)
++   {
++     if (des_key_file)
++       result=load_des_key_file(des_key_file);
++   }
++#endif
++#ifdef HAVE_REPLICATION
++ if (options & REFRESH_SLAVE)
++ {
++   tmp_write_to_binlog= 0;
++   pthread_mutex_lock(&LOCK_active_mi);
++   if (reset_slave(thd, active_mi))
++     result=1;
++   pthread_mutex_unlock(&LOCK_active_mi);
++ }
++#endif
++ if (options & REFRESH_USER_RESOURCES)
++   reset_mqh((LEX_USER *) NULL);
++ *write_to_binlog= tmp_write_to_binlog;
++ return result;
++}
++
++/*
++  kill on thread
++
++  SYNOPSIS
++    kill_one_thread()
++    thd			Thread class
++    id			Thread id
++
++  NOTES
++    This is written such that we have a short lock on LOCK_thread_count
++*/
++
++void kill_one_thread(THD *thd, ulong id, bool only_kill_query)
++{
++  THD *tmp;
++  uint error=ER_NO_SUCH_THREAD;
++  VOID(pthread_mutex_lock(&LOCK_thread_count)); // For unlink from list
++  I_List_iterator<THD> it(threads);
++  while ((tmp=it++))
++  {
++    if (tmp->thread_id == id)
++    {
++      pthread_mutex_lock(&tmp->LOCK_delete);	// Lock from delete
++      break;
++    }
++  }
++  VOID(pthread_mutex_unlock(&LOCK_thread_count));
++  if (tmp)
++  {
++    if ((thd->security_ctx->master_access & SUPER_ACL) ||
++	!strcmp(thd->security_ctx->user, tmp->security_ctx->user))
++    {
++      tmp->awake(only_kill_query ? THD::KILL_QUERY : THD::KILL_CONNECTION);
++      error=0;
++    }
++    else
++      error=ER_KILL_DENIED_ERROR;
++    pthread_mutex_unlock(&tmp->LOCK_delete);
++  }
++
++  if (!error)
++    send_ok(thd);
++  else
++    my_error(error, MYF(0), id);
++}
++
++
++	/* If pointer is not a null pointer, append filename to it */
++
++static bool append_file_to_dir(THD *thd, const char **filename_ptr,
++			       const char *table_name)
++{
++  char buff[FN_REFLEN],*ptr, *end;
++  if (!*filename_ptr)
++    return 0;					// nothing to do
++
++  /* Check that the filename is not too long and it's a hard path */
++  if (strlen(*filename_ptr)+strlen(table_name) >= FN_REFLEN-1 ||
++      !test_if_hard_path(*filename_ptr))
++  {
++    my_error(ER_WRONG_TABLE_NAME, MYF(0), *filename_ptr);
++    return 1;
++  }
++  /* Fix is using unix filename format on dos */
++  strmov(buff,*filename_ptr);
++  end=convert_dirname(buff, *filename_ptr, NullS);
++  if (!(ptr=thd->alloc((uint) (end-buff)+(uint) strlen(table_name)+1)))
++    return 1;					// End of memory
++  *filename_ptr=ptr;
++  strxmov(ptr,buff,table_name,NullS);
++  return 0;
++}
++
++
++/*
++  Check if the select is a simple select (not an union)
++
++  SYNOPSIS
++    check_simple_select()
++
++  RETURN VALUES
++    0	ok
++    1	error	; In this case the error messege is sent to the client
++*/
++
++bool check_simple_select()
++{
++  THD *thd= current_thd;
++  LEX *lex= thd->lex;
++  if (lex->current_select != &lex->select_lex)
++  {
++    char command[80];
++    strmake(command, lex->yylval->symbol.str,
++	    min(lex->yylval->symbol.length, sizeof(command)-1));
++    my_error(ER_CANT_USE_OPTION_HERE, MYF(0), command);
++    return 1;
++  }
++  return 0;
++}
++
++
++Comp_creator *comp_eq_creator(bool invert)
++{
++  return invert?(Comp_creator *)&ne_creator:(Comp_creator *)&eq_creator;
++}
++
++
++Comp_creator *comp_ge_creator(bool invert)
++{
++  return invert?(Comp_creator *)&lt_creator:(Comp_creator *)&ge_creator;
++}
++
++
++Comp_creator *comp_gt_creator(bool invert)
++{
++  return invert?(Comp_creator *)&le_creator:(Comp_creator *)&gt_creator;
++}
++
++
++Comp_creator *comp_le_creator(bool invert)
++{
++  return invert?(Comp_creator *)&gt_creator:(Comp_creator *)&le_creator;
++}
++
++
++Comp_creator *comp_lt_creator(bool invert)
++{
++  return invert?(Comp_creator *)&ge_creator:(Comp_creator *)&lt_creator;
++}
++
++
++Comp_creator *comp_ne_creator(bool invert)
++{
++  return invert?(Comp_creator *)&eq_creator:(Comp_creator *)&ne_creator;
++}
++
++
++/*
++  Construct ALL/ANY/SOME subquery Item
++
++  SYNOPSIS
++    all_any_subquery_creator()
++    left_expr - pointer to left expression
++    cmp - compare function creator
++    all - true if we create ALL subquery
++    select_lex - pointer on parsed subquery structure
++
++  RETURN VALUE
++    constructed Item (or 0 if out of memory)
++*/
++Item * all_any_subquery_creator(Item *left_expr,
++				chooser_compare_func_creator cmp,
++				bool all,
++				SELECT_LEX *select_lex)
++{
++  if ((cmp == &comp_eq_creator) && !all)       //  = ANY <=> IN
++    return new Item_in_subselect(left_expr, select_lex);
++
++  if ((cmp == &comp_ne_creator) && all)        // <> ALL <=> NOT IN
++    return new Item_func_not(new Item_in_subselect(left_expr, select_lex));
++
++  Item_allany_subselect *it=
++    new Item_allany_subselect(left_expr, cmp, select_lex, all);
++  if (all)
++    return it->upper_item= new Item_func_not_all(it);	/* ALL */
++
++  return it->upper_item= new Item_func_nop_all(it);      /* ANY/SOME */
++}
++
++
++/*
++  CREATE INDEX and DROP INDEX are implemented by calling ALTER TABLE with
++  the proper arguments.  This isn't very fast but it should work for most
++  cases.
++
++  In the future ALTER TABLE will notice that only added indexes
++  and create these one by one for the existing table without having to do
++  a full rebuild.
++
++  One should normally create all indexes with CREATE TABLE or ALTER TABLE.
++*/
++
++bool mysql_create_index(THD *thd, TABLE_LIST *table_list, List<Key> &keys)
++{
++  List<create_field> fields;
++  ALTER_INFO alter_info;
++  alter_info.flags= ALTER_ADD_INDEX;
++  HA_CREATE_INFO create_info;
++  DBUG_ENTER("mysql_create_index");
++  bzero((char*) &create_info,sizeof(create_info));
++  create_info.db_type=DB_TYPE_DEFAULT;
++  create_info.default_table_charset= thd->variables.collation_database;
++  DBUG_RETURN(mysql_alter_table(thd,table_list->db,table_list->table_name,
++				&create_info, table_list,
++				fields, keys, 0, (ORDER*)0,
++                                0, &alter_info, 1));
++}
++
++
++bool mysql_drop_index(THD *thd, TABLE_LIST *table_list, ALTER_INFO *alter_info)
++{
++  List<create_field> fields;
++  List<Key> keys;
++  HA_CREATE_INFO create_info;
++  DBUG_ENTER("mysql_drop_index");
++  bzero((char*) &create_info,sizeof(create_info));
++  create_info.db_type=DB_TYPE_DEFAULT;
++  create_info.default_table_charset= thd->variables.collation_database;
++  alter_info->clear();
++  alter_info->flags= ALTER_DROP_INDEX;
++  DBUG_RETURN(mysql_alter_table(thd,table_list->db,table_list->table_name,
++				&create_info, table_list,
++				fields, keys, 0, (ORDER*)0,
++                                0, alter_info, 1));
++}
++
++
++/*
++  Multi update query pre-check
++
++  SYNOPSIS
++    multi_update_precheck()
++    thd		Thread handler
++    tables	Global/local table list (have to be the same)
++
++  RETURN VALUE
++    FALSE OK
++    TRUE  Error
++*/
++
++bool multi_update_precheck(THD *thd, TABLE_LIST *tables)
++{
++  const char *msg= 0;
++  TABLE_LIST *table;
++  LEX *lex= thd->lex;
++  SELECT_LEX *select_lex= &lex->select_lex;
++  DBUG_ENTER("multi_update_precheck");
++
++  if (select_lex->item_list.elements != lex->value_list.elements)
++  {
++    my_message(ER_WRONG_VALUE_COUNT, ER(ER_WRONG_VALUE_COUNT), MYF(0));
++    DBUG_RETURN(TRUE);
++  }
++  /*
++    Ensure that we have UPDATE or SELECT privilege for each table
++    The exact privilege is checked in mysql_multi_update()
++  */
++  for (table= tables; table; table= table->next_local)
++  {
++    if (table->derived)
++      table->grant.privilege= SELECT_ACL;
++    else if ((check_access(thd, UPDATE_ACL, table->db,
++                           &table->grant.privilege, 0, 1,
++                           test(table->schema_table)) ||
++              grant_option &&
++              check_grant(thd, UPDATE_ACL, table, 0, 1, 1)) &&
++             (check_access(thd, SELECT_ACL, table->db,
++                           &table->grant.privilege, 0, 0,
++                           test(table->schema_table)) ||
++              grant_option && check_grant(thd, SELECT_ACL, table, 0, 1, 0)))
++      DBUG_RETURN(TRUE);
++
++    table->table_in_first_from_clause= 1;
++  }
++  /*
++    Is there tables of subqueries?
++  */
++  if (&lex->select_lex != lex->all_selects_list || lex->time_zone_tables_used)
++  {
++    DBUG_PRINT("info",("Checking sub query list"));
++    for (table= tables; table; table= table->next_global)
++    {
++      if (!my_tz_check_n_skip_implicit_tables(&table,
++                                              lex->time_zone_tables_used) &&
++          !table->table_in_first_from_clause)
++      {
++	if (check_access(thd, SELECT_ACL, table->db,
++			 &table->grant.privilege, 0, 0,
++                         test(table->schema_table)) ||
++	    grant_option && check_grant(thd, SELECT_ACL, table, 0, 1, 0))
++	  DBUG_RETURN(TRUE);
++      }
++    }
++  }
++
++  if (select_lex->order_list.elements)
++    msg= "ORDER BY";
++  else if (select_lex->select_limit)
++    msg= "LIMIT";
++  if (msg)
++  {
++    my_error(ER_WRONG_USAGE, MYF(0), "UPDATE", msg);
++    DBUG_RETURN(TRUE);
++  }
++  DBUG_RETURN(FALSE);
++}
++
++/*
++  Multi delete query pre-check
++
++  SYNOPSIS
++    multi_delete_precheck()
++    thd			Thread handler
++    tables		Global/local table list
++
++  RETURN VALUE
++    FALSE OK
++    TRUE  error
++*/
++
++bool multi_delete_precheck(THD *thd, TABLE_LIST *tables)
++{
++  SELECT_LEX *select_lex= &thd->lex->select_lex;
++  TABLE_LIST *aux_tables=
++    (TABLE_LIST *)thd->lex->auxiliary_table_list.first;
++  TABLE_LIST **save_query_tables_own_last= thd->lex->query_tables_own_last;
++  DBUG_ENTER("multi_delete_precheck");
++
++  /* sql_yacc guarantees that tables and aux_tables are not zero */
++  DBUG_ASSERT(aux_tables != 0);
++  if (check_db_used(thd, tables) || check_db_used(thd,aux_tables) ||
++      check_table_access(thd, SELECT_ACL, tables, 0))
++    DBUG_RETURN(TRUE);
++
++  /*
++    Since aux_tables list is not part of LEX::query_tables list we
++    have to juggle with LEX::query_tables_own_last value to be able
++    call check_table_access() safely.
++  */
++  thd->lex->query_tables_own_last= 0;
++  if (check_table_access(thd, DELETE_ACL, aux_tables, 0))
++  {
++    thd->lex->query_tables_own_last= save_query_tables_own_last;
++    DBUG_RETURN(TRUE);
++  }
++  thd->lex->query_tables_own_last= save_query_tables_own_last;
++
++  if ((thd->options & OPTION_SAFE_UPDATES) && !select_lex->where)
++  {
++    my_message(ER_UPDATE_WITHOUT_KEY_IN_SAFE_MODE,
++               ER(ER_UPDATE_WITHOUT_KEY_IN_SAFE_MODE), MYF(0));
++    DBUG_RETURN(TRUE);
++  }
++  DBUG_RETURN(FALSE);
++}
++
++
++/*
++  Link tables in auxilary table list of multi-delete with corresponding
++  elements in main table list, and set proper locks for them.
++
++  SYNOPSIS
++    multi_delete_set_locks_and_link_aux_tables()
++      lex - pointer to LEX representing multi-delete
++
++  RETURN VALUE
++    FALSE - success
++    TRUE  - error
++*/
++
++bool multi_delete_set_locks_and_link_aux_tables(LEX *lex)
++{
++  TABLE_LIST *tables= (TABLE_LIST*)lex->select_lex.table_list.first;
++  TABLE_LIST *target_tbl;
++  DBUG_ENTER("multi_delete_set_locks_and_link_aux_tables");
++
++  lex->table_count= 0;
++
++  for (target_tbl= (TABLE_LIST *)lex->auxiliary_table_list.first;
++       target_tbl; target_tbl= target_tbl->next_local)
++  {
++    lex->table_count++;
++    /* All tables in aux_tables must be found in FROM PART */
++    TABLE_LIST *walk;
++    for (walk= tables; walk; walk= walk->next_local)
++    {
++      if (!my_strcasecmp(table_alias_charset,
++			 target_tbl->alias, walk->alias) &&
++	  !strcmp(walk->db, target_tbl->db))
++	break;
++    }
++    if (!walk)
++    {
++      my_error(ER_UNKNOWN_TABLE, MYF(0),
++               target_tbl->table_name, "MULTI DELETE");
++      DBUG_RETURN(TRUE);
++    }
++    if (!walk->derived)
++    {
++      target_tbl->table_name= walk->table_name;
++      target_tbl->table_name_length= walk->table_name_length;
++    }
++    walk->updating= target_tbl->updating;
++    walk->lock_type= target_tbl->lock_type;
++    target_tbl->correspondent_table= walk;	// Remember corresponding table
++  }
++  DBUG_RETURN(FALSE);
++}
++
++
++/*
++  simple UPDATE query pre-check
++
++  SYNOPSIS
++    update_precheck()
++    thd		Thread handler
++    tables	Global table list
++
++  RETURN VALUE
++    FALSE OK
++    TRUE  Error
++*/
++
++bool update_precheck(THD *thd, TABLE_LIST *tables)
++{
++  DBUG_ENTER("update_precheck");
++  if (thd->lex->select_lex.item_list.elements != thd->lex->value_list.elements)
++  {
++    my_message(ER_WRONG_VALUE_COUNT, ER(ER_WRONG_VALUE_COUNT), MYF(0));
++    DBUG_RETURN(TRUE);
++  }
++  DBUG_RETURN(check_db_used(thd, tables) ||
++	       check_one_table_access(thd, UPDATE_ACL, tables));
++}
++
++
++/*
++  simple DELETE query pre-check
++
++  SYNOPSIS
++    delete_precheck()
++    thd		Thread handler
++    tables	Global table list
++
++  RETURN VALUE
++    FALSE  OK
++    TRUE   error
++*/
++
++bool delete_precheck(THD *thd, TABLE_LIST *tables)
++{
++  DBUG_ENTER("delete_precheck");
++  if (check_one_table_access(thd, DELETE_ACL, tables))
++    DBUG_RETURN(TRUE);
++  /* Set privilege for the WHERE clause */
++  tables->grant.want_privilege=(SELECT_ACL & ~tables->grant.privilege);
++  DBUG_RETURN(FALSE);
++}
++
++
++/*
++  simple INSERT query pre-check
++
++  SYNOPSIS
++    insert_precheck()
++    thd		Thread handler
++    tables	Global table list
++
++  RETURN VALUE
++    FALSE  OK
++    TRUE   error
++*/
++
++bool insert_precheck(THD *thd, TABLE_LIST *tables)
++{
++  LEX *lex= thd->lex;
++  DBUG_ENTER("insert_precheck");
++
++  /*
++    Check that we have modify privileges for the first table and
++    select privileges for the rest
++  */
++  ulong privilege= (INSERT_ACL |
++                    (lex->duplicates == DUP_REPLACE ? DELETE_ACL : 0) |
++                    (lex->value_list.elements ? UPDATE_ACL : 0));
++
++  if (check_one_table_access(thd, privilege, tables))
++    DBUG_RETURN(TRUE);
++
++  if (lex->update_list.elements != lex->value_list.elements)
++  {
++    my_message(ER_WRONG_VALUE_COUNT, ER(ER_WRONG_VALUE_COUNT), MYF(0));
++    DBUG_RETURN(TRUE);
++  }
++  if (check_db_used(thd, tables))
++    DBUG_RETURN(TRUE);
++  DBUG_RETURN(FALSE);
++}
++
++
++/*
++  CREATE TABLE query pre-check
++
++  SYNOPSIS
++    create_table_precheck()
++    thd			Thread handler
++    tables		Global table list
++    create_table	Table which will be created
++
++  RETURN VALUE
++    FALSE   OK
++    TRUE   Error
++*/
++
++bool create_table_precheck(THD *thd, TABLE_LIST *tables,
++                           TABLE_LIST *create_table)
++{
++  LEX *lex= thd->lex;
++  SELECT_LEX *select_lex= &lex->select_lex;
++  ulong want_priv;
++  bool error= TRUE;                                 // Error message is given
++  DBUG_ENTER("create_table_precheck");
++
++  want_priv= ((lex->create_info.options & HA_LEX_CREATE_TMP_TABLE) ?
++              CREATE_TMP_ACL : CREATE_ACL);
++  lex->create_info.alias= create_table->alias;
++  if (check_access(thd, want_priv, create_table->db,
++		   &create_table->grant.privilege, 0, 0,
++                   test(create_table->schema_table)) ||
++      check_merge_table_access(thd, create_table->db,
++			       (TABLE_LIST *)
++			       lex->create_info.merge_list.first))
++    goto err;
++  if (grant_option && want_priv != CREATE_TMP_ACL &&
++      check_grant(thd, want_priv, create_table, 0, 1, 0))
++    goto err;
++
++  if (select_lex->item_list.elements)
++  {
++    /* Check permissions for used tables in CREATE TABLE ... SELECT */
++
++#ifdef NOT_NECESSARY_TO_CHECK_CREATE_TABLE_EXIST_WHEN_PREPARING_STATEMENT
++    /* This code throws an ill error for CREATE TABLE t1 SELECT * FROM t1 */
++    /*
++      Only do the check for PS, becasue we on execute we have to check that
++      against the opened tables to ensure we don't use a table that is part
++      of the view (which can only be done after the table has been opened).
++    */
++    if (thd->stmt_arena->is_stmt_prepare_or_first_sp_execute())
++    {
++      /*
++        For temporary tables we don't have to check if the created table exists
++      */
++      if (!(lex->create_info.options & HA_LEX_CREATE_TMP_TABLE) &&
++          find_table_in_global_list(tables, create_table->db,
++                                    create_table->table_name))
++      {
++	error= FALSE;
++        goto err;
++      }
++    }
++#endif
++    if (tables && check_table_access(thd, SELECT_ACL, tables,0))
++      goto err;
++  }
++  error= FALSE;
++
++err:
++  DBUG_RETURN(error);
++}
++
++
++/*
++  negate given expression
++
++  SYNOPSIS
++    negate_expression()
++    thd  thread handler
++    expr expression for negation
++
++  RETURN
++    negated expression
++*/
++
++Item *negate_expression(THD *thd, Item *expr)
++{
++  Item *negated;
++  if (expr->type() == Item::FUNC_ITEM &&
++      ((Item_func *) expr)->functype() == Item_func::NOT_FUNC)
++  {
++    /* it is NOT(NOT( ... )) */
++    Item *arg= ((Item_func *) expr)->arguments()[0];
++    enum_parsing_place place= thd->lex->current_select->parsing_place;
++    if (arg->is_bool_func() || place == IN_WHERE || place == IN_HAVING)
++      return arg;
++    /*
++      if it is not boolean function then we have to emulate value of
++      not(not(a)), it will be a != 0
++    */
++    return new Item_func_ne(arg, new Item_int((char*) "0", 0, 1));
++  }
++
++  if ((negated= expr->neg_transformer(thd)) != 0)
++    return negated;
++  return new Item_func_not(expr);
++}
++
++/*
++  Set the specified definer to the default value, which is the current user in
++  the thread.
++ 
++  SYNOPSIS
++    get_default_definer()
++    thd       [in] thread handler
++    definer   [out] definer
++*/
++ 
++void get_default_definer(THD *thd, LEX_USER *definer)
++{
++  const Security_context *sctx= thd->security_ctx;
++
++  definer->user.str= (char *) sctx->priv_user;
++  definer->user.length= strlen(definer->user.str);
++
++  definer->host.str= (char *) sctx->priv_host;
++  definer->host.length= strlen(definer->host.str);
++}
++
++
++/*
++  Create default definer for the specified THD.
++
++  SYNOPSIS
++    create_default_definer()
++    thd         [in] thread handler
++
++  RETURN
++    On success, return a valid pointer to the created and initialized
++    LEX_USER, which contains definer information.
++    On error, return 0.
++*/
++
++LEX_USER *create_default_definer(THD *thd)
++{
++  LEX_USER *definer;
++
++  if (! (definer= (LEX_USER*) thd->alloc(sizeof(LEX_USER))))
++    return 0;
++
++  get_default_definer(thd, definer);
++
++  return definer;
++}
++
++
++/*
++  Create definer with the given user and host names.
++
++  SYNOPSIS
++    create_definer()
++    thd         [in] thread handler
++    user_name   [in] user name
++    host_name   [in] host name
++
++  RETURN
++    On success, return a valid pointer to the created and initialized
++    LEX_USER, which contains definer information.
++    On error, return 0.
++*/
++
++LEX_USER *create_definer(THD *thd, LEX_STRING *user_name, LEX_STRING *host_name)
++{
++  LEX_USER *definer;
++
++  /* Create and initialize. */
++
++  if (! (definer= (LEX_USER*) thd->alloc(sizeof(LEX_USER))))
++    return 0;
++
++  definer->user= *user_name;
++  definer->host= *host_name;
++
++  return definer;
++}
++
++
++/*
++  Retuns information about user or current user.
++
++  SYNOPSIS
++    get_current_user()
++    thd         [in] thread handler
++    user        [in] user
++
++  RETURN
++    On success, return a valid pointer to initialized
++    LEX_USER, which contains user information.
++    On error, return 0.
++*/
++
++LEX_USER *get_current_user(THD *thd, LEX_USER *user)
++{
++  if (!user->user.str)  // current_user
++    return create_default_definer(thd);
++
++  return user;
++}
++
++
++/*
++  Check that length of a string does not exceed some limit.
++
++  SYNOPSIS
++    check_string_length()
++      str         string to be checked
++      err_msg     error message to be displayed if the string is too long
++      max_length  max length
++
++  RETURN
++    FALSE   the passed string is not longer than max_length
++    TRUE    the passed string is longer than max_length
++*/
++
++bool check_string_length(LEX_STRING *str, const char *err_msg,
++                         uint max_length)
++{
++  if (str->length <= max_length)
++    return FALSE;
++
++  my_error(ER_WRONG_STRING_LENGTH, MYF(0), str->str, err_msg, max_length);
++
++  return TRUE;
++}
+diff -urNad mysql-5.0-etch~/sql/sql_show.cc mysql-5.0-etch/sql/sql_show.cc
+--- mysql-5.0-etch~/sql/sql_show.cc	2007-05-28 18:56:15.000000000 +0200
++++ mysql-5.0-etch/sql/sql_show.cc	2007-05-28 19:12:52.000000000 +0200
+@@ -2141,7 +2141,7 @@
+   */
+   thd->reset_n_backup_open_tables_state(&open_tables_state_backup);
+ 
+-  if (lsel)
++  if (lsel && lsel->table_list.first)
+   {
+     TABLE_LIST *show_table_list= (TABLE_LIST*) lsel->table_list.first;
+     bool res;
+diff -urNad mysql-5.0-etch~/sql/sql_show.cc.orig mysql-5.0-etch/sql/sql_show.cc.orig
+--- mysql-5.0-etch~/sql/sql_show.cc.orig	1970-01-01 01:00:00.000000000 +0100
++++ mysql-5.0-etch/sql/sql_show.cc.orig	2007-05-28 18:56:15.000000000 +0200
+@@ -0,0 +1,4350 @@
++/* Copyright (C) 2000-2004 MySQL AB
++
++   This program is free software; you can redistribute it and/or modify
++   it under the terms of the GNU General Public License as published by
++   the Free Software Foundation; either version 2 of the License, or
++   (at your option) any later version.
++
++   This program is distributed in the hope that it will be useful,
++   but WITHOUT ANY WARRANTY; without even the implied warranty of
++   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++   GNU General Public License for more details.
++
++   You should have received a copy of the GNU General Public License
++   along with this program; if not, write to the Free Software
++   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */
++
++
++/* Function with list databases, tables or fields */
++
++#include "mysql_priv.h"
++#include "sql_select.h"                         // For select_describe
++#include "repl_failsafe.h"
++#include "sp.h"
++#include "sp_head.h"
++#include "sql_trigger.h"
++#include <my_dir.h>
++
++#ifdef HAVE_BERKELEY_DB
++#include "ha_berkeley.h"			// For berkeley_show_logs
++#endif
++
++static const char *grant_names[]={
++  "select","insert","update","delete","create","drop","reload","shutdown",
++  "process","file","grant","references","index","alter"};
++
++#ifndef NO_EMBEDDED_ACCESS_CHECKS
++static TYPELIB grant_types = { sizeof(grant_names)/sizeof(char **),
++                               "grant_types",
++                               grant_names, NULL};
++#endif
++
++static int
++store_create_info(THD *thd, TABLE_LIST *table_list, String *packet);
++static void
++append_algorithm(TABLE_LIST *table, String *buff);
++static int
++view_store_create_info(THD *thd, TABLE_LIST *table, String *buff);
++static bool schema_table_store_record(THD *thd, TABLE *table);
++
++
++/***************************************************************************
++** List all table types supported 
++***************************************************************************/
++
++bool mysqld_show_storage_engines(THD *thd)
++{
++  List<Item> field_list;
++  Protocol *protocol= thd->protocol;
++  DBUG_ENTER("mysqld_show_storage_engines");
++
++  field_list.push_back(new Item_empty_string("Engine",10));
++  field_list.push_back(new Item_empty_string("Support",10));
++  field_list.push_back(new Item_empty_string("Comment",80));
++
++  if (protocol->send_fields(&field_list,
++                            Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
++    DBUG_RETURN(TRUE);
++
++  const char *default_type_name= 
++    ha_get_storage_engine((enum db_type)thd->variables.table_type);
++
++  handlerton **types;
++  for (types= sys_table_types; *types; types++)
++  {
++    if (!((*types)->flags & HTON_HIDDEN))
++    {
++      protocol->prepare_for_resend();
++      protocol->store((*types)->name, system_charset_info);
++      const char *option_name= show_comp_option_name[(int) (*types)->state];
++
++      if ((*types)->state == SHOW_OPTION_YES &&
++          !my_strcasecmp(system_charset_info, default_type_name, (*types)->name))
++        option_name= "DEFAULT";
++      protocol->store(option_name, system_charset_info);
++      protocol->store((*types)->comment, system_charset_info);
++      if (protocol->write())
++        DBUG_RETURN(TRUE);
++    }
++  }
++  send_eof(thd);
++  DBUG_RETURN(FALSE);
++}
++
++
++/***************************************************************************
++ List all privileges supported
++***************************************************************************/
++
++struct show_privileges_st {
++  const char *privilege;
++  const char *context;
++  const char *comment;
++};
++
++static struct show_privileges_st sys_privileges[]=
++{
++  {"Alter", "Tables",  "To alter the table"},
++  {"Alter routine", "Functions,Procedures",  "To alter or drop stored functions/procedures"},
++  {"Create", "Databases,Tables,Indexes",  "To create new databases and tables"},
++  {"Create routine","Functions,Procedures","To use CREATE FUNCTION/PROCEDURE"},
++  {"Create temporary tables","Databases","To use CREATE TEMPORARY TABLE"},
++  {"Create view", "Tables",  "To create new views"},
++  {"Create user", "Server Admin",  "To create new users"},
++  {"Delete", "Tables",  "To delete existing rows"},
++  {"Drop", "Databases,Tables", "To drop databases, tables, and views"},
++  {"Execute", "Functions,Procedures", "To execute stored routines"},
++  {"File", "File access on server",   "To read and write files on the server"},
++  {"Grant option",  "Databases,Tables,Functions,Procedures", "To give to other users those privileges you possess"},
++  {"Index", "Tables",  "To create or drop indexes"},
++  {"Insert", "Tables",  "To insert data into tables"},
++  {"Lock tables","Databases","To use LOCK TABLES (together with SELECT privilege)"},
++  {"Process", "Server Admin", "To view the plain text of currently executing queries"},
++  {"References", "Databases,Tables", "To have references on tables"},
++  {"Reload", "Server Admin", "To reload or refresh tables, logs and privileges"},
++  {"Replication client","Server Admin","To ask where the slave or master servers are"},
++  {"Replication slave","Server Admin","To read binary log events from the master"},
++  {"Select", "Tables",  "To retrieve rows from table"},
++  {"Show databases","Server Admin","To see all databases with SHOW DATABASES"},
++  {"Show view","Tables","To see views with SHOW CREATE VIEW"},
++  {"Shutdown","Server Admin", "To shut down the server"},
++  {"Super","Server Admin","To use KILL thread, SET GLOBAL, CHANGE MASTER, etc."},
++  {"Update", "Tables",  "To update existing rows"},
++  {"Usage","Server Admin","No privileges - allow connect only"},
++  {NullS, NullS, NullS}
++};
++
++bool mysqld_show_privileges(THD *thd)
++{
++  List<Item> field_list;
++  Protocol *protocol= thd->protocol;
++  DBUG_ENTER("mysqld_show_privileges");
++
++  field_list.push_back(new Item_empty_string("Privilege",10));
++  field_list.push_back(new Item_empty_string("Context",15));
++  field_list.push_back(new Item_empty_string("Comment",NAME_LEN));
++
++  if (protocol->send_fields(&field_list,
++                            Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
++    DBUG_RETURN(TRUE);
++
++  show_privileges_st *privilege= sys_privileges;
++  for (privilege= sys_privileges; privilege->privilege ; privilege++)
++  {
++    protocol->prepare_for_resend();
++    protocol->store(privilege->privilege, system_charset_info);
++    protocol->store(privilege->context, system_charset_info);
++    protocol->store(privilege->comment, system_charset_info);
++    if (protocol->write())
++      DBUG_RETURN(TRUE);
++  }
++  send_eof(thd);
++  DBUG_RETURN(FALSE);
++}
++
++
++/***************************************************************************
++  List all column types
++***************************************************************************/
++
++struct show_column_type_st
++{
++  const char *type;
++  uint size;
++  const char *min_value;
++  const char *max_value;
++  uint precision;
++  uint scale;
++  const char *nullable;
++  const char *auto_increment;
++  const char *unsigned_attr;
++  const char *zerofill;
++  const char *searchable;
++  const char *case_sensitivity;
++  const char *default_value;
++  const char *comment;
++};
++
++/* TODO: Add remaning types */
++
++static struct show_column_type_st sys_column_types[]=
++{
++  {"tinyint",
++    1,  "-128",  "127",  0,  0,  "YES",  "YES",
++    "NO",   "YES", "YES",  "NO",  "NULL,0",
++    "A very small integer"},
++  {"tinyint unsigned",
++    1,  "0"   ,  "255",  0,  0,  "YES",  "YES",
++    "YES",  "YES",  "YES",  "NO",  "NULL,0",
++    "A very small integer"},
++};
++
++bool mysqld_show_column_types(THD *thd)
++{
++  List<Item> field_list;
++  Protocol *protocol= thd->protocol;
++  DBUG_ENTER("mysqld_show_column_types");
++
++  field_list.push_back(new Item_empty_string("Type",30));
++  field_list.push_back(new Item_int("Size",(longlong) 1,21));
++  field_list.push_back(new Item_empty_string("Min_Value",20));
++  field_list.push_back(new Item_empty_string("Max_Value",20));
++  field_list.push_back(new Item_return_int("Prec", 4, MYSQL_TYPE_SHORT));
++  field_list.push_back(new Item_return_int("Scale", 4, MYSQL_TYPE_SHORT));
++  field_list.push_back(new Item_empty_string("Nullable",4));
++  field_list.push_back(new Item_empty_string("Auto_Increment",4));
++  field_list.push_back(new Item_empty_string("Unsigned",4));
++  field_list.push_back(new Item_empty_string("Zerofill",4));
++  field_list.push_back(new Item_empty_string("Searchable",4));
++  field_list.push_back(new Item_empty_string("Case_Sensitive",4));
++  field_list.push_back(new Item_empty_string("Default",NAME_LEN));
++  field_list.push_back(new Item_empty_string("Comment",NAME_LEN));
++
++  if (protocol->send_fields(&field_list,
++                            Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
++    DBUG_RETURN(TRUE);
++
++  /* TODO: Change the loop to not use 'i' */
++  for (uint i=0; i < sizeof(sys_column_types)/sizeof(sys_column_types[0]); i++)
++  {
++    protocol->prepare_for_resend();
++    protocol->store(sys_column_types[i].type, system_charset_info);
++    protocol->store((ulonglong) sys_column_types[i].size);
++    protocol->store(sys_column_types[i].min_value, system_charset_info);
++    protocol->store(sys_column_types[i].max_value, system_charset_info);
++    protocol->store_short((longlong) sys_column_types[i].precision);
++    protocol->store_short((longlong) sys_column_types[i].scale);
++    protocol->store(sys_column_types[i].nullable, system_charset_info);
++    protocol->store(sys_column_types[i].auto_increment, system_charset_info);
++    protocol->store(sys_column_types[i].unsigned_attr, system_charset_info);
++    protocol->store(sys_column_types[i].zerofill, system_charset_info);
++    protocol->store(sys_column_types[i].searchable, system_charset_info);
++    protocol->store(sys_column_types[i].case_sensitivity, system_charset_info);
++    protocol->store(sys_column_types[i].default_value, system_charset_info);
++    protocol->store(sys_column_types[i].comment, system_charset_info);
++    if (protocol->write())
++      DBUG_RETURN(TRUE);
++  }
++  send_eof(thd);
++  DBUG_RETURN(FALSE);
++}
++
++
++/*
++  find_files() - find files in a given directory.
++
++  SYNOPSIS
++    find_files()
++    thd                 thread handler
++    files               put found files in this list
++    db                  database name to set in TABLE_LIST structure
++    path                path to database
++    wild                filter for found files
++    dir                 read databases in path if TRUE, read .frm files in
++                        database otherwise
++
++  RETURN
++    FIND_FILES_OK       success
++    FIND_FILES_OOM      out of memory error
++    FIND_FILES_DIR      no such directory, or directory can't be read
++*/
++
++enum find_files_result {
++  FIND_FILES_OK,
++  FIND_FILES_OOM,
++  FIND_FILES_DIR
++};
++
++static
++find_files_result
++find_files(THD *thd, List<char> *files, const char *db,
++           const char *path, const char *wild, bool dir)
++{
++  uint i;
++  char *ext;
++  MY_DIR *dirp;
++  FILEINFO *file;
++#ifndef NO_EMBEDDED_ACCESS_CHECKS
++  uint col_access=thd->col_access;
++#endif
++  TABLE_LIST table_list;
++  DBUG_ENTER("find_files");
++
++  if (wild && !wild[0])
++    wild=0;
++
++  bzero((char*) &table_list,sizeof(table_list));
++
++  if (!(dirp = my_dir(path,MYF(dir ? MY_WANT_STAT : 0))))
++  {
++    if (my_errno == ENOENT)
++      my_error(ER_BAD_DB_ERROR, MYF(ME_BELL+ME_WAITTANG), db);
++    else
++      my_error(ER_CANT_READ_DIR, MYF(ME_BELL+ME_WAITTANG), path, my_errno);
++    DBUG_RETURN(FIND_FILES_DIR);
++  }
++
++  for (i=0 ; i < (uint) dirp->number_off_files  ; i++)
++  {
++    file=dirp->dir_entry+i;
++    if (dir)
++    {                                           /* Return databases */
++#ifdef USE_SYMDIR
++      char *ext;
++      char buff[FN_REFLEN];
++      if (my_use_symdir && !strcmp(ext=fn_ext(file->name), ".sym"))
++      {
++	/* Only show the sym file if it points to a directory */
++	char *end;
++        *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, file->mystat, MYF(0)))
++               continue;
++       }
++#endif
++        if (file->name[0] == '.' || !MY_S_ISDIR(file->mystat->st_mode) ||
++            (wild && wild_compare(file->name,wild,0)))
++          continue;
++    }
++    else
++    {
++        // Return only .frm files which aren't temp files.
++      if (my_strcasecmp(system_charset_info, ext=fn_ext(file->name),reg_ext) ||
++          is_prefix(file->name,tmp_file_prefix))
++        continue;
++      *ext=0;
++      if (wild)
++      {
++	if (lower_case_table_names)
++	{
++	  if (wild_case_compare(files_charset_info, file->name, wild))
++	    continue;
++	}
++	else if (wild_compare(file->name,wild,0))
++	  continue;
++      }
++    }
++#ifndef NO_EMBEDDED_ACCESS_CHECKS
++    /* Don't show tables where we don't have any privileges */
++    if (db && !(col_access & TABLE_ACLS))
++    {
++      table_list.db= (char*) db;
++      table_list.db_length= strlen(db);
++      table_list.table_name= file->name;
++      table_list.table_name_length= strlen(file->name);
++      table_list.grant.privilege=col_access;
++      if (check_grant(thd, TABLE_ACLS, &table_list, 1, 1, 1))
++        continue;
++    }
++#endif
++    if (files->push_back(thd->strdup(file->name)))
++    {
++      my_dirend(dirp);
++      DBUG_RETURN(FIND_FILES_OOM);
++    }
++  }
++  DBUG_PRINT("info",("found: %d files", files->elements));
++  my_dirend(dirp);
++
++  VOID(ha_find_files(thd,db,path,wild,dir,files));
++
++  DBUG_RETURN(FIND_FILES_OK);
++}
++
++
++bool
++mysqld_show_create(THD *thd, TABLE_LIST *table_list)
++{
++  Protocol *protocol= thd->protocol;
++  char buff[2048];
++  String buffer(buff, sizeof(buff), system_charset_info);
++  DBUG_ENTER("mysqld_show_create");
++  DBUG_PRINT("enter",("db: %s  table: %s",table_list->db,
++                      table_list->table_name));
++
++  /* We want to preserve the tree for views. */
++  thd->lex->view_prepare_mode= TRUE;
++
++  /* Only one table for now, but VIEW can involve several tables */
++  if (open_normal_and_derived_tables(thd, table_list, 0))
++  {
++    if (!table_list->view || thd->net.last_errno != ER_VIEW_INVALID)
++      DBUG_RETURN(TRUE);
++
++    /*
++      Clear all messages with 'error' level status and
++      issue a warning with 'warning' level status in 
++      case of invalid view and last error is ER_VIEW_INVALID
++    */
++    mysql_reset_errors(thd, true);
++    thd->clear_error();
++
++    push_warning_printf(thd,MYSQL_ERROR::WARN_LEVEL_WARN,
++                        ER_VIEW_INVALID,
++                        ER(ER_VIEW_INVALID),
++                        table_list->view_db.str,
++                        table_list->view_name.str);
++  }
++
++  /* TODO: add environment variables show when it become possible */
++  if (thd->lex->only_view && !table_list->view)
++  {
++    my_error(ER_WRONG_OBJECT, MYF(0),
++             table_list->db, table_list->table_name, "VIEW");
++    DBUG_RETURN(TRUE);
++  }
++
++  buffer.length(0);
++  if ((table_list->view ?
++       view_store_create_info(thd, table_list, &buffer) :
++       store_create_info(thd, table_list, &buffer)))
++    DBUG_RETURN(TRUE);
++
++  List<Item> field_list;
++  if (table_list->view)
++  {
++    field_list.push_back(new Item_empty_string("View",NAME_LEN));
++    field_list.push_back(new Item_empty_string("Create View",
++                                               max(buffer.length(),1024)));
++  }
++  else
++  {
++    field_list.push_back(new Item_empty_string("Table",NAME_LEN));
++    // 1024 is for not to confuse old clients
++    field_list.push_back(new Item_empty_string("Create Table",
++                                               max(buffer.length(),1024)));
++  }
++
++  if (protocol->send_fields(&field_list,
++                            Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
++    DBUG_RETURN(TRUE);
++  protocol->prepare_for_resend();
++  if (table_list->view)
++    protocol->store(table_list->view_name.str, system_charset_info);
++  else
++  {
++    if (table_list->schema_table)
++      protocol->store(table_list->schema_table->table_name,
++                      system_charset_info);
++    else
++      protocol->store(table_list->table->alias, system_charset_info);
++  }
++  protocol->store(buffer.ptr(), buffer.length(), buffer.charset());
++
++  if (protocol->write())
++    DBUG_RETURN(TRUE);
++  send_eof(thd);
++  DBUG_RETURN(FALSE);
++}
++
++bool mysqld_show_create_db(THD *thd, char *dbname,
++                           HA_CREATE_INFO *create_info)
++{
++  Security_context *sctx= thd->security_ctx;
++  char buff[2048];
++  String buffer(buff, sizeof(buff), system_charset_info);
++#ifndef NO_EMBEDDED_ACCESS_CHECKS
++  uint db_access;
++#endif
++  HA_CREATE_INFO create;
++  uint create_options = create_info ? create_info->options : 0;
++  Protocol *protocol=thd->protocol;
++  DBUG_ENTER("mysql_show_create_db");
++
++  if (check_db_name(dbname))
++  {
++    my_error(ER_WRONG_DB_NAME, MYF(0), dbname);
++    DBUG_RETURN(TRUE);
++  }
++
++#ifndef NO_EMBEDDED_ACCESS_CHECKS
++  if (test_all_bits(sctx->master_access, DB_ACLS))
++    db_access=DB_ACLS;
++  else
++    db_access= (acl_get(sctx->host, sctx->ip, sctx->priv_user, dbname, 0) |
++		sctx->master_access);
++  if (!(db_access & DB_ACLS) && (!grant_option || check_grant_db(thd,dbname)))
++  {
++    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);
++    DBUG_RETURN(TRUE);
++  }
++#endif
++  if (!my_strcasecmp(system_charset_info, dbname,
++                     information_schema_name.str))
++  {
++    dbname= information_schema_name.str;
++    create.default_table_charset= system_charset_info;
++  }
++  else
++  {
++    if (check_db_dir_existence(dbname))
++    {
++      my_error(ER_BAD_DB_ERROR, MYF(0), dbname);
++      DBUG_RETURN(TRUE);
++    }
++
++    load_db_opt_by_name(thd, dbname, &create);
++  }
++  List<Item> field_list;
++  field_list.push_back(new Item_empty_string("Database",NAME_LEN));
++  field_list.push_back(new Item_empty_string("Create Database",1024));
++
++  if (protocol->send_fields(&field_list,
++                            Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
++    DBUG_RETURN(TRUE);
++
++  protocol->prepare_for_resend();
++  protocol->store(dbname, strlen(dbname), system_charset_info);
++  buffer.length(0);
++  buffer.append(STRING_WITH_LEN("CREATE DATABASE "));
++  if (create_options & HA_LEX_CREATE_IF_NOT_EXISTS)
++    buffer.append(STRING_WITH_LEN("/*!32312 IF NOT EXISTS*/ "));
++  append_identifier(thd, &buffer, dbname, strlen(dbname));
++
++  if (create.default_table_charset)
++  {
++    buffer.append(STRING_WITH_LEN(" /*!40100"));
++    buffer.append(STRING_WITH_LEN(" DEFAULT CHARACTER SET "));
++    buffer.append(create.default_table_charset->csname);
++    if (!(create.default_table_charset->state & MY_CS_PRIMARY))
++    {
++      buffer.append(STRING_WITH_LEN(" COLLATE "));
++      buffer.append(create.default_table_charset->name);
++    }
++    buffer.append(STRING_WITH_LEN(" */"));
++  }
++  protocol->store(buffer.ptr(), buffer.length(), buffer.charset());
++
++  if (protocol->write())
++    DBUG_RETURN(TRUE);
++  send_eof(thd);
++  DBUG_RETURN(FALSE);
++}
++
++bool
++mysqld_show_logs(THD *thd)
++{
++  List<Item> field_list;
++  Protocol *protocol= thd->protocol;
++  DBUG_ENTER("mysqld_show_logs");
++
++  field_list.push_back(new Item_empty_string("File",FN_REFLEN));
++  field_list.push_back(new Item_empty_string("Type",10));
++  field_list.push_back(new Item_empty_string("Status",10));
++
++  if (protocol->send_fields(&field_list,
++                            Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
++    DBUG_RETURN(TRUE);
++
++#ifdef HAVE_BERKELEY_DB
++  if ((have_berkeley_db == SHOW_OPTION_YES) && berkeley_show_logs(protocol))
++    DBUG_RETURN(TRUE);
++#endif
++
++  send_eof(thd);
++  DBUG_RETURN(FALSE);
++}
++
++
++/****************************************************************************
++  Return only fields for API mysql_list_fields
++  Use "show table wildcard" in mysql instead of this
++****************************************************************************/
++
++void
++mysqld_list_fields(THD *thd, TABLE_LIST *table_list, const char *wild)
++{
++  TABLE *table;
++  DBUG_ENTER("mysqld_list_fields");
++  DBUG_PRINT("enter",("table: %s",table_list->table_name));
++
++  if (open_normal_and_derived_tables(thd, table_list, 0))
++    DBUG_VOID_RETURN;
++  table= table_list->table;
++
++  List<Item> field_list;
++
++  Field **ptr,*field;
++  for (ptr=table->field ; (field= *ptr); ptr++)
++  {
++    if (!wild || !wild[0] || 
++        !wild_case_compare(system_charset_info, field->field_name,wild))
++    {
++      if (table_list->view)
++        field_list.push_back(new Item_ident_for_show(field,
++                                                     table_list->view_db.str,
++                                                     table_list->view_name.str));
++      else
++        field_list.push_back(new Item_field(field));
++    }
++  }
++  restore_record(table, s->default_values);              // Get empty record
++  if (thd->protocol->send_fields(&field_list, Protocol::SEND_DEFAULTS |
++                                              Protocol::SEND_EOF))
++    DBUG_VOID_RETURN;
++  thd->protocol->flush();
++  DBUG_VOID_RETURN;
++}
++
++
++int
++mysqld_dump_create_info(THD *thd, TABLE_LIST *table_list, int fd)
++{
++  Protocol *protocol= thd->protocol;
++  String *packet= protocol->storage_packet();
++  DBUG_ENTER("mysqld_dump_create_info");
++  DBUG_PRINT("enter",("table: %s",table_list->table->s->table_name));
++
++  protocol->prepare_for_resend();
++  if (store_create_info(thd, table_list, packet))
++    DBUG_RETURN(-1);
++
++  if (fd < 0)
++  {
++    if (protocol->write())
++      DBUG_RETURN(-1);
++    protocol->flush();
++  }
++  else
++  {
++    if (my_write(fd, (const byte*) packet->ptr(), packet->length(),
++		 MYF(MY_WME)))
++      DBUG_RETURN(-1);
++  }
++  DBUG_RETURN(0);
++}
++
++/*
++  Go through all character combinations and ensure that sql_lex.cc can
++  parse it as an identifier.
++
++  SYNOPSIS
++  require_quotes()
++  name			attribute name
++  name_length		length of name
++
++  RETURN
++    #	Pointer to conflicting character
++    0	No conflicting character
++*/
++
++static const char *require_quotes(const char *name, uint name_length)
++{
++  uint length;
++  const char *end= name + name_length;
++
++  for (; name < end ; name++)
++  {
++    uchar chr= (uchar) *name;
++    length= my_mbcharlen(system_charset_info, chr);
++    if (length == 1 && !system_charset_info->ident_map[chr])
++      return name;
++  }
++  return 0;
++}
++
++
++/*
++  Quote the given identifier if needed and append it to the target string.
++  If the given identifier is empty, it will be quoted.
++
++  SYNOPSIS
++  append_identifier()
++  thd                   thread handler
++  packet                target string
++  name                  the identifier to be appended
++  name_length           length of the appending identifier
++*/
++
++void
++append_identifier(THD *thd, String *packet, const char *name, uint length)
++{
++  const char *name_end;
++  char quote_char;
++  int q= get_quote_char_for_identifier(thd, name, length);
++
++  if (q == EOF)
++  {
++    packet->append(name, length, system_charset_info);
++    return;
++  }
++
++  /*
++    The identifier must be quoted as it includes a quote character or
++   it's a keyword
++  */
++
++  VOID(packet->reserve(length*2 + 2));
++  quote_char= (char) q;
++  packet->append(&quote_char, 1, system_charset_info);
++
++  for (name_end= name+length ; name < name_end ; name+= length)
++  {
++    uchar chr= (uchar) *name;
++    length= my_mbcharlen(system_charset_info, chr);
++    /*
++      my_mbcharlen can retur 0 on a wrong multibyte
++      sequence. It is possible when upgrading from 4.0,
++      and identifier contains some accented characters.
++      The manual says it does not work. So we'll just
++      change length to 1 not to hang in the endless loop.
++    */
++    if (!length)
++      length= 1;
++    if (length == 1 && chr == (uchar) quote_char)
++      packet->append(&quote_char, 1, system_charset_info);
++    packet->append(name, length, packet->charset());
++  }
++  packet->append(&quote_char, 1, system_charset_info);
++}
++
++
++/*
++  Get the quote character for displaying an identifier.
++
++  SYNOPSIS
++    get_quote_char_for_identifier()
++    thd		Thread handler
++    name	name to quote
++    length	length of name
++
++  IMPLEMENTATION
++    Force quoting in the following cases:
++      - name is empty (for one, it is possible when we use this function for
++        quoting user and host names for DEFINER clause);
++      - name is a keyword;
++      - name includes a special character;
++    Otherwise identifier is quoted only if the option OPTION_QUOTE_SHOW_CREATE
++    is set.
++
++  RETURN
++    EOF	  No quote character is needed
++    #	  Quote character
++*/
++
++int get_quote_char_for_identifier(THD *thd, const char *name, uint length)
++{
++  if (length &&
++      !is_keyword(name,length) &&
++      !require_quotes(name, length) &&
++      !(thd->options & OPTION_QUOTE_SHOW_CREATE))
++    return EOF;
++  if (thd->variables.sql_mode & MODE_ANSI_QUOTES)
++    return '"';
++  return '`';
++}
++
++
++/* Append directory name (if exists) to CREATE INFO */
++
++static void append_directory(THD *thd, String *packet, const char *dir_type,
++			     const char *filename)
++{
++  if (filename && !(thd->variables.sql_mode & MODE_NO_DIR_IN_CREATE))
++  {
++    uint length= dirname_length(filename);
++    packet->append(' ');
++    packet->append(dir_type);
++    packet->append(STRING_WITH_LEN(" DIRECTORY='"));
++#ifdef __WIN__
++    /* Convert \ to / to be able to create table on unix */
++    char *winfilename= (char*) thd->memdup(filename, length);
++    char *pos, *end;
++    for (pos= winfilename, end= pos+length ; pos < end ; pos++)
++    {
++      if (*pos == '\\')
++        *pos = '/';
++    }
++    filename= winfilename;
++#endif
++    packet->append(filename, length);
++    packet->append('\'');
++  }
++}
++
++
++#define LIST_PROCESS_HOST_LEN 64
++
++static int
++store_create_info(THD *thd, TABLE_LIST *table_list, String *packet)
++{
++  List<Item> field_list;
++  char tmp[MAX_FIELD_WIDTH], *for_str, buff[128], *end;
++  const char *alias;
++  String type(tmp, sizeof(tmp), system_charset_info);
++  Field **ptr,*field;
++  uint primary_key;
++  KEY *key_info;
++  TABLE *table= table_list->table;
++  handler *file= table->file;
++  TABLE_SHARE *share= table->s;
++  HA_CREATE_INFO create_info;
++  my_bool foreign_db_mode=    (thd->variables.sql_mode & (MODE_POSTGRESQL |
++							  MODE_ORACLE |
++							  MODE_MSSQL |
++							  MODE_DB2 |
++							  MODE_MAXDB |
++							  MODE_ANSI)) != 0;
++  my_bool limited_mysql_mode= (thd->variables.sql_mode &
++			       (MODE_NO_FIELD_OPTIONS | MODE_MYSQL323 |
++				MODE_MYSQL40)) != 0;
++  DBUG_ENTER("store_create_info");
++  DBUG_PRINT("enter",("table: %s", table->s->table_name));
++
++  restore_record(table, s->default_values); // Get empty record
++
++  if (share->tmp_table)
++    packet->append(STRING_WITH_LEN("CREATE TEMPORARY TABLE "));
++  else
++    packet->append(STRING_WITH_LEN("CREATE TABLE "));
++  if (table_list->schema_table)
++    alias= table_list->schema_table->table_name;
++  else
++    alias= (lower_case_table_names == 2 ? table->alias :
++            share->table_name);
++  append_identifier(thd, packet, alias, strlen(alias));
++  packet->append(STRING_WITH_LEN(" (\n"));
++
++  for (ptr=table->field ; (field= *ptr); ptr++)
++  {
++    bool has_default;
++    bool has_now_default;
++    uint flags = field->flags;
++
++    if (ptr != table->field)
++      packet->append(STRING_WITH_LEN(",\n"));
++
++    packet->append(STRING_WITH_LEN("  "));
++    append_identifier(thd,packet,field->field_name, strlen(field->field_name));
++    packet->append(' ');
++    // check for surprises from the previous call to Field::sql_type()
++    if (type.ptr() != tmp)
++      type.set(tmp, sizeof(tmp), system_charset_info);
++    else
++      type.set_charset(system_charset_info);
++
++    field->sql_type(type);
++    packet->append(type.ptr(), type.length(), system_charset_info);
++
++    if (field->has_charset() && 
++        !(thd->variables.sql_mode & (MODE_MYSQL323 | MODE_MYSQL40)))
++    {
++      if (field->charset() != share->table_charset)
++      {
++	packet->append(STRING_WITH_LEN(" character set "));
++	packet->append(field->charset()->csname);
++      }
++      /* 
++	For string types dump collation name only if 
++	collation is not primary for the given charset
++      */
++      if (!(field->charset()->state & MY_CS_PRIMARY))
++      {
++	packet->append(STRING_WITH_LEN(" collate "));
++	packet->append(field->charset()->name);
++      }
++    }
++
++    if (flags & NOT_NULL_FLAG)
++      packet->append(STRING_WITH_LEN(" NOT NULL"));
++    else if (field->type() == FIELD_TYPE_TIMESTAMP)
++    {
++      /*
++        TIMESTAMP field require explicit NULL flag, because unlike
++        all other fields they are treated as NOT NULL by default.
++      */
++      packet->append(STRING_WITH_LEN(" NULL"));
++    }
++
++    /* 
++      Again we are using CURRENT_TIMESTAMP instead of NOW because it is
++      more standard 
++    */
++    has_now_default= table->timestamp_field == field && 
++                     field->unireg_check != Field::TIMESTAMP_UN_FIELD;
++    
++    has_default= (field->type() != FIELD_TYPE_BLOB &&
++                  !(field->flags & NO_DEFAULT_VALUE_FLAG) &&
++		  field->unireg_check != Field::NEXT_NUMBER &&
++                  !((thd->variables.sql_mode & (MODE_MYSQL323 | MODE_MYSQL40))
++		    && has_now_default));
++
++    if (has_default)
++    {
++      packet->append(STRING_WITH_LEN(" default "));
++      if (has_now_default)
++        packet->append(STRING_WITH_LEN("CURRENT_TIMESTAMP"));
++      else if (!field->is_null())
++      {                                             // Not null by default
++        type.set(tmp, sizeof(tmp), field->charset());
++        field->val_str(&type);
++	if (type.length())
++	{
++	  String def_val;
++          uint dummy_errors;
++	  /* convert to system_charset_info == utf8 */
++	  def_val.copy(type.ptr(), type.length(), field->charset(),
++		       system_charset_info, &dummy_errors);
++          append_unescaped(packet, def_val.ptr(), def_val.length());
++	}
++        else
++	  packet->append(STRING_WITH_LEN("''"));
++      }
++      else if (field->maybe_null())
++        packet->append(STRING_WITH_LEN("NULL"));    // Null as default
++      else
++        packet->append(tmp);
++    }
++
++    if (!limited_mysql_mode && table->timestamp_field == field && 
++        field->unireg_check != Field::TIMESTAMP_DN_FIELD)
++      packet->append(STRING_WITH_LEN(" on update CURRENT_TIMESTAMP"));
++
++    if (field->unireg_check == Field::NEXT_NUMBER && 
++        !(thd->variables.sql_mode & MODE_NO_FIELD_OPTIONS))
++      packet->append(STRING_WITH_LEN(" auto_increment"));
++
++    if (field->comment.length)
++    {
++      packet->append(STRING_WITH_LEN(" COMMENT "));
++      append_unescaped(packet, field->comment.str, field->comment.length);
++    }
++  }
++
++  key_info= table->key_info;
++  bzero((char*) &create_info, sizeof(create_info));
++  file->update_create_info(&create_info);
++  primary_key= share->primary_key;
++
++  for (uint i=0 ; i < share->keys ; i++,key_info++)
++  {
++    KEY_PART_INFO *key_part= key_info->key_part;
++    bool found_primary=0;
++    packet->append(STRING_WITH_LEN(",\n  "));
++
++    if (i == primary_key && !strcmp(key_info->name, primary_key_name))
++    {
++      found_primary=1;
++      packet->append(STRING_WITH_LEN("PRIMARY "));
++    }
++    else if (key_info->flags & HA_NOSAME)
++      packet->append(STRING_WITH_LEN("UNIQUE "));
++    else if (key_info->flags & HA_FULLTEXT)
++      packet->append(STRING_WITH_LEN("FULLTEXT "));
++    else if (key_info->flags & HA_SPATIAL)
++      packet->append(STRING_WITH_LEN("SPATIAL "));
++    packet->append(STRING_WITH_LEN("KEY "));
++
++    if (!found_primary)
++     append_identifier(thd, packet, key_info->name, strlen(key_info->name));
++
++    if (!(thd->variables.sql_mode & MODE_NO_KEY_OPTIONS) &&
++	!limited_mysql_mode && !foreign_db_mode)
++    {
++      if (key_info->algorithm == HA_KEY_ALG_BTREE)
++        packet->append(STRING_WITH_LEN(" USING BTREE"));
++
++      if (key_info->algorithm == HA_KEY_ALG_HASH)
++        packet->append(STRING_WITH_LEN(" USING HASH"));
++
++      // +BAR: send USING only in non-default case: non-spatial rtree
++      if ((key_info->algorithm == HA_KEY_ALG_RTREE) &&
++	  !(key_info->flags & HA_SPATIAL))
++        packet->append(STRING_WITH_LEN(" USING RTREE"));
++
++      // No need to send USING FULLTEXT, it is sent as FULLTEXT KEY
++    }
++    packet->append(STRING_WITH_LEN(" ("));
++
++    for (uint j=0 ; j < key_info->key_parts ; j++,key_part++)
++    {
++      if (j)
++        packet->append(',');
++
++      if (key_part->field)
++        append_identifier(thd,packet,key_part->field->field_name,
++			  strlen(key_part->field->field_name));
++      if (key_part->field &&
++          (key_part->length !=
++           table->field[key_part->fieldnr-1]->key_length() &&
++           !(key_info->flags & HA_FULLTEXT)))
++      {
++        buff[0] = '(';
++        char* end=int10_to_str((long) key_part->length /
++			       key_part->field->charset()->mbmaxlen,
++			       buff + 1,10);
++        *end++ = ')';
++        packet->append(buff,(uint) (end-buff));
++      }
++    }
++    packet->append(')');
++  }
++
++  /*
++    Get possible foreign key definitions stored in InnoDB and append them
++    to the CREATE TABLE statement
++  */
++
++  if ((for_str= file->get_foreign_key_create_info()))
++  {
++    packet->append(for_str, strlen(for_str));
++    file->free_foreign_key_create_info(for_str);
++  }
++
++  packet->append(STRING_WITH_LEN("\n)"));
++  if (!(thd->variables.sql_mode & MODE_NO_TABLE_OPTIONS) && !foreign_db_mode)
++  {
++    if (thd->variables.sql_mode & (MODE_MYSQL323 | MODE_MYSQL40))
++      packet->append(STRING_WITH_LEN(" TYPE="));
++    else
++      packet->append(STRING_WITH_LEN(" ENGINE="));
++    packet->append(file->table_type());
++
++    /*
++      Add AUTO_INCREMENT=... if there is an AUTO_INCREMENT column,
++      and NEXT_ID > 1 (the default).  We must not print the clause
++      for engines that do not support this as it would break the
++      import of dumps, but as of this writing, the test for whether
++      AUTO_INCREMENT columns are allowed and wether AUTO_INCREMENT=...
++      is supported is identical, !(file->table_flags() & HA_NO_AUTO_INCREMENT))
++      Because of that, we do not explicitly test for the feature,
++      but may extrapolate its existence from that of an AUTO_INCREMENT column.
++    */
++
++    if(create_info.auto_increment_value > 1)
++    {
++      packet->append(" AUTO_INCREMENT=", 16);
++      end= longlong10_to_str(create_info.auto_increment_value, buff,10);
++      packet->append(buff, (uint) (end - buff));
++    }
++
++    
++    if (share->table_charset &&
++	!(thd->variables.sql_mode & MODE_MYSQL323) &&
++	!(thd->variables.sql_mode & MODE_MYSQL40))
++    {
++      packet->append(STRING_WITH_LEN(" DEFAULT CHARSET="));
++      packet->append(share->table_charset->csname);
++      if (!(share->table_charset->state & MY_CS_PRIMARY))
++      {
++	packet->append(STRING_WITH_LEN(" COLLATE="));
++	packet->append(table->s->table_charset->name);
++      }
++    }
++
++    if (share->min_rows)
++    {
++      packet->append(STRING_WITH_LEN(" MIN_ROWS="));
++      end= longlong10_to_str(share->min_rows, buff, 10);
++      packet->append(buff, (uint) (end- buff));
++    }
++
++    if (share->max_rows && !table_list->schema_table)
++    {
++      packet->append(STRING_WITH_LEN(" MAX_ROWS="));
++      end= longlong10_to_str(share->max_rows, buff, 10);
++      packet->append(buff, (uint) (end - buff));
++    }
++
++    if (share->avg_row_length)
++    {
++      packet->append(STRING_WITH_LEN(" AVG_ROW_LENGTH="));
++      end= longlong10_to_str(share->avg_row_length, buff,10);
++      packet->append(buff, (uint) (end - buff));
++    }
++
++    if (share->db_create_options & HA_OPTION_PACK_KEYS)
++      packet->append(STRING_WITH_LEN(" PACK_KEYS=1"));
++    if (share->db_create_options & HA_OPTION_NO_PACK_KEYS)
++      packet->append(STRING_WITH_LEN(" PACK_KEYS=0"));
++    if (share->db_create_options & HA_OPTION_CHECKSUM)
++      packet->append(STRING_WITH_LEN(" CHECKSUM=1"));
++    if (share->db_create_options & HA_OPTION_DELAY_KEY_WRITE)
++      packet->append(STRING_WITH_LEN(" DELAY_KEY_WRITE=1"));
++    if (share->row_type != ROW_TYPE_DEFAULT)
++    {
++      packet->append(STRING_WITH_LEN(" ROW_FORMAT="));
++      packet->append(ha_row_type[(uint) share->row_type]);
++    }
++    table->file->append_create_info(packet);
++    if (share->comment.length)
++    {
++      packet->append(STRING_WITH_LEN(" COMMENT="));
++      append_unescaped(packet, share->comment.str, share->comment.length);
++    }
++    if (share->connect_string.length)
++    {
++      packet->append(STRING_WITH_LEN(" CONNECTION="));
++      append_unescaped(packet, share->connect_string.str, share->connect_string.length);
++    }
++    if (file->raid_type)
++    {
++      uint length;
++      length= my_snprintf(buff,sizeof(buff),
++			  " RAID_TYPE=%s RAID_CHUNKS=%d RAID_CHUNKSIZE=%ld",
++			  my_raid_type(file->raid_type), file->raid_chunks,
++			  file->raid_chunksize/RAID_BLOCK_SIZE);
++      packet->append(buff, length);
++    }
++    append_directory(thd, packet, "DATA",  create_info.data_file_name);
++    append_directory(thd, packet, "INDEX", create_info.index_file_name);
++  }
++  DBUG_RETURN(0);
++}
++
++void
++view_store_options(THD *thd, TABLE_LIST *table, String *buff)
++{
++  append_algorithm(table, buff);
++  append_definer(thd, buff, &table->definer.user, &table->definer.host);
++  if (table->view_suid)
++    buff->append(STRING_WITH_LEN("SQL SECURITY DEFINER "));
++  else
++    buff->append(STRING_WITH_LEN("SQL SECURITY INVOKER "));
++}
++
++
++/*
++  Append DEFINER clause to the given buffer.
++  
++  SYNOPSIS
++    append_definer()
++    thd           [in] thread handle
++    buffer        [inout] buffer to hold DEFINER clause
++    definer_user  [in] user name part of definer
++    definer_host  [in] host name part of definer
++*/
++
++static void append_algorithm(TABLE_LIST *table, String *buff)
++{
++  buff->append(STRING_WITH_LEN("ALGORITHM="));
++  switch ((int8)table->algorithm) {
++  case VIEW_ALGORITHM_UNDEFINED:
++    buff->append(STRING_WITH_LEN("UNDEFINED "));
++    break;
++  case VIEW_ALGORITHM_TMPTABLE:
++    buff->append(STRING_WITH_LEN("TEMPTABLE "));
++    break;
++  case VIEW_ALGORITHM_MERGE:
++    buff->append(STRING_WITH_LEN("MERGE "));
++    break;
++  default:
++    DBUG_ASSERT(0); // never should happen
++  }
++}
++
++
++/*
++  Append DEFINER clause to the given buffer.
++  
++  SYNOPSIS
++    append_definer()
++    thd           [in] thread handle
++    buffer        [inout] buffer to hold DEFINER clause
++    definer_user  [in] user name part of definer
++    definer_host  [in] host name part of definer
++*/
++
++void append_definer(THD *thd, String *buffer, const LEX_STRING *definer_user,
++                    const LEX_STRING *definer_host)
++{
++  buffer->append(STRING_WITH_LEN("DEFINER="));
++  append_identifier(thd, buffer, definer_user->str, definer_user->length);
++  buffer->append('@');
++  append_identifier(thd, buffer, definer_host->str, definer_host->length);
++  buffer->append(' ');
++}
++
++
++static int
++view_store_create_info(THD *thd, TABLE_LIST *table, String *buff)
++{
++  my_bool foreign_db_mode= (thd->variables.sql_mode & (MODE_POSTGRESQL |
++                                                       MODE_ORACLE |
++                                                       MODE_MSSQL |
++                                                       MODE_DB2 |
++                                                       MODE_MAXDB |
++                                                       MODE_ANSI)) != 0;
++  /*
++     Compact output format for view can be used
++     - if user has db of this view as current db
++     - if this view only references table inside it's own db
++  */
++  if (!thd->db || strcmp(thd->db, table->view_db.str))
++    table->compact_view_format= FALSE;
++  else
++  {
++    TABLE_LIST *tbl;
++    table->compact_view_format= TRUE;
++    for (tbl= thd->lex->query_tables;
++         tbl;
++         tbl= tbl->next_global)
++    {
++      if (strcmp(table->view_db.str, tbl->view ? tbl->view_db.str :tbl->db)!= 0)
++      {
++        table->compact_view_format= FALSE;
++        break;
++      }
++    }
++  }
++
++  buff->append(STRING_WITH_LEN("CREATE "));
++  if (!foreign_db_mode)
++  {
++    view_store_options(thd, table, buff);
++  }
++  buff->append(STRING_WITH_LEN("VIEW "));
++  if (!table->compact_view_format)
++  {
++    append_identifier(thd, buff, table->view_db.str, table->view_db.length);
++    buff->append('.');
++  }
++  append_identifier(thd, buff, table->view_name.str, table->view_name.length);
++  buff->append(STRING_WITH_LEN(" AS "));
++
++  /*
++    We can't just use table->query, because our SQL_MODE may trigger
++    a different syntax, like when ANSI_QUOTES is defined.
++  */
++  table->view->unit.print(buff);
++
++  if (table->with_check != VIEW_CHECK_NONE)
++  {
++    if (table->with_check == VIEW_CHECK_LOCAL)
++      buff->append(STRING_WITH_LEN(" WITH LOCAL CHECK OPTION"));
++    else
++      buff->append(STRING_WITH_LEN(" WITH CASCADED CHECK OPTION"));
++  }
++  return 0;
++}
++
++
++/****************************************************************************
++  Return info about all processes
++  returns for each thread: thread id, user, host, db, command, info
++****************************************************************************/
++
++class thread_info :public ilink {
++public:
++  static void *operator new(size_t size)
++  {
++    return (void*) sql_alloc((uint) size);
++  }
++  static void operator delete(void *ptr __attribute__((unused)),
++                              size_t size __attribute__((unused)))
++  { TRASH(ptr, size); }
++
++  ulong thread_id;
++  time_t start_time;
++  uint   command;
++  const char *user,*host,*db,*proc_info,*state_info;
++  char *query;
++};
++
++#ifdef HAVE_EXPLICIT_TEMPLATE_INSTANTIATION
++template class I_List<thread_info>;
++#endif
++
++void mysqld_list_processes(THD *thd,const char *user, bool verbose)
++{
++  Item *field;
++  List<Item> field_list;
++  I_List<thread_info> thread_infos;
++  ulong max_query_length= (verbose ? thd->variables.max_allowed_packet :
++			   PROCESS_LIST_WIDTH);
++  Protocol *protocol= thd->protocol;
++  DBUG_ENTER("mysqld_list_processes");
++
++  field_list.push_back(new Item_int("Id",0,11));
++  field_list.push_back(new Item_empty_string("User",16));
++  field_list.push_back(new Item_empty_string("Host",LIST_PROCESS_HOST_LEN));
++  field_list.push_back(field=new Item_empty_string("db",NAME_LEN));
++  field->maybe_null=1;
++  field_list.push_back(new Item_empty_string("Command",16));
++  field_list.push_back(new Item_return_int("Time",7, FIELD_TYPE_LONG));
++  field_list.push_back(field=new Item_empty_string("State",30));
++  field->maybe_null=1;
++  field_list.push_back(field=new Item_empty_string("Info",max_query_length));
++  field->maybe_null=1;
++  if (protocol->send_fields(&field_list,
++                            Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
++    DBUG_VOID_RETURN;
++
++  VOID(pthread_mutex_lock(&LOCK_thread_count)); // For unlink from list
++  if (!thd->killed)
++  {
++    I_List_iterator<THD> it(threads);
++    THD *tmp;
++    while ((tmp=it++))
++    {
++      Security_context *tmp_sctx= tmp->security_ctx;
++      struct st_my_thread_var *mysys_var;
++      if ((tmp->vio_ok() || tmp->system_thread) &&
++          (!user || (tmp_sctx->user && !strcmp(tmp_sctx->user, user))))
++      {
++        thread_info *thd_info= new thread_info;
++
++        thd_info->thread_id=tmp->thread_id;
++        thd_info->user= thd->strdup(tmp_sctx->user ? tmp_sctx->user :
++                                    (tmp->system_thread ?
++                                     "system user" : "unauthenticated user"));
++	if (tmp->peer_port && (tmp_sctx->host || tmp_sctx->ip) &&
++            thd->security_ctx->host_or_ip[0])
++	{
++	  if ((thd_info->host= thd->alloc(LIST_PROCESS_HOST_LEN+1)))
++	    my_snprintf((char *) thd_info->host, LIST_PROCESS_HOST_LEN,
++			"%s:%u", tmp_sctx->host_or_ip, tmp->peer_port);
++	}
++	else
++	  thd_info->host= thd->strdup(tmp_sctx->host_or_ip[0] ? 
++                                      tmp_sctx->host_or_ip : 
++                                      tmp_sctx->host ? tmp_sctx->host : "");
++        if ((thd_info->db=tmp->db))             // Safe test
++          thd_info->db=thd->strdup(thd_info->db);
++        thd_info->command=(int) tmp->command;
++        if ((mysys_var= tmp->mysys_var))
++          pthread_mutex_lock(&mysys_var->mutex);
++        thd_info->proc_info= (char*) (tmp->killed == THD::KILL_CONNECTION? "Killed" : 0);
++#ifndef EMBEDDED_LIBRARY
++        thd_info->state_info= (char*) (tmp->locked ? "Locked" :
++                                       tmp->net.reading_or_writing ?
++                                       (tmp->net.reading_or_writing == 2 ?
++                                        "Writing to net" :
++                                        thd_info->command == COM_SLEEP ? "" :
++                                        "Reading from net") :
++                                       tmp->proc_info ? tmp->proc_info :
++                                       tmp->mysys_var &&
++                                       tmp->mysys_var->current_cond ?
++                                       "Waiting on cond" : NullS);
++#else
++        thd_info->state_info= (char*)"Writing to net";
++#endif
++        if (mysys_var)
++          pthread_mutex_unlock(&mysys_var->mutex);
++
++#if !defined(DONT_USE_THR_ALARM) && ! defined(SCO)
++        if (pthread_kill(tmp->real_id,0))
++          tmp->proc_info="*** DEAD ***";        // This shouldn't happen
++#endif
++#ifdef EXTRA_DEBUG
++        thd_info->start_time= tmp->time_after_lock;
++#else
++        thd_info->start_time= tmp->start_time;
++#endif
++        thd_info->query=0;
++        if (tmp->query)
++        {
++	  /* 
++            query_length is always set to 0 when we set query = NULL; see
++	    the comment in sql_class.h why this prevents crashes in possible
++            races with query_length
++          */
++          uint length= min(max_query_length, tmp->query_length);
++          thd_info->query=(char*) thd->strmake(tmp->query,length);
++        }
++        thread_infos.append(thd_info);
++      }
++    }
++  }
++  VOID(pthread_mutex_unlock(&LOCK_thread_count));
++
++  thread_info *thd_info;
++  time_t now= time(0);
++  while ((thd_info=thread_infos.get()))
++  {
++    protocol->prepare_for_resend();
++    protocol->store((ulonglong) thd_info->thread_id);
++    protocol->store(thd_info->user, system_charset_info);
++    protocol->store(thd_info->host, system_charset_info);
++    protocol->store(thd_info->db, system_charset_info);
++    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);
++    if (thd_info->start_time)
++      protocol->store((uint32) (now - thd_info->start_time));
++    else
++      protocol->store_null();
++    protocol->store(thd_info->state_info, system_charset_info);
++    protocol->store(thd_info->query, system_charset_info);
++    if (protocol->write())
++      break; /* purecov: inspected */
++  }
++  send_eof(thd);
++  DBUG_VOID_RETURN;
++}
++
++/*****************************************************************************
++  Status functions
++*****************************************************************************/
++
++
++static bool show_status_array(THD *thd, const char *wild,
++                              show_var_st *variables,
++                              enum enum_var_type value_type,
++                              struct system_status_var *status_var,
++                              const char *prefix, TABLE *table)
++{
++  char buff[1024], *prefix_end;
++  /* the variable name should not be longer then 80 characters */
++  char name_buffer[80];
++  int len;
++  LEX_STRING null_lex_str;
++  DBUG_ENTER("show_status_array");
++
++  null_lex_str.str= 0;				// For sys_var->value_ptr()
++  null_lex_str.length= 0;
++
++  prefix_end=strnmov(name_buffer, prefix, sizeof(name_buffer)-1);
++  len=name_buffer + sizeof(name_buffer) - prefix_end;
++
++  for (; variables->name; variables++)
++  {
++    strnmov(prefix_end, variables->name, len);
++    name_buffer[sizeof(name_buffer)-1]=0;       /* Safety */
++    SHOW_TYPE show_type=variables->type;
++    if (show_type == SHOW_VARS)
++    {
++      show_status_array(thd, wild, (show_var_st *) variables->value,
++                        value_type, status_var, variables->name, table);
++    }
++    else
++    {
++      if (!(wild && wild[0] && wild_case_compare(system_charset_info,
++                                                 name_buffer, wild)))
++      {
++        char *value=variables->value;
++        const char *pos, *end;                  // We assign a lot of const's
++        long nr;
++        if (show_type == SHOW_SYS)
++        {
++          show_type= ((sys_var*) value)->type();
++          value=     (char*) ((sys_var*) value)->value_ptr(thd, value_type,
++                                                           &null_lex_str);
++        }
++
++        pos= end= buff;
++        switch (show_type) {
++        case SHOW_LONG_STATUS:
++        case SHOW_LONG_CONST_STATUS:
++          value= ((char *) status_var + (ulong) value);
++          /* fall through */
++        case SHOW_LONG:
++        case SHOW_LONG_CONST:
++          end= int10_to_str(*(long*) value, buff, 10);
++          break;
++        case SHOW_LONGLONG:
++          end= longlong10_to_str(*(longlong*) value, buff, 10);
++          break;
++        case SHOW_HA_ROWS:
++          end= longlong10_to_str((longlong) *(ha_rows*) value, buff, 10);
++          break;
++        case SHOW_BOOL:
++          end= strmov(buff, *(bool*) value ? "ON" : "OFF");
++          break;
++        case SHOW_MY_BOOL:
++          end= strmov(buff, *(my_bool*) value ? "ON" : "OFF");
++          break;
++        case SHOW_INT_CONST:
++        case SHOW_INT:
++          end= int10_to_str((long) *(uint32*) value, buff, 10);
++          break;
++        case SHOW_HAVE:
++        {
++          SHOW_COMP_OPTION tmp= *(SHOW_COMP_OPTION*) value;
++          pos= show_comp_option_name[(int) tmp];
++          end= strend(pos);
++          break;
++        }
++        case SHOW_CHAR:
++        {
++          if (!(pos= value))
++            pos= "";
++          end= strend(pos);
++          break;
++        }
++        case SHOW_STARTTIME:
++          nr= (long) (thd->query_start() - start_time);
++          end= int10_to_str(nr, buff, 10);
++          break;
++        case SHOW_QUESTION:
++          end= int10_to_str((long) thd->query_id, buff, 10);
++          break;
++#ifdef HAVE_REPLICATION
++        case SHOW_RPL_STATUS:
++          end= strmov(buff, rpl_status_type[(int)rpl_status]);
++          break;
++        case SHOW_SLAVE_RUNNING:
++        {
++          pthread_mutex_lock(&LOCK_active_mi);
++          end= strmov(buff, (active_mi && active_mi->slave_running &&
++                             active_mi->rli.slave_running) ? "ON" : "OFF");
++          pthread_mutex_unlock(&LOCK_active_mi);
++          break;
++        }
++        case SHOW_SLAVE_RETRIED_TRANS:
++        {
++          /*
++            TODO: in 5.1 with multimaster, have one such counter per line in
++            SHOW SLAVE STATUS, and have the sum over all lines here.
++          */
++          pthread_mutex_lock(&LOCK_active_mi);
++          if (active_mi)
++          {
++            pthread_mutex_lock(&active_mi->rli.data_lock);
++            end= int10_to_str(active_mi->rli.retried_trans, buff, 10);
++            pthread_mutex_unlock(&active_mi->rli.data_lock);
++          }
++          pthread_mutex_unlock(&LOCK_active_mi);
++          break;
++        }
++        case SHOW_SLAVE_SKIP_ERRORS:
++        {
++          MY_BITMAP *bitmap= (MY_BITMAP *)value;
++          if (!use_slave_mask || bitmap_is_clear_all(bitmap))
++          {
++            end= strmov(buff, "OFF");
++          }
++          else if (bitmap_is_set_all(bitmap))
++          {
++            end= strmov(buff, "ALL");
++          }
++          else
++          {
++            /* 10 is enough assuming errors are max 4 digits */
++            int i;
++            for (i= 1;
++                 i < MAX_SLAVE_ERROR && (uint) (end-buff) < sizeof(buff)-10;
++                 i++)
++            {
++              if (bitmap_is_set(bitmap, i))
++              {
++                end= int10_to_str(i, (char*) end, 10);
++                *(char*) end++= ',';
++              }
++            }
++            if (end != buff)
++              end--;				// Remove last ','
++            if (i < MAX_SLAVE_ERROR)
++              end= strmov((char*) end, "...");  // Couldn't show all errors
++          }
++          break;
++        }
++#endif /* HAVE_REPLICATION */
++        case SHOW_OPENTABLES:
++          end= int10_to_str((long) cached_tables(), buff, 10);
++          break;
++        case SHOW_CHAR_PTR:
++        {
++          if (!(pos= *(char**) value))
++            pos= "";
++          end= strend(pos);
++          break;
++        }
++        case SHOW_DOUBLE_STATUS:
++        {
++          value= ((char *) status_var + (ulong) value);
++          end= buff + sprintf(buff, "%f", *(double*) value);
++          break;
++        }
++#ifdef HAVE_OPENSSL
++          /* First group - functions relying on CTX */
++        case SHOW_SSL_CTX_SESS_ACCEPT:
++          end= int10_to_str((long) (!ssl_acceptor_fd ? 0 :
++                                    SSL_CTX_sess_accept(ssl_acceptor_fd->
++                                                        ssl_context)),
++                            buff, 10);
++          break;
++        case SHOW_SSL_CTX_SESS_ACCEPT_GOOD:
++          end= int10_to_str((long) (!ssl_acceptor_fd ? 0 :
++                                    SSL_CTX_sess_accept_good(ssl_acceptor_fd->
++                                                             ssl_context)),
++                            buff, 10);
++          break;
++        case SHOW_SSL_CTX_SESS_CONNECT_GOOD:
++          end= int10_to_str((long) (!ssl_acceptor_fd ? 0 :
++                                    SSL_CTX_sess_connect_good(ssl_acceptor_fd->
++                                                              ssl_context)),
++                            buff, 10);
++          break;
++        case SHOW_SSL_CTX_SESS_ACCEPT_RENEGOTIATE:
++          end= int10_to_str((long) (!ssl_acceptor_fd ? 0 :
++                                    SSL_CTX_sess_accept_renegotiate(ssl_acceptor_fd->ssl_context)),
++                            buff, 10);
++          break;
++        case SHOW_SSL_CTX_SESS_CONNECT_RENEGOTIATE:
++          end= int10_to_str((long) (!ssl_acceptor_fd ? 0 :
++                                    SSL_CTX_sess_connect_renegotiate(ssl_acceptor_fd-> ssl_context)),
++                            buff, 10);
++          break;
++        case SHOW_SSL_CTX_SESS_CB_HITS:
++          end= int10_to_str((long) (!ssl_acceptor_fd ? 0 :
++                                    SSL_CTX_sess_cb_hits(ssl_acceptor_fd->
++                                                         ssl_context)),
++                            buff, 10);
++          break;
++        case SHOW_SSL_CTX_SESS_HITS:
++          end= int10_to_str((long) (!ssl_acceptor_fd ? 0 :
++                                    SSL_CTX_sess_hits(ssl_acceptor_fd->
++                                                      ssl_context)),
++                            buff, 10);
++          break;
++        case SHOW_SSL_CTX_SESS_CACHE_FULL:
++          end= int10_to_str((long) (!ssl_acceptor_fd ? 0 :
++                                    SSL_CTX_sess_cache_full(ssl_acceptor_fd->
++                                                            ssl_context)),
++                            buff, 10);
++          break;
++        case SHOW_SSL_CTX_SESS_MISSES:
++          end= int10_to_str((long) (!ssl_acceptor_fd ? 0 :
++                                    SSL_CTX_sess_misses(ssl_acceptor_fd->
++                                                        ssl_context)),
++                            buff, 10);
++          break;
++        case SHOW_SSL_CTX_SESS_TIMEOUTS:
++          end= int10_to_str((long) (!ssl_acceptor_fd ? 0 :
++                                    SSL_CTX_sess_timeouts(ssl_acceptor_fd->ssl_context)),
++                            buff,10);
++          break;
++        case SHOW_SSL_CTX_SESS_NUMBER:
++          end= int10_to_str((long) (!ssl_acceptor_fd ? 0 :
++                                    SSL_CTX_sess_number(ssl_acceptor_fd->ssl_context)),
++                            buff,10);
++          break;
++        case SHOW_SSL_CTX_SESS_CONNECT:
++          end= int10_to_str((long) (!ssl_acceptor_fd ? 0 :
++                                    SSL_CTX_sess_connect(ssl_acceptor_fd->ssl_context)),
++                            buff,10);
++          break;
++        case SHOW_SSL_CTX_SESS_GET_CACHE_SIZE:
++          end= int10_to_str((long) (!ssl_acceptor_fd ? 0 :
++                                    SSL_CTX_sess_get_cache_size(ssl_acceptor_fd->ssl_context)),
++                            buff,10);
++          break;
++        case SHOW_SSL_CTX_GET_VERIFY_MODE:
++          end= int10_to_str((long) (!ssl_acceptor_fd ? 0 :
++                                    SSL_CTX_get_verify_mode(ssl_acceptor_fd->ssl_context)),
++                            buff,10);
++          break;
++        case SHOW_SSL_CTX_GET_VERIFY_DEPTH:
++          end= int10_to_str((long) (!ssl_acceptor_fd ? 0 :
++                                    SSL_CTX_get_verify_depth(ssl_acceptor_fd->ssl_context)),
++                            buff,10);
++          break;
++        case SHOW_SSL_CTX_GET_SESSION_CACHE_MODE:
++          if (!ssl_acceptor_fd)
++          {
++            pos= "NONE";
++            end= pos+4;
++            break;
++          }
++          switch (SSL_CTX_get_session_cache_mode(ssl_acceptor_fd->ssl_context))
++          {
++          case SSL_SESS_CACHE_OFF:
++            pos= "OFF";
++            break;
++          case SSL_SESS_CACHE_CLIENT:
++            pos= "CLIENT";
++            break;
++          case SSL_SESS_CACHE_SERVER:
++            pos= "SERVER";
++            break;
++          case SSL_SESS_CACHE_BOTH:
++            pos= "BOTH";
++            break;
++          case SSL_SESS_CACHE_NO_AUTO_CLEAR:
++            pos= "NO_AUTO_CLEAR";
++            break;
++          case SSL_SESS_CACHE_NO_INTERNAL_LOOKUP:
++            pos= "NO_INTERNAL_LOOKUP";
++            break;
++          default:
++            pos= "Unknown";
++            break;
++          }
++          end= strend(pos);
++          break;
++          /* First group - functions relying on SSL */
++        case SHOW_SSL_GET_VERSION:
++          pos= (thd->net.vio->ssl_arg ?
++                SSL_get_version((SSL*) thd->net.vio->ssl_arg) : "");
++          end= strend(pos);
++          break;
++        case SHOW_SSL_SESSION_REUSED:
++          end= int10_to_str((long) (thd->net.vio->ssl_arg ?
++                                    SSL_session_reused((SSL*) thd->net.vio->
++                                                       ssl_arg) :
++                                    0),
++                            buff, 10);
++          break;
++        case SHOW_SSL_GET_DEFAULT_TIMEOUT:
++          end= int10_to_str((long) (thd->net.vio->ssl_arg ?
++                                    SSL_get_default_timeout((SSL*) thd->net.vio->
++                                                            ssl_arg) :
++                                    0),
++                            buff, 10);
++          break;
++        case SHOW_SSL_GET_VERIFY_MODE:
++          end= int10_to_str((long) (thd->net.vio->ssl_arg ?
++                                    SSL_get_verify_mode((SSL*) thd->net.vio->
++                                                        ssl_arg):
++                                    0),
++                            buff, 10);
++          break;
++        case SHOW_SSL_GET_VERIFY_DEPTH:
++          end= int10_to_str((long) (thd->net.vio->ssl_arg ?
++                                    SSL_get_verify_depth((SSL*) thd->net.vio->
++                                                         ssl_arg):
++                                    0),
++                            buff, 10);
++          break;
++        case SHOW_SSL_GET_CIPHER:
++          pos= (thd->net.vio->ssl_arg ?
++                SSL_get_cipher((SSL*) thd->net.vio->ssl_arg) : "" );
++          end= strend(pos);
++          break;
++        case SHOW_SSL_GET_CIPHER_LIST:
++          if (thd->net.vio->ssl_arg)
++          {
++            char *to= buff;
++            for (int i=0 ; i++ ;)
++            {
++              const char *p= SSL_get_cipher_list((SSL*) thd->net.vio->ssl_arg,i);
++              if (p == NULL)
++                break;
++              to= strmov(to, p);
++              *to++= ':';
++            }
++            if (to != buff)
++              to--;				// Remove last ':'
++            end= to;
++          }
++          break;
++
++#endif /* HAVE_OPENSSL */
++        case SHOW_KEY_CACHE_LONG:
++        case SHOW_KEY_CACHE_CONST_LONG:
++          value= (value-(char*) &dflt_key_cache_var)+ (char*) dflt_key_cache;
++          end= int10_to_str(*(long*) value, buff, 10);
++          break;
++        case SHOW_KEY_CACHE_LONGLONG:
++	  value= (value-(char*) &dflt_key_cache_var)+ (char*) dflt_key_cache;
++	  end= longlong10_to_str(*(longlong*) value, buff, 10);
++	  break;
++        case SHOW_NET_COMPRESSION:
++          end= strmov(buff, thd->net.compress ? "ON" : "OFF");
++          break;
++        case SHOW_UNDEF:				// Show never happen
++        case SHOW_SYS:
++          break;					// Return empty string
++        default:
++          break;
++        }
++        restore_record(table, s->default_values);
++        table->field[0]->store(name_buffer, strlen(name_buffer),
++                               system_charset_info);
++        table->field[1]->store(pos, (uint32) (end - pos), system_charset_info);
++        if (schema_table_store_record(thd, table))
++          DBUG_RETURN(TRUE);
++      }
++    }
++  }
++
++  DBUG_RETURN(FALSE);
++}
++
++
++/* collect status for all running threads */
++
++void calc_sum_of_all_status(STATUS_VAR *to)
++{
++  DBUG_ENTER("calc_sum_of_all_status");
++
++  /* Ensure that thread id not killed during loop */
++  VOID(pthread_mutex_lock(&LOCK_thread_count)); // For unlink from list
++
++  I_List_iterator<THD> it(threads);
++  THD *tmp;
++  
++  /* Get global values as base */
++  *to= global_status_var;
++  
++  /* Add to this status from existing threads */
++  while ((tmp= it++))
++    add_to_status(to, &tmp->status_var);
++  
++  VOID(pthread_mutex_unlock(&LOCK_thread_count));
++  DBUG_VOID_RETURN;
++}
++
++
++LEX_STRING *make_lex_string(THD *thd, LEX_STRING *lex_str,
++                            const char* str, uint length,
++                            bool allocate_lex_string)
++{
++  MEM_ROOT *mem= thd->mem_root;
++  if (allocate_lex_string)
++    if (!(lex_str= (LEX_STRING *)thd->alloc(sizeof(LEX_STRING))))
++      return 0;
++  lex_str->str= strmake_root(mem, str, length);
++  lex_str->length= length;
++  return lex_str;
++}
++
++
++/* INFORMATION_SCHEMA name */
++LEX_STRING information_schema_name= {(char*)"information_schema", 18};
++
++/* This is only used internally, but we need it here as a forward reference */
++extern ST_SCHEMA_TABLE schema_tables[];
++
++typedef struct st_index_field_values
++{
++  const char *db_value, *table_value;
++} INDEX_FIELD_VALUES;
++
++
++/*
++  Store record to I_S table, convert HEAP table
++  to MyISAM if necessary
++
++  SYNOPSIS
++    schema_table_store_record()
++    thd                   thread handler
++    table                 Information schema table to be updated
++
++  RETURN
++    0	                  success
++    1	                  error
++*/
++
++static bool schema_table_store_record(THD *thd, TABLE *table)
++{
++  int error;
++  if ((error= table->file->write_row(table->record[0])))
++  {
++    if (create_myisam_from_heap(thd, table, 
++                                table->pos_in_table_list->schema_table_param,
++                                error, 0))
++      return 1;
++  }
++  return 0;
++}
++
++
++void get_index_field_values(LEX *lex, INDEX_FIELD_VALUES *index_field_values)
++{
++  const char *wild= lex->wild ? lex->wild->ptr() : NullS;
++  switch (lex->orig_sql_command) {
++  case SQLCOM_SHOW_DATABASES:
++    index_field_values->db_value= wild;
++    break;
++  case SQLCOM_SHOW_TABLES:
++  case SQLCOM_SHOW_TABLE_STATUS:
++  case SQLCOM_SHOW_TRIGGERS:
++    index_field_values->db_value= lex->select_lex.db;
++    index_field_values->table_value= wild;
++    break;
++  default:
++    index_field_values->db_value= NullS;
++    index_field_values->table_value= NullS;
++    break;
++  }
++}
++
++
++int make_table_list(THD *thd, SELECT_LEX *sel,
++                    char *db, char *table)
++{
++  Table_ident *table_ident;
++  LEX_STRING ident_db, ident_table;
++  ident_db.str= db; 
++  ident_db.length= strlen(db);
++  ident_table.str= table;
++  ident_table.length= strlen(table);
++  table_ident= new Table_ident(thd, ident_db, ident_table, 1);
++  sel->init_query();
++  if (!sel->add_table_to_list(thd, table_ident, 0, 0, TL_READ,
++                             (List<String> *) 0, (List<String> *) 0))
++    return 1;
++  return 0;
++}
++
++
++bool uses_only_table_name_fields(Item *item, TABLE_LIST *table)
++{
++  if (item->type() == Item::FUNC_ITEM)
++  {
++    Item_func *item_func= (Item_func*)item;
++    Item **child;
++    Item **item_end= (item_func->arguments()) + item_func->argument_count();
++    for (child= item_func->arguments(); child != item_end; child++)
++    {
++      if (!uses_only_table_name_fields(*child, table))
++        return 0;
++    }
++  }
++  else if (item->type() == Item::FIELD_ITEM)
++  {
++    Item_field *item_field= (Item_field*)item;
++    CHARSET_INFO *cs= system_charset_info;
++    ST_SCHEMA_TABLE *schema_table= table->schema_table;
++    ST_FIELD_INFO *field_info= schema_table->fields_info;
++    const char *field_name1= schema_table->idx_field1 >= 0 ? field_info[schema_table->idx_field1].field_name : "";
++    const char *field_name2= schema_table->idx_field2 >= 0 ? field_info[schema_table->idx_field2].field_name : "";
++    if (table->table != item_field->field->table ||
++        (cs->coll->strnncollsp(cs, (uchar *) field_name1, strlen(field_name1),
++                               (uchar *) item_field->field_name, 
++                               strlen(item_field->field_name), 0) &&
++         cs->coll->strnncollsp(cs, (uchar *) field_name2, strlen(field_name2),
++                               (uchar *) item_field->field_name, 
++                               strlen(item_field->field_name), 0)))
++      return 0;
++  }
++  else if (item->type() == Item::REF_ITEM)
++    return uses_only_table_name_fields(item->real_item(), table);
++  if (item->type() == Item::SUBSELECT_ITEM &&
++      !item->const_item())
++    return 0;
++
++  return 1;
++}
++
++
++static COND * make_cond_for_info_schema(COND *cond, TABLE_LIST *table)
++{
++  if (!cond)
++    return (COND*) 0;
++  if (cond->type() == Item::COND_ITEM)
++  {
++    if (((Item_cond*) cond)->functype() == Item_func::COND_AND_FUNC)
++    {
++      /* Create new top level AND item */
++      Item_cond_and *new_cond=new Item_cond_and;
++      if (!new_cond)
++	return (COND*) 0;
++      List_iterator<Item> li(*((Item_cond*) cond)->argument_list());
++      Item *item;
++      while ((item=li++))
++      {
++	Item *fix= make_cond_for_info_schema(item, table);
++	if (fix)
++	  new_cond->argument_list()->push_back(fix);
++      }
++      switch (new_cond->argument_list()->elements) {
++      case 0:
++	return (COND*) 0;
++      case 1:
++	return new_cond->argument_list()->head();
++      default:
++	new_cond->quick_fix_field();
++	return new_cond;
++      }
++    }
++    else
++    {						// Or list
++      Item_cond_or *new_cond=new Item_cond_or;
++      if (!new_cond)
++	return (COND*) 0;
++      List_iterator<Item> li(*((Item_cond*) cond)->argument_list());
++      Item *item;
++      while ((item=li++))
++      {
++	Item *fix=make_cond_for_info_schema(item, table);
++	if (!fix)
++	  return (COND*) 0;
++	new_cond->argument_list()->push_back(fix);
++      }
++      new_cond->quick_fix_field();
++      new_cond->top_level_item();
++      return new_cond;
++    }
++  }
++
++  if (!uses_only_table_name_fields(cond, table))
++    return (COND*) 0;
++  return cond;
++}
++
++
++enum enum_schema_tables get_schema_table_idx(ST_SCHEMA_TABLE *schema_table)
++{
++  return (enum enum_schema_tables) (schema_table - &schema_tables[0]);
++}
++
++
++/*
++  Create db names list. Information schema name always is first in list
++
++  SYNOPSIS
++    make_db_list()
++    thd                   thread handler
++    files                 list of db names
++    wild                  wild string
++    idx_field_vals        idx_field_vals->db_name contains db name or
++                          wild string
++    with_i_schema         returns 1 if we added 'IS' name to list
++                          otherwise returns 0
++    is_wild_value         if value is 1 then idx_field_vals->db_name is
++                          wild string otherwise it's db name; 
++
++  RETURN
++    zero                  success
++    non-zero              error
++*/
++
++int make_db_list(THD *thd, List<char> *files,
++                 INDEX_FIELD_VALUES *idx_field_vals,
++                 bool *with_i_schema, bool is_wild_value)
++{
++  LEX *lex= thd->lex;
++  *with_i_schema= 0;
++  get_index_field_values(lex, idx_field_vals);
++  if (is_wild_value)
++  {
++    /*
++      This part of code is only for SHOW DATABASES command.
++      idx_field_vals->db_value can be 0 when we don't use
++      LIKE clause (see also get_index_field_values() function)
++    */
++    if (!idx_field_vals->db_value ||
++        !wild_case_compare(system_charset_info, 
++                           information_schema_name.str,
++                           idx_field_vals->db_value))
++    {
++      *with_i_schema= 1;
++      if (files->push_back(thd->strdup(information_schema_name.str)))
++        return 1;
++    }
++    return (find_files(thd, files, NullS, mysql_data_home,
++                       idx_field_vals->db_value, 1) != FIND_FILES_OK);
++  }
++
++  /*
++    This part of code is for SHOW TABLES, SHOW TABLE STATUS commands.
++    idx_field_vals->db_value can't be 0 (see get_index_field_values()
++    function). lex->orig_sql_command can be not equal to SQLCOM_END
++    only in case of executing of SHOW commands.
++  */
++  if (lex->orig_sql_command != SQLCOM_END)
++  {
++    if (!my_strcasecmp(system_charset_info, information_schema_name.str,
++                       idx_field_vals->db_value))
++    {
++      *with_i_schema= 1;
++      return files->push_back(thd->strdup(information_schema_name.str));
++    }
++    return files->push_back(thd->strdup(idx_field_vals->db_value));
++  }
++
++  /*
++    Create list of existing databases. It is used in case
++    of select from information schema table
++  */
++  if (files->push_back(thd->strdup(information_schema_name.str)))
++    return 1;
++  *with_i_schema= 1;
++  return (find_files(thd, files, NullS,
++                     mysql_data_home, NullS, 1) != FIND_FILES_OK);
++}
++
++
++int schema_tables_add(THD *thd, List<char> *files, const char *wild)
++{
++  ST_SCHEMA_TABLE *tmp_schema_table= schema_tables;
++  for (; tmp_schema_table->table_name; tmp_schema_table++)
++  {
++    if (tmp_schema_table->hidden)
++      continue;
++    if (wild)
++    {
++      if (lower_case_table_names)
++      {
++        if (wild_case_compare(files_charset_info,
++                              tmp_schema_table->table_name,
++                              wild))
++          continue;
++      }
++      else if (wild_compare(tmp_schema_table->table_name, wild, 0))
++        continue;
++    }
++    if (files->push_back(thd->strdup(tmp_schema_table->table_name)))
++      return 1;
++  }
++  return 0;
++}
++
++
++int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond)
++{
++  LEX *lex= thd->lex;
++  TABLE *table= tables->table;
++  SELECT_LEX *select_lex= &lex->select_lex;
++  SELECT_LEX *old_all_select_lex= lex->all_selects_list;
++  enum_sql_command save_sql_command= lex->sql_command;
++  SELECT_LEX *lsel= tables->schema_select_lex;
++  ST_SCHEMA_TABLE *schema_table= tables->schema_table;
++  SELECT_LEX sel;
++  INDEX_FIELD_VALUES idx_field_vals;
++  char path[FN_REFLEN], *end, *base_name, *orig_base_name, *file_name;
++  uint len;
++  bool with_i_schema;
++  enum enum_schema_tables schema_table_idx;
++  List<char> bases;
++  List_iterator_fast<char> it(bases);
++  COND *partial_cond;
++  Security_context *sctx= thd->security_ctx;
++  uint derived_tables= lex->derived_tables; 
++  int error= 1;
++  db_type not_used;
++  Open_tables_state open_tables_state_backup;
++  bool save_view_prepare_mode= lex->view_prepare_mode;
++  Query_tables_list query_tables_list_backup;
++  lex->view_prepare_mode= TRUE;
++  DBUG_ENTER("get_all_tables");
++
++  LINT_INIT(end);
++  LINT_INIT(len);
++
++  lex->reset_n_backup_query_tables_list(&query_tables_list_backup);
++
++  /*
++    We should not introduce deadlocks even if we already have some
++    tables open and locked, since we won't lock tables which we will
++    open and will ignore possible name-locks for these tables.
++  */
++  thd->reset_n_backup_open_tables_state(&open_tables_state_backup);
++
++  if (lsel)
++  {
++    TABLE_LIST *show_table_list= (TABLE_LIST*) lsel->table_list.first;
++    bool res;
++
++    lex->all_selects_list= lsel;
++    /*
++      Restore thd->temporary_tables to be able to process
++      temporary tables(only for 'show index' & 'show columns').
++      This should be changed when processing of temporary tables for
++      I_S tables will be done.
++    */
++    thd->temporary_tables= open_tables_state_backup.temporary_tables;
++    /*
++      Let us set fake sql_command so views won't try to merge
++      themselves into main statement. If we don't do this,
++      SELECT * from information_schema.xxxx will cause problems.
++      SQLCOM_SHOW_FIELDS is used because it satisfies 'only_view_structure()' 
++    */
++    lex->sql_command= SQLCOM_SHOW_FIELDS;
++    res= open_normal_and_derived_tables(thd, show_table_list,
++                                        MYSQL_LOCK_IGNORE_FLUSH);
++    lex->sql_command= save_sql_command;
++    /*
++      get_all_tables() returns 1 on failure and 0 on success thus
++      return only these and not the result code of ::process_table()
++
++      We should use show_table_list->alias instead of 
++      show_table_list->table_name because table_name
++      could be changed during opening of I_S tables. It's safe
++      to use alias because alias contains original table name 
++      in this case(this part of code is used only for 
++      'show columns' & 'show statistics' commands).
++    */
++    error= test(schema_table->process_table(thd, show_table_list,
++                                            table, res, 
++                                            (show_table_list->view ?
++                                             show_table_list->view_db.str :
++                                             show_table_list->db),
++                                            show_table_list->alias));
++    thd->temporary_tables= 0;
++    close_tables_for_reopen(thd, &show_table_list);
++    goto err;
++  }
++
++  schema_table_idx= get_schema_table_idx(schema_table);
++
++  if (make_db_list(thd, &bases, &idx_field_vals,
++                   &with_i_schema, 0))
++    goto err;
++
++  partial_cond= make_cond_for_info_schema(cond, tables);
++  it.rewind(); /* To get access to new elements in basis list */
++  while ((orig_base_name= base_name= it++) ||
++	 /*
++	   generate error for non existing database.
++	   (to save old behaviour for SHOW TABLES FROM db)
++	 */
++	 ((lex->orig_sql_command == SQLCOM_SHOW_TABLES ||
++           lex->orig_sql_command == SQLCOM_SHOW_TABLE_STATUS) &&
++	  (base_name= select_lex->db) && !bases.elements))
++  {
++#ifndef NO_EMBEDDED_ACCESS_CHECKS
++    if (!check_access(thd,SELECT_ACL, base_name, 
++                      &thd->col_access, 0, 1, with_i_schema) ||
++        sctx->master_access & (DB_ACLS | SHOW_DB_ACL) ||
++	acl_get(sctx->host, sctx->ip, sctx->priv_user, base_name,0) ||
++	(grant_option && !check_grant_db(thd, base_name)))
++#endif
++    {
++      List<char> files;
++      if (with_i_schema)                      // information schema table names
++      {
++        if (schema_tables_add(thd, &files, idx_field_vals.table_value))
++          goto err;
++      }
++      else
++      {
++        strxmov(path, mysql_data_home, "/", base_name, NullS);
++        end= path + (len= unpack_dirname(path,path));
++        len= FN_LEN - len;
++        find_files_result res= find_files(thd, &files, base_name, 
++                                          path, idx_field_vals.table_value, 0);
++        if (res != FIND_FILES_OK)
++        {
++          /*
++            Downgrade errors about problems with database directory to
++            warnings if this is not a 'SHOW' command.  Another thread
++            may have dropped database, and we may still have a name
++            for that directory.
++          */
++          if (res == FIND_FILES_DIR && lex->orig_sql_command == SQLCOM_END)
++          {
++            push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
++                         thd->net.last_errno, thd->net.last_error);
++            thd->clear_error();
++            continue;
++          }
++          else
++          {
++            goto err;
++          }
++        }
++        if (lower_case_table_names)
++          orig_base_name= thd->strdup(base_name);
++      }
++
++      List_iterator_fast<char> it_files(files);
++      while ((file_name= it_files++))
++      {
++	restore_record(table, s->default_values);
++        table->field[schema_table->idx_field1]->
++          store(base_name, strlen(base_name), system_charset_info);
++        table->field[schema_table->idx_field2]->
++          store(file_name, strlen(file_name),system_charset_info);
++        if (!partial_cond || partial_cond->val_int())
++        {
++          if (schema_table_idx == SCH_TABLE_NAMES)
++          {
++            if (lex->verbose || lex->orig_sql_command == SQLCOM_END)
++            {
++              if (with_i_schema)
++              {
++                table->field[3]->store(STRING_WITH_LEN("SYSTEM VIEW"),
++                                       system_charset_info);
++              }
++              else
++              {
++                my_snprintf(end, len, "/%s%s", file_name, reg_ext);
++                switch (mysql_frm_type(thd, path, &not_used)) {
++                case FRMTYPE_ERROR:
++                  table->field[3]->store(STRING_WITH_LEN("ERROR"),
++                                         system_charset_info);
++                  break;
++                case FRMTYPE_TABLE:
++                  table->field[3]->store(STRING_WITH_LEN("BASE TABLE"),
++                                         system_charset_info);
++                  break;
++                case FRMTYPE_VIEW:
++                  table->field[3]->store(STRING_WITH_LEN("VIEW"),
++                                         system_charset_info);
++                  break;
++                default:
++                  DBUG_ASSERT(0);
++                }
++              }
++            }
++            if (schema_table_store_record(thd, table))
++              goto err;
++          }
++          else
++          {
++            int res;
++            /*
++              Set the parent lex of 'sel' because it is needed by sel.init_query()
++              which is called inside make_table_list.
++            */
++            sel.parent_lex= lex;
++            if (make_table_list(thd, &sel, base_name, file_name))
++              goto err;
++            TABLE_LIST *show_table_list= (TABLE_LIST*) sel.table_list.first;
++            lex->all_selects_list= &sel;
++            lex->derived_tables= 0;
++            lex->sql_command= SQLCOM_SHOW_FIELDS;
++            res= open_normal_and_derived_tables(thd, show_table_list,
++                                                MYSQL_LOCK_IGNORE_FLUSH);
++            lex->sql_command= save_sql_command;
++            /*
++              We should use show_table_list->alias instead of 
++              show_table_list->table_name because table_name
++              could be changed during opening of I_S tables. It's safe
++              to use alias because alias contains original table name 
++              in this case.
++            */
++            res= schema_table->process_table(thd, show_table_list, table,
++                                             res, orig_base_name,
++                                             show_table_list->alias);
++            close_tables_for_reopen(thd, &show_table_list);
++            DBUG_ASSERT(!lex->query_tables_own_last);
++            if (res)
++              goto err;
++          }
++        }
++      }
++      /*
++        If we have information schema its always the first table and only
++        the first table. Reset for other tables.
++      */
++      with_i_schema= 0;
++    }
++  }
++
++  error= 0;
++err:
++  thd->restore_backup_open_tables_state(&open_tables_state_backup);
++  lex->restore_backup_query_tables_list(&query_tables_list_backup);
++  lex->derived_tables= derived_tables;
++  lex->all_selects_list= old_all_select_lex;
++  lex->view_prepare_mode= save_view_prepare_mode;
++  lex->sql_command= save_sql_command;
++  DBUG_RETURN(error);
++}
++
++
++bool store_schema_shemata(THD* thd, TABLE *table, const char *db_name,
++                          CHARSET_INFO *cs)
++{
++  restore_record(table, s->default_values);
++  table->field[1]->store(db_name, strlen(db_name), system_charset_info);
++  table->field[2]->store(cs->csname, strlen(cs->csname), system_charset_info);
++  table->field[3]->store(cs->name, strlen(cs->name), system_charset_info);
++  return schema_table_store_record(thd, table);
++}
++
++
++int fill_schema_shemata(THD *thd, TABLE_LIST *tables, COND *cond)
++{
++  /*
++    TODO: fill_schema_shemata() is called when new client is connected.
++    Returning error status in this case leads to client hangup.
++  */
++
++  INDEX_FIELD_VALUES idx_field_vals;
++  List<char> files;
++  char *file_name;
++  bool with_i_schema;
++  HA_CREATE_INFO create;
++  TABLE *table= tables->table;
++  Security_context *sctx= thd->security_ctx;
++  DBUG_ENTER("fill_schema_shemata");
++
++  if (make_db_list(thd, &files, &idx_field_vals,
++                   &with_i_schema, 1))
++    DBUG_RETURN(1);
++
++  List_iterator_fast<char> it(files);
++  while ((file_name=it++))
++  {
++    if (with_i_schema)       // information schema name is always first in list
++    {
++      if (store_schema_shemata(thd, table, file_name,
++                               system_charset_info))
++        DBUG_RETURN(1);
++      with_i_schema= 0;
++      continue;
++    }
++#ifndef NO_EMBEDDED_ACCESS_CHECKS
++    if (sctx->master_access & (DB_ACLS | SHOW_DB_ACL) ||
++	acl_get(sctx->host, sctx->ip, sctx->priv_user, file_name,0) ||
++	(grant_option && !check_grant_db(thd, file_name)))
++#endif
++    {
++      load_db_opt_by_name(thd, file_name, &create);
++
++      if (store_schema_shemata(thd, table, file_name,
++                               create.default_table_charset))
++        DBUG_RETURN(1);
++    }
++  }
++  DBUG_RETURN(0);
++}
++
++
++static int get_schema_tables_record(THD *thd, struct st_table_list *tables,
++				    TABLE *table, bool res,
++				    const char *base_name,
++				    const char *file_name)
++{
++  const char *tmp_buff;
++  TIME time;
++  CHARSET_INFO *cs= system_charset_info;
++  DBUG_ENTER("get_schema_tables_record");
++
++  restore_record(table, s->default_values);
++  table->field[1]->store(base_name, strlen(base_name), cs);
++  table->field[2]->store(file_name, strlen(file_name), cs);
++  if (res)
++  {
++    /*
++      there was errors during opening tables
++    */
++    const char *error= thd->net.last_error;
++    if (tables->view)
++      table->field[3]->store(STRING_WITH_LEN("VIEW"), cs);
++    else if (tables->schema_table)
++      table->field[3]->store(STRING_WITH_LEN("SYSTEM VIEW"), cs);
++    else
++      table->field[3]->store(STRING_WITH_LEN("BASE TABLE"), cs);
++    table->field[20]->store(error, strlen(error), cs);
++    thd->clear_error();
++  }
++  else if (tables->view)
++  {
++    table->field[3]->store(STRING_WITH_LEN("VIEW"), cs);
++    table->field[20]->store(STRING_WITH_LEN("VIEW"), cs);
++  }
++  else
++  {
++    TABLE *show_table= tables->table;
++    TABLE_SHARE *share= show_table->s;
++    handler *file= show_table->file;
++
++    file->info(HA_STATUS_VARIABLE | HA_STATUS_TIME | HA_STATUS_AUTO |
++               HA_STATUS_NO_LOCK);
++    if (share->tmp_table == SYSTEM_TMP_TABLE)
++      table->field[3]->store(STRING_WITH_LEN("SYSTEM VIEW"), cs);
++    else if (share->tmp_table)
++      table->field[3]->store(STRING_WITH_LEN("LOCAL TEMPORARY"), cs);
++    else
++      table->field[3]->store(STRING_WITH_LEN("BASE TABLE"), cs);
++
++    for (int i= 4; i < 20; i++)
++    {
++      if (i == 7 || (i > 12 && i < 17) || i == 18)
++        continue;
++      table->field[i]->set_notnull();
++    }
++    tmp_buff= file->table_type();
++    table->field[4]->store(tmp_buff, strlen(tmp_buff), cs);
++    table->field[5]->store((longlong) share->frm_version, TRUE);
++    enum row_type row_type = file->get_row_type();
++    switch (row_type) {
++    case ROW_TYPE_NOT_USED:
++    case ROW_TYPE_DEFAULT:
++      tmp_buff= ((share->db_options_in_use &
++		  HA_OPTION_COMPRESS_RECORD) ? "Compressed" :
++		 (share->db_options_in_use & HA_OPTION_PACK_RECORD) ?
++		 "Dynamic" : "Fixed");
++      break;
++    case ROW_TYPE_FIXED:
++      tmp_buff= "Fixed";
++      break;
++    case ROW_TYPE_DYNAMIC:
++      tmp_buff= "Dynamic";
++      break;
++    case ROW_TYPE_COMPRESSED:
++      tmp_buff= "Compressed";
++      break;
++    case ROW_TYPE_REDUNDANT:
++      tmp_buff= "Redundant";
++      break;
++    case ROW_TYPE_COMPACT:
++      tmp_buff= "Compact";
++      break;
++    }
++    table->field[6]->store(tmp_buff, strlen(tmp_buff), cs);
++    if (!tables->schema_table)
++    {
++      table->field[7]->store((longlong) file->records, TRUE);
++      table->field[7]->set_notnull();
++    }
++    table->field[8]->store((longlong) file->mean_rec_length, TRUE);
++    table->field[9]->store((longlong) file->data_file_length, TRUE);
++    if (file->max_data_file_length)
++    {
++      table->field[10]->store((longlong) file->max_data_file_length, TRUE);
++    }
++    table->field[11]->store((longlong) file->index_file_length, TRUE);
++    table->field[12]->store((longlong) file->delete_length, TRUE);
++    if (show_table->found_next_number_field)
++    {
++      table->field[13]->store((longlong) file->auto_increment_value, TRUE);
++      table->field[13]->set_notnull();
++    }
++    if (file->create_time)
++    {
++      thd->variables.time_zone->gmt_sec_to_TIME(&time,
++                                                file->create_time);
++      table->field[14]->store_time(&time, MYSQL_TIMESTAMP_DATETIME);
++      table->field[14]->set_notnull();
++    }
++    if (file->update_time)
++    {
++      thd->variables.time_zone->gmt_sec_to_TIME(&time,
++                                                file->update_time);
++      table->field[15]->store_time(&time, MYSQL_TIMESTAMP_DATETIME);
++      table->field[15]->set_notnull();
++    }
++    if (file->check_time)
++    {
++      thd->variables.time_zone->gmt_sec_to_TIME(&time, file->check_time);
++      table->field[16]->store_time(&time, MYSQL_TIMESTAMP_DATETIME);
++      table->field[16]->set_notnull();
++    }
++    tmp_buff= (share->table_charset ?
++               share->table_charset->name : "default");
++    table->field[17]->store(tmp_buff, strlen(tmp_buff), cs);
++    if (file->table_flags() & (ulong) HA_HAS_CHECKSUM)
++    {
++      table->field[18]->store((longlong) file->checksum(), TRUE);
++      table->field[18]->set_notnull();
++    }
++
++    char option_buff[350],*ptr;
++    ptr=option_buff;
++    if (share->min_rows)
++    {
++      ptr=strmov(ptr," min_rows=");
++      ptr=longlong10_to_str(share->min_rows,ptr,10);
++    }
++    if (share->max_rows)
++    {
++      ptr=strmov(ptr," max_rows=");
++      ptr=longlong10_to_str(share->max_rows,ptr,10);
++    }
++    if (share->avg_row_length)
++    {
++      ptr=strmov(ptr," avg_row_length=");
++      ptr=longlong10_to_str(share->avg_row_length,ptr,10);
++    }
++    if (share->db_create_options & HA_OPTION_PACK_KEYS)
++      ptr=strmov(ptr," pack_keys=1");
++    if (share->db_create_options & HA_OPTION_NO_PACK_KEYS)
++      ptr=strmov(ptr," pack_keys=0");
++    if (share->db_create_options & HA_OPTION_CHECKSUM)
++      ptr=strmov(ptr," checksum=1");
++    if (share->db_create_options & HA_OPTION_DELAY_KEY_WRITE)
++      ptr=strmov(ptr," delay_key_write=1");
++    if (share->row_type != ROW_TYPE_DEFAULT)
++      ptr=strxmov(ptr, " row_format=", 
++                  ha_row_type[(uint) share->row_type],
++                  NullS);
++    if (file->raid_type)
++    {
++      char buff[100];
++      my_snprintf(buff,sizeof(buff),
++                  " raid_type=%s raid_chunks=%d raid_chunksize=%ld",
++                  my_raid_type(file->raid_type), file->raid_chunks,
++                  file->raid_chunksize/RAID_BLOCK_SIZE);
++      ptr=strmov(ptr,buff);
++    }
++    table->field[19]->store(option_buff+1,
++                            (ptr == option_buff ? 0 : 
++                             (uint) (ptr-option_buff)-1), cs);
++    {
++      char *comment;
++      comment= show_table->file->update_table_comment(share->comment.str);
++      if (comment)
++      {
++        table->field[20]->store(comment,
++                                (comment == share->comment.str ?
++                                 share->comment.length : 
++                                 strlen(comment)), cs);
++        if (comment != share->comment.str)
++          my_free(comment, MYF(0));
++      }
++    }
++  }
++  DBUG_RETURN(schema_table_store_record(thd, table));
++}
++
++
++static int get_schema_column_record(THD *thd, struct st_table_list *tables,
++				    TABLE *table, bool res,
++				    const char *base_name,
++				    const char *file_name)
++{
++  LEX *lex= thd->lex;
++  const char *wild= lex->wild ? lex->wild->ptr() : NullS;
++  CHARSET_INFO *cs= system_charset_info;
++  TABLE *show_table;
++  handler *file;
++  Field **ptr,*field;
++  int count;
++  uint base_name_length, file_name_length;
++  DBUG_ENTER("get_schema_column_record");
++
++  if (res)
++  {
++    if (lex->orig_sql_command != SQLCOM_SHOW_FIELDS)
++    {
++      /*
++        I.e. we are in SELECT FROM INFORMATION_SCHEMA.COLUMS
++        rather than in SHOW COLUMNS
++      */ 
++      push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
++                   thd->net.last_errno, thd->net.last_error);
++      thd->clear_error();
++      res= 0;
++    }
++    DBUG_RETURN(res);
++  }
++
++  show_table= tables->table;
++  file= show_table->file;
++  count= 0;
++  file->info(HA_STATUS_VARIABLE | HA_STATUS_NO_LOCK);
++  restore_record(show_table, s->default_values);
++  base_name_length= strlen(base_name);
++  file_name_length= strlen(file_name);
++
++  for (ptr=show_table->field; (field= *ptr) ; ptr++)
++  {
++    const char *tmp_buff;
++    byte *pos;
++    bool is_blob;
++    uint flags=field->flags;
++    char tmp[MAX_FIELD_WIDTH];
++    char tmp1[MAX_FIELD_WIDTH];
++    String type(tmp,sizeof(tmp), system_charset_info);
++    char *end;
++    int decimals, field_length;
++
++    if (wild && wild[0] &&
++        wild_case_compare(system_charset_info, field->field_name,wild))
++      continue;
++
++    flags= field->flags;
++    count++;
++    /* Get default row, with all NULL fields set to NULL */
++    restore_record(table, s->default_values);
++
++#ifndef NO_EMBEDDED_ACCESS_CHECKS
++    uint col_access;
++    check_access(thd,SELECT_ACL | EXTRA_ACL, base_name,
++                 &tables->grant.privilege, 0, 0, test(tables->schema_table));
++    col_access= get_column_grant(thd, &tables->grant, 
++                                 base_name, file_name,
++                                 field->field_name) & COL_ACLS;
++    if (lex->orig_sql_command != SQLCOM_SHOW_FIELDS  && 
++        !tables->schema_table && !col_access)
++      continue;
++    end= tmp;
++    for (uint bitnr=0; col_access ; col_access>>=1,bitnr++)
++    {
++      if (col_access & 1)
++      {
++        *end++=',';
++        end=strmov(end,grant_types.type_names[bitnr]);
++      }
++    }
++    table->field[17]->store(tmp+1,end == tmp ? 0 : (uint) (end-tmp-1), cs);
++
++#endif
++    table->field[1]->store(base_name, base_name_length, cs);
++    table->field[2]->store(file_name, file_name_length, cs);
++    table->field[3]->store(field->field_name, strlen(field->field_name),
++                           cs);
++    table->field[4]->store((longlong) count, TRUE);
++    field->sql_type(type);
++    table->field[14]->store(type.ptr(), type.length(), cs);		
++    tmp_buff= strchr(type.ptr(), '(');
++    table->field[7]->store(type.ptr(),
++                           (tmp_buff ? tmp_buff - type.ptr() :
++                            type.length()), cs);
++    if (show_table->timestamp_field == field &&
++        field->unireg_check != Field::TIMESTAMP_UN_FIELD)
++    {
++      table->field[5]->store(STRING_WITH_LEN("CURRENT_TIMESTAMP"), cs);
++      table->field[5]->set_notnull();
++    }
++    else if (field->unireg_check != Field::NEXT_NUMBER &&
++             !field->is_null() &&
++             !(field->flags & NO_DEFAULT_VALUE_FLAG))
++    {
++      String def(tmp1,sizeof(tmp1), cs);
++      type.set(tmp, sizeof(tmp), field->charset());
++      field->val_str(&type);
++      uint dummy_errors;
++      def.copy(type.ptr(), type.length(), type.charset(), cs, &dummy_errors);
++      table->field[5]->store(def.ptr(), def.length(), def.charset());
++      table->field[5]->set_notnull();
++    }
++    else if (field->unireg_check == Field::NEXT_NUMBER ||
++             lex->orig_sql_command != SQLCOM_SHOW_FIELDS ||
++             field->maybe_null())
++      table->field[5]->set_null();                // Null as default
++    else
++    {
++      table->field[5]->store("",0, cs);
++      table->field[5]->set_notnull();
++    }
++    pos=(byte*) ((flags & NOT_NULL_FLAG) ?  "NO" : "YES");
++    table->field[6]->store((const char*) pos,
++                           strlen((const char*) pos), cs);
++    is_blob= (field->type() == FIELD_TYPE_BLOB);
++    if (field->has_charset() || is_blob ||
++        field->real_type() == MYSQL_TYPE_VARCHAR ||  // For varbinary type
++        field->real_type() == MYSQL_TYPE_STRING)     // For binary type
++    {
++      uint32 octet_max_length= field->max_length();
++      if (is_blob && octet_max_length != (uint32) 4294967295U)
++        octet_max_length /= field->charset()->mbmaxlen;
++      longlong char_max_len= is_blob ? 
++        (longlong) octet_max_length / field->charset()->mbminlen :
++        (longlong) octet_max_length / field->charset()->mbmaxlen;
++      table->field[8]->store(char_max_len, TRUE);
++      table->field[8]->set_notnull();
++      table->field[9]->store((longlong) octet_max_length, TRUE);
++      table->field[9]->set_notnull();
++    }
++
++    /*
++      Calculate field_length and decimals.
++      They are set to -1 if they should not be set (we should return NULL)
++    */
++
++    decimals= field->decimals();
++    switch (field->type()) {
++    case FIELD_TYPE_NEWDECIMAL:
++      field_length= ((Field_new_decimal*) field)->precision;
++      break;
++    case FIELD_TYPE_DECIMAL:
++      field_length= field->field_length - (decimals  ? 2 : 1);
++      break;
++    case FIELD_TYPE_TINY:
++    case FIELD_TYPE_SHORT:
++    case FIELD_TYPE_LONG:
++    case FIELD_TYPE_LONGLONG:
++    case FIELD_TYPE_INT24:
++      field_length= field->max_length() - 1;
++      break;
++    case FIELD_TYPE_BIT:
++      field_length= field->max_length();
++      decimals= -1;                             // return NULL
++      break;
++    case FIELD_TYPE_FLOAT:  
++    case FIELD_TYPE_DOUBLE:
++      field_length= field->field_length;
++      if (decimals == NOT_FIXED_DEC)
++        decimals= -1;                           // return NULL
++    break;
++    default:
++      field_length= decimals= -1;
++      break;
++    }
++
++    if (field_length >= 0)
++    {
++      table->field[10]->store((longlong) field_length, TRUE);
++      table->field[10]->set_notnull();
++    }
++    if (decimals >= 0)
++    {
++      table->field[11]->store((longlong) decimals, TRUE);
++      table->field[11]->set_notnull();
++    }
++
++    if (field->has_charset())
++    {
++      pos=(byte*) field->charset()->csname;
++      table->field[12]->store((const char*) pos,
++                              strlen((const char*) pos), cs);
++      table->field[12]->set_notnull();
++      pos=(byte*) field->charset()->name;
++      table->field[13]->store((const char*) pos,
++                              strlen((const char*) pos), cs);
++      table->field[13]->set_notnull();
++    }
++    pos=(byte*) ((field->flags & PRI_KEY_FLAG) ? "PRI" :
++                 (field->flags & UNIQUE_KEY_FLAG) ? "UNI" :
++                 (field->flags & MULTIPLE_KEY_FLAG) ? "MUL":"");
++    table->field[15]->store((const char*) pos,
++                            strlen((const char*) pos), cs);
++
++    end= tmp;
++    if (field->unireg_check == Field::NEXT_NUMBER)
++      end=strmov(tmp,"auto_increment");
++    table->field[16]->store(tmp, (uint) (end-tmp), cs);
++
++    table->field[18]->store(field->comment.str, field->comment.length, cs);
++    if (schema_table_store_record(thd, table))
++      DBUG_RETURN(1);
++  }
++  DBUG_RETURN(0);
++}
++
++
++
++int fill_schema_charsets(THD *thd, TABLE_LIST *tables, COND *cond)
++{
++  CHARSET_INFO **cs;
++  const char *wild= thd->lex->wild ? thd->lex->wild->ptr() : NullS;
++  TABLE *table= tables->table;
++  CHARSET_INFO *scs= system_charset_info;
++
++  for (cs= all_charsets ; cs < all_charsets+255 ; cs++)
++  {
++    CHARSET_INFO *tmp_cs= cs[0];
++    if (tmp_cs && (tmp_cs->state & MY_CS_PRIMARY) && 
++        (tmp_cs->state & MY_CS_AVAILABLE) &&
++        !(wild && wild[0] &&
++	  wild_case_compare(scs, tmp_cs->csname,wild)))
++    {
++      const char *comment;
++      restore_record(table, s->default_values);
++      table->field[0]->store(tmp_cs->csname, strlen(tmp_cs->csname), scs);
++      table->field[1]->store(tmp_cs->name, strlen(tmp_cs->name), scs);
++      comment= tmp_cs->comment ? tmp_cs->comment : "";
++      table->field[2]->store(comment, strlen(comment), scs);
++      table->field[3]->store((longlong) tmp_cs->mbmaxlen, TRUE);
++      if (schema_table_store_record(thd, table))
++        return 1;
++    }
++  }
++  return 0;
++}
++
++
++int fill_schema_collation(THD *thd, TABLE_LIST *tables, COND *cond)
++{
++  CHARSET_INFO **cs;
++  const char *wild= thd->lex->wild ? thd->lex->wild->ptr() : NullS;
++  TABLE *table= tables->table;
++  CHARSET_INFO *scs= system_charset_info;
++  for (cs= all_charsets ; cs < all_charsets+255 ; cs++ )
++  {
++    CHARSET_INFO **cl;
++    CHARSET_INFO *tmp_cs= cs[0];
++    if (!tmp_cs || !(tmp_cs->state & MY_CS_AVAILABLE) || 
++        !(tmp_cs->state & MY_CS_PRIMARY))
++      continue;
++    for (cl= all_charsets; cl < all_charsets+255 ;cl ++)
++    {
++      CHARSET_INFO *tmp_cl= cl[0];
++      if (!tmp_cl || !(tmp_cl->state & MY_CS_AVAILABLE) || 
++          !my_charset_same(tmp_cs, tmp_cl))
++	continue;
++      if (!(wild && wild[0] &&
++	  wild_case_compare(scs, tmp_cl->name,wild)))
++      {
++	const char *tmp_buff;
++	restore_record(table, s->default_values);
++	table->field[0]->store(tmp_cl->name, strlen(tmp_cl->name), scs);
++        table->field[1]->store(tmp_cl->csname , strlen(tmp_cl->csname), scs);
++        table->field[2]->store((longlong) tmp_cl->number, TRUE);
++        tmp_buff= (tmp_cl->state & MY_CS_PRIMARY) ? "Yes" : "";
++	table->field[3]->store(tmp_buff, strlen(tmp_buff), scs);
++        tmp_buff= (tmp_cl->state & MY_CS_COMPILED)? "Yes" : "";
++	table->field[4]->store(tmp_buff, strlen(tmp_buff), scs);
++        table->field[5]->store((longlong) tmp_cl->strxfrm_multiply, TRUE);
++        if (schema_table_store_record(thd, table))
++          return 1;
++      }
++    }
++  }
++  return 0;
++}
++
++
++int fill_schema_coll_charset_app(THD *thd, TABLE_LIST *tables, COND *cond)
++{
++  CHARSET_INFO **cs;
++  TABLE *table= tables->table;
++  CHARSET_INFO *scs= system_charset_info;
++  for (cs= all_charsets ; cs < all_charsets+255 ; cs++ )
++  {
++    CHARSET_INFO **cl;
++    CHARSET_INFO *tmp_cs= cs[0];
++    if (!tmp_cs || !(tmp_cs->state & MY_CS_AVAILABLE) || 
++        !(tmp_cs->state & MY_CS_PRIMARY))
++      continue;
++    for (cl= all_charsets; cl < all_charsets+255 ;cl ++)
++    {
++      CHARSET_INFO *tmp_cl= cl[0];
++      if (!tmp_cl || !(tmp_cl->state & MY_CS_AVAILABLE) || 
++          !my_charset_same(tmp_cs,tmp_cl))
++	continue;
++      restore_record(table, s->default_values);
++      table->field[0]->store(tmp_cl->name, strlen(tmp_cl->name), scs);
++      table->field[1]->store(tmp_cl->csname , strlen(tmp_cl->csname), scs);
++      if (schema_table_store_record(thd, table))
++        return 1;
++    }
++  }
++  return 0;
++}
++
++
++bool store_schema_proc(THD *thd, TABLE *table, TABLE *proc_table,
++                       const char *wild, bool full_access, const char *sp_user)
++{
++  String tmp_string;
++  String sp_db, sp_name, definer;
++  TIME time;
++  LEX *lex= thd->lex;
++  CHARSET_INFO *cs= system_charset_info;
++  get_field(thd->mem_root, proc_table->field[0], &sp_db);
++  get_field(thd->mem_root, proc_table->field[1], &sp_name);
++  get_field(thd->mem_root, proc_table->field[11], &definer);
++  if (!full_access)
++    full_access= !strcmp(sp_user, definer.ptr());
++  if (!full_access && check_some_routine_access(thd, sp_db.ptr(), sp_name.ptr(),
++                                                proc_table->field[2]->val_int() ==
++                                                TYPE_ENUM_PROCEDURE))
++    return 0;
++
++  if (lex->orig_sql_command == SQLCOM_SHOW_STATUS_PROC &&
++      proc_table->field[2]->val_int() == TYPE_ENUM_PROCEDURE ||
++      lex->orig_sql_command == SQLCOM_SHOW_STATUS_FUNC &&
++      proc_table->field[2]->val_int() == TYPE_ENUM_FUNCTION ||
++      lex->orig_sql_command == SQLCOM_END)
++  {
++    restore_record(table, s->default_values);
++    if (!wild || !wild[0] || !wild_compare(sp_name.ptr(), wild, 0))
++    {
++      int enum_idx= (int) proc_table->field[5]->val_int();
++      table->field[3]->store(sp_name.ptr(), sp_name.length(), cs);
++      get_field(thd->mem_root, proc_table->field[3], &tmp_string);
++      table->field[0]->store(tmp_string.ptr(), tmp_string.length(), cs);
++      table->field[2]->store(sp_db.ptr(), sp_db.length(), cs);
++      get_field(thd->mem_root, proc_table->field[2], &tmp_string);
++      table->field[4]->store(tmp_string.ptr(), tmp_string.length(), cs);
++      if (proc_table->field[2]->val_int() == TYPE_ENUM_FUNCTION)
++      {
++        get_field(thd->mem_root, proc_table->field[9], &tmp_string);
++        table->field[5]->store(tmp_string.ptr(), tmp_string.length(), cs);
++        table->field[5]->set_notnull();
++      }
++      if (full_access)
++      {
++        get_field(thd->mem_root, proc_table->field[10], &tmp_string);
++        table->field[7]->store(tmp_string.ptr(), tmp_string.length(), cs);
++        table->field[7]->set_notnull();
++      }
++      table->field[6]->store(STRING_WITH_LEN("SQL"), cs);
++      table->field[10]->store(STRING_WITH_LEN("SQL"), cs);
++      get_field(thd->mem_root, proc_table->field[6], &tmp_string);
++      table->field[11]->store(tmp_string.ptr(), tmp_string.length(), cs);
++      table->field[12]->store(sp_data_access_name[enum_idx].str, 
++                              sp_data_access_name[enum_idx].length , cs);
++      get_field(thd->mem_root, proc_table->field[7], &tmp_string);
++      table->field[14]->store(tmp_string.ptr(), tmp_string.length(), cs);
++      bzero((char *)&time, sizeof(time));
++      ((Field_timestamp *) proc_table->field[12])->get_time(&time);
++      table->field[15]->store_time(&time, MYSQL_TIMESTAMP_DATETIME);
++      bzero((char *)&time, sizeof(time));
++      ((Field_timestamp *) proc_table->field[13])->get_time(&time);
++      table->field[16]->store_time(&time, MYSQL_TIMESTAMP_DATETIME);
++      get_field(thd->mem_root, proc_table->field[14], &tmp_string);
++      table->field[17]->store(tmp_string.ptr(), tmp_string.length(), cs);
++      get_field(thd->mem_root, proc_table->field[15], &tmp_string);
++      table->field[18]->store(tmp_string.ptr(), tmp_string.length(), cs);
++      table->field[19]->store(definer.ptr(), definer.length(), cs);
++      return schema_table_store_record(thd, table);
++    }
++  }
++  return 0;
++}
++
++
++int fill_schema_proc(THD *thd, TABLE_LIST *tables, COND *cond)
++{
++  TABLE *proc_table;
++  TABLE_LIST proc_tables;
++  const char *wild= thd->lex->wild ? thd->lex->wild->ptr() : NullS;
++  int res= 0;
++  TABLE *table= tables->table;
++  bool full_access;
++  char definer[USER_HOST_BUFF_SIZE];
++  Open_tables_state open_tables_state_backup;
++  DBUG_ENTER("fill_schema_proc");
++
++  strxmov(definer, thd->security_ctx->priv_user, "@",
++          thd->security_ctx->priv_host, NullS);
++  /* We use this TABLE_LIST instance only for checking of privileges. */
++  bzero((char*) &proc_tables,sizeof(proc_tables));
++  proc_tables.db= (char*) "mysql";
++  proc_tables.db_length= 5;
++  proc_tables.table_name= proc_tables.alias= (char*) "proc";
++  proc_tables.table_name_length= 4;
++  proc_tables.lock_type= TL_READ;
++  full_access= !check_table_access(thd, SELECT_ACL, &proc_tables, 1);
++  if (!(proc_table= open_proc_table_for_read(thd, &open_tables_state_backup)))
++  {
++    DBUG_RETURN(1);
++  }
++  proc_table->file->ha_index_init(0);
++  if ((res= proc_table->file->index_first(proc_table->record[0])))
++  {
++    res= (res == HA_ERR_END_OF_FILE) ? 0 : 1;
++    goto err;
++  }
++  if (store_schema_proc(thd, table, proc_table, wild, full_access, definer))
++  {
++    res= 1;
++    goto err;
++  }
++  while (!proc_table->file->index_next(proc_table->record[0]))
++  {
++    if (store_schema_proc(thd, table, proc_table, wild, full_access, definer))
++    {
++      res= 1;
++      goto err;
++    }
++  }
++
++err:
++  proc_table->file->ha_index_end();
++  close_proc_table(thd, &open_tables_state_backup);
++  DBUG_RETURN(res);
++}
++
++
++static int get_schema_stat_record(THD *thd, struct st_table_list *tables,
++				  TABLE *table, bool res,
++				  const char *base_name,
++				  const char *file_name)
++{
++  CHARSET_INFO *cs= system_charset_info;
++  DBUG_ENTER("get_schema_stat_record");
++  if (res)
++  {
++    if (thd->lex->orig_sql_command != SQLCOM_SHOW_KEYS)
++    {
++      /*
++        I.e. we are in SELECT FROM INFORMATION_SCHEMA.STATISTICS
++        rather than in SHOW KEYS
++      */
++      if (!tables->view)
++        push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
++                     thd->net.last_errno, thd->net.last_error);
++      thd->clear_error();
++      res= 0;
++    }
++    DBUG_RETURN(res);
++  }
++  else if (!tables->view)
++  {
++    TABLE *show_table= tables->table;
++    KEY *key_info=show_table->key_info;
++    show_table->file->info(HA_STATUS_VARIABLE |
++                           HA_STATUS_NO_LOCK |
++                           HA_STATUS_TIME);
++    for (uint i=0 ; i < show_table->s->keys ; i++,key_info++)
++    {
++      KEY_PART_INFO *key_part= key_info->key_part;
++      const char *str;
++      for (uint j=0 ; j < key_info->key_parts ; j++,key_part++)
++      {
++        restore_record(table, s->default_values);
++        table->field[1]->store(base_name, strlen(base_name), cs);
++        table->field[2]->store(file_name, strlen(file_name), cs);
++        table->field[3]->store((longlong) ((key_info->flags &
++                                            HA_NOSAME) ? 0 : 1), TRUE);
++        table->field[4]->store(base_name, strlen(base_name), cs);
++        table->field[5]->store(key_info->name, strlen(key_info->name), cs);
++        table->field[6]->store((longlong) (j+1), TRUE);
++        str=(key_part->field ? key_part->field->field_name :
++             "?unknown field?");
++        table->field[7]->store(str, strlen(str), cs);
++        if (show_table->file->index_flags(i, j, 0) & HA_READ_ORDER)
++        {
++          table->field[8]->store(((key_part->key_part_flag &
++                                   HA_REVERSE_SORT) ?
++                                  "D" : "A"), 1, cs);
++          table->field[8]->set_notnull();
++        }
++        KEY *key=show_table->key_info+i;
++        if (key->rec_per_key[j])
++        {
++          ha_rows records=(show_table->file->records /
++                           key->rec_per_key[j]);
++          table->field[9]->store((longlong) records, TRUE);
++          table->field[9]->set_notnull();
++        }
++        if (!(key_info->flags & HA_FULLTEXT) &&
++            (key_part->field &&
++             key_part->length !=
++             show_table->field[key_part->fieldnr-1]->key_length()))
++        {
++          table->field[10]->store((longlong) key_part->length /
++                                  key_part->field->charset()->mbmaxlen, 1);
++          table->field[10]->set_notnull();
++        }
++        uint flags= key_part->field ? key_part->field->flags : 0;
++        const char *pos=(char*) ((flags & NOT_NULL_FLAG) ? "" : "YES");
++        table->field[12]->store(pos, strlen(pos), cs);
++        pos= show_table->file->index_type(i);
++        table->field[13]->store(pos, strlen(pos), cs);
++        if (!show_table->s->keys_in_use.is_set(i))
++          table->field[14]->store(STRING_WITH_LEN("disabled"), cs);
++        else
++          table->field[14]->store("", 0, cs);
++        table->field[14]->set_notnull();
++        if (schema_table_store_record(thd, table))
++          DBUG_RETURN(1);
++      }
++    }
++  }
++  DBUG_RETURN(res);
++}
++
++
++static int get_schema_views_record(THD *thd, struct st_table_list *tables,
++				   TABLE *table, bool res,
++				   const char *base_name,
++				   const char *file_name)
++{
++  CHARSET_INFO *cs= system_charset_info;
++  DBUG_ENTER("get_schema_views_record");
++  char definer[USER_HOST_BUFF_SIZE];
++  uint definer_len;
++
++  if (tables->view)
++  {
++    Security_context *sctx= thd->security_ctx;
++    if (!tables->allowed_show)
++    {
++      if (!my_strcasecmp(system_charset_info, tables->definer.user.str,
++                         sctx->priv_user) &&
++          !my_strcasecmp(system_charset_info, tables->definer.host.str,
++                         sctx->priv_host))
++        tables->allowed_show= TRUE;
++    }
++    restore_record(table, s->default_values);
++    table->field[1]->store(tables->view_db.str, tables->view_db.length, cs);
++    table->field[2]->store(tables->view_name.str, tables->view_name.length, cs);
++    if (tables->allowed_show)
++    {
++      char buff[2048];
++      String qwe_str(buff, sizeof(buff), cs);
++      qwe_str.length(0);
++      qwe_str.append(STRING_WITH_LEN("/* "));
++      append_algorithm(tables, &qwe_str);
++      qwe_str.append(STRING_WITH_LEN("*/ "));
++      qwe_str.append(tables->query.str, tables->query.length);
++      table->field[3]->store(qwe_str.ptr(), qwe_str.length(), cs);
++    }
++
++    if (tables->with_check != VIEW_CHECK_NONE)
++    {
++      if (tables->with_check == VIEW_CHECK_LOCAL)
++        table->field[4]->store(STRING_WITH_LEN("LOCAL"), cs);
++      else
++        table->field[4]->store(STRING_WITH_LEN("CASCADED"), cs);
++    }
++    else
++      table->field[4]->store(STRING_WITH_LEN("NONE"), cs);
++
++    if (tables->updatable_view)
++      table->field[5]->store(STRING_WITH_LEN("YES"), cs);
++    else
++      table->field[5]->store(STRING_WITH_LEN("NO"), cs);
++    definer_len= (strxmov(definer, tables->definer.user.str, "@",
++                          tables->definer.host.str, NullS) - definer);
++    table->field[6]->store(definer, definer_len, cs);
++    if (tables->view_suid)
++      table->field[7]->store(STRING_WITH_LEN("DEFINER"), cs);
++    else
++      table->field[7]->store(STRING_WITH_LEN("INVOKER"), cs);
++    if (schema_table_store_record(thd, table))
++      DBUG_RETURN(1);
++    if (res)
++      push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, 
++                   thd->net.last_errno, thd->net.last_error);
++  }
++  if (res) 
++    thd->clear_error();
++  DBUG_RETURN(0);
++}
++
++
++bool store_constraints(THD *thd, TABLE *table, const char *db,
++                       const char *tname, const char *key_name,
++                       uint key_len, const char *con_type, uint con_len)
++{
++  CHARSET_INFO *cs= system_charset_info;
++  restore_record(table, s->default_values);
++  table->field[1]->store(db, strlen(db), cs);
++  table->field[2]->store(key_name, key_len, cs);
++  table->field[3]->store(db, strlen(db), cs);
++  table->field[4]->store(tname, strlen(tname), cs);
++  table->field[5]->store(con_type, con_len, cs);
++  return schema_table_store_record(thd, table);
++}
++
++
++static int get_schema_constraints_record(THD *thd, struct st_table_list *tables,
++					 TABLE *table, bool res,
++					 const char *base_name,
++					 const char *file_name)
++{
++  DBUG_ENTER("get_schema_constraints_record");
++  if (res)
++  {
++    if (!tables->view)
++      push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
++                   thd->net.last_errno, thd->net.last_error);
++    thd->clear_error();
++    DBUG_RETURN(0);
++  }
++  else if (!tables->view)
++  {
++    List<FOREIGN_KEY_INFO> f_key_list;
++    TABLE *show_table= tables->table;
++    KEY *key_info=show_table->key_info;
++    uint primary_key= show_table->s->primary_key;
++    show_table->file->info(HA_STATUS_VARIABLE | 
++                           HA_STATUS_NO_LOCK |
++                           HA_STATUS_TIME);
++    for (uint i=0 ; i < show_table->s->keys ; i++, key_info++)
++    {
++      if (i != primary_key && !(key_info->flags & HA_NOSAME))
++        continue;
++
++      if (i == primary_key && !strcmp(key_info->name, primary_key_name))
++      {
++        if (store_constraints(thd, table, base_name, file_name, key_info->name,
++                              strlen(key_info->name),
++                              STRING_WITH_LEN("PRIMARY KEY")))
++          DBUG_RETURN(1);
++      }
++      else if (key_info->flags & HA_NOSAME)
++      {
++        if (store_constraints(thd, table, base_name, file_name, key_info->name,
++                              strlen(key_info->name),
++                              STRING_WITH_LEN("UNIQUE")))
++          DBUG_RETURN(1);
++      }
++    }
++
++    show_table->file->get_foreign_key_list(thd, &f_key_list);
++    FOREIGN_KEY_INFO *f_key_info;
++    List_iterator_fast<FOREIGN_KEY_INFO> it(f_key_list);
++    while ((f_key_info=it++))
++    {
++      if (store_constraints(thd, table, base_name, file_name, 
++                            f_key_info->forein_id->str,
++                            strlen(f_key_info->forein_id->str),
++                            "FOREIGN KEY", 11))
++        DBUG_RETURN(1);
++    }
++  }
++  DBUG_RETURN(res);
++}
++
++
++static bool store_trigger(THD *thd, TABLE *table, const char *db,
++                          const char *tname, LEX_STRING *trigger_name,
++                          enum trg_event_type event,
++                          enum trg_action_time_type timing,
++                          LEX_STRING *trigger_stmt,
++                          ulong sql_mode,
++                          LEX_STRING *definer_buffer)
++{
++  CHARSET_INFO *cs= system_charset_info;
++  byte *sql_mode_str;
++  ulong sql_mode_len;
++
++  restore_record(table, s->default_values);
++  table->field[1]->store(db, strlen(db), cs);
++  table->field[2]->store(trigger_name->str, trigger_name->length, cs);
++  table->field[3]->store(trg_event_type_names[event].str,
++                         trg_event_type_names[event].length, cs);
++  table->field[5]->store(db, strlen(db), cs);
++  table->field[6]->store(tname, strlen(tname), cs);
++  table->field[9]->store(trigger_stmt->str, trigger_stmt->length, cs);
++  table->field[10]->store(STRING_WITH_LEN("ROW"), cs);
++  table->field[11]->store(trg_action_time_type_names[timing].str,
++                          trg_action_time_type_names[timing].length, cs);
++  table->field[14]->store(STRING_WITH_LEN("OLD"), cs);
++  table->field[15]->store(STRING_WITH_LEN("NEW"), cs);
++
++  sql_mode_str=
++    sys_var_thd_sql_mode::symbolic_mode_representation(thd,
++                                                       sql_mode,
++                                                       &sql_mode_len);
++  table->field[17]->store((const char*)sql_mode_str, sql_mode_len, cs);
++  table->field[18]->store((const char *)definer_buffer->str, definer_buffer->length, cs);
++  return schema_table_store_record(thd, table);
++}
++
++
++static int get_schema_triggers_record(THD *thd, struct st_table_list *tables,
++				      TABLE *table, bool res,
++				      const char *base_name,
++				      const char *file_name)
++{
++  DBUG_ENTER("get_schema_triggers_record");
++  /*
++    res can be non zero value when processed table is a view or
++    error happened during opening of processed table.
++  */
++  if (res)
++  {
++    if (!tables->view)
++      push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
++                   thd->net.last_errno, thd->net.last_error);
++    thd->clear_error();
++    DBUG_RETURN(0);
++  }
++  if (!tables->view && tables->table->triggers)
++  {
++    Table_triggers_list *triggers= tables->table->triggers;
++    int event, timing;
++    for (event= 0; event < (int)TRG_EVENT_MAX; event++)
++    {
++      for (timing= 0; timing < (int)TRG_ACTION_MAX; timing++)
++      {
++        LEX_STRING trigger_name;
++        LEX_STRING trigger_stmt;
++        ulong sql_mode;
++        char definer_holder[USER_HOST_BUFF_SIZE];
++        LEX_STRING definer_buffer;
++        definer_buffer.str= definer_holder;
++        if (triggers->get_trigger_info(thd, (enum trg_event_type) event,
++                                       (enum trg_action_time_type)timing,
++                                       &trigger_name, &trigger_stmt,
++                                       &sql_mode,
++                                       &definer_buffer))
++          continue;
++
++        if (store_trigger(thd, table, base_name, file_name, &trigger_name,
++                         (enum trg_event_type) event,
++                         (enum trg_action_time_type) timing, &trigger_stmt,
++                         sql_mode,
++                         &definer_buffer))
++          DBUG_RETURN(1);
++      }
++    }
++  }
++  DBUG_RETURN(0);
++}
++
++
++void store_key_column_usage(TABLE *table, const char*db, const char *tname,
++                            const char *key_name, uint key_len, 
++                            const char *con_type, uint con_len, longlong idx)
++{
++  CHARSET_INFO *cs= system_charset_info;
++  table->field[1]->store(db, strlen(db), cs);
++  table->field[2]->store(key_name, key_len, cs);
++  table->field[4]->store(db, strlen(db), cs);
++  table->field[5]->store(tname, strlen(tname), cs);
++  table->field[6]->store(con_type, con_len, cs);
++  table->field[7]->store((longlong) idx, TRUE);
++}
++
++
++static int get_schema_key_column_usage_record(THD *thd,
++					      struct st_table_list *tables,
++					      TABLE *table, bool res,
++					      const char *base_name,
++					      const char *file_name)
++{
++  DBUG_ENTER("get_schema_key_column_usage_record");
++  if (res)
++  {
++    if (!tables->view)
++      push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
++                   thd->net.last_errno, thd->net.last_error);
++    thd->clear_error();
++    DBUG_RETURN(0);
++  }
++  else if (!tables->view)
++  {
++    List<FOREIGN_KEY_INFO> f_key_list;
++    TABLE *show_table= tables->table;
++    KEY *key_info=show_table->key_info;
++    uint primary_key= show_table->s->primary_key;
++    show_table->file->info(HA_STATUS_VARIABLE | 
++                           HA_STATUS_NO_LOCK |
++                           HA_STATUS_TIME);
++    for (uint i=0 ; i < show_table->s->keys ; i++, key_info++)
++    {
++      if (i != primary_key && !(key_info->flags & HA_NOSAME))
++        continue;
++      uint f_idx= 0;
++      KEY_PART_INFO *key_part= key_info->key_part;
++      for (uint j=0 ; j < key_info->key_parts ; j++,key_part++)
++      {
++        if (key_part->field)
++        {
++          f_idx++;
++          restore_record(table, s->default_values);
++          store_key_column_usage(table, base_name, file_name,
++                                 key_info->name,
++                                 strlen(key_info->name), 
++                                 key_part->field->field_name, 
++                                 strlen(key_part->field->field_name),
++                                 (longlong) f_idx);
++          if (schema_table_store_record(thd, table))
++            DBUG_RETURN(1);
++        }
++      }
++    }
++
++    show_table->file->get_foreign_key_list(thd, &f_key_list);
++    FOREIGN_KEY_INFO *f_key_info;
++    List_iterator_fast<FOREIGN_KEY_INFO> it(f_key_list);
++    while ((f_key_info= it++))
++    {
++      LEX_STRING *f_info;
++      LEX_STRING *r_info;
++      List_iterator_fast<LEX_STRING> it(f_key_info->foreign_fields),
++        it1(f_key_info->referenced_fields);
++      uint f_idx= 0;
++      while ((f_info= it++))
++      {
++        r_info= it1++;
++        f_idx++;
++        restore_record(table, s->default_values);
++        store_key_column_usage(table, base_name, file_name,
++                               f_key_info->forein_id->str,
++                               f_key_info->forein_id->length,
++                               f_info->str, f_info->length,
++                               (longlong) f_idx);
++        table->field[8]->store((longlong) f_idx, TRUE);
++        table->field[8]->set_notnull();
++        table->field[9]->store(f_key_info->referenced_db->str,
++                               f_key_info->referenced_db->length,
++                               system_charset_info);
++        table->field[9]->set_notnull();
++        table->field[10]->store(f_key_info->referenced_table->str,
++                                f_key_info->referenced_table->length, 
++                                system_charset_info);
++        table->field[10]->set_notnull();
++        table->field[11]->store(r_info->str, r_info->length,
++                                system_charset_info);
++        table->field[11]->set_notnull();
++        if (schema_table_store_record(thd, table))
++          DBUG_RETURN(1);
++      }
++    }
++  }
++  DBUG_RETURN(res);
++}
++
++
++int fill_open_tables(THD *thd, TABLE_LIST *tables, COND *cond)
++{
++  DBUG_ENTER("fill_open_tables");
++  const char *wild= thd->lex->wild ? thd->lex->wild->ptr() : NullS;
++  TABLE *table= tables->table;
++  CHARSET_INFO *cs= system_charset_info;
++  OPEN_TABLE_LIST *open_list;
++  if (!(open_list=list_open_tables(thd,thd->lex->select_lex.db, wild))
++            && thd->is_fatal_error)
++    DBUG_RETURN(1);
++
++  for (; open_list ; open_list=open_list->next)
++  {
++    restore_record(table, s->default_values);
++    table->field[0]->store(open_list->db, strlen(open_list->db), cs);
++    table->field[1]->store(open_list->table, strlen(open_list->table), cs);
++    table->field[2]->store((longlong) open_list->in_use, TRUE);
++    table->field[3]->store((longlong) open_list->locked, TRUE);
++    if (schema_table_store_record(thd, table))
++      DBUG_RETURN(1);
++  }
++  DBUG_RETURN(0);
++}
++
++
++int fill_variables(THD *thd, TABLE_LIST *tables, COND *cond)
++{
++  DBUG_ENTER("fill_variables");
++  int res= 0;
++  LEX *lex= thd->lex;
++  const char *wild= lex->wild ? lex->wild->ptr() : NullS;
++  pthread_mutex_lock(&LOCK_global_system_variables);
++  res= show_status_array(thd, wild, init_vars, 
++                         lex->option_type, 0, "", tables->table);
++  pthread_mutex_unlock(&LOCK_global_system_variables);
++  DBUG_RETURN(res);
++}
++
++
++int fill_status(THD *thd, TABLE_LIST *tables, COND *cond)
++{
++  DBUG_ENTER("fill_status");
++  LEX *lex= thd->lex;
++  const char *wild= lex->wild ? lex->wild->ptr() : NullS;
++  int res= 0;
++  STATUS_VAR tmp;
++  ha_update_statistics();                    /* Export engines statistics */
++  pthread_mutex_lock(&LOCK_status);
++  if (lex->option_type == OPT_GLOBAL)
++    calc_sum_of_all_status(&tmp);
++  res= show_status_array(thd, wild, status_vars, OPT_GLOBAL,
++                         (lex->option_type == OPT_GLOBAL ? 
++                          &tmp: &thd->status_var), "",tables->table);
++  pthread_mutex_unlock(&LOCK_status);
++  DBUG_RETURN(res);
++}
++
++
++/*
++  Find schema_tables elment by name
++
++  SYNOPSIS
++    find_schema_table()
++    thd                 thread handler
++    table_name          table name
++
++  RETURN
++    0	table not found
++    #   pointer to 'shema_tables' element
++*/
++
++ST_SCHEMA_TABLE *find_schema_table(THD *thd, const char* table_name)
++{
++  ST_SCHEMA_TABLE *schema_table= schema_tables;
++  for (; schema_table->table_name; schema_table++)
++  {
++    if (!my_strcasecmp(system_charset_info,
++                       schema_table->table_name,
++                       table_name))
++      return schema_table;
++  }
++  return 0;
++}
++
++
++ST_SCHEMA_TABLE *get_schema_table(enum enum_schema_tables schema_table_idx)
++{
++  return &schema_tables[schema_table_idx];
++}
++
++
++/*
++  Create information_schema table using schema_table data
++
++  SYNOPSIS
++    create_schema_table()
++    thd	       	          thread handler
++    schema_table          pointer to 'shema_tables' element
++
++  RETURN
++    #	                  Pointer to created table
++    0	                  Can't create table
++*/
++
++TABLE *create_schema_table(THD *thd, TABLE_LIST *table_list)
++{
++  int field_count= 0;
++  Item *item;
++  TABLE *table;
++  List<Item> field_list;
++  ST_SCHEMA_TABLE *schema_table= table_list->schema_table;
++  ST_FIELD_INFO *fields_info= schema_table->fields_info;
++  CHARSET_INFO *cs= system_charset_info;
++  DBUG_ENTER("create_schema_table");
++
++  for (; fields_info->field_name; fields_info++)
++  {
++    switch (fields_info->field_type) {
++    case MYSQL_TYPE_LONG:
++      if (!(item= new Item_int(fields_info->field_name,
++                               fields_info->value,
++                               fields_info->field_length)))
++      {
++        DBUG_RETURN(0);
++      }
++      break;
++    case MYSQL_TYPE_TIMESTAMP:
++      if (!(item=new Item_datetime(fields_info->field_name)))
++      {
++        DBUG_RETURN(0);
++      }
++      break;
++    default:
++      /* this should be changed when Item_empty_string is fixed(in 4.1) */
++      if (!(item= new Item_empty_string("", 0, cs)))
++      {
++        DBUG_RETURN(0);
++      }
++      item->max_length= fields_info->field_length * cs->mbmaxlen;
++      item->set_name(fields_info->field_name,
++                     strlen(fields_info->field_name), cs);
++      break;
++    }
++    field_list.push_back(item);
++    item->maybe_null= fields_info->maybe_null;
++    field_count++;
++  }
++  TMP_TABLE_PARAM *tmp_table_param =
++    (TMP_TABLE_PARAM*) (thd->calloc(sizeof(TMP_TABLE_PARAM)));
++  tmp_table_param->init();
++  tmp_table_param->table_charset= cs;
++  tmp_table_param->field_count= field_count;
++  tmp_table_param->schema_table= 1;
++  SELECT_LEX *select_lex= thd->lex->current_select;
++  if (!(table= create_tmp_table(thd, tmp_table_param,
++                                field_list, (ORDER*) 0, 0, 0, 
++                                (select_lex->options | thd->options |
++                                 TMP_TABLE_ALL_COLUMNS),
++                                HA_POS_ERROR, table_list->alias)))
++    DBUG_RETURN(0);
++  table_list->schema_table_param= tmp_table_param;
++  DBUG_RETURN(table);
++}
++
++
++/*
++  For old SHOW compatibility. It is used when
++  old SHOW doesn't have generated column names
++  Make list of fields for SHOW
++
++  SYNOPSIS
++    make_old_format()
++    thd			thread handler
++    schema_table        pointer to 'schema_tables' element
++
++  RETURN
++   -1	errror
++    0	success
++*/
++
++int make_old_format(THD *thd, ST_SCHEMA_TABLE *schema_table)
++{
++  ST_FIELD_INFO *field_info= schema_table->fields_info;
++  Name_resolution_context *context= &thd->lex->select_lex.context;
++  for (; field_info->field_name; field_info++)
++  {
++    if (field_info->old_name)
++    {
++      Item_field *field= new Item_field(context,
++                                        NullS, NullS, field_info->field_name);
++      if (field)
++      {
++        field->set_name(field_info->old_name,
++                        strlen(field_info->old_name),
++                        system_charset_info);
++        if (add_item_to_list(thd, field))
++          return 1;
++      }
++    }
++  }
++  return 0;
++}
++
++
++int make_schemata_old_format(THD *thd, ST_SCHEMA_TABLE *schema_table)
++{
++  char tmp[128];
++  LEX *lex= thd->lex;
++  SELECT_LEX *sel= lex->current_select;
++  Name_resolution_context *context= &sel->context;
++
++  if (!sel->item_list.elements)
++  {
++    ST_FIELD_INFO *field_info= &schema_table->fields_info[1];
++    String buffer(tmp,sizeof(tmp), system_charset_info);
++    Item_field *field= new Item_field(context,
++                                      NullS, NullS, field_info->field_name);
++    if (!field || add_item_to_list(thd, field))
++      return 1;
++    buffer.length(0);
++    buffer.append(field_info->old_name);
++    if (lex->wild && lex->wild->ptr())
++    {
++      buffer.append(STRING_WITH_LEN(" ("));
++      buffer.append(lex->wild->ptr());
++      buffer.append(')');
++    }
++    field->set_name(buffer.ptr(), buffer.length(), system_charset_info);
++  }
++  return 0;
++}
++
++
++int make_table_names_old_format(THD *thd, ST_SCHEMA_TABLE *schema_table)
++{
++  char tmp[128];
++  String buffer(tmp,sizeof(tmp), thd->charset());
++  LEX *lex= thd->lex;
++  Name_resolution_context *context= &lex->select_lex.context;
++
++  ST_FIELD_INFO *field_info= &schema_table->fields_info[2];
++  buffer.length(0);
++  buffer.append(field_info->old_name);
++  buffer.append(lex->select_lex.db);
++  if (lex->wild && lex->wild->ptr())
++  {
++    buffer.append(STRING_WITH_LEN(" ("));
++    buffer.append(lex->wild->ptr());
++    buffer.append(')');
++  }
++  Item_field *field= new Item_field(context,
++                                    NullS, NullS, field_info->field_name);
++  if (add_item_to_list(thd, field))
++    return 1;
++  field->set_name(buffer.ptr(), buffer.length(), system_charset_info);
++  if (thd->lex->verbose)
++  {
++    field->set_name(buffer.ptr(), buffer.length(), system_charset_info);
++    field_info= &schema_table->fields_info[3];
++    field= new Item_field(context, NullS, NullS, field_info->field_name);
++    if (add_item_to_list(thd, field))
++      return 1;
++    field->set_name(field_info->old_name, strlen(field_info->old_name),
++                    system_charset_info);
++  }
++  return 0;
++}
++
++
++int make_columns_old_format(THD *thd, ST_SCHEMA_TABLE *schema_table)
++{
++  int fields_arr[]= {3, 14, 13, 6, 15, 5, 16, 17, 18, -1};
++  int *field_num= fields_arr;
++  ST_FIELD_INFO *field_info;
++  Name_resolution_context *context= &thd->lex->select_lex.context;
++
++  for (; *field_num >= 0; field_num++)
++  {
++    field_info= &schema_table->fields_info[*field_num];
++    if (!thd->lex->verbose && (*field_num == 13 ||
++                               *field_num == 17 ||
++                               *field_num == 18))
++      continue;
++    Item_field *field= new Item_field(context,
++                                      NullS, NullS, field_info->field_name);
++    if (field)
++    {
++      field->set_name(field_info->old_name,
++                      strlen(field_info->old_name),
++                      system_charset_info);
++      if (add_item_to_list(thd, field))
++        return 1;
++    }
++  }
++  return 0;
++}
++
++
++int make_character_sets_old_format(THD *thd, ST_SCHEMA_TABLE *schema_table)
++{
++  int fields_arr[]= {0, 2, 1, 3, -1};
++  int *field_num= fields_arr;
++  ST_FIELD_INFO *field_info;
++  Name_resolution_context *context= &thd->lex->select_lex.context;
++
++  for (; *field_num >= 0; field_num++)
++  {
++    field_info= &schema_table->fields_info[*field_num];
++    Item_field *field= new Item_field(context,
++                                      NullS, NullS, field_info->field_name);
++    if (field)
++    {
++      field->set_name(field_info->old_name,
++                      strlen(field_info->old_name),
++                      system_charset_info);
++      if (add_item_to_list(thd, field))
++        return 1;
++    }
++  }
++  return 0;
++}
++
++
++int make_proc_old_format(THD *thd, ST_SCHEMA_TABLE *schema_table)
++{
++  int fields_arr[]= {2, 3, 4, 19, 16, 15, 14, 18, -1};
++  int *field_num= fields_arr;
++  ST_FIELD_INFO *field_info;
++  Name_resolution_context *context= &thd->lex->select_lex.context;
++
++  for (; *field_num >= 0; field_num++)
++  {
++    field_info= &schema_table->fields_info[*field_num];
++    Item_field *field= new Item_field(context,
++                                      NullS, NullS, field_info->field_name);
++    if (field)
++    {
++      field->set_name(field_info->old_name,
++                      strlen(field_info->old_name),
++                      system_charset_info);
++      if (add_item_to_list(thd, field))
++        return 1;
++    }
++  }
++  return 0;
++}
++
++
++/*
++  Create information_schema table
++
++  SYNOPSIS
++  mysql_schema_table()
++    thd                thread handler
++    lex                pointer to LEX
++    table_list         pointer to table_list
++
++  RETURN
++    0	success
++    1   error
++*/
++
++int mysql_schema_table(THD *thd, LEX *lex, TABLE_LIST *table_list)
++{
++  TABLE *table;
++  DBUG_ENTER("mysql_schema_table");
++  if (!(table= table_list->schema_table->create_table(thd, table_list)))
++  {
++    DBUG_RETURN(1);
++  }
++  table->s->tmp_table= SYSTEM_TMP_TABLE;
++  table->grant.privilege= SELECT_ACL;
++  /*
++    This test is necessary to make
++    case insensitive file systems +
++    upper case table names(information schema tables) +
++    views
++    working correctly
++  */
++  if (table_list->schema_table_name)
++    table->alias_name_used= my_strcasecmp(table_alias_charset,
++                                          table_list->schema_table_name,
++                                          table_list->alias);
++  table_list->table_name= (char*) table->s->table_name;
++  table_list->table_name_length= strlen(table->s->table_name);
++  table_list->table= table;
++  table->next= thd->derived_tables;
++  thd->derived_tables= table;
++  table_list->select_lex->options |= OPTION_SCHEMA_TABLE;
++  lex->safe_to_cache_query= 0;
++
++  if (table_list->schema_table_reformed) // show command
++  {
++    SELECT_LEX *sel= lex->current_select;
++    Item *item;
++    Field_translator *transl, *org_transl;
++
++    if (table_list->field_translation)
++    {
++      Field_translator *end= table_list->field_translation_end;
++      for (transl= table_list->field_translation; transl < end; transl++)
++      {
++        if (!transl->item->fixed &&
++            transl->item->fix_fields(thd, &transl->item))
++          DBUG_RETURN(1);
++      }
++      DBUG_RETURN(0);
++    }
++    List_iterator_fast<Item> it(sel->item_list);
++    if (!(transl=
++          (Field_translator*)(thd->stmt_arena->
++                              alloc(sel->item_list.elements *
++                                    sizeof(Field_translator)))))
++    {
++      DBUG_RETURN(1);
++    }
++    for (org_transl= transl; (item= it++); transl++)
++    {
++      transl->item= item;
++      transl->name= item->name;
++      if (!item->fixed && item->fix_fields(thd, &transl->item))
++      {
++        DBUG_RETURN(1);
++      }
++    }
++    table_list->field_translation= org_transl;
++    table_list->field_translation_end= transl;
++  }
++
++  DBUG_RETURN(0);
++}
++
++
++/*
++  Generate select from information_schema table
++
++  SYNOPSIS
++    make_schema_select()
++    thd                  thread handler
++    sel                  pointer to SELECT_LEX
++    schema_table_idx     index of 'schema_tables' element
++
++  RETURN
++    0	success
++    1   error
++*/
++
++int make_schema_select(THD *thd, SELECT_LEX *sel,
++		       enum enum_schema_tables schema_table_idx)
++{
++  ST_SCHEMA_TABLE *schema_table= get_schema_table(schema_table_idx);
++  LEX_STRING db, table;
++  DBUG_ENTER("mysql_schema_select");
++  /*
++     We have to make non const db_name & table_name
++     because of lower_case_table_names
++  */
++  make_lex_string(thd, &db, information_schema_name.str,
++                  information_schema_name.length, 0);
++  make_lex_string(thd, &table, schema_table->table_name,
++                  strlen(schema_table->table_name), 0);
++  if (schema_table->old_format(thd, schema_table) ||   /* Handle old syntax */
++      !sel->add_table_to_list(thd, new Table_ident(thd, db, table, 0),
++                              0, 0, TL_READ, (List<String> *) 0,
++                              (List<String> *) 0))
++  {
++    DBUG_RETURN(1);
++  }
++  DBUG_RETURN(0);
++}
++
++
++/*
++  Fill temporary schema tables before SELECT
++
++  SYNOPSIS
++    get_schema_tables_result()
++    join  join which use schema tables
++    executed_place place where I_S table processed
++
++  RETURN
++    FALSE success
++    TRUE  error
++*/
++
++bool get_schema_tables_result(JOIN *join,
++                              enum enum_schema_table_state executed_place)
++{
++  JOIN_TAB *tmp_join_tab= join->join_tab+join->tables;
++  THD *thd= join->thd;
++  LEX *lex= thd->lex;
++  bool result= 0;
++  DBUG_ENTER("get_schema_tables_result");
++
++  thd->no_warnings_for_error= 1;
++  for (JOIN_TAB *tab= join->join_tab; tab < tmp_join_tab; tab++)
++  {  
++    if (!tab->table || !tab->table->pos_in_table_list)
++      break;
++
++    TABLE_LIST *table_list= tab->table->pos_in_table_list;
++    if (table_list->schema_table && thd->fill_derived_tables())
++    {
++      bool is_subselect= (&lex->unit != lex->current_select->master_unit() &&
++                          lex->current_select->master_unit()->item);
++      /*
++        If schema table is already processed and
++        the statement is not a subselect then
++        we don't need to fill this table again.
++        If schema table is already processed and
++        schema_table_state != executed_place then
++        table is already processed and
++        we should skip second data processing.
++      */
++      if (table_list->schema_table_state &&
++          (!is_subselect || table_list->schema_table_state != executed_place))
++        continue;
++
++      /*
++        if table is used in a subselect and
++        table has been processed earlier with the same
++        'executed_place' value then we should refresh the table.
++      */
++      if (table_list->schema_table_state && is_subselect)
++      {
++        table_list->table->file->extra(HA_EXTRA_RESET_STATE);
++        table_list->table->file->delete_all_rows();
++        free_io_cache(table_list->table);
++        filesort_free_buffers(table_list->table,1);
++        table_list->table->null_row= 0;
++      }
++      else
++        table_list->table->file->records= 0;
++
++      if (table_list->schema_table->fill_table(thd, table_list,
++                                               tab->select_cond))
++      {
++        result= 1;
++        join->error= 1;
++        table_list->schema_table_state= executed_place;
++        break;
++      }
++      table_list->schema_table_state= executed_place;
++    }
++  }
++  thd->no_warnings_for_error= 0;
++  DBUG_RETURN(result);
++}
++
++
++ST_FIELD_INFO schema_fields_info[]=
++{
++  {"CATALOG_NAME", FN_REFLEN, MYSQL_TYPE_STRING, 0, 1, 0},
++  {"SCHEMA_NAME", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, "Database"},
++  {"DEFAULT_CHARACTER_SET_NAME", 64, MYSQL_TYPE_STRING, 0, 0, 0},
++  {"DEFAULT_COLLATION_NAME", 64, MYSQL_TYPE_STRING, 0, 0, 0},
++  {"SQL_PATH", FN_REFLEN, MYSQL_TYPE_STRING, 0, 1, 0},
++  {0, 0, MYSQL_TYPE_STRING, 0, 0, 0}
++};
++
++
++ST_FIELD_INFO tables_fields_info[]=
++{
++  {"TABLE_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 1, 0},
++  {"TABLE_SCHEMA",NAME_LEN, MYSQL_TYPE_STRING, 0, 0, 0},
++  {"TABLE_NAME", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, "Name"},
++  {"TABLE_TYPE", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, 0},
++  {"ENGINE", NAME_LEN, MYSQL_TYPE_STRING, 0, 1, "Engine"},
++  {"VERSION", 21 , MYSQL_TYPE_LONG, 0, 1, "Version"},
++  {"ROW_FORMAT", 10, MYSQL_TYPE_STRING, 0, 1, "Row_format"},
++  {"TABLE_ROWS", 21 , MYSQL_TYPE_LONG, 0, 1, "Rows"},
++  {"AVG_ROW_LENGTH", 21 , MYSQL_TYPE_LONG, 0, 1, "Avg_row_length"},
++  {"DATA_LENGTH", 21 , MYSQL_TYPE_LONG, 0, 1, "Data_length"},
++  {"MAX_DATA_LENGTH", 21 , MYSQL_TYPE_LONG, 0, 1, "Max_data_length"},
++  {"INDEX_LENGTH", 21 , MYSQL_TYPE_LONG, 0, 1, "Index_length"},
++  {"DATA_FREE", 21 , MYSQL_TYPE_LONG, 0, 1, "Data_free"},
++  {"AUTO_INCREMENT", 21 , MYSQL_TYPE_LONG, 0, 1, "Auto_increment"},
++  {"CREATE_TIME", 0, MYSQL_TYPE_TIMESTAMP, 0, 1, "Create_time"},
++  {"UPDATE_TIME", 0, MYSQL_TYPE_TIMESTAMP, 0, 1, "Update_time"},
++  {"CHECK_TIME", 0, MYSQL_TYPE_TIMESTAMP, 0, 1, "Check_time"},
++  {"TABLE_COLLATION", 64, MYSQL_TYPE_STRING, 0, 1, "Collation"},
++  {"CHECKSUM", 21 , MYSQL_TYPE_LONG, 0, 1, "Checksum"},
++  {"CREATE_OPTIONS", 255, MYSQL_TYPE_STRING, 0, 1, "Create_options"},
++  {"TABLE_COMMENT", 80, MYSQL_TYPE_STRING, 0, 0, "Comment"},
++  {0, 0, MYSQL_TYPE_STRING, 0, 0, 0}
++};
++
++
++ST_FIELD_INFO columns_fields_info[]=
++{
++  {"TABLE_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 1, 0},
++  {"TABLE_SCHEMA", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, 0},
++  {"TABLE_NAME", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, 0},
++  {"COLUMN_NAME", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, "Field"},
++  {"ORDINAL_POSITION", 21 , MYSQL_TYPE_LONG, 0, 0, 0},
++  {"COLUMN_DEFAULT", MAX_FIELD_VARCHARLENGTH, MYSQL_TYPE_STRING, 0, 1, "Default"},
++  {"IS_NULLABLE", 3, MYSQL_TYPE_STRING, 0, 0, "Null"},
++  {"DATA_TYPE", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, 0},
++  {"CHARACTER_MAXIMUM_LENGTH", 21 , MYSQL_TYPE_LONG, 0, 1, 0},
++  {"CHARACTER_OCTET_LENGTH", 21 , MYSQL_TYPE_LONG, 0, 1, 0},
++  {"NUMERIC_PRECISION", 21 , MYSQL_TYPE_LONG, 0, 1, 0},
++  {"NUMERIC_SCALE", 21 , MYSQL_TYPE_LONG, 0, 1, 0},
++  {"CHARACTER_SET_NAME", 64, MYSQL_TYPE_STRING, 0, 1, 0},
++  {"COLLATION_NAME", 64, MYSQL_TYPE_STRING, 0, 1, "Collation"},
++  {"COLUMN_TYPE", 65535, MYSQL_TYPE_STRING, 0, 0, "Type"},
++  {"COLUMN_KEY", 3, MYSQL_TYPE_STRING, 0, 0, "Key"},
++  {"EXTRA", 20, MYSQL_TYPE_STRING, 0, 0, "Extra"},
++  {"PRIVILEGES", 80, MYSQL_TYPE_STRING, 0, 0, "Privileges"},
++  {"COLUMN_COMMENT", 255, MYSQL_TYPE_STRING, 0, 0, "Comment"},
++  {0, 0, MYSQL_TYPE_STRING, 0, 0, 0}
++};
++
++
++ST_FIELD_INFO charsets_fields_info[]=
++{
++  {"CHARACTER_SET_NAME", 64, MYSQL_TYPE_STRING, 0, 0, "Charset"},
++  {"DEFAULT_COLLATE_NAME", 64, MYSQL_TYPE_STRING, 0, 0, "Default collation"},
++  {"DESCRIPTION", 60, MYSQL_TYPE_STRING, 0, 0, "Description"},
++  {"MAXLEN", 3 ,MYSQL_TYPE_LONG, 0, 0, "Maxlen"},
++  {0, 0, MYSQL_TYPE_STRING, 0, 0, 0}
++};
++
++
++ST_FIELD_INFO collation_fields_info[]=
++{
++  {"COLLATION_NAME", 64, MYSQL_TYPE_STRING, 0, 0, "Collation"},
++  {"CHARACTER_SET_NAME", 64, MYSQL_TYPE_STRING, 0, 0, "Charset"},
++  {"ID", 11, MYSQL_TYPE_LONG, 0, 0, "Id"},
++  {"IS_DEFAULT", 3, MYSQL_TYPE_STRING, 0, 0, "Default"},
++  {"IS_COMPILED", 3, MYSQL_TYPE_STRING, 0, 0, "Compiled"},
++  {"SORTLEN", 3 ,MYSQL_TYPE_LONG, 0, 0, "Sortlen"},
++  {0, 0, MYSQL_TYPE_STRING, 0, 0, 0}
++};
++
++
++ST_FIELD_INFO coll_charset_app_fields_info[]=
++{
++  {"COLLATION_NAME", 64, MYSQL_TYPE_STRING, 0, 0, 0},
++  {"CHARACTER_SET_NAME", 64, MYSQL_TYPE_STRING, 0, 0, 0},
++  {0, 0, MYSQL_TYPE_STRING, 0, 0, 0}
++};
++
++
++ST_FIELD_INFO proc_fields_info[]=
++{
++  {"SPECIFIC_NAME", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, 0},
++  {"ROUTINE_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 1, 0},
++  {"ROUTINE_SCHEMA", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, "Db"},
++  {"ROUTINE_NAME", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, "Name"},
++  {"ROUTINE_TYPE", 9, MYSQL_TYPE_STRING, 0, 0, "Type"},
++  {"DTD_IDENTIFIER", NAME_LEN, MYSQL_TYPE_STRING, 0, 1, 0},
++  {"ROUTINE_BODY", 8, MYSQL_TYPE_STRING, 0, 0, 0},
++  {"ROUTINE_DEFINITION", 65535, MYSQL_TYPE_STRING, 0, 1, 0},
++  {"EXTERNAL_NAME", NAME_LEN, MYSQL_TYPE_STRING, 0, 1, 0},
++  {"EXTERNAL_LANGUAGE", NAME_LEN, MYSQL_TYPE_STRING, 0, 1, 0},
++  {"PARAMETER_STYLE", 8, MYSQL_TYPE_STRING, 0, 0, 0},
++  {"IS_DETERMINISTIC", 3, MYSQL_TYPE_STRING, 0, 0, 0},
++  {"SQL_DATA_ACCESS", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, 0},
++  {"SQL_PATH", NAME_LEN, MYSQL_TYPE_STRING, 0, 1, 0},
++  {"SECURITY_TYPE", 7, MYSQL_TYPE_STRING, 0, 0, "Security_type"},
++  {"CREATED", 0, MYSQL_TYPE_TIMESTAMP, 0, 0, "Created"},
++  {"LAST_ALTERED", 0, MYSQL_TYPE_TIMESTAMP, 0, 0, "Modified"},
++  {"SQL_MODE", 65535, MYSQL_TYPE_STRING, 0, 0, 0},
++  {"ROUTINE_COMMENT", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, "Comment"},
++  {"DEFINER", 77, MYSQL_TYPE_STRING, 0, 0, "Definer"},
++  {0, 0, MYSQL_TYPE_STRING, 0, 0, 0}
++};
++
++
++ST_FIELD_INFO stat_fields_info[]=
++{
++  {"TABLE_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 1, 0},
++  {"TABLE_SCHEMA", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, 0},
++  {"TABLE_NAME", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, "Table"},
++  {"NON_UNIQUE", 1, MYSQL_TYPE_LONG, 0, 0, "Non_unique"},
++  {"INDEX_SCHEMA", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, 0},
++  {"INDEX_NAME", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, "Key_name"},
++  {"SEQ_IN_INDEX", 2, MYSQL_TYPE_LONG, 0, 0, "Seq_in_index"},
++  {"COLUMN_NAME", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, "Column_name"},
++  {"COLLATION", 1, MYSQL_TYPE_STRING, 0, 1, "Collation"},
++  {"CARDINALITY", 21, MYSQL_TYPE_LONG, 0, 1, "Cardinality"},
++  {"SUB_PART", 3, MYSQL_TYPE_LONG, 0, 1, "Sub_part"},
++  {"PACKED", 10, MYSQL_TYPE_STRING, 0, 1, "Packed"},
++  {"NULLABLE", 3, MYSQL_TYPE_STRING, 0, 0, "Null"},
++  {"INDEX_TYPE", 16, MYSQL_TYPE_STRING, 0, 0, "Index_type"},
++  {"COMMENT", 16, MYSQL_TYPE_STRING, 0, 1, "Comment"},
++  {0, 0, MYSQL_TYPE_STRING, 0, 0, 0}
++};
++
++
++ST_FIELD_INFO view_fields_info[]=
++{
++  {"TABLE_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 1, 0},
++  {"TABLE_SCHEMA", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, 0},
++  {"TABLE_NAME", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, 0},
++  {"VIEW_DEFINITION", 65535, MYSQL_TYPE_STRING, 0, 0, 0},
++  {"CHECK_OPTION", 8, MYSQL_TYPE_STRING, 0, 0, 0},
++  {"IS_UPDATABLE", 3, MYSQL_TYPE_STRING, 0, 0, 0},
++  {"DEFINER", 77, MYSQL_TYPE_STRING, 0, 0, 0},
++  {"SECURITY_TYPE", 7, MYSQL_TYPE_STRING, 0, 0, 0},
++  {0, 0, MYSQL_TYPE_STRING, 0, 0, 0}
++};
++
++
++ST_FIELD_INFO user_privileges_fields_info[]=
++{
++  {"GRANTEE", 81, MYSQL_TYPE_STRING, 0, 0, 0},
++  {"TABLE_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 1, 0},
++  {"PRIVILEGE_TYPE", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, 0},
++  {"IS_GRANTABLE", 3, MYSQL_TYPE_STRING, 0, 0, 0},
++  {0, 0, MYSQL_TYPE_STRING, 0, 0, 0}
++};
++
++
++ST_FIELD_INFO schema_privileges_fields_info[]=
++{
++  {"GRANTEE", 81, MYSQL_TYPE_STRING, 0, 0, 0},
++  {"TABLE_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 1, 0},
++  {"TABLE_SCHEMA", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, 0},
++  {"PRIVILEGE_TYPE", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, 0},
++  {"IS_GRANTABLE", 3, MYSQL_TYPE_STRING, 0, 0, 0},
++  {0, 0, MYSQL_TYPE_STRING, 0, 0, 0}
++};
++
++
++ST_FIELD_INFO table_privileges_fields_info[]=
++{
++  {"GRANTEE", 81, MYSQL_TYPE_STRING, 0, 0, 0},
++  {"TABLE_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 1, 0},
++  {"TABLE_SCHEMA", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, 0},
++  {"TABLE_NAME", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, 0},
++  {"PRIVILEGE_TYPE", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, 0},
++  {"IS_GRANTABLE", 3, MYSQL_TYPE_STRING, 0, 0, 0},
++  {0, 0, MYSQL_TYPE_STRING, 0, 0, 0}
++};
++
++
++ST_FIELD_INFO column_privileges_fields_info[]=
++{
++  {"GRANTEE", 81, MYSQL_TYPE_STRING, 0, 0, 0},
++  {"TABLE_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 1, 0},
++  {"TABLE_SCHEMA", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, 0},
++  {"TABLE_NAME", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, 0},
++  {"COLUMN_NAME", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, 0},
++  {"PRIVILEGE_TYPE", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, 0},
++  {"IS_GRANTABLE", 3, MYSQL_TYPE_STRING, 0, 0, 0},
++  {0, 0, MYSQL_TYPE_STRING, 0, 0, 0}
++};
++
++
++ST_FIELD_INFO table_constraints_fields_info[]=
++{
++  {"CONSTRAINT_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 1, 0},
++  {"CONSTRAINT_SCHEMA", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, 0},
++  {"CONSTRAINT_NAME", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, 0},
++  {"TABLE_SCHEMA", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, 0},
++  {"TABLE_NAME", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, 0},
++  {"CONSTRAINT_TYPE", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, 0},
++  {0, 0, MYSQL_TYPE_STRING, 0, 0, 0}
++};
++
++
++ST_FIELD_INFO key_column_usage_fields_info[]=
++{
++  {"CONSTRAINT_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 1, 0},
++  {"CONSTRAINT_SCHEMA", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, 0},
++  {"CONSTRAINT_NAME", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, 0},
++  {"TABLE_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 1, 0},
++  {"TABLE_SCHEMA", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, 0},
++  {"TABLE_NAME", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, 0},
++  {"COLUMN_NAME", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, 0},
++  {"ORDINAL_POSITION", 10 ,MYSQL_TYPE_LONG, 0, 0, 0},
++  {"POSITION_IN_UNIQUE_CONSTRAINT", 10 ,MYSQL_TYPE_LONG, 0, 1, 0},
++  {"REFERENCED_TABLE_SCHEMA", NAME_LEN, MYSQL_TYPE_STRING, 0, 1, 0},
++  {"REFERENCED_TABLE_NAME", NAME_LEN, MYSQL_TYPE_STRING, 0, 1, 0},
++  {"REFERENCED_COLUMN_NAME", NAME_LEN, MYSQL_TYPE_STRING, 0, 1, 0},
++  {0, 0, MYSQL_TYPE_STRING, 0, 0, 0}
++};
++
++
++ST_FIELD_INFO table_names_fields_info[]=
++{
++  {"TABLE_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 1, 0},
++  {"TABLE_SCHEMA",NAME_LEN, MYSQL_TYPE_STRING, 0, 0, 0},
++  {"TABLE_NAME", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, "Tables_in_"},
++  {"TABLE_TYPE", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, "Table_type"},
++  {0, 0, MYSQL_TYPE_STRING, 0, 0, 0}
++};
++
++
++ST_FIELD_INFO open_tables_fields_info[]=
++{
++  {"Database", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, "Database"},
++  {"Table",NAME_LEN, MYSQL_TYPE_STRING, 0, 0, "Table"},
++  {"In_use", 1, MYSQL_TYPE_LONG, 0, 0, "In_use"},
++  {"Name_locked", 4, MYSQL_TYPE_LONG, 0, 0, "Name_locked"},
++  {0, 0, MYSQL_TYPE_STRING, 0, 0, 0}
++};
++
++
++ST_FIELD_INFO triggers_fields_info[]=
++{
++  {"TRIGGER_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 1, 0},
++  {"TRIGGER_SCHEMA",NAME_LEN, MYSQL_TYPE_STRING, 0, 0, 0},
++  {"TRIGGER_NAME", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, "Trigger"},
++  {"EVENT_MANIPULATION", 6, MYSQL_TYPE_STRING, 0, 0, "Event"},
++  {"EVENT_OBJECT_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 1, 0},
++  {"EVENT_OBJECT_SCHEMA",NAME_LEN, MYSQL_TYPE_STRING, 0, 0, 0},
++  {"EVENT_OBJECT_TABLE", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, "Table"},
++  {"ACTION_ORDER", 4, MYSQL_TYPE_LONG, 0, 0, 0},
++  {"ACTION_CONDITION", 65535, MYSQL_TYPE_STRING, 0, 1, 0},
++  {"ACTION_STATEMENT", 65535, MYSQL_TYPE_STRING, 0, 0, "Statement"},
++  {"ACTION_ORIENTATION", 9, MYSQL_TYPE_STRING, 0, 0, 0},
++  {"ACTION_TIMING", 6, MYSQL_TYPE_STRING, 0, 0, "Timing"},
++  {"ACTION_REFERENCE_OLD_TABLE", NAME_LEN, MYSQL_TYPE_STRING, 0, 1, 0},
++  {"ACTION_REFERENCE_NEW_TABLE", NAME_LEN, MYSQL_TYPE_STRING, 0, 1, 0},
++  {"ACTION_REFERENCE_OLD_ROW", 3, MYSQL_TYPE_STRING, 0, 0, 0},
++  {"ACTION_REFERENCE_NEW_ROW", 3, MYSQL_TYPE_STRING, 0, 0, 0},
++  {"CREATED", 0, MYSQL_TYPE_TIMESTAMP, 0, 1, "Created"},
++  {"SQL_MODE", 65535, MYSQL_TYPE_STRING, 0, 0, "sql_mode"},
++  {"DEFINER", 65535, MYSQL_TYPE_STRING, 0, 0, "Definer"},
++  {0, 0, MYSQL_TYPE_STRING, 0, 0, 0}
++};
++
++
++ST_FIELD_INFO variables_fields_info[]=
++{
++  {"Variable_name", 80, MYSQL_TYPE_STRING, 0, 0, "Variable_name"},
++  {"Value", FN_REFLEN, MYSQL_TYPE_STRING, 0, 0, "Value"},
++  {0, 0, MYSQL_TYPE_STRING, 0, 0, 0}
++};
++
++
++/*
++  Description of ST_FIELD_INFO in table.h
++*/
++
++ST_SCHEMA_TABLE schema_tables[]=
++{
++  {"CHARACTER_SETS", charsets_fields_info, create_schema_table, 
++   fill_schema_charsets, make_character_sets_old_format, 0, -1, -1, 0},
++  {"COLLATIONS", collation_fields_info, create_schema_table, 
++   fill_schema_collation, make_old_format, 0, -1, -1, 0},
++  {"COLLATION_CHARACTER_SET_APPLICABILITY", coll_charset_app_fields_info,
++   create_schema_table, fill_schema_coll_charset_app, 0, 0, -1, -1, 0},
++  {"COLUMNS", columns_fields_info, create_schema_table, 
++   get_all_tables, make_columns_old_format, get_schema_column_record, 1, 2, 0},
++  {"COLUMN_PRIVILEGES", column_privileges_fields_info, create_schema_table,
++    fill_schema_column_privileges, 0, 0, -1, -1, 0},
++  {"KEY_COLUMN_USAGE", key_column_usage_fields_info, create_schema_table,
++    get_all_tables, 0, get_schema_key_column_usage_record, 4, 5, 0},
++  {"OPEN_TABLES", open_tables_fields_info, create_schema_table,
++   fill_open_tables, make_old_format, 0, -1, -1, 1},
++  {"ROUTINES", proc_fields_info, create_schema_table, 
++    fill_schema_proc, make_proc_old_format, 0, -1, -1, 0},
++  {"SCHEMATA", schema_fields_info, create_schema_table,
++   fill_schema_shemata, make_schemata_old_format, 0, 1, -1, 0},
++  {"SCHEMA_PRIVILEGES", schema_privileges_fields_info, create_schema_table,
++    fill_schema_schema_privileges, 0, 0, -1, -1, 0},
++  {"STATISTICS", stat_fields_info, create_schema_table, 
++    get_all_tables, make_old_format, get_schema_stat_record, 1, 2, 0},
++  {"STATUS", variables_fields_info, create_schema_table, fill_status, 
++   make_old_format, 0, -1, -1, 1},
++  {"TABLES", tables_fields_info, create_schema_table, 
++   get_all_tables, make_old_format, get_schema_tables_record, 1, 2, 0},
++  {"TABLE_CONSTRAINTS", table_constraints_fields_info, create_schema_table,
++    get_all_tables, 0, get_schema_constraints_record, 3, 4, 0},
++  {"TABLE_NAMES", table_names_fields_info, create_schema_table,
++   get_all_tables, make_table_names_old_format, 0, 1, 2, 1},
++  {"TABLE_PRIVILEGES", table_privileges_fields_info, create_schema_table,
++    fill_schema_table_privileges, 0, 0, -1, -1, 0},
++  {"TRIGGERS", triggers_fields_info, create_schema_table,
++   get_all_tables, make_old_format, get_schema_triggers_record, 5, 6, 0},
++  {"USER_PRIVILEGES", user_privileges_fields_info, create_schema_table, 
++    fill_schema_user_privileges, 0, 0, -1, -1, 0},
++  {"VARIABLES", variables_fields_info, create_schema_table, fill_variables,
++   make_old_format, 0, -1, -1, 1},
++  {"VIEWS", view_fields_info, create_schema_table, 
++    get_all_tables, 0, get_schema_views_record, 1, 2, 0},
++  {0, 0, 0, 0, 0, 0, 0, 0, 0}
++};
++
++
++#ifdef HAVE_EXPLICIT_TEMPLATE_INSTANTIATION
++template class List_iterator_fast<char>;
++template class List<char>;
++#endif


Property changes on: branches/etch-5.0/debian/patches/92_SECURITY_CVE-2007-2691_thd_privs.dpatch
___________________________________________________________________
Name: svn:executable
   + *




More information about the Pkg-mysql-commits mailing list