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-1301 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
108static 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
120ha_myisammrg::ha_myisammrg(handlerton *hton, TABLE_SHARE *table_arg)
121 :handler(hton, table_arg), file(0), is_cloned(0)
122{
123 init_sql_alloc(&children_mem_root, "ha_myisammrg",
124 FN_REFLEN + ALLOC_ROOT_MIN_BLOCK_SIZE, 0, MYF(0));
125}
126
127
128/**
129 @brief Destructor
130*/
131
132ha_myisammrg::~ha_myisammrg(void)
133{
134 free_root(&children_mem_root, MYF(0));
135}
136
137
138static const char *ha_myisammrg_exts[] = {
139 MYRG_NAME_EXT,
140 NullS
141};
142extern int table2myisam(TABLE *table_arg, MI_KEYDEF **keydef_out,
143 MI_COLUMNDEF **recinfo_out, uint *records_out);
144extern 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);
151static void split_file_name(const char *file_name,
152 LEX_STRING *db, LEX_STRING *name);
153
154
155extern "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
175const 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
226CPP_UNNAMED_NS_START
227
228extern "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
326CPP_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*/
333static 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
358int 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
436int 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
564end:
565 DBUG_RETURN(0);
566}
567
568
569/**
570 A context of myrg_attach_children() callback.
571*/
572
573class Mrg_attach_children_callback_param
574{
575public:
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;
587public:
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 {}
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
623CPP_UNNAMED_NS_START
624
625extern "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
730CPP_UNNAMED_NS_END
731
732/**
733 Returns a cloned instance of the current handler.
734
735 @return A cloned handler instance.
736 */
737handler *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
803int ha_myisammrg::attach_children(void)
804{
805 MYRG_TABLE *u_table;
806 MI_COLUMNDEF *recinfo;
807 MI_KEYDEF *keyinfo;
808 uint recs;
809 uint keys= table->s->keys;
810 TABLE_LIST *parent_l= table->pos_in_table_list;
811 int error;
812 Mrg_attach_children_callback_param param(parent_l, this->children_l, child_def_list);
813 DBUG_ENTER("ha_myisammrg::attach_children");
814 DBUG_PRINT("myrg", ("table: '%s'.'%s' %p", table->s->db.str,
815 table->s->table_name.str, table));
816 DBUG_PRINT("myrg", ("test_if_locked: %u", this->test_if_locked));
817
818 /* Must call this with open table. */
819 DBUG_ASSERT(this->file);
820
821 /*
822 A MERGE table with no children (empty union) is always seen as
823 attached internally.
824 */
825 if (!this->file->tables)
826 {
827 DBUG_PRINT("myrg", ("empty merge table union"));
828 goto end;
829 }
830 DBUG_PRINT("myrg", ("child tables: %u", this->file->tables));
831
832 /* Must not call this with attached children. */
833 DBUG_ASSERT(!this->file->children_attached);
834
835 DEBUG_SYNC(current_thd, "before_myisammrg_attach");
836 /* Must call this with children list in place. */
837 DBUG_ASSERT(this->table->pos_in_table_list->next_global == this->children_l);
838
839 if (myrg_attach_children(this->file, this->test_if_locked |
840 current_thd->open_options,
841 myisammrg_attach_children_callback, &param,
842 (my_bool *) &param.need_compat_check))
843 {
844 error= my_errno;
845 goto err;
846 }
847 DBUG_PRINT("myrg", ("calling myrg_extrafunc"));
848 myrg_extrafunc(file, query_cache_invalidate_by_MyISAM_filename_ref);
849 if (!(test_if_locked == HA_OPEN_WAIT_IF_LOCKED ||
850 test_if_locked == HA_OPEN_ABORT_IF_LOCKED))
851 myrg_extra(file,HA_EXTRA_NO_WAIT_LOCK,0);
852 info(HA_STATUS_NO_LOCK | HA_STATUS_VARIABLE | HA_STATUS_CONST);
853 if (!(test_if_locked & HA_OPEN_WAIT_IF_LOCKED))
854 myrg_extra(file,HA_EXTRA_WAIT_LOCK,0);
855
856 /*
857 The compatibility check is required only if one or more children do
858 not match their table def version from the last check. This will
859 always happen at the first attach because the reference child def
860 version is initialized to 'undefined' at open.
861 */
862 DBUG_PRINT("myrg", ("need_compat_check: %d", param.need_compat_check));
863 if (param.need_compat_check)
864 {
865 TABLE_LIST *child_l;
866
867 if (table->s->reclength != stats.mean_rec_length && stats.mean_rec_length)
868 {
869 DBUG_PRINT("error",("reclength: %lu mean_rec_length: %lu",
870 table->s->reclength, stats.mean_rec_length));
871 if (test_if_locked & HA_OPEN_FOR_REPAIR)
872 {
873 /* purecov: begin inspected */
874 myrg_print_wrong_table(file->open_tables->table->filename);
875 /* purecov: end */
876 }
877 error= HA_ERR_WRONG_MRG_TABLE_DEF;
878 goto err;
879 }
880 /*
881 Both recinfo and keyinfo are allocated by my_multi_malloc(), thus
882 only recinfo must be freed.
883 */
884 if ((error= table2myisam(table, &keyinfo, &recinfo, &recs)))
885 {
886 /* purecov: begin inspected */
887 DBUG_PRINT("error", ("failed to convert TABLE object to MyISAM "
888 "key and column definition"));
889 goto err;
890 /* purecov: end */
891 }
892 for (u_table= file->open_tables; u_table < file->end_table; u_table++)
893 {
894 if (check_definition(keyinfo, recinfo, keys, recs,
895 u_table->table->s->keyinfo, u_table->table->s->rec,
896 u_table->table->s->base.keys,
897 u_table->table->s->base.fields, false, NULL))
898 {
899 DBUG_PRINT("error", ("table definition mismatch: '%s'",
900 u_table->table->filename));
901 error= HA_ERR_WRONG_MRG_TABLE_DEF;
902 if (!(this->test_if_locked & HA_OPEN_FOR_REPAIR))
903 {
904 my_free(recinfo);
905 goto err;
906 }
907 /* purecov: begin inspected */
908 myrg_print_wrong_table(u_table->table->filename);
909 /* purecov: end */
910 }
911 }
912 my_free(recinfo);
913 if (error == HA_ERR_WRONG_MRG_TABLE_DEF)
914 goto err; /* purecov: inspected */
915
916 List_iterator_fast<Mrg_child_def> def_it(child_def_list);
917 DBUG_ASSERT(this->children_l);
918 for (child_l= this->children_l; ; child_l= child_l->next_global)
919 {
920 Mrg_child_def *mrg_child_def= def_it++;
921 mrg_child_def->set_child_def_version(
922 child_l->table->s->get_table_ref_type(),
923 child_l->table->s->get_table_def_version());
924
925 if (&child_l->next_global == this->children_last_l)
926 break;
927 }
928 }
929#if !defined(BIG_TABLES) || SIZEOF_OFF_T == 4
930 /* Merge table has more than 2G rows */
931 if (table->s->crashed)
932 {
933 DBUG_PRINT("error", ("MERGE table marked crashed"));
934 error= HA_ERR_WRONG_MRG_TABLE_DEF;
935 goto err;
936 }
937#endif
938
939 end:
940 DBUG_RETURN(0);
941
942err:
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
963int 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
1075int 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
1091int ha_myisammrg::write_row(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
1108int 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
1114int ha_myisammrg::delete_row(const uchar * buf)
1115{
1116 DBUG_ASSERT(this->file->children_attached);
1117 return myrg_delete(file,buf);
1118}
1119
1120int 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
1129int 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
1138int 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
1147int 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
1154int 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
1161int 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
1168int 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
1175int 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
1189int ha_myisammrg::rnd_init(bool scan)
1190{
1191 DBUG_ASSERT(this->file->children_attached);
1192 return myrg_reset(file);
1193}
1194
1195
1196int 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
1204int 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
1211void 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
1219ha_rows ha_myisammrg::records_in_range(uint inx, key_range *min_key,
1220 key_range *max_key)
1221{
1222 DBUG_ASSERT(this->file->children_attached);
1223 return (ha_rows) myrg_records_in_range(file, (int) inx, min_key, max_key);
1224}
1225
1226
1227int ha_myisammrg::delete_all_rows()
1228{
1229 int err= 0;
1230 MYRG_TABLE *table;
1231 DBUG_ENTER("ha_myisammrg::delete_all_rows");
1232
1233 for (table= file->open_tables; table != file->end_table; table++)
1234 {
1235 if ((err= mi_delete_all_rows(table->table)))
1236 break;
1237 }
1238
1239 DBUG_RETURN(err);
1240}
1241
1242
1243int ha_myisammrg::info(uint flag)
1244{
1245 MYMERGE_INFO mrg_info;
1246 DBUG_ASSERT(this->file->children_attached);
1247 (void) myrg_status(file,&mrg_info,flag);
1248 /*
1249 The following fails if one has not compiled MySQL with -DBIG_TABLES
1250 and one has more than 2^32 rows in the merge tables.
1251 */
1252 stats.records = (ha_rows) mrg_info.records;
1253 stats.deleted = (ha_rows) mrg_info.deleted;
1254#if !defined(BIG_TABLES) || SIZEOF_OFF_T == 4
1255 if ((mrg_info.records >= (ulonglong) 1 << 32) ||
1256 (mrg_info.deleted >= (ulonglong) 1 << 32))
1257 table->s->crashed= 1;
1258#endif
1259 stats.data_file_length= mrg_info.data_file_length;
1260 if (mrg_info.errkey >= (int) table_share->keys)
1261 {
1262 /*
1263 If value of errkey is higher than the number of keys
1264 on the table set errkey to MAX_KEY. This will be
1265 treated as unknown key case and error message generator
1266 won't try to locate key causing segmentation fault.
1267 */
1268 mrg_info.errkey= MAX_KEY;
1269 }
1270 table->s->keys_in_use.set_prefix(table->s->keys);
1271 stats.mean_rec_length= mrg_info.reclength;
1272
1273 /*
1274 The handler::block_size is used all over the code in index scan cost
1275 calculations. It is used to get number of disk seeks required to
1276 retrieve a number of index tuples.
1277 If the merge table has N underlying tables, then (assuming underlying
1278 tables have equal size, the only "simple" approach we can use)
1279 retrieving X index records from a merge table will require N times more
1280 disk seeks compared to doing the same on a MyISAM table with equal
1281 number of records.
1282 In the edge case (file_tables > myisam_block_size) we'll get
1283 block_size==0, and index calculation code will act as if we need one
1284 disk seek to retrieve one index tuple.
1285
1286 TODO: In 5.2 index scan cost calculation will be factored out into a
1287 virtual function in class handler and we'll be able to remove this hack.
1288 */
1289 stats.block_size= 0;
1290 if (file->tables)
1291 stats.block_size= myisam_block_size / file->tables;
1292
1293 stats.update_time= 0;
1294#if SIZEOF_OFF_T > 4
1295 ref_length=6; // Should be big enough
1296#else
1297 ref_length=4; // Can't be > than my_off_t
1298#endif
1299 if (flag & HA_STATUS_CONST)
1300 {
1301 if (table->s->key_parts && mrg_info.rec_per_key)
1302 {
1303#ifdef HAVE_valgrind
1304 /*
1305 valgrind may be unhappy about it, because optimizer may access values
1306 between file->keys and table->key_parts, that will be uninitialized.
1307 It's safe though, because even if opimizer will decide to use a key
1308 with such a number, it'll be an error later anyway.
1309 */
1310 bzero((char*) table->key_info[0].rec_per_key,
1311 sizeof(table->key_info[0].rec_per_key[0]) * table->s->key_parts);
1312#endif
1313 memcpy((char*) table->key_info[0].rec_per_key,
1314 (char*) mrg_info.rec_per_key,
1315 sizeof(table->key_info[0].rec_per_key[0]) *
1316 MY_MIN(file->keys, table->s->key_parts));
1317 }
1318 }
1319 if (flag & HA_STATUS_ERRKEY)
1320 {
1321 errkey= mrg_info.errkey;
1322 my_store_ptr(dup_ref, ref_length, mrg_info.dupp_key_pos);
1323 }
1324 return 0;
1325}
1326
1327
1328int ha_myisammrg::extra(enum ha_extra_function operation)
1329{
1330 if (operation == HA_EXTRA_ADD_CHILDREN_LIST)
1331 {
1332 int rc= add_children_list();
1333 return(rc);
1334 }
1335 else if (operation == HA_EXTRA_ATTACH_CHILDREN)
1336 {
1337 int rc= attach_children();
1338 if (!rc)
1339 (void) extra(HA_EXTRA_NO_READCHECK); // Not needed in SQL
1340 return(rc);
1341 }
1342 else if (operation == HA_EXTRA_IS_ATTACHED_CHILDREN)
1343 {
1344 /* For the upper layer pretend empty MERGE union is never attached. */
1345 return(file && file->tables && file->children_attached);
1346 }
1347 else if (operation == HA_EXTRA_DETACH_CHILDREN)
1348 {
1349 /*
1350 Note that detach must not touch the children in any way.
1351 They may have been closed at ths point already.
1352 */
1353 int rc= detach_children();
1354 return(rc);
1355 }
1356
1357 /* As this is just a mapping, we don't have to force the underlying
1358 tables to be closed */
1359 if (operation == HA_EXTRA_FORCE_REOPEN ||
1360 operation == HA_EXTRA_PREPARE_FOR_DROP ||
1361 operation == HA_EXTRA_PREPARE_FOR_RENAME)
1362 return 0;
1363 if (operation == HA_EXTRA_MMAP && !opt_myisam_use_mmap)
1364 return 0;
1365 return myrg_extra(file,operation,0);
1366}
1367
1368int ha_myisammrg::reset(void)
1369{
1370 /* This is normally called with detached children. */
1371 return myrg_reset(file);
1372}
1373
1374/* To be used with WRITE_CACHE, EXTRA_CACHE and BULK_INSERT_BEGIN */
1375
1376int ha_myisammrg::extra_opt(enum ha_extra_function operation, ulong cache_size)
1377{
1378 DBUG_ASSERT(this->file->children_attached);
1379 return myrg_extra(file, operation, (void*) &cache_size);
1380}
1381
1382int ha_myisammrg::external_lock(THD *thd, int lock_type)
1383{
1384 /*
1385 This can be called with no children attached. E.g. FLUSH TABLES
1386 unlocks and re-locks tables under LOCK TABLES, but it does not open
1387 them first. So they are detached all the time. But locking of the
1388 children should work anyway because thd->open_tables is not changed
1389 during FLUSH TABLES.
1390
1391 If this handler instance has been cloned, we still must call
1392 myrg_lock_database().
1393 */
1394 if (is_cloned)
1395 return myrg_lock_database(file, lock_type);
1396 return 0;
1397}
1398
1399uint ha_myisammrg::lock_count(void) const
1400{
1401 return 0;
1402}
1403
1404
1405THR_LOCK_DATA **ha_myisammrg::store_lock(THD *thd,
1406 THR_LOCK_DATA **to,
1407 enum thr_lock_type lock_type)
1408{
1409 MYRG_TABLE *open_table;
1410
1411 /*
1412 This method can be called while another thread is attaching the
1413 children. If the processor reorders instructions or write to memory,
1414 'children_attached' could be set before 'open_tables' has all the
1415 pointers to the children. Use of a mutex here and in
1416 myrg_attach_children() forces consistent data.
1417 */
1418 mysql_mutex_lock(&this->file->mutex);
1419
1420 /*
1421 When MERGE table is open, but not yet attached, other threads
1422 could flush it, which means calling mysql_lock_abort_for_thread()
1423 on this threads TABLE. 'children_attached' is FALSE in this
1424 situation. Since the table is not locked, return no lock data.
1425 */
1426 if (!this->file->children_attached)
1427 goto end; /* purecov: tested */
1428
1429 for (open_table=file->open_tables ;
1430 open_table != file->end_table ;
1431 open_table++)
1432 open_table->table->lock.priority|= THR_LOCK_MERGE_PRIV;
1433
1434 end:
1435 mysql_mutex_unlock(&this->file->mutex);
1436 return to;
1437}
1438
1439
1440/* Find out database name and table name from a filename */
1441
1442static void split_file_name(const char *file_name,
1443 LEX_STRING *db, LEX_STRING *name)
1444{
1445 size_t dir_length, prefix_length;
1446 char buff[FN_REFLEN];
1447
1448 db->length= 0;
1449 strmake_buf(buff, file_name);
1450 dir_length= dirname_length(buff);
1451 if (dir_length > 1)
1452 {
1453 /* Get database */
1454 buff[dir_length-1]= 0; // Remove end '/'
1455 prefix_length= dirname_length(buff);
1456 db->str= (char*) file_name+ prefix_length;
1457 db->length= dir_length - prefix_length -1;
1458 }
1459 name->str= (char*) file_name+ dir_length;
1460 name->length= (uint) (fn_ext(name->str) - name->str);
1461}
1462
1463
1464void ha_myisammrg::update_create_info(HA_CREATE_INFO *create_info)
1465{
1466 DBUG_ENTER("ha_myisammrg::update_create_info");
1467
1468 if (!(create_info->used_fields & HA_CREATE_USED_UNION))
1469 {
1470 TABLE_LIST *child_table;
1471 THD *thd=current_thd;
1472
1473 create_info->merge_list.next= &create_info->merge_list.first;
1474 create_info->merge_list.elements=0;
1475
1476 if (children_l != NULL)
1477 {
1478 for (child_table= children_l;;
1479 child_table= child_table->next_global)
1480 {
1481 TABLE_LIST *ptr;
1482
1483 if (!(ptr= (TABLE_LIST *) thd->calloc(sizeof(TABLE_LIST))))
1484 goto err;
1485
1486 if (!(ptr->table_name.str= thd->strmake(child_table->table_name.str,
1487 child_table->table_name.length)))
1488 goto err;
1489 ptr->table_name.length= child_table->table_name.length;
1490 if (child_table->db.str && !(ptr->db.str= thd->strmake(child_table->db.str,
1491 child_table->db.length)))
1492 goto err;
1493 ptr->db.length= child_table->db.length;
1494
1495 create_info->merge_list.elements++;
1496 (*create_info->merge_list.next)= ptr;
1497 create_info->merge_list.next= &ptr->next_local;
1498
1499 if (&child_table->next_global == children_last_l)
1500 break;
1501 }
1502 }
1503 *create_info->merge_list.next=0;
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
1511err:
1512 create_info->merge_list.elements=0;
1513 create_info->merge_list.first=0;
1514 DBUG_VOID_RETURN;
1515}
1516
1517
1518int ha_myisammrg::create_mrg(const char *name, HA_CREATE_INFO *create_info)
1519{
1520 char buff[FN_REFLEN];
1521 const char **table_names, **pos;
1522 TABLE_LIST *tables= create_info->merge_list.first;
1523 THD *thd= current_thd;
1524 size_t dirlgt= dirname_length(name);
1525 DBUG_ENTER("ha_myisammrg::create_mrg");
1526
1527 /* Allocate a table_names array in thread mem_root. */
1528 if (!(table_names= (const char**)
1529 thd->alloc((create_info->merge_list.elements+1) * sizeof(char*))))
1530 DBUG_RETURN(HA_ERR_OUT_OF_MEM); /* purecov: inspected */
1531
1532 /* Create child path names. */
1533 for (pos= table_names; tables; tables= tables->next_local)
1534 {
1535 const char *table_name= buff;
1536
1537 /*
1538 Construct the path to the MyISAM table. Try to meet two conditions:
1539 1.) Allow to include MyISAM tables from different databases, and
1540 2.) allow for moving DATADIR around in the file system.
1541 The first means that we need paths in the .MRG file. The second
1542 means that we should not have absolute paths in the .MRG file.
1543 The best, we can do, is to use 'mysql_data_home', which is '.'
1544 in mysqld and may be an absolute path in an embedded server.
1545 This means that it might not be possible to move the DATADIR of
1546 an embedded server without changing the paths in the .MRG file.
1547
1548 Do the same even for temporary tables. MERGE children are now
1549 opened through the table cache. They are opened by db.table_name,
1550 not by their path name.
1551 */
1552 size_t length= build_table_filename(buff, sizeof(buff),
1553 tables->db.str, tables->table_name.str, "", 0);
1554 /*
1555 If a MyISAM table is in the same directory as the MERGE table,
1556 we use the table name without a path. This means that the
1557 DATADIR can easily be moved even for an embedded server as long
1558 as the MyISAM tables are from the same database as the MERGE table.
1559 */
1560 if ((dirname_length(buff) == dirlgt) && ! memcmp(buff, name, dirlgt))
1561 {
1562 table_name+= dirlgt;
1563 length-= dirlgt;
1564 }
1565 if (!(table_name= thd->strmake(table_name, length)))
1566 DBUG_RETURN(HA_ERR_OUT_OF_MEM); /* purecov: inspected */
1567
1568 *pos++= table_name;
1569 }
1570 *pos=0;
1571
1572 /* Create a MERGE meta file from the table_names array. */
1573 int res= myrg_create(name, table_names, create_info->merge_insert_method, 0);
1574 DBUG_RETURN(res);
1575}
1576
1577
1578int ha_myisammrg::create(const char *name, TABLE *form,
1579 HA_CREATE_INFO *create_info)
1580{
1581 char buff[FN_REFLEN];
1582 DBUG_ENTER("ha_myisammrg::create");
1583 fn_format(buff, name, "", MYRG_NAME_EXT, MY_UNPACK_FILENAME | MY_APPEND_EXT);
1584 int res= create_mrg(buff, create_info);
1585 DBUG_RETURN(res);
1586}
1587
1588
1589void ha_myisammrg::append_create_info(String *packet)
1590{
1591 const char *current_db;
1592 size_t db_length;
1593 THD *thd= current_thd;
1594 TABLE_LIST *open_table, *first;
1595
1596 if (file->merge_insert_method != MERGE_INSERT_DISABLED)
1597 {
1598 packet->append(STRING_WITH_LEN(" INSERT_METHOD="));
1599 packet->append(get_type(&merge_insert_method,file->merge_insert_method-1));
1600 }
1601 /*
1602 There is no sence adding UNION clause in case there is no underlying
1603 tables specified.
1604 */
1605 if (file->open_tables == file->end_table)
1606 return;
1607 packet->append(STRING_WITH_LEN(" UNION=("));
1608
1609 current_db= table->s->db.str;
1610 db_length= table->s->db.length;
1611
1612 for (first= open_table= children_l;;
1613 open_table= open_table->next_global)
1614 {
1615 LEX_CSTRING db= open_table->db;
1616
1617 if (open_table != first)
1618 packet->append(',');
1619 /* Report database for mapped table if it isn't in current database */
1620 if (db.length &&
1621 (db_length != db.length ||
1622 strncmp(current_db, db.str, db.length)))
1623 {
1624 append_identifier(thd, packet, db.str, db.length);
1625 packet->append('.');
1626 }
1627 append_identifier(thd, packet, &open_table->table_name);
1628 if (&open_table->next_global == children_last_l)
1629 break;
1630 }
1631 packet->append(')');
1632}
1633
1634
1635enum_alter_inplace_result
1636ha_myisammrg::check_if_supported_inplace_alter(TABLE *altered_table,
1637 Alter_inplace_info *ha_alter_info)
1638{
1639 /*
1640 We always support inplace ALTER in the new API, because old
1641 HA_NO_COPY_ON_ALTER table_flags() hack prevents non-inplace ALTER anyway.
1642 */
1643 return HA_ALTER_INPLACE_EXCLUSIVE_LOCK;
1644}
1645
1646
1647bool ha_myisammrg::inplace_alter_table(TABLE *altered_table,
1648 Alter_inplace_info *ha_alter_info)
1649{
1650 char tmp_path[FN_REFLEN];
1651 const char *name= table->s->normalized_path.str;
1652 DBUG_ENTER("ha_myisammrg::inplace_alter_table");
1653 fn_format(tmp_path, name, "", MYRG_NAME_TMPEXT, MY_UNPACK_FILENAME | MY_APPEND_EXT);
1654 int res= create_mrg(tmp_path, ha_alter_info->create_info);
1655 if (res)
1656 mysql_file_delete(rg_key_file_MRG, tmp_path, MYF(0));
1657 else
1658 {
1659 char path[FN_REFLEN];
1660 fn_format(path, name, "", MYRG_NAME_EXT, MY_UNPACK_FILENAME | MY_APPEND_EXT);
1661 if (mysql_file_rename(rg_key_file_MRG, tmp_path, path, MYF(0)))
1662 {
1663 res= my_errno;
1664 mysql_file_delete(rg_key_file_MRG, tmp_path, MYF(0));
1665 }
1666 }
1667 DBUG_RETURN(res);
1668}
1669
1670int ha_myisammrg::check(THD* thd, HA_CHECK_OPT* check_opt)
1671{
1672 return this->file->children_attached ? HA_ADMIN_OK : HA_ADMIN_CORRUPT;
1673}
1674
1675
1676ha_rows ha_myisammrg::records()
1677{
1678 return myrg_records(file);
1679}
1680
1681uint ha_myisammrg::count_query_cache_dependant_tables(uint8 *tables_type)
1682{
1683 MYRG_INFO *file = myrg_info();
1684 /*
1685 Here should be following statement
1686 (*tables_type)|= HA_CACHE_TBL_NONTRANSACT;
1687 but it has no effect because HA_CACHE_TBL_NONTRANSACT is 0
1688 */
1689 return (uint)(file->end_table - file->open_tables);
1690}
1691
1692
1693my_bool ha_myisammrg::register_query_cache_dependant_tables(THD *thd
1694 __attribute__((unused)),
1695 Query_cache *cache,
1696 Query_cache_block_table **block_table,
1697 uint *n)
1698{
1699 MYRG_INFO *file =myrg_info();
1700 DBUG_ENTER("ha_myisammrg::register_query_cache_dependant_tables");
1701
1702 for (MYRG_TABLE *table =file->open_tables;
1703 table != file->end_table ;
1704 table++)
1705 {
1706 char key[MAX_DBKEY_LENGTH];
1707 uint32 db_length;
1708 uint key_length= cache->filename_2_table_key(key, table->table->filename,
1709 &db_length);
1710 (++(*block_table))->n= ++(*n);
1711 /*
1712 There are not callback function for for MyISAM, and engine data
1713 */
1714 if (!cache->insert_table(thd, key_length, key, (*block_table),
1715 db_length, 0,
1716 table_cache_type(),
1717 0, 0, TRUE))
1718 DBUG_RETURN(TRUE);
1719 }
1720 DBUG_RETURN(FALSE);
1721}
1722
1723
1724void ha_myisammrg::set_lock_type(enum thr_lock_type lock)
1725{
1726 handler::set_lock_type(lock);
1727 if (children_l != NULL)
1728 {
1729 for (TABLE_LIST *child_table= children_l;;
1730 child_table= child_table->next_global)
1731 {
1732 child_table->lock_type=
1733 child_table->table->reginfo.lock_type= lock;
1734
1735 if (&child_table->next_global == children_last_l)
1736 break;
1737 }
1738 }
1739}
1740
1741extern int myrg_panic(enum ha_panic_function flag);
1742int myisammrg_panic(handlerton *hton, ha_panic_function flag)
1743{
1744 return myrg_panic(flag);
1745}
1746
1747static int myisammrg_init(void *p)
1748{
1749 handlerton *myisammrg_hton;
1750
1751 myisammrg_hton= (handlerton *)p;
1752
1753#ifdef HAVE_PSI_INTERFACE
1754 init_myisammrg_psi_keys();
1755#endif
1756
1757 myisammrg_hton->db_type= DB_TYPE_MRG_MYISAM;
1758 myisammrg_hton->create= myisammrg_create_handler;
1759 myisammrg_hton->panic= myisammrg_panic;
1760 myisammrg_hton->flags= HTON_NO_PARTITION;
1761 myisammrg_hton->tablefile_extensions= ha_myisammrg_exts;
1762
1763 return 0;
1764}
1765
1766struct st_mysql_storage_engine myisammrg_storage_engine=
1767{ MYSQL_HANDLERTON_INTERFACE_VERSION };
1768
1769maria_declare_plugin(myisammrg)
1770{
1771 MYSQL_STORAGE_ENGINE_PLUGIN,
1772 &myisammrg_storage_engine,
1773 "MRG_MyISAM",
1774 "MySQL AB",
1775 "Collection of identical MyISAM tables",
1776 PLUGIN_LICENSE_GPL,
1777 myisammrg_init, /* Plugin Init */
1778 NULL, /* Plugin Deinit */
1779 0x0100, /* 1.0 */
1780 NULL, /* status variables */
1781 NULL, /* system variables */
1782 "1.0", /* string version */
1783 MariaDB_PLUGIN_MATURITY_STABLE /* maturity */
1784}
1785maria_declare_plugin_end;
1786