1 /* Copyright (c) 2001, 2016, Oracle and/or its affiliates. All rights reserved.
2
3 This program is free software; you can redistribute it and/or modify
4 it under the terms of the GNU General Public License, version 2.0,
5 as published by the Free Software Foundation.
6
7 This program is also distributed with certain software (including
8 but not limited to OpenSSL) that is licensed under separate terms,
9 as designated in a particular file or component or in included license
10 documentation. The authors of MySQL hereby grant you an additional
11 permission to link the program and your derivative works with the
12 separately licensed software that they have included with MySQL.
13
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License, version 2.0, for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software Foundation,
21 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA */
22
23
24 /* HANDLER ... commands - direct access to ISAM */
25
26 /* TODO:
27 HANDLER blabla OPEN [ AS foobar ] [ (column-list) ]
28
29 the most natural (easiest, fastest) way to do it is to
30 compute List<Item> field_list not in mysql_ha_read
31 but in mysql_ha_open, and then store it in TABLE structure.
32
33 The problem here is that mysql_parse calls free_item to free all the
34 items allocated at the end of every query. The workaround would to
35 keep two item lists per THD - normal free_list and handler_items.
36 The second is to be freeed only on thread end. mysql_ha_open should
37 then do { handler_items=concat(handler_items, free_list); free_list=0; }
38
39 But !!! do_command calls free_root at the end of every query and frees up
40 all the sql_alloc'ed memory. It's harder to work around...
41 */
42
43 /*
44 The information about open HANDLER objects is stored in a HASH.
45 It holds objects of type TABLE_LIST, which are indexed by table
46 name/alias, and allows us to quickly find a HANDLER table for any
47 operation at hand - be it HANDLER READ or HANDLER CLOSE.
48
49 It also allows us to maintain an "open" HANDLER even in cases
50 when there is no physically open cursor. E.g. a FLUSH TABLE
51 statement in this or some other connection demands that all open
52 HANDLERs against the flushed table are closed. In order to
53 preserve the information about an open HANDLER, we don't perform
54 a complete HANDLER CLOSE, but only close the TABLE object. The
55 corresponding TABLE_LIST is kept in the cache with 'table'
56 pointer set to NULL. The table will be reopened on next access
57 (this, however, leads to loss of cursor position, unless the
58 cursor points at the first record).
59 */
60
61 #include "sql_priv.h"
62 #include "sql_handler.h"
63 #include "unireg.h" // REQUIRED: for other includes
64 #include "sql_base.h" // close_thread_tables
65 #include "lock.h" // mysql_unlock_tables
66 #include "key.h" // key_copy
67 #include "sql_base.h" // insert_fields
68 #include "sql_select.h"
69 #include "transaction.h"
70 #include "sql_parse.h" // check_table_access
71
72 #define HANDLER_TABLES_HASH_SIZE 120
73
74 static enum enum_ha_read_modes rkey_to_rnext[]=
75 { RNEXT_SAME, RNEXT, RPREV, RNEXT, RPREV, RNEXT, RPREV, RPREV };
76
77 static bool mysql_ha_open_table(THD *thd, TABLE_LIST *table);
78
79 /*
80 Get hash key and hash key length.
81
82 SYNOPSIS
83 mysql_ha_hash_get_key()
84 tables Pointer to the hash object.
85 key_len_p (out) Pointer to the result for key length.
86 first Unused.
87
88 DESCRIPTION
89 The hash object is an TABLE_LIST struct.
90 The hash key is the alias name.
91 The hash key length is the alias name length plus one for the
92 terminateing NUL character.
93
94 RETURN
95 Pointer to the TABLE_LIST struct.
96 */
97
mysql_ha_hash_get_key(TABLE_LIST * tables,size_t * key_len_p,my_bool first MY_ATTRIBUTE ((unused)))98 static char *mysql_ha_hash_get_key(TABLE_LIST *tables, size_t *key_len_p,
99 my_bool first MY_ATTRIBUTE((unused)))
100 {
101 *key_len_p= strlen(tables->alias) + 1 ; /* include '\0' in comparisons */
102 return tables->alias;
103 }
104
105
106 /*
107 Free an hash object.
108
109 SYNOPSIS
110 mysql_ha_hash_free()
111 tables Pointer to the hash object.
112
113 DESCRIPTION
114 The hash object is an TABLE_LIST struct.
115
116 RETURN
117 Nothing
118 */
119
mysql_ha_hash_free(TABLE_LIST * tables)120 static void mysql_ha_hash_free(TABLE_LIST *tables)
121 {
122 my_free(tables);
123 }
124
125 /**
126 Close a HANDLER table.
127
128 @param thd Thread identifier.
129 @param tables A list of tables with the first entry to close.
130
131 @note Though this function takes a list of tables, only the first list entry
132 will be closed.
133 @note Broadcasts refresh if it closed a table with old version.
134 */
135
mysql_ha_close_table(THD * thd,TABLE_LIST * tables)136 static void mysql_ha_close_table(THD *thd, TABLE_LIST *tables)
137 {
138
139 if (tables->table && !tables->table->s->tmp_table)
140 {
141 /* Non temporary table. */
142 tables->table->file->ha_index_or_rnd_end();
143 tables->table->open_by_handler= 0;
144 close_thread_table(thd, &tables->table);
145 thd->mdl_context.release_lock(tables->mdl_request.ticket);
146 }
147 else if (tables->table)
148 {
149 /* Must be a temporary table */
150 TABLE *table= tables->table;
151 table->file->ha_index_or_rnd_end();
152 table->query_id= thd->query_id;
153 table->open_by_handler= 0;
154 mark_tmp_table_for_reuse(table);
155 }
156
157 /* Mark table as closed, ready for re-open if necessary. */
158 tables->table= NULL;
159 /* Safety, cleanup the pointer to satisfy MDL assertions. */
160 tables->mdl_request.ticket= NULL;
161 }
162
163
164 /**
165 Execute a HANDLER OPEN statement.
166
167 @param thd The current thread.
168
169 @retval FALSE on success.
170 @retval TRUE on failure.
171 */
172
execute(THD * thd)173 bool Sql_cmd_handler_open::execute(THD *thd)
174 {
175 TABLE_LIST *hash_tables = NULL;
176 char *db, *name, *alias;
177 uint dblen, namelen, aliaslen;
178 TABLE_LIST *tables= (TABLE_LIST*) thd->lex->select_lex.table_list.first;
179 DBUG_ENTER("Sql_cmd_handler_open::execute");
180 DBUG_PRINT("enter",("'%s'.'%s' as '%s'",
181 tables->db, tables->table_name, tables->alias));
182
183 if (thd->locked_tables_mode)
184 {
185 my_error(ER_LOCK_OR_ACTIVE_TRANSACTION, MYF(0));
186 DBUG_RETURN(TRUE);
187 }
188 if (tables->schema_table)
189 {
190 my_error(ER_WRONG_USAGE, MYF(0), "HANDLER OPEN",
191 INFORMATION_SCHEMA_NAME.str);
192 DBUG_PRINT("exit",("ERROR"));
193 DBUG_RETURN(TRUE);
194 }
195
196 if (! my_hash_inited(&thd->handler_tables_hash))
197 {
198 /*
199 HASH entries are of type TABLE_LIST.
200 */
201 if (my_hash_init(&thd->handler_tables_hash, &my_charset_latin1,
202 HANDLER_TABLES_HASH_SIZE, 0, 0,
203 (my_hash_get_key) mysql_ha_hash_get_key,
204 (my_hash_free_key) mysql_ha_hash_free, 0))
205 {
206 DBUG_PRINT("exit",("ERROR"));
207 DBUG_RETURN(TRUE);
208 }
209 }
210 else
211 {
212 /*
213 Otherwise we might have handler with the same name already.
214
215 Note that it is safe to disclose this information before doing privilege
216 check. Current user can always find out that handler is open by using
217 HANDLER ... READ command, which doesn't requires any privileges.
218 */
219 if (my_hash_search(&thd->handler_tables_hash, (uchar*) tables->alias,
220 strlen(tables->alias) + 1))
221 {
222 DBUG_PRINT("info",("duplicate '%s'", tables->alias));
223 DBUG_PRINT("exit",("ERROR"));
224 my_error(ER_NONUNIQ_TABLE, MYF(0), tables->alias);
225 DBUG_RETURN(TRUE);
226 }
227 }
228
229 /* copy the TABLE_LIST struct */
230 dblen= strlen(tables->db) + 1;
231 namelen= strlen(tables->table_name) + 1;
232 aliaslen= strlen(tables->alias) + 1;
233 if (!(my_multi_malloc(MYF(MY_WME),
234 &hash_tables, (uint) sizeof(*hash_tables),
235 &db, (uint) dblen,
236 &name, (uint) namelen,
237 &alias, (uint) aliaslen,
238 NullS)))
239 {
240 DBUG_PRINT("exit",("ERROR"));
241 DBUG_RETURN(TRUE);
242 }
243 /* structure copy */
244 *hash_tables= *tables;
245 hash_tables->db= db;
246 hash_tables->table_name= name;
247 hash_tables->alias= alias;
248 memcpy(hash_tables->db, tables->db, dblen);
249 memcpy(hash_tables->table_name, tables->table_name, namelen);
250 memcpy(hash_tables->alias, tables->alias, aliaslen);
251 /*
252 We can't request lock with explicit duration for this table
253 right from the start as open_tables() can't handle properly
254 back-off for such locks.
255 */
256 hash_tables->mdl_request.init(MDL_key::TABLE, db, name, MDL_SHARED,
257 MDL_TRANSACTION);
258 /* for now HANDLER can be used only for real TABLES */
259 hash_tables->required_type= FRMTYPE_TABLE;
260
261 /* add to hash */
262 if (my_hash_insert(&thd->handler_tables_hash, (uchar*) hash_tables))
263 {
264 my_free(hash_tables);
265 DBUG_PRINT("exit",("ERROR"));
266 DBUG_RETURN(TRUE);
267 }
268
269 if (open_temporary_tables(thd, hash_tables) ||
270 check_table_access(thd, SELECT_ACL, hash_tables, FALSE, UINT_MAX,
271 FALSE) ||
272 mysql_ha_open_table(thd, hash_tables))
273
274 {
275 my_hash_delete(&thd->handler_tables_hash, (uchar*) hash_tables);
276 DBUG_PRINT("exit",("ERROR"));
277 DBUG_RETURN(TRUE);
278 }
279
280 my_ok(thd);
281
282 DBUG_PRINT("exit",("OK"));
283 DBUG_RETURN(FALSE);
284 }
285
286
287 /**
288 Auxiliary function which opens or re-opens table for HANDLER statements.
289
290 @param thd Thread context..
291 @param hash_tables Table list element for table to open.
292
293 @retval FALSE - Success.
294 @retval TRUE - Failure.
295 */
296
mysql_ha_open_table(THD * thd,TABLE_LIST * hash_tables)297 static bool mysql_ha_open_table(THD *thd, TABLE_LIST *hash_tables)
298 {
299 TABLE *backup_open_tables;
300 MDL_savepoint mdl_savepoint;
301 uint counter;
302 bool error;
303
304 DBUG_ENTER("mysql_ha_open_table");
305
306 DBUG_ASSERT(!thd->locked_tables_mode);
307
308 /*
309 Save and reset the open_tables list so that open_tables() won't
310 be able to access (or know about) the previous list. And on return
311 from open_tables(), thd->open_tables will contain only the opened
312 table.
313
314 See open_table() back-off comments for more details.
315 */
316 backup_open_tables= thd->open_tables;
317 thd->set_open_tables(NULL);
318 mdl_savepoint= thd->mdl_context.mdl_savepoint();
319
320 /*
321 'hash_tables->table' must be NULL, unless there is pre-opened
322 temporary table. open_tables() will set it if successful.
323 */
324 DBUG_ASSERT(! hash_tables->table || is_temporary_table(hash_tables));
325
326 error= open_tables(thd, &hash_tables, &counter, 0);
327
328 if (! error &&
329 ! (hash_tables->table->file->ha_table_flags() & HA_CAN_SQL_HANDLER))
330 {
331 my_error(ER_ILLEGAL_HA, MYF(0), hash_tables->alias);
332 error= TRUE;
333 }
334 if (!error &&
335 hash_tables->mdl_request.ticket &&
336 thd->mdl_context.has_lock(mdl_savepoint,
337 hash_tables->mdl_request.ticket))
338 {
339 /* The ticket returned is within a savepoint. Make a copy. */
340 error= thd->mdl_context.clone_ticket(&hash_tables->mdl_request);
341 hash_tables->table->mdl_ticket= hash_tables->mdl_request.ticket;
342 }
343 if (error)
344 {
345 /*
346 No need to rollback statement transaction, it's not started.
347 If called for re-open, no need to rollback either,
348 it will be done at statement end.
349 */
350 DBUG_ASSERT(thd->transaction.stmt.is_empty());
351 close_thread_tables(thd);
352 thd->mdl_context.rollback_to_savepoint(mdl_savepoint);
353 thd->set_open_tables(backup_open_tables);
354 hash_tables->table= NULL;
355 /* Safety, cleanup the pointer to satisfy MDL assertions. */
356 hash_tables->mdl_request.ticket= NULL;
357 DBUG_PRINT("exit",("ERROR"));
358 DBUG_RETURN(TRUE);
359 }
360 thd->set_open_tables(backup_open_tables);
361 if (hash_tables->mdl_request.ticket)
362 {
363 thd->mdl_context.set_lock_duration(hash_tables->mdl_request.ticket,
364 MDL_EXPLICIT);
365 thd->mdl_context.set_needs_thr_lock_abort(TRUE);
366 }
367
368 /*
369 Assert that the above check prevents opening of views and merge tables.
370 For temporary tables, TABLE::next can be set even if only one table
371 was opened for HANDLER as it is used to link them together
372 (see thd->temporary_tables).
373 */
374 DBUG_ASSERT(hash_tables->table->next == NULL ||
375 hash_tables->table->s->tmp_table);
376 /*
377 If it's a temp table, don't reset table->query_id as the table is
378 being used by this handler. For non-temp tables we use this flag
379 in asserts.
380 */
381 hash_tables->table->open_by_handler= 1;
382
383 DBUG_PRINT("exit",("OK"));
384 DBUG_RETURN(FALSE);
385 }
386
387
388 /**
389 Execute a HANDLER CLOSE statement.
390
391 @param thd The current thread.
392
393 @note Closes the table that is associated (on the handler tables hash)
394 with the name (TABLE_LIST::alias) of the specified table.
395
396 @retval FALSE on success.
397 @retval TRUE on failure.
398 */
399
execute(THD * thd)400 bool Sql_cmd_handler_close::execute(THD *thd)
401 {
402 TABLE_LIST *tables= (TABLE_LIST*) thd->lex->select_lex.table_list.first;
403 TABLE_LIST *hash_tables;
404 DBUG_ENTER("Sql_cmd_handler_close::execute");
405 DBUG_PRINT("enter",("'%s'.'%s' as '%s'",
406 tables->db, tables->table_name, tables->alias));
407
408 if (thd->locked_tables_mode)
409 {
410 my_error(ER_LOCK_OR_ACTIVE_TRANSACTION, MYF(0));
411 DBUG_RETURN(TRUE);
412 }
413 if ((hash_tables= (TABLE_LIST*) my_hash_search(&thd->handler_tables_hash,
414 (uchar*) tables->alias,
415 strlen(tables->alias) + 1)))
416 {
417 mysql_ha_close_table(thd, hash_tables);
418 my_hash_delete(&thd->handler_tables_hash, (uchar*) hash_tables);
419 }
420 else
421 {
422 my_error(ER_UNKNOWN_TABLE, MYF(0), tables->alias, "HANDLER");
423 DBUG_PRINT("exit",("ERROR"));
424 DBUG_RETURN(TRUE);
425 }
426
427 /*
428 Mark MDL_context as no longer breaking protocol if we have
429 closed last HANDLER.
430 */
431 if (! thd->handler_tables_hash.records)
432 thd->mdl_context.set_needs_thr_lock_abort(FALSE);
433
434 my_ok(thd);
435 DBUG_PRINT("exit", ("OK"));
436 DBUG_RETURN(FALSE);
437 }
438
439
440 /**
441 A helper class to process an error from mysql_lock_tables().
442 HANDLER READ statement's attempt to lock the subject table
443 may get aborted if there is a pending DDL. In that case
444 we close the table, reopen it, and try to read again.
445 This is implicit and obscure, since HANDLER position
446 is lost in the process, but it's the legacy server
447 behaviour we should preserve.
448 */
449
450 class Sql_handler_lock_error_handler: public Internal_error_handler
451 {
452 public:
453 virtual
454 bool handle_condition(THD *thd,
455 uint sql_errno,
456 const char *sqlstate,
457 Sql_condition::enum_warning_level level,
458 const char* msg,
459 Sql_condition **cond_hdl);
460
need_reopen() const461 bool need_reopen() const { return m_need_reopen; };
init()462 void init() { m_need_reopen= FALSE; };
463 private:
464 bool m_need_reopen;
465 };
466
467
468 /**
469 Handle an error from mysql_lock_tables().
470 Ignore ER_LOCK_ABORTED errors.
471 */
472
473 bool
474 Sql_handler_lock_error_handler::
handle_condition(THD * thd,uint sql_errno,const char * sqlstate,Sql_condition::enum_warning_level level,const char * msg,Sql_condition ** cond_hdl)475 handle_condition(THD *thd,
476 uint sql_errno,
477 const char *sqlstate,
478 Sql_condition::enum_warning_level level,
479 const char* msg,
480 Sql_condition **cond_hdl)
481 {
482 *cond_hdl= NULL;
483 if (sql_errno == ER_LOCK_ABORTED)
484 m_need_reopen= TRUE;
485
486 return m_need_reopen;
487 }
488
489
490 /**
491 Execute a HANDLER READ statement.
492
493 @param thd The current thread.
494
495 @note Closes the table that is associated (on the handler tables hash)
496 with the name (TABLE_LIST::alias) of the specified table.
497
498 @retval FALSE on success.
499 @retval TRUE on failure.
500 */
501
execute(THD * thd)502 bool Sql_cmd_handler_read::execute(THD *thd)
503 {
504 TABLE_LIST *hash_tables;
505 TABLE *table, *backup_open_tables;
506 MYSQL_LOCK *lock;
507 List<Item> list;
508 Protocol *protocol= thd->protocol;
509 char buff[MAX_FIELD_WIDTH];
510 String buffer(buff, sizeof(buff), system_charset_info);
511 int error, keyno= -1;
512 uint num_rows;
513 uchar *UNINIT_VAR(key);
514 uint UNINIT_VAR(key_len);
515 Sql_handler_lock_error_handler sql_handler_lock_error;
516 LEX *lex= thd->lex;
517 SELECT_LEX *select_lex= &lex->select_lex;
518 SELECT_LEX_UNIT *unit= &lex->unit;
519 TABLE_LIST *tables= select_lex->table_list.first;
520 enum enum_ha_read_modes mode= m_read_mode;
521 Item *cond= select_lex->where;
522 ha_rows select_limit_cnt, offset_limit_cnt;
523 DBUG_ENTER("Sql_cmd_handler_read::execute");
524 DBUG_PRINT("enter",("'%s'.'%s' as '%s'",
525 tables->db, tables->table_name, tables->alias));
526
527 if (thd->locked_tables_mode)
528 {
529 my_error(ER_LOCK_OR_ACTIVE_TRANSACTION, MYF(0));
530 DBUG_RETURN(TRUE);
531 }
532
533 /* Accessing data in XA_IDLE or XA_PREPARED is not allowed. */
534 enum xa_states xa_state= thd->transaction.xid_state.xa_state;
535 if (tables && (xa_state == XA_IDLE || xa_state == XA_PREPARED))
536 {
537 my_error(ER_XAER_RMFAIL, MYF(0), xa_state_names[xa_state]);
538 DBUG_RETURN(true);
539 }
540
541 /*
542 There is no need to check for table permissions here, because
543 if a user has no permissions to read a table, he won't be
544 able to open it (with SQLCOM_HA_OPEN) in the first place.
545 */
546
547 /* Get limit counters from SELECT_LEX. */
548 unit->set_limit(select_lex);
549 select_limit_cnt= unit->select_limit_cnt;
550 offset_limit_cnt= unit->offset_limit_cnt;
551
552 select_lex->context.resolve_in_table_list_only(tables);
553 list.push_front(new Item_field(&select_lex->context,
554 NULL, NULL, "*"));
555 List_iterator<Item> it(list);
556 it++;
557
558 retry:
559 if ((hash_tables= (TABLE_LIST*) my_hash_search(&thd->handler_tables_hash,
560 (uchar*) tables->alias,
561 strlen(tables->alias) + 1)))
562 {
563 table= hash_tables->table;
564 DBUG_PRINT("info-in-hash",("'%s'.'%s' as '%s' table: 0x%lx",
565 hash_tables->db, hash_tables->table_name,
566 hash_tables->alias, (long) table));
567 if (!table)
568 {
569 /*
570 The handler table has been closed. Re-open it.
571 */
572 if (mysql_ha_open_table(thd, hash_tables))
573 {
574 DBUG_PRINT("exit",("reopen failed"));
575 goto err0;
576 }
577
578 table= hash_tables->table;
579 DBUG_PRINT("info",("re-opened '%s'.'%s' as '%s' tab %p",
580 hash_tables->db, hash_tables->table_name,
581 hash_tables->alias, table));
582 }
583 }
584 else
585 table= NULL;
586
587 if (!table)
588 {
589 my_error(ER_UNKNOWN_TABLE, MYF(0), tables->alias, "HANDLER");
590 goto err0;
591 }
592
593 /* save open_tables state */
594 backup_open_tables= thd->open_tables;
595 /* Always a one-element list, see mysql_ha_open(). */
596 DBUG_ASSERT(hash_tables->table->next == NULL ||
597 hash_tables->table->s->tmp_table);
598 /*
599 mysql_lock_tables() needs thd->open_tables to be set correctly to
600 be able to handle aborts properly.
601 */
602 thd->set_open_tables(hash_tables->table);
603
604
605 sql_handler_lock_error.init();
606 thd->push_internal_handler(&sql_handler_lock_error);
607
608 lock= mysql_lock_tables(thd, &thd->open_tables, 1, 0);
609
610 thd->pop_internal_handler();
611 /*
612 In 5.1 and earlier, mysql_lock_tables() could replace the TABLE
613 object with another one (reopen it). This is no longer the case
614 with new MDL.
615 */
616 DBUG_ASSERT(hash_tables->table == thd->open_tables);
617 /* Restore previous context. */
618 thd->set_open_tables(backup_open_tables);
619
620 if (sql_handler_lock_error.need_reopen())
621 {
622 DBUG_ASSERT(!lock && !thd->is_error());
623 /*
624 Always close statement transaction explicitly,
625 so that the engine doesn't have to count locks.
626 There should be no need to perform transaction
627 rollback due to deadlock.
628 */
629 DBUG_ASSERT(! thd->transaction_rollback_request);
630 trans_rollback_stmt(thd);
631 mysql_ha_close_table(thd, hash_tables);
632 goto retry;
633 }
634
635 if (!lock)
636 goto err0; // mysql_lock_tables() printed error message already
637
638 // Always read all columns
639 hash_tables->table->read_set= &hash_tables->table->s->all_set;
640 tables->table= hash_tables->table;
641
642 if (cond)
643 {
644 if (table->query_id != thd->query_id)
645 cond->cleanup(); // File was reopened
646 if ((!cond->fixed &&
647 cond->fix_fields(thd, &cond)) || cond->check_cols(1))
648 goto err;
649 }
650
651 if (m_key_name)
652 {
653 keyno= find_type((char*) m_key_name,
654 &table->s->keynames,
655 FIND_TYPE_NO_PREFIX) - 1;
656 if (keyno < 0)
657 {
658 my_error(ER_KEY_DOES_NOT_EXITS, MYF(0), m_key_name, tables->alias);
659 goto err;
660 }
661 /* Check if the same index involved. */
662 if ((uint) keyno != table->file->get_index())
663 {
664 if (mode == RNEXT)
665 mode= RFIRST;
666 else if (mode == RPREV)
667 mode= RLAST;
668 }
669 }
670
671 if (insert_fields(thd, &select_lex->context,
672 tables->db, tables->alias, &it, 0))
673 goto err;
674
675 protocol->send_result_set_metadata(&list, Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF);
676
677 /*
678 In ::external_lock InnoDB resets the fields which tell it that
679 the handle is used in the HANDLER interface. Tell it again that
680 we are using it for HANDLER.
681 */
682
683 table->file->init_table_handle_for_HANDLER();
684
685 for (num_rows=0; num_rows < select_limit_cnt; )
686 {
687 switch (mode) {
688 case RNEXT:
689 if (m_key_name)
690 {
691 if (table->file->inited == handler::INDEX)
692 {
693 /* Check if we read from the same index. */
694 DBUG_ASSERT((uint) keyno == table->file->get_index());
695 error= table->file->ha_index_next(table->record[0]);
696 break;
697 }
698 }
699 else if (table->file->inited == handler::RND)
700 {
701 error= table->file->ha_rnd_next(table->record[0]);
702 break;
703 }
704 // fallthrough
705 /*
706 Fall through to HANDLER ... READ ... FIRST case if we are trying
707 to read next row in index order after starting reading rows in
708 natural order, or, vice versa, trying to read next row in natural
709 order after reading previous rows in index order.
710 */
711 case RFIRST:
712 if (m_key_name)
713 {
714 if (!(error= table->file->ha_index_or_rnd_end()) &&
715 !(error= table->file->ha_index_init(keyno, 1)))
716 error= table->file->ha_index_first(table->record[0]);
717 }
718 else
719 {
720 if (!(error= table->file->ha_index_or_rnd_end()) &&
721 !(error= table->file->ha_rnd_init(1)))
722 error= table->file->ha_rnd_next(table->record[0]);
723 }
724 mode=RNEXT;
725 break;
726 case RPREV:
727 DBUG_ASSERT(m_key_name != 0);
728 /* Check if we read from the same index. */
729 DBUG_ASSERT((uint) keyno == table->file->get_index());
730 if (table->file->inited == handler::INDEX)
731 {
732 error= table->file->ha_index_prev(table->record[0]);
733 break;
734 }
735 // fallthrough
736 // for more info, see comment before 'case RFIRST'.
737 case RLAST:
738 DBUG_ASSERT(m_key_name != 0);
739 if (!(error= table->file->ha_index_or_rnd_end()) &&
740 !(error= table->file->ha_index_init(keyno, 1)))
741 error= table->file->ha_index_last(table->record[0]);
742 mode=RPREV;
743 break;
744 case RNEXT_SAME:
745 /* Continue scan on "(keypart1,keypart2,...)=(c1, c2, ...) */
746 DBUG_ASSERT(table->file->inited == handler::INDEX);
747 error= table->file->ha_index_next_same(table->record[0], key, key_len);
748 break;
749 case RKEY:
750 {
751 DBUG_ASSERT(m_key_name != 0);
752 KEY *keyinfo=table->key_info+keyno;
753 KEY_PART_INFO *key_part=keyinfo->key_part;
754 if (m_key_expr->elements > keyinfo->user_defined_key_parts)
755 {
756 my_error(ER_TOO_MANY_KEY_PARTS, MYF(0), keyinfo->user_defined_key_parts);
757 goto err;
758 }
759 List_iterator<Item> it_ke(*m_key_expr);
760 Item *item;
761 key_part_map keypart_map;
762 for (keypart_map= key_len=0 ; (item=it_ke++) ; key_part++)
763 {
764 my_bitmap_map *old_map;
765 // 'item' can be changed by fix_fields() call
766 if ((!item->fixed &&
767 item->fix_fields(thd, it_ke.ref())) ||
768 (item= *it_ke.ref())->check_cols(1))
769 goto err;
770 if (item->used_tables() & ~RAND_TABLE_BIT)
771 {
772 my_error(ER_WRONG_ARGUMENTS,MYF(0),"HANDLER ... READ");
773 goto err;
774 }
775 old_map= dbug_tmp_use_all_columns(table, table->write_set);
776 (void) item->save_in_field(key_part->field, 1);
777 dbug_tmp_restore_column_map(table->write_set, old_map);
778 key_len+=key_part->store_length;
779 keypart_map= (keypart_map << 1) | 1;
780 }
781
782 if (!(key= (uchar*) thd->calloc(ALIGN_SIZE(key_len))))
783 goto err;
784 if ((error= table->file->ha_index_or_rnd_end()))
785 break;
786 key_copy(key, table->record[0], table->key_info + keyno, key_len);
787 if (!(error= table->file->ha_index_init(keyno, 1)))
788 error= table->file->ha_index_read_map(table->record[0],
789 key, keypart_map, m_rkey_mode);
790 mode=rkey_to_rnext[(int)m_rkey_mode];
791 break;
792 }
793 default:
794 my_message(ER_ILLEGAL_HA, ER(ER_ILLEGAL_HA), MYF(0));
795 goto err;
796 }
797
798 if (error)
799 {
800 if (error == HA_ERR_RECORD_DELETED)
801 continue;
802 if (error != HA_ERR_KEY_NOT_FOUND && error != HA_ERR_END_OF_FILE)
803 {
804 sql_print_error("mysql_ha_read: Got error %d when reading table '%s'",
805 error, tables->table_name);
806 table->file->print_error(error,MYF(0));
807 goto err;
808 }
809 goto ok;
810 }
811 if (cond && !cond->val_int())
812 {
813 if (thd->is_error())
814 goto err;
815 continue;
816 }
817 if (num_rows >= offset_limit_cnt)
818 {
819 protocol->prepare_for_resend();
820
821 if (protocol->send_result_set_row(&list))
822 goto err;
823
824 protocol->write();
825 }
826 num_rows++;
827 }
828 ok:
829 /*
830 Always close statement transaction explicitly,
831 so that the engine doesn't have to count locks.
832 */
833 trans_commit_stmt(thd);
834 mysql_unlock_tables(thd,lock);
835 my_eof(thd);
836 DBUG_PRINT("exit",("OK"));
837 DBUG_RETURN(FALSE);
838
839 err:
840 trans_rollback_stmt(thd);
841 mysql_unlock_tables(thd,lock);
842 err0:
843 DBUG_PRINT("exit",("ERROR"));
844 DBUG_RETURN(TRUE);
845 }
846
847
848 /**
849 Scan the handler tables hash for matching tables.
850
851 @param thd Thread identifier.
852 @param tables The list of tables to remove.
853
854 @return Pointer to head of linked list (TABLE_LIST::next_local) of matching
855 TABLE_LIST elements from handler_tables_hash. Otherwise, NULL if no
856 table was matched.
857 */
858
mysql_ha_find(THD * thd,TABLE_LIST * tables)859 static TABLE_LIST *mysql_ha_find(THD *thd, TABLE_LIST *tables)
860 {
861 TABLE_LIST *hash_tables, *head= NULL, *first= tables;
862 DBUG_ENTER("mysql_ha_find");
863
864 /* search for all handlers with matching table names */
865 for (uint i= 0; i < thd->handler_tables_hash.records; i++)
866 {
867 hash_tables= (TABLE_LIST*) my_hash_element(&thd->handler_tables_hash, i);
868 for (tables= first; tables; tables= tables->next_local)
869 {
870 if (tables->is_anonymous_derived_table())
871 continue;
872 if ((! *tables->get_db_name() ||
873 ! my_strcasecmp(&my_charset_latin1,
874 hash_tables->get_db_name(),
875 tables->get_db_name())) &&
876 ! my_strcasecmp(&my_charset_latin1,
877 hash_tables->get_table_name(),
878 tables->get_table_name()))
879 break;
880 }
881 if (tables)
882 {
883 hash_tables->next_local= head;
884 head= hash_tables;
885 }
886 }
887
888 DBUG_RETURN(head);
889 }
890
891
892 /**
893 Remove matching tables from the HANDLER's hash table.
894
895 @param thd Thread identifier.
896 @param tables The list of tables to remove.
897
898 @note Broadcasts refresh if it closed a table with old version.
899 */
900
mysql_ha_rm_tables(THD * thd,TABLE_LIST * tables)901 void mysql_ha_rm_tables(THD *thd, TABLE_LIST *tables)
902 {
903 TABLE_LIST *hash_tables, *next;
904 DBUG_ENTER("mysql_ha_rm_tables");
905
906 DBUG_ASSERT(tables);
907
908 hash_tables= mysql_ha_find(thd, tables);
909
910 while (hash_tables)
911 {
912 next= hash_tables->next_local;
913 if (hash_tables->table)
914 mysql_ha_close_table(thd, hash_tables);
915 my_hash_delete(&thd->handler_tables_hash, (uchar*) hash_tables);
916 hash_tables= next;
917 }
918
919 /*
920 Mark MDL_context as no longer breaking protocol if we have
921 closed last HANDLER.
922 */
923 if (! thd->handler_tables_hash.records)
924 thd->mdl_context.set_needs_thr_lock_abort(FALSE);
925
926 DBUG_VOID_RETURN;
927 }
928
929
930 /**
931 Close cursors of matching tables from the HANDLER's hash table.
932
933 @param thd Thread identifier.
934 @param tables The list of tables to flush.
935 */
936
mysql_ha_flush_tables(THD * thd,TABLE_LIST * all_tables)937 void mysql_ha_flush_tables(THD *thd, TABLE_LIST *all_tables)
938 {
939 DBUG_ENTER("mysql_ha_flush_tables");
940
941 for (TABLE_LIST *table_list= all_tables; table_list;
942 table_list= table_list->next_global)
943 {
944 TABLE_LIST *hash_tables= mysql_ha_find(thd, table_list);
945 /* Close all aliases of the same table. */
946 while (hash_tables)
947 {
948 TABLE_LIST *next_local= hash_tables->next_local;
949 if (hash_tables->table)
950 mysql_ha_close_table(thd, hash_tables);
951 hash_tables= next_local;
952 }
953 }
954
955 DBUG_VOID_RETURN;
956 }
957
958
959 /**
960 Flush (close and mark for re-open) all tables that should be should
961 be reopen.
962
963 @param thd Thread identifier.
964
965 @note Broadcasts refresh if it closed a table with old version.
966 */
967
mysql_ha_flush(THD * thd)968 void mysql_ha_flush(THD *thd)
969 {
970 TABLE_LIST *hash_tables;
971 DBUG_ENTER("mysql_ha_flush");
972
973 mysql_mutex_assert_not_owner(&LOCK_open);
974
975 /*
976 Don't try to flush open HANDLERs when we're working with
977 system tables. The main MDL context is backed up and we can't
978 properly release HANDLER locks stored there.
979 */
980 if (thd->state_flags & Open_tables_state::BACKUPS_AVAIL)
981 DBUG_VOID_RETURN;
982
983 for (uint i= 0; i < thd->handler_tables_hash.records; i++)
984 {
985 hash_tables= (TABLE_LIST*) my_hash_element(&thd->handler_tables_hash, i);
986 /*
987 TABLE::mdl_ticket is 0 for temporary tables so we need extra check.
988 */
989 if (hash_tables->table &&
990 ((hash_tables->table->mdl_ticket &&
991 hash_tables->table->mdl_ticket->has_pending_conflicting_lock()) ||
992 (!hash_tables->table->s->tmp_table &&
993 hash_tables->table->s->has_old_version())))
994 mysql_ha_close_table(thd, hash_tables);
995 }
996
997 DBUG_VOID_RETURN;
998 }
999
1000
1001 /**
1002 Remove temporary tables from the HANDLER's hash table. The reason
1003 for having a separate function, rather than calling
1004 mysql_ha_rm_tables() is that it is not always feasible (e.g. in
1005 close_temporary_tables) to obtain a TABLE_LIST containing the
1006 temporary tables.
1007
1008 @See close_temporary_tables
1009 @param thd Thread identifier.
1010 */
mysql_ha_rm_temporary_tables(THD * thd)1011 void mysql_ha_rm_temporary_tables(THD *thd)
1012 {
1013 DBUG_ENTER("mysql_ha_rm_temporary_tables");
1014
1015 TABLE_LIST *tmp_handler_tables= NULL;
1016 for (uint i= 0; i < thd->handler_tables_hash.records; i++)
1017 {
1018 TABLE_LIST *handler_table= reinterpret_cast<TABLE_LIST*>
1019 (my_hash_element(&thd->handler_tables_hash, i));
1020
1021 if (handler_table->table && handler_table->table->s->tmp_table)
1022 {
1023 handler_table->next_local= tmp_handler_tables;
1024 tmp_handler_tables= handler_table;
1025 }
1026 }
1027
1028 while (tmp_handler_tables)
1029 {
1030 TABLE_LIST *nl= tmp_handler_tables->next_local;
1031 mysql_ha_close_table(thd, tmp_handler_tables);
1032 my_hash_delete(&thd->handler_tables_hash, (uchar*) tmp_handler_tables);
1033 tmp_handler_tables= nl;
1034 }
1035
1036 /*
1037 Mark MDL_context as no longer breaking protocol if we have
1038 closed last HANDLER.
1039 */
1040 if (thd->handler_tables_hash.records == 0)
1041 {
1042 thd->mdl_context.set_needs_thr_lock_abort(FALSE);
1043 }
1044 DBUG_VOID_RETURN;
1045 }
1046
1047
1048 /**
1049 Close all HANDLER's tables.
1050
1051 @param thd Thread identifier.
1052
1053 @note Broadcasts refresh if it closed a table with old version.
1054 */
1055
mysql_ha_cleanup(THD * thd)1056 void mysql_ha_cleanup(THD *thd)
1057 {
1058 TABLE_LIST *hash_tables;
1059 DBUG_ENTER("mysql_ha_cleanup");
1060
1061 for (uint i= 0; i < thd->handler_tables_hash.records; i++)
1062 {
1063 hash_tables= (TABLE_LIST*) my_hash_element(&thd->handler_tables_hash, i);
1064 if (hash_tables->table)
1065 mysql_ha_close_table(thd, hash_tables);
1066 }
1067
1068 my_hash_free(&thd->handler_tables_hash);
1069
1070 DBUG_VOID_RETURN;
1071 }
1072
1073
1074 /**
1075 Set explicit duration for metadata locks corresponding to open HANDLERs
1076 to protect them from being released at the end of transaction.
1077
1078 @param thd Thread identifier.
1079 */
1080
mysql_ha_set_explicit_lock_duration(THD * thd)1081 void mysql_ha_set_explicit_lock_duration(THD *thd)
1082 {
1083 TABLE_LIST *hash_tables;
1084 DBUG_ENTER("mysql_ha_set_explicit_lock_duration");
1085
1086 for (uint i= 0; i < thd->handler_tables_hash.records; i++)
1087 {
1088 hash_tables= (TABLE_LIST*) my_hash_element(&thd->handler_tables_hash, i);
1089 if (hash_tables->table && hash_tables->table->mdl_ticket)
1090 thd->mdl_context.set_lock_duration(hash_tables->table->mdl_ticket,
1091 MDL_EXPLICIT);
1092 }
1093 DBUG_VOID_RETURN;
1094 }
1095
1096