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