summaryrefslogtreecommitdiff
path: root/sql
diff options
context:
space:
mode:
authorMichael Widenius <monty@askmonty.org>2011-01-04 00:55:41 +0200
committerMichael Widenius <monty@askmonty.org>2011-01-04 00:55:41 +0200
commite63b5546c597f65696868eaf69159107bc4a8e44 (patch)
treeab42b4b18f932bc855e3b5494f6474581ecb0813 /sql
parent1eb21dc4be059b808f79de4702aaa9204ae4a41e (diff)
downloadmariadb-git-e63b5546c597f65696868eaf69159107bc4a8e44.tar.gz
Implementation of MWL#172: Add support for prepared statements to HANDLER READ
It includes speed optimizations for HANDLER READ by caching as much as possible in HANDLER OPEN Other things: - Added mysqld option --disable-thr-alarm to be able to benchmark things without thr_alarm - Changed 'Locked' state to 'System lock' and 'Table lock' (these where used in the code but never shown to end user) - Better error message if mysql_install_db.sh fails - Moved handler function prototypes to sql_handler.h - Remove not anymore used 'thd->locked' member include/thr_alarm.h: Added my_disable_thr_alarm include/thr_lock.h: Add new member to THR_LOCK_DATA to remember original lock type state. This is needed as thr_unlock() resets type to TL_UNLOCK. mysql-test/include/check_no_concurrent_insert.inc: Locked -> Table lock mysql-test/include/handler.inc: Locked -> Table lock mysql-test/r/handler_innodb.result: Updated results for new tests mysql-test/r/handler_myisam.result: Updated results for new tests mysql-test/r/sp-threads.result: Locked -> Table lock mysql-test/suite/binlog/t/binlog_stm_row.test: Locked -> Table lock mysql-test/suite/funcs_1/datadict/processlist_val.inc: Locked -> Table lock mysql-test/suite/pbxt/t/lock_multi.test: Locked -> Table lock mysql-test/suite/sys_vars/r/concurrent_insert_func.result: Locked -> Table lock mysql-test/suite/sys_vars/t/concurrent_insert_func.test: Locked -> Table lock mysql-test/suite/sys_vars/t/delayed_insert_limit_func.test: Locked -> Table lock mysql-test/suite/sys_vars/t/query_cache_wlock_invalidate_func.test: Locked -> Table lock mysql-test/suite/sys_vars/t/sql_low_priority_updates_func.test: Locked -> Table lock mysql-test/t/insert_notembedded.test: Locked -> Table lock mysql-test/t/lock_multi.test: Locked -> Table lock mysql-test/t/merge-big.test: Locked -> Table lock mysql-test/t/multi_update.test: Locked -> Table lock mysql-test/t/query_cache_28249.test: Locked -> Table lock mysql-test/t/sp_notembedded.test: Locked -> Table lock mysql-test/t/sp_sync.test: Locked -> Table lock mysql-test/t/status.test: Locked -> Table lock mysql-test/t/trigger_notembedded.test: Locked -> Table lock mysys/thr_alarm.c: Added option to disable thr_alarm mysys/thr_lock.c: Detect loops scripts/mysql_install_db.sh: Give better error message if something goes wrong sql/Makefile.am: Added sql_handler.h sql/lock.cc: Split functions to allow one to cache value if store_lock() (for HANDLER functions). - Split mysql_lock_tables() into two functions, where first one allocates MYSQL_LOCK and other other one uses it. - Made get_lock_data() an external function. - Added argument to mysql_unlock_tables() to not free sql_lock. - Added argument to reset_lock_data() to reset lock structure to initial state (as after get_lock_data()) sql/mysql_priv.h: Moved handler function prototypes to sql_handler.h Added new lock functions. sql/mysqld.cc: Added --thread-alarm startup option sql/net_serv.cc: Don't call vio_blocking() if not needed sql/sql_base.cc: include sql_handler.h sql/sql_class.cc: include sql_handler.h Remove not anymore used 'thd->locked' member sql/sql_class.h: Remove not anymore used 'thd->locked' member sql/sql_db.cc: include sql_handler.h sql/sql_delete.cc: include sql_handler.h sql/sql_handler.cc: Rewrote all code to use SQL_HANDLER instead of TABLE_LIST (original interface) Rewrote mysql_ha_open() to cache all things from TABLE_LIST and items for field list, where etc. In mysql_ha_open() also cache MYSQL_LOCK structure from get_lock_data(). Split functions into smaller sub functions (needed to be able to implement mysql_ha_read_prepare()) Added mysql_ha_read_prepare() to allow one to prepare HANDLER READ. sql/sql_handler.h: Interface to sql_handler.cc sql/sql_parse.cc: include sql_handler.h sql/sql_prepare.cc: Added mysql_test_handler_read(), prepare for HANDLER READ sql/sql_rename.cc: include sql_handler.h sql/sql_show.cc: Removed usage of thd->locked sql/sql_table.cc: include sql_handler.h sql/sql_trigger.cc: include sql_handler.h
Diffstat (limited to 'sql')
-rw-r--r--sql/Makefile.am2
-rw-r--r--sql/lock.cc304
-rw-r--r--sql/mysql_priv.h21
-rw-r--r--sql/mysqld.cc11
-rw-r--r--sql/net_serv.cc6
-rw-r--r--sql/sql_base.cc1
-rw-r--r--sql/sql_class.cc3
-rw-r--r--sql/sql_class.h2
-rw-r--r--sql/sql_db.cc1
-rw-r--r--sql/sql_delete.cc1
-rw-r--r--sql/sql_handler.cc603
-rw-r--r--sql/sql_handler.h61
-rw-r--r--sql/sql_parse.cc1
-rw-r--r--sql/sql_prepare.cc61
-rw-r--r--sql/sql_rename.cc2
-rw-r--r--sql/sql_show.cc6
-rw-r--r--sql/sql_table.cc1
-rw-r--r--sql/sql_trigger.cc1
18 files changed, 690 insertions, 398 deletions
diff --git a/sql/Makefile.am b/sql/Makefile.am
index afbef9ca197..05cb56bfab7 100644
--- a/sql/Makefile.am
+++ b/sql/Makefile.am
@@ -81,7 +81,7 @@ noinst_HEADERS = item.h item_func.h item_sum.h item_cmpfunc.h \
event_data_objects.h event_scheduler.h \
sql_partition.h partition_info.h partition_element.h \
contributors.h sql_servers.h \
- multi_range_read.h \
+ multi_range_read.h sql_handler.h \
sql_join_cache.h \
create_options.h \
sql_expression_cache.h
diff --git a/sql/lock.cc b/sql/lock.cc
index 8f5b5ac233f..49f0cfdfa90 100644
--- a/sql/lock.cc
+++ b/sql/lock.cc
@@ -84,41 +84,11 @@
extern HASH open_cache;
-/* flags for get_lock_data */
-#define GET_LOCK_UNLOCK 1
-#define GET_LOCK_STORE_LOCKS 2
-
-static MYSQL_LOCK *get_lock_data(THD *thd, TABLE **table,uint count,
- uint flags, TABLE **write_locked);
-static void reset_lock_data(MYSQL_LOCK *sql_lock);
+static void reset_lock_data(MYSQL_LOCK *sql_lock, bool unlock);
static int lock_external(THD *thd, TABLE **table,uint count);
static int unlock_external(THD *thd, TABLE **table,uint count);
static void print_lock_error(int error, const char *);
-/*
- Lock tables.
-
- SYNOPSIS
- mysql_lock_tables()
- thd The current thread.
- tables An array of pointers to the tables to lock.
- count The number of tables to lock.
- flags Options:
- MYSQL_LOCK_IGNORE_GLOBAL_READ_LOCK Ignore a global read lock
- MYSQL_LOCK_IGNORE_GLOBAL_READ_ONLY Ignore SET GLOBAL READ_ONLY
- MYSQL_LOCK_IGNORE_FLUSH Ignore a flush tables.
- MYSQL_LOCK_NOTIFY_IF_NEED_REOPEN Instead of reopening altered
- or dropped tables by itself,
- mysql_lock_tables() should
- notify upper level and rely
- on caller doing this.
- need_reopen Out parameter, TRUE if some tables were altered
- or deleted and should be reopened by caller.
-
- RETURN
- A lock structure pointer on success.
- NULL on error or if some tables should be reopen.
-*/
/* Map the return value of thr_lock to an error from errmsg.txt */
static int thr_lock_errno_to_mysql[]=
@@ -132,6 +102,7 @@ static int thr_lock_errno_to_mysql[]=
@param flags Lock flags
@return 0 if all the check passed, non zero if a check failed.
*/
+
int mysql_lock_tables_check(THD *thd, TABLE **tables, uint count, uint flags)
{
bool log_table_write_query;
@@ -194,81 +165,118 @@ int mysql_lock_tables_check(THD *thd, TABLE **tables, uint count, uint flags)
DBUG_RETURN(0);
}
+
+/*
+ Lock tables.
+
+ SYNOPSIS
+ mysql_lock_tables()
+ thd The current thread.
+ tables An array of pointers to the tables to lock.
+ count The number of tables to lock.
+ flags Options:
+ MYSQL_LOCK_IGNORE_GLOBAL_READ_LOCK Ignore a global read lock
+ MYSQL_LOCK_IGNORE_GLOBAL_READ_ONLY Ignore SET GLOBAL READ_ONLY
+ MYSQL_LOCK_IGNORE_FLUSH Ignore a flush tables.
+ MYSQL_LOCK_NOTIFY_IF_NEED_REOPEN Instead of reopening altered
+ or dropped tables by itself,
+ mysql_lock_tables() should
+ notify upper level and rely
+ on caller doing this.
+ need_reopen Out parameter, TRUE if some tables were altered
+ or deleted and should be reopened by caller.
+
+ RETURN
+ A lock structure pointer on success.
+ NULL on error or if some tables should be reopen.
+*/
+
MYSQL_LOCK *mysql_lock_tables(THD *thd, TABLE **tables, uint count,
uint flags, bool *need_reopen)
{
- MYSQL_LOCK *sql_lock;
TABLE *write_lock_used;
- int rc;
-
- DBUG_ENTER("mysql_lock_tables");
+ MYSQL_LOCK *sql_lock;
+ DBUG_ENTER("mysql_lock_tables(tables)");
*need_reopen= FALSE;
-
if (mysql_lock_tables_check(thd, tables, count, flags))
- DBUG_RETURN (NULL);
+ DBUG_RETURN(NULL);
- for (;;)
+ if (!(sql_lock= get_lock_data(thd, tables, count, GET_LOCK_STORE_LOCKS,
+ &write_lock_used)) ||
+ ! sql_lock->table_count)
+ DBUG_RETURN(sql_lock);
+
+ if (mysql_lock_tables(thd, sql_lock, write_lock_used != 0, flags,
+ need_reopen))
{
- if (! (sql_lock= get_lock_data(thd, tables, count, GET_LOCK_STORE_LOCKS,
- &write_lock_used)) ||
- ! sql_lock->table_count)
- break;
+ /* Clear the lock type of all lock data to avoid reusage. */
+ reset_lock_data(sql_lock, 1);
+ my_free(sql_lock, MYF(0));
+ sql_lock= 0;
+ }
+ DBUG_RETURN(sql_lock);
+}
+
+
+/**
+ Lock a table based on a MYSQL_LOCK structure.
- if (global_read_lock && write_lock_used &&
- ! (flags & MYSQL_LOCK_IGNORE_GLOBAL_READ_LOCK))
+ mysql_lock_tables()
+
+ @param thd The current thread.
+ @param sql_lock Tables that should be locked
+ @param write_lock_used 1 if any of the tables are write locked
+ @param flags See mysql_lock_tables()
+ @param need_reopen Out parameter, TRUE if some tables were altered
+ or deleted and should be reopened by caller.
+
+ @return 0 ok
+ @return 1 error
+*/
+
+bool mysql_lock_tables(THD *thd, MYSQL_LOCK *sql_lock,
+ bool write_lock_used,
+ uint flags, bool *need_reopen)
+{
+ int rc;
+ bool error= 1;
+ DBUG_ENTER("mysql_lock_tables(sql_lock)");
+
+ *need_reopen= FALSE;
+ for (;;)
+ {
+ if (write_lock_used && !(flags & MYSQL_LOCK_IGNORE_GLOBAL_READ_LOCK))
{
- /*
- Someone has issued LOCK ALL TABLES FOR READ and we want a write lock
- Wait until the lock is gone
- */
- if (wait_if_global_read_lock(thd, 1, 1))
+ if (global_read_lock)
{
- /* Clear the lock type of all lock data to avoid reusage. */
- reset_lock_data(sql_lock);
- my_free((uchar*) sql_lock,MYF(0));
- sql_lock=0;
- break;
+ /*
+ Someone has issued LOCK ALL TABLES FOR READ and we want a write lock
+ Wait until the lock is gone
+ */
+ if (wait_if_global_read_lock(thd, 1, 1))
+ break;
+ if (thd->version != refresh_version)
+ goto retry;
}
- if (thd->version != refresh_version)
+
+ if (opt_readonly &&
+ !(thd->security_ctx->master_access & SUPER_ACL) &&
+ !thd->slave_thread)
{
- /* Clear the lock type of all lock data to avoid reusage. */
- reset_lock_data(sql_lock);
- my_free((uchar*) sql_lock,MYF(0));
- goto retry;
+ /*
+ Someone has issued SET GLOBAL READ_ONLY=1 and we want a write lock.
+ We do not wait for READ_ONLY=0, and fail.
+ */
+ my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--read-only");
+ break;
}
}
- if (!(flags & MYSQL_LOCK_IGNORE_GLOBAL_READ_ONLY) &&
- write_lock_used &&
- opt_readonly &&
- !(thd->security_ctx->master_access & SUPER_ACL) &&
- !thd->slave_thread)
- {
- /*
- Someone has issued SET GLOBAL READ_ONLY=1 and we want a write lock.
- We do not wait for READ_ONLY=0, and fail.
- */
- reset_lock_data(sql_lock);
- my_free((uchar*) sql_lock, MYF(0));
- sql_lock=0;
- my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--read-only");
- break;
- }
-
thd_proc_info(thd, "System lock");
- DBUG_PRINT("info", ("thd->proc_info %s", thd->proc_info));
if (lock_external(thd, sql_lock->table, sql_lock->table_count))
- {
- /* Clear the lock type of all lock data to avoid reusage. */
- reset_lock_data(sql_lock);
- my_free((uchar*) sql_lock,MYF(0));
- sql_lock=0;
break;
- }
thd_proc_info(thd, "Table lock");
- DBUG_PRINT("info", ("thd->proc_info %s", thd->proc_info));
- thd->locked=1;
/* Copy the lock data array. thr_multi_lock() reorders its contens. */
memcpy(sql_lock->locks + sql_lock->lock_count, sql_lock->locks,
sql_lock->lock_count * sizeof(*sql_lock->locks));
@@ -277,70 +285,66 @@ MYSQL_LOCK *mysql_lock_tables(THD *thd, TABLE **tables, uint count,
sql_lock->lock_count,
sql_lock->lock_count,
thd->lock_id)];
- if (rc > 1) /* a timeout or a deadlock */
+ if (rc) /* Locking failed */
{
VOID(unlock_external(thd, sql_lock->table, sql_lock->table_count));
- my_error(rc, MYF(0));
- my_free((uchar*) sql_lock,MYF(0));
- sql_lock= 0;
- break;
- }
- else if (rc == 1) /* aborted */
- {
- /*
- reset_lock_data is required here. If thr_multi_lock fails it
- resets lock type for tables, which were locked before (and
- including) one that caused error. Lock type for other tables
- preserved.
- */
- reset_lock_data(sql_lock);
- thd->some_tables_deleted=1; // Try again
- sql_lock->lock_count= 0; // Locks are already freed
+ if (rc > 1)
+ {
+ /* a timeout or a deadlock */
+ my_error(rc, MYF(0));
+ break;
+ }
+ /* We where aborted and should try again from upper level*/
+ thd->some_tables_deleted= 1;
}
- else if (!thd->some_tables_deleted || (flags & MYSQL_LOCK_IGNORE_FLUSH))
+ else
{
/*
- Thread was killed or lock aborted. Let upper level close all
- used tables and retry or give error.
+ Lock worked. Now check that nothing happend while we where waiting
+ to get the lock that would require us to free it.
*/
- thd->locked=0;
- break;
- }
- else if (!thd->open_tables)
- {
- // Only using temporary tables, no need to unlock
- thd->some_tables_deleted=0;
- thd->locked=0;
- break;
+ error= 0;
+ if (!thd->some_tables_deleted || (flags & MYSQL_LOCK_IGNORE_FLUSH))
+ {
+ /*
+ Table was not signaled for deletion or we don't care if it was.
+ Return with table as locked.
+ */
+ break;
+ }
+ else if (!thd->open_tables && !(flags & MYSQL_LOCK_NOT_TEMPORARY))
+ {
+ /*
+ Only using temporary tables, no need to unlock.
+ We need the flag as open_tables is not enough to distingush if
+ we are only using temporary tables for tables used trough
+ the HANDLER interface.
+
+ We reset some_tables_deleted as it doesn't make sense to have this
+ one when we are only using temporary tables.
+ */
+ thd->some_tables_deleted=0;
+ break;
+ }
+ /* some table was altered or deleted. reopen tables marked deleted */
+ error= 1;
+ mysql_unlock_tables(thd, sql_lock, 0);
}
- thd_proc_info(thd, 0);
- /* some table was altered or deleted. reopen tables marked deleted */
- mysql_unlock_tables(thd,sql_lock);
- thd->locked=0;
retry:
- sql_lock=0;
if (flags & MYSQL_LOCK_NOTIFY_IF_NEED_REOPEN)
{
*need_reopen= TRUE;
break;
}
if (wait_for_tables(thd))
- break; // Couldn't open tables
- }
- thd_proc_info(thd, 0);
- if (thd->killed)
- {
- thd->send_kill_message();
- if (sql_lock)
- {
- mysql_unlock_tables(thd,sql_lock);
- sql_lock=0;
- }
+ break; // Couldn't open tables
+ reset_lock_data(sql_lock, 0); // Set org locks and retry
}
+ thd_proc_info(thd, 0);
thd->set_time_after_lock();
- DBUG_RETURN (sql_lock);
+ DBUG_RETURN(error);
}
@@ -380,15 +384,15 @@ static int lock_external(THD *thd, TABLE **tables, uint count)
DBUG_RETURN(0);
}
-
-void mysql_unlock_tables(THD *thd, MYSQL_LOCK *sql_lock)
+void mysql_unlock_tables(THD *thd, MYSQL_LOCK *sql_lock, bool free_lock)
{
DBUG_ENTER("mysql_unlock_tables");
if (sql_lock->table_count)
VOID(unlock_external(thd,sql_lock->table,sql_lock->table_count));
if (sql_lock->lock_count)
thr_multi_unlock(sql_lock->locks,sql_lock->lock_count, 0);
- my_free((uchar*) sql_lock,MYF(0));
+ if (free_lock)
+ my_free((uchar*) sql_lock,MYF(0));
DBUG_VOID_RETURN;
}
@@ -847,12 +851,12 @@ static int unlock_external(THD *thd, TABLE **table,uint count)
@param write_lock_used Store pointer to last table with WRITE_ALLOW_WRITE
*/
-static MYSQL_LOCK *get_lock_data(THD *thd, TABLE **table_ptr, uint count,
- uint flags, TABLE **write_lock_used)
+MYSQL_LOCK *get_lock_data(THD *thd, TABLE **table_ptr, uint count,
+ uint flags, TABLE **write_lock_used)
{
uint i,tables,lock_count;
MYSQL_LOCK *sql_lock;
- THR_LOCK_DATA **locks, **locks_buf, **locks_start;
+ THR_LOCK_DATA **locks, **locks_buf;
TABLE **to, **table_buf;
DBUG_ENTER("get_lock_data");
@@ -891,7 +895,7 @@ static MYSQL_LOCK *get_lock_data(THD *thd, TABLE **table_ptr, uint count,
{
TABLE *table;
enum thr_lock_type lock_type;
-
+ THR_LOCK_DATA **locks_start;
if ((table=table_ptr[i])->s->tmp_table == NON_TRANSACTIONAL_TMP_TABLE)
continue;
lock_type= table->reginfo.lock_type;
@@ -904,12 +908,11 @@ static MYSQL_LOCK *get_lock_data(THD *thd, TABLE **table_ptr, uint count,
my_error(ER_OPEN_AS_READONLY,MYF(0),table->alias.c_ptr());
/* Clear the lock type of the lock data that are stored already. */
sql_lock->lock_count= (uint) (locks - sql_lock->locks);
- reset_lock_data(sql_lock);
+ reset_lock_data(sql_lock, 1);
my_free((uchar*) sql_lock,MYF(0));
DBUG_RETURN(0);
}
}
- THR_LOCK_DATA **org_locks = locks;
locks_start= locks;
locks= table->file->store_lock(thd, locks,
(flags & GET_LOCK_UNLOCK) ? TL_IGNORE :
@@ -922,8 +925,13 @@ static MYSQL_LOCK *get_lock_data(THD *thd, TABLE **table_ptr, uint count,
}
*to++= table;
if (locks)
- for ( ; org_locks != locks ; org_locks++)
- (*org_locks)->debug_print_param= (void *) table;
+ {
+ for ( ; locks_start != locks ; locks_start++)
+ {
+ (*locks_start)->debug_print_param= (void *) table;
+ (*locks_start)->org_type= (*locks_start)->type;
+ }
+ }
}
/*
We do not use 'tables', because there are cases where store_lock()
@@ -964,10 +972,13 @@ static MYSQL_LOCK *get_lock_data(THD *thd, TABLE **table_ptr, uint count,
Clear the lock type of all lock data. This ensures that the next
lock request will set its lock type properly.
- @param sql_lock The MySQL lock.
+ @param sql_lock The MySQL lock.
+ @param unlock If set, then set lock type to TL_UNLOCK,
+ otherwise set to original lock type from
+ get_store_lock().
*/
-static void reset_lock_data(MYSQL_LOCK *sql_lock)
+static void reset_lock_data(MYSQL_LOCK *sql_lock, bool unlock)
{
THR_LOCK_DATA **ldata;
THR_LOCK_DATA **ldata_end;
@@ -975,10 +986,7 @@ static void reset_lock_data(MYSQL_LOCK *sql_lock)
for (ldata= sql_lock->locks, ldata_end= ldata + sql_lock->lock_count;
ldata < ldata_end;
ldata++)
- {
- /* Reset lock type. */
- (*ldata)->type= TL_UNLOCK;
- }
+ (*ldata)->type= unlock ? TL_UNLOCK : (*ldata)->org_type;
}
diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h
index ca5ccc699cd..e530e38e4f5 100644
--- a/sql/mysql_priv.h
+++ b/sql/mysql_priv.h
@@ -1487,15 +1487,6 @@ void mysqld_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);
-void mysql_ha_flush(THD *thd);
-void mysql_ha_rm_tables(THD *thd, TABLE_LIST *tables, bool is_locked);
-void mysql_ha_cleanup(THD *thd);
-
/* sql_base.cc */
#define TMP_TABLE_KEY_EXTRA 8
void set_item_name(Item *item,char *pos,uint length);
@@ -2194,6 +2185,10 @@ extern struct st_VioSSLFd * ssl_acceptor_fd;
MYSQL_LOCK *mysql_lock_tables(THD *thd, TABLE **table, uint count,
uint flags, bool *need_reopen);
+bool mysql_lock_tables(THD *thd, MYSQL_LOCK *sql_lock,
+ bool write_lock_used,
+ 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
@@ -2201,8 +2196,12 @@ MYSQL_LOCK *mysql_lock_tables(THD *thd, TABLE **table, uint count,
#define MYSQL_OPEN_TEMPORARY_ONLY 0x0008
#define MYSQL_LOCK_IGNORE_GLOBAL_READ_ONLY 0x0010
#define MYSQL_LOCK_PERF_SCHEMA 0x0020
+#define MYSQL_LOCK_NOT_TEMPORARY 0x0040
+/* flags for get_lock_data */
+#define GET_LOCK_UNLOCK 1
+#define GET_LOCK_STORE_LOCKS 2
-void mysql_unlock_tables(THD *thd, MYSQL_LOCK *sql_lock);
+void mysql_unlock_tables(THD *thd, MYSQL_LOCK *sql_lock, bool free_lock= 1);
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,
@@ -2223,6 +2222,8 @@ 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);
+MYSQL_LOCK *get_lock_data(THD *thd, TABLE **table_ptr, uint count,
+ uint flags, TABLE **write_lock_used);
/* Lock based on name */
int lock_and_wait_for_table_name(THD *thd, TABLE_LIST *table_list);
diff --git a/sql/mysqld.cc b/sql/mysqld.cc
index d7a457c6921..c086c34a39a 100644
--- a/sql/mysqld.cc
+++ b/sql/mysqld.cc
@@ -457,7 +457,7 @@ static bool volatile ready_to_exit;
static my_bool opt_debugging= 0, opt_external_locking= 0, opt_console= 0;
static my_bool opt_short_log_format= 0;
static my_bool opt_ignore_wrong_options= 0, opt_expect_abort= 0;
-static my_bool opt_sync= 0;
+static my_bool opt_sync= 0, opt_thread_alarm;
static uint kill_cached_threads, wake_thread;
ulong thread_created;
uint thread_handling;
@@ -6003,7 +6003,7 @@ enum options_mysqld
OPT_RANGE_ALLOC_BLOCK_SIZE, OPT_ALLOW_SUSPICIOUS_UDFS,
OPT_QUERY_ALLOC_BLOCK_SIZE, OPT_QUERY_PREALLOC_SIZE,
OPT_TRANS_ALLOC_BLOCK_SIZE, OPT_TRANS_PREALLOC_SIZE,
- OPT_SYNC_FRM, OPT_SYNC_BINLOG, OPT_SYNC,
+ OPT_SYNC_FRM, OPT_SYNC_BINLOG, OPT_SYNC, OPT_THREAD_ALARM,
OPT_SYNC_REPLICATION,
OPT_SYNC_REPLICATION_SLAVE_ID,
OPT_SYNC_REPLICATION_TIMEOUT,
@@ -6328,7 +6328,7 @@ struct my_option my_long_options[] =
"Disable initialization of builtin InnoDB plugin.",
0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
{"init-connect", OPT_INIT_CONNECT,
- "Command(s) that are executed for each new connection.",
+ "Command(s) that are executed for each new connection (but not for SUPER users).",
&opt_init_connect, &opt_init_connect, 0, GET_STR_ALLOC,
REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
#ifndef DISABLE_GRANT_OPTIONS
@@ -7579,6 +7579,10 @@ thread is in the relay logs.",
"error. Used only if the connection has active cursors.",
&table_lock_wait_timeout, &table_lock_wait_timeout,
0, GET_ULONG, REQUIRED_ARG, 50, 1, 1024 * 1024 * 1024, 0, 1, 0},
+ {"thread-alarm", OPT_THREAD_ALARM,
+ "Enable/disable system thread alarm calls. Should only be turned off when running tests or debugging!!",
+ &opt_thread_alarm, &opt_thread_alarm, 0, GET_BOOL, NO_ARG, 1, 0, 0, 0, 0,
+ 0},
{"thread_cache_size", OPT_THREAD_CACHE_SIZE,
"How many threads we should keep in a cache for reuse.",
&thread_cache_size, &thread_cache_size, 0, GET_ULONG,
@@ -9358,6 +9362,7 @@ static int get_options(int *argc,char **argv)
*/
my_disable_locking= myisam_single_user= test(opt_external_locking == 0);
my_disable_sync= opt_sync == 0;
+ my_disable_thr_alarm= opt_thread_alarm == 0;
my_default_record_cache_size=global_system_variables.read_buff_size;
myisam_max_temp_length=
(my_off_t) global_system_variables.myisam_max_sort_file_size;
diff --git a/sql/net_serv.cc b/sql/net_serv.cc
index 3441d1e120a..1b313da8ce1 100644
--- a/sql/net_serv.cc
+++ b/sql/net_serv.cc
@@ -697,7 +697,8 @@ net_real_write(NET *net,const uchar *packet, size_t len)
{
my_bool old_mode;
thr_end_alarm(&alarmed);
- vio_blocking(net->vio, net_blocking, &old_mode);
+ if (!net_blocking)
+ vio_blocking(net->vio, net_blocking, &old_mode);
}
net->reading_or_writing=0;
DBUG_RETURN(((int) (pos != end)));
@@ -988,7 +989,8 @@ end:
{
my_bool old_mode;
thr_end_alarm(&alarmed);
- vio_blocking(net->vio, net_blocking, &old_mode);
+ if (!net_blocking)
+ vio_blocking(net->vio, net_blocking, &old_mode);
}
net->reading_or_writing=0;
#ifdef DEBUG_DATA_PACKETS
diff --git a/sql/sql_base.cc b/sql/sql_base.cc
index 530ebf85955..01c72db0c8e 100644
--- a/sql/sql_base.cc
+++ b/sql/sql_base.cc
@@ -22,6 +22,7 @@
#include "sp_head.h"
#include "sp.h"
#include "sql_trigger.h"
+#include "sql_handler.h"
#include <m_ctype.h>
#include <my_dir.h>
#include <hash.h>
diff --git a/sql/sql_class.cc b/sql/sql_class.cc
index 084e2c8d78c..11447389186 100644
--- a/sql/sql_class.cc
+++ b/sql/sql_class.cc
@@ -44,6 +44,7 @@
#include "sp_cache.h"
#include "sql_select.h" /* declares create_tmp_table() */
#include "debug_sync.h"
+#include "sql_handler.h"
/*
The following is used to initialise Table_ident with a internal
@@ -702,7 +703,7 @@ THD::THD()
catalog= (char*)"std"; // the only catalog we have for now
main_security_ctx.init();
security_ctx= &main_security_ctx;
- locked=some_tables_deleted=no_errors=password= 0;
+ some_tables_deleted=no_errors=password= 0;
query_start_used= 0;
count_cuted_fields= CHECK_FIELD_IGNORE;
killed= NOT_KILLED;
diff --git a/sql/sql_class.h b/sql/sql_class.h
index 7f3b0cbfb03..8e0f1664286 100644
--- a/sql/sql_class.h
+++ b/sql/sql_class.h
@@ -1944,7 +1944,7 @@ public:
bool slave_thread, one_shot_set;
/* tells if current statement should binlog row-based(1) or stmt-based(0) */
bool current_stmt_binlog_row_based;
- bool locked, some_tables_deleted;
+ bool some_tables_deleted;
bool last_cuted_field;
bool no_errors, password;
bool extra_port; /* If extra connection */
diff --git a/sql/sql_db.cc b/sql/sql_db.cc
index 7aa48524b20..b5478287fda 100644
--- a/sql/sql_db.cc
+++ b/sql/sql_db.cc
@@ -20,6 +20,7 @@
#include <mysys_err.h>
#include "sp.h"
#include "events.h"
+#include "sql_handler.h"
#include <my_dir.h>
#include <m_ctype.h>
#include "log.h"
diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc
index 5564d628594..e2cb17090a1 100644
--- a/sql/sql_delete.cc
+++ b/sql/sql_delete.cc
@@ -23,6 +23,7 @@
#include "sql_select.h"
#include "sp_head.h"
#include "sql_trigger.h"
+#include "sql_handler.h"
/**
Implement DELETE SQL word.
diff --git a/sql/sql_handler.cc b/sql/sql_handler.cc
index 69be8c8e9b4..eaae5efe21c 100644
--- a/sql/sql_handler.cc
+++ b/sql/sql_handler.cc
@@ -56,9 +56,13 @@
second container. When the table is flushed, the pointer is cleared.
*/
+#ifdef USE_PRAGMA_IMPLEMENTATION
+#pragma implementation // gcc: Class implementation
+#endif
+
#include "mysql_priv.h"
#include "sql_select.h"
-#include <assert.h>
+#include "sql_handler.h"
#define HANDLER_TABLES_HASH_SIZE 120
@@ -66,6 +70,28 @@ static enum enum_ha_read_modes rkey_to_rnext[]=
{ RNEXT_SAME, RNEXT, RPREV, RNEXT, RPREV, RNEXT, RPREV, RPREV };
/*
+ Set handler to state after create, but keep base information about
+ which table is used
+*/
+
+void SQL_HANDLER::reset()
+{
+ fields.empty();
+ arena.free_items();
+ free_root(&mem_root, MYF(0));
+ my_free(lock, MYF(MY_ALLOW_ZERO_PTR));
+ init();
+}
+
+/* Free all allocated data */
+
+SQL_HANDLER::~SQL_HANDLER()
+{
+ reset();
+ my_free(base_data, MYF(MY_ALLOW_ZERO_PTR));
+}
+
+/*
Get hash key and hash key length.
SYNOPSIS
@@ -84,11 +110,11 @@ static enum enum_ha_read_modes rkey_to_rnext[]=
Pointer to the TABLE_LIST struct.
*/
-static char *mysql_ha_hash_get_key(TABLE_LIST *tables, size_t *key_len_p,
+static char *mysql_ha_hash_get_key(SQL_HANDLER *table, size_t *key_len,
my_bool first __attribute__((unused)))
{
- *key_len_p= strlen(tables->alias) + 1 ; /* include '\0' in comparisons */
- return tables->alias;
+ *key_len= table->handler_name.length + 1 ; /* include '\0' in comparisons */
+ return table->handler_name.str;
}
@@ -106,9 +132,9 @@ static char *mysql_ha_hash_get_key(TABLE_LIST *tables, size_t *key_len_p,
Nothing
*/
-static void mysql_ha_hash_free(TABLE_LIST *tables)
+static void mysql_ha_hash_free(SQL_HANDLER *table)
{
- my_free((char*) tables, MYF(0));
+ delete table;
}
/**
@@ -120,14 +146,21 @@ static void mysql_ha_hash_free(TABLE_LIST *tables)
@note Though this function takes a list of tables, only the first list entry
will be closed.
+ @mote handler_object is not deleted!
@note Broadcasts refresh if it closed a table with old version.
*/
-static void mysql_ha_close_table(THD *thd, TABLE_LIST *tables,
+static void mysql_ha_close_table(SQL_HANDLER *handler,
bool is_locked)
{
+ THD *thd= handler->thd;
+ TABLE *table= handler->table;
TABLE **table_ptr;
+ /* check if table was already closed */
+ if (!table)
+ return;
+
/*
Though we could take the table pointer from hash_tables->table,
we must follow the thd->handler_tables chain anyway, as we need the
@@ -135,13 +168,13 @@ static void mysql_ha_close_table(THD *thd, TABLE_LIST *tables,
for close_thread_table().
*/
for (table_ptr= &(thd->handler_tables);
- *table_ptr && (*table_ptr != tables->table);
+ *table_ptr && (*table_ptr != table);
table_ptr= &(*table_ptr)->next)
;
if (*table_ptr)
{
- (*table_ptr)->file->ha_index_or_rnd_end();
+ table->file->ha_index_or_rnd_end();
if (! is_locked)
VOID(pthread_mutex_lock(&LOCK_open));
if (close_thread_table(thd, table_ptr))
@@ -152,17 +185,15 @@ static void mysql_ha_close_table(THD *thd, TABLE_LIST *tables,
if (! is_locked)
VOID(pthread_mutex_unlock(&LOCK_open));
}
- else if (tables->table)
+ else
{
/* Must be a temporary table */
- TABLE *table= tables->table;
table->file->ha_index_or_rnd_end();
table->query_id= thd->query_id;
table->open_by_handler= 0;
}
-
- /* Mark table as closed, ready for re-open if necessary. */
- tables->table= NULL;
+ my_free(handler->lock, MYF(MY_ALLOW_ZERO_PTR));
+ handler->init();
}
/*
@@ -178,7 +209,7 @@ static void mysql_ha_close_table(THD *thd, TABLE_LIST *tables,
Though this function takes a list of tables, only the first list entry
will be opened.
'reopen' is set when a handler table is to be re-opened. In this case,
- 'tables' is the pointer to the hashed TABLE_LIST object which has been
+ 'tables' is the pointer to the hashed SQL_HANDLER object which has been
saved on the original open.
'reopen' is also used to suppress the sending of an 'ok' message.
@@ -187,17 +218,17 @@ static void mysql_ha_close_table(THD *thd, TABLE_LIST *tables,
TRUE Error
*/
-bool mysql_ha_open(THD *thd, TABLE_LIST *tables, bool reopen)
+bool mysql_ha_open(THD *thd, TABLE_LIST *tables, SQL_HANDLER *reopen)
{
- TABLE_LIST *hash_tables = NULL;
- char *db, *name, *alias;
- uint dblen, namelen, aliaslen, counter;
+ SQL_HANDLER *sql_handler= 0;
+ uint counter;
int error;
- TABLE *backup_open_tables;
+ TABLE *table, *backup_open_tables, *write_lock_used;
+ Query_arena backup_arena;
DBUG_ENTER("mysql_ha_open");
DBUG_PRINT("enter",("'%s'.'%s' as '%s' reopen: %d",
tables->db, tables->table_name, tables->alias,
- (int) reopen));
+ reopen != 0));
if (tables->schema_table)
{
@@ -210,7 +241,7 @@ bool mysql_ha_open(THD *thd, TABLE_LIST *tables, bool reopen)
if (! hash_inited(&thd->handler_tables_hash))
{
/*
- HASH entries are of type TABLE_LIST.
+ HASH entries are of type SQL_HANDLER
*/
if (hash_init(&thd->handler_tables_hash, &my_charset_latin1,
HANDLER_TABLES_HASH_SIZE, 0, 0,
@@ -288,8 +319,10 @@ bool mysql_ha_open(THD *thd, TABLE_LIST *tables, bool reopen)
if (error)
goto err;
+ table= tables->table;
+
/* There can be only one table in '*tables'. */
- if (! (tables->table->file->ha_table_flags() & HA_CAN_SQL_HANDLER))
+ if (! (table->file->ha_table_flags() & HA_CAN_SQL_HANDLER))
{
my_error(ER_ILLEGAL_HA, MYF(0), tables->alias);
goto err;
@@ -297,36 +330,69 @@ bool mysql_ha_open(THD *thd, TABLE_LIST *tables, bool reopen)
if (! reopen)
{
- /* copy the TABLE_LIST struct */
- dblen= strlen(tables->db) + 1;
- namelen= strlen(tables->table_name) + 1;
- aliaslen= strlen(tables->alias) + 1;
- if (!(my_multi_malloc(MYF(MY_WME),
- &hash_tables, (uint) sizeof(*hash_tables),
- &db, (uint) dblen,
- &name, (uint) namelen,
- &alias, (uint) aliaslen,
+ /* copy data to sql_handler */
+ if (!(sql_handler= new SQL_HANDLER(thd)))
+ goto err;
+ init_alloc_root(&sql_handler->mem_root, 1024, 0);
+
+ sql_handler->table= table;
+ sql_handler->db.length= strlen(tables->db);
+ sql_handler->table_name.length= strlen(tables->table_name);
+ sql_handler->handler_name.length= strlen(tables->alias);
+
+ if (!(my_multi_malloc(MY_WME,
+ &sql_handler->db.str,
+ (uint) sql_handler->db.length + 1,
+ &sql_handler->table_name.str,
+ (uint) sql_handler->table_name.length + 1,
+ &sql_handler->handler_name.str,
+ (uint) sql_handler->handler_name.length + 1,
NullS)))
goto err;
- /* structure copy */
- *hash_tables= *tables;
- hash_tables->db= db;
- hash_tables->table_name= name;
- hash_tables->alias= alias;
- memcpy(hash_tables->db, tables->db, dblen);
- memcpy(hash_tables->table_name, tables->table_name, namelen);
- memcpy(hash_tables->alias, tables->alias, aliaslen);
+ sql_handler->base_data= sql_handler->db.str; // Free this
+ memcpy(sql_handler->db.str, tables->db, sql_handler->db.length +1);
+ memcpy(sql_handler->table_name.str, tables->table_name,
+ sql_handler->table_name.length+1);
+ memcpy(sql_handler->handler_name.str, tables->alias,
+ sql_handler->handler_name.length +1);
/* add to hash */
- if (my_hash_insert(&thd->handler_tables_hash, (uchar*) hash_tables))
+ if (my_hash_insert(&thd->handler_tables_hash, (uchar*) sql_handler))
goto err;
}
+ else
+ {
+ sql_handler= reopen;
+ sql_handler->reset();
+ }
+ sql_handler->table= table;
+
+ if (!(sql_handler->lock= get_lock_data(thd, &sql_handler->table, 1,
+ GET_LOCK_STORE_LOCKS,
+ &write_lock_used)))
+ goto err;
+
+ /* Get a list of all fields for send_fields */
+ thd->set_n_backup_active_arena(&sql_handler->arena, &backup_arena);
+ error= table->fill_item_list(&sql_handler->fields);
+ thd->restore_active_arena(&sql_handler->arena, &backup_arena);
+
+ if (error)
+ {
+ if (reopen)
+ sql_handler= 0;
+ goto err;
+ }
+
+ /* Always read all columns */
+ table->read_set= &table->s->all_set;
+ table->vcol_set= &table->s->all_set;
/*
If it's a temp table, don't reset table->query_id as the table is
being used by this handler. Otherwise, no meaning at all.
*/
- tables->table->open_by_handler= 1;
+ table->open_by_handler= 1;
if (! reopen)
my_ok(thd);
@@ -334,10 +400,13 @@ bool mysql_ha_open(THD *thd, TABLE_LIST *tables, bool reopen)
DBUG_RETURN(FALSE);
err:
- if (hash_tables)
- my_free((char*) hash_tables, MYF(0));
+ delete sql_handler;
if (tables->table)
- mysql_ha_close_table(thd, tables, FALSE);
+ {
+ SQL_HANDLER tmp_sql_handler(thd);
+ tmp_sql_handler.table= tables->table;
+ mysql_ha_close_table(&tmp_sql_handler, FALSE);
+ }
DBUG_PRINT("exit",("ERROR"));
DBUG_RETURN(TRUE);
}
@@ -362,17 +431,17 @@ err:
bool mysql_ha_close(THD *thd, TABLE_LIST *tables)
{
- TABLE_LIST *hash_tables;
+ SQL_HANDLER *handler;
DBUG_ENTER("mysql_ha_close");
DBUG_PRINT("enter",("'%s'.'%s' as '%s'",
tables->db, tables->table_name, tables->alias));
- if ((hash_tables= (TABLE_LIST*) hash_search(&thd->handler_tables_hash,
- (uchar*) tables->alias,
- strlen(tables->alias) + 1)))
+ if ((handler= (SQL_HANDLER*) hash_search(&thd->handler_tables_hash,
+ (uchar*) tables->alias,
+ strlen(tables->alias) + 1)))
{
- mysql_ha_close_table(thd, hash_tables, FALSE);
- hash_delete(&thd->handler_tables_hash, (uchar*) hash_tables);
+ mysql_ha_close_table(handler, FALSE);
+ hash_delete(&thd->handler_tables_hash, (uchar*) handler);
}
else
{
@@ -387,6 +456,161 @@ bool mysql_ha_close(THD *thd, TABLE_LIST *tables)
}
+/**
+ Finds an open HANDLER table.
+
+ @params name Name of handler to open
+
+ @return 0 failure
+ @return handler
+*/
+
+SQL_HANDLER *mysql_ha_find_handler(THD *thd, const char *name)
+{
+ SQL_HANDLER *handler;
+ if ((handler= (SQL_HANDLER*) hash_search(&thd->handler_tables_hash,
+ (uchar*) name,
+ strlen(name) + 1)))
+ {
+ DBUG_PRINT("info-in-hash",("'%s'.'%s' as '%s' table: %p",
+ handler->db.str,
+ handler->table_name.str,
+ handler->handler_name.str, handler->table));
+ if (!handler->table)
+ {
+ /* The handler table has been closed. Re-open it. */
+ TABLE_LIST tmp;
+ tmp.init_one_table(handler->db.str, handler->table_name.str,
+ TL_READ);
+ tmp.alias= handler->handler_name.str;
+
+ if (mysql_ha_open(thd, &tmp, handler))
+ {
+ DBUG_PRINT("exit",("reopen failed"));
+ return 0;
+ }
+ }
+ }
+ else
+ {
+ my_error(ER_UNKNOWN_TABLE, MYF(0), name, "HANDLER");
+ return 0;
+ }
+ return handler;
+}
+
+
+/**
+ Check that condition and key name are ok
+
+ @param handler
+ @param mode Read mode (RFIRST, RNEXT etc...)
+ @param keyname Key to use.
+ @param key_expr List of key column values
+ @param cond Where clause
+ @param in_prepare If we are in prepare phase (we can't evalute items yet)
+
+ @return 0 ok
+ @return 1 error
+
+ In ok, then values of used key and mode is stored in sql_handler
+*/
+
+static bool
+mysql_ha_fix_cond_and_key(SQL_HANDLER *handler,
+ enum enum_ha_read_modes mode, char *keyname,
+ List<Item> *key_expr,
+ Item *cond, bool in_prepare)
+{
+ THD *thd= handler->thd;
+ TABLE *table= handler->table;
+ if (cond)
+ {
+ /* This can only be true for temp tables */
+ if (table->query_id != thd->query_id)
+ cond->cleanup(); // File was reopened
+ if ((!cond->fixed &&
+ cond->fix_fields(thd, &cond)) || cond->check_cols(1))
+ return 1;
+ }
+
+ if (keyname)
+ {
+ /* Check if same as last keyname. If not, do a full lookup */
+ if (handler->keyno < 0 ||
+ my_strcasecmp(&my_charset_latin1,
+ keyname,
+ table->s->key_info[handler->keyno].name))
+ {
+ if ((handler->keyno= find_type(keyname, &table->s->keynames, 1+2)-1)<0)
+ {
+ my_error(ER_KEY_DOES_NOT_EXITS, MYF(0), keyname,
+ handler->handler_name);
+ return 1;
+ }
+ }
+
+ /* Check key parts */
+ if (mode == RKEY)
+ {
+ TABLE *table= handler->table;
+ KEY *keyinfo= table->key_info + handler->keyno;
+ KEY_PART_INFO *key_part= keyinfo->key_part;
+ List_iterator<Item> it_ke(*key_expr);
+ Item *item;
+ key_part_map keypart_map;
+ uint key_len;
+
+ if (key_expr->elements > keyinfo->key_parts)
+ {
+ my_error(ER_TOO_MANY_KEY_PARTS, MYF(0), keyinfo->key_parts);
+ return 1;
+ }
+ for (keypart_map= key_len=0 ; (item=it_ke++) ; key_part++)
+ {
+ my_bitmap_map *old_map;
+ /* note that 'item' can be changed by fix_fields() call */
+ if ((!item->fixed &&
+ item->fix_fields(thd, it_ke.ref())) ||
+ (item= *it_ke.ref())->check_cols(1))
+ return 1;
+ if (item->used_tables() & ~(RAND_TABLE_BIT | PARAM_TABLE_BIT))
+ {
+ my_error(ER_WRONG_ARGUMENTS,MYF(0),"HANDLER ... READ");
+ return 1;
+ }
+ if (!in_prepare)
+ {
+ old_map= dbug_tmp_use_all_columns(table, table->write_set);
+ (void) item->save_in_field(key_part->field, 1);
+ dbug_tmp_restore_column_map(table->write_set, old_map);
+ }
+ key_len+= key_part->store_length;
+ keypart_map= (keypart_map << 1) | 1;
+ }
+ handler->keypart_map= keypart_map;
+ handler->key_len= key_len;
+ }
+ else
+ {
+ /*
+ Check if the same index involved.
+ We need to always do this check because we may not have yet
+ called the handler since the last keyno change.
+ */
+ if ((uint) handler->keyno != table->file->get_index())
+ {
+ if (mode == RNEXT)
+ mode= RFIRST;
+ else if (mode == RPREV)
+ mode= RLAST;
+ }
+ }
+ }
+ handler->mode= mode; // Store adjusted mode
+ return 0;
+}
+
/*
Read from a HANDLER table.
@@ -413,147 +637,77 @@ bool mysql_ha_read(THD *thd, TABLE_LIST *tables,
enum ha_rkey_function ha_rkey_mode, Item *cond,
ha_rows select_limit_cnt, ha_rows offset_limit_cnt)
{
- TABLE_LIST *hash_tables;
- TABLE *table, *backup_open_tables;
- MYSQL_LOCK *lock;
+ SQL_HANDLER *handler;
+ TABLE *table;
List<Item> list;
Protocol *protocol= thd->protocol;
char buff[MAX_FIELD_WIDTH];
String buffer(buff, sizeof(buff), system_charset_info);
- int error, keyno= -1;
+ int error, keyno;
uint num_rows;
uchar *UNINIT_VAR(key);
uint UNINIT_VAR(key_len);
bool need_reopen;
+ List_iterator<Item> it;
DBUG_ENTER("mysql_ha_read");
DBUG_PRINT("enter",("'%s'.'%s' as '%s'",
tables->db, tables->table_name, tables->alias));
- thd->lex->select_lex.context.resolve_in_table_list_only(tables);
- list.push_front(new Item_field(&thd->lex->select_lex.context,
- NULL, NULL, "*"));
- List_iterator<Item> it(list);
- it++;
retry:
- if ((hash_tables= (TABLE_LIST*) hash_search(&thd->handler_tables_hash,
- (uchar*) tables->alias,
- strlen(tables->alias) + 1)))
+ if (!(handler= mysql_ha_find_handler(thd, tables->alias)))
+ goto err0;
+
+ table= handler->table;
+ tables->table= table; // This is used by fix_fields
+
+ /* save open_tables state */
+ if (handler->lock->lock_count > 0)
{
- table= hash_tables->table;
- DBUG_PRINT("info-in-hash",("'%s'.'%s' as '%s' table: 0x%lx",
- hash_tables->db, hash_tables->table_name,
- hash_tables->alias, (long) table));
- if (!table)
+ bool lock_error;
+
+ handler->lock->locks[0]->type= handler->lock->locks[0]->org_type;
+ lock_error= mysql_lock_tables(thd, handler->lock, 0,
+ (MYSQL_LOCK_NOTIFY_IF_NEED_REOPEN |
+ (handler->table->s->tmp_table ==
+ NO_TMP_TABLE ?
+ MYSQL_LOCK_NOT_TEMPORARY : 0)),
+ &need_reopen);
+ if (need_reopen)
{
- /*
- The handler table has been closed. Re-open it.
- */
- if (mysql_ha_open(thd, hash_tables, 1))
+ mysql_ha_close_table(handler, FALSE);
+ if (thd->stmt_arena->is_stmt_execute())
{
- DBUG_PRINT("exit",("reopen failed"));
+ /*
+ As we have already sent field list and types to the client, we can't
+ handle any changes in the table format for prepared statements.
+ Better to force a reprepare.
+ */
+ my_error(ER_NEED_REPREPARE, MYF(0));
goto err0;
}
- table= hash_tables->table;
- DBUG_PRINT("info",("re-opened '%s'.'%s' as '%s' tab %p",
- hash_tables->db, hash_tables->table_name,
- hash_tables->alias, table));
- }
-
-#if MYSQL_VERSION_ID < 40100
- if (*tables->db && strcmp(table->table_cache_key, tables->db))
- {
- DBUG_PRINT("info",("wrong db"));
- table= NULL;
+ /*
+ The lock might have been aborted, we need to manually reset
+ thd->some_tables_deleted because handler's tables are closed
+ in a non-standard way. Otherwise we might loop indefinitely.
+ */
+ thd->some_tables_deleted= 0;
+ goto retry;
}
-#endif
- }
- else
- table= NULL;
-
- if (!table)
- {
-#if MYSQL_VERSION_ID < 40100
- char buff[MAX_DBKEY_LENGTH];
- if (*tables->db)
- strxnmov(buff, sizeof(buff)-1, tables->db, ".", tables->table_name,
- NullS);
- else
- strncpy(buff, tables->alias, sizeof(buff));
- my_error(ER_UNKNOWN_TABLE, MYF(0), buff, "HANDLER");
-#else
- my_error(ER_UNKNOWN_TABLE, MYF(0), tables->alias, "HANDLER");
-#endif
- goto err0;
- }
- tables->table=table;
-
- /* save open_tables state */
- backup_open_tables= thd->open_tables;
- /*
- mysql_lock_tables() needs thd->open_tables to be set correctly to
- be able to handle aborts properly. When the abort happens, it's
- safe to not protect thd->handler_tables because it won't close any
- tables.
- */
- thd->open_tables= thd->handler_tables;
-
- lock= mysql_lock_tables(thd, &tables->table, 1,
- MYSQL_LOCK_NOTIFY_IF_NEED_REOPEN, &need_reopen);
-
- /* restore previous context */
- thd->open_tables= backup_open_tables;
-
- if (need_reopen)
- {
- mysql_ha_close_table(thd, hash_tables, FALSE);
- /*
- The lock might have been aborted, we need to manually reset
- thd->some_tables_deleted because handler's tables are closed
- in a non-standard way. Otherwise we might loop indefinitely.
- */
- thd->some_tables_deleted= 0;
- goto retry;
- }
-
- if (!lock)
- goto err0; // mysql_lock_tables() printed error message already
-
- // Always read all columns
- tables->table->read_set= &tables->table->s->all_set;
-
- if (cond)
- {
- if (table->query_id != thd->query_id)
- cond->cleanup(); // File was reopened
- if ((!cond->fixed &&
- cond->fix_fields(thd, &cond)) || cond->check_cols(1))
- goto err;
- }
- if (keyname)
- {
- if ((keyno=find_type(keyname, &table->s->keynames, 1+2)-1)<0)
- {
- my_error(ER_KEY_DOES_NOT_EXITS, MYF(0), keyname, tables->alias);
- goto err;
- }
- /* Check if the same index involved. */
- if ((uint) keyno != table->file->get_index())
- {
- if (mode == RNEXT)
- mode= RFIRST;
- else if (mode == RPREV)
- mode= RLAST;
- }
+ if (lock_error)
+ goto err0; // mysql_lock_tables() printed error message already
}
- if (insert_fields(thd, &thd->lex->select_lex.context,
- tables->db, tables->alias, &it, 0))
+ if (mysql_ha_fix_cond_and_key(handler, mode, keyname, key_expr, cond, 0))
goto err;
+ mode= handler->mode;
+ keyno= handler->keyno;
- protocol->send_fields(&list, Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF);
+ it.init(handler->fields);
+ protocol->send_fields(&handler->fields,
+ Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF);
/*
In ::external_lock InnoDB resets the fields which tell it that
@@ -576,9 +730,7 @@ retry:
error= table->file->ha_index_next(table->record[0]);
}
else
- {
error= table->file->ha_rnd_next(table->record[0]);
- }
break;
}
/* else fall through */
@@ -595,7 +747,7 @@ retry:
if (!(error= table->file->ha_rnd_init(1)))
error= table->file->ha_rnd_next(table->record[0]);
}
- mode=RNEXT;
+ mode= RNEXT;
break;
case RPREV:
DBUG_ASSERT(keyname != 0);
@@ -612,7 +764,7 @@ retry:
table->file->ha_index_or_rnd_end();
table->file->ha_index_init(keyno, 1);
error= table->file->ha_index_last(table->record[0]);
- mode=RPREV;
+ mode= RPREV;
break;
case RNEXT_SAME:
/* Continue scan on "(keypart1,keypart2,...)=(c1, c2, ...) */
@@ -622,44 +774,17 @@ retry:
case RKEY:
{
DBUG_ASSERT(keyname != 0);
- KEY *keyinfo=table->key_info+keyno;
- KEY_PART_INFO *key_part=keyinfo->key_part;
- if (key_expr->elements > keyinfo->key_parts)
- {
- my_error(ER_TOO_MANY_KEY_PARTS, MYF(0), keyinfo->key_parts);
- goto err;
- }
- List_iterator<Item> it_ke(*key_expr);
- Item *item;
- key_part_map keypart_map;
- for (keypart_map= key_len=0 ; (item=it_ke++) ; key_part++)
- {
- my_bitmap_map *old_map;
- // 'item' can be changed by fix_fields() call
- if ((!item->fixed &&
- item->fix_fields(thd, it_ke.ref())) ||
- (item= *it_ke.ref())->check_cols(1))
- goto err;
- if (item->used_tables() & ~RAND_TABLE_BIT)
- {
- my_error(ER_WRONG_ARGUMENTS,MYF(0),"HANDLER ... READ");
- goto err;
- }
- old_map= dbug_tmp_use_all_columns(table, table->write_set);
- (void) item->save_in_field(key_part->field, 1);
- dbug_tmp_restore_column_map(table->write_set, old_map);
- key_len+=key_part->store_length;
- keypart_map= (keypart_map << 1) | 1;
- }
- if (!(key= (uchar*) thd->calloc(ALIGN_SIZE(key_len))))
+ if (!(key= (uchar*) thd->calloc(ALIGN_SIZE(handler->key_len))))
goto err;
table->file->ha_index_or_rnd_end();
table->file->ha_index_init(keyno, 1);
- key_copy(key, table->record[0], table->key_info + keyno, key_len);
+ key_copy(key, table->record[0], table->key_info + keyno,
+ handler->key_len);
error= table->file->ha_index_read_map(table->record[0],
- key, keypart_map, ha_rkey_mode);
- mode=rkey_to_rnext[(int)ha_rkey_mode];
+ key, handler->keypart_map,
+ ha_rkey_mode);
+ mode= rkey_to_rnext[(int)ha_rkey_mode];
break;
}
default:
@@ -703,13 +828,13 @@ retry:
num_rows++;
}
ok:
- mysql_unlock_tables(thd,lock);
+ mysql_unlock_tables(thd, handler->lock, 0);
my_eof(thd);
DBUG_PRINT("exit",("OK"));
DBUG_RETURN(FALSE);
err:
- mysql_unlock_tables(thd,lock);
+ mysql_unlock_tables(thd, handler->lock, 0);
err0:
DBUG_PRINT("exit",("ERROR"));
DBUG_RETURN(TRUE);
@@ -717,6 +842,28 @@ err0:
/**
+ Prepare for handler read
+
+ For parameters, see mysql_ha_read()
+*/
+
+SQL_HANDLER *mysql_ha_read_prepare(THD *thd, TABLE_LIST *tables,
+ enum enum_ha_read_modes mode, char *keyname,
+ List<Item> *key_expr, Item *cond)
+{
+ SQL_HANDLER *handler;
+ DBUG_ENTER("mysql_ha_read_prepare");
+ if (!(handler= mysql_ha_find_handler(thd, tables->alias)))
+ DBUG_RETURN(0);
+ tables->table= handler->table; // This is used by fix_fields
+ if (mysql_ha_fix_cond_and_key(handler, mode, keyname, key_expr, cond, 1))
+ DBUG_RETURN(0);
+ DBUG_RETURN(handler);
+}
+
+
+
+/**
Scan the handler tables hash for matching tables.
@param thd Thread identifier.
@@ -727,30 +874,32 @@ err0:
table was matched.
*/
-static TABLE_LIST *mysql_ha_find(THD *thd, TABLE_LIST *tables)
+static SQL_HANDLER *mysql_ha_find_match(THD *thd, TABLE_LIST *tables)
{
- TABLE_LIST *hash_tables, *head= NULL, *first= tables;
- DBUG_ENTER("mysql_ha_find");
+ SQL_HANDLER *hash_tables, *head= NULL;
+ TABLE_LIST *first= tables;
+ DBUG_ENTER("mysql_ha_find_match");
/* search for all handlers with matching table names */
for (uint i= 0; i < thd->handler_tables_hash.records; i++)
{
- hash_tables= (TABLE_LIST*) hash_element(&thd->handler_tables_hash, i);
+ hash_tables= (SQL_HANDLER*) hash_element(&thd->handler_tables_hash, i);
+
for (tables= first; tables; tables= tables->next_local)
{
if ((! *tables->db ||
- ! my_strcasecmp(&my_charset_latin1, hash_tables->db, tables->db)) &&
- ! my_strcasecmp(&my_charset_latin1, hash_tables->table_name,
+ ! my_strcasecmp(&my_charset_latin1, hash_tables->db.str,
+ tables->db)) &&
+ ! my_strcasecmp(&my_charset_latin1, hash_tables->table_name.str,
tables->table_name))
+ {
+ /* Link into hash_tables list */
+ hash_tables->next= head;
+ head= hash_tables;
break;
- }
- if (tables)
- {
- hash_tables->next_local= head;
- head= hash_tables;
+ }
}
}
-
DBUG_RETURN(head);
}
@@ -767,18 +916,18 @@ static TABLE_LIST *mysql_ha_find(THD *thd, TABLE_LIST *tables)
void mysql_ha_rm_tables(THD *thd, TABLE_LIST *tables, bool is_locked)
{
- TABLE_LIST *hash_tables, *next;
+ SQL_HANDLER *hash_tables, *next;
DBUG_ENTER("mysql_ha_rm_tables");
DBUG_ASSERT(tables);
- hash_tables= mysql_ha_find(thd, tables);
+ hash_tables= mysql_ha_find_match(thd, tables);
while (hash_tables)
{
- next= hash_tables->next_local;
+ next= hash_tables->next;
if (hash_tables->table)
- mysql_ha_close_table(thd, hash_tables, is_locked);
+ mysql_ha_close_table(hash_tables, is_locked);
hash_delete(&thd->handler_tables_hash, (uchar*) hash_tables);
hash_tables= next;
}
@@ -798,16 +947,16 @@ void mysql_ha_rm_tables(THD *thd, TABLE_LIST *tables, bool is_locked)
void mysql_ha_flush(THD *thd)
{
- TABLE_LIST *hash_tables;
+ SQL_HANDLER *hash_tables;
DBUG_ENTER("mysql_ha_flush");
safe_mutex_assert_owner(&LOCK_open);
for (uint i= 0; i < thd->handler_tables_hash.records; i++)
{
- hash_tables= (TABLE_LIST*) hash_element(&thd->handler_tables_hash, i);
+ hash_tables= (SQL_HANDLER*) hash_element(&thd->handler_tables_hash, i);
if (hash_tables->table && hash_tables->table->needs_reopen_or_name_lock())
- mysql_ha_close_table(thd, hash_tables, TRUE);
+ mysql_ha_close_table(hash_tables, TRUE);
}
DBUG_VOID_RETURN;
@@ -824,14 +973,14 @@ void mysql_ha_flush(THD *thd)
void mysql_ha_cleanup(THD *thd)
{
- TABLE_LIST *hash_tables;
+ SQL_HANDLER *hash_tables;
DBUG_ENTER("mysql_ha_cleanup");
for (uint i= 0; i < thd->handler_tables_hash.records; i++)
{
- hash_tables= (TABLE_LIST*) hash_element(&thd->handler_tables_hash, i);
+ hash_tables= (SQL_HANDLER*) hash_element(&thd->handler_tables_hash, i);
if (hash_tables->table)
- mysql_ha_close_table(thd, hash_tables, FALSE);
+ mysql_ha_close_table(hash_tables, FALSE);
}
hash_free(&thd->handler_tables_hash);
diff --git a/sql/sql_handler.h b/sql/sql_handler.h
new file mode 100644
index 00000000000..54e72e9f50e
--- /dev/null
+++ b/sql/sql_handler.h
@@ -0,0 +1,61 @@
+/* Copyright (C) 2010 Monty Program 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; version 2 of the License.
+
+ 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 */
+
+#ifdef USE_PRAGMA_INTERFACE
+#pragma interface /* gcc class implementation */
+#endif
+
+/* Open handlers are stored here */
+
+class SQL_HANDLER {
+public:
+ TABLE *table;
+ List<Item> fields; /* Fields, set on open */
+ THD *thd;
+ LEX_STRING handler_name;
+ LEX_STRING db;
+ LEX_STRING table_name;
+ MEM_ROOT mem_root;
+ MYSQL_LOCK *lock;
+
+ key_part_map keypart_map;
+ int keyno; /* Used key */
+ uint key_len;
+ enum enum_ha_read_modes mode;
+
+ /* This is only used when deleting many handler objects */
+ SQL_HANDLER *next;
+
+ Query_arena arena;
+ char *base_data;
+ SQL_HANDLER(THD *thd_arg) :
+ thd(thd_arg), arena(&mem_root, Query_arena::INITIALIZED)
+ { init(); clear_alloc_root(&mem_root); base_data= 0; }
+ void init() { keyno= -1; table= 0; lock= 0; }
+ void reset();
+
+ ~SQL_HANDLER();
+};
+
+bool mysql_ha_open(THD *thd, TABLE_LIST *tables, SQL_HANDLER *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);
+void mysql_ha_flush(THD *thd);
+void mysql_ha_rm_tables(THD *thd, TABLE_LIST *tables, bool is_locked);
+void mysql_ha_cleanup(THD *thd);
+
+SQL_HANDLER *mysql_ha_read_prepare(THD *thd, TABLE_LIST *tables,
+ enum enum_ha_read_modes mode, char *keyname,
+ List<Item> *key_expr, Item *cond);
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc
index 9212017d955..aa3a696e2fa 100644
--- a/sql/sql_parse.cc
+++ b/sql/sql_parse.cc
@@ -28,6 +28,7 @@
#include "events.h"
#include "sql_trigger.h"
#include "debug_sync.h"
+#include "sql_handler.h"
#ifdef WITH_ARIA_STORAGE_ENGINE
#include "../storage/maria/ha_maria.h"
diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc
index a1243589ed1..8a9870e2865 100644
--- a/sql/sql_prepare.cc
+++ b/sql/sql_prepare.cc
@@ -95,6 +95,7 @@ When one supplies long data for a placeholder:
#else
#include <mysql_com.h>
#endif
+#include "sql_handler.h"
/**
A result class used to send cursor rows using the binary protocol.
@@ -243,6 +244,8 @@ static bool send_prep_stmt(Prepared_statement *stmt, uint columns)
int error;
THD *thd= stmt->thd;
DBUG_ENTER("send_prep_stmt");
+ DBUG_PRINT("enter",("stmt->id: %lu columns: %d param_count: %d",
+ stmt->id, columns, stmt->param_count));
buff[0]= 0; /* OK packet indicator */
int4store(buff+1, stmt->id);
@@ -1835,6 +1838,56 @@ static bool mysql_test_insert_select(Prepared_statement *stmt,
return res;
}
+/**
+ Validate SELECT statement.
+
+ In case of success, if this query is not EXPLAIN, send column list info
+ back to the client.
+
+ @param stmt prepared statement
+ @param tables list of tables used in the query
+
+ @retval 0 success
+ @retval 1 error, error message is set in THD
+ @retval 2 success, and statement metadata has been sent
+*/
+
+static int mysql_test_handler_read(Prepared_statement *stmt,
+ TABLE_LIST *tables)
+{
+ THD *thd= stmt->thd;
+ LEX *lex= stmt->lex;
+ SQL_HANDLER *ha_table;
+ DBUG_ENTER("mysql_test_select");
+
+ lex->select_lex.context.resolve_in_select_list= TRUE;
+
+ /*
+ We don't have to test for permissions as this is already done during
+ HANDLER OPEN
+ */
+ if (!(ha_table= mysql_ha_read_prepare(thd, tables, lex->ha_read_mode,
+ lex->ident.str,
+ lex->insert_list,
+ lex->select_lex.where)))
+ DBUG_RETURN(1);
+
+ if (!stmt->is_sql_prepare())
+ {
+ if (!lex->result && !(lex->result= new (stmt->mem_root) select_send))
+ {
+ my_error(ER_OUTOFMEMORY, MYF(0), sizeof(select_send));
+ DBUG_RETURN(1);
+ }
+ if (send_prep_stmt(stmt, ha_table->fields.elements) ||
+ lex->result->send_fields(ha_table->fields, Protocol::SEND_EOF) ||
+ thd->protocol->flush())
+ DBUG_RETURN(1);
+ DBUG_RETURN(2);
+ }
+ DBUG_RETURN(0);
+}
+
/**
Perform semantic analysis of the parsed tree and send a response packet
@@ -1949,6 +2002,14 @@ static bool check_prepared_statement(Prepared_statement *stmt)
res= mysql_test_insert_select(stmt, tables);
break;
+ case SQLCOM_HA_READ:
+ res= mysql_test_handler_read(stmt, tables);
+ {
+ /* Statement and field info has already been sent */
+ DBUG_RETURN(FALSE);
+ }
+ break;
+
/*
Note that we don't need to have cases in this list if they are
marked with CF_STATUS_COMMAND in sql_command_flags
diff --git a/sql/sql_rename.cc b/sql/sql_rename.cc
index e85e730db5b..5c8d1add04b 100644
--- a/sql/sql_rename.cc
+++ b/sql/sql_rename.cc
@@ -19,7 +19,7 @@
#include "mysql_priv.h"
#include "sql_trigger.h"
-
+#include "sql_handler.h"
static TABLE_LIST *rename_tables(THD *thd, TABLE_LIST *table_list,
bool skip_error);
diff --git a/sql/sql_show.cc b/sql/sql_show.cc
index cdfb982b372..61f3709476c 100644
--- a/sql/sql_show.cc
+++ b/sql/sql_show.cc
@@ -1942,8 +1942,7 @@ void mysqld_list_processes(THD *thd,const char *user, bool verbose)
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 ?
+ thd_info->state_info= (char*) (tmp->net.reading_or_writing ?
(tmp->net.reading_or_writing == 2 ?
"Writing to net" :
thd_info->command == COM_SLEEP ? "" :
@@ -2068,8 +2067,7 @@ int fill_schema_processlist(THD* thd, TABLE_LIST* tables, COND* cond)
table->field[5]->store(utime / 1000000, TRUE);
/* STATE */
#ifndef EMBEDDED_LIBRARY
- val= (char*) (tmp->locked ? "Locked" :
- tmp->net.reading_or_writing ?
+ val= (char*) (tmp->net.reading_or_writing ?
(tmp->net.reading_or_writing == 2 ?
"Writing to net" :
tmp->command == COM_SLEEP ? "" :
diff --git a/sql/sql_table.cc b/sql/sql_table.cc
index 05a9c66cf80..b0f5c839412 100644
--- a/sql/sql_table.cc
+++ b/sql/sql_table.cc
@@ -24,6 +24,7 @@
#include "sql_trigger.h"
#include "sql_show.h"
#include "debug_sync.h"
+#include "sql_handler.h"
#ifdef __WIN__
#include <io.h>
diff --git a/sql/sql_trigger.cc b/sql/sql_trigger.cc
index 1c4add27e57..8b95a36bc35 100644
--- a/sql/sql_trigger.cc
+++ b/sql/sql_trigger.cc
@@ -19,6 +19,7 @@
#include "sp_head.h"
#include "sql_trigger.h"
#include "parse_file.h"
+#include "sql_handler.h"
/*************************************************************************/