1
/* Copyright (C) 2000-2006 MySQL AB
3
This program is free software; you can redistribute it and/or modify
4
it under the terms of the GNU General Public License as published by
5
the Free Software Foundation; version 2 of the License.
7
This program is distributed in the hope that it will be useful,
8
but WITHOUT ANY WARRANTY; without even the implied warranty of
9
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10
GNU General Public License for more details.
12
You should have received a copy of the GNU General Public License
13
along with this program; if not, write to the Free Software
14
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
20
A MyISAM MERGE table is kind of a union of zero or more MyISAM tables.
22
Besides the normal form file (.frm) a MERGE table has a meta file
23
(.MRG) with a list of tables. These are paths to the MyISAM table
24
files. The last two components of the path contain the database name
25
and the table name respectively.
27
When a MERGE table is open, there exists an TABLE object for the MERGE
28
table itself and a TABLE object for each of the MyISAM tables. For
29
abbreviated writing, I call the MERGE table object "parent" and the
30
MyISAM table objects "children".
32
A MERGE table is almost always opened through open_and_lock_tables()
33
and hence through open_tables(). When the parent appears in the list
34
of tables to open, the initial open of the handler does nothing but
35
read the meta file and collect a list of TABLE_LIST objects for the
36
children. This list is attached to the parent TABLE object as
37
TABLE::child_l. The end of the children list is saved in
40
Back in open_tables(), add_merge_table_list() is called. It updates
41
each list member with the lock type and a back pointer to the parent
42
TABLE_LIST object TABLE_LIST::parent_l. The list is then inserted in
43
the list of tables to open, right behind the parent. Consequently,
44
open_tables() opens the children, one after the other. The TABLE
45
references of the TABLE_LIST objects are implicitly set to the open
46
tables. The children are opened as independent MyISAM tables, right as
47
if they are used by the SQL statement.
49
TABLE_LIST::parent_l is required to find the parent 1. when the last
50
child has been opened and children are to be attached, and 2. when an
51
error happens during child open and the child list must be removed
52
from the queuery list. In these cases the current child does not have
53
TABLE::parent set or does not have a TABLE at all respectively.
55
When the last child is open, attach_merge_children() is called. It
56
removes the list of children from the open list. Then the children are
57
"attached" to the parent. All required references between parent and
60
The MERGE storage engine sets up an array with references to the
61
low-level MyISAM table objects (MI_INFO). It remembers the state of
62
the table in MYRG_INFO::children_attached.
64
Every child TABLE::parent references the parent TABLE object. That way
65
TABLE objects belonging to a MERGE table can be identified.
66
TABLE::parent is required because the parent and child TABLE objects
67
can live longer than the parent TABLE_LIST object. So the path
68
child->pos_in_table_list->parent_l->table can be broken.
70
If necessary, the compatibility of parent and children is checked.
71
This check is necessary when any of the objects are reopend. This is
72
detected by comparing the current table def version against the
73
remembered child def version. On parent open, the list members are
74
initialized to an "impossible"/"undefined" version value. So the check
75
is always executed on the first attach.
77
The version check is done in myisammrg_attach_children_callback(),
78
which is called for every child. ha_myisammrg::attach_children()
79
initializes 'need_compat_check' to FALSE and
80
myisammrg_attach_children_callback() sets it ot TRUE if a table
81
def version mismatches the remembered child def version.
83
Finally the parent TABLE::children_attached is set.
87
On parent open the storage engine structures are allocated and initialized.
88
They stay with the open table until its final close.
93
#ifdef USE_PRAGMA_IMPLEMENTATION
94
#pragma implementation // gcc: Class implementation
97
#define MYSQL_SERVER 1
98
#include "mysql_priv.h"
99
#include <mysql/plugin.h>
101
#include "../myisam/ha_myisam.h"
102
#include "ha_myisammrg.h"
103
#include "myrg_def.h"
106
static handler *myisammrg_create_handler(handlerton *hton,
110
return new (mem_root) ha_myisammrg(hton, table);
118
ha_myisammrg::ha_myisammrg(handlerton *hton, TABLE_SHARE *table_arg)
119
:handler(hton, table_arg), file(0), is_cloned(0)
127
ha_myisammrg::~ha_myisammrg(void)
131
static const char *ha_myisammrg_exts[] = {
135
extern int table2myisam(TABLE *table_arg, MI_KEYDEF **keydef_out,
136
MI_COLUMNDEF **recinfo_out, uint *records_out);
137
extern int check_definition(MI_KEYDEF *t1_keyinfo, MI_COLUMNDEF *t1_recinfo,
138
uint t1_keys, uint t1_recs,
139
MI_KEYDEF *t2_keyinfo, MI_COLUMNDEF *t2_recinfo,
140
uint t2_keys, uint t2_recs, bool strict,
142
static void split_file_name(const char *file_name,
143
LEX_STRING *db, LEX_STRING *name);
146
extern "C" void myrg_print_wrong_table(const char *table_name)
148
LEX_STRING db= {NULL, 0}, name;
150
split_file_name(table_name, &db, &name);
151
memcpy(buf, db.str, db.length);
153
memcpy(buf + db.length + 1, name.str, name.length);
154
buf[db.length + name.length + 1]= 0;
155
push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_ERROR,
156
ER_ADMIN_WRONG_MRG_TABLE, ER(ER_ADMIN_WRONG_MRG_TABLE),
161
const char **ha_myisammrg::bas_ext() const
163
return ha_myisammrg_exts;
167
const char *ha_myisammrg::index_type(uint key_number)
169
return ((table->key_info[key_number].flags & HA_FULLTEXT) ?
171
(table->key_info[key_number].flags & HA_SPATIAL) ?
173
(table->key_info[key_number].algorithm == HA_KEY_ALG_RTREE) ?
180
@brief Callback function for open of a MERGE parent table.
182
@detail This function adds a TABLE_LIST object for a MERGE child table
183
to a list of tables of the parent TABLE object. It is called for
186
The list of child TABLE_LIST objects is kept in the TABLE object of
187
the parent for the whole life time of the MERGE table. It is
188
inserted in the statement list behind the MERGE parent TABLE_LIST
189
object when the MERGE table is opened. It is removed from the
190
statement list after the last child is opened.
192
All memeory used for the child TABLE_LIST objects and the strings
193
referred by it are taken from the parent TABLE::mem_root. Thus they
194
are all freed implicitly at the final close of the table.
196
TABLE::child_l -> TABLE_LIST::next_global -> TABLE_LIST::next_global
199
# # +--------- TABLE_LIST::prev_global
201
# |<--- TABLE_LIST::prev_global |
203
TABLE::child_last_l -----------------------------------------+
205
@param[in] callback_param data pointer as given to myrg_parent_open()
206
@param[in] filename file name of MyISAM table
214
static int myisammrg_parent_open_callback(void *callback_param,
215
const char *filename)
217
ha_myisammrg *ha_myrg;
221
const char *table_name;
223
char dir_path[FN_REFLEN];
224
DBUG_ENTER("myisammrg_parent_open_callback");
226
/* Extract child table name and database name from filename. */
227
dirlen= dirname_length(filename);
228
if (dirlen >= FN_REFLEN)
230
/* purecov: begin inspected */
231
DBUG_PRINT("error", ("name too long: '%.64s'", filename));
232
my_errno= ENAMETOOLONG;
236
table_name= filename + dirlen;
237
dirlen--; /* Strip off trailing '/'. */
238
memcpy(dir_path, filename, dirlen);
239
dir_path[dirlen]= '\0';
240
db= base_name(dir_path);
241
dirlen-= db - dir_path; /* This is now the length of 'db'. */
242
DBUG_PRINT("myrg", ("open: '%s'.'%s'", db, table_name));
244
ha_myrg= (ha_myisammrg*) callback_param;
245
parent= ha_myrg->table_ptr();
247
/* Get a TABLE_LIST object. */
248
if (!(child_l= (TABLE_LIST*) alloc_root(&parent->mem_root,
249
sizeof(TABLE_LIST))))
251
/* purecov: begin inspected */
252
DBUG_PRINT("error", ("my_malloc error: %d", my_errno));
256
bzero((char*) child_l, sizeof(TABLE_LIST));
258
/* Set database (schema) name. */
259
child_l->db_length= dirlen;
260
child_l->db= strmake_root(&parent->mem_root, db, dirlen);
261
/* Set table name. */
262
child_l->table_name_length= strlen(table_name);
263
child_l->table_name= strmake_root(&parent->mem_root, table_name,
264
child_l->table_name_length);
265
/* Convert to lowercase if required. */
266
if (lower_case_table_names && child_l->table_name_length)
267
child_l->table_name_length= my_casedn_str(files_charset_info,
268
child_l->table_name);
270
child_l->alias= child_l->table_name;
272
/* Initialize table map to 'undefined'. */
273
child_l->init_child_def_version();
275
/* Link TABLE_LIST object into the parent list. */
276
if (!parent->child_last_l)
278
/* Initialize parent->child_last_l when handling first child. */
279
parent->child_last_l= &parent->child_l;
281
*parent->child_last_l= child_l;
282
child_l->prev_global= parent->child_last_l;
283
parent->child_last_l= &child_l->next_global;
290
@brief Callback function for attaching a MERGE child table.
292
@detail This function retrieves the MyISAM table handle from the
293
next child table. It is called for each child table.
295
@param[in] callback_param data pointer as given to
296
myrg_attach_children()
298
@return pointer to open MyISAM table structure
299
@retval !=NULL OK, returning pointer
300
@retval NULL, my_errno == 0 Ok, no more child tables
301
@retval NULL, my_errno != 0 error
304
static MI_INFO *myisammrg_attach_children_callback(void *callback_param)
306
ha_myisammrg *ha_myrg;
311
DBUG_ENTER("myisammrg_attach_children_callback");
314
ha_myrg= (ha_myisammrg*) callback_param;
315
parent= ha_myrg->table_ptr();
317
/* Get child list item. */
318
child_l= ha_myrg->next_child_attach;
321
DBUG_PRINT("myrg", ("No more children to attach"));
324
child= child_l->table;
325
DBUG_PRINT("myrg", ("child table: '%s'.'%s' 0x%lx", child->s->db.str,
326
child->s->table_name.str, (long) child));
328
Prepare for next child. Used as child_l in next call to this function.
329
We cannot rely on a NULL-terminated chain.
331
if (&child_l->next_global == parent->child_last_l)
333
DBUG_PRINT("myrg", ("attaching last child"));
334
ha_myrg->next_child_attach= NULL;
337
ha_myrg->next_child_attach= child_l->next_global;
339
/* Set parent reference. */
340
child->parent= parent;
343
Do a quick compatibility check. The table def version is set when
344
the table share is created. The child def version is copied
345
from the table def version after a sucessful compatibility check.
346
We need to repeat the compatibility check only if a child is opened
347
from a different share than last time it was used with this MERGE
350
DBUG_PRINT("myrg", ("table_def_version last: %lu current: %lu",
351
(ulong) child_l->get_child_def_version(),
352
(ulong) child->s->get_table_def_version()));
353
if (child_l->get_child_def_version() != child->s->get_table_def_version())
354
ha_myrg->need_compat_check= TRUE;
357
If parent is temporary, children must be temporary too and vice
358
versa. This check must be done for every child on every open because
359
the table def version can overlap between temporary and
360
non-temporary tables. We need to detect the case where a
361
non-temporary table has been replaced with a temporary table of the
362
same version. Or vice versa. A very unlikely case, but it could
365
if (child->s->tmp_table != parent->s->tmp_table)
367
DBUG_PRINT("error", ("temporary table mismatch parent: %d child: %d",
368
parent->s->tmp_table, child->s->tmp_table));
369
my_errno= HA_ERR_WRONG_MRG_TABLE_DEF;
373
/* Extract the MyISAM table structure pointer from the handler object. */
374
if ((child->file->ht->db_type != DB_TYPE_MYISAM) ||
375
!(myisam= ((ha_myisam*) child->file)->file_ptr()))
377
DBUG_PRINT("error", ("no MyISAM handle for child table: '%s'.'%s' 0x%lx",
378
child->s->db.str, child->s->table_name.str,
380
my_errno= HA_ERR_WRONG_MRG_TABLE_DEF;
382
DBUG_PRINT("myrg", ("MyISAM handle: 0x%lx my_errno: %d",
383
my_errno ? NULL : (long) myisam, my_errno));
386
DBUG_RETURN(my_errno ? NULL : myisam);
391
@brief Open a MERGE parent table, not its children.
393
@detail This function initializes the MERGE storage engine structures
394
and adds a child list of TABLE_LIST to the parent TABLE.
396
@param[in] name MERGE table path name
397
@param[in] mode read/write mode, unused
398
@param[in] test_if_locked open flags
402
@retval -1 Error, my_errno gives reason
405
int ha_myisammrg::open(const char *name, int mode __attribute__((unused)),
408
DBUG_ENTER("ha_myisammrg::open");
409
DBUG_PRINT("myrg", ("name: '%s' table: 0x%lx", name, (long) table));
410
DBUG_PRINT("myrg", ("test_if_locked: %u", test_if_locked));
412
/* Save for later use. */
413
this->test_if_locked= test_if_locked;
415
/* retrieve children table list. */
420
Open and attaches the MyISAM tables,that are under the MERGE table
421
parent, on the MyISAM storage engine interface directly within the
422
MERGE engine. The new MyISAM table instances, as well as the MERGE
423
clone itself, are not visible in the table cache. This is not a
424
problem because all locking is handled by the original MERGE table
425
from which this is cloned of.
427
if (!(file= myrg_open(table->s->normalized_path.str, table->db_stat,
428
HA_OPEN_IGNORE_IF_LOCKED)))
430
DBUG_PRINT("error", ("my_errno %d", my_errno));
431
DBUG_RETURN(my_errno ? my_errno : -1);
434
file->children_attached= TRUE;
436
info(HA_STATUS_NO_LOCK | HA_STATUS_VARIABLE | HA_STATUS_CONST);
438
else if (!(file= myrg_parent_open(name, myisammrg_parent_open_callback, this)))
440
DBUG_PRINT("error", ("my_errno %d", my_errno));
441
DBUG_RETURN(my_errno ? my_errno : -1);
443
DBUG_PRINT("myrg", ("MYRG_INFO: 0x%lx", (long) file));
448
Returns a cloned instance of the current handler.
450
@return A cloned handler instance.
452
handler *ha_myisammrg::clone(MEM_ROOT *mem_root)
454
MYRG_TABLE *u_table,*newu_table;
455
ha_myisammrg *new_handler=
456
(ha_myisammrg*) get_new_handler(table->s, mem_root, table->s->db_type());
460
/* Inform ha_myisammrg::open() that it is a cloned handler */
461
new_handler->is_cloned= TRUE;
463
Allocate handler->ref here because otherwise ha_open will allocate it
464
on this->table->mem_root and we will not be able to reclaim that memory
465
when the clone handler object is destroyed.
467
if (!(new_handler->ref= (uchar*) alloc_root(mem_root, ALIGN_SIZE(ref_length)*2)))
473
if (new_handler->ha_open(table, table->s->normalized_path.str, table->db_stat,
474
HA_OPEN_IGNORE_IF_LOCKED))
481
Iterate through the original child tables and
482
copy the state into the cloned child tables.
483
We need to do this because all the child tables
484
can be involved in delete.
486
newu_table= new_handler->file->open_tables;
487
for (u_table= file->open_tables; u_table < file->end_table; u_table++)
489
newu_table->table->state= u_table->table->state;
498
@brief Attach children to a MERGE table.
500
@detail Let the storage engine attach its children through a callback
501
function. Check table definitions for consistency.
503
@note Special thd->open_options may be in effect. We can make use of
504
them in attach. I.e. we use HA_OPEN_FOR_REPAIR to report the names
505
of mismatching child tables. We cannot transport these options in
506
ha_myisammrg::test_if_locked because they may change after the
507
parent is opened. The parent is kept open in the table cache over
508
multiple statements and can be used by other threads. Open options
509
can change over time.
513
@retval != 0 Error, my_errno gives reason
516
int ha_myisammrg::attach_children(void)
519
MI_COLUMNDEF *recinfo;
522
uint keys= table->s->keys;
524
DBUG_ENTER("ha_myisammrg::attach_children");
525
DBUG_PRINT("myrg", ("table: '%s'.'%s' 0x%lx", table->s->db.str,
526
table->s->table_name.str, (long) table));
527
DBUG_PRINT("myrg", ("test_if_locked: %u", this->test_if_locked));
528
DBUG_ASSERT(!this->file->children_attached);
531
Initialize variables that are used, modified, and/or set by
532
myisammrg_attach_children_callback().
533
'next_child_attach' traverses the chain of TABLE_LIST objects
534
that has been compiled during myrg_parent_open(). Every call
535
to myisammrg_attach_children_callback() moves the pointer to
537
'need_compat_check' is set by myisammrg_attach_children_callback()
538
if a child fails the table def version check.
539
'my_errno' is set by myisammrg_attach_children_callback() in
542
next_child_attach= table->child_l;
543
need_compat_check= FALSE;
546
if (myrg_attach_children(this->file, this->test_if_locked |
547
current_thd->open_options,
548
myisammrg_attach_children_callback, this,
549
(my_bool *) &need_compat_check))
551
DBUG_PRINT("error", ("my_errno %d", my_errno));
552
DBUG_RETURN(my_errno ? my_errno : -1);
554
DBUG_PRINT("myrg", ("calling myrg_extrafunc"));
555
myrg_extrafunc(file, query_cache_invalidate_by_MyISAM_filename_ref);
556
if (!(test_if_locked == HA_OPEN_WAIT_IF_LOCKED ||
557
test_if_locked == HA_OPEN_ABORT_IF_LOCKED))
558
myrg_extra(file,HA_EXTRA_NO_WAIT_LOCK,0);
559
info(HA_STATUS_NO_LOCK | HA_STATUS_VARIABLE | HA_STATUS_CONST);
560
if (!(test_if_locked & HA_OPEN_WAIT_IF_LOCKED))
561
myrg_extra(file,HA_EXTRA_WAIT_LOCK,0);
564
The compatibility check is required only if one or more children do
565
not match their table def version from the last check. This will
566
always happen at the first attach because the reference child def
567
version is initialized to 'undefined' at open.
569
DBUG_PRINT("myrg", ("need_compat_check: %d", need_compat_check));
570
if (need_compat_check)
574
if (table->s->reclength != stats.mean_rec_length && stats.mean_rec_length)
576
DBUG_PRINT("error",("reclength: %lu mean_rec_length: %lu",
577
table->s->reclength, stats.mean_rec_length));
578
if (test_if_locked & HA_OPEN_FOR_REPAIR)
579
myrg_print_wrong_table(file->open_tables->table->filename);
580
error= HA_ERR_WRONG_MRG_TABLE_DEF;
584
Both recinfo and keyinfo are allocated by my_multi_malloc(), thus
585
only recinfo must be freed.
587
if ((error= table2myisam(table, &keyinfo, &recinfo, &recs)))
589
/* purecov: begin inspected */
590
DBUG_PRINT("error", ("failed to convert TABLE object to MyISAM "
591
"key and column definition"));
595
for (u_table= file->open_tables; u_table < file->end_table; u_table++)
597
if (check_definition(keyinfo, recinfo, keys, recs,
598
u_table->table->s->keyinfo, u_table->table->s->rec,
599
u_table->table->s->base.keys,
600
u_table->table->s->base.fields, false, NULL))
602
DBUG_PRINT("error", ("table definition mismatch: '%s'",
603
u_table->table->filename));
604
error= HA_ERR_WRONG_MRG_TABLE_DEF;
605
if (!(this->test_if_locked & HA_OPEN_FOR_REPAIR))
607
my_free((uchar*) recinfo, MYF(0));
610
myrg_print_wrong_table(u_table->table->filename);
613
my_free((uchar*) recinfo, MYF(0));
614
if (error == HA_ERR_WRONG_MRG_TABLE_DEF)
617
/* All checks passed so far. Now update child def version. */
618
for (child_l= table->child_l; ; child_l= child_l->next_global)
620
child_l->set_child_def_version(
621
child_l->table->s->get_table_def_version());
623
if (&child_l->next_global == table->child_last_l)
627
#if !defined(BIG_TABLES) || SIZEOF_OFF_T == 4
628
/* Merge table has more than 2G rows */
629
if (table->s->crashed)
631
DBUG_PRINT("error", ("MERGE table marked crashed"));
632
error= HA_ERR_WRONG_MRG_TABLE_DEF;
639
myrg_detach_children(file);
640
DBUG_RETURN(my_errno= error);
645
@brief Detach all children from a MERGE table.
647
@note Detach must not touch the children in any way.
648
They may have been closed at ths point already.
649
All references to the children should be removed.
653
@retval != 0 Error, my_errno gives reason
656
int ha_myisammrg::detach_children(void)
658
DBUG_ENTER("ha_myisammrg::detach_children");
659
DBUG_ASSERT(this->file && this->file->children_attached);
661
if (myrg_detach_children(this->file))
663
/* purecov: begin inspected */
664
DBUG_PRINT("error", ("my_errno %d", my_errno));
665
DBUG_RETURN(my_errno ? my_errno : -1);
673
@brief Close a MERGE parent table, not its children.
675
@note The children are expected to be closed separately by the caller.
679
@retval != 0 Error, my_errno gives reason
682
int ha_myisammrg::close(void)
685
DBUG_ENTER("ha_myisammrg::close");
687
Children must not be attached here. Unless the MERGE table has no
688
children or the handler instance has been cloned. In these cases
689
children_attached is always true.
691
DBUG_ASSERT(!this->file->children_attached || !this->file->tables || this->is_cloned);
692
rc= myrg_close(file);
697
int ha_myisammrg::write_row(uchar * buf)
699
DBUG_ENTER("ha_myisammrg::write_row");
700
DBUG_ASSERT(this->file->children_attached);
701
ha_statistic_increment(&SSV::ha_write_count);
703
if (file->merge_insert_method == MERGE_INSERT_DISABLED || !file->tables)
704
DBUG_RETURN(HA_ERR_TABLE_READONLY);
706
if (table->timestamp_field_type & TIMESTAMP_AUTO_SET_ON_INSERT)
707
table->timestamp_field->set_time();
708
if (table->next_number_field && buf == table->record[0])
711
if ((error= update_auto_increment()))
712
DBUG_RETURN(error); /* purecov: inspected */
714
DBUG_RETURN(myrg_write(file,buf));
717
int ha_myisammrg::update_row(const uchar * old_data, uchar * new_data)
719
DBUG_ASSERT(this->file->children_attached);
720
ha_statistic_increment(&SSV::ha_update_count);
721
if (table->timestamp_field_type & TIMESTAMP_AUTO_SET_ON_UPDATE)
722
table->timestamp_field->set_time();
723
return myrg_update(file,old_data,new_data);
726
int ha_myisammrg::delete_row(const uchar * buf)
728
DBUG_ASSERT(this->file->children_attached);
729
ha_statistic_increment(&SSV::ha_delete_count);
730
return myrg_delete(file,buf);
733
int ha_myisammrg::index_read_map(uchar * buf, const uchar * key,
734
key_part_map keypart_map,
735
enum ha_rkey_function find_flag)
737
DBUG_ASSERT(this->file->children_attached);
738
ha_statistic_increment(&SSV::ha_read_key_count);
739
int error=myrg_rkey(file,buf,active_index, key, keypart_map, find_flag);
740
table->status=error ? STATUS_NOT_FOUND: 0;
744
int ha_myisammrg::index_read_idx_map(uchar * buf, uint index, const uchar * key,
745
key_part_map keypart_map,
746
enum ha_rkey_function find_flag)
748
DBUG_ASSERT(this->file->children_attached);
749
ha_statistic_increment(&SSV::ha_read_key_count);
750
int error=myrg_rkey(file,buf,index, key, keypart_map, find_flag);
751
table->status=error ? STATUS_NOT_FOUND: 0;
755
int ha_myisammrg::index_read_last_map(uchar *buf, const uchar *key,
756
key_part_map keypart_map)
758
DBUG_ASSERT(this->file->children_attached);
759
ha_statistic_increment(&SSV::ha_read_key_count);
760
int error=myrg_rkey(file,buf,active_index, key, keypart_map,
761
HA_READ_PREFIX_LAST);
762
table->status=error ? STATUS_NOT_FOUND: 0;
766
int ha_myisammrg::index_next(uchar * buf)
768
DBUG_ASSERT(this->file->children_attached);
769
ha_statistic_increment(&SSV::ha_read_next_count);
770
int error=myrg_rnext(file,buf,active_index);
771
table->status=error ? STATUS_NOT_FOUND: 0;
775
int ha_myisammrg::index_prev(uchar * buf)
777
DBUG_ASSERT(this->file->children_attached);
778
ha_statistic_increment(&SSV::ha_read_prev_count);
779
int error=myrg_rprev(file,buf, active_index);
780
table->status=error ? STATUS_NOT_FOUND: 0;
784
int ha_myisammrg::index_first(uchar * buf)
786
DBUG_ASSERT(this->file->children_attached);
787
ha_statistic_increment(&SSV::ha_read_first_count);
788
int error=myrg_rfirst(file, buf, active_index);
789
table->status=error ? STATUS_NOT_FOUND: 0;
793
int ha_myisammrg::index_last(uchar * buf)
795
DBUG_ASSERT(this->file->children_attached);
796
ha_statistic_increment(&SSV::ha_read_last_count);
797
int error=myrg_rlast(file, buf, active_index);
798
table->status=error ? STATUS_NOT_FOUND: 0;
802
int ha_myisammrg::index_next_same(uchar * buf,
803
const uchar *key __attribute__((unused)),
804
uint length __attribute__((unused)))
807
DBUG_ASSERT(this->file->children_attached);
808
ha_statistic_increment(&SSV::ha_read_next_count);
811
error= myrg_rnext_same(file,buf);
812
} while (error == HA_ERR_RECORD_DELETED);
813
table->status=error ? STATUS_NOT_FOUND: 0;
818
int ha_myisammrg::rnd_init(bool scan)
820
DBUG_ASSERT(this->file->children_attached);
821
return myrg_reset(file);
825
int ha_myisammrg::rnd_next(uchar *buf)
827
DBUG_ASSERT(this->file->children_attached);
828
ha_statistic_increment(&SSV::ha_read_rnd_next_count);
829
int error=myrg_rrnd(file, buf, HA_OFFSET_ERROR);
830
table->status=error ? STATUS_NOT_FOUND: 0;
835
int ha_myisammrg::rnd_pos(uchar * buf, uchar *pos)
837
DBUG_ASSERT(this->file->children_attached);
838
ha_statistic_increment(&SSV::ha_read_rnd_count);
839
int error=myrg_rrnd(file, buf, my_get_ptr(pos,ref_length));
840
table->status=error ? STATUS_NOT_FOUND: 0;
844
void ha_myisammrg::position(const uchar *record)
846
DBUG_ASSERT(this->file->children_attached);
847
ulonglong row_position= myrg_position(file);
848
my_store_ptr(ref, ref_length, (my_off_t) row_position);
852
ha_rows ha_myisammrg::records_in_range(uint inx, key_range *min_key,
855
DBUG_ASSERT(this->file->children_attached);
856
return (ha_rows) myrg_records_in_range(file, (int) inx, min_key, max_key);
860
int ha_myisammrg::info(uint flag)
862
MYMERGE_INFO mrg_info;
863
DBUG_ASSERT(this->file->children_attached);
864
(void) myrg_status(file,&mrg_info,flag);
866
The following fails if one has not compiled MySQL with -DBIG_TABLES
867
and one has more than 2^32 rows in the merge tables.
869
stats.records = (ha_rows) mrg_info.records;
870
stats.deleted = (ha_rows) mrg_info.deleted;
871
#if !defined(BIG_TABLES) || SIZEOF_OFF_T == 4
872
if ((mrg_info.records >= (ulonglong) 1 << 32) ||
873
(mrg_info.deleted >= (ulonglong) 1 << 32))
874
table->s->crashed= 1;
876
stats.data_file_length= mrg_info.data_file_length;
877
if (mrg_info.errkey >= (int) table_share->keys)
880
If value of errkey is higher than the number of keys
881
on the table set errkey to MAX_KEY. This will be
882
treated as unknown key case and error message generator
883
won't try to locate key causing segmentation fault.
885
mrg_info.errkey= MAX_KEY;
887
table->s->keys_in_use.set_prefix(table->s->keys);
888
stats.mean_rec_length= mrg_info.reclength;
891
The handler::block_size is used all over the code in index scan cost
892
calculations. It is used to get number of disk seeks required to
893
retrieve a number of index tuples.
894
If the merge table has N underlying tables, then (assuming underlying
895
tables have equal size, the only "simple" approach we can use)
896
retrieving X index records from a merge table will require N times more
897
disk seeks compared to doing the same on a MyISAM table with equal
899
In the edge case (file_tables > myisam_block_size) we'll get
900
block_size==0, and index calculation code will act as if we need one
901
disk seek to retrieve one index tuple.
903
TODO: In 5.2 index scan cost calculation will be factored out into a
904
virtual function in class handler and we'll be able to remove this hack.
908
stats.block_size= myisam_block_size / file->tables;
910
stats.update_time= 0;
912
ref_length=6; // Should be big enough
914
ref_length=4; // Can't be > than my_off_t
916
if (flag & HA_STATUS_CONST)
918
if (table->s->key_parts && mrg_info.rec_per_key)
922
valgrind may be unhappy about it, because optimizer may access values
923
between file->keys and table->key_parts, that will be uninitialized.
924
It's safe though, because even if opimizer will decide to use a key
925
with such a number, it'll be an error later anyway.
927
bzero((char*) table->key_info[0].rec_per_key,
928
sizeof(table->key_info[0].rec_per_key[0]) * table->s->key_parts);
930
memcpy((char*) table->key_info[0].rec_per_key,
931
(char*) mrg_info.rec_per_key,
932
sizeof(table->key_info[0].rec_per_key[0]) *
933
min(file->keys, table->s->key_parts));
936
if (flag & HA_STATUS_ERRKEY)
938
errkey= mrg_info.errkey;
939
my_store_ptr(dup_ref, ref_length, mrg_info.dupp_key_pos);
945
int ha_myisammrg::extra(enum ha_extra_function operation)
947
if (operation == HA_EXTRA_ATTACH_CHILDREN)
949
int rc= attach_children();
951
(void) extra(HA_EXTRA_NO_READCHECK); // Not needed in SQL
954
else if (operation == HA_EXTRA_DETACH_CHILDREN)
957
Note that detach must not touch the children in any way.
958
They may have been closed at ths point already.
960
int rc= detach_children();
964
/* As this is just a mapping, we don't have to force the underlying
965
tables to be closed */
966
if (operation == HA_EXTRA_FORCE_REOPEN ||
967
operation == HA_EXTRA_PREPARE_FOR_DROP)
969
return myrg_extra(file,operation,0);
972
int ha_myisammrg::reset(void)
974
return myrg_reset(file);
977
/* To be used with WRITE_CACHE, EXTRA_CACHE and BULK_INSERT_BEGIN */
979
int ha_myisammrg::extra_opt(enum ha_extra_function operation, ulong cache_size)
981
DBUG_ASSERT(this->file->children_attached);
982
if ((specialflag & SPECIAL_SAFE_MODE) && operation == HA_EXTRA_WRITE_CACHE)
984
return myrg_extra(file, operation, (void*) &cache_size);
987
int ha_myisammrg::external_lock(THD *thd, int lock_type)
989
DBUG_ASSERT(this->file->children_attached);
990
return myrg_lock_database(file,lock_type);
993
uint ha_myisammrg::lock_count(void) const
996
Return the real lock count even if the children are not attached.
997
This method is used for allocating memory. If we would return 0
998
to another thread (e.g. doing FLUSH TABLE), and attach the children
999
before the other thread calls store_lock(), then we would return
1000
more locks in store_lock() than we claimed by lock_count(). The
1001
other tread would overrun its memory.
1003
return file->tables;
1007
THR_LOCK_DATA **ha_myisammrg::store_lock(THD *thd,
1009
enum thr_lock_type lock_type)
1011
MYRG_TABLE *open_table;
1014
This method can be called while another thread is attaching the
1015
children. If the processor reorders instructions or write to memory,
1016
'children_attached' could be set before 'open_tables' has all the
1017
pointers to the children. Use of a mutex here and in
1018
myrg_attach_children() forces consistent data.
1020
pthread_mutex_lock(&this->file->mutex);
1023
When MERGE table is open, but not yet attached, other threads
1024
could flush it, which means call mysql_lock_abort_for_thread()
1025
on this threads TABLE. 'children_attached' is FALSE in this
1026
situaton. Since the table is not locked, return no lock data.
1028
if (!this->file->children_attached)
1029
goto end; /* purecov: tested */
1031
for (open_table=file->open_tables ;
1032
open_table != file->end_table ;
1035
*(to++)= &open_table->table->lock;
1036
if (lock_type != TL_IGNORE && open_table->table->lock.type == TL_UNLOCK)
1037
open_table->table->lock.type=lock_type;
1041
pthread_mutex_unlock(&this->file->mutex);
1046
/* Find out database name and table name from a filename */
1048
static void split_file_name(const char *file_name,
1049
LEX_STRING *db, LEX_STRING *name)
1051
size_t dir_length, prefix_length;
1052
char buff[FN_REFLEN];
1055
strmake(buff, file_name, sizeof(buff)-1);
1056
dir_length= dirname_length(buff);
1060
buff[dir_length-1]= 0; // Remove end '/'
1061
prefix_length= dirname_length(buff);
1062
db->str= (char*) file_name+ prefix_length;
1063
db->length= dir_length - prefix_length -1;
1065
name->str= (char*) file_name+ dir_length;
1066
name->length= (uint) (fn_ext(name->str) - name->str);
1070
void ha_myisammrg::update_create_info(HA_CREATE_INFO *create_info)
1072
DBUG_ENTER("ha_myisammrg::update_create_info");
1074
if (!(create_info->used_fields & HA_CREATE_USED_UNION))
1076
MYRG_TABLE *open_table;
1077
THD *thd=current_thd;
1079
create_info->merge_list.next= &create_info->merge_list.first;
1080
create_info->merge_list.elements=0;
1082
for (open_table=file->open_tables ;
1083
open_table != file->end_table ;
1087
LEX_STRING db, name;
1090
if (!(ptr = (TABLE_LIST *) thd->calloc(sizeof(TABLE_LIST))))
1092
split_file_name(open_table->table->filename, &db, &name);
1093
if (!(ptr->table_name= thd->strmake(name.str, name.length)))
1095
if (db.length && !(ptr->db= thd->strmake(db.str, db.length)))
1098
create_info->merge_list.elements++;
1099
(*create_info->merge_list.next) = (uchar*) ptr;
1100
create_info->merge_list.next= (uchar**) &ptr->next_local;
1102
*create_info->merge_list.next=0;
1104
if (!(create_info->used_fields & HA_CREATE_USED_INSERT_METHOD))
1106
create_info->merge_insert_method = file->merge_insert_method;
1111
create_info->merge_list.elements=0;
1112
create_info->merge_list.first=0;
1117
int ha_myisammrg::create(const char *name, register TABLE *form,
1118
HA_CREATE_INFO *create_info)
1120
char buff[FN_REFLEN];
1121
const char **table_names, **pos;
1122
TABLE_LIST *tables= (TABLE_LIST*) create_info->merge_list.first;
1123
THD *thd= current_thd;
1124
size_t dirlgt= dirname_length(name);
1125
DBUG_ENTER("ha_myisammrg::create");
1127
/* Allocate a table_names array in thread mem_root. */
1128
if (!(table_names= (const char**)
1129
thd->alloc((create_info->merge_list.elements+1) * sizeof(char*))))
1130
DBUG_RETURN(HA_ERR_OUT_OF_MEM);
1132
/* Create child path names. */
1133
for (pos= table_names; tables; tables= tables->next_local)
1135
const char *table_name;
1138
Construct the path to the MyISAM table. Try to meet two conditions:
1139
1.) Allow to include MyISAM tables from different databases, and
1140
2.) allow for moving DATADIR around in the file system.
1141
The first means that we need paths in the .MRG file. The second
1142
means that we should not have absolute paths in the .MRG file.
1143
The best, we can do, is to use 'mysql_data_home', which is '.'
1144
in mysqld and may be an absolute path in an embedded server.
1145
This means that it might not be possible to move the DATADIR of
1146
an embedded server without changing the paths in the .MRG file.
1148
Do the same even for temporary tables. MERGE children are now
1149
opened through the table cache. They are opened by db.table_name,
1150
not by their path name.
1152
uint length= build_table_filename(buff, sizeof(buff),
1153
tables->db, tables->table_name, "", 0);
1155
If a MyISAM table is in the same directory as the MERGE table,
1156
we use the table name without a path. This means that the
1157
DATADIR can easily be moved even for an embedded server as long
1158
as the MyISAM tables are from the same database as the MERGE table.
1160
if ((dirname_length(buff) == dirlgt) && ! memcmp(buff, name, dirlgt))
1161
table_name= tables->table_name;
1163
if (! (table_name= thd->strmake(buff, length)))
1164
DBUG_RETURN(HA_ERR_OUT_OF_MEM); /* purecov: inspected */
1170
/* Create a MERGE meta file from the table_names array. */
1171
DBUG_RETURN(myrg_create(fn_format(buff,name,"","",
1172
MY_RESOLVE_SYMLINKS|
1173
MY_UNPACK_FILENAME|MY_APPEND_EXT),
1175
create_info->merge_insert_method,
1180
void ha_myisammrg::append_create_info(String *packet)
1182
const char *current_db;
1184
THD *thd= current_thd;
1185
MYRG_TABLE *open_table, *first;
1187
if (file->merge_insert_method != MERGE_INSERT_DISABLED)
1189
packet->append(STRING_WITH_LEN(" INSERT_METHOD="));
1190
packet->append(get_type(&merge_insert_method,file->merge_insert_method-1));
1193
There is no sence adding UNION clause in case there is no underlying
1196
if (file->open_tables == file->end_table)
1198
packet->append(STRING_WITH_LEN(" UNION=("));
1200
current_db= table->s->db.str;
1201
db_length= table->s->db.length;
1203
for (first=open_table=file->open_tables ;
1204
open_table != file->end_table ;
1207
LEX_STRING db, name;
1210
split_file_name(open_table->table->filename, &db, &name);
1211
if (open_table != first)
1212
packet->append(',');
1213
/* Report database for mapped table if it isn't in current database */
1215
(db_length != db.length ||
1216
strncmp(current_db, db.str, db.length)))
1218
append_identifier(thd, packet, db.str, db.length);
1219
packet->append('.');
1221
append_identifier(thd, packet, name.str, name.length);
1223
packet->append(')');
1227
bool ha_myisammrg::check_if_incompatible_data(HA_CREATE_INFO *info,
1231
For myisammrg, we should always re-generate the mapping file as this
1234
return COMPATIBLE_DATA_NO;
1238
int ha_myisammrg::check(THD* thd, HA_CHECK_OPT* check_opt)
1244
ha_rows ha_myisammrg::records()
1246
return myrg_records(file);
1250
extern int myrg_panic(enum ha_panic_function flag);
1251
int myisammrg_panic(handlerton *hton, ha_panic_function flag)
1253
return myrg_panic(flag);
1256
static int myisammrg_init(void *p)
1258
handlerton *myisammrg_hton;
1260
myisammrg_hton= (handlerton *)p;
1262
myisammrg_hton->db_type= DB_TYPE_MRG_MYISAM;
1263
myisammrg_hton->create= myisammrg_create_handler;
1264
myisammrg_hton->panic= myisammrg_panic;
1265
myisammrg_hton->flags= HTON_NO_PARTITION;
1270
struct st_mysql_storage_engine myisammrg_storage_engine=
1271
{ MYSQL_HANDLERTON_INTERFACE_VERSION };
1273
mysql_declare_plugin(myisammrg)
1275
MYSQL_STORAGE_ENGINE_PLUGIN,
1276
&myisammrg_storage_engine,
1279
"Collection of identical MyISAM tables",
1281
myisammrg_init, /* Plugin Init */
1282
NULL, /* Plugin Deinit */
1284
NULL, /* status variables */
1285
NULL, /* system variables */
1286
NULL /* config options */
1288
mysql_declare_plugin_end;