sql_parse.cpp
上传用户:romrleung
上传日期:2022-05-23
资源大小:18897k
文件大小:167k
- /* 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 */
- #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
- #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 */
- #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);
- #endif
- static void decrease_user_connections(USER_CONN *uc);
- static bool check_db_used(THD *thd,TABLE_LIST *tables);
- static bool check_multi_update_lock(THD *thd, TABLE_LIST *tables,
- List<Item> *fields, SELECT_LEX *select_lex);
- static void remove_escape(char *name);
- static void refresh_status(void);
- static bool append_file_to_dir(THD *thd, const char **filename_ptr,
- const char *table_name);
-
- static TABLE_LIST* get_table_by_alias(TABLE_LIST* tl, const char* db,
- const char* alias);
- 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",
- "Error" // Last command number
- };
- static char empty_c_string[1]= {0}; // Used for not defined 'db'
- #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 %dn", 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 automaticly closed
- close_thread_tables(thd); // Free tables
- }
- }
- static bool end_active_trans(THD *thd)
- {
- int error=0;
- if (thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN |
- OPTION_TABLE_LOCK))
- {
- thd->options&= ~(ulong) (OPTION_BEGIN | OPTION_STATUS_NO_TRANS_UPDATE);
- thd->server_status&= ~SERVER_STATUS_IN_TRANS;
- if (ha_commit(thd))
- error=1;
- }
- 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 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[USERNAME_LENGTH+HOSTNAME_LENGTH+2];
- 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)))))
- {
- 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->user_len= user_len;
- uc->host= uc->user + uc->user_len + 1;
- uc->len= temp_len;
- uc->connections= 0;
- 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);
- 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;
- }
- /*
- Check if user exist and password supplied is correct.
- SYNOPSIS
- check_user()
- thd thread handle, thd->{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 recieved 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 depened 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->user, thd->master_access, thd->priv_user, thd->db and
- thd->db_access 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->master_access= GLOBAL_ACLS; // Full rights
- /* Change database if necessary: OK or FAIL is sent in mysql_change_db */
- if (db && db[0])
- {
- thd->db= 0;
- thd->db_length= 0;
- if (mysql_change_db(thd, db))
- {
- if (thd->user_connect)
- decrease_user_connections(thd->user_connect);
- DBUG_RETURN(-1);
- }
- }
- else
- 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(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 accidently free a wrong pointer
- if connect failed. Also in case of 'CHANGE USER' failure, current
- database will be switched to 'no database selected'.
- */
- thd->db= 0;
- thd->db_length= 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(thd, ER_SERVER_IS_IN_SECURE_AUTH_MODE,
- thd->user, thd->host_or_ip);
- mysql_log.write(thd, COM_CONNECT, ER(ER_SERVER_IS_IN_SECURE_AUTH_MODE),
- thd->user, thd->host_or_ip);
- DBUG_RETURN(-1);
- }
- if (send_old_password_request(thd) ||
- my_net_read(net) != SCRAMBLE_LENGTH_323 + 1) // We have to read very
- { // specific packet size
- 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->master_access & NO_ACCESS)) // authentification is OK
- {
- DBUG_PRINT("info",
- ("Capabilities: %d packet_length: %ld Host: '%s' "
- "Login user: '%s' Priv_user: '%s' Using password: %s "
- "Access: %u db: '%s'",
- thd->client_capabilities, thd->max_client_packet_length,
- thd->host_or_ip, thd->user, thd->priv_user,
- passwd_len ? "yes": "no",
- thd->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->master_access & SUPER_ACL);
- VOID(pthread_mutex_unlock(&LOCK_thread_count));
- if (!count_ok)
- { // too many connections
- 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->priv_user == thd->user ?
- (char*) "%s@%s on %s" :
- (char*) "%s@%s as anonymous on %s"),
- thd->user, thd->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->db_access=0;
- /* Don't allow user to connect if he has done too many queries */
- if ((ur.questions || ur.updates || ur.connections ||
- max_user_connections) &&
- get_or_create_user_conn(thd,thd->user,thd->host_or_ip,&ur))
- DBUG_RETURN(-1);
- if (thd->user_connect &&
- (thd->user_connect->user_resources.connections ||
- max_user_connections) &&
- check_for_max_user_connections(thd, thd->user_connect))
- DBUG_RETURN(-1);
- /* Change database if necessary: OK or FAIL is sent in mysql_change_db */
- if (db && db[0])
- {
- if (mysql_change_db(thd, db))
- {
- if (thd->user_connect)
- decrease_user_connections(thd->user_connect);
- DBUG_RETURN(-1);
- }
- }
- else
- 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(thd, ER_NOT_SUPPORTED_AUTH_MODE);
- mysql_log.write(thd,COM_CONNECT,ER(ER_NOT_SUPPORTED_AUTH_MODE));
- DBUG_RETURN(-1);
- }
- net_printf(thd, ER_ACCESS_DENIED_ERROR,
- thd->user,
- thd->host_or_ip,
- passwd_len ? ER(ER_YES) : ER(ER_NO));
- mysql_log.write(thd, COM_CONNECT, ER(ER_ACCESS_DENIED_ERROR),
- thd->user,
- thd->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)
- {
- (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);
- }
- /*
- 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 &&
- max_user_connections < (uint) uc->connections)
- {
- net_printf(thd,ER_TOO_MANY_USER_CONNECTIONS, uc->user);
- error=1;
- goto end;
- }
- time_out_user_resource_limits(thd, uc);
- if (uc->user_resources.connections &&
- uc->user_resources.connections <= uc->conn_per_hour)
- {
- net_printf(thd, ER_USER_LIMIT_REACHED, uc->user,
- "max_connections_per_hour",
- (long) uc->user_resources.connections);
- 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);
- }
- #endif /* NO_EMBEDDED_ACCESS_CHECKS */
- /*
- 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;
- }
- void free_max_user_conn(void)
- {
- hash_free(&hash_user_connections);
- }
- /*
- 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.
- */
- 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]=1;
- uc_update_queries[SQLCOM_INSERT]=1;
- uc_update_queries[SQLCOM_INSERT_SELECT]=1;
- uc_update_queries[SQLCOM_DELETE]=1;
- 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]=1;
- uc_update_queries[SQLCOM_REPLACE_SELECT]=1;
- 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_DELETE_MULTI]=1;
- uc_update_queries[SQLCOM_DROP_INDEX]=1;
- uc_update_queries[SQLCOM_UPDATE_MULTI]=1;
- }
- bool is_update_query(enum enum_sql_command command)
- {
- DBUG_ASSERT(command >= 0 && command <= SQLCOM_END);
- return uc_update_queries[command];
- }
- /*
- 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(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(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(THD *thd, 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[USERNAME_LENGTH+HOSTNAME_LENGTH+2];
- 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]=' '; 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 */
- }
- /*
- 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)));
- if (!thd->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->ip= my_strdup(ip,MYF(0))))
- return (ER_OUT_OF_RESOURCES);
- thd->host_or_ip= thd->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->host=ip_to_hostname(&thd->remote.sin_addr,&connect_errors);
- /* Cut very long hostnames to avoid possible overflows */
- if (thd->host)
- {
- if (thd->host != my_localhost)
- thd->host[min(strlen(thd->host), HOSTNAME_LENGTH)]= 0;
- thd->host_or_ip= thd->host;
- }
- if (connect_errors > max_connect_errors)
- return(ER_HOST_IS_BLOCKED);
- }
- DBUG_PRINT("info",("Host: %s ip: %s",
- thd->host ? thd->host : "unknown host",
- thd->ip ? thd->ip : "unknown ip"));
- if (acl_check_host(thd->host,thd->ip))
- return(ER_HOST_NOT_PRIVILEGED);
- }
- else /* Hostname given means that the connection was on a socket */
- {
- DBUG_PRINT("info",("Host: %s",thd->host));
- thd->host_or_ip= thd->host;
- thd->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 avalaible! */
- #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);
- #ifdef TO_BE_REMOVED_IN_4_1_RELEASE
- /*
- This is just a safety check against any client that would use the old
- CLIENT_CHANGE_USER flag
- */
- if ((thd->client_capabilities & CLIENT_PROTOCOL_41) &&
- !(thd->client_capabilities & (CLIENT_RESERVED |
- CLIENT_SECURE_CONNECTION |
- CLIENT_MULTI_RESULTS)))
- thd->client_capabilities&= ~CLIENT_PROTOCOL_41;
- #endif
- 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]));
- /*
- 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((uint) net->read_pos[8], 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;
- }
- 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: %d", 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 read user information (pkt_len= %lu)",
- pkt_len));
- 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;
- 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 ' '.
- */
- 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;
- /* 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, strlen(db),
- thd->charset(), &dummy_errors)]= 0;
- db= db_buff;
- }
- user_buff[copy_and_convert(user_buff, sizeof(user_buff)-1,
- system_charset_info, user, strlen(user),
- thd->charset(), &dummy_errors)]= ' ';
- user= user_buff;
- if (thd->user)
- x_free(thd->user);
- if (!(thd->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;
- 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_decl(handle_one_connection,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 %dn",
- thd->thread_id));
- // now that we've called my_thread_init(), it is safe to call DBUG_*
- #if defined(__WIN__)
- init_signals(); // IRENA; testing ?
- #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
- 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;
- thd->thread_stack= (char*) &thd;
- if ((error=check_connection(thd)))
- { // Wrong permissions
- if (error > 0)
- net_printf(thd,error,thd->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(thd->ip, thd->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->set_time();
- thd->init_for_queries();
- if (sys_init_connect.value_length && !(thd->master_access & SUPER_ACL))
- {
- execute_init_command(thd, &sys_init_connect, &LOCK_sys_init_connect);
- if (thd->query_error)
- thd->killed= 1;
- }
- while (!net->error && net->vio != 0 && !thd->killed)
- {
- if (do_command(thd))
- break;
- }
- if (thd->user_connect)
- decrease_user_connections(thd->user_connect);
- free_root(thd->mem_root,MYF(0));
- 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"),
- thd->user ? thd->user : "unauthenticated",
- thd->host_or_ip,
- (net->last_errno ? ER(net->last_errno) :
- ER(ER_UNKNOWN_ERROR)));
- 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;
- } 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
- */
- extern "C" pthread_handler_decl(handle_bootstrap,arg)
- {
- THD *thd=(THD*) arg;
- FILE *file=bootstrap_file;
- char *buff;
- /* The following must be called before DBUG_ENTER */
- 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->priv_user=thd->user=(char*) my_strdup("boot", MYF(MY_WME));
- 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))
- {
- send_error(thd, thd->net.last_errno, NullS);
- thd->is_fatal_error= 1;
- 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);
- thd->query[length] = ' ';
- thd->query_id=query_id++;
- if (mqh_used && thd->user_connect && check_mqh(thd, SQLCOM_END))
- {
- thd->net.error = 0;
- close_thread_tables(thd); // Free tables
- free_root(thd->mem_root,MYF(MY_KEEP_PREALLOC));
- break;
- }
- 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));
- free_root(&thd->transaction.mem_root,MYF(MY_KEEP_PREALLOC));
- }
- /* thd->fatal_error should be set in case something went wrong */
- end:
- #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); // Never reached
- }
- /* This works because items are allocated with sql_alloc() */
- void free_items(Item *item)
- {
- for (; item ; item=item->next)
- item->delete_self();
- }
- /* This works because items are allocated with sql_alloc() */
- void cleanup_items(Item *item)
- {
- for (; item ; item=item->next)
- item->cleanup();
- }
- int mysql_table_dump(THD* thd, char* db, char* tbl_name, int fd)
- {
- 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->real_name = table_list->alias = tbl_name;
- table_list->lock_type = TL_READ_NO_INSERT;
- table_list->next = 0;
- if (!db || check_db_name(db))
- {
- net_printf(thd,ER_WRONG_DB_NAME, db ? db : "NULL");
- goto err;
- }
- if (lower_case_table_names)
- my_casedn_str(files_charset_info, tbl_name);
- remove_escape(table_list->real_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, -1)))
- {
- my_error(ER_GET_ERRNO, MYF(0), my_errno);
- goto err;
- }
- net_flush(&thd->net);
- if ((error= table->file->dump(thd,fd)))
- my_error(ER_GET_ERRNO, MYF(0), error);
- err:
- close_thread_tables(thd);
- DBUG_RETURN(error);
- }
- #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.
- }
- 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");
- 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->set_time();
- VOID(pthread_mutex_lock(&LOCK_thread_count));
- thd->query_id=query_id;
- if (command != COM_STATISTICS && command != COM_PING)
- 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(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))
- mysql_log.write(thd,command,"%s",thd->db);
- 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;
- uint tbl_len= *(uchar*) (packet + db_len + 1);
- statistic_increment(com_other, &LOCK_status);
- thd->enable_slow_log= opt_log_slow_admin_statements;
- db= thd->alloc(db_len + tbl_len + 2);
- tbl_name= strmake(db, packet + 1, db_len)+1;
- strmake(tbl_name, packet + db_len + 2, tbl_len);
- if (mysql_table_dump(thd, db, tbl_name, -1))
- send_error(thd); // dump to NET
- break;
- }
- case COM_CHANGE_USER:
- {
- thd->change_user();
- thd->clear_error(); // if errors from rollback
- statistic_increment(com_other, &LOCK_status);
- char *user= (char*) packet;
- char *passwd= strend(user)+1;
- /*
- Old clients send null-terminated string (' ' for empty string) for
- password. New clients send the size (1 byte) + string (not null
- terminated, so also ' ' 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 incomming packet */
- if ((uint) ((uchar*) db - net->read_pos) > packet_length)
- {
- send_error(thd, ER_UNKNOWN_COM_ERROR);
- 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_master_access= thd->master_access;
- uint save_db_access= thd->db_access;
- uint save_db_length= thd->db_length;
- char *save_user= thd->user;
- char *save_priv_user= thd->priv_user;
- char *save_db= thd->db;
- USER_CONN *save_user_connect= thd->user_connect;
-
- if (!(thd->user= my_strdup(user, MYF(0))))
- {
- thd->user= save_user;
- send_error(thd, ER_OUT_OF_RESOURCES);
- 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)
- {
- /* authentification failure, we shall restore old user */
- if (res > 0)
- send_error(thd, ER_UNKNOWN_COM_ERROR);
- x_free(thd->user);
- thd->user= save_user;
- thd->priv_user= save_priv_user;
- thd->user_connect= save_user_connect;
- thd->master_access= save_master_access;
- thd->db_access= save_db_access;
- thd->db= save_db;
- thd->db_length= save_db_length;
- }
- else
- {
- /* we've authenticated new user */
- if (save_user_connect)
- decrease_user_connections(save_user_connect);
- x_free((gptr) save_db);
- x_free((gptr) save_user);
- }
- break;
- }
- case COM_EXECUTE:
- {
- mysql_stmt_execute(thd, packet, packet_length);
- break;
- }
- case COM_LONG_DATA:
- {
- mysql_stmt_get_longdata(thd, packet, packet_length);
- break;
- }
- case COM_PREPARE:
- {
- mysql_stmt_prepare(thd, packet, packet_length);
- break;
- }
- case COM_CLOSE_STMT:
- {
- mysql_stmt_free(thd, packet);
- break;
- }
- case COM_RESET_STMT:
- {
- 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;
- mysql_log.write(thd,command,"%s",thd->query);
- DBUG_PRINT("query",("%-.4096s",thd->query));
- mysql_parse(thd,thd->query, thd->query_length);
- while (!thd->killed && !thd->is_fatal_error && thd->lex->found_colon)
- {
- char *packet= thd->lex->found_colon;
- /*
- Multiple queries exits, execute them individually
- in embedded server - just store them to be executed later
- */
- #ifndef EMBEDDED_LIBRARY
- if (thd->lock || thd->open_tables || thd->derived_tables)
- close_thread_tables(thd);
- #endif
- 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= 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));
- #ifndef EMBEDDED_LIBRARY
- mysql_parse(thd, packet, length);
- #else
- /*
- 'packet' can point inside the query_rest's buffer
- so we have to do memmove here
- */
- if (thd->query_rest.length() > length)
- {
- memmove(thd->query_rest.c_ptr(), packet, length);
- thd->query_rest.length(length);
- }
- else
- thd->query_rest.copy(packet, length, thd->query_rest.charset());
- thd->server_status&= ~ (SERVER_QUERY_NO_INDEX_USED |
- SERVER_QUERY_NO_GOOD_INDEX_USED);
- break;
- #endif /*EMBEDDED_LIBRARY*/
- }
- 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
- send_error(thd,ER_NOT_ALLOWED_COMMAND); /* purecov: inspected */
- break;
- #else
- {
- char *fields, *pend;
- TABLE_LIST table_list;
- LEX_STRING conv_name;
- statistic_increment(com_stat[SQLCOM_SHOW_FIELDS],&LOCK_status);
- bzero((char*) &table_list,sizeof(table_list));
- if (!(table_list.db=thd->db))
- {
- send_error(thd,ER_NO_DB_ERROR);
- break;
- }
- thd->free_list=0;
- pend= strend(packet);
- thd->convert_string(&conv_name, system_charset_info,
- packet, (uint) (pend-packet), thd->charset());
- table_list.alias= table_list.real_name= conv_name.str;
- packet= pend+1;
- 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.real_name,fields);
- if (lower_case_table_names)
- my_casedn_str(files_charset_info, table_list.real_name);
- remove_escape(table_list.real_name); // This can't have wildcards
- if (check_access(thd,SELECT_ACL,table_list.db,&table_list.grant.privilege,
- 0, 0))
- break;
- if (grant_option &&
- check_grant(thd, SELECT_ACL, &table_list, 2, UINT_MAX, 0))
- break;
- mysqld_list_fields(thd,&table_list,fields);
- free_items(thd->free_list);
- thd->free_list= 0;
- 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(com_stat[SQLCOM_CREATE_DB],&LOCK_status);
- // null test to handle EOM
- if (!db || !(alias= thd->strdup(db)) || check_db_name(db))
- {
- net_printf(thd,ER_WRONG_DB_NAME, db ? db : "NULL");
- break;
- }
- if (check_access(thd,CREATE_ACL,db,0,1,0))
- break;
- mysql_log.write(thd,command,packet);
- bzero(&create_info, sizeof(create_info));
- if (mysql_create_db(thd, (lower_case_table_names == 2 ? alias : db),
- &create_info, 0) < 0)
- send_error(thd, thd->killed ? ER_SERVER_SHUTDOWN : 0);
- break;
- }
- case COM_DROP_DB: // QQ: To be removed
- {
- statistic_increment(com_stat[SQLCOM_DROP_DB],&LOCK_status);
- char *db=thd->strdup(packet), *alias;
- // null test to handle EOM
- if (!db || !(alias= thd->strdup(db)) || check_db_name(db))
- {
- net_printf(thd,ER_WRONG_DB_NAME, db ? db : "NULL");
- break;
- }
- if (check_access(thd,DROP_ACL,db,0,1,0))
- break;
- if (thd->locked_tables || thd->active_transaction())
- {
- send_error(thd,ER_LOCK_OR_ACTIVE_TRANSACTION);
- break;
- }
- mysql_log.write(thd,command,db);
- if (mysql_rm_db(thd, (lower_case_table_names == 2 ? alias : db),
- 0, 0) < 0)
- send_error(thd, thd->killed ? ER_SERVER_SHUTDOWN : 0);
- break;
- }
- #ifndef EMBEDDED_LIBRARY
- case COM_BINLOG_DUMP:
- {
- ulong pos;
- ushort flags;
- uint32 slave_server_id;
- statistic_increment(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:
- {
- statistic_increment(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, NULL))
- send_error(thd, 0);
- else
- send_ok(thd);
- break;
- }
- #ifndef EMBEDDED_LIBRARY
- case COM_SHUTDOWN:
- {
- statistic_increment(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");
- send_error(thd);
- 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
- free_root(thd->mem_root,MYF(0));
- free_root(&thd->transaction.mem_root,MYF(0));
- kill_mysql();
- error=TRUE;
- break;
- }
- #endif
- case COM_STATISTICS:
- {
- mysql_log.write(thd,command,NullS);
- statistic_increment(com_stat[SQLCOM_SHOW_STATUS],&LOCK_status);
- #ifndef EMBEDDED_LIBRARY
- char buff[200];
- #else
- char *buff= thd->net.last_error;
- #endif
- ulong uptime = (ulong) (thd->start_time - start_time);
- sprintf((char*) buff,
- "Uptime: %ld Threads: %d Questions: %lu Slow queries: %ld Opens: %ld Flush tables: %ld Open tables: %u Queries per second avg: %.3f",
- uptime,
- (int) thread_count,thd->query_id,long_query_count,
- opened_tables,refresh_version, cached_tables(),
- uptime ? (float)thd->query_id/(float)uptime : 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(com_other,&LOCK_status);
- send_ok(thd); // Tell client we are alive
- break;
- case COM_PROCESS_INFO:
- statistic_increment(com_stat[SQLCOM_SHOW_PROCESSLIST],&LOCK_status);
- if (!thd->priv_user[0] && check_global_access(thd,PROCESS_ACL))
- break;
- mysql_log.write(thd,command,NullS);
- mysqld_list_processes(thd,
- thd->master_access & PROCESS_ACL ?
- NullS : thd->priv_user, 0);
- break;
- case COM_PROCESS_KILL:
- {
- statistic_increment(com_stat[SQLCOM_KILL],&LOCK_status);
- ulong id=(ulong) uint4korr(packet);
- kill_one_thread(thd,id);
- break;
- }
- case COM_SET_OPTION:
- {
- statistic_increment(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:
- send_error(thd, ER_UNKNOWN_COM_ERROR);
- break;
- }
- break;
- }
- case COM_DEBUG:
- statistic_increment(com_other,&LOCK_status);
- if (check_global_access(thd, SUPER_ACL))
- break; /* purecov: inspected */
- mysql_print_status(thd);
- 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:
- send_error(thd, ER_UNKNOWN_COM_ERROR);
- break;
- }
- if (thd->lock || thd->open_tables || thd->derived_tables)
- {
- thd->proc_info="closing tables";
- close_thread_tables(thd); /* Free tables */
- }
- if (thd->is_fatal_error)
- send_error(thd,0); // End of memory ?
- 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=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)))
- {
- long_query_count++;
- mysql_slow_log.write(thd, thd->query, thd->query_length, start_of_query);
- }
- }
- }
- /*
- Read query from packet and store in thd->query
- Used in COM_QUERY and COM_PREPARE
- DESCRIPTION
- Sets the following THD variables:
- query
- query_length
- RETURN VALUES
- 0 ok
- 1 error; In this case thd->fatal_error is set
- */
- bool alloc_query(THD *thd, char *packet, ulong 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--;
- }
- 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 1;
- 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);
- if (!(specialflag & SPECIAL_NO_PRIOR))
- my_pthread_setprio(pthread_self(),QUERY_PRIOR);
- return 0;
- }
- 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;
- }
- /****************************************************************************
- ** mysql_execute_command
- ** Execute command saved in thd and current_lex->sql_command
- ****************************************************************************/
- void
- mysql_execute_command(THD *thd)
- {
- int res= 0;
- LEX *lex= thd->lex;
- SELECT_LEX *select_lex= &lex->select_lex;
- TABLE_LIST *tables= (TABLE_LIST*) select_lex->table_list.first;
- SELECT_LEX_UNIT *unit= &lex->unit;
- DBUG_ENTER("mysql_execute_command");
- /*
- 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.
- */
- if (tables || &lex->select_lex != lex->all_selects_list ||
- lex->time_zone_tables_used)
- mysql_reset_errors(thd);
- /*
- When subselects or time_zone info is used in a query
- we create a new TABLE_LIST containing all referenced tables
- and set local variable 'tables' to point to this list.
- */
- if ((&lex->select_lex != lex->all_selects_list ||
- lex->time_zone_tables_used) &&
- lex->unit.create_total_list(thd, lex, &tables))
- DBUG_VOID_RETURN;
- #ifdef HAVE_REPLICATION
- if (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,tables))
- {
- /* we warn the slave SQL thread */
- my_error(ER_SLAVE_IGNORED_TABLE, MYF(0));
- reset_one_shot_variables(thd);
- DBUG_VOID_RETURN;
- }
- #ifndef TO_BE_DELETED
- /*
- This is a workaround to deal with the shortcoming in 3.23.44-3.23.46
- masters in RELEASE_LOCK() logging. We re-write SELECT RELEASE_LOCK()
- as DO RELEASE_LOCK()
- */
- if (lex->sql_command == SQLCOM_SELECT)
- {
- lex->sql_command = SQLCOM_DO;
- lex->insert_list = &select_lex->item_list;
- }
- #endif
- }
- #endif /* HAVE_REPLICATION */
- /*
- When option readonly is set deny operations which change tables.
- Except for the replication thread and the 'super' users.
- */
- if (opt_readonly &&
- !(thd->slave_thread || (thd->master_access & SUPER_ACL)) &&
- (uc_update_queries[lex->sql_command] > 0))
- {
- net_printf(thd, ER_OPTION_PREVENTS_STATEMENT, "--read-only");
- DBUG_VOID_RETURN;
- }
- statistic_increment(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= thd->variables.select_limit;
- }
- select_result *result=lex->result;
- if (tables)
- {
- res=check_table_access(thd,
- lex->exchange ? SELECT_ACL | FILE_ACL :
- SELECT_ACL,
- tables,0);
- }
- else
- res=check_access(thd, lex->exchange ? SELECT_ACL | FILE_ACL : SELECT_ACL,
- any_db,0,0,0);
- if (res)
- {
- res=0;
- break; // Error message is given
- }
- /*
- In case of single SELECT unit->global_parameters points on first SELECT
- TODO: move counters to SELECT_LEX
- */
- unit->offset_limit_cnt= (ha_rows) unit->global_parameters->offset_limit;
- unit->select_limit_cnt= (ha_rows) (unit->global_parameters->select_limit+
- unit->global_parameters->offset_limit);
- if (unit->select_limit_cnt <
- (ha_rows) unit->global_parameters->select_limit)
- unit->select_limit_cnt= HA_POS_ERROR; // no limit
- if (unit->select_limit_cnt == HA_POS_ERROR && !select_lex->next_select())
- select_lex->options&= ~OPTION_FOUND_ROWS;
- if (!(res=open_and_lock_tables(thd,tables)))
- {
- if (lex->describe)
- {
- if (!(result= new select_send()))
- {
- send_error(thd, ER_OUT_OF_RESOURCES);
- DBUG_VOID_RETURN;
- }
- 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(' ');
- push_warning(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
- ER_YES, str.ptr());
- }
- result->send_eof();
- delete result;
- }
- else
- {
- if (!result && !(result= new select_send()))
- {
- res= -1;
- break;
- }
- query_cache_store_query(thd, tables);
- res= handle_select(thd, lex, result);
- if (result != lex->result)
- delete result;
- }
- }
- break;
- }
- case SQLCOM_PREPARE:
- {
- char *query_str;
- uint query_len;
- if (lex->prepared_stmt_code_is_varref)
- {
- /* This is PREPARE stmt FROM @var. */
- String str;
- CHARSET_INFO *to_cs= thd->variables.collation_connection;
- bool need_conversion;
- user_var_entry *entry;
- String *pstr= &str;
- uint32 unused;
- /*
- Convert @var contents to string in connection character set. Although
- it is known that int/real/NULL value cannot be a valid query we still
- convert it for error messages to uniform.
- */
- if ((entry=
- (user_var_entry*)hash_search(&thd->user_vars,
- (byte*)lex->prepared_stmt_code.str,
- lex->prepared_stmt_code.length))
- && entry->value)
- {
- my_bool is_var_null;
- pstr= entry->val_str(&is_var_null, &str, NOT_FIXED_DEC);
- /*
- NULL value of variable checked early as entry->value so here
- we can't get NULL in normal conditions
- */
- DBUG_ASSERT(!is_var_null);
- if (!pstr)
- {
- res= -1;
- break; // EOM (error should be reported by allocator)
- }
- }
- else
- {
- /*
- variable absent or equal to NULL, so we need to set variable to
- something reasonable to get readable error message during parsing
- */
- str.set("NULL", 4, &my_charset_latin1);
- }
- need_conversion=
- String::needs_conversion(pstr->length(), pstr->charset(),
- to_cs, &unused);
- query_len= need_conversion? (pstr->length() * to_cs->mbmaxlen) :
- pstr->length();
- if (!(query_str= alloc_root(thd->mem_root, query_len+1)))
- {
- res= -1;
- break; // EOM (error should be reported by allocator)
- }
- if (need_conversion)
- {
- uint dummy_errors;
- query_len= copy_and_convert(query_str, query_len, to_cs,
- pstr->ptr(), pstr->length(),
- pstr->charset(), &dummy_errors);
- }
- else
- memcpy(query_str, pstr->ptr(), pstr->length());
- query_str[query_len]= 0;
- }
- else
- {
- query_str= lex->prepared_stmt_code.str;
- query_len= lex->prepared_stmt_code.length;
- DBUG_PRINT("info", ("PREPARE: %.*s FROM '%.*s' n",
- lex->prepared_stmt_name.length,
- lex->prepared_stmt_name.str,
- query_len, query_str));
- }
- thd->command= COM_PREPARE;
- if (!mysql_stmt_prepare(thd, query_str, query_len + 1,
- &lex->prepared_stmt_name))
- send_ok(thd, 0L, 0L, "Statement prepared");
- break;
- }
- case SQLCOM_EXECUTE:
- {
- DBUG_PRINT("info", ("EXECUTE: %.*sn",
- lex->prepared_stmt_name.length,
- lex->prepared_stmt_name.str));
- mysql_sql_stmt_execute(thd, &lex->prepared_stmt_name);
- lex->prepared_stmt_params.empty();
- break;
- }
- case SQLCOM_DEALLOCATE_PREPARE:
- {
- Statement* stmt;
- DBUG_PRINT("info", ("DEALLOCATE PREPARE: %.*sn",
- lex->prepared_stmt_name.length,
- lex->prepared_stmt_name.str));
- /* We account deallocate in the same manner as mysql_stmt_close */
- statistic_increment(com_stmt_close, &LOCK_status);
- if ((stmt= thd->stmt_map.find_by_name(&lex->prepared_stmt_name)))
- {
- thd->stmt_map.erase(stmt);
- send_ok(thd);
- }
- else
- {
- res= -1;
- my_error(ER_UNKNOWN_STMT_HANDLER, MYF(0),
- lex->prepared_stmt_name.length, lex->prepared_stmt_name.str,
- "DEALLOCATE PREPARE");
- }
- break;
- }
- case SQLCOM_DO:
- if (tables && ((res= check_table_access(thd, SELECT_ACL, tables,0)) ||
- (res= open_and_lock_tables(thd,tables))))
- break;
- res= mysql_do(thd, *lex->insert_list);
- if (thd->net.report_error)
- res= -1;
- 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:
- {
- if (check_global_access(thd, SUPER_ACL))
- goto error;
- // PURGE MASTER LOGS BEFORE 'data'
- res = purge_master_logs_before_date(thd, lex->purge_time);
- 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
- net_printf(thd, ER_NOT_SUPPORTED_YET, "SHOW NEW MASTER");
- res= 1;
- #else
- res = show_new_master(thd);
- #endif
- break;
- }
- #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 = show_binlog_events(thd);
- break;
- }
- #endif
- case SQLCOM_BACKUP_TABLE:
- {
- if (check_db_used(thd,tables) ||
- check_table_access(thd,SELECT_ACL, 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, tables);
- break;
- }
- case SQLCOM_RESTORE_TABLE:
- {
- if (check_db_used(thd,tables) ||
- check_table_access(thd, INSERT_ACL, 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, tables);
- break;
- }
- case SQLCOM_ASSIGN_TO_KEYCACHE:
- {
- if (check_db_used(thd, tables) ||
- check_access(thd, INDEX_ACL, tables->db,
- &tables->grant.privilege, 0, 0))
- goto error;
- res= mysql_assign_to_keycache(thd, tables, &lex->name_and_length);
- break;
- }
- case SQLCOM_PRELOAD_KEYS:
- {
- if (check_db_used(thd, tables) ||
- check_access(thd, INDEX_ACL, tables->db,
- &tables->grant.privilege, 0, 0))
- goto error;
- res = mysql_preload_keys(thd, tables);
- 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))
- res= -1;
- 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;
- }
- #endif
- #ifdef HAVE_REPLICATION
- case SQLCOM_LOAD_MASTER_TABLE:
- {
- if (!tables->db)
- tables->db=thd->db;
- if (check_access(thd,CREATE_ACL,tables->db,&tables->grant.privilege,0,0))
- goto error; /* purecov: inspected */
- if (grant_option)
- {
- /* Check that the first table has CREATE privilege */
- if (check_grant(thd, CREATE_ACL, tables, 0, 1, 0))
- goto error;
- }
- if (strlen(tables->real_name) > NAME_LEN)
- {
- net_printf(thd,ER_WRONG_TABLE_NAME, tables->real_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, tables->db, tables->real_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;
- }
- /* Skip first table, which is the table we are creating */
- TABLE_LIST *create_table, *create_table_local;
- tables= lex->unlink_first_table(tables, &create_table,
- &create_table_local);
- if ((res= create_table_precheck(thd, tables, create_table)))
- goto unsent_create_error;
- #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->real_name) ||
- append_file_to_dir(thd,&lex->create_info.index_file_name,
- create_table->real_name))
- {
- res=-1;
- goto unsent_create_error;
- }
- #endif
- /*
- If we are using SET CHARSET without DEFAULT, add an implicite
- 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 (wait_if_global_read_lock(thd, 0, 1))
- {
- res= -1;
- goto unsent_create_error;
- }
- if (select_lex->item_list.elements) // With select
- {
- select_result *result;
- select_lex->options|= SELECT_NO_UNLOCK;
- unit->offset_limit_cnt= select_lex->offset_limit;
- unit->select_limit_cnt= select_lex->select_limit+
- select_lex->offset_limit;
- if (unit->select_limit_cnt < select_lex->select_limit)
- unit->select_limit_cnt= HA_POS_ERROR; // No limit
- if (!(res=open_and_lock_tables(thd,tables)))
- {
- res= -1; // If error
- if ((result=new select_create(create_table->db,
- create_table->real_name,
- &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
- */
- select_lex->resolve_mode= SELECT_LEX::SELECT_MODE;
- res=handle_select(thd, lex, result);
- select_lex->resolve_mode= SELECT_LEX::NOMATTER_MODE;
- }
- //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->real_name, &lex->create_info,
- lex->create_list,
- lex->key_list,0,0);
- }
- if (!res)
- send_ok(thd);
- }
- /*
- 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);
- unsent_create_error:
- // put tables back for PS rexecuting
- tables= lex->link_first_table_back(tables, create_table,
- create_table_local);
- break;
- }
- case SQLCOM_CREATE_INDEX:
- if (check_one_table_access(thd, INDEX_ACL, tables))
- goto error; /* purecov: inspected */
- thd->enable_slow_log= opt_log_slow_admin_statements;
- if (end_active_trans(thd))
- res= -1;
- else
- res = mysql_create_index(thd, tables, 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)
- {
- send_error(thd,ER_LOCK_OR_ACTIVE_TRANSACTION);
- break;
- }
- {
- 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:
- #if defined(DONT_ALLOW_SHOW_COMMANDS)
- send_error(thd,ER_NOT_ALLOWED_COMMAND); /* purecov: inspected */
- break;
- #else
- {
- ulong priv=0;
- if (lex->name && (!lex->name[0] || strlen(lex->name) > NAME_LEN))
- {
- net_printf(thd, ER_WRONG_TABLE_NAME, lex->name);
- res= 1;
- break;
- }
- if (!select_lex->db)
- {
- /*
- In the case of ALTER TABLE ... RENAME we should supply the
- default database if the new name is not explicitly qualified
- by a database. (Bug #11493)
- */
- if (lex->alter_info.flags & ALTER_RENAME)
- {
- if (! thd->db)
- {
- send_error(thd,ER_NO_DB_ERROR);
- goto error;
- }
- select_lex->db= thd->db;
- }
- else
- select_lex->db=tables->db;
- }
- if (check_access(thd,ALTER_ACL,tables->db,&tables->grant.privilege,0,0) ||
- check_access(thd,INSERT_ACL | CREATE_ACL,select_lex->db,&priv,0,0)||
- check_merge_table_access(thd, tables->db,
- (TABLE_LIST *)
- lex->create_info.merge_list.first))
- goto error; /* purecov: inspected */
- if (grant_option)
- {
- if (check_grant(thd, ALTER_ACL, 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.real_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 */
- lex->create_info.data_file_name=lex->create_info.index_file_name=0;
- /* ALTER TABLE ends previous transaction */
- if (end_active_trans(thd))
- res= -1;
- else
- {
- thd->enable_slow_log= opt_log_slow_admin_statements;
- res= mysql_alter_table(thd, select_lex->db, lex->name,
- &lex->create_info,
- tables, lex->create_list,
- lex->key_list,
- select_lex->order_list.elements,
- (ORDER *) select_lex->order_list.first,
- lex->duplicates, lex->ignore, &lex->alter_info);
- }
- break;
- }
- #endif /*DONT_ALLOW_SHOW_COMMANDS*/
- case SQLCOM_RENAME_TABLE:
- {
- TABLE_LIST *table;
- if (check_db_used(thd,tables))
- goto error;
- for (table=tables ; table ; table=table->next->next)
- {
- if (check_access(thd, ALTER_ACL | DROP_ACL, table->db,
- &table->grant.privilege,0,0) ||
- check_access(thd, INSERT_ACL | CREATE_ACL, table->next->db,
- &table->next->grant.privilege,0,0))
- 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[0];
- old_list.next=new_list.next=0;
- if (check_grant(thd, ALTER_ACL, &old_list, 0, UINT_MAX, 0) ||
- (!test_all_bits(table->next->grant.privilege,
- INSERT_ACL | CREATE_ACL) &&
- check_grant(thd, INSERT_ACL | CREATE_ACL, &new_list, 0,
- UINT_MAX, 0)))
- goto error;
- }
- }
- query_cache_invalidate3(thd, tables, 0);
- if (end_active_trans(thd))
- res= -1;
- else if (mysql_rename_tables(thd,tables))
- res= -1;
- break;
- }
- #ifndef EMBEDDED_LIBRARY
- case SQLCOM_SHOW_BINLOGS:
- #ifdef DONT_ALLOW_SHOW_COMMANDS
- send_error(thd,ER_NOT_ALLOWED_COMMAND); /* purecov: inspected */
- DBUG_VOID_RETURN;
- #else
- {
- if (check_global_access(thd, SUPER_ACL))
- goto error;
- res = show_binlogs(thd);
- break;
- }
- #endif
- #endif /* EMBEDDED_LIBRARY */
- case SQLCOM_SHOW_CREATE:
- #ifdef DONT_ALLOW_SHOW_COMMANDS
- send_error(thd,ER_NOT_ALLOWED_COMMAND); /* purecov: inspected */
- DBUG_VOID_RETURN;
- #else
- {
- if (check_db_used(thd, tables) ||
- check_access(thd, SELECT_ACL | EXTRA_ACL, tables->db,
- &tables->grant.privilege,0,0))
- goto error;
- if (grant_option && check_grant(thd, SELECT_ACL, tables, 2, UINT_MAX, 0))
- goto error;
- res= mysqld_show_create(thd, tables);
- break;
- }
- #endif
- case SQLCOM_CHECKSUM:
- {
- if (check_db_used(thd,tables) ||
- check_table_access(thd, SELECT_ACL | EXTRA_ACL , tables,0))
- goto error; /* purecov: inspected */
- res = mysql_checksum_table(thd, tables, &lex->check_opt);
- break;
- }
- case SQLCOM_REPAIR:
- {
- if (check_db_used(thd,tables) ||
- check_table_access(thd,SELECT_ACL | INSERT_ACL, tables,0))
- goto error; /* purecov: inspected */
- thd->enable_slow_log= opt_log_slow_admin_statements;
- res = mysql_repair_table(thd, tables, &lex->check_opt);
- /* ! we write after unlocking the table */
- if (!res && !lex->no_write_to_binlog)
- {
- mysql_update_log.write(thd, thd->query, thd->query_length);
- 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);
- }
- }
- break;
- }
- case SQLCOM_CHECK:
- {
- if (check_db_used(thd,tables) ||
- check_table_access(thd, SELECT_ACL | EXTRA_ACL , tables,0))
- goto error; /* purecov: inspected */
- thd->enable_slow_log= opt_log_slow_admin_statements;
- res = mysql_check_table(thd, tables, &lex->check_opt);
- break;
- }
- case SQLCOM_ANALYZE:
- {
- if (check_db_used(thd,tables) ||
- check_table_access(thd,SELECT_ACL | INSERT_ACL, tables,0))
- goto error; /* purecov: inspected */
- thd->enable_slow_log= opt_log_slow_admin_statements;
- res = mysql_analyze_table(thd, tables, &lex->check_opt);
- /* ! we write after unlocking the table */
- if (!res && !lex->no_write_to_binlog)
- {
- mysql_update_log.write(thd, thd->query, thd->query_length);
- 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);
- }
- }
- break;
- }
- case SQLCOM_OPTIMIZE:
- {
- if (check_db_used(thd,tables) ||
- check_table_access(thd,SELECT_ACL | INSERT_ACL, 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, tables, 1) :
- mysql_optimize_table(thd, tables, &lex->check_opt);
- /* ! we write after unlocking the table */
- if (!res && !lex->no_write_to_binlog)
- {
- mysql_update_log.write(thd, thd->query, thd->query_length);
- 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);
- }
- }
- break;
- }
- case SQLCOM_UPDATE:
- if (update_precheck(thd, tables))
- break;
- res= mysql_update(thd,tables,
- select_lex->item_list,
- lex->value_list,
- select_lex->where,
- select_lex->order_list.elements,
- (ORDER *) select_lex->order_list.first,
- select_lex->select_limit,
- lex->duplicates, lex->ignore);
- if (thd->net.report_error)
- res= -1;
- break;
- case SQLCOM_UPDATE_MULTI:
- {
- if ((res= multi_update_precheck(thd, tables)))
- break;
- if ((res= mysql_multi_update_lock(thd, tables, &select_lex->item_list,
- select_lex)))
- break;
- #ifdef HAVE_REPLICATION
- /* Check slave filtering rules */
- if (thd->slave_thread)
- if (all_tables_not_ok(thd,tables))
- {
- /* we warn the slave SQL thread */
- my_error(ER_SLAVE_IGNORED_TABLE, MYF(0));
- break;
- }
- #endif /* HAVE_REPLICATION */
- res= mysql_multi_update(thd,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:
- {
- if ((res= insert_precheck(thd, tables)))
- break;
- res= mysql_insert(thd,tables,lex->field_list,lex->many_values,
- lex->update_list, lex->value_list,
- lex->duplicates, lex->ignore);
- if (thd->net.report_error)
- res= -1;
- break;
- }
- case SQLCOM_REPLACE_SELECT:
- case SQLCOM_INSERT_SELECT:
- {
- TABLE_LIST *first_local_table= (TABLE_LIST *) select_lex->table_list.first;
- TABLE_LIST dup_tables;
- TABLE *insert_table;
- if ((res= insert_precheck(thd, tables)))
- break;
- /* Fix lock for first table */
- if (tables->lock_type == TL_WRITE_DELAYED)
- tables->lock_type= TL_WRITE;
- /* Don't unlock tables until command is written to binary log */
- select_lex->options|= SELECT_NO_UNLOCK;
- select_result *result;
- unit->offset_limit_cnt= select_lex->offset_limit;
- unit->select_limit_cnt= select_lex->select_limit+select_lex->offset_limit;