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