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