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