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