[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, ¬_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(¤t_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(¤t_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 *)<_creator:(Comp_creator *)&ge_creator;
++}
++
++
++Comp_creator *comp_gt_creator(bool invert)
++{
++ return invert?(Comp_creator *)&le_creator:(Comp_creator *)>_creator;
++}
++
++
++Comp_creator *comp_le_creator(bool invert)
++{
++ return invert?(Comp_creator *)>_creator:(Comp_creator *)&le_creator;
++}
++
++
++Comp_creator *comp_lt_creator(bool invert)
++{
++ return invert?(Comp_creator *)&ge_creator:(Comp_creator *)<_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("e_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("e_char, 1, system_charset_info);
++ packet->append(name, length, packet->charset());
++ }
++ packet->append("e_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, ¬_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