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