1 | /* Copyright (c) 2001, 2015, Oracle and/or its affiliates. |
2 | Copyright (c) 2011, 2016, MariaDB Corporation |
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 Street, Fifth Floor, Boston, MA 02110-1301, USA */ |
16 | |
17 | |
18 | /* HANDLER ... commands - direct access to ISAM */ |
19 | |
20 | /* TODO: |
21 | HANDLER blabla OPEN [ AS foobar ] [ (column-list) ] |
22 | |
23 | the most natural (easiest, fastest) way to do it is to |
24 | compute List<Item> field_list not in mysql_ha_read |
25 | but in mysql_ha_open, and then store it in TABLE structure. |
26 | |
27 | The problem here is that mysql_parse calls free_item to free all the |
28 | items allocated at the end of every query. The workaround would to |
29 | keep two item lists per THD - normal free_list and handler_items. |
30 | The second is to be freeed only on thread end. mysql_ha_open should |
31 | then do { handler_items=concat(handler_items, free_list); free_list=0; } |
32 | |
33 | But !!! do_command calls free_root at the end of every query and frees up |
34 | all the memory allocated on THD::mem_root. It's harder to work around... |
35 | */ |
36 | |
37 | /* |
38 | The information about open HANDLER objects is stored in a HASH. |
39 | It holds objects of type TABLE_LIST, which are indexed by table |
40 | name/alias, and allows us to quickly find a HANDLER table for any |
41 | operation at hand - be it HANDLER READ or HANDLER CLOSE. |
42 | |
43 | It also allows us to maintain an "open" HANDLER even in cases |
44 | when there is no physically open cursor. E.g. a FLUSH TABLE |
45 | statement in this or some other connection demands that all open |
46 | HANDLERs against the flushed table are closed. In order to |
47 | preserve the information about an open HANDLER, we don't perform |
48 | a complete HANDLER CLOSE, but only close the TABLE object. The |
49 | corresponding TABLE_LIST is kept in the cache with 'table' |
50 | pointer set to NULL. The table will be reopened on next access |
51 | (this, however, leads to loss of cursor position, unless the |
52 | cursor points at the first record). |
53 | */ |
54 | |
55 | #include "mariadb.h" |
56 | #include "sql_priv.h" |
57 | #include "sql_handler.h" |
58 | #include "sql_base.h" // close_thread_tables |
59 | #include "lock.h" // mysql_unlock_tables |
60 | #include "key.h" // key_copy |
61 | #include "sql_base.h" // insert_fields |
62 | #include "sql_select.h" |
63 | #include "transaction.h" |
64 | |
65 | #ifdef USE_PRAGMA_IMPLEMENTATION |
66 | #pragma implementation // gcc: Class implementation |
67 | #endif |
68 | |
69 | #define HANDLER_TABLES_HASH_SIZE 120 |
70 | |
71 | static enum enum_ha_read_modes rkey_to_rnext[]= |
72 | { RNEXT_SAME, RNEXT, RPREV, RNEXT, RPREV, RNEXT, RPREV, RPREV }; |
73 | |
74 | /* |
75 | Set handler to state after create, but keep base information about |
76 | which table is used |
77 | */ |
78 | |
79 | void SQL_HANDLER::reset() |
80 | { |
81 | fields.empty(); |
82 | arena.free_items(); |
83 | free_root(&mem_root, MYF(0)); |
84 | my_free(lock); |
85 | init(); |
86 | } |
87 | |
88 | /* Free all allocated data */ |
89 | |
90 | SQL_HANDLER::~SQL_HANDLER() |
91 | { |
92 | reset(); |
93 | my_free(base_data); |
94 | } |
95 | |
96 | /* |
97 | Get hash key and hash key length. |
98 | |
99 | SYNOPSIS |
100 | mysql_ha_hash_get_key() |
101 | tables Pointer to the hash object. |
102 | key_len_p (out) Pointer to the result for key length. |
103 | first Unused. |
104 | |
105 | DESCRIPTION |
106 | The hash object is an TABLE_LIST struct. |
107 | The hash key is the alias name. |
108 | The hash key length is the alias name length plus one for the |
109 | terminateing NUL character. |
110 | |
111 | RETURN |
112 | Pointer to the TABLE_LIST struct. |
113 | */ |
114 | |
115 | static char *mysql_ha_hash_get_key(SQL_HANDLER *table, size_t *key_len, |
116 | my_bool first __attribute__((unused))) |
117 | { |
118 | *key_len= table->handler_name.length + 1 ; /* include '\0' in comparisons */ |
119 | return (char*) table->handler_name.str; |
120 | } |
121 | |
122 | |
123 | /* |
124 | Free an hash object. |
125 | |
126 | SYNOPSIS |
127 | mysql_ha_hash_free() |
128 | tables Pointer to the hash object. |
129 | |
130 | DESCRIPTION |
131 | The hash object is an TABLE_LIST struct. |
132 | |
133 | RETURN |
134 | Nothing |
135 | */ |
136 | |
137 | static void mysql_ha_hash_free(SQL_HANDLER *table) |
138 | { |
139 | delete table; |
140 | } |
141 | |
142 | static void mysql_ha_close_childs(THD *thd, TABLE_LIST *current_table_list, |
143 | TABLE_LIST **next_global) |
144 | { |
145 | TABLE_LIST *table_list; |
146 | DBUG_ENTER("mysql_ha_close_childs" ); |
147 | DBUG_PRINT("info" ,("current_table_list: %p" , current_table_list)); |
148 | DBUG_PRINT("info" ,("next_global: %p" , *next_global)); |
149 | for (table_list = *next_global; table_list; table_list = *next_global) |
150 | { |
151 | *next_global = table_list->next_global; |
152 | DBUG_PRINT("info" ,("table_name: %s.%s" , table_list->table->s->db.str, |
153 | table_list->table->s->table_name.str)); |
154 | DBUG_PRINT("info" ,("parent_l: %p" , table_list->parent_l)); |
155 | if (table_list->parent_l == current_table_list) |
156 | { |
157 | DBUG_PRINT("info" ,("found child" )); |
158 | TABLE *table = table_list->table; |
159 | if (table) |
160 | { |
161 | table->open_by_handler= 0; |
162 | if (!table->s->tmp_table) |
163 | { |
164 | (void) close_thread_table(thd, &table); |
165 | thd->mdl_context.release_lock(table_list->mdl_request.ticket); |
166 | } |
167 | else |
168 | { |
169 | thd->mark_tmp_table_as_free_for_reuse(table); |
170 | } |
171 | } |
172 | mysql_ha_close_childs(thd, table_list, next_global); |
173 | } |
174 | else |
175 | { |
176 | /* the end of child tables */ |
177 | *next_global = table_list; |
178 | break; |
179 | } |
180 | } |
181 | DBUG_VOID_RETURN; |
182 | } |
183 | |
184 | /** |
185 | Close a HANDLER table. |
186 | |
187 | @param thd Thread identifier. |
188 | @param tables A list of tables with the first entry to close. |
189 | |
190 | @note Though this function takes a list of tables, only the first list entry |
191 | will be closed. |
192 | @mote handler_object is not deleted! |
193 | @note Broadcasts refresh if it closed a table with old version. |
194 | */ |
195 | |
196 | static void mysql_ha_close_table(SQL_HANDLER *handler) |
197 | { |
198 | THD *thd= handler->thd; |
199 | TABLE *table= handler->table; |
200 | TABLE_LIST *current_table_list= NULL, *next_global; |
201 | |
202 | /* check if table was already closed */ |
203 | if (!table) |
204 | return; |
205 | |
206 | if ((next_global= table->file->get_next_global_for_child())) |
207 | current_table_list= next_global->parent_l; |
208 | |
209 | table->open_by_handler= 0; |
210 | if (!table->s->tmp_table) |
211 | { |
212 | /* Non temporary table. */ |
213 | if (handler->lock) |
214 | { |
215 | // Mark it unlocked, like in reset_lock_data() |
216 | reset_lock_data(handler->lock, 1); |
217 | } |
218 | |
219 | table->file->ha_index_or_rnd_end(); |
220 | close_thread_table(thd, &table); |
221 | if (current_table_list) |
222 | mysql_ha_close_childs(thd, current_table_list, &next_global); |
223 | thd->mdl_context.release_lock(handler->mdl_request.ticket); |
224 | } |
225 | else |
226 | { |
227 | /* Must be a temporary table */ |
228 | table->file->ha_index_or_rnd_end(); |
229 | if (current_table_list) |
230 | mysql_ha_close_childs(thd, current_table_list, &next_global); |
231 | thd->mark_tmp_table_as_free_for_reuse(table); |
232 | } |
233 | my_free(handler->lock); |
234 | handler->init(); |
235 | } |
236 | |
237 | /* |
238 | Open a HANDLER table. |
239 | |
240 | SYNOPSIS |
241 | mysql_ha_open() |
242 | thd Thread identifier. |
243 | tables A list of tables with the first entry to open. |
244 | reopen Re-open a previously opened handler table. |
245 | |
246 | DESCRIPTION |
247 | Though this function takes a list of tables, only the first list entry |
248 | will be opened. |
249 | 'reopen' is set when a handler table is to be re-opened. In this case, |
250 | 'tables' is the pointer to the hashed SQL_HANDLER object which has been |
251 | saved on the original open. |
252 | 'reopen' is also used to suppress the sending of an 'ok' message. |
253 | |
254 | RETURN |
255 | FALSE OK |
256 | TRUE Error |
257 | */ |
258 | |
259 | bool mysql_ha_open(THD *thd, TABLE_LIST *tables, SQL_HANDLER *reopen) |
260 | { |
261 | SQL_HANDLER *sql_handler= 0; |
262 | uint counter; |
263 | bool error; |
264 | TABLE *table, *backup_open_tables; |
265 | MDL_savepoint mdl_savepoint; |
266 | Query_arena backup_arena; |
267 | DBUG_ENTER("mysql_ha_open" ); |
268 | DBUG_PRINT("enter" ,("'%s'.'%s' as '%s' reopen: %d" , |
269 | tables->db.str, tables->table_name.str, tables->alias.str, |
270 | reopen != 0)); |
271 | |
272 | if (thd->locked_tables_mode) |
273 | { |
274 | my_error(ER_LOCK_OR_ACTIVE_TRANSACTION, MYF(0)); |
275 | DBUG_RETURN(TRUE); |
276 | } |
277 | if (tables->schema_table) |
278 | { |
279 | my_error(ER_WRONG_USAGE, MYF(0), "HANDLER OPEN" , |
280 | INFORMATION_SCHEMA_NAME.str); |
281 | DBUG_PRINT("exit" ,("ERROR" )); |
282 | DBUG_RETURN(TRUE); |
283 | } |
284 | |
285 | if (! my_hash_inited(&thd->handler_tables_hash)) |
286 | { |
287 | /* |
288 | HASH entries are of type SQL_HANDLER |
289 | */ |
290 | if (my_hash_init(&thd->handler_tables_hash, &my_charset_latin1, |
291 | HANDLER_TABLES_HASH_SIZE, 0, 0, |
292 | (my_hash_get_key) mysql_ha_hash_get_key, |
293 | (my_hash_free_key) mysql_ha_hash_free, 0)) |
294 | { |
295 | DBUG_PRINT("exit" ,("ERROR" )); |
296 | DBUG_RETURN(TRUE); |
297 | } |
298 | } |
299 | else if (! reopen) /* Otherwise we have 'tables' already. */ |
300 | { |
301 | if (my_hash_search(&thd->handler_tables_hash, (uchar*) tables->alias.str, |
302 | tables->alias.length + 1)) |
303 | { |
304 | DBUG_PRINT("info" ,("duplicate '%s'" , tables->alias.str)); |
305 | DBUG_PRINT("exit" ,("ERROR" )); |
306 | my_error(ER_NONUNIQ_TABLE, MYF(0), tables->alias.str); |
307 | DBUG_RETURN(TRUE); |
308 | } |
309 | } |
310 | |
311 | /* |
312 | Save and reset the open_tables list so that open_tables() won't |
313 | be able to access (or know about) the previous list. And on return |
314 | from open_tables(), thd->open_tables will contain only the opened |
315 | table. |
316 | |
317 | See open_table() back-off comments for more details. |
318 | */ |
319 | backup_open_tables= thd->open_tables; |
320 | thd->set_open_tables(NULL); |
321 | |
322 | /* |
323 | open_tables() will set 'tables->table' if successful. |
324 | It must be NULL for a real open when calling open_tables(). |
325 | */ |
326 | DBUG_ASSERT(! tables->table); |
327 | |
328 | /* |
329 | We can't request lock with explicit duration for this table |
330 | right from the start as open_tables() can't handle properly |
331 | back-off for such locks. |
332 | */ |
333 | tables->mdl_request.init(MDL_key::TABLE, tables->db.str, tables->table_name.str, |
334 | MDL_SHARED_READ, MDL_TRANSACTION); |
335 | mdl_savepoint= thd->mdl_context.mdl_savepoint(); |
336 | |
337 | /* for now HANDLER can be used only for real TABLES */ |
338 | tables->required_type= TABLE_TYPE_NORMAL; |
339 | |
340 | /* |
341 | We use open_tables() here, rather than, say, |
342 | open_ltable() or open_table() because we would like to be able |
343 | to open a temporary table. |
344 | */ |
345 | error= (thd->open_temporary_tables(tables) || |
346 | open_tables(thd, &tables, &counter, 0)); |
347 | |
348 | if (unlikely(error)) |
349 | goto err; |
350 | |
351 | table= tables->table; |
352 | |
353 | /* There can be only one table in '*tables'. */ |
354 | if (! (table->file->ha_table_flags() & HA_CAN_SQL_HANDLER)) |
355 | { |
356 | my_error(ER_ILLEGAL_HA, MYF(0), table->file->table_type(), |
357 | table->s->db.str, table->s->table_name.str); |
358 | goto err; |
359 | } |
360 | |
361 | DBUG_PRINT("info" ,("clone_tickets start" )); |
362 | for (TABLE_LIST *table_list= tables; table_list; |
363 | table_list= table_list->next_global) |
364 | { |
365 | DBUG_PRINT("info" ,("table_list %s.%s" , table_list->table->s->db.str, |
366 | table_list->table->s->table_name.str)); |
367 | if (table_list->mdl_request.ticket && |
368 | thd->mdl_context.has_lock(mdl_savepoint, table_list->mdl_request.ticket)) |
369 | { |
370 | DBUG_PRINT("info" ,("clone_tickets" )); |
371 | /* The ticket returned is within a savepoint. Make a copy. */ |
372 | error= thd->mdl_context.clone_ticket(&table_list->mdl_request); |
373 | table_list->table->mdl_ticket= table_list->mdl_request.ticket; |
374 | if (unlikely(error)) |
375 | goto err; |
376 | } |
377 | } |
378 | DBUG_PRINT("info" ,("clone_tickets end" )); |
379 | |
380 | if (! reopen) |
381 | { |
382 | /* copy data to sql_handler */ |
383 | if (!(sql_handler= new SQL_HANDLER(thd))) |
384 | goto err; |
385 | init_alloc_root(&sql_handler->mem_root, "sql_handler" , 1024, 0, |
386 | MYF(MY_THREAD_SPECIFIC)); |
387 | |
388 | sql_handler->db.length= tables->db.length; |
389 | sql_handler->table_name.length= tables->table_name.length; |
390 | sql_handler->handler_name.length= tables->alias.length; |
391 | |
392 | if (!(my_multi_malloc(MY_WME, |
393 | &sql_handler->base_data, |
394 | (uint) sql_handler->db.length + 1, |
395 | &sql_handler->table_name.str, |
396 | (uint) sql_handler->table_name.length + 1, |
397 | &sql_handler->handler_name.str, |
398 | (uint) sql_handler->handler_name.length + 1, |
399 | NullS))) |
400 | goto err; |
401 | sql_handler->db.str= sql_handler->base_data; |
402 | memcpy((char*) sql_handler->db.str, tables->db.str, tables->db.length +1); |
403 | memcpy((char*) sql_handler->table_name.str, tables->table_name.str, |
404 | tables->table_name.length+1); |
405 | memcpy((char*) sql_handler->handler_name.str, tables->alias.str, |
406 | tables->alias.length +1); |
407 | |
408 | /* add to hash */ |
409 | if (my_hash_insert(&thd->handler_tables_hash, (uchar*) sql_handler)) |
410 | goto err; |
411 | } |
412 | else |
413 | { |
414 | sql_handler= reopen; |
415 | sql_handler->reset(); |
416 | } |
417 | sql_handler->table= table; |
418 | memcpy(&sql_handler->mdl_request, &tables->mdl_request, |
419 | sizeof(tables->mdl_request)); |
420 | |
421 | if (!(sql_handler->lock= get_lock_data(thd, &sql_handler->table, 1, |
422 | GET_LOCK_STORE_LOCKS))) |
423 | goto err; |
424 | |
425 | /* Get a list of all fields for send_fields */ |
426 | thd->set_n_backup_active_arena(&sql_handler->arena, &backup_arena); |
427 | error= table->fill_item_list(&sql_handler->fields); |
428 | thd->restore_active_arena(&sql_handler->arena, &backup_arena); |
429 | if (unlikely(error)) |
430 | goto err; |
431 | |
432 | /* Always read all columns */ |
433 | table->read_set= &table->s->all_set; |
434 | if (table->vcol_set) |
435 | table->vcol_set= &table->s->all_set; |
436 | |
437 | /* Restore the state. */ |
438 | thd->set_open_tables(backup_open_tables); |
439 | DBUG_PRINT("info" ,("set_lock_duration start" )); |
440 | if (sql_handler->mdl_request.ticket) |
441 | { |
442 | thd->mdl_context.set_lock_duration(sql_handler->mdl_request.ticket, |
443 | MDL_EXPLICIT); |
444 | thd->mdl_context.set_needs_thr_lock_abort(TRUE); |
445 | } |
446 | for (TABLE_LIST *table_list= tables->next_global; table_list; |
447 | table_list= table_list->next_global) |
448 | { |
449 | DBUG_PRINT("info" ,("table_list %s.%s" , table_list->table->s->db.str, |
450 | table_list->table->s->table_name.str)); |
451 | if (table_list->mdl_request.ticket) |
452 | { |
453 | thd->mdl_context.set_lock_duration(table_list->mdl_request.ticket, |
454 | MDL_EXPLICIT); |
455 | thd->mdl_context.set_needs_thr_lock_abort(TRUE); |
456 | } |
457 | } |
458 | DBUG_PRINT("info" ,("set_lock_duration end" )); |
459 | |
460 | /* |
461 | If it's a temp table, don't reset table->query_id as the table is |
462 | being used by this handler. For non-temp tables we use this flag |
463 | in asserts. |
464 | */ |
465 | for (TABLE_LIST *table_list= tables; table_list; |
466 | table_list= table_list->next_global) |
467 | { |
468 | table_list->table->open_by_handler= 1; |
469 | } |
470 | |
471 | /* Safety, cleanup the pointer to satisfy MDL assertions. */ |
472 | tables->mdl_request.ticket= NULL; |
473 | |
474 | if (! reopen) |
475 | my_ok(thd); |
476 | DBUG_PRINT("exit" ,("OK" )); |
477 | DBUG_RETURN(FALSE); |
478 | |
479 | err: |
480 | /* |
481 | No need to rollback statement transaction, it's not started. |
482 | If called with reopen flag, no need to rollback either, |
483 | it will be done at statement end. |
484 | */ |
485 | DBUG_ASSERT(thd->transaction.stmt.is_empty()); |
486 | close_thread_tables(thd); |
487 | thd->mdl_context.rollback_to_savepoint(mdl_savepoint); |
488 | thd->set_open_tables(backup_open_tables); |
489 | if (sql_handler) |
490 | { |
491 | if (!reopen) |
492 | my_hash_delete(&thd->handler_tables_hash, (uchar*) sql_handler); |
493 | else |
494 | sql_handler->reset(); // or should it be init() ? |
495 | } |
496 | DBUG_PRINT("exit" ,("ERROR" )); |
497 | DBUG_RETURN(TRUE); |
498 | } |
499 | |
500 | |
501 | /* |
502 | Close a HANDLER table by alias or table name |
503 | |
504 | SYNOPSIS |
505 | mysql_ha_close() |
506 | thd Thread identifier. |
507 | tables A list of tables with the first entry to close. |
508 | |
509 | DESCRIPTION |
510 | Closes the table that is associated (on the handler tables hash) with the |
511 | name (table->alias) of the specified table. |
512 | |
513 | RETURN |
514 | FALSE ok |
515 | TRUE error |
516 | */ |
517 | |
518 | bool mysql_ha_close(THD *thd, TABLE_LIST *tables) |
519 | { |
520 | SQL_HANDLER *handler; |
521 | DBUG_ENTER("mysql_ha_close" ); |
522 | DBUG_PRINT("enter" ,("'%s'.'%s' as '%s'" , |
523 | tables->db.str, tables->table_name.str, tables->alias.str)); |
524 | |
525 | if (thd->locked_tables_mode) |
526 | { |
527 | my_error(ER_LOCK_OR_ACTIVE_TRANSACTION, MYF(0)); |
528 | DBUG_RETURN(TRUE); |
529 | } |
530 | if ((my_hash_inited(&thd->handler_tables_hash)) && |
531 | (handler= (SQL_HANDLER*) my_hash_search(&thd->handler_tables_hash, |
532 | (const uchar*) tables->alias.str, |
533 | tables->alias.length + 1))) |
534 | { |
535 | mysql_ha_close_table(handler); |
536 | my_hash_delete(&thd->handler_tables_hash, (uchar*) handler); |
537 | } |
538 | else |
539 | { |
540 | my_error(ER_UNKNOWN_TABLE, MYF(0), tables->alias.str, "HANDLER" ); |
541 | DBUG_PRINT("exit" ,("ERROR" )); |
542 | DBUG_RETURN(TRUE); |
543 | } |
544 | |
545 | /* |
546 | Mark MDL_context as no longer breaking protocol if we have |
547 | closed last HANDLER. |
548 | */ |
549 | if (! thd->handler_tables_hash.records) |
550 | thd->mdl_context.set_needs_thr_lock_abort(FALSE); |
551 | |
552 | my_ok(thd); |
553 | DBUG_PRINT("exit" , ("OK" )); |
554 | DBUG_RETURN(FALSE); |
555 | } |
556 | |
557 | |
558 | /** |
559 | Finds an open HANDLER table. |
560 | |
561 | @params name Name of handler to open |
562 | |
563 | @return 0 failure |
564 | @return handler |
565 | */ |
566 | |
567 | static SQL_HANDLER *mysql_ha_find_handler(THD *thd, const LEX_CSTRING *name) |
568 | { |
569 | SQL_HANDLER *handler; |
570 | if ((my_hash_inited(&thd->handler_tables_hash)) && |
571 | (handler= (SQL_HANDLER*) my_hash_search(&thd->handler_tables_hash, |
572 | (const uchar*) name->str, |
573 | name->length + 1))) |
574 | { |
575 | DBUG_PRINT("info-in-hash" ,("'%s'.'%s' as '%s' table: %p" , |
576 | handler->db.str, |
577 | handler->table_name.str, |
578 | handler->handler_name.str, handler->table)); |
579 | if (!handler->table) |
580 | { |
581 | /* The handler table has been closed. Re-open it. */ |
582 | TABLE_LIST tmp; |
583 | tmp.init_one_table(&handler->db, &handler->table_name, |
584 | &handler->handler_name, TL_READ); |
585 | |
586 | if (mysql_ha_open(thd, &tmp, handler)) |
587 | { |
588 | DBUG_PRINT("exit" ,("reopen failed" )); |
589 | return 0; |
590 | } |
591 | } |
592 | } |
593 | else |
594 | { |
595 | my_error(ER_UNKNOWN_TABLE, MYF(0), name->str, "HANDLER" ); |
596 | return 0; |
597 | } |
598 | return handler; |
599 | } |
600 | |
601 | |
602 | /** |
603 | Check that condition and key name are ok |
604 | |
605 | @param handler |
606 | @param mode Read mode (RFIRST, RNEXT etc...) |
607 | @param keyname Key to use. |
608 | @param key_expr List of key column values |
609 | @param cond Where clause |
610 | @param in_prepare If we are in prepare phase (we can't evalute items yet) |
611 | |
612 | @return 0 ok |
613 | @return 1 error |
614 | |
615 | In ok, then values of used key and mode is stored in sql_handler |
616 | */ |
617 | |
618 | static bool |
619 | mysql_ha_fix_cond_and_key(SQL_HANDLER *handler, |
620 | enum enum_ha_read_modes mode, const char *keyname, |
621 | List<Item> *key_expr, enum ha_rkey_function ha_rkey_mode, |
622 | Item *cond, bool in_prepare) |
623 | { |
624 | THD *thd= handler->thd; |
625 | TABLE *table= handler->table; |
626 | if (cond) |
627 | { |
628 | /* This can only be true for temp tables */ |
629 | if (table->query_id != thd->query_id) |
630 | cond->cleanup(); // File was reopened |
631 | if ((!cond->fixed && |
632 | cond->fix_fields(thd, &cond)) || cond->check_cols(1)) |
633 | return 1; |
634 | } |
635 | |
636 | if (keyname) |
637 | { |
638 | /* Check if same as last keyname. If not, do a full lookup */ |
639 | if (handler->keyno < 0 || |
640 | my_strcasecmp(&my_charset_latin1, |
641 | keyname, |
642 | table->s->key_info[handler->keyno].name.str)) |
643 | { |
644 | if ((handler->keyno= find_type(keyname, &table->s->keynames, |
645 | FIND_TYPE_NO_PREFIX) - 1) < 0) |
646 | { |
647 | my_error(ER_KEY_DOES_NOT_EXITS, MYF(0), keyname, |
648 | handler->handler_name.str); |
649 | return 1; |
650 | } |
651 | } |
652 | |
653 | /* Check key parts */ |
654 | if (mode == RKEY) |
655 | { |
656 | TABLE *table= handler->table; |
657 | KEY *keyinfo= table->key_info + handler->keyno; |
658 | KEY_PART_INFO *key_part= keyinfo->key_part; |
659 | List_iterator<Item> it_ke(*key_expr); |
660 | Item *item; |
661 | key_part_map keypart_map; |
662 | uint key_len; |
663 | const KEY *c_key= table->s->key_info + handler->keyno; |
664 | |
665 | if ((c_key->flags & HA_SPATIAL) || |
666 | c_key->algorithm == HA_KEY_ALG_FULLTEXT || |
667 | (ha_rkey_mode != HA_READ_KEY_EXACT && |
668 | (table->file->index_flags(handler->keyno, 0, TRUE) & |
669 | (HA_READ_NEXT | HA_READ_PREV | HA_READ_RANGE)) == 0)) |
670 | { |
671 | my_error(ER_KEY_DOESNT_SUPPORT, MYF(0), |
672 | table->file->index_type(handler->keyno), keyinfo->name); |
673 | return 1; |
674 | } |
675 | |
676 | if (key_expr->elements > keyinfo->user_defined_key_parts) |
677 | { |
678 | my_error(ER_TOO_MANY_KEY_PARTS, MYF(0), |
679 | keyinfo->user_defined_key_parts); |
680 | return 1; |
681 | } |
682 | |
683 | if (key_expr->elements < keyinfo->user_defined_key_parts && |
684 | (table->file->index_flags(handler->keyno, 0, TRUE) & |
685 | HA_ONLY_WHOLE_INDEX)) |
686 | { |
687 | my_error(ER_KEY_DOESNT_SUPPORT, MYF(0), |
688 | table->file->index_type(handler->keyno), keyinfo->name); |
689 | return 1; |
690 | } |
691 | |
692 | for (keypart_map= key_len=0 ; (item=it_ke++) ; key_part++) |
693 | { |
694 | my_bitmap_map *old_map; |
695 | /* note that 'item' can be changed by fix_fields() call */ |
696 | if ((!item->fixed && |
697 | item->fix_fields(thd, it_ke.ref())) || |
698 | (item= *it_ke.ref())->check_cols(1)) |
699 | return 1; |
700 | if (item->used_tables() & ~(RAND_TABLE_BIT | PARAM_TABLE_BIT)) |
701 | { |
702 | my_error(ER_WRONG_ARGUMENTS,MYF(0),"HANDLER ... READ" ); |
703 | return 1; |
704 | } |
705 | if (!in_prepare) |
706 | { |
707 | old_map= dbug_tmp_use_all_columns(table, table->write_set); |
708 | (void) item->save_in_field(key_part->field, 1); |
709 | dbug_tmp_restore_column_map(table->write_set, old_map); |
710 | } |
711 | key_len+= key_part->store_length; |
712 | keypart_map= (keypart_map << 1) | 1; |
713 | } |
714 | handler->keypart_map= keypart_map; |
715 | handler->key_len= key_len; |
716 | } |
717 | else |
718 | { |
719 | /* |
720 | Check if the same index involved. |
721 | We need to always do this check because we may not have yet |
722 | called the handler since the last keyno change. |
723 | */ |
724 | if ((uint) handler->keyno != table->file->get_index()) |
725 | { |
726 | if (mode == RNEXT) |
727 | mode= RFIRST; |
728 | else if (mode == RPREV) |
729 | mode= RLAST; |
730 | } |
731 | } |
732 | } |
733 | else if (table->file->inited != handler::RND) |
734 | { |
735 | /* Convert RNEXT to RFIRST if we haven't started row scan */ |
736 | if (mode == RNEXT) |
737 | mode= RFIRST; |
738 | } |
739 | handler->mode= mode; // Store adjusted mode |
740 | return 0; |
741 | } |
742 | |
743 | /* |
744 | Read from a HANDLER table. |
745 | |
746 | SYNOPSIS |
747 | mysql_ha_read() |
748 | thd Thread identifier. |
749 | tables A list of tables with the first entry to read. |
750 | mode |
751 | keyname |
752 | key_expr |
753 | ha_rkey_mode |
754 | cond |
755 | select_limit_cnt |
756 | offset_limit_cnt |
757 | |
758 | RETURN |
759 | FALSE ok |
760 | TRUE error |
761 | */ |
762 | |
763 | bool mysql_ha_read(THD *thd, TABLE_LIST *tables, |
764 | enum enum_ha_read_modes mode, const char *keyname, |
765 | List<Item> *key_expr, |
766 | enum ha_rkey_function ha_rkey_mode, Item *cond, |
767 | ha_rows select_limit_cnt, ha_rows offset_limit_cnt) |
768 | { |
769 | SQL_HANDLER *handler; |
770 | TABLE *table; |
771 | Protocol *protocol= thd->protocol; |
772 | char buff[MAX_FIELD_WIDTH]; |
773 | String buffer(buff, sizeof(buff), system_charset_info); |
774 | int error, keyno; |
775 | uint num_rows; |
776 | uchar *UNINIT_VAR(key); |
777 | MDL_deadlock_and_lock_abort_error_handler sql_handler_lock_error; |
778 | DBUG_ENTER("mysql_ha_read" ); |
779 | DBUG_PRINT("enter" ,("'%s'.'%s' as '%s'" , |
780 | tables->db.str, tables->table_name.str, tables->alias.str)); |
781 | |
782 | if (thd->locked_tables_mode) |
783 | { |
784 | my_error(ER_LOCK_OR_ACTIVE_TRANSACTION, MYF(0)); |
785 | DBUG_RETURN(TRUE); |
786 | } |
787 | |
788 | retry: |
789 | if (!(handler= mysql_ha_find_handler(thd, &tables->alias))) |
790 | goto err0; |
791 | |
792 | table= handler->table; |
793 | tables->table= table; // This is used by fix_fields |
794 | table->pos_in_table_list= tables; |
795 | |
796 | if (handler->lock->table_count > 0) |
797 | { |
798 | int lock_error; |
799 | |
800 | THR_LOCK_DATA **pos,**end; |
801 | for (pos= handler->lock->locks, |
802 | end= handler->lock->locks + handler->lock->lock_count; |
803 | pos < end; |
804 | pos++) |
805 | { |
806 | pos[0]->type= pos[0]->org_type; |
807 | } |
808 | |
809 | /* save open_tables state */ |
810 | TABLE* backup_open_tables= thd->open_tables; |
811 | /* Always a one-element list, see mysql_ha_open(). */ |
812 | DBUG_ASSERT(table->next == NULL || table->s->tmp_table); |
813 | /* |
814 | mysql_lock_tables() needs thd->open_tables to be set correctly to |
815 | be able to handle aborts properly. |
816 | */ |
817 | thd->set_open_tables(table); |
818 | |
819 | sql_handler_lock_error.init(); |
820 | thd->push_internal_handler(&sql_handler_lock_error); |
821 | |
822 | lock_error= mysql_lock_tables(thd, handler->lock, |
823 | (table->s->tmp_table == NO_TMP_TABLE ? |
824 | MYSQL_LOCK_NOT_TEMPORARY : 0)); |
825 | |
826 | thd->pop_internal_handler(); |
827 | |
828 | /* |
829 | In 5.1 and earlier, mysql_lock_tables() could replace the TABLE |
830 | object with another one (reopen it). This is no longer the case |
831 | with new MDL. |
832 | */ |
833 | DBUG_ASSERT(table == thd->open_tables); |
834 | /* Restore previous context. */ |
835 | thd->set_open_tables(backup_open_tables); |
836 | |
837 | if (sql_handler_lock_error.need_reopen()) |
838 | { |
839 | DBUG_ASSERT(lock_error && !thd->is_error()); |
840 | /* |
841 | Always close statement transaction explicitly, |
842 | so that the engine doesn't have to count locks. |
843 | There should be no need to perform transaction |
844 | rollback due to deadlock. |
845 | */ |
846 | DBUG_ASSERT(! thd->transaction_rollback_request); |
847 | trans_rollback_stmt(thd); |
848 | mysql_ha_close_table(handler); |
849 | if (thd->stmt_arena->is_stmt_execute()) |
850 | { |
851 | /* |
852 | As we have already sent field list and types to the client, we can't |
853 | handle any changes in the table format for prepared statements. |
854 | Better to force a reprepare. |
855 | */ |
856 | my_error(ER_NEED_REPREPARE, MYF(0)); |
857 | goto err0; |
858 | } |
859 | goto retry; |
860 | } |
861 | |
862 | if (unlikely(lock_error)) |
863 | goto err0; // mysql_lock_tables() printed error message already |
864 | } |
865 | |
866 | if (mysql_ha_fix_cond_and_key(handler, mode, keyname, key_expr, |
867 | ha_rkey_mode, cond, 0)) |
868 | goto err; |
869 | mode= handler->mode; |
870 | keyno= handler->keyno; |
871 | |
872 | protocol->send_result_set_metadata(&handler->fields, |
873 | Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF); |
874 | |
875 | /* |
876 | In ::external_lock InnoDB resets the fields which tell it that |
877 | the handle is used in the HANDLER interface. Tell it again that |
878 | we are using it for HANDLER. |
879 | */ |
880 | |
881 | table->file->init_table_handle_for_HANDLER(); |
882 | |
883 | for (num_rows=0; num_rows < select_limit_cnt; ) |
884 | { |
885 | switch (mode) { |
886 | case RNEXT: |
887 | if (table->file->inited != handler::NONE) |
888 | { |
889 | if ((error= table->file->can_continue_handler_scan())) |
890 | break; |
891 | if (keyname) |
892 | { |
893 | /* Check if we read from the same index. */ |
894 | DBUG_ASSERT((uint) keyno == table->file->get_index()); |
895 | error= table->file->ha_index_next(table->record[0]); |
896 | } |
897 | else |
898 | error= table->file->ha_rnd_next(table->record[0]); |
899 | break; |
900 | } |
901 | /* else fall through */ |
902 | case RFIRST: |
903 | if (keyname) |
904 | { |
905 | if (likely(!(error= table->file->ha_index_or_rnd_end())) && |
906 | likely(!(error= table->file->ha_index_init(keyno, 1)))) |
907 | error= table->file->ha_index_first(table->record[0]); |
908 | } |
909 | else |
910 | { |
911 | if (likely(!(error= table->file->ha_index_or_rnd_end())) && |
912 | likely(!(error= table->file->ha_rnd_init(1)))) |
913 | error= table->file->ha_rnd_next(table->record[0]); |
914 | } |
915 | mode= RNEXT; |
916 | break; |
917 | case RPREV: |
918 | DBUG_ASSERT(keyname != 0); |
919 | /* Check if we read from the same index. */ |
920 | DBUG_ASSERT((uint) keyno == table->file->get_index()); |
921 | if (table->file->inited != handler::NONE) |
922 | { |
923 | if ((error= table->file->can_continue_handler_scan())) |
924 | break; |
925 | error= table->file->ha_index_prev(table->record[0]); |
926 | break; |
927 | } |
928 | /* else fall through */ |
929 | case RLAST: |
930 | DBUG_ASSERT(keyname != 0); |
931 | if (likely(!(error= table->file->ha_index_or_rnd_end())) && |
932 | likely(!(error= table->file->ha_index_init(keyno, 1)))) |
933 | error= table->file->ha_index_last(table->record[0]); |
934 | mode=RPREV; |
935 | break; |
936 | case RNEXT_SAME: |
937 | /* Continue scan on "(keypart1,keypart2,...)=(c1, c2, ...) */ |
938 | DBUG_ASSERT(keyname != 0); |
939 | error= table->file->ha_index_next_same(table->record[0], key, |
940 | handler->key_len); |
941 | break; |
942 | case RKEY: |
943 | { |
944 | DBUG_ASSERT(keyname != 0); |
945 | |
946 | if (unlikely(!(key= (uchar*) thd->calloc(ALIGN_SIZE(handler->key_len))))) |
947 | goto err; |
948 | if (unlikely((error= table->file->ha_index_or_rnd_end()))) |
949 | break; |
950 | key_copy(key, table->record[0], table->key_info + keyno, |
951 | handler->key_len); |
952 | if (unlikely(!(error= table->file->ha_index_init(keyno, 1)))) |
953 | error= table->file->ha_index_read_map(table->record[0], |
954 | key, handler->keypart_map, |
955 | ha_rkey_mode); |
956 | mode= rkey_to_rnext[(int)ha_rkey_mode]; |
957 | break; |
958 | } |
959 | default: |
960 | my_error(ER_ILLEGAL_HA, MYF(0), table->file->table_type(), |
961 | table->s->db.str, table->s->table_name.str); |
962 | goto err; |
963 | } |
964 | |
965 | if (unlikely(error)) |
966 | { |
967 | if (error != HA_ERR_KEY_NOT_FOUND && error != HA_ERR_END_OF_FILE) |
968 | { |
969 | /* Don't give error in the log file for some expected problems */ |
970 | if (error != HA_ERR_RECORD_CHANGED && error != HA_ERR_WRONG_COMMAND) |
971 | sql_print_error("mysql_ha_read: Got error %d when reading " |
972 | "table '%s'" , |
973 | error, tables->table_name.str); |
974 | table->file->print_error(error,MYF(0)); |
975 | table->file->ha_index_or_rnd_end(); |
976 | goto err; |
977 | } |
978 | goto ok; |
979 | } |
980 | if (cond && !cond->val_int()) |
981 | { |
982 | if (thd->is_error()) |
983 | goto err; |
984 | continue; |
985 | } |
986 | if (num_rows >= offset_limit_cnt) |
987 | { |
988 | protocol->prepare_for_resend(); |
989 | |
990 | if (protocol->send_result_set_row(&handler->fields)) |
991 | goto err; |
992 | |
993 | protocol->write(); |
994 | } |
995 | num_rows++; |
996 | } |
997 | ok: |
998 | /* |
999 | Always close statement transaction explicitly, |
1000 | so that the engine doesn't have to count locks. |
1001 | */ |
1002 | trans_commit_stmt(thd); |
1003 | mysql_unlock_tables(thd, handler->lock, 0); |
1004 | my_eof(thd); |
1005 | DBUG_PRINT("exit" ,("OK" )); |
1006 | DBUG_RETURN(FALSE); |
1007 | |
1008 | err: |
1009 | trans_rollback_stmt(thd); |
1010 | mysql_unlock_tables(thd, handler->lock, 0); |
1011 | err0: |
1012 | DBUG_PRINT("exit" ,("ERROR" )); |
1013 | DBUG_RETURN(TRUE); |
1014 | } |
1015 | |
1016 | |
1017 | /** |
1018 | Prepare for handler read |
1019 | |
1020 | For parameters, see mysql_ha_read() |
1021 | */ |
1022 | |
1023 | SQL_HANDLER *mysql_ha_read_prepare(THD *thd, TABLE_LIST *tables, |
1024 | enum enum_ha_read_modes mode, |
1025 | const char *keyname, |
1026 | List<Item> *key_expr, enum ha_rkey_function ha_rkey_mode, |
1027 | Item *cond) |
1028 | { |
1029 | SQL_HANDLER *handler; |
1030 | DBUG_ENTER("mysql_ha_read_prepare" ); |
1031 | if (!(handler= mysql_ha_find_handler(thd, &tables->alias))) |
1032 | DBUG_RETURN(0); |
1033 | tables->table= handler->table; // This is used by fix_fields |
1034 | if (mysql_ha_fix_cond_and_key(handler, mode, keyname, key_expr, |
1035 | ha_rkey_mode, cond, 1)) |
1036 | DBUG_RETURN(0); |
1037 | DBUG_RETURN(handler); |
1038 | } |
1039 | |
1040 | |
1041 | |
1042 | /** |
1043 | Scan the handler tables hash for matching tables. |
1044 | |
1045 | @param thd Thread identifier. |
1046 | @param tables The list of tables to remove. |
1047 | |
1048 | @return Pointer to head of linked list (TABLE_LIST::next_local) of matching |
1049 | TABLE_LIST elements from handler_tables_hash. Otherwise, NULL if no |
1050 | table was matched. |
1051 | */ |
1052 | |
1053 | static SQL_HANDLER *mysql_ha_find_match(THD *thd, TABLE_LIST *tables) |
1054 | { |
1055 | SQL_HANDLER *hash_tables, *head= NULL; |
1056 | TABLE_LIST *first= tables; |
1057 | DBUG_ENTER("mysql_ha_find_match" ); |
1058 | |
1059 | /* search for all handlers with matching table names */ |
1060 | for (uint i= 0; i < thd->handler_tables_hash.records; i++) |
1061 | { |
1062 | hash_tables= (SQL_HANDLER*) my_hash_element(&thd->handler_tables_hash, i); |
1063 | |
1064 | for (tables= first; tables; tables= tables->next_local) |
1065 | { |
1066 | if (tables->is_anonymous_derived_table()) |
1067 | continue; |
1068 | if ((! tables->db.str[0] || |
1069 | ! my_strcasecmp(&my_charset_latin1, hash_tables->db.str, |
1070 | tables->get_db_name())) && |
1071 | ! my_strcasecmp(&my_charset_latin1, hash_tables->table_name.str, |
1072 | tables->get_table_name())) |
1073 | { |
1074 | /* Link into hash_tables list */ |
1075 | hash_tables->next= head; |
1076 | head= hash_tables; |
1077 | break; |
1078 | } |
1079 | } |
1080 | } |
1081 | DBUG_RETURN(head); |
1082 | } |
1083 | |
1084 | |
1085 | /** |
1086 | Remove matching tables from the HANDLER's hash table. |
1087 | |
1088 | @param thd Thread identifier. |
1089 | @param tables The list of tables to remove. |
1090 | |
1091 | @note Broadcasts refresh if it closed a table with old version. |
1092 | */ |
1093 | |
1094 | void mysql_ha_rm_tables(THD *thd, TABLE_LIST *tables) |
1095 | { |
1096 | SQL_HANDLER *hash_tables, *next; |
1097 | DBUG_ENTER("mysql_ha_rm_tables" ); |
1098 | |
1099 | DBUG_ASSERT(tables); |
1100 | |
1101 | hash_tables= mysql_ha_find_match(thd, tables); |
1102 | |
1103 | while (hash_tables) |
1104 | { |
1105 | next= hash_tables->next; |
1106 | if (hash_tables->table) |
1107 | mysql_ha_close_table(hash_tables); |
1108 | my_hash_delete(&thd->handler_tables_hash, (uchar*) hash_tables); |
1109 | hash_tables= next; |
1110 | } |
1111 | |
1112 | /* |
1113 | Mark MDL_context as no longer breaking protocol if we have |
1114 | closed last HANDLER. |
1115 | */ |
1116 | if (! thd->handler_tables_hash.records) |
1117 | thd->mdl_context.set_needs_thr_lock_abort(FALSE); |
1118 | |
1119 | DBUG_VOID_RETURN; |
1120 | } |
1121 | |
1122 | |
1123 | /** |
1124 | Close cursors of matching tables from the HANDLER's hash table. |
1125 | |
1126 | @param thd Thread identifier. |
1127 | @param tables The list of tables to flush. |
1128 | */ |
1129 | |
1130 | void mysql_ha_flush_tables(THD *thd, TABLE_LIST *all_tables) |
1131 | { |
1132 | DBUG_ENTER("mysql_ha_flush_tables" ); |
1133 | |
1134 | for (TABLE_LIST *table_list= all_tables; table_list; |
1135 | table_list= table_list->next_global) |
1136 | { |
1137 | SQL_HANDLER *hash_tables= mysql_ha_find_match(thd, table_list); |
1138 | /* Close all aliases of the same table. */ |
1139 | while (hash_tables) |
1140 | { |
1141 | SQL_HANDLER *next_local= hash_tables->next; |
1142 | if (hash_tables->table) |
1143 | mysql_ha_close_table(hash_tables); |
1144 | hash_tables= next_local; |
1145 | } |
1146 | } |
1147 | |
1148 | DBUG_VOID_RETURN; |
1149 | } |
1150 | |
1151 | |
1152 | /** |
1153 | Flush (close and mark for re-open) all tables that should be should |
1154 | be reopen. |
1155 | |
1156 | @param thd Thread identifier. |
1157 | |
1158 | @note Broadcasts refresh if it closed a table with old version. |
1159 | */ |
1160 | |
1161 | void mysql_ha_flush(THD *thd) |
1162 | { |
1163 | SQL_HANDLER *hash_tables; |
1164 | DBUG_ENTER("mysql_ha_flush" ); |
1165 | |
1166 | /* |
1167 | Don't try to flush open HANDLERs when we're working with |
1168 | system tables. The main MDL context is backed up and we can't |
1169 | properly release HANDLER locks stored there. |
1170 | */ |
1171 | if (thd->state_flags & Open_tables_state::BACKUPS_AVAIL) |
1172 | DBUG_VOID_RETURN; |
1173 | |
1174 | for (uint i= 0; i < thd->handler_tables_hash.records; i++) |
1175 | { |
1176 | hash_tables= (SQL_HANDLER*) my_hash_element(&thd->handler_tables_hash, i); |
1177 | /* |
1178 | TABLE::mdl_ticket is 0 for temporary tables so we need extra check. |
1179 | */ |
1180 | if (hash_tables->table && |
1181 | ((hash_tables->table->mdl_ticket && |
1182 | hash_tables->table->mdl_ticket->has_pending_conflicting_lock()) || |
1183 | (!hash_tables->table->s->tmp_table && |
1184 | hash_tables->table->s->tdc->flushed))) |
1185 | mysql_ha_close_table(hash_tables); |
1186 | } |
1187 | |
1188 | DBUG_VOID_RETURN; |
1189 | } |
1190 | |
1191 | |
1192 | /** |
1193 | Close all HANDLER's tables. |
1194 | |
1195 | @param thd Thread identifier. |
1196 | |
1197 | @note Broadcasts refresh if it closed a table with old version. |
1198 | */ |
1199 | |
1200 | void mysql_ha_cleanup(THD *thd) |
1201 | { |
1202 | SQL_HANDLER *hash_tables; |
1203 | DBUG_ENTER("mysql_ha_cleanup" ); |
1204 | |
1205 | for (uint i= 0; i < thd->handler_tables_hash.records; i++) |
1206 | { |
1207 | hash_tables= (SQL_HANDLER*) my_hash_element(&thd->handler_tables_hash, i); |
1208 | if (hash_tables->table) |
1209 | mysql_ha_close_table(hash_tables); |
1210 | } |
1211 | |
1212 | my_hash_free(&thd->handler_tables_hash); |
1213 | |
1214 | DBUG_VOID_RETURN; |
1215 | } |
1216 | |
1217 | |
1218 | /** |
1219 | Set explicit duration for metadata locks corresponding to open HANDLERs |
1220 | to protect them from being released at the end of transaction. |
1221 | |
1222 | @param thd Thread identifier. |
1223 | */ |
1224 | |
1225 | void mysql_ha_set_explicit_lock_duration(THD *thd) |
1226 | { |
1227 | SQL_HANDLER *hash_tables; |
1228 | DBUG_ENTER("mysql_ha_set_explicit_lock_duration" ); |
1229 | |
1230 | for (uint i= 0; i < thd->handler_tables_hash.records; i++) |
1231 | { |
1232 | hash_tables= (SQL_HANDLER*) my_hash_element(&thd->handler_tables_hash, i); |
1233 | if (hash_tables->table && hash_tables->table->mdl_ticket) |
1234 | thd->mdl_context.set_lock_duration(hash_tables->table->mdl_ticket, |
1235 | MDL_EXPLICIT); |
1236 | } |
1237 | DBUG_VOID_RETURN; |
1238 | } |
1239 | |
1240 | |
1241 | /** |
1242 | Remove temporary tables from the HANDLER's hash table. The reason |
1243 | for having a separate function, rather than calling |
1244 | mysql_ha_rm_tables() is that it is not always feasible (e.g. in |
1245 | THD::close_temporary_tables) to obtain a TABLE_LIST containing the |
1246 | temporary tables. |
1247 | |
1248 | @See THD::close_temporary_tables() |
1249 | @param thd Thread identifier. |
1250 | */ |
1251 | void mysql_ha_rm_temporary_tables(THD *thd) |
1252 | { |
1253 | DBUG_ENTER("mysql_ha_rm_temporary_tables" ); |
1254 | |
1255 | TABLE_LIST *tmp_handler_tables= NULL; |
1256 | for (uint i= 0; i < thd->handler_tables_hash.records; i++) |
1257 | { |
1258 | TABLE_LIST *handler_table= reinterpret_cast<TABLE_LIST*> |
1259 | (my_hash_element(&thd->handler_tables_hash, i)); |
1260 | |
1261 | if (handler_table->table && handler_table->table->s->tmp_table) |
1262 | { |
1263 | handler_table->next_local= tmp_handler_tables; |
1264 | tmp_handler_tables= handler_table; |
1265 | } |
1266 | } |
1267 | |
1268 | if (tmp_handler_tables) |
1269 | mysql_ha_rm_tables(thd, tmp_handler_tables); |
1270 | |
1271 | DBUG_VOID_RETURN; |
1272 | } |
1273 | |