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
71static 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
79void 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
90SQL_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
115static 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
137static void mysql_ha_hash_free(SQL_HANDLER *table)
138{
139 delete table;
140}
141
142static 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
196static 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
259bool 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
479err:
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
518bool 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
567static 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
618static bool
619mysql_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
763bool 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
788retry:
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 }
997ok:
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
1008err:
1009 trans_rollback_stmt(thd);
1010 mysql_unlock_tables(thd, handler->lock, 0);
1011err0:
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
1023SQL_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
1053static 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
1094void 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
1130void 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
1161void 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
1200void 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
1225void 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*/
1251void 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