~drizzle-trunk/drizzle/development

« back to all changes in this revision

Viewing changes to drizzled/sql_handler.cc

  • Committer: Brian Aker
  • Date: 2008-12-19 07:02:38 UTC
  • Revision ID: brian@tangent.org-20081219070238-569uxp3vsr6r37v1
Updated/fix to foreign key test.

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