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