mirror of
https://github.com/MariaDB/server.git
synced 2025-08-08 11:22:35 +03:00
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
This commit is contained in:
@@ -56,15 +56,41 @@
|
||||
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
|
||||
|
||||
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.
|
||||
|
||||
@@ -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));
|
||||
/*
|
||||
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 MYSQL_VERSION_ID < 40100
|
||||
if (*tables->db && strcmp(table->table_cache_key, tables->db))
|
||||
{
|
||||
DBUG_PRINT("info",("wrong db"));
|
||||
table= NULL;
|
||||
}
|
||||
#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_error)
|
||||
goto err0; // mysql_lock_tables() printed error message already
|
||||
}
|
||||
|
||||
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 (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,19 +828,41 @@ 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);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
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.
|
||||
|
||||
@@ -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);
|
||||
|
Reference in New Issue
Block a user