1
/* Copyright (C) 2000-2004 MySQL AB
2
This program is free software; you can redistribute it and/or modify
3
it under the terms of the GNU General Public License as published by
4
the Free Software Foundation; version 2 of the License.
6
This program is distributed in the hope that it will be useful,
7
but WITHOUT ANY WARRANTY; without even the implied warranty of
8
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9
GNU General Public License for more details.
11
You should have received a copy of the GNU General Public License
12
along with this program; if not, write to the Free Software
13
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
16
/* HANDLER ... commands - direct access to ISAM */
19
HANDLER blabla OPEN [ AS foobar ] [ (column-list) ]
21
the most natural (easiest, fastest) way to do it is to
22
compute List<Item> field_list not in mysql_ha_read
23
but in mysql_ha_open, and then store it in TABLE structure.
25
The problem here is that mysql_parse calls free_item to free all the
26
items allocated at the end of every query. The workaround would to
27
keep two item lists per THD - normal free_list and handler_items.
28
The second is to be freeed only on thread end. mysql_ha_open should
29
then do { handler_items=concat(handler_items, free_list); free_list=0; }
31
But !!! do_command calls free_root at the end of every query and frees up
32
all the sql_alloc'ed memory. It's harder to work around...
36
There are two containers holding information about open handler tables.
37
The first is 'thd->handler_tables'. It is a linked list of TABLE objects.
38
It is used like 'thd->open_tables' in the table cache. The trick is to
39
exchange these two lists during open and lock of tables. Thus the normal
40
table cache code can be used.
41
The second container is a HASH. It holds objects of the type TABLE_LIST.
42
Despite its name, no lists of tables but only single structs are hashed
43
(the 'next' pointer is always NULL). The reason for theis second container
44
is, that we want handler tables to survive FLUSH TABLE commands. A table
45
affected by FLUSH TABLE must be closed so that other threads are not
46
blocked by handler tables still in use. Since we use the normal table cache
47
functions with 'thd->handler_tables', the closed tables are removed from
48
this list. Hence we need the original open information for the handler
49
table in the case that it is used again. This information is handed over
50
to mysql_ha_open() as a TABLE_LIST. So we store this information in the
51
second container, where it is not affected by FLUSH TABLE. The second
52
container is implemented as a hash for performance reasons. Consequently,
53
we use it not only for re-opening a handler table, but also for the
54
HANDLER ... READ commands. For this purpose, we store a pointer to the
55
TABLE structure (in the first container) in the TBALE_LIST object in the
56
second container. When the table is flushed, the pointer is cleared.
59
#include "mysql_priv.h"
60
#include "sql_select.h"
63
#define HANDLER_TABLES_HASH_SIZE 120
65
static enum enum_ha_read_modes rkey_to_rnext[]=
66
{ RNEXT_SAME, RNEXT, RPREV, RNEXT, RPREV, RNEXT, RPREV, RPREV };
69
Get hash key and hash key length.
72
mysql_ha_hash_get_key()
73
tables Pointer to the hash object.
74
key_len_p (out) Pointer to the result for key length.
78
The hash object is an TABLE_LIST struct.
79
The hash key is the alias name.
80
The hash key length is the alias name length plus one for the
81
terminateing NUL character.
84
Pointer to the TABLE_LIST struct.
87
static char *mysql_ha_hash_get_key(TABLE_LIST *tables, size_t *key_len_p,
88
my_bool first __attribute__((unused)))
90
*key_len_p= strlen(tables->alias) + 1 ; /* include '\0' in comparisons */
100
tables Pointer to the hash object.
103
The hash object is an TABLE_LIST struct.
109
static void mysql_ha_hash_free(TABLE_LIST *tables)
111
my_free((char*) tables, MYF(0));
115
Close a HANDLER table.
117
@param thd Thread identifier.
118
@param tables A list of tables with the first entry to close.
119
@param is_locked If LOCK_open is locked.
121
@note Though this function takes a list of tables, only the first list entry
123
@note Broadcasts refresh if it closed a table with old version.
126
static void mysql_ha_close_table(THD *thd, TABLE_LIST *tables,
132
Though we could take the table pointer from hash_tables->table,
133
we must follow the thd->handler_tables chain anyway, as we need the
134
address of the 'next' pointer referencing this table
135
for close_thread_table().
137
for (table_ptr= &(thd->handler_tables);
138
*table_ptr && (*table_ptr != tables->table);
139
table_ptr= &(*table_ptr)->next)
144
(*table_ptr)->file->ha_index_or_rnd_end();
146
VOID(pthread_mutex_lock(&LOCK_open));
147
if (close_thread_table(thd, table_ptr))
149
/* Tell threads waiting for refresh that something has happened */
153
VOID(pthread_mutex_unlock(&LOCK_open));
155
else if (tables->table)
157
/* Must be a temporary table */
158
TABLE *table= tables->table;
159
table->file->ha_index_or_rnd_end();
160
table->query_id= thd->query_id;
161
table->open_by_handler= 0;
164
/* Mark table as closed, ready for re-open if necessary. */
169
Open a HANDLER table.
173
thd Thread identifier.
174
tables A list of tables with the first entry to open.
175
reopen Re-open a previously opened handler table.
178
Though this function takes a list of tables, only the first list entry
180
'reopen' is set when a handler table is to be re-opened. In this case,
181
'tables' is the pointer to the hashed TABLE_LIST object which has been
182
saved on the original open.
183
'reopen' is also used to suppress the sending of an 'ok' message.
190
bool mysql_ha_open(THD *thd, TABLE_LIST *tables, bool reopen)
192
TABLE_LIST *hash_tables = NULL;
193
char *db, *name, *alias;
194
uint dblen, namelen, aliaslen, counter;
196
TABLE *backup_open_tables;
197
DBUG_ENTER("mysql_ha_open");
198
DBUG_PRINT("enter",("'%s'.'%s' as '%s' reopen: %d",
199
tables->db, tables->table_name, tables->alias,
202
if (tables->schema_table)
204
my_error(ER_WRONG_USAGE, MYF(0), "HANDLER OPEN",
205
INFORMATION_SCHEMA_NAME.str);
206
DBUG_PRINT("exit",("ERROR"));
210
if (! hash_inited(&thd->handler_tables_hash))
213
HASH entries are of type TABLE_LIST.
215
if (hash_init(&thd->handler_tables_hash, &my_charset_latin1,
216
HANDLER_TABLES_HASH_SIZE, 0, 0,
217
(hash_get_key) mysql_ha_hash_get_key,
218
(hash_free_key) mysql_ha_hash_free, 0))
221
else if (! reopen) /* Otherwise we have 'tables' already. */
223
if (hash_search(&thd->handler_tables_hash, (uchar*) tables->alias,
224
strlen(tables->alias) + 1))
226
DBUG_PRINT("info",("duplicate '%s'", tables->alias));
227
my_error(ER_NONUNIQ_TABLE, MYF(0), tables->alias);
233
Save and reset the open_tables list so that open_tables() won't
234
be able to access (or know about) the previous list. And on return
235
from open_tables(), thd->open_tables will contain only the opened
238
The thd->handler_tables list is kept as-is to avoid deadlocks if
239
open_table(), called by open_tables(), needs to back-off because
240
of a pending name-lock on the table being opened.
242
See open_table() back-off comments for more details.
244
backup_open_tables= thd->open_tables;
245
thd->open_tables= NULL;
248
open_tables() will set 'tables->table' if successful.
249
It must be NULL for a real open when calling open_tables().
251
DBUG_ASSERT(! tables->table);
253
/* for now HANDLER can be used only for real TABLES */
254
tables->required_type= FRMTYPE_TABLE;
256
We use open_tables() here, rather than, say,
257
open_ltable() or open_table() because we would like to be able
258
to open a temporary table.
260
error= open_tables(thd, &tables, &counter, 0);
261
if (thd->open_tables)
263
if (thd->open_tables->next)
266
We opened something that is more than a single table.
267
This happens with MERGE engine. Don't try to link
268
this mess into thd->handler_tables list, close it
269
and report an error. We must do it right away
270
because mysql_ha_close_table(), called down the road,
271
can close a single table only.
273
close_thread_tables(thd);
274
my_error(ER_ILLEGAL_HA, MYF(0), tables->alias);
279
/* Merge the opened table into handler_tables list. */
280
thd->open_tables->next= thd->handler_tables;
281
thd->handler_tables= thd->open_tables;
285
/* Restore the state. */
286
thd->open_tables= backup_open_tables;
291
/* There can be only one table in '*tables'. */
292
if (! (tables->table->file->ha_table_flags() & HA_CAN_SQL_HANDLER))
294
my_error(ER_ILLEGAL_HA, MYF(0), tables->alias);
300
/* copy the TABLE_LIST struct */
301
dblen= strlen(tables->db) + 1;
302
namelen= strlen(tables->table_name) + 1;
303
aliaslen= strlen(tables->alias) + 1;
304
if (!(my_multi_malloc(MYF(MY_WME),
305
&hash_tables, (uint) sizeof(*hash_tables),
307
&name, (uint) namelen,
308
&alias, (uint) aliaslen,
312
*hash_tables= *tables;
314
hash_tables->table_name= name;
315
hash_tables->alias= alias;
316
memcpy(hash_tables->db, tables->db, dblen);
317
memcpy(hash_tables->table_name, tables->table_name, namelen);
318
memcpy(hash_tables->alias, tables->alias, aliaslen);
321
if (my_hash_insert(&thd->handler_tables_hash, (uchar*) hash_tables))
326
If it's a temp table, don't reset table->query_id as the table is
327
being used by this handler. Otherwise, no meaning at all.
329
tables->table->open_by_handler= 1;
333
DBUG_PRINT("exit",("OK"));
338
my_free((char*) hash_tables, MYF(0));
340
mysql_ha_close_table(thd, tables, FALSE);
341
DBUG_PRINT("exit",("ERROR"));
347
Close a HANDLER table by alias or table name
351
thd Thread identifier.
352
tables A list of tables with the first entry to close.
355
Closes the table that is associated (on the handler tables hash) with the
356
name (table->alias) of the specified table.
363
bool mysql_ha_close(THD *thd, TABLE_LIST *tables)
365
TABLE_LIST *hash_tables;
366
DBUG_ENTER("mysql_ha_close");
367
DBUG_PRINT("enter",("'%s'.'%s' as '%s'",
368
tables->db, tables->table_name, tables->alias));
370
if ((hash_tables= (TABLE_LIST*) hash_search(&thd->handler_tables_hash,
371
(uchar*) tables->alias,
372
strlen(tables->alias) + 1)))
374
mysql_ha_close_table(thd, hash_tables, FALSE);
375
hash_delete(&thd->handler_tables_hash, (uchar*) hash_tables);
379
my_error(ER_UNKNOWN_TABLE, MYF(0), tables->alias, "HANDLER");
380
DBUG_PRINT("exit",("ERROR"));
385
DBUG_PRINT("exit", ("OK"));
391
Read from a HANDLER table.
395
thd Thread identifier.
396
tables A list of tables with the first entry to read.
410
bool mysql_ha_read(THD *thd, TABLE_LIST *tables,
411
enum enum_ha_read_modes mode, char *keyname,
412
List<Item> *key_expr,
413
enum ha_rkey_function ha_rkey_mode, Item *cond,
414
ha_rows select_limit_cnt, ha_rows offset_limit_cnt)
416
TABLE_LIST *hash_tables;
417
TABLE *table, *backup_open_tables;
420
Protocol *protocol= thd->protocol;
421
char buff[MAX_FIELD_WIDTH];
422
String buffer(buff, sizeof(buff), system_charset_info);
423
int error, keyno= -1;
425
uchar *UNINIT_VAR(key);
426
uint UNINIT_VAR(key_len);
428
DBUG_ENTER("mysql_ha_read");
429
DBUG_PRINT("enter",("'%s'.'%s' as '%s'",
430
tables->db, tables->table_name, tables->alias));
432
thd->lex->select_lex.context.resolve_in_table_list_only(tables);
433
list.push_front(new Item_field(&thd->lex->select_lex.context,
435
List_iterator<Item> it(list);
439
if ((hash_tables= (TABLE_LIST*) hash_search(&thd->handler_tables_hash,
440
(uchar*) tables->alias,
441
strlen(tables->alias) + 1)))
443
table= hash_tables->table;
444
DBUG_PRINT("info-in-hash",("'%s'.'%s' as '%s' table: 0x%lx",
445
hash_tables->db, hash_tables->table_name,
446
hash_tables->alias, (long) table));
450
The handler table has been closed. Re-open it.
452
if (mysql_ha_open(thd, hash_tables, 1))
454
DBUG_PRINT("exit",("reopen failed"));
458
table= hash_tables->table;
459
DBUG_PRINT("info",("re-opened '%s'.'%s' as '%s' tab %p",
460
hash_tables->db, hash_tables->table_name,
461
hash_tables->alias, table));
464
#if MYSQL_VERSION_ID < 40100
465
if (*tables->db && strcmp(table->table_cache_key, tables->db))
467
DBUG_PRINT("info",("wrong db"));
477
#if MYSQL_VERSION_ID < 40100
478
char buff[MAX_DBKEY_LENGTH];
480
strxnmov(buff, sizeof(buff)-1, tables->db, ".", tables->table_name,
483
strncpy(buff, tables->alias, sizeof(buff));
484
my_error(ER_UNKNOWN_TABLE, MYF(0), buff, "HANDLER");
486
my_error(ER_UNKNOWN_TABLE, MYF(0), tables->alias, "HANDLER");
492
/* save open_tables state */
493
backup_open_tables= thd->open_tables;
495
mysql_lock_tables() needs thd->open_tables to be set correctly to
496
be able to handle aborts properly. When the abort happens, it's
497
safe to not protect thd->handler_tables because it won't close any
500
thd->open_tables= thd->handler_tables;
502
lock= mysql_lock_tables(thd, &tables->table, 1,
503
MYSQL_LOCK_NOTIFY_IF_NEED_REOPEN, &need_reopen);
505
/* restore previous context */
506
thd->open_tables= backup_open_tables;
510
mysql_ha_close_table(thd, hash_tables, FALSE);
512
The lock might have been aborted, we need to manually reset
513
thd->some_tables_deleted because handler's tables are closed
514
in a non-standard way. Otherwise we might loop indefinitely.
516
thd->some_tables_deleted= 0;
521
goto err0; // mysql_lock_tables() printed error message already
523
// Always read all columns
524
tables->table->read_set= &tables->table->s->all_set;
528
if (table->query_id != thd->query_id)
529
cond->cleanup(); // File was reopened
531
cond->fix_fields(thd, &cond)) || cond->check_cols(1))
537
if ((keyno=find_type(keyname, &table->s->keynames, 1+2)-1)<0)
539
my_error(ER_KEY_DOES_NOT_EXITS, MYF(0), keyname, tables->alias);
544
if (insert_fields(thd, &thd->lex->select_lex.context,
545
tables->db, tables->alias, &it, 0))
548
protocol->send_fields(&list, Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF);
551
In ::external_lock InnoDB resets the fields which tell it that
552
the handle is used in the HANDLER interface. Tell it again that
553
we are using it for HANDLER.
556
table->file->init_table_handle_for_HANDLER();
558
for (num_rows=0; num_rows < select_limit_cnt; )
562
if (table->file->inited != handler::NONE)
565
table->file->index_next(table->record[0]) :
566
table->file->rnd_next(table->record[0]);
569
/* else fall through */
573
table->file->ha_index_or_rnd_end();
574
table->file->ha_index_init(keyno, 1);
575
error= table->file->index_first(table->record[0]);
579
table->file->ha_index_or_rnd_end();
580
if (!(error= table->file->ha_rnd_init(1)))
581
error= table->file->rnd_next(table->record[0]);
586
DBUG_ASSERT(keyname != 0);
587
if (table->file->inited != handler::NONE)
589
error=table->file->index_prev(table->record[0]);
592
/* else fall through */
594
DBUG_ASSERT(keyname != 0);
595
table->file->ha_index_or_rnd_end();
596
table->file->ha_index_init(keyno, 1);
597
error= table->file->index_last(table->record[0]);
601
/* Continue scan on "(keypart1,keypart2,...)=(c1, c2, ...) */
602
DBUG_ASSERT(keyname != 0);
603
error= table->file->index_next_same(table->record[0], key, key_len);
607
DBUG_ASSERT(keyname != 0);
608
KEY *keyinfo=table->key_info+keyno;
609
KEY_PART_INFO *key_part=keyinfo->key_part;
610
if (key_expr->elements > keyinfo->key_parts)
612
my_error(ER_TOO_MANY_KEY_PARTS, MYF(0), keyinfo->key_parts);
615
List_iterator<Item> it_ke(*key_expr);
617
key_part_map keypart_map;
618
for (keypart_map= key_len=0 ; (item=it_ke++) ; key_part++)
620
my_bitmap_map *old_map;
621
// 'item' can be changed by fix_fields() call
623
item->fix_fields(thd, it_ke.ref())) ||
624
(item= *it_ke.ref())->check_cols(1))
626
if (item->used_tables() & ~RAND_TABLE_BIT)
628
my_error(ER_WRONG_ARGUMENTS,MYF(0),"HANDLER ... READ");
631
old_map= dbug_tmp_use_all_columns(table, table->write_set);
632
(void) item->save_in_field(key_part->field, 1);
633
dbug_tmp_restore_column_map(table->write_set, old_map);
634
key_len+=key_part->store_length;
635
keypart_map= (keypart_map << 1) | 1;
638
if (!(key= (uchar*) thd->calloc(ALIGN_SIZE(key_len))))
640
table->file->ha_index_or_rnd_end();
641
table->file->ha_index_init(keyno, 1);
642
key_copy(key, table->record[0], table->key_info + keyno, key_len);
643
error= table->file->index_read_map(table->record[0],
644
key, keypart_map, ha_rkey_mode);
645
mode=rkey_to_rnext[(int)ha_rkey_mode];
649
my_message(ER_ILLEGAL_HA, ER(ER_ILLEGAL_HA), MYF(0));
655
if (error == HA_ERR_RECORD_DELETED)
657
if (error != HA_ERR_KEY_NOT_FOUND && error != HA_ERR_END_OF_FILE)
659
sql_print_error("mysql_ha_read: Got error %d when reading table '%s'",
660
error, tables->table_name);
661
table->file->print_error(error,MYF(0));
666
if (cond && !cond->val_int())
668
if (num_rows >= offset_limit_cnt)
671
protocol->prepare_for_resend();
675
if (item->send(thd->protocol, &buffer))
677
protocol->free(); // Free used
678
my_message(ER_OUT_OF_RESOURCES, ER(ER_OUT_OF_RESOURCES), MYF(0));
687
mysql_unlock_tables(thd,lock);
689
DBUG_PRINT("exit",("OK"));
693
mysql_unlock_tables(thd,lock);
695
DBUG_PRINT("exit",("ERROR"));
701
Scan the handler tables hash for matching tables.
703
@param thd Thread identifier.
704
@param tables The list of tables to remove.
706
@return Pointer to head of linked list (TABLE_LIST::next_local) of matching
707
TABLE_LIST elements from handler_tables_hash. Otherwise, NULL if no
711
static TABLE_LIST *mysql_ha_find(THD *thd, TABLE_LIST *tables)
713
TABLE_LIST *hash_tables, *head= NULL, *first= tables;
714
DBUG_ENTER("mysql_ha_find");
716
/* search for all handlers with matching table names */
717
for (uint i= 0; i < thd->handler_tables_hash.records; i++)
719
hash_tables= (TABLE_LIST*) hash_element(&thd->handler_tables_hash, i);
720
for (tables= first; tables; tables= tables->next_local)
722
if ((! *tables->db ||
723
! my_strcasecmp(&my_charset_latin1, hash_tables->db, tables->db)) &&
724
! my_strcasecmp(&my_charset_latin1, hash_tables->table_name,
730
hash_tables->next_local= head;
740
Remove matching tables from the HANDLER's hash table.
742
@param thd Thread identifier.
743
@param tables The list of tables to remove.
744
@param is_locked If LOCK_open is locked.
746
@note Broadcasts refresh if it closed a table with old version.
749
void mysql_ha_rm_tables(THD *thd, TABLE_LIST *tables, bool is_locked)
751
TABLE_LIST *hash_tables, *next;
752
DBUG_ENTER("mysql_ha_rm_tables");
756
hash_tables= mysql_ha_find(thd, tables);
760
next= hash_tables->next_local;
761
if (hash_tables->table)
762
mysql_ha_close_table(thd, hash_tables, is_locked);
763
hash_delete(&thd->handler_tables_hash, (uchar*) hash_tables);
772
Flush (close and mark for re-open) all tables that should be should
775
@param thd Thread identifier.
777
@note Broadcasts refresh if it closed a table with old version.
780
void mysql_ha_flush(THD *thd)
782
TABLE_LIST *hash_tables;
783
DBUG_ENTER("mysql_ha_flush");
785
safe_mutex_assert_owner(&LOCK_open);
787
for (uint i= 0; i < thd->handler_tables_hash.records; i++)
789
hash_tables= (TABLE_LIST*) hash_element(&thd->handler_tables_hash, i);
790
if (hash_tables->table && hash_tables->table->needs_reopen_or_name_lock())
791
mysql_ha_close_table(thd, hash_tables, TRUE);
799
Close all HANDLER's tables.
801
@param thd Thread identifier.
803
@note Broadcasts refresh if it closed a table with old version.
806
void mysql_ha_cleanup(THD *thd)
808
TABLE_LIST *hash_tables;
809
DBUG_ENTER("mysql_ha_cleanup");
811
for (uint i= 0; i < thd->handler_tables_hash.records; i++)
813
hash_tables= (TABLE_LIST*) hash_element(&thd->handler_tables_hash, i);
814
if (hash_tables->table)
815
mysql_ha_close_table(thd, hash_tables, FALSE);
818
hash_free(&thd->handler_tables_hash);