1 /* Copyright (c) 2000, 2011, Oracle and/or its affiliates
2    Copyright (c) 2009, 2016, MariaDB
3 
4    This program is free software; you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation; version 2 of the License.
7 
8    This program is distributed in the hope that it will be useful,
9    but WITHOUT ANY WARRANTY; without even the implied warranty of
10    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11    GNU General Public License for more details.
12 
13    You should have received a copy of the GNU General Public License
14    along with this program; if not, write to the Free Software
15    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335  USA */
16 
17 
18 /*
19   MyISAM MERGE tables
20 
21   A MyISAM MERGE table is kind of a union of zero or more MyISAM tables.
22 
23   Besides the normal form file (.frm) a MERGE table has a meta file
24   (.MRG) with a list of tables. These are paths to the MyISAM table
25   files. The last two components of the path contain the database name
26   and the table name respectively.
27 
28   When a MERGE table is open, there exists an TABLE object for the MERGE
29   table itself and a TABLE object for each of the MyISAM tables. For
30   abbreviated writing, I call the MERGE table object "parent" and the
31   MyISAM table objects "children".
32 
33   A MERGE table is almost always opened through open_and_lock_tables()
34   and hence through open_tables(). When the parent appears in the list
35   of tables to open, the initial open of the handler does nothing but
36   read the meta file and collect a list of TABLE_LIST objects for the
37   children. This list is attached to the handler object as
38   ha_myisammrg::children_l. The end of the children list is saved in
39   ha_myisammrg::children_last_l.
40 
41   Back in open_tables(), handler::extra(HA_EXTRA_ADD_CHILDREN_LIST) is
42   called. It updates each list member with the lock type and a back
43   pointer to the parent TABLE_LIST object TABLE_LIST::parent_l. The list
44   is then inserted in the list of tables to open, right behind the
45   parent. Consequently, open_tables() opens the children, one after the
46   other. The TABLE references of the TABLE_LIST objects are implicitly
47   set to the open tables by open_table(). The children are opened as
48   independent MyISAM tables, right as if they are used by the SQL
49   statement.
50 
51   When all tables from the statement query list are open,
52   handler::extra(HA_EXTRA_ATTACH_CHILDREN) is called. It "attaches" the
53   children to the parent. All required references between parent and
54   children are set up.
55 
56   The MERGE storage engine sets up an array with references to the
57   low-level MyISAM table objects (MI_INFO). It remembers the state of
58   the table in MYRG_INFO::children_attached.
59 
60   If necessary, the compatibility of parent and children is checked.
61   This check is necessary when any of the objects are reopend. This is
62   detected by comparing the current table def version against the
63   remembered child def version. On parent open, the list members are
64   initialized to an "impossible"/"undefined" version value. So the check
65   is always executed on the first attach.
66 
67   The version check is done in myisammrg_attach_children_callback(),
68   which is called for every child. ha_myisammrg::attach_children()
69   initializes 'need_compat_check' to FALSE and
70   myisammrg_attach_children_callback() sets it ot TRUE if a table
71   def version mismatches the remembered child def version.
72 
73   The children chain remains in the statement query list until the table
74   is closed or the children are detached. This is done so that the
75   children are locked by lock_tables().
76 
77   At statement end the children are detached. At the next statement
78   begin the open-add-attach sequence repeats. There is no exception for
79   LOCK TABLES. The fresh establishment of the parent-child relationship
80   before every statement catches numerous cases of ALTER/FLUSH/DROP/etc
81   of parent or children during LOCK TABLES.
82 
83   ---
84 
85   On parent open the storage engine structures are allocated and initialized.
86   They stay with the open table until its final close.
87 */
88 
89 #ifdef USE_PRAGMA_IMPLEMENTATION
90 #pragma implementation				// gcc: Class implementation
91 #endif
92 
93 #define MYSQL_SERVER 1
94 #include <my_global.h>
95 #include "sql_priv.h"
96 #include "unireg.h"
97 #include "sql_cache.h"                          // query_cache_*
98 #include "sql_show.h"                           // append_identifier
99 #include "sql_table.h"                         // build_table_filename
100 #include <m_ctype.h>
101 #include "../myisam/ha_myisam.h"
102 #include "ha_myisammrg.h"
103 #include "myrg_def.h"
104 #include "thr_malloc.h"                         // init_sql_alloc
105 #include "sql_class.h"                          // THD
106 #include "debug_sync.h"
107 
myisammrg_create_handler(handlerton * hton,TABLE_SHARE * table,MEM_ROOT * mem_root)108 static handler *myisammrg_create_handler(handlerton *hton,
109                                          TABLE_SHARE *table,
110                                          MEM_ROOT *mem_root)
111 {
112   return new (mem_root) ha_myisammrg(hton, table);
113 }
114 
115 
116 /**
117   @brief Constructor
118 */
119 
ha_myisammrg(handlerton * hton,TABLE_SHARE * table_arg)120 ha_myisammrg::ha_myisammrg(handlerton *hton, TABLE_SHARE *table_arg)
121   :handler(hton, table_arg), file(0), is_cloned(0)
122 {
123   init_sql_alloc(rg_key_memory_children, &children_mem_root,
124                  FN_REFLEN + ALLOC_ROOT_MIN_BLOCK_SIZE, 0, MYF(0));
125 }
126 
127 
128 /**
129   @brief Destructor
130 */
131 
~ha_myisammrg(void)132 ha_myisammrg::~ha_myisammrg(void)
133 {
134   free_root(&children_mem_root, MYF(0));
135 }
136 
137 
138 static const char *ha_myisammrg_exts[] = {
139   MYRG_NAME_EXT,
140   NullS
141 };
142 extern int table2myisam(TABLE *table_arg, MI_KEYDEF **keydef_out,
143                         MI_COLUMNDEF **recinfo_out, uint *records_out);
144 extern int check_definition(MI_KEYDEF *t1_keyinfo,
145                             MI_COLUMNDEF *t1_recinfo,
146                             uint t1_keys, uint t1_recs,
147                             MI_KEYDEF *t2_keyinfo,
148                             MI_COLUMNDEF *t2_recinfo,
149                             uint t2_keys, uint t2_recs, bool strict,
150                             TABLE *table_arg);
151 static void split_file_name(const char *file_name,
152 			    LEX_STRING *db, LEX_STRING *name);
153 
154 
myrg_print_wrong_table(const char * table_name)155 extern "C" void myrg_print_wrong_table(const char *table_name)
156 {
157   LEX_STRING db= {NULL, 0}, name;
158   char buf[FN_REFLEN];
159   split_file_name(table_name, &db, &name);
160   memcpy(buf, db.str, db.length);
161   buf[db.length]= '.';
162   memcpy(buf + db.length + 1, name.str, name.length);
163   buf[db.length + name.length + 1]= 0;
164   /*
165     Push an error to be reported as part of CHECK/REPAIR result-set.
166     Note that calling my_error() from handler is a hack which is kept
167     here to avoid refactoring. Normally engines should report errors
168     through return value which will be interpreted by caller using
169     handler::print_error() call.
170   */
171   my_error(ER_ADMIN_WRONG_MRG_TABLE, MYF(0), buf);
172 }
173 
174 
index_type(uint key_number)175 const char *ha_myisammrg::index_type(uint key_number)
176 {
177   return ((table->key_info[key_number].flags & HA_FULLTEXT) ?
178 	  "FULLTEXT" :
179 	  (table->key_info[key_number].flags & HA_SPATIAL) ?
180 	  "SPATIAL" :
181 	  (table->key_info[key_number].algorithm == HA_KEY_ALG_RTREE) ?
182 	  "RTREE" :
183 	  "BTREE");
184 }
185 
186 
187 /**
188   Callback function for open of a MERGE parent table.
189 
190   @param[in]    callback_param  data pointer as given to myrg_parent_open()
191                                 this is used to pass the handler handle
192   @param[in]    filename        file name of MyISAM table
193                                 without extension.
194 
195   @return status
196     @retval     0               OK
197     @retval     != 0            Error
198 
199   @detail
200 
201     This function adds a TABLE_LIST object for a MERGE child table to a
202     list of tables in the parent handler object. It is called for each
203     child table.
204 
205     The list of child TABLE_LIST objects is kept in the handler object
206     of the parent for the whole life time of the MERGE table. It is
207     inserted in the statement query list behind the MERGE parent
208     TABLE_LIST object when the MERGE table is opened. It is removed from
209     the statement query list at end of statement or at children detach.
210 
211     All memory used for the child TABLE_LIST objects and the strings
212     referred by it are taken from the parent
213     ha_myisammrg::children_mem_root. Thus they are all freed implicitly at
214     the final close of the table.
215 
216     children_l -> TABLE_LIST::next_global -> TABLE_LIST::next_global
217     #             #               ^          #               ^
218     #             #               |          #               |
219     #             #               +--------- TABLE_LIST::prev_global
220     #             #                                          |
221     #       |<--- TABLE_LIST::prev_global                    |
222     #                                                        |
223     children_last_l -----------------------------------------+
224 */
225 
226 CPP_UNNAMED_NS_START
227 
myisammrg_parent_open_callback(void * callback_param,const char * filename)228 extern "C" int myisammrg_parent_open_callback(void *callback_param,
229                                               const char *filename)
230 {
231   ha_myisammrg  *ha_myrg= (ha_myisammrg*) callback_param;
232   TABLE         *parent= ha_myrg->table_ptr();
233   Mrg_child_def *mrg_child_def;
234   char          *db;
235   char          *table_name;
236   size_t        dirlen;
237   size_t        db_length;
238   size_t        table_name_length;
239   char          dir_path[FN_REFLEN];
240   char          name_buf[NAME_LEN];
241   DBUG_ENTER("myisammrg_parent_open_callback");
242 
243   /*
244     Depending on MySQL version, filename may be encoded by table name to
245     file name encoding or not. Always encoded if parent table is created
246     by 5.1.46+. Encoded if parent is created by 5.1.6+ and child table is
247     in different database.
248   */
249   if (!has_path(filename))
250   {
251     /* Child is in the same database as parent. */
252     db_length= parent->s->db.length;
253     db= strmake_root(&ha_myrg->children_mem_root, parent->s->db.str, db_length);
254     /* Child table name is encoded in parent dot-MRG starting with 5.1.46. */
255     if (parent->s->mysql_version >= 50146)
256     {
257       table_name_length= filename_to_tablename(filename, name_buf,
258                                                sizeof(name_buf));
259       table_name= strmake_root(&ha_myrg->children_mem_root, name_buf,
260                                table_name_length);
261     }
262     else
263     {
264       table_name_length= strlen(filename);
265       table_name= strmake_root(&ha_myrg->children_mem_root, filename,
266                                table_name_length);
267     }
268   }
269   else
270   {
271     DBUG_ASSERT(strlen(filename) < sizeof(dir_path));
272     fn_format(dir_path, filename, "", "", 0);
273     /* Extract child table name and database name from filename. */
274     dirlen= dirname_length(dir_path);
275     /* Child db/table name is encoded in parent dot-MRG starting with 5.1.6. */
276     if (parent->s->mysql_version >= 50106)
277     {
278       table_name_length= filename_to_tablename(dir_path + dirlen, name_buf,
279                                                sizeof(name_buf));
280       table_name= strmake_root(&ha_myrg->children_mem_root, name_buf,
281                                table_name_length);
282       dir_path[dirlen - 1]= 0;
283       dirlen= dirname_length(dir_path);
284       db_length= filename_to_tablename(dir_path + dirlen, name_buf, sizeof(name_buf));
285       db= strmake_root(&ha_myrg->children_mem_root, name_buf, db_length);
286     }
287     else
288     {
289       table_name_length= strlen(dir_path + dirlen);
290       table_name= strmake_root(&ha_myrg->children_mem_root, dir_path + dirlen,
291                                table_name_length);
292       dir_path[dirlen - 1]= 0;
293       dirlen= dirname_length(dir_path);
294       db_length= strlen(dir_path + dirlen);
295       db= strmake_root(&ha_myrg->children_mem_root, dir_path + dirlen,
296                        db_length);
297     }
298   }
299 
300   if (! db || ! table_name)
301     DBUG_RETURN(1);
302 
303   DBUG_PRINT("myrg", ("open: '%.*s'.'%.*s'", (int) db_length, db,
304                       (int) table_name_length, table_name));
305 
306   /* Convert to lowercase if required. */
307   if (lower_case_table_names && table_name_length)
308   {
309     /* purecov: begin tested */
310     table_name_length= my_casedn_str(files_charset_info, table_name);
311     /* purecov: end */
312   }
313 
314   mrg_child_def= new (&ha_myrg->children_mem_root)
315                  Mrg_child_def(db, db_length, table_name, table_name_length);
316 
317   if (! mrg_child_def ||
318       ha_myrg->child_def_list.push_back(mrg_child_def,
319                                         &ha_myrg->children_mem_root))
320   {
321     DBUG_RETURN(1);
322   }
323   DBUG_RETURN(0);
324 }
325 
326 CPP_UNNAMED_NS_END
327 
328 
329 /*
330   Set external_ref for the child MyISAM tables. They need this to be set in
331   order to check for killed status.
332 */
myrg_set_external_ref(MYRG_INFO * m_info,void * ext_ref_arg)333 static void myrg_set_external_ref(MYRG_INFO *m_info, void *ext_ref_arg)
334 {
335   int i;
336   for (i= 0; i < (int)m_info->tables; i++)
337   {
338     m_info->open_tables[i].table->external_ref= ext_ref_arg;
339   }
340 }
341 
342 /**
343   Open a MERGE parent table, but not its children.
344 
345   @param[in]    name               MERGE table path name
346   @param[in]    mode               read/write mode, unused
347   @param[in]    test_if_locked_arg open flags
348 
349   @return       status
350   @retval     0                    OK
351   @retval     -1                   Error, my_errno gives reason
352 
353   @detail
354   This function initializes the MERGE storage engine structures
355   and adds a child list of TABLE_LIST to the parent handler.
356 */
357 
open(const char * name,int mode,uint test_if_locked_arg)358 int ha_myisammrg::open(const char *name, int mode __attribute__((unused)),
359                        uint test_if_locked_arg)
360 {
361   DBUG_ENTER("ha_myisammrg::open");
362   DBUG_PRINT("myrg", ("name: '%s'  table: %p", name, table));
363   DBUG_PRINT("myrg", ("test_if_locked_arg: %u", test_if_locked_arg));
364 
365   /* Must not be used when table is open. */
366   DBUG_ASSERT(!this->file);
367 
368   /* Save for later use. */
369   test_if_locked= test_if_locked_arg;
370 
371   /* In case this handler was open and closed before, free old data. */
372   free_root(&this->children_mem_root, MYF(MY_MARK_BLOCKS_FREE));
373 
374   /*
375     Initialize variables that are used, modified, and/or set by
376     myisammrg_parent_open_callback().
377     'children_l' is the head of the children chain.
378     'children_last_l' points to the end of the children chain.
379     'my_errno' is set by myisammrg_parent_open_callback() in
380     case of an error.
381   */
382   children_l= NULL;
383   children_last_l= NULL;
384   child_def_list.empty();
385   my_errno= 0;
386 
387   /* retrieve children table list. */
388   if (is_cloned)
389   {
390     /*
391       Open and attaches the MyISAM tables,that are under the MERGE table
392       parent, on the MyISAM storage engine interface directly within the
393       MERGE engine. The new MyISAM table instances, as well as the MERGE
394       clone itself, are not visible in the table cache. This is not a
395       problem because all locking is handled by the original MERGE table
396       from which this is cloned of.
397     */
398     if (!(file= myrg_open(name, table->db_stat,  HA_OPEN_IGNORE_IF_LOCKED)))
399     {
400       DBUG_PRINT("error", ("my_errno %d", my_errno));
401       DBUG_RETURN(my_errno ? my_errno : -1);
402     }
403 
404     file->children_attached= TRUE;
405     myrg_set_external_ref(file, (void*)table);
406 
407     info(HA_STATUS_NO_LOCK | HA_STATUS_VARIABLE | HA_STATUS_CONST);
408   }
409   else if (!(file= myrg_parent_open(name, myisammrg_parent_open_callback, this)))
410   {
411     /* purecov: begin inspected */
412     DBUG_PRINT("error", ("my_errno %d", my_errno));
413     DBUG_RETURN(my_errno ? my_errno : -1);
414     /* purecov: end */
415   }
416   DBUG_PRINT("myrg", ("MYRG_INFO: %p  child tables: %u",
417                       file, file->tables));
418   DBUG_RETURN(0);
419 }
420 
421 
422 /**
423   Add list of MERGE children to a TABLE_LIST chain.
424 
425   @return status
426     @retval     0               OK
427     @retval     != 0            Error
428 
429   @detail
430     When a MERGE parent table has just been opened, insert the
431     TABLE_LIST chain from the MERGE handler into the table list used for
432     opening tables for this statement. This lets the children be opened
433     too.
434 */
435 
add_children_list(void)436 int ha_myisammrg::add_children_list(void)
437 {
438   TABLE_LIST  *parent_l= this->table->pos_in_table_list;
439   THD  *thd= table->in_use;
440   List_iterator_fast<Mrg_child_def> it(child_def_list);
441   Mrg_child_def *mrg_child_def;
442   DBUG_ENTER("ha_myisammrg::add_children_list");
443   DBUG_PRINT("myrg", ("table: '%s'.'%s' %p", this->table->s->db.str,
444                       this->table->s->table_name.str, this->table));
445 
446   /* Must call this with open table. */
447   DBUG_ASSERT(this->file);
448 
449   /* Ignore this for empty MERGE tables (UNION=()). */
450   if (!this->file->tables)
451   {
452     DBUG_PRINT("myrg", ("empty merge table union"));
453     goto end;
454   }
455 
456   /* Must not call this with attached children. */
457   DBUG_ASSERT(!this->file->children_attached);
458 
459   /* Must not call this with children list in place. */
460   DBUG_ASSERT(this->children_l == NULL);
461 
462   /*
463     Prevent inclusion of another MERGE table, which could make infinite
464     recursion.
465   */
466   if (parent_l->parent_l)
467   {
468     my_error(ER_ADMIN_WRONG_MRG_TABLE, MYF(0), parent_l->alias.str);
469     DBUG_RETURN(1);
470   }
471 
472   while ((mrg_child_def= it++))
473   {
474     TABLE_LIST  *child_l;
475     LEX_CSTRING db;
476     LEX_CSTRING table_name;
477 
478     child_l= (TABLE_LIST*) thd->alloc(sizeof(TABLE_LIST));
479     db.str= (char*) thd->memdup(mrg_child_def->db.str, mrg_child_def->db.length+1);
480     db.length= mrg_child_def->db.length;
481     table_name.str= (char*) thd->memdup(mrg_child_def->name.str,
482                                         mrg_child_def->name.length+1);
483     table_name.length= mrg_child_def->name.length;
484 
485     if (child_l == NULL || db.str == NULL || table_name.str == NULL)
486       DBUG_RETURN(1);
487 
488     child_l->init_one_table(&db, &table_name, 0, parent_l->lock_type);
489     /* Set parent reference. Used to detect MERGE in children list. */
490     child_l->parent_l= parent_l;
491     /* Copy select_lex. Used in unique_table() at least. */
492     child_l->select_lex= parent_l->select_lex;
493     /* Set the expected table version, to not cause spurious re-prepare. */
494     child_l->set_table_ref_id(mrg_child_def->get_child_table_ref_type(),
495                               mrg_child_def->get_child_def_version());
496     /*
497       Copy parent's prelocking attribute to allow opening of child
498       temporary residing in the prelocking list.
499     */
500     child_l->prelocking_placeholder= parent_l->prelocking_placeholder;
501     /*
502       For statements which acquire a SNW metadata lock on a parent table and
503       then later try to upgrade it to an X lock (e.g. ALTER TABLE), SNW
504       locks should be also taken on the children tables.
505 
506       Otherwise we end up in a situation where the thread trying to upgrade SNW
507       to X lock on the parent also holds a SR metadata lock and a read
508       thr_lock.c lock on the child. As a result, another thread might be
509       blocked on the thr_lock.c lock for the child after successfully acquiring
510       a SR or SW metadata lock on it. If at the same time this second thread
511       has a shared metadata lock on the parent table or there is some other
512       thread which has a shared metadata lock on the parent and is waiting for
513       this second thread, we get a deadlock. This deadlock cannot be properly
514       detected by the MDL subsystem as part of the waiting happens within
515       thr_lock.c. By taking SNW locks on the child tables we ensure that any
516       thread which waits for a thread doing SNW -> X upgrade, does this within
517       the MDL subsystem and thus potential deadlocks are exposed to the deadlock
518       detector.
519 
520       We don't do the same thing for SNRW locks as this would allow
521       DDL on implicitly locked underlying tables of a MERGE table.
522     */
523     if (! thd->locked_tables_mode &&
524         parent_l->mdl_request.type == MDL_SHARED_UPGRADABLE)
525       child_l->mdl_request.set_type(MDL_SHARED_NO_WRITE);
526     /* Link TABLE_LIST object into the children list. */
527     if (this->children_last_l)
528       child_l->prev_global= this->children_last_l;
529     else
530     {
531       /* Initialize children_last_l when handling first child. */
532       this->children_last_l= &this->children_l;
533     }
534     *this->children_last_l= child_l;
535     this->children_last_l= &child_l->next_global;
536   }
537 
538   /* Insert children into the table list. */
539   if (parent_l->next_global)
540     parent_l->next_global->prev_global= this->children_last_l;
541   *this->children_last_l= parent_l->next_global;
542   parent_l->next_global= this->children_l;
543   this->children_l->prev_global= &parent_l->next_global;
544   /*
545     We have to update LEX::query_tables_last if children are added to
546     the tail of the table list in order to be able correctly add more
547     elements to it (e.g. as part of prelocking process).
548   */
549   if (thd->lex->query_tables_last == &parent_l->next_global)
550     thd->lex->query_tables_last= this->children_last_l;
551   /*
552     The branch below works only when re-executing a prepared
553     statement or a stored routine statement:
554     We've just modified query_tables_last. Keep it in sync with
555     query_tables_last_own, if it was set by the prelocking code.
556     This ensures that the check that prohibits double updates (*)
557     can correctly identify what tables belong to the main statement.
558     (*) A double update is, e.g. when a user issues UPDATE t1 and
559     t1 has an AFTER UPDATE trigger that also modifies t1.
560   */
561   if (thd->lex->query_tables_own_last == &parent_l->next_global)
562     thd->lex->query_tables_own_last= this->children_last_l;
563 
564 end:
565   DBUG_RETURN(0);
566 }
567 
568 
569 /**
570   A context of myrg_attach_children() callback.
571 */
572 
573 class Mrg_attach_children_callback_param
574 {
575 public:
576   /**
577     'need_compat_check' is set by myisammrg_attach_children_callback()
578     if a child fails the table def version check.
579   */
580   bool need_compat_check;
581   /** TABLE_LIST identifying this merge parent. */
582   TABLE_LIST *parent_l;
583   /** Iterator position, the current child to attach. */
584   TABLE_LIST *next_child_attach;
585   List_iterator_fast<Mrg_child_def> def_it;
586   Mrg_child_def *mrg_child_def;
587 public:
Mrg_attach_children_callback_param(TABLE_LIST * parent_l_arg,TABLE_LIST * first_child,List<Mrg_child_def> & child_def_list)588   Mrg_attach_children_callback_param(TABLE_LIST *parent_l_arg,
589                                      TABLE_LIST *first_child,
590                                      List<Mrg_child_def> &child_def_list)
591     :need_compat_check(FALSE),
592     parent_l(parent_l_arg),
593     next_child_attach(first_child),
594     def_it(child_def_list),
595     mrg_child_def(def_it++)
596   {}
next()597   void next()
598   {
599     next_child_attach= next_child_attach->next_global;
600     if (next_child_attach && next_child_attach->parent_l != parent_l)
601       next_child_attach= NULL;
602     if (mrg_child_def)
603       mrg_child_def= def_it++;
604   }
605 };
606 
607 
608 /**
609   Callback function for attaching a MERGE child table.
610 
611   @param[in]    callback_param  data pointer as given to myrg_attach_children()
612                                 this is used to pass the handler handle
613 
614   @return       pointer to open MyISAM table structure
615     @retval     !=NULL                  OK, returning pointer
616     @retval     NULL,                   Error.
617 
618   @detail
619     This function retrieves the MyISAM table handle from the
620     next child table. It is called for each child table.
621 */
622 
623 CPP_UNNAMED_NS_START
624 
myisammrg_attach_children_callback(void * callback_param)625 extern "C" MI_INFO *myisammrg_attach_children_callback(void *callback_param)
626 {
627   Mrg_attach_children_callback_param *param=
628     (Mrg_attach_children_callback_param*) callback_param;
629   TABLE         *parent= param->parent_l->table;
630   TABLE         *child;
631   TABLE_LIST    *child_l= param->next_child_attach;
632   Mrg_child_def *mrg_child_def= param->mrg_child_def;
633   MI_INFO       *myisam= NULL;
634   DBUG_ENTER("myisammrg_attach_children_callback");
635 
636   /*
637     Number of children in the list and MYRG_INFO::tables_count,
638     which is used by caller of this function, should always match.
639   */
640   DBUG_ASSERT(child_l);
641 
642   child= child_l->table;
643   /* Prepare for next child. */
644   param->next();
645 
646   /*
647     When MERGE table is opened for CHECK or REPAIR TABLE statements,
648     failure to open any of underlying tables is ignored until this moment
649     (this is needed to provide complete list of the problematic underlying
650     tables in CHECK/REPAIR TABLE output).
651     Here we detect such a situation and report an appropriate error.
652   */
653   if (! child)
654   {
655     DBUG_PRINT("error", ("failed to open underlying table '%s'.'%s'",
656                          child_l->db.str, child_l->table_name.str));
657     /*
658       This should only happen inside of CHECK/REPAIR TABLE or
659       for the tables added by the pre-locking code.
660     */
661     DBUG_ASSERT(current_thd->open_options & HA_OPEN_FOR_REPAIR ||
662                 child_l->prelocking_placeholder);
663     goto end;
664   }
665 
666   /*
667     Do a quick compatibility check. The table def version is set when
668     the table share is created. The child def version is copied
669     from the table def version after a successful compatibility check.
670     We need to repeat the compatibility check only if a child is opened
671     from a different share than last time it was used with this MERGE
672     table.
673   */
674   DBUG_PRINT("myrg", ("table_def_version last: %lu  current: %lu",
675                       (ulong) mrg_child_def->get_child_def_version(),
676                       (ulong) child->s->get_table_def_version()));
677   if (mrg_child_def->get_child_def_version() != child->s->get_table_def_version())
678     param->need_compat_check= TRUE;
679 
680   /*
681     If child is temporary, parent must be temporary as well. Other
682     parent/child combinations are allowed. This check must be done for
683     every child on every open because the table def version can overlap
684     between temporary and non-temporary tables. We need to detect the
685     case where a non-temporary table has been replaced with a temporary
686     table of the same version. Or vice versa. A very unlikely case, but
687     it could happen. (Note that the condition was different from
688     5.1.23/6.0.4(Bug#19627) to 5.5.6 (Bug#36171): child->s->tmp_table !=
689     parent->s->tmp_table. Tables were required to have the same status.)
690   */
691   if (child->s->tmp_table && !parent->s->tmp_table)
692   {
693     DBUG_PRINT("error", ("temporary table mismatch parent: %d  child: %d",
694                          parent->s->tmp_table, child->s->tmp_table));
695     goto end;
696   }
697 
698   /* Extract the MyISAM table structure pointer from the handler object. */
699   if ((child->file->ht->db_type != DB_TYPE_MYISAM) ||
700       !(myisam= ((ha_myisam*) child->file)->file_ptr()))
701   {
702     DBUG_PRINT("error", ("no MyISAM handle for child table: '%s'.'%s' %p",
703                          child->s->db.str, child->s->table_name.str,
704                          child));
705   }
706 
707   DBUG_PRINT("myrg", ("MyISAM handle: %p", myisam));
708 
709  end:
710 
711   if (!myisam &&
712       (current_thd->open_options & HA_OPEN_FOR_REPAIR))
713   {
714     char buf[2*NAME_LEN + 1 + 1];
715     strxnmov(buf, sizeof(buf) - 1, child_l->db.str, ".",
716              child_l->table_name.str, NULL);
717     /*
718       Push an error to be reported as part of CHECK/REPAIR result-set.
719       Note that calling my_error() from handler is a hack which is kept
720       here to avoid refactoring. Normally engines should report errors
721       through return value which will be interpreted by caller using
722       handler::print_error() call.
723     */
724     my_error(ER_ADMIN_WRONG_MRG_TABLE, MYF(0), buf);
725   }
726 
727   DBUG_RETURN(myisam);
728 }
729 
730 CPP_UNNAMED_NS_END
731 
732 /**
733    Returns a cloned instance of the current handler.
734 
735    @return A cloned handler instance.
736  */
clone(const char * name,MEM_ROOT * mem_root)737 handler *ha_myisammrg::clone(const char *name, MEM_ROOT *mem_root)
738 {
739   MYRG_TABLE    *u_table,*newu_table;
740   ha_myisammrg *new_handler=
741     (ha_myisammrg*) get_new_handler(table->s, mem_root, table->s->db_type());
742   if (!new_handler)
743     return NULL;
744 
745   /* Inform ha_myisammrg::open() that it is a cloned handler */
746   new_handler->is_cloned= TRUE;
747   /*
748     Allocate handler->ref here because otherwise ha_open will allocate it
749     on this->table->mem_root and we will not be able to reclaim that memory
750     when the clone handler object is destroyed.
751   */
752   if (!(new_handler->ref= (uchar*) alloc_root(mem_root, ALIGN_SIZE(ref_length)*2)))
753   {
754     delete new_handler;
755     return NULL;
756   }
757 
758   if (new_handler->ha_open(table, name, table->db_stat,
759                            HA_OPEN_IGNORE_IF_LOCKED))
760   {
761     delete new_handler;
762     return NULL;
763   }
764 
765   /*
766     Iterate through the original child tables and
767     copy the state into the cloned child tables.
768     We need to do this because all the child tables
769     can be involved in delete.
770   */
771   newu_table= new_handler->file->open_tables;
772   for (u_table= file->open_tables; u_table < file->end_table; u_table++)
773   {
774     newu_table->table->state= u_table->table->state;
775     newu_table++;
776   }
777 
778   return new_handler;
779  }
780 
781 
782 /**
783   Attach children to a MERGE table.
784 
785   @return status
786     @retval     0               OK
787     @retval     != 0            Error, my_errno gives reason
788 
789   @detail
790     Let the storage engine attach its children through a callback
791     function. Check table definitions for consistency.
792 
793   @note
794     Special thd->open_options may be in effect. We can make use of
795     them in attach. I.e. we use HA_OPEN_FOR_REPAIR to report the names
796     of mismatching child tables. We cannot transport these options in
797     ha_myisammrg::test_if_locked because they may change after the
798     parent is opened. The parent is kept open in the table cache over
799     multiple statements and can be used by other threads. Open options
800     can change over time.
801 */
802 
attach_children(void)803 int ha_myisammrg::attach_children(void)
804 {
805   MYRG_TABLE    *u_table;
806   MI_COLUMNDEF  *recinfo;
807   MI_KEYDEF     *keyinfo;
808   uint          recs;
809   uint          keys= table->s->keys;
810   TABLE_LIST   *parent_l= table->pos_in_table_list;
811   int           error;
812   Mrg_attach_children_callback_param param(parent_l, this->children_l, child_def_list);
813   DBUG_ENTER("ha_myisammrg::attach_children");
814   DBUG_PRINT("myrg", ("table: '%s'.'%s' %p", table->s->db.str,
815                       table->s->table_name.str, table));
816   DBUG_PRINT("myrg", ("test_if_locked: %u", this->test_if_locked));
817 
818   /* Must call this with open table. */
819   DBUG_ASSERT(this->file);
820 
821   /*
822     A MERGE table with no children (empty union) is always seen as
823     attached internally.
824   */
825   if (!this->file->tables)
826   {
827     DBUG_PRINT("myrg", ("empty merge table union"));
828     goto end;
829   }
830   DBUG_PRINT("myrg", ("child tables: %u", this->file->tables));
831 
832   /* Must not call this with attached children. */
833   DBUG_ASSERT(!this->file->children_attached);
834 
835   DEBUG_SYNC(current_thd, "before_myisammrg_attach");
836   /* Must call this with children list in place. */
837   DBUG_ASSERT(this->table->pos_in_table_list->next_global == this->children_l);
838 
839   if (myrg_attach_children(this->file, this->test_if_locked |
840                            current_thd->open_options,
841                            myisammrg_attach_children_callback, &param,
842                            (my_bool *) &param.need_compat_check))
843   {
844     error= my_errno;
845     goto err;
846   }
847   DBUG_PRINT("myrg", ("calling myrg_extrafunc"));
848   myrg_extrafunc(file, query_cache_invalidate_by_MyISAM_filename_ref);
849   if (!(test_if_locked == HA_OPEN_WAIT_IF_LOCKED ||
850 	test_if_locked == HA_OPEN_ABORT_IF_LOCKED))
851     myrg_extra(file,HA_EXTRA_NO_WAIT_LOCK,0);
852   info(HA_STATUS_NO_LOCK | HA_STATUS_VARIABLE | HA_STATUS_CONST);
853   if (!(test_if_locked & HA_OPEN_WAIT_IF_LOCKED))
854     myrg_extra(file,HA_EXTRA_WAIT_LOCK,0);
855 
856   /*
857     The compatibility check is required only if one or more children do
858     not match their table def version from the last check. This will
859     always happen at the first attach because the reference child def
860     version is initialized to 'undefined' at open.
861   */
862   DBUG_PRINT("myrg", ("need_compat_check: %d", param.need_compat_check));
863   if (param.need_compat_check)
864   {
865     TABLE_LIST *child_l;
866 
867     if (table->s->reclength != stats.mean_rec_length && stats.mean_rec_length)
868     {
869       DBUG_PRINT("error",("reclength: %lu  mean_rec_length: %lu",
870                           table->s->reclength, stats.mean_rec_length));
871       if (test_if_locked & HA_OPEN_FOR_REPAIR)
872       {
873         /* purecov: begin inspected */
874         myrg_print_wrong_table(file->open_tables->table->filename);
875         /* purecov: end */
876       }
877       error= HA_ERR_WRONG_MRG_TABLE_DEF;
878       goto err;
879     }
880     /*
881       Both recinfo and keyinfo are allocated by my_multi_malloc(), thus
882       only recinfo must be freed.
883     */
884     if ((error= table2myisam(table, &keyinfo, &recinfo, &recs)))
885     {
886       /* purecov: begin inspected */
887       DBUG_PRINT("error", ("failed to convert TABLE object to MyISAM "
888                            "key and column definition"));
889       goto err;
890       /* purecov: end */
891     }
892     for (u_table= file->open_tables; u_table < file->end_table; u_table++)
893     {
894       if (check_definition(keyinfo, recinfo, keys, recs,
895                            u_table->table->s->keyinfo, u_table->table->s->rec,
896                            u_table->table->s->base.keys,
897                            u_table->table->s->base.fields, false, NULL))
898       {
899         DBUG_PRINT("error", ("table definition mismatch: '%s'",
900                              u_table->table->filename));
901         error= HA_ERR_WRONG_MRG_TABLE_DEF;
902         if (!(this->test_if_locked & HA_OPEN_FOR_REPAIR))
903         {
904           my_free(recinfo);
905           goto err;
906         }
907         /* purecov: begin inspected */
908         myrg_print_wrong_table(u_table->table->filename);
909         /* purecov: end */
910       }
911     }
912     my_free(recinfo);
913     if (error == HA_ERR_WRONG_MRG_TABLE_DEF)
914       goto err; /* purecov: inspected */
915 
916     List_iterator_fast<Mrg_child_def> def_it(child_def_list);
917     DBUG_ASSERT(this->children_l);
918     for (child_l= this->children_l; ; child_l= child_l->next_global)
919     {
920       Mrg_child_def *mrg_child_def= def_it++;
921       mrg_child_def->set_child_def_version(
922         child_l->table->s->get_table_ref_type(),
923         child_l->table->s->get_table_def_version());
924 
925       if (&child_l->next_global == this->children_last_l)
926         break;
927     }
928   }
929 #if !defined(BIG_TABLES) || SIZEOF_OFF_T == 4
930   /* Merge table has more than 2G rows */
931   if (table->s->crashed)
932   {
933     DBUG_PRINT("error", ("MERGE table marked crashed"));
934     error= HA_ERR_WRONG_MRG_TABLE_DEF;
935     goto err;
936   }
937 #endif
938 
939  end:
940   DBUG_RETURN(0);
941 
942 err:
943   DBUG_PRINT("error", ("attaching MERGE children failed: %d", error));
944   print_error(error, MYF(0));
945   detach_children();
946   DBUG_RETURN(my_errno= error);
947 }
948 
949 
950 /**
951   Detach all children from a MERGE table and from the query list of tables.
952 
953   @return status
954     @retval     0               OK
955     @retval     != 0            Error, my_errno gives reason
956 
957   @note
958     Detach must not touch the child TABLE objects in any way.
959     They may have been closed at ths point already.
960     All references to the children should be removed.
961 */
962 
detach_children(void)963 int ha_myisammrg::detach_children(void)
964 {
965   TABLE_LIST *child_l;
966   DBUG_ENTER("ha_myisammrg::detach_children");
967 
968   /* Must call this with open table. */
969   DBUG_ASSERT(this->file);
970 
971   /* A MERGE table with no children (empty union) cannot be detached. */
972   if (!this->file->tables)
973   {
974     DBUG_PRINT("myrg", ("empty merge table union"));
975     goto end;
976   }
977 
978   if (this->children_l)
979   {
980     THD *thd= table->in_use;
981 
982     /* Clear TABLE references. */
983     for (child_l= this->children_l; ; child_l= child_l->next_global)
984     {
985       /*
986         Do not DBUG_ASSERT(child_l->table); open_tables might be
987         incomplete.
988 
989         Clear the table reference.
990       */
991       child_l->table= NULL;
992       /* Similarly, clear the ticket reference. */
993       child_l->mdl_request.ticket= NULL;
994 
995       /* Break when this was the last child. */
996       if (&child_l->next_global == this->children_last_l)
997         break;
998     }
999     /*
1000       Remove children from the table list. This won't fail if called
1001       twice. The list is terminated after removal.
1002 
1003       If the parent is LEX::query_tables_own_last and pre-locked tables
1004       follow (tables used by stored functions or triggers), the children
1005       are inserted behind the parent and before the pre-locked tables. But
1006       we do not adjust LEX::query_tables_own_last. The pre-locked tables
1007       could have chopped off the list by clearing
1008       *LEX::query_tables_own_last. This did also chop off the children. If
1009       we would copy the reference from *this->children_last_l in this
1010       case, we would put the chopped off pre-locked tables back to the
1011       list. So we refrain from copying it back, if the destination has
1012       been set to NULL meanwhile.
1013     */
1014     if (this->children_l->prev_global && *this->children_l->prev_global)
1015       *this->children_l->prev_global= *this->children_last_l;
1016     if (*this->children_last_l)
1017       (*this->children_last_l)->prev_global= this->children_l->prev_global;
1018 
1019     /*
1020       If table elements being removed are at the end of table list we
1021       need to adjust LEX::query_tables_last member to point to the
1022       new last element of the list.
1023     */
1024     if (thd->lex->query_tables_last == this->children_last_l)
1025       thd->lex->query_tables_last= this->children_l->prev_global;
1026 
1027     /*
1028       If the statement requires prelocking, and prelocked
1029       tables were added right after merge children, modify the
1030       last own table pointer to point at prev_global of the merge
1031       parent.
1032     */
1033     if (thd->lex->query_tables_own_last == this->children_last_l)
1034       thd->lex->query_tables_own_last= this->children_l->prev_global;
1035 
1036     /* Terminate child list. So it cannot be tried to remove again. */
1037     *this->children_last_l= NULL;
1038     this->children_l->prev_global= NULL;
1039 
1040     /* Forget about the children, we don't own their memory. */
1041     this->children_l= NULL;
1042     this->children_last_l= NULL;
1043   }
1044 
1045   if (!this->file->children_attached)
1046   {
1047     DBUG_PRINT("myrg", ("merge children are already detached"));
1048     goto end;
1049   }
1050 
1051   if (myrg_detach_children(this->file))
1052   {
1053     /* purecov: begin inspected */
1054     print_error(my_errno, MYF(0));
1055     DBUG_RETURN(my_errno ? my_errno : -1);
1056     /* purecov: end */
1057   }
1058 
1059  end:
1060   DBUG_RETURN(0);
1061 }
1062 
1063 
1064 /**
1065   Close a MERGE parent table, but not its children.
1066 
1067   @return status
1068     @retval     0               OK
1069     @retval     != 0            Error, my_errno gives reason
1070 
1071   @note
1072     The children are expected to be closed separately by the caller.
1073 */
1074 
close(void)1075 int ha_myisammrg::close(void)
1076 {
1077   int rc;
1078   DBUG_ENTER("ha_myisammrg::close");
1079   /*
1080     There are cases where children are not explicitly detached before
1081     close. detach_children() protects itself against double detach.
1082   */
1083   if (!is_cloned)
1084     detach_children();
1085 
1086   rc= myrg_close(file);
1087   file= 0;
1088   DBUG_RETURN(rc);
1089 }
1090 
write_row(const uchar * buf)1091 int ha_myisammrg::write_row(const uchar * buf)
1092 {
1093   DBUG_ENTER("ha_myisammrg::write_row");
1094   DBUG_ASSERT(this->file->children_attached);
1095 
1096   if (file->merge_insert_method == MERGE_INSERT_DISABLED || !file->tables)
1097     DBUG_RETURN(HA_ERR_TABLE_READONLY);
1098 
1099   if (table->next_number_field && buf == table->record[0])
1100   {
1101     int error;
1102     if ((error= update_auto_increment()))
1103       DBUG_RETURN(error); /* purecov: inspected */
1104   }
1105   DBUG_RETURN(myrg_write(file,buf));
1106 }
1107 
update_row(const uchar * old_data,const uchar * new_data)1108 int ha_myisammrg::update_row(const uchar * old_data, const uchar * new_data)
1109 {
1110   DBUG_ASSERT(this->file->children_attached);
1111   return myrg_update(file,old_data,new_data);
1112 }
1113 
delete_row(const uchar * buf)1114 int ha_myisammrg::delete_row(const uchar * buf)
1115 {
1116   DBUG_ASSERT(this->file->children_attached);
1117   return myrg_delete(file,buf);
1118 }
1119 
index_read_map(uchar * buf,const uchar * key,key_part_map keypart_map,enum ha_rkey_function find_flag)1120 int ha_myisammrg::index_read_map(uchar * buf, const uchar * key,
1121                                  key_part_map keypart_map,
1122                                  enum ha_rkey_function find_flag)
1123 {
1124   DBUG_ASSERT(this->file->children_attached);
1125   int error=myrg_rkey(file,buf,active_index, key, keypart_map, find_flag);
1126   return error;
1127 }
1128 
index_read_idx_map(uchar * buf,uint index,const uchar * key,key_part_map keypart_map,enum ha_rkey_function find_flag)1129 int ha_myisammrg::index_read_idx_map(uchar * buf, uint index, const uchar * key,
1130                                      key_part_map keypart_map,
1131                                      enum ha_rkey_function find_flag)
1132 {
1133   DBUG_ASSERT(this->file->children_attached);
1134   int error=myrg_rkey(file,buf,index, key, keypart_map, find_flag);
1135   return error;
1136 }
1137 
index_read_last_map(uchar * buf,const uchar * key,key_part_map keypart_map)1138 int ha_myisammrg::index_read_last_map(uchar *buf, const uchar *key,
1139                                       key_part_map keypart_map)
1140 {
1141   DBUG_ASSERT(this->file->children_attached);
1142   int error=myrg_rkey(file,buf,active_index, key, keypart_map,
1143 		      HA_READ_PREFIX_LAST);
1144   return error;
1145 }
1146 
index_next(uchar * buf)1147 int ha_myisammrg::index_next(uchar * buf)
1148 {
1149   DBUG_ASSERT(this->file->children_attached);
1150   int error=myrg_rnext(file,buf,active_index);
1151   return error;
1152 }
1153 
index_prev(uchar * buf)1154 int ha_myisammrg::index_prev(uchar * buf)
1155 {
1156   DBUG_ASSERT(this->file->children_attached);
1157   int error=myrg_rprev(file,buf, active_index);
1158   return error;
1159 }
1160 
index_first(uchar * buf)1161 int ha_myisammrg::index_first(uchar * buf)
1162 {
1163   DBUG_ASSERT(this->file->children_attached);
1164   int error=myrg_rfirst(file, buf, active_index);
1165   return error;
1166 }
1167 
index_last(uchar * buf)1168 int ha_myisammrg::index_last(uchar * buf)
1169 {
1170   DBUG_ASSERT(this->file->children_attached);
1171   int error=myrg_rlast(file, buf, active_index);
1172   return error;
1173 }
1174 
index_next_same(uchar * buf,const uchar * key,uint length)1175 int ha_myisammrg::index_next_same(uchar * buf,
1176                                   const uchar *key __attribute__((unused)),
1177                                   uint length __attribute__((unused)))
1178 {
1179   int error;
1180   DBUG_ASSERT(this->file->children_attached);
1181   do
1182   {
1183     error= myrg_rnext_same(file,buf);
1184   } while (error == HA_ERR_RECORD_DELETED);
1185   return error;
1186 }
1187 
1188 
rnd_init(bool scan)1189 int ha_myisammrg::rnd_init(bool scan)
1190 {
1191   DBUG_ASSERT(this->file->children_attached);
1192   return myrg_reset(file);
1193 }
1194 
1195 
rnd_next(uchar * buf)1196 int ha_myisammrg::rnd_next(uchar *buf)
1197 {
1198   DBUG_ASSERT(this->file->children_attached);
1199   int error=myrg_rrnd(file, buf, HA_OFFSET_ERROR);
1200   return error;
1201 }
1202 
1203 
rnd_pos(uchar * buf,uchar * pos)1204 int ha_myisammrg::rnd_pos(uchar * buf, uchar *pos)
1205 {
1206   DBUG_ASSERT(this->file->children_attached);
1207   int error=myrg_rrnd(file, buf, my_get_ptr(pos,ref_length));
1208   return error;
1209 }
1210 
position(const uchar * record)1211 void ha_myisammrg::position(const uchar *record)
1212 {
1213   DBUG_ASSERT(this->file->children_attached);
1214   ulonglong row_position= myrg_position(file);
1215   my_store_ptr(ref, ref_length, (my_off_t) row_position);
1216 }
1217 
1218 
records_in_range(uint inx,const key_range * min_key,const key_range * max_key,page_range * pages)1219 ha_rows ha_myisammrg::records_in_range(uint inx,
1220                                        const key_range *min_key,
1221                                        const key_range *max_key,
1222                                        page_range *pages)
1223 {
1224   DBUG_ASSERT(this->file->children_attached);
1225   return (ha_rows) myrg_records_in_range(file, (int) inx, min_key, max_key,
1226                                          pages);
1227 }
1228 
1229 
delete_all_rows()1230 int ha_myisammrg::delete_all_rows()
1231 {
1232   int err= 0;
1233   MYRG_TABLE *table;
1234   DBUG_ENTER("ha_myisammrg::delete_all_rows");
1235 
1236   for (table= file->open_tables; table != file->end_table; table++)
1237   {
1238     if ((err= mi_delete_all_rows(table->table)))
1239       break;
1240   }
1241 
1242   DBUG_RETURN(err);
1243 }
1244 
1245 
info(uint flag)1246 int ha_myisammrg::info(uint flag)
1247 {
1248   MYMERGE_INFO mrg_info;
1249   DBUG_ASSERT(this->file->children_attached);
1250   (void) myrg_status(file,&mrg_info,flag);
1251   /*
1252     The following fails if one has not compiled MySQL with -DBIG_TABLES
1253     and one has more than 2^32 rows in the merge tables.
1254   */
1255   stats.records = (ha_rows) mrg_info.records;
1256   stats.deleted = (ha_rows) mrg_info.deleted;
1257 #if !defined(BIG_TABLES) || SIZEOF_OFF_T == 4
1258   if ((mrg_info.records >= (ulonglong) 1 << 32) ||
1259       (mrg_info.deleted >= (ulonglong) 1 << 32))
1260     table->s->crashed= 1;
1261 #endif
1262   stats.data_file_length= mrg_info.data_file_length;
1263   if (mrg_info.errkey >= (int) table_share->keys)
1264   {
1265     /*
1266      If value of errkey is higher than the number of keys
1267      on the table set errkey to MAX_KEY. This will be
1268      treated as unknown key case and error message generator
1269      won't try to locate key causing segmentation fault.
1270     */
1271     mrg_info.errkey= MAX_KEY;
1272   }
1273   table->s->keys_in_use.set_prefix(table->s->keys);
1274   stats.mean_rec_length= mrg_info.reclength;
1275 
1276   /*
1277     The handler::block_size is used all over the code in index scan cost
1278     calculations. It is used to get number of disk seeks required to
1279     retrieve a number of index tuples.
1280     If the merge table has N underlying tables, then (assuming underlying
1281     tables have equal size, the only "simple" approach we can use)
1282     retrieving X index records from a merge table will require N times more
1283     disk seeks compared to doing the same on a MyISAM table with equal
1284     number of records.
1285     In the edge case (file_tables > myisam_block_size) we'll get
1286     block_size==0, and index calculation code will act as if we need one
1287     disk seek to retrieve one index tuple.
1288 
1289     TODO: In 5.2 index scan cost calculation will be factored out into a
1290     virtual function in class handler and we'll be able to remove this hack.
1291   */
1292   stats.block_size= 0;
1293   if (file->tables)
1294     stats.block_size= myisam_block_size / file->tables;
1295 
1296   stats.update_time= 0;
1297 #if SIZEOF_OFF_T > 4
1298   ref_length=6;					// Should be big enough
1299 #else
1300   ref_length=4;					// Can't be > than my_off_t
1301 #endif
1302   if (flag & HA_STATUS_CONST)
1303   {
1304     if (table->s->key_parts && mrg_info.rec_per_key)
1305     {
1306 #ifdef HAVE_valgrind
1307       /*
1308         valgrind may be unhappy about it, because optimizer may access values
1309         between file->keys and table->key_parts, that will be uninitialized.
1310         It's safe though, because even if opimizer will decide to use a key
1311         with such a number, it'll be an error later anyway.
1312       */
1313       bzero((char*) table->key_info[0].rec_per_key,
1314             sizeof(table->key_info[0].rec_per_key[0]) * table->s->key_parts);
1315 #endif
1316       memcpy((char*) table->key_info[0].rec_per_key,
1317 	     (char*) mrg_info.rec_per_key,
1318              sizeof(table->key_info[0].rec_per_key[0]) *
1319              MY_MIN(file->keys, table->s->key_parts));
1320     }
1321   }
1322   if (flag & HA_STATUS_ERRKEY)
1323   {
1324     errkey= mrg_info.errkey;
1325     my_store_ptr(dup_ref, ref_length, mrg_info.dupp_key_pos);
1326   }
1327   return 0;
1328 }
1329 
1330 
extra(enum ha_extra_function operation)1331 int ha_myisammrg::extra(enum ha_extra_function operation)
1332 {
1333   if (operation == HA_EXTRA_ADD_CHILDREN_LIST)
1334   {
1335     int rc= add_children_list();
1336     return(rc);
1337   }
1338   else if (operation == HA_EXTRA_ATTACH_CHILDREN)
1339   {
1340     int rc= attach_children();
1341     if (!rc)
1342       (void) extra(HA_EXTRA_NO_READCHECK); // Not needed in SQL
1343     return(rc);
1344   }
1345   else if (operation == HA_EXTRA_IS_ATTACHED_CHILDREN)
1346   {
1347     /* For the upper layer pretend empty MERGE union is never attached. */
1348     return(file && file->tables && file->children_attached);
1349   }
1350   else if (operation == HA_EXTRA_DETACH_CHILDREN)
1351   {
1352     /*
1353       Note that detach must not touch the children in any way.
1354       They may have been closed at ths point already.
1355     */
1356     int rc= detach_children();
1357     return(rc);
1358   }
1359 
1360   /* As this is just a mapping, we don't have to force the underlying
1361      tables to be closed */
1362   if (operation == HA_EXTRA_FORCE_REOPEN ||
1363       operation == HA_EXTRA_PREPARE_FOR_DROP ||
1364       operation == HA_EXTRA_PREPARE_FOR_RENAME)
1365     return 0;
1366   if (operation == HA_EXTRA_MMAP && !opt_myisam_use_mmap)
1367     return 0;
1368   return myrg_extra(file,operation,0);
1369 }
1370 
reset(void)1371 int ha_myisammrg::reset(void)
1372 {
1373   /* This is normally called with detached children. */
1374   return myrg_reset(file);
1375 }
1376 
1377 /* To be used with WRITE_CACHE, EXTRA_CACHE and BULK_INSERT_BEGIN */
1378 
extra_opt(enum ha_extra_function operation,ulong cache_size)1379 int ha_myisammrg::extra_opt(enum ha_extra_function operation, ulong cache_size)
1380 {
1381   DBUG_ASSERT(this->file->children_attached);
1382   return myrg_extra(file, operation, (void*) &cache_size);
1383 }
1384 
external_lock(THD * thd,int lock_type)1385 int ha_myisammrg::external_lock(THD *thd, int lock_type)
1386 {
1387   /*
1388     This can be called with no children attached. E.g. FLUSH TABLES
1389     unlocks and re-locks tables under LOCK TABLES, but it does not open
1390     them first. So they are detached all the time. But locking of the
1391     children should work anyway because thd->open_tables is not changed
1392     during FLUSH TABLES.
1393 
1394     If this handler instance has been cloned, we still must call
1395     myrg_lock_database().
1396   */
1397   if (is_cloned)
1398     return myrg_lock_database(file, lock_type);
1399   return 0;
1400 }
1401 
lock_count(void) const1402 uint ha_myisammrg::lock_count(void) const
1403 {
1404   return 0;
1405 }
1406 
1407 
store_lock(THD * thd,THR_LOCK_DATA ** to,enum thr_lock_type lock_type)1408 THR_LOCK_DATA **ha_myisammrg::store_lock(THD *thd,
1409 					 THR_LOCK_DATA **to,
1410 					 enum thr_lock_type lock_type)
1411 {
1412   MYRG_TABLE *open_table;
1413 
1414   /*
1415     This method can be called while another thread is attaching the
1416     children. If the processor reorders instructions or write to memory,
1417     'children_attached' could be set before 'open_tables' has all the
1418     pointers to the children. Use of a mutex here and in
1419     myrg_attach_children() forces consistent data.
1420   */
1421   mysql_mutex_lock(&this->file->mutex);
1422 
1423   /*
1424     When MERGE table is open, but not yet attached, other threads
1425     could flush it, which means calling mysql_lock_abort_for_thread()
1426     on this threads TABLE. 'children_attached' is FALSE in this
1427     situation. Since the table is not locked, return no lock data.
1428   */
1429   if (!this->file->children_attached)
1430     goto end; /* purecov: tested */
1431 
1432   for (open_table=file->open_tables ;
1433        open_table != file->end_table ;
1434        open_table++)
1435     open_table->table->lock.priority|= THR_LOCK_MERGE_PRIV;
1436 
1437  end:
1438   mysql_mutex_unlock(&this->file->mutex);
1439   return to;
1440 }
1441 
1442 
1443 /* Find out database name and table name from a filename */
1444 
split_file_name(const char * file_name,LEX_STRING * db,LEX_STRING * name)1445 static void split_file_name(const char *file_name,
1446 			    LEX_STRING *db, LEX_STRING *name)
1447 {
1448   size_t dir_length, prefix_length;
1449   char buff[FN_REFLEN];
1450 
1451   db->length= 0;
1452   strmake_buf(buff, file_name);
1453   dir_length= dirname_length(buff);
1454   if (dir_length > 1)
1455   {
1456     /* Get database */
1457     buff[dir_length-1]= 0;			// Remove end '/'
1458     prefix_length= dirname_length(buff);
1459     db->str= (char*) file_name+ prefix_length;
1460     db->length= dir_length - prefix_length -1;
1461   }
1462   name->str= (char*) file_name+ dir_length;
1463   name->length= (uint) (fn_ext(name->str) - name->str);
1464 }
1465 
1466 
update_create_info(HA_CREATE_INFO * create_info)1467 void ha_myisammrg::update_create_info(HA_CREATE_INFO *create_info)
1468 {
1469   DBUG_ENTER("ha_myisammrg::update_create_info");
1470 
1471   if (!(create_info->used_fields & HA_CREATE_USED_UNION))
1472   {
1473     TABLE_LIST *child_table, *end= NULL;
1474     THD *thd=ha_thd();
1475 
1476     if (children_l != NULL)
1477     {
1478       for (child_table= children_l;; child_table= child_table->next_global)
1479       {
1480         TABLE_LIST *ptr;
1481 
1482         if (!(ptr= (TABLE_LIST *) thd->calloc(sizeof(TABLE_LIST))))
1483           DBUG_VOID_RETURN;
1484 
1485         if (!(ptr->table_name.str= thd->strmake(child_table->table_name.str,
1486                                                 child_table->table_name.length)))
1487           DBUG_VOID_RETURN;
1488         ptr->table_name.length= child_table->table_name.length;
1489         if (child_table->db.str && !(ptr->db.str= thd->strmake(child_table->db.str,
1490                                                                child_table->db.length)))
1491           DBUG_VOID_RETURN;
1492         ptr->db.length= child_table->db.length;
1493 
1494         if (create_info->merge_list)
1495           end->next_local= ptr;
1496         else
1497           create_info->merge_list= ptr;
1498         end= ptr;
1499 
1500         if (&child_table->next_global == children_last_l)
1501           break;
1502       }
1503     }
1504   }
1505   if (!(create_info->used_fields & HA_CREATE_USED_INSERT_METHOD))
1506   {
1507     create_info->merge_insert_method = file->merge_insert_method;
1508   }
1509   DBUG_VOID_RETURN;
1510 }
1511 
1512 
create_mrg(const char * name,HA_CREATE_INFO * create_info)1513 int ha_myisammrg::create_mrg(const char *name, HA_CREATE_INFO *create_info)
1514 {
1515   char buff[FN_REFLEN];
1516   const char **table_names, **pos;
1517   TABLE_LIST *tables= create_info->merge_list;
1518   THD *thd= ha_thd();
1519   size_t dirlgt= dirname_length(name);
1520   uint ntables= 0;
1521   DBUG_ENTER("ha_myisammrg::create_mrg");
1522 
1523   for (tables= create_info->merge_list; tables; tables= tables->next_local)
1524     ntables++;
1525 
1526   /* Allocate a table_names array in thread mem_root. */
1527   if (!(pos= table_names= (const char**) thd->alloc((ntables + 1) * sizeof(char*))))
1528     DBUG_RETURN(HA_ERR_OUT_OF_MEM); /* purecov: inspected */
1529 
1530   /* Create child path names. */
1531   for (tables= create_info->merge_list; tables; tables= tables->next_local)
1532   {
1533     const char *table_name= buff;
1534 
1535     /*
1536       Construct the path to the MyISAM table. Try to meet two conditions:
1537       1.) Allow to include MyISAM tables from different databases, and
1538       2.) allow for moving DATADIR around in the file system.
1539       The first means that we need paths in the .MRG file. The second
1540       means that we should not have absolute paths in the .MRG file.
1541       The best, we can do, is to use 'mysql_data_home', which is '.'
1542       in mysqld and may be an absolute path in an embedded server.
1543       This means that it might not be possible to move the DATADIR of
1544       an embedded server without changing the paths in the .MRG file.
1545 
1546       Do the same even for temporary tables. MERGE children are now
1547       opened through the table cache. They are opened by db.table_name,
1548       not by their path name.
1549     */
1550     size_t length= build_table_filename(buff, sizeof(buff),
1551                                       tables->db.str, tables->table_name.str, "", 0);
1552     /*
1553       If a MyISAM table is in the same directory as the MERGE table,
1554       we use the table name without a path. This means that the
1555       DATADIR can easily be moved even for an embedded server as long
1556       as the MyISAM tables are from the same database as the MERGE table.
1557     */
1558     if ((dirname_length(buff) == dirlgt) && ! memcmp(buff, name, dirlgt))
1559     {
1560       table_name+= dirlgt;
1561       length-= dirlgt;
1562     }
1563     if (!(table_name= thd->strmake(table_name, length)))
1564       DBUG_RETURN(HA_ERR_OUT_OF_MEM); /* purecov: inspected */
1565 
1566     *pos++= table_name;
1567   }
1568   *pos=0;
1569 
1570   /* Create a MERGE meta file from the table_names array. */
1571   int res= myrg_create(name, table_names, create_info->merge_insert_method, 0);
1572   DBUG_RETURN(res);
1573 }
1574 
1575 
create(const char * name,TABLE * form,HA_CREATE_INFO * create_info)1576 int ha_myisammrg::create(const char *name, TABLE *form,
1577 			 HA_CREATE_INFO *create_info)
1578 {
1579   char buff[FN_REFLEN];
1580   DBUG_ENTER("ha_myisammrg::create");
1581   fn_format(buff, name, "", MYRG_NAME_EXT, MY_UNPACK_FILENAME | MY_APPEND_EXT);
1582   int res= create_mrg(buff, create_info);
1583   DBUG_RETURN(res);
1584 }
1585 
1586 
append_create_info(String * packet)1587 void ha_myisammrg::append_create_info(String *packet)
1588 {
1589   const char *current_db;
1590   size_t db_length;
1591   THD *thd= current_thd;
1592   TABLE_LIST *open_table, *first;
1593 
1594   if (file->merge_insert_method != MERGE_INSERT_DISABLED)
1595   {
1596     packet->append(STRING_WITH_LEN(" INSERT_METHOD="));
1597     packet->append(get_type(&merge_insert_method,file->merge_insert_method-1));
1598   }
1599   /*
1600     There is no sence adding UNION clause in case there is no underlying
1601     tables specified.
1602   */
1603   if (file->open_tables == file->end_table)
1604     return;
1605   packet->append(STRING_WITH_LEN(" UNION=("));
1606 
1607   current_db= table->s->db.str;
1608   db_length=  table->s->db.length;
1609 
1610   for (first= open_table= children_l;;
1611        open_table= open_table->next_global)
1612   {
1613     LEX_CSTRING db= open_table->db;
1614 
1615     if (open_table != first)
1616       packet->append(',');
1617     /* Report database for mapped table if it isn't in current database */
1618     if (db.length &&
1619 	(db_length != db.length ||
1620 	 strncmp(current_db, db.str, db.length)))
1621     {
1622       append_identifier(thd, packet, db.str, db.length);
1623       packet->append('.');
1624     }
1625     append_identifier(thd, packet, &open_table->table_name);
1626     if (&open_table->next_global == children_last_l)
1627       break;
1628   }
1629   packet->append(')');
1630 }
1631 
1632 
1633 enum_alter_inplace_result
check_if_supported_inplace_alter(TABLE * altered_table,Alter_inplace_info * ha_alter_info)1634 ha_myisammrg::check_if_supported_inplace_alter(TABLE *altered_table,
1635                                                Alter_inplace_info *ha_alter_info)
1636 {
1637   /*
1638     We always support inplace ALTER in the new API, because old
1639     HA_NO_COPY_ON_ALTER table_flags() hack prevents non-inplace ALTER anyway.
1640   */
1641   return HA_ALTER_INPLACE_EXCLUSIVE_LOCK;
1642 }
1643 
1644 
inplace_alter_table(TABLE * altered_table,Alter_inplace_info * ha_alter_info)1645 bool ha_myisammrg::inplace_alter_table(TABLE *altered_table,
1646                                        Alter_inplace_info *ha_alter_info)
1647 {
1648   char tmp_path[FN_REFLEN];
1649   const char *name= table->s->normalized_path.str;
1650   DBUG_ENTER("ha_myisammrg::inplace_alter_table");
1651   fn_format(tmp_path, name, "", MYRG_NAME_TMPEXT, MY_UNPACK_FILENAME | MY_APPEND_EXT);
1652   int res= create_mrg(tmp_path, ha_alter_info->create_info);
1653   if (res)
1654     mysql_file_delete(rg_key_file_MRG, tmp_path, MYF(0));
1655   else
1656   {
1657     char path[FN_REFLEN];
1658     fn_format(path, name, "", MYRG_NAME_EXT, MY_UNPACK_FILENAME | MY_APPEND_EXT);
1659     if (mysql_file_rename(rg_key_file_MRG, tmp_path, path, MYF(0)))
1660     {
1661       res= my_errno;
1662       mysql_file_delete(rg_key_file_MRG, tmp_path, MYF(0));
1663     }
1664   }
1665   DBUG_RETURN(res);
1666 }
1667 
check(THD * thd,HA_CHECK_OPT * check_opt)1668 int ha_myisammrg::check(THD* thd, HA_CHECK_OPT* check_opt)
1669 {
1670   return this->file->children_attached ? HA_ADMIN_OK : HA_ADMIN_CORRUPT;
1671 }
1672 
1673 
records()1674 ha_rows ha_myisammrg::records()
1675 {
1676   return myrg_records(file);
1677 }
1678 
count_query_cache_dependant_tables(uint8 * tables_type)1679 uint ha_myisammrg::count_query_cache_dependant_tables(uint8 *tables_type)
1680 {
1681   MYRG_INFO *file = myrg_info();
1682   /*
1683     Here should be following statement
1684   (*tables_type)|= HA_CACHE_TBL_NONTRANSACT;
1685     but it has no effect because HA_CACHE_TBL_NONTRANSACT is 0
1686   */
1687   return (uint)(file->end_table - file->open_tables);
1688 }
1689 
1690 
register_query_cache_dependant_tables(THD * thd,Query_cache * cache,Query_cache_block_table ** block_table,uint * n)1691 my_bool ha_myisammrg::register_query_cache_dependant_tables(THD *thd
1692                                           __attribute__((unused)),
1693                                           Query_cache *cache,
1694                                           Query_cache_block_table **block_table,
1695                                           uint *n)
1696 {
1697   MYRG_INFO *file =myrg_info();
1698   DBUG_ENTER("ha_myisammrg::register_query_cache_dependant_tables");
1699 
1700   for (MYRG_TABLE *table =file->open_tables;
1701        table != file->end_table ;
1702        table++)
1703   {
1704     char key[MAX_DBKEY_LENGTH];
1705     uint32 db_length;
1706     uint key_length= cache->filename_2_table_key(key, table->table->filename,
1707                                                  &db_length);
1708     (++(*block_table))->n= ++(*n);
1709     /*
1710       There are not callback function for for MyISAM, and engine data
1711     */
1712     if (!cache->insert_table(thd, key_length, key, (*block_table),
1713                              db_length, 0,
1714                              table_cache_type(),
1715                              0, 0, TRUE))
1716       DBUG_RETURN(TRUE);
1717   }
1718   DBUG_RETURN(FALSE);
1719 }
1720 
1721 
set_lock_type(enum thr_lock_type lock)1722 void ha_myisammrg::set_lock_type(enum thr_lock_type lock)
1723 {
1724   handler::set_lock_type(lock);
1725   if (children_l != NULL)
1726   {
1727     for (TABLE_LIST *child_table= children_l;;
1728          child_table= child_table->next_global)
1729     {
1730       child_table->lock_type=
1731         child_table->table->reginfo.lock_type= lock;
1732 
1733       if (&child_table->next_global == children_last_l)
1734         break;
1735     }
1736   }
1737 }
1738 
1739 extern int myrg_panic(enum ha_panic_function flag);
myisammrg_panic(handlerton * hton,ha_panic_function flag)1740 int myisammrg_panic(handlerton *hton, ha_panic_function flag)
1741 {
1742   return myrg_panic(flag);
1743 }
1744 
myisammrg_init(void * p)1745 static int myisammrg_init(void *p)
1746 {
1747   handlerton *myisammrg_hton;
1748 
1749   myisammrg_hton= (handlerton *)p;
1750 
1751 #ifdef HAVE_PSI_INTERFACE
1752   init_myisammrg_psi_keys();
1753 #endif
1754 
1755   myisammrg_hton->db_type= DB_TYPE_MRG_MYISAM;
1756   myisammrg_hton->create= myisammrg_create_handler;
1757   myisammrg_hton->panic= myisammrg_panic;
1758   myisammrg_hton->flags= HTON_NO_PARTITION;
1759   myisammrg_hton->tablefile_extensions= ha_myisammrg_exts;
1760 
1761   return 0;
1762 }
1763 
1764 struct st_mysql_storage_engine myisammrg_storage_engine=
1765 { MYSQL_HANDLERTON_INTERFACE_VERSION };
1766 
maria_declare_plugin(myisammrg)1767 maria_declare_plugin(myisammrg)
1768 {
1769   MYSQL_STORAGE_ENGINE_PLUGIN,
1770   &myisammrg_storage_engine,
1771   "MRG_MyISAM",
1772   "MySQL AB",
1773   "Collection of identical MyISAM tables",
1774   PLUGIN_LICENSE_GPL,
1775   myisammrg_init, /* Plugin Init */
1776   NULL, /* Plugin Deinit */
1777   0x0100, /* 1.0 */
1778   NULL,                       /* status variables                */
1779   NULL,                       /* system variables                */
1780   "1.0",                      /* string version */
1781   MariaDB_PLUGIN_MATURITY_STABLE /* maturity */
1782 }
1783 maria_declare_plugin_end;
1784