~drizzle-trunk/drizzle/development

« back to all changes in this revision

Viewing changes to sql/sql_handler.cc

  • Committer: brian
  • Date: 2008-06-25 05:29:13 UTC
  • Revision ID: brian@localhost.localdomain-20080625052913-6upwo0jsrl4lnapl
clean slate

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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.
 
5
 
 
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.
 
10
 
 
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 */
 
14
 
 
15
 
 
16
/* HANDLER ... commands - direct access to ISAM */
 
17
 
 
18
/* TODO:
 
19
  HANDLER blabla OPEN [ AS foobar ] [ (column-list) ]
 
20
 
 
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.
 
24
 
 
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; }
 
30
 
 
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...
 
33
*/
 
34
 
 
35
/*
 
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.
 
57
*/
 
58
 
 
59
#include "mysql_priv.h"
 
60
#include "sql_select.h"
 
61
#include <assert.h>
 
62
 
 
63
#define HANDLER_TABLES_HASH_SIZE 120
 
64
 
 
65
/**
 
66
  Close a HANDLER table.
 
67
 
 
68
  @param thd Thread identifier.
 
69
  @param tables A list of tables with the first entry to close.
 
70
  @param is_locked If LOCK_open is locked.
 
71
 
 
72
  @note Though this function takes a list of tables, only the first list entry
 
73
  will be closed.
 
74
  @note Broadcasts refresh if it closed a table with old version.
 
75
*/
 
76
 
 
77
static void mysql_ha_close_table(THD *thd, TABLE_LIST *tables,
 
78
                                 bool is_locked)
 
79
{
 
80
  TABLE **table_ptr;
 
81
 
 
82
  /*
 
83
    Though we could take the table pointer from hash_tables->table,
 
84
    we must follow the thd->handler_tables chain anyway, as we need the
 
85
    address of the 'next' pointer referencing this table
 
86
    for close_thread_table().
 
87
  */
 
88
  for (table_ptr= &(thd->handler_tables);
 
89
       *table_ptr && (*table_ptr != tables->table);
 
90
         table_ptr= &(*table_ptr)->next)
 
91
    ;
 
92
 
 
93
  if (*table_ptr)
 
94
  {
 
95
    (*table_ptr)->file->ha_index_or_rnd_end();
 
96
    if (! is_locked)
 
97
      VOID(pthread_mutex_lock(&LOCK_open));
 
98
    if (close_thread_table(thd, table_ptr))
 
99
    {
 
100
      /* Tell threads waiting for refresh that something has happened */
 
101
      broadcast_refresh();
 
102
    }
 
103
    if (! is_locked)
 
104
      VOID(pthread_mutex_unlock(&LOCK_open));
 
105
  }
 
106
  else if (tables->table)
 
107
  {
 
108
    /* Must be a temporary table */
 
109
    TABLE *table= tables->table;
 
110
    table->file->ha_index_or_rnd_end();
 
111
    table->query_id= thd->query_id;
 
112
    table->open_by_handler= 0;
 
113
  }
 
114
}
 
115
 
 
116
 
 
117
/*
 
118
  Close a HANDLER table by alias or table name
 
119
 
 
120
  SYNOPSIS
 
121
    mysql_ha_close()
 
122
    thd                         Thread identifier.
 
123
    tables                      A list of tables with the first entry to close.
 
124
 
 
125
  DESCRIPTION
 
126
    Closes the table that is associated (on the handler tables hash) with the
 
127
    name (table->alias) of the specified table.
 
128
 
 
129
  RETURN
 
130
    FALSE ok
 
131
    TRUE  error
 
132
*/
 
133
 
 
134
bool mysql_ha_close(THD *thd, TABLE_LIST *tables)
 
135
{
 
136
  TABLE_LIST    *hash_tables;
 
137
  DBUG_ENTER("mysql_ha_close");
 
138
  DBUG_PRINT("enter",("'%s'.'%s' as '%s'",
 
139
                      tables->db, tables->table_name, tables->alias));
 
140
 
 
141
  if ((hash_tables= (TABLE_LIST*) hash_search(&thd->handler_tables_hash,
 
142
                                              (uchar*) tables->alias,
 
143
                                              strlen(tables->alias) + 1)))
 
144
  {
 
145
    mysql_ha_close_table(thd, hash_tables, FALSE);
 
146
    hash_delete(&thd->handler_tables_hash, (uchar*) hash_tables);
 
147
  }
 
148
  else
 
149
  {
 
150
    my_error(ER_UNKNOWN_TABLE, MYF(0), tables->alias, "HANDLER");
 
151
    DBUG_PRINT("exit",("ERROR"));
 
152
    DBUG_RETURN(TRUE);
 
153
  }
 
154
 
 
155
  my_ok(thd);
 
156
  DBUG_PRINT("exit", ("OK"));
 
157
  DBUG_RETURN(FALSE);
 
158
}
 
159
 
 
160
 
 
161
/**
 
162
  Scan the handler tables hash for matching tables.
 
163
 
 
164
  @param thd Thread identifier.
 
165
  @param tables The list of tables to remove.
 
166
 
 
167
  @return Pointer to head of linked list (TABLE_LIST::next_local) of matching
 
168
          TABLE_LIST elements from handler_tables_hash. Otherwise, NULL if no
 
169
          table was matched.
 
170
*/
 
171
 
 
172
static TABLE_LIST *mysql_ha_find(THD *thd, TABLE_LIST *tables)
 
173
{
 
174
  TABLE_LIST *hash_tables, *head= NULL, *first= tables;
 
175
  DBUG_ENTER("mysql_ha_find");
 
176
 
 
177
  /* search for all handlers with matching table names */
 
178
  for (uint i= 0; i < thd->handler_tables_hash.records; i++)
 
179
  {
 
180
    hash_tables= (TABLE_LIST*) hash_element(&thd->handler_tables_hash, i);
 
181
    for (tables= first; tables; tables= tables->next_local)
 
182
    {
 
183
      if ((! *tables->db ||
 
184
          ! my_strcasecmp(&my_charset_latin1, hash_tables->db, tables->db)) &&
 
185
          ! my_strcasecmp(&my_charset_latin1, hash_tables->table_name,
 
186
                          tables->table_name))
 
187
        break;
 
188
    }
 
189
    if (tables)
 
190
    {
 
191
      hash_tables->next_local= head;
 
192
      head= hash_tables;
 
193
    }
 
194
  }
 
195
 
 
196
  DBUG_RETURN(head);
 
197
}
 
198
 
 
199
 
 
200
/**
 
201
  Remove matching tables from the HANDLER's hash table.
 
202
 
 
203
  @param thd Thread identifier.
 
204
  @param tables The list of tables to remove.
 
205
  @param is_locked If LOCK_open is locked.
 
206
 
 
207
  @note Broadcasts refresh if it closed a table with old version.
 
208
*/
 
209
 
 
210
void mysql_ha_rm_tables(THD *thd, TABLE_LIST *tables, bool is_locked)
 
211
{
 
212
  TABLE_LIST *hash_tables, *next;
 
213
  DBUG_ENTER("mysql_ha_rm_tables");
 
214
 
 
215
  DBUG_ASSERT(tables);
 
216
 
 
217
  hash_tables= mysql_ha_find(thd, tables);
 
218
 
 
219
  while (hash_tables)
 
220
  {
 
221
    next= hash_tables->next_local;
 
222
    if (hash_tables->table)
 
223
      mysql_ha_close_table(thd, hash_tables, is_locked);
 
224
    hash_delete(&thd->handler_tables_hash, (uchar*) hash_tables);
 
225
    hash_tables= next;
 
226
  }
 
227
 
 
228
  DBUG_VOID_RETURN;
 
229
}
 
230
 
 
231
 
 
232
/**
 
233
  Flush (close and mark for re-open) all tables that should be should
 
234
  be reopen.
 
235
 
 
236
  @param thd Thread identifier.
 
237
 
 
238
  @note Broadcasts refresh if it closed a table with old version.
 
239
*/
 
240
 
 
241
void mysql_ha_flush(THD *thd)
 
242
{
 
243
  TABLE_LIST *hash_tables;
 
244
  DBUG_ENTER("mysql_ha_flush");
 
245
 
 
246
  safe_mutex_assert_owner(&LOCK_open);
 
247
 
 
248
  for (uint i= 0; i < thd->handler_tables_hash.records; i++)
 
249
  {
 
250
    hash_tables= (TABLE_LIST*) hash_element(&thd->handler_tables_hash, i);
 
251
    if (hash_tables->table && hash_tables->table->needs_reopen_or_name_lock())
 
252
    {
 
253
      mysql_ha_close_table(thd, hash_tables, TRUE);
 
254
      /* Mark table as closed, ready for re-open. */
 
255
      hash_tables->table= NULL;
 
256
    }
 
257
  }
 
258
 
 
259
  DBUG_VOID_RETURN;
 
260
}
 
261
 
 
262
 
 
263
/**
 
264
  Close all HANDLER's tables.
 
265
 
 
266
  @param thd Thread identifier.
 
267
 
 
268
  @note Broadcasts refresh if it closed a table with old version.
 
269
*/
 
270
 
 
271
void mysql_ha_cleanup(THD *thd)
 
272
{
 
273
  TABLE_LIST *hash_tables;
 
274
  DBUG_ENTER("mysql_ha_cleanup");
 
275
 
 
276
  for (uint i= 0; i < thd->handler_tables_hash.records; i++)
 
277
  {
 
278
    hash_tables= (TABLE_LIST*) hash_element(&thd->handler_tables_hash, i);
 
279
    if (hash_tables->table)
 
280
      mysql_ha_close_table(thd, hash_tables, FALSE);
 
281
   }
 
282
 
 
283
  hash_free(&thd->handler_tables_hash);
 
284
 
 
285
  DBUG_VOID_RETURN;
 
286
}