Quelle ha_myisammrg.cc
Sprache: C
/* Copyright (c) 2000, 2011, Oracle and/or its affiliates
Copyright ( c ) 2009 , 2016 , MariaDB
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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA */
/*
MyISAM MERGE tables
A MyISAM MERGE table is kind of a union of zero or more MyISAM tables .
Besides the normal form file ( . frm ) a MERGE table has a meta file
( . MRG ) with a list of tables . These are paths to the MyISAM table
files . The last two components of the path contain the database name
and the table name respectively .
When a MERGE table is open , there exists an TABLE object for the MERGE
table itself and a TABLE object for each of the MyISAM tables . For
abbreviated writing , I call the MERGE table object " parent " and the
MyISAM table objects " children " .
A MERGE table is almost always opened through open_and_lock_tables ( )
and hence through open_tables ( ) . When the parent appears in the list
of tables to open , the initial open of the handler does nothing but
read the meta file and collect a list of TABLE_LIST objects for the
children . This list is attached to the handler object as
ha_myisammrg : : children_l . The end of the children list is saved in
ha_myisammrg : : children_last_l .
Back in open_tables ( ) , handler : : extra ( HA_EXTRA_ADD_CHILDREN_LIST ) is
called . It updates each list member with the lock type and a back
pointer to the parent TABLE_LIST object TABLE_LIST : : parent_l . The list
is then inserted in the list of tables to open , right behind the
parent . Consequently , open_tables ( ) opens the children , one after the
other . The TABLE references of the TABLE_LIST objects are implicitly
set to the open tables by open_table ( ) . The children are opened as
independent MyISAM tables , right as if they are used by the SQL
statement .
When all tables from the statement query list are open ,
handler : : extra ( HA_EXTRA_ATTACH_CHILDREN ) is called . It " attaches " the
children to the parent . All required references between parent and
children are set up .
The MERGE storage engine sets up an array with references to the
low - level MyISAM table objects ( MI_INFO ) . It remembers the state of
the table in MYRG_INFO : : children_attached .
If necessary , the compatibility of parent and children is checked .
This check is necessary when any of the objects are reopened . This is
detected by comparing the current table def version against the
remembered child def version . On parent open , the list members are
initialized to an " impossible " / " undefined " version value . So the check
is always executed on the first attach .
The version check is done in myisammrg_attach_children_callback ( ) ,
which is called for every child . ha_myisammrg : : attach_children ( )
initializes ' need_compat_check ' to FALSE and
myisammrg_attach_children_callback ( ) sets it ot TRUE if a table
def version mismatches the remembered child def version .
The children chain remains in the statement query list until the table
is closed or the children are detached . This is done so that the
children are locked by lock_tables ( ) .
At statement end the children are detached . At the next statement
begin the open - add - attach sequence repeats . There is no exception for
LOCK TABLES . The fresh establishment of the parent - child relationship
before every statement catches numerous cases of ALTER / FLUSH / DROP / etc
of parent or children during LOCK TABLES .
- - -
On parent open the storage engine structures are allocated and initialized .
They stay with the open table until its final close .
*/
#define MYSQL_SERVER 1
#include <my_global.h>
#include "sql_priv.h"
#include "unireg.h"
#include "sql_cache.h" // query_cache_*
#include "sql_show.h" // append_identifier
#include "sql_table.h" // build_table_filename
#include <m_ctype.h>
#include "../myisam/ha_myisam.h"
#include "ha_myisammrg.h"
#include "myrg_def.h"
#include "thr_malloc.h" // init_sql_alloc
#include "sql_class.h" // THD
#include "debug_sync.h"
static handler *myisammrg_create_handler(handlerton *hton,
TABLE_SHARE *table,
MEM_ROOT *mem_root)
{
return new (mem_root) ha_myisammrg(hton, table);
}
/**
@ brief Constructor
*/
ha_myisammrg::ha_myisammrg(handlerton *hton, TABLE_SHARE *table_arg)
:handler(hton, table_arg), file(0 ), is_cloned(0 )
{
init_sql_alloc(rg_key_memory_children, &children_mem_root,
FN_REFLEN, 0 , MYF(0 ));
}
/**
@ brief Destructor
*/
ha_myisammrg::~ha_myisammrg(void )
{
free_root(&children_mem_root, MYF(0 ));
}
static const char *ha_myisammrg_exts[] = {
MYRG_NAME_EXT,
NullS
};
extern int table2myisam(TABLE *table_arg, MI_KEYDEF **keydef_out,
MI_COLUMNDEF **recinfo_out, uint *records_out);
extern int check_definition(MI_KEYDEF *t1_keyinfo,
MI_COLUMNDEF *t1_recinfo,
uint t1_keys, uint t1_recs,
MI_KEYDEF *t2_keyinfo,
MI_COLUMNDEF *t2_recinfo,
uint t2_keys, uint t2_recs, bool strict,
TABLE *table_arg);
static void split_file_name(const char *file_name,
LEX_STRING *db, LEX_STRING *name);
extern "C" void myrg_print_wrong_table(const char *table_name)
{
LEX_STRING db= {NULL, 0 }, name;
char buf[FN_REFLEN];
split_file_name(table_name, &db, &name);
memcpy(buf, db.str, db.length);
buf[db.length]= '.' ;
memcpy(buf + db.length + 1 , name.str, name.length);
buf[db.length + name.length + 1 ]= 0 ;
/*
Push an error to be reported as part of CHECK / REPAIR result - set .
Note that calling my_error ( ) from handler is a hack which is kept
here to avoid refactoring . Normally engines should report errors
through return value which will be interpreted by caller using
handler : : print_error ( ) call .
*/
my_error(ER_ADMIN_WRONG_MRG_TABLE, MYF(0 ), buf);
}
/**
Callback function for open of a MERGE parent table .
@ param [ in ] callback_param data pointer as given to myrg_parent_open ( )
this is used to pass the handler handle
@ param [ in ] filename file name of MyISAM table
without extension .
@ return status
@ retval 0 OK
@ retval ! = 0 Error
@ detail
This function adds a TABLE_LIST object for a MERGE child table to a
list of tables in the parent handler object . It is called for each
child table .
The list of child TABLE_LIST objects is kept in the handler object
of the parent for the whole life time of the MERGE table . It is
inserted in the statement query list behind the MERGE parent
TABLE_LIST object when the MERGE table is opened . It is removed from
the statement query list at end of statement or at children detach .
All memory used for the child TABLE_LIST objects and the strings
referred by it are taken from the parent
ha_myisammrg : : children_mem_root . Thus they are all freed implicitly at
the final close of the table .
children_l - > TABLE_LIST : : next_global - > TABLE_LIST : : next_global
# # ^ # ^
# # | # |
# # + - - - - - - - - - TABLE_LIST : : prev_global
# # |
# | < - - - TABLE_LIST : : prev_global |
# |
children_last_l - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
*/
CPP_UNNAMED_NS_START
extern "C" int myisammrg_parent_open_callback(void *callback_param,
const char *filename)
{
ha_myisammrg *ha_myrg= (ha_myisammrg*) callback_param;
TABLE *parent= ha_myrg->table_ptr();
Mrg_child_def *mrg_child_def;
LEX_STRING db, table_name;
char dir_path[FN_REFLEN];
DBUG_ENTER("myisammrg_parent_open_callback" );
/*
Depending on MySQL version , filename may be encoded by table name to
file name encoding or not . Always encoded if parent table is created
by 5 . 1 . 46 + . Encoded if parent is created by 5 . 1 . 6 + and child table is
in different database .
*/
if (!has_path(filename))
{
/* Child is in the same database as parent. */
db= ha_myrg->make_child_ident(parent->s->db);
/* Child table name is encoded in parent dot-MRG starting with 5.1.46. */
table_name= (parent->s->mysql_version >= 50146 ) ?
ha_myrg->make_child_ident_filename_to_tablename(filename,
lower_case_table_names) :
ha_myrg->make_child_ident_opt_casedn(Lex_cstring_strlen(filename),
lower_case_table_names);
}
else
{
DBUG_ASSERT(strlen(filename) < sizeof (dir_path));
fn_format(dir_path, filename, "" , "" , 0 );
/* Extract child table name and database name from filename. */
size_t dirlen= dirname_length(dir_path);
/* Child db/table name is encoded in parent dot-MRG starting with 5.1.6. */
if (parent->s->mysql_version >= 50106 )
{
table_name= ha_myrg->make_child_ident_filename_to_tablename(
dir_path + dirlen,
lower_case_table_names);
dir_path[dirlen - 1 ]= 0 ;
dirlen= dirname_length(dir_path);
db= ha_myrg->make_child_ident_filename_to_tablename(dir_path + dirlen,
false );
}
else
{
table_name= ha_myrg->make_child_ident_opt_casedn(
Lex_cstring_strlen(dir_path + dirlen),
lower_case_table_names);
dir_path[dirlen - 1 ]= 0 ;
dirlen= dirname_length(dir_path);
db= ha_myrg->make_child_ident(Lex_cstring_strlen(dir_path + dirlen));
}
}
if (! db.str || ! table_name.str)
DBUG_RETURN(1 );
DBUG_PRINT("myrg" , ("open: '%.*s'.'%.*s'" , (int ) db.length, db.str,
(int ) table_name.length, table_name.str));
mrg_child_def= new (&ha_myrg->children_mem_root)
Mrg_child_def(db.str, db.length,
table_name.str, table_name.length);
if (! mrg_child_def ||
ha_myrg->child_def_list.push_back(mrg_child_def,
&ha_myrg->children_mem_root))
{
DBUG_RETURN(1 );
}
DBUG_RETURN(0 );
}
CPP_UNNAMED_NS_END
/*
Set external_ref for the child MyISAM tables . They need this to be set in
order to check for killed status .
*/
static void myrg_set_external_ref(MYRG_INFO *m_info, void *ext_ref_arg)
{
int i;
for (i= 0 ; i < (int )m_info->tables; i++)
{
m_info->open_tables[i].table->external_ref= ext_ref_arg;
}
}
IO_AND_CPU_COST ha_myisammrg::rnd_pos_time(ha_rows rows)
{
IO_AND_CPU_COST cost= handler::rnd_pos_time(rows);
/*
Row data is not cached . costs . row_lookup_cost includes the cost of
the reading the row from system ( probably cached by the OS ) .
*/
cost.io= 0 ;
return cost;
}
IO_AND_CPU_COST ha_myisammrg::keyread_time(uint index, ulong ranges,
ha_rows rows,
ulonglong blocks)
{
IO_AND_CPU_COST cost= handler::keyread_time(index, ranges, rows, blocks);
if (!blocks)
{
cost.io*= file->tables;
cost.cpu*= file->tables;
}
/* Add the cost of having to do a key lookup in all trees */
if (file->tables)
cost.cpu+= (file->tables-1 ) * (ranges * KEY_LOOKUP_COST);
return cost;
}
/**
Open a MERGE parent table , but not its children .
@ param [ in ] name MERGE table path name
@ param [ in ] mode read / write mode , unused
@ param [ in ] test_if_locked_arg open flags
@ return status
@ retval 0 OK
@ retval - 1 Error , my_errno gives reason
@ detail
This function initializes the MERGE storage engine structures
and adds a child list of TABLE_LIST to the parent handler .
*/
int ha_myisammrg::open(const char *name, int mode __attribute__((unused)),
uint test_if_locked_arg)
{
DBUG_ENTER("ha_myisammrg::open" );
DBUG_PRINT("myrg" , ("name: '%s' table: %p" , name, table));
DBUG_PRINT("myrg" , ("test_if_locked_arg: %u" , test_if_locked_arg));
/* Must not be used when table is open. */
DBUG_ASSERT(!this ->file);
/* Save for later use. */
test_if_locked= test_if_locked_arg;
/* In case this handler was open and closed before, free old data. */
free_root(&this ->children_mem_root, MYF(MY_MARK_BLOCKS_FREE));
/*
Initialize variables that are used , modified , and / or set by
myisammrg_parent_open_callback ( ) .
' children_l ' is the head of the children chain .
' children_last_l ' points to the end of the children chain .
' my_errno ' is set by myisammrg_parent_open_callback ( ) in
case of an error .
*/
children_l= NULL;
children_last_l= NULL;
child_def_list.empty();
my_errno= 0 ;
/* retrieve children table list. */
if (is_cloned)
{
/*
Open and attaches the MyISAM tables , that are under the MERGE table
parent , on the MyISAM storage engine interface directly within the
MERGE engine . The new MyISAM table instances , as well as the MERGE
clone itself , are not visible in the table cache . This is not a
problem because all locking is handled by the original MERGE table
from which this is cloned of .
*/
if (!(file= myrg_open(name, table->db_stat, HA_OPEN_IGNORE_IF_LOCKED)))
{
DBUG_PRINT("error" , ("my_errno %d" , my_errno));
DBUG_RETURN(my_errno ? my_errno : -1 );
}
file->children_attached= TRUE ;
myrg_set_external_ref(file, (void *)table);
info(HA_STATUS_NO_LOCK | HA_STATUS_VARIABLE | HA_STATUS_CONST);
}
else if (!(file= myrg_parent_open(name, myisammrg_parent_open_callback, this )))
{
/* purecov: begin inspected */
DBUG_PRINT("error" , ("my_errno %d" , my_errno));
DBUG_RETURN(my_errno ? my_errno : -1 );
/* purecov: end */
}
DBUG_PRINT("myrg" , ("MYRG_INFO: %p child tables: %u" ,
file, file->tables));
DBUG_RETURN(0 );
}
/**
Add list of MERGE children to a TABLE_LIST chain .
@ return status
@ retval 0 OK
@ retval ! = 0 Error
@ detail
When a MERGE parent table has just been opened , insert the
TABLE_LIST chain from the MERGE handler into the table list used for
opening tables for this statement . This lets the children be opened
too .
*/
int ha_myisammrg::add_children_list(void )
{
TABLE_LIST *parent_l= this ->table->pos_in_table_list;
THD *thd= table->in_use;
List_iterator_fast<Mrg_child_def> it(child_def_list);
Mrg_child_def *mrg_child_def;
DBUG_ENTER("ha_myisammrg::add_children_list" );
DBUG_PRINT("myrg" , ("table: '%s'.'%s' %p" , this ->table->s->db.str,
this ->table->s->table_name.str, this ->table));
/* Must call this with open table. */
DBUG_ASSERT(this ->file);
/* Ignore this for empty MERGE tables (UNION=()). */
if (!this ->file->tables)
{
DBUG_PRINT("myrg" , ("empty merge table union" ));
goto end;
}
/* Must not call this with attached children. */
DBUG_ASSERT(!this ->file->children_attached);
/* Must not call this with children list in place. */
DBUG_ASSERT(this ->children_l == NULL);
/*
Prevent inclusion of another MERGE table , which could make infinite
recursion .
*/
if (parent_l->parent_l)
{
my_error(ER_ADMIN_WRONG_MRG_TABLE, MYF(0 ), parent_l->alias.str);
DBUG_RETURN(1 );
}
while ((mrg_child_def= it++))
{
TABLE_LIST *child_l;
LEX_CSTRING db;
LEX_CSTRING table_name;
child_l= thd->alloc<TABLE_LIST>(1 );
db.str= (char *) thd->memdup(mrg_child_def->db.str, mrg_child_def->db.length+1 );
db.length= mrg_child_def->db.length;
table_name.str= (char *) thd->memdup(mrg_child_def->name.str,
mrg_child_def->name.length+1 );
table_name.length= mrg_child_def->name.length;
if (child_l == NULL || db.str == NULL || table_name.str == NULL)
DBUG_RETURN(1 );
child_l->init_one_table(&db, &table_name, 0 , parent_l->lock_type);
/* Set parent reference. Used to detect MERGE in children list. */
child_l->parent_l= parent_l;
/* Copy select_lex. Used in unique_table() at least. */
child_l->select_lex= parent_l->select_lex;
/* Set the expected table version, to not cause spurious re-prepare. */
child_l->set_table_ref_id(mrg_child_def->get_child_table_ref_type(),
mrg_child_def->get_child_def_version());
/*
Copy parent ' s prelocking attribute to allow opening of child
temporary residing in the prelocking list .
*/
child_l->prelocking_placeholder= parent_l->prelocking_placeholder;
/*
For statements which acquire a SNW metadata lock on a parent table and
then later try to upgrade it to an X lock ( e . g . ALTER TABLE ) , SNW
locks should be also taken on the children tables .
Otherwise we end up in a situation where the thread trying to upgrade SNW
to X lock on the parent also holds a SR metadata lock and a read
thr_lock . c lock on the child . As a result , another thread might be
blocked on the thr_lock . c lock for the child after successfully acquiring
a SR or SW metadata lock on it . If at the same time this second thread
has a shared metadata lock on the parent table or there is some other
thread which has a shared metadata lock on the parent and is waiting for
this second thread , we get a deadlock . This deadlock cannot be properly
detected by the MDL subsystem as part of the waiting happens within
thr_lock . c . By taking SNW locks on the child tables we ensure that any
thread which waits for a thread doing SNW - > X upgrade , does this within
the MDL subsystem and thus potential deadlocks are exposed to the deadlock
detector .
We don ' t do the same thing for SNRW locks as this would allow
DDL on implicitly locked underlying tables of a MERGE table .
*/
if (! thd->locked_tables_mode &&
parent_l->mdl_request.type == MDL_SHARED_UPGRADABLE)
child_l->mdl_request.set_type(MDL_SHARED_NO_WRITE);
/* Link TABLE_LIST object into the children list. */
if (this ->children_last_l)
child_l->prev_global= this ->children_last_l;
else
{
/* Initialize children_last_l when handling first child. */
this ->children_last_l= &this ->children_l;
}
*this ->children_last_l= child_l;
this ->children_last_l= &child_l->next_global;
}
/* Insert children into the table list. */
if (parent_l->next_global)
parent_l->next_global->prev_global= this ->children_last_l;
*this ->children_last_l= parent_l->next_global;
parent_l->next_global= this ->children_l;
this ->children_l->prev_global= &parent_l->next_global;
/*
We have to update LEX : : query_tables_last if children are added to
the tail of the table list in order to be able correctly add more
elements to it ( e . g . as part of prelocking process ) .
*/
if (thd->lex->query_tables_last == &parent_l->next_global)
thd->lex->query_tables_last= this ->children_last_l;
/*
The branch below works only when re - executing a prepared
statement or a stored routine statement :
We ' ve just modified query_tables_last . Keep it in sync with
query_tables_last_own , if it was set by the prelocking code .
This ensures that the check that prohibits double updates ( * )
can correctly identify what tables belong to the main statement .
( * ) A double update is , e . g . when a user issues UPDATE t1 and
t1 has an AFTER UPDATE trigger that also modifies t1 .
*/
if (thd->lex->query_tables_own_last == &parent_l->next_global)
thd->lex->query_tables_own_last= this ->children_last_l;
end:
DBUG_RETURN(0 );
}
/**
A context of myrg_attach_children ( ) callback .
*/
class Mrg_attach_children_callback_param
{
public :
/**
' need_compat_check ' is set by myisammrg_attach_children_callback ( )
if a child fails the table def version check .
*/
bool need_compat_check;
/** TABLE_LIST identifying this merge parent. */
TABLE_LIST *parent_l;
/** Iterator position, the current child to attach. */
TABLE_LIST *next_child_attach;
List_iterator_fast<Mrg_child_def> def_it;
Mrg_child_def *mrg_child_def;
public :
Mrg_attach_children_callback_param(TABLE_LIST *parent_l_arg,
TABLE_LIST *first_child,
List<Mrg_child_def> &child_def_list)
:need_compat_check(FALSE ),
parent_l(parent_l_arg),
next_child_attach(first_child),
def_it(child_def_list),
mrg_child_def(def_it++)
{}
void next()
{
next_child_attach= next_child_attach->next_global;
if (next_child_attach && next_child_attach->parent_l != parent_l)
next_child_attach= NULL;
if (mrg_child_def)
mrg_child_def= def_it++;
}
};
/**
Callback function for attaching a MERGE child table .
@ param [ in ] callback_param data pointer as given to myrg_attach_children ( )
this is used to pass the handler handle
@ return pointer to open MyISAM table structure
@ retval ! = NULL OK , returning pointer
@ retval NULL , Error .
@ detail
This function retrieves the MyISAM table handle from the
next child table . It is called for each child table .
*/
CPP_UNNAMED_NS_START
extern "C" MI_INFO *myisammrg_attach_children_callback(void *callback_param)
{
Mrg_attach_children_callback_param *param=
(Mrg_attach_children_callback_param*) callback_param;
TABLE *parent= param->parent_l->table;
TABLE *child;
TABLE_LIST *child_l= param->next_child_attach;
Mrg_child_def *mrg_child_def= param->mrg_child_def;
MI_INFO *myisam= NULL;
DBUG_ENTER("myisammrg_attach_children_callback" );
/*
Number of children in the list and MYRG_INFO : : tables_count ,
which is used by caller of this function , should always match .
*/
DBUG_ASSERT(child_l);
child= child_l->table;
/* Prepare for next child. */
param->next();
/*
When MERGE table is opened for CHECK or REPAIR TABLE statements ,
failure to open any of underlying tables is ignored until this moment
( this is needed to provide complete list of the problematic underlying
tables in CHECK / REPAIR TABLE output ) .
Here we detect such a situation and report an appropriate error .
*/
if (! child)
{
DBUG_PRINT("error" , ("failed to open underlying table '%s'.'%s'" ,
child_l->db.str, child_l->table_name.str));
/*
This should only happen inside of CHECK / REPAIR TABLE or
for the tables added by the pre - locking code .
*/
DBUG_ASSERT(current_thd->open_options & HA_OPEN_FOR_REPAIR ||
child_l->prelocking_placeholder);
goto end;
}
/*
Do a quick compatibility check . The table def version is set when
the table share is created . The child def version is copied
from the table def version after a successful compatibility check .
We need to repeat the compatibility check only if a child is opened
from a different share than last time it was used with this MERGE
table .
*/
DBUG_PRINT("myrg" , ("table_def_version last: %lu current: %lu" ,
(ulong) mrg_child_def->get_child_def_version(),
(ulong) child->s->get_table_def_version()));
if (mrg_child_def->get_child_def_version() != child->s->get_table_def_version())
param->need_compat_check= TRUE ;
/*
If child is temporary , parent must be temporary as well . Other
parent / child combinations are allowed . This check must be done for
every child on every open because the table def version can overlap
between temporary and non - temporary tables . We need to detect the
case where a non - temporary table has been replaced with a temporary
table of the same version . Or vice versa . A very unlikely case , but
it could happen . ( Note that the condition was different from
5 . 1 . 23 / 6 . 0 . 4 ( Bug # 19627 ) to 5 . 5 . 6 ( Bug # 36171 ) : child - > s - > tmp_table ! =
parent - > s - > tmp_table . Tables were required to have the same status . )
*/
if (child->s->tmp_table && !parent->s->tmp_table)
{
DBUG_PRINT("error" , ("temporary table mismatch parent: %d child: %d" ,
parent->s->tmp_table, child->s->tmp_table));
goto end;
}
/* Extract the MyISAM table structure pointer from the handler object. */
if ((child->file->ht->db_type != DB_TYPE_MYISAM) ||
!(myisam= ((ha_myisam*) child->file)->file_ptr()))
{
DBUG_PRINT("error" , ("no MyISAM handle for child table: '%s'.'%s' %p" ,
child->s->db.str, child->s->table_name.str,
child));
}
DBUG_PRINT("myrg" , ("MyISAM handle: %p" , myisam));
end:
if (!myisam &&
(current_thd->open_options & HA_OPEN_FOR_REPAIR))
{
char buf[2 *NAME_LEN + 1 + 1 ];
strxnmov(buf, sizeof (buf) - 1 , child_l->db.str, "." ,
child_l->table_name.str, NULL);
/*
Push an error to be reported as part of CHECK / REPAIR result - set .
Note that calling my_error ( ) from handler is a hack which is kept
here to avoid refactoring . Normally engines should report errors
through return value which will be interpreted by caller using
handler : : print_error ( ) call .
*/
my_error(ER_ADMIN_WRONG_MRG_TABLE, MYF(0 ), buf);
}
DBUG_RETURN(myisam);
}
CPP_UNNAMED_NS_END
/**
Returns a cloned instance of the current handler .
@ return A cloned handler instance .
*/
handler *ha_myisammrg::clone(const char *name, MEM_ROOT *mem_root)
{
MYRG_TABLE *u_table,*newu_table;
ha_myisammrg *new_handler=
(ha_myisammrg*) get_new_handler(table->s, mem_root, table->s->db_type());
if (!new_handler)
return NULL;
/* Inform ha_myisammrg::open() that it is a cloned handler */
new_handler->is_cloned= TRUE ;
/*
Allocate handler - > ref here because otherwise ha_open will allocate it
on this - > table - > mem_root and we will not be able to reclaim that memory
when the clone handler object is destroyed .
*/
if (!(new_handler->ref= (uchar*) alloc_root(mem_root, ALIGN_SIZE(ref_length)*2 )))
{
delete new_handler;
return NULL;
}
if (new_handler->ha_open(table, name, table->db_stat,
HA_OPEN_IGNORE_IF_LOCKED))
{
delete new_handler;
return NULL;
}
/*
Iterate through the original child tables and
copy the state into the cloned child tables .
We need to do this because all the child tables
can be involved in delete .
*/
newu_table= new_handler->file->open_tables;
for (u_table= file->open_tables; u_table < file->end_table; u_table++)
{
newu_table->table->state= u_table->table->state;
newu_table++;
}
return new_handler;
}
/**
Attach children to a MERGE table .
@ return status
@ retval 0 OK
@ retval ! = 0 Error , my_errno gives reason
@ detail
Let the storage engine attach its children through a callback
function . Check table definitions for consistency .
@ note
Special thd - > open_options may be in effect . We can make use of
them in attach . I . e . we use HA_OPEN_FOR_REPAIR to report the names
of mismatching child tables . We cannot transport these options in
ha_myisammrg : : test_if_locked because they may change after the
parent is opened . The parent is kept open in the table cache over
multiple statements and can be used by other threads . Open options
can change over time .
*/
int ha_myisammrg::attach_children(void )
{
MYRG_TABLE *u_table;
MI_COLUMNDEF *recinfo;
MI_KEYDEF *keyinfo;
uint recs;
uint keys= table->s->keys;
TABLE_LIST *parent_l= table->pos_in_table_list;
int error;
Mrg_attach_children_callback_param param(parent_l, this ->children_l, child_def_list);
DBUG_ENTER("ha_myisammrg::attach_children" );
DBUG_PRINT("myrg" , ("table: '%s'.'%s' %p" , table->s->db.str,
table->s->table_name.str, table));
DBUG_PRINT("myrg" , ("test_if_locked: %u" , this ->test_if_locked));
/* Must call this with open table. */
DBUG_ASSERT(this ->file);
/*
A MERGE table with no children ( empty union ) is always seen as
attached internally .
*/
if (!this ->file->tables)
{
DBUG_PRINT("myrg" , ("empty merge table union" ));
goto end;
}
DBUG_PRINT("myrg" , ("child tables: %u" , this ->file->tables));
/* Must not call this with attached children. */
DBUG_ASSERT(!this ->file->children_attached);
DEBUG_SYNC(current_thd, "before_myisammrg_attach" );
/* Must call this with children list in place. */
DBUG_ASSERT(this ->table->pos_in_table_list->next_global == this ->children_l);
if (myrg_attach_children(this ->file, this ->test_if_locked |
current_thd->open_options,
myisammrg_attach_children_callback, ¶m,
(my_bool *) ¶m.need_compat_check))
{
error= my_errno;
goto err;
}
DBUG_PRINT("myrg" , ("calling myrg_extrafunc" ));
myrg_extrafunc(file, query_cache_invalidate_by_MyISAM_filename_ref);
if (!(test_if_locked == HA_OPEN_WAIT_IF_LOCKED ||
test_if_locked == HA_OPEN_ABORT_IF_LOCKED))
myrg_extra(file,HA_EXTRA_NO_WAIT_LOCK,0 );
info(HA_STATUS_NO_LOCK | HA_STATUS_VARIABLE | HA_STATUS_CONST);
if (!(test_if_locked & HA_OPEN_WAIT_IF_LOCKED))
myrg_extra(file,HA_EXTRA_WAIT_LOCK,0 );
/*
The compatibility check is required only if one or more children do
not match their table def version from the last check . This will
always happen at the first attach because the reference child def
version is initialized to ' undefined ' at open .
*/
DBUG_PRINT("myrg" , ("need_compat_check: %d" , param.need_compat_check));
if (param.need_compat_check)
{
TABLE_LIST *child_l;
if (table->s->reclength != stats.mean_rec_length && stats.mean_rec_length)
{
DBUG_PRINT("error" ,("reclength: %lu mean_rec_length: %lu" ,
table->s->reclength, stats.mean_rec_length));
if (test_if_locked & HA_OPEN_FOR_REPAIR)
{
/* purecov: begin inspected */
myrg_print_wrong_table(file->open_tables->table->filename);
/* purecov: end */
}
error= HA_ERR_WRONG_MRG_TABLE_DEF;
goto err;
}
/*
Both recinfo and keyinfo are allocated by my_multi_malloc ( ) , thus
only recinfo must be freed .
*/
if ((error= table2myisam(table, &keyinfo, &recinfo, &recs)))
{
/* purecov: begin inspected */
DBUG_PRINT("error" , ("failed to convert TABLE object to MyISAM "
"key and column definition" ));
goto err;
/* purecov: end */
}
for (u_table= file->open_tables; u_table < file->end_table; u_table++)
{
if (check_definition(keyinfo, recinfo, keys, recs,
u_table->table->s->keyinfo, u_table->table->s->rec,
u_table->table->s->base.keys,
u_table->table->s->base.fields, false , NULL))
{
DBUG_PRINT("error" , ("table definition mismatch: '%s'" ,
u_table->table->filename));
error= HA_ERR_WRONG_MRG_TABLE_DEF;
if (!(this ->test_if_locked & HA_OPEN_FOR_REPAIR))
{
my_free(recinfo);
goto err;
}
/* purecov: begin inspected */
myrg_print_wrong_table(u_table->table->filename);
/* purecov: end */
}
}
my_free(recinfo);
if (error == HA_ERR_WRONG_MRG_TABLE_DEF)
goto err; /* purecov: inspected */
List_iterator_fast<Mrg_child_def> def_it(child_def_list);
DBUG_ASSERT(this ->children_l);
for (child_l= this ->children_l; ; child_l= child_l->next_global)
{
Mrg_child_def *mrg_child_def= def_it++;
mrg_child_def->set_child_def_version(
child_l->table->s->get_table_ref_type(),
child_l->table->s->get_table_def_version());
if (&child_l->next_global == this ->children_last_l)
break ;
}
}
#if SIZEOF_OFF_T == 4
/* Merge table has more than 2G rows */
if (table->s->crashed)
{
DBUG_PRINT("error" , ("MERGE table marked crashed" ));
error= HA_ERR_WRONG_MRG_TABLE_DEF;
goto err;
}
#endif
end:
DBUG_RETURN(0 );
err:
DBUG_PRINT("error" , ("attaching MERGE children failed: %d" , error));
print_error(error, MYF(0 ));
detach_children();
DBUG_RETURN(my_errno= error);
}
/**
Detach all children from a MERGE table and from the query list of tables .
@ return status
@ retval 0 OK
@ retval ! = 0 Error , my_errno gives reason
@ note
Detach must not touch the child TABLE objects in any way .
They may have been closed at ths point already .
All references to the children should be removed .
*/
int ha_myisammrg::detach_children(void )
{
TABLE_LIST *child_l;
DBUG_ENTER("ha_myisammrg::detach_children" );
/* Must call this with open table. */
DBUG_ASSERT(this ->file);
/* A MERGE table with no children (empty union) cannot be detached. */
if (!this ->file->tables)
{
DBUG_PRINT("myrg" , ("empty merge table union" ));
goto end;
}
if (this ->children_l)
{
THD *thd= table->in_use;
/* Clear TABLE references. */
for (child_l= this ->children_l; ; child_l= child_l->next_global)
{
/*
Do not DBUG_ASSERT ( child_l - > table ) ; open_tables might be
incomplete .
Clear the table reference .
*/
child_l->table= NULL;
/* Similarly, clear the ticket reference. */
child_l->mdl_request.ticket= NULL;
/* Break when this was the last child. */
if (&child_l->next_global == this ->children_last_l)
break ;
}
/*
Remove children from the table list . This won ' t fail if called
twice . The list is terminated after removal .
If the parent is LEX : : query_tables_own_last and pre - locked tables
follow ( tables used by stored functions or triggers ) , the children
are inserted behind the parent and before the pre - locked tables . But
we do not adjust LEX : : query_tables_own_last . The pre - locked tables
could have chopped off the list by clearing
* LEX : : query_tables_own_last . This did also chop off the children . If
we would copy the reference from * this - > children_last_l in this
case , we would put the chopped off pre - locked tables back to the
list . So we refrain from copying it back , if the destination has
been set to NULL meanwhile .
*/
if (this ->children_l->prev_global && *this ->children_l->prev_global)
*this ->children_l->prev_global= *this ->children_last_l;
if (*this ->children_last_l)
(*this ->children_last_l)->prev_global= this ->children_l->prev_global;
/*
If table elements being removed are at the end of table list we
need to adjust LEX : : query_tables_last member to point to the
new last element of the list .
*/
if (thd->lex->query_tables_last == this ->children_last_l)
thd->lex->query_tables_last= this ->children_l->prev_global;
/*
If the statement requires prelocking , and prelocked
tables were added right after merge children , modify the
last own table pointer to point at prev_global of the merge
parent .
*/
if (thd->lex->query_tables_own_last == this ->children_last_l)
thd->lex->query_tables_own_last= this ->children_l->prev_global;
/* Terminate child list. So it cannot be tried to remove again. */
*this ->children_last_l= NULL;
this ->children_l->prev_global= NULL;
/* Forget about the children, we don't own their memory. */
this ->children_l= NULL;
this ->children_last_l= NULL;
}
if (!this ->file->children_attached)
{
DBUG_PRINT("myrg" , ("merge children are already detached" ));
goto end;
}
if (myrg_detach_children(this ->file))
{
/* purecov: begin inspected */
print_error(my_errno, MYF(0 ));
DBUG_RETURN(my_errno ? my_errno : -1 );
/* purecov: end */
}
end:
DBUG_RETURN(0 );
}
/**
Close a MERGE parent table , but not its children .
@ return status
@ retval 0 OK
@ retval ! = 0 Error , my_errno gives reason
@ note
The children are expected to be closed separately by the caller .
*/
int ha_myisammrg::close(void )
{
int rc;
DBUG_ENTER("ha_myisammrg::close" );
/*
There are cases where children are not explicitly detached before
close . detach_children ( ) protects itself against double detach .
*/
if (!is_cloned)
detach_children();
rc= myrg_close(file);
file= 0 ;
DBUG_RETURN(rc);
}
int ha_myisammrg::write_row(const uchar * buf)
{
DBUG_ENTER("ha_myisammrg::write_row" );
DBUG_ASSERT(this ->file->children_attached);
if (file->merge_insert_method == MERGE_INSERT_DISABLED || !file->tables)
DBUG_RETURN(HA_ERR_TABLE_READONLY);
if (table->next_number_field && buf == table->record[0 ])
{
int error;
if ((error= update_auto_increment()))
DBUG_RETURN(error); /* purecov: inspected */
}
DBUG_RETURN(myrg_write(file,buf));
}
int ha_myisammrg::update_row(const uchar * old_data, const uchar * new_data)
{
DBUG_ASSERT(this ->file->children_attached);
return myrg_update(file,old_data,new_data);
}
int ha_myisammrg::delete_row(const uchar * buf)
{
DBUG_ASSERT(this ->file->children_attached);
return myrg_delete(file,buf);
}
int ha_myisammrg::index_read_map(uchar * buf, const uchar * key,
key_part_map keypart_map,
enum ha_rkey_function find_flag)
{
DBUG_ASSERT(this ->file->children_attached);
int error=myrg_rkey(file,buf,active_index, key, keypart_map, find_flag);
return error;
}
int ha_myisammrg::index_read_idx_map(uchar * buf, uint index, const uchar * key,
key_part_map keypart_map,
enum ha_rkey_function find_flag)
{
DBUG_ASSERT(this ->file->children_attached);
int error=myrg_rkey(file,buf,index, key, keypart_map, find_flag);
return error;
}
int ha_myisammrg::index_read_last_map(uchar *buf, const uchar *key,
key_part_map keypart_map)
{
DBUG_ASSERT(this ->file->children_attached);
int error=myrg_rkey(file,buf,active_index, key, keypart_map,
HA_READ_PREFIX_LAST);
return error;
}
int ha_myisammrg::index_next(uchar * buf)
{
DBUG_ASSERT(this ->file->children_attached);
int error=myrg_rnext(file,buf,active_index);
return error;
}
int ha_myisammrg::index_prev(uchar * buf)
{
DBUG_ASSERT(this ->file->children_attached);
int error=myrg_rprev(file,buf, active_index);
return error;
}
int ha_myisammrg::index_first(uchar * buf)
{
DBUG_ASSERT(this ->file->children_attached);
int error=myrg_rfirst(file, buf, active_index);
return error;
}
int ha_myisammrg::index_last(uchar * buf)
{
DBUG_ASSERT(this ->file->children_attached);
int error=myrg_rlast(file, buf, active_index);
return error;
}
int ha_myisammrg::index_next_same(uchar * buf,
const uchar *key __attribute__((unused)),
uint length __attribute__((unused)))
{
int error;
DBUG_ASSERT(this ->file->children_attached);
do
{
error= myrg_rnext_same(file,buf);
} while (error == HA_ERR_RECORD_DELETED);
return error;
}
int ha_myisammrg::rnd_init(bool scan)
{
DBUG_ASSERT(this ->file->children_attached);
return myrg_reset(file);
}
int ha_myisammrg::rnd_next(uchar *buf)
{
DBUG_ASSERT(this ->file->children_attached);
int error=myrg_rrnd(file, buf, HA_OFFSET_ERROR);
return error;
}
int ha_myisammrg::rnd_pos(uchar * buf, uchar *pos)
{
DBUG_ASSERT(this ->file->children_attached);
int error=myrg_rrnd(file, buf, my_get_ptr(pos,ref_length));
return error;
}
void ha_myisammrg::position(const uchar *record)
{
DBUG_ASSERT(this ->file->children_attached);
ulonglong row_position= myrg_position(file);
my_store_ptr(ref, ref_length, (my_off_t) row_position);
}
ha_rows ha_myisammrg::records_in_range(uint inx,
const key_range *min_key,
const key_range *max_key,
page_range *pages)
{
DBUG_ASSERT(this ->file->children_attached);
return (ha_rows) myrg_records_in_range(file, (int ) inx, min_key, max_key,
pages);
}
int ha_myisammrg::delete_all_rows()
{
int err= 0 ;
MYRG_TABLE *table;
DBUG_ENTER("ha_myisammrg::delete_all_rows" );
for (table= file->open_tables; table != file->end_table; table++)
{
if ((err= mi_delete_all_rows(table->table)))
break ;
}
DBUG_RETURN(err);
}
int ha_myisammrg::info(uint flag)
{
MYMERGE_INFO mrg_info;
(void ) myrg_status(file,&mrg_info,flag);
/*
The following fails if one has not compiled MySQL with - DBIG_TABLES
and one has more than 2 ^ 32 rows in the merge tables .
*/
stats.records = (ha_rows) mrg_info.records;
stats.deleted = (ha_rows) mrg_info.deleted;
#if SIZEOF_OFF_T == 4
if ((mrg_info.records >= (ulonglong) 1 << 32 ) ||
(mrg_info.deleted >= (ulonglong) 1 << 32 ))
table->s->crashed= 1 ;
#endif
stats.data_file_length= mrg_info.data_file_length;
if (mrg_info.errkey >= (int ) table_share->keys)
{
/*
If value of errkey is higher than the number of keys
on the table set errkey to MAX_KEY . This will be
treated as unknown key case and error message generator
won ' t try to locate key causing segmentation fault .
*/
mrg_info.errkey= MAX_KEY;
}
table->s->keys_in_use.set_prefix(table->s->keys);
stats.mean_rec_length= mrg_info.reclength;
/*
The handler : : block_size is used all over the code in index scan cost
calculations . It is used to get number of disk seeks required to
retrieve a number of index tuples .
If the merge table has N underlying tables , there will be
N more disk seeks compared to a scanning a normal MyISAM table .
The number of bytes read is the rougly the same for a normal MyISAM
and a MyISAM merge tables .
*/
stats.block_size= myisam_block_size;
stats.update_time= 0 ;
#if SIZEOF_OFF_T > 4
ref_length=6 ; // Should be big enough
#else
ref_length=4 ; // Can't be > than my_off_t
#endif
if (flag & HA_STATUS_CONST)
{
if (table->s->key_parts && mrg_info.rec_per_key)
{
#ifdef HAVE_valgrind
/*
valgrind may be unhappy about it , because optimizer may access values
between file - > keys and table - > key_parts , that will be uninitialized .
It ' s safe though , because even if optimizer will decide to use a key
with such a number , it ' ll be an error later anyway .
*/
bzero((char *) table->key_info[0 ].rec_per_key,
sizeof (table->key_info[0 ].rec_per_key[0 ]) * table->s->key_parts);
#endif
memcpy((char *) table->key_info[0 ].rec_per_key,
(char *) mrg_info.rec_per_key,
sizeof (table->key_info[0 ].rec_per_key[0 ]) *
MY_MIN(file->keys, table->s->key_parts));
}
}
if (flag & HA_STATUS_ERRKEY)
{
errkey= mrg_info.errkey;
my_store_ptr(dup_ref, ref_length, mrg_info.dupp_key_pos);
}
return 0 ;
}
int ha_myisammrg::extra(enum ha_extra_function operation)
{
if (operation == HA_EXTRA_ADD_CHILDREN_LIST)
{
int rc= add_children_list();
return (rc);
}
else if (operation == HA_EXTRA_ATTACH_CHILDREN)
{
int rc= attach_children();
if (!rc)
(void ) extra(HA_EXTRA_NO_READCHECK); // Not needed in SQL
return (rc);
}
else if (operation == HA_EXTRA_IS_ATTACHED_CHILDREN)
{
/* For the upper layer pretend empty MERGE union is never attached. */
return (file && file->tables && file->children_attached);
}
else if (operation == HA_EXTRA_DETACH_CHILDREN)
{
/*
Note that detach must not touch the children in any way .
They may have been closed at ths point already .
*/
int rc= detach_children();
return (rc);
}
/* As this is just a mapping, we don't have to force the underlying
tables to be closed */
if (operation == HA_EXTRA_FORCE_REOPEN ||
operation == HA_EXTRA_PREPARE_FOR_DROP ||
operation == HA_EXTRA_PREPARE_FOR_RENAME)
return 0 ;
if (operation == HA_EXTRA_MMAP && !opt_myisam_use_mmap)
return 0 ;
return myrg_extra(file,operation,0 );
}
int ha_myisammrg::reset(void )
{
/* This is normally called with detached children. */
return myrg_reset(file);
}
/* To be used with WRITE_CACHE, EXTRA_CACHE and BULK_INSERT_BEGIN */
int ha_myisammrg::extra_opt(enum ha_extra_function operation, ulong cache_size)
{
DBUG_ASSERT(this ->file->children_attached);
return myrg_extra(file, operation, (void *) &cache_size);
}
int ha_myisammrg::external_lock(THD *thd, int lock_type)
{
/*
This can be called with no children attached . E . g . FLUSH TABLES
unlocks and re - locks tables under LOCK TABLES , but it does not open
them first . So they are detached all the time . But locking of the
children should work anyway because thd - > open_tables is not changed
during FLUSH TABLES .
If this handler instance has been cloned , we still must call
myrg_lock_database ( ) .
*/
if (is_cloned)
return myrg_lock_database(file, lock_type);
return 0 ;
}
uint ha_myisammrg::lock_count(void ) const
{
return 0 ;
}
THR_LOCK_DATA **ha_myisammrg::store_lock(THD *thd,
THR_LOCK_DATA **to,
enum thr_lock_type lock_type)
{
MYRG_TABLE *open_table;
/*
This method can be called while another thread is attaching the
children . If the processor reorders instructions or write to memory ,
' children_attached ' could be set before ' open_tables ' has all the
pointers to the children . Use of a mutex here and in
myrg_attach_children ( ) forces consistent data .
*/
mysql_mutex_lock(&this ->file->mutex);
/*
When MERGE table is open , but not yet attached , other threads
could flush it , which means calling mysql_lock_abort_for_thread ( )
on this threads TABLE . ' children_attached ' is FALSE in this
situation . Since the table is not locked , return no lock data .
*/
if (!this ->file->children_attached)
goto end; /* purecov: tested */
for (open_table=file->open_tables ;
open_table != file->end_table ;
open_table++)
open_table->table->lock.priority|= THR_LOCK_MERGE_PRIV;
end:
mysql_mutex_unlock(&this ->file->mutex);
return to;
}
/* Find out database name and table name from a filename */
static void split_file_name(const char *file_name,
LEX_STRING *db, LEX_STRING *name)
{
size_t dir_length, prefix_length;
char buff[FN_REFLEN];
db->length= 0 ;
strmake_buf(buff, file_name);
dir_length= dirname_length(buff);
if (dir_length > 1 )
{
/* Get database */
buff[dir_length-1 ]= 0 ; // Remove end '/'
prefix_length= dirname_length(buff);
db->str= (char *) file_name+ prefix_length;
db->length= dir_length - prefix_length -1 ;
}
name->str= (char *) file_name+ dir_length;
name->length= (uint) (fn_ext(name->str) - name->str);
}
void ha_myisammrg::update_create_info(HA_CREATE_INFO *create_info)
{
DBUG_ENTER("ha_myisammrg::update_create_info" );
if (!(create_info->used_fields & HA_CREATE_USED_UNION))
{
TABLE_LIST *child_table, *end= NULL;
THD *thd=ha_thd();
if (children_l != NULL)
{
for (child_table= children_l;; child_table= child_table->next_global)
{
TABLE_LIST *ptr;
if (!(ptr= thd->calloc<TABLE_LIST>(1 )))
DBUG_VOID_RETURN;
if (!(ptr->table_name.str= thd->strmake(child_table->table_name.str,
child_table->table_name.length)))
DBUG_VOID_RETURN;
ptr->table_name.length= child_table->table_name.length;
if (child_table->db.str && !(ptr->db.str= thd->strmake(child_table->db.str,
child_table->db.length)))
DBUG_VOID_RETURN;
ptr->db.length= child_table->db.length;
if (create_info->merge_list)
end->next_local= ptr;
else
create_info->merge_list= ptr;
end= ptr;
if (&child_table->next_global == children_last_l)
break ;
}
}
}
if (!(create_info->used_fields & HA_CREATE_USED_INSERT_METHOD))
{
create_info->merge_insert_method = file->merge_insert_method;
}
DBUG_VOID_RETURN;
}
int ha_myisammrg::create_mrg(const char *name, HA_CREATE_INFO *create_info)
{
char buff[FN_REFLEN];
const char **table_names, **pos;
TABLE_LIST *tables= create_info->merge_list;
THD *thd= ha_thd();
size_t dirlgt= dirname_length(name);
uint ntables= 0 ;
DBUG_ENTER("ha_myisammrg::create_mrg" );
for (tables= create_info->merge_list; tables; tables= tables->next_local)
ntables++;
/* Allocate a table_names array in thread mem_root. */
if (!(pos= table_names= thd->alloc<const char *>(ntables + 1 )))
DBUG_RETURN(HA_ERR_OUT_OF_MEM); /* purecov: inspected */
/* Create child path names. */
for (tables= create_info->merge_list; tables; tables= tables->next_local)
{
const char *table_name= buff;
/*
Construct the path to the MyISAM table . Try to meet two conditions :
1 . ) Allow to include MyISAM tables from different databases , and
2 . ) allow for moving DATADIR around in the file system .
The first means that we need paths in the . MRG file . The second
means that we should not have absolute paths in the . MRG file .
The best , we can do , is to use ' mysql_data_home ' , which is ' . '
in mysqld and may be an absolute path in an embedded server .
This means that it might not be possible to move the DATADIR of
an embedded server without changing the paths in the . MRG file .
Do the same even for temporary tables . MERGE children are now
opened through the table cache . They are opened by db . table_name ,
not by their path name .
*/
size_t length= build_table_filename(buff, sizeof (buff), tables->db.str,
tables->table_name.str, "" , 0 );
DBUG_ASSERT(length > 0 );
/*
If a MyISAM table is in the same directory as the MERGE table ,
we use the table name without a path . This means that the
DATADIR can easily be moved even for an embedded server as long
as the MyISAM tables are from the same database as the MERGE table .
*/
if ((dirname_length(buff) == dirlgt) && ! memcmp(buff, name, dirlgt))
{
table_name+= dirlgt;
length-= dirlgt;
}
if (!(table_name= thd->strmake(table_name, length)))
DBUG_RETURN(HA_ERR_OUT_OF_MEM); /* purecov: inspected */
*pos++= table_name;
}
*pos=0 ;
/* Create a MERGE meta file from the table_names array. */
int res= myrg_create(name, table_names, create_info->merge_insert_method, 0 );
DBUG_RETURN(res);
}
int ha_myisammrg::create(const char *name, TABLE *form,
HA_CREATE_INFO *create_info)
{
char buff[FN_REFLEN];
DBUG_ENTER("ha_myisammrg::create" );
if (form->s->total_keys > form->s->keys)
{
my_error(ER_ILLEGAL_HA_CREATE_OPTION, MYF(0 ), "MERGE" , "VECTOR" );
DBUG_RETURN(HA_ERR_UNSUPPORTED);
}
fn_format(buff, name, "" , MYRG_NAME_EXT, MY_UNPACK_FILENAME | MY_APPEND_EXT);
int res= create_mrg(buff, create_info);
DBUG_RETURN(res);
}
void ha_myisammrg::append_create_info(String *packet)
{
const char *current_db;
size_t db_length;
THD *thd= current_thd;
TABLE_LIST *open_table, *first;
if (file->merge_insert_method != MERGE_INSERT_DISABLED)
{
const char *type;
packet->append(STRING_WITH_LEN(" INSERT_METHOD=" ));
type= get_type(&merge_insert_method,file->merge_insert_method-1 );
packet->append(type, strlen(type));
}
/*
There is no sence adding UNION clause in case there is no underlying
tables specified .
*/
if (file->open_tables == file->end_table)
return ;
packet->append(STRING_WITH_LEN(" UNION=(" ));
current_db= table->s->db.str;
db_length= table->s->db.length;
for (first= open_table= children_l;;
open_table= open_table->next_global)
{
LEX_CSTRING db= open_table->db;
if (open_table != first)
packet->append(',' );
/* Report database for mapped table if it isn't in current database */
if (db.length &&
(db_length != db.length ||
strncmp(current_db, db.str, db.length)))
{
append_identifier(thd, packet, db.str, db.length);
packet->append('.' );
}
append_identifier(thd, packet, &open_table->table_name);
if (&open_table->next_global == children_last_l)
break ;
}
packet->append(')' );
}
enum_alter_inplace_result
ha_myisammrg::check_if_supported_inplace_alter(TABLE *altered_table,
Alter_inplace_info *ha_alter_info)
{
/*
We always support inplace ALTER in the new API , because old
HA_NO_COPY_ON_ALTER table_flags ( ) hack prevents non - inplace ALTER anyway .
*/
return HA_ALTER_INPLACE_EXCLUSIVE_LOCK;
}
bool ha_myisammrg::inplace_alter_table(TABLE *altered_table,
Alter_inplace_info *ha_alter_info)
{
char tmp_path[FN_REFLEN];
const char *name= table->s->normalized_path.str;
DBUG_ENTER("ha_myisammrg::inplace_alter_table" );
fn_format(tmp_path, name, "" , MYRG_NAME_TMPEXT, MY_UNPACK_FILENAME | MY_APPEND_EXT);
int res= create_mrg(tmp_path, ha_alter_info->create_info);
if (res)
mysql_file_delete(rg_key_file_MRG, tmp_path, MYF(0 ));
else
{
char path[FN_REFLEN];
fn_format(path, name, "" , MYRG_NAME_EXT, MY_UNPACK_FILENAME | MY_APPEND_EXT);
if (mysql_file_rename(rg_key_file_MRG, tmp_path, path, MYF(0 )))
{
res= my_errno;
mysql_file_delete(rg_key_file_MRG, tmp_path, MYF(0 ));
}
}
DBUG_RETURN(res);
}
int ha_myisammrg::check(THD* thd, HA_CHECK_OPT* check_opt)
{
return this ->file->children_attached ? HA_ADMIN_OK : HA_ADMIN_CORRUPT;
}
ha_rows ha_myisammrg::records()
{
return myrg_records(file);
}
uint ha_myisammrg::count_query_cache_dependant_tables(uint8 *tables_type)
{
MYRG_INFO *file = myrg_info();
/*
Here should be following statement
( * tables_type ) | = HA_CACHE_TBL_NONTRANSACT ;
but it has no effect because HA_CACHE_TBL_NONTRANSACT is 0
*/
return (uint)(file->end_table - file->open_tables);
}
my_bool ha_myisammrg::register_query_cache_dependant_tables(THD *thd
__attribute__((unused)),
Query_cache *cache,
Query_cache_block_table **block_table,
uint *n)
{
MYRG_INFO *file =myrg_info();
DBUG_ENTER("ha_myisammrg::register_query_cache_dependant_tables" );
for (MYRG_TABLE *table =file->open_tables;
table != file->end_table ;
table++)
{
char key[MAX_DBKEY_LENGTH];
uint32 db_length;
uint key_length= cache->filename_2_table_key(key, table->table->filename,
&db_length);
(++(*block_table))->n= ++(*n);
/*
There are not callback function for for MyISAM , and engine data
*/
if (!cache->insert_table(thd, key_length, key, (*block_table),
db_length, 0 ,
table_cache_type(),
0 , 0 , TRUE ))
DBUG_RETURN(TRUE );
}
DBUG_RETURN(FALSE );
}
void ha_myisammrg::set_lock_type(enum thr_lock_type lock)
{
handler::set_lock_type(lock);
if (children_l != NULL)
{
for (TABLE_LIST *child_table= children_l;;
child_table= child_table->next_global)
{
child_table->lock_type=
child_table->table->reginfo.lock_type= lock;
if (&child_table->next_global == children_last_l)
break ;
}
}
}
extern int myrg_panic(enum ha_panic_function flag);
int myisammrg_panic(handlerton *hton, ha_panic_function flag)
{
return myrg_panic(flag);
}
static void myisammrg_update_optimizer_costs(OPTIMIZER_COSTS *costs)
{
myisam_update_optimizer_costs(costs);
}
static int myisammrg_init(void *p)
{
handlerton *myisammrg_hton;
myisammrg_hton= (handlerton *)p;
#ifdef HAVE_PSI_INTERFACE
init_myisammrg_psi_keys();
#endif
myisammrg_hton->db_type= DB_TYPE_MRG_MYISAM;
myisammrg_hton->create= myisammrg_create_handler;
myisammrg_hton->panic= myisammrg_panic;
myisammrg_hton->flags= HTON_NO_PARTITION;
myisammrg_hton->tablefile_extensions= ha_myisammrg_exts;
myisammrg_hton->update_optimizer_costs= myisammrg_update_optimizer_costs;
return 0 ;
}
struct st_mysql_storage_engine myisammrg_storage_engine=
{ MYSQL_HANDLERTON_INTERFACE_VERSION };
maria_declare_plugin(myisammrg)
{
MYSQL_STORAGE_ENGINE_PLUGIN,
&myisammrg_storage_engine,
"MRG_MyISAM" ,
"MySQL AB" ,
"Collection of identical MyISAM tables" ,
PLUGIN_LICENSE_GPL,
myisammrg_init, /* Plugin Init */
NULL, /* Plugin Deinit */
0 x0100, /* 1.0 */
NULL, /* status variables */
NULL, /* system variables */
"1.0" , /* string version */
MariaDB_PLUGIN_MATURITY_STABLE /* maturity */
}
maria_declare_plugin_end;
Messung V0.5 in Prozent C=93 H=88 G=90
¤ Diese beiden folgenden Angebotsgruppen bietet das Unternehmen0.54Angebot
(Wie Sie bei der Firma Beratungs- und Dienstleistungen beauftragen können 2026-10-08)
¤
*Eine klare Vorstellung vom Zielzustand
2026-10-10
Neuigkeiten
Aktuelles
Motto des Tages
letze Version des Elbe Quellennavigators
Jenseits des Üblichen ....
Besucher
Statistik
#Sources=1126438
#Domains=1867298