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