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