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, ¶m,
842 (my_bool *) ¶m.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