MySQL 5.6.14 Source Code Document
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
table_cache.cc
1 /* Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
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 as published by
5  the Free Software Foundation; version 2 of the License.
6 
7  This program is distributed in the hope that it will be useful,
8  but WITHOUT ANY WARRANTY; without even the implied warranty of
9  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10  GNU General Public License for more details.
11 
12  You should have received a copy of the GNU General Public License
13  along with this program; if not, write to the Free Software
14  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
15 
16 #include "table_cache.h"
17 #include "sql_test.h" // lock_descriptions[]
18 
19 
23 Table_cache_manager table_cache_manager;
24 
25 
26 #ifdef HAVE_PSI_INTERFACE
27 PSI_mutex_key Table_cache::m_lock_key;
28 PSI_mutex_info Table_cache::m_mutex_keys[]= {
29  { &m_lock_key, "LOCK_table_cache", 0}
30 };
31 #endif
32 
33 
34 extern "C" uchar *table_cache_key(const uchar *record,
35  size_t *length,
36  my_bool not_used __attribute__((unused)))
37 {
38  TABLE_SHARE *share= ((Table_cache_element*)record)->get_share();
39  *length= share->table_cache_key.length;
40  return (uchar*) share->table_cache_key.str;
41 }
42 
43 
44 extern "C" void table_cache_free_entry(Table_cache_element *element)
45 {
46  delete element;
47 }
48 
49 
58 {
59  mysql_mutex_init(m_lock_key, &m_lock, MY_MUTEX_INIT_FAST);
60  m_unused_tables= NULL;
61  m_table_count= 0;
62 
63  if (my_hash_init(&m_cache, &my_charset_bin,
64  table_cache_size_per_instance, 0, 0,
65  table_cache_key, (my_hash_free_key) table_cache_free_entry,
66  0))
67  {
68  mysql_mutex_destroy(&m_lock);
69  return true;
70  }
71  return false;
72 }
73 
74 
78 {
79  my_hash_free(&m_cache);
80  mysql_mutex_destroy(&m_lock);
81 }
82 
83 
87 {
88 #ifdef HAVE_PSI_INTERFACE
89  mysql_mutex_register("sql", m_mutex_keys, array_elements(m_mutex_keys));
90 #endif
91 }
92 
93 
94 #ifdef EXTRA_DEBUG
95 void Table_cache::check_unused()
96 {
97  uint count= 0;
98 
99  if (m_unused_tables != NULL)
100  {
101  TABLE *cur_link= m_unused_tables;
102  TABLE *start_link= m_unused_tables;
103  do
104  {
105  if (cur_link != cur_link->next->prev || cur_link != cur_link->prev->next)
106  {
107  DBUG_PRINT("error",("Unused_links aren't linked properly"));
108  return;
109  }
110  } while (count++ < m_table_count &&
111  (cur_link= cur_link->next) != start_link);
112  if (cur_link != start_link)
113  DBUG_PRINT("error",("Unused_links aren't connected"));
114  }
115 
116  for (uint idx= 0; idx < m_cache.records; idx++)
117  {
119  (Table_cache_element*) my_hash_element(&m_cache, idx);
120 
121  Table_cache_element::TABLE_list::Iterator it(el->free_tables);
122  TABLE *entry;
123  while ((entry= it++))
124  {
125  /* We must not have TABLEs in the free list that have their file closed. */
126  DBUG_ASSERT(entry->db_stat && entry->file);
127  /* Merge children should be detached from a merge parent */
128  DBUG_ASSERT(! entry->file->extra(HA_EXTRA_IS_ATTACHED_CHILDREN));
129 
130  if (entry->in_use)
131  DBUG_PRINT("error",("Used table is in share's list of unused tables"));
132  count--;
133  }
134  it.init(el->used_tables);
135  while ((entry= it++))
136  {
137  if (!entry->in_use)
138  DBUG_PRINT("error",("Unused table is in share's list of used tables"));
139  }
140  }
141 
142  if (count != 0)
143  DBUG_PRINT("error",("Unused_links doesn't match open_cache: diff: %d",
144  count));
145 }
146 #endif
147 
148 
152 {
153  assert_owner();
154 
155  while (m_unused_tables)
156  {
157  TABLE *table_to_free= m_unused_tables;
158  remove_table(table_to_free);
159  intern_close_table(table_to_free);
160  }
161 }
162 
163 
164 #ifndef DBUG_OFF
165 
170 {
171  uint unused= 0;
172  uint count=0;
173 
174  compile_time_assert(TL_WRITE_ONLY+1 == array_elements(lock_descriptions));
175 
176  for (uint idx= 0; idx < m_cache.records; idx++)
177  {
179  (Table_cache_element*) my_hash_element(&m_cache, idx);
180 
181  Table_cache_element::TABLE_list::Iterator it(el->used_tables);
182  TABLE *entry;
183  while ((entry= it++))
184  {
185  printf("%-14.14s %-32s%6ld%8ld%6d %s\n",
186  entry->s->db.str, entry->s->table_name.str, entry->s->version,
187  entry->in_use->thread_id, entry->db_stat ? 1 : 0,
188  lock_descriptions[(int)entry->reginfo.lock_type]);
189  }
190  it.init(el->free_tables);
191  while ((entry= it++))
192  {
193  unused++;
194  printf("%-14.14s %-32s%6ld%8ld%6d %s\n",
195  entry->s->db.str, entry->s->table_name.str, entry->s->version,
196  0L, entry->db_stat ? 1 : 0, "Not in use");
197  }
198  }
199 
200  if (m_unused_tables != NULL)
201  {
202  TABLE *start_link= m_unused_tables;
203  TABLE *lnk= m_unused_tables;
204  do
205  {
206  if (lnk != lnk->next->prev || lnk != lnk->prev->next)
207  {
208  printf("unused_links isn't linked properly\n");
209  return;
210  }
211  } while (count++ < m_table_count && (lnk= lnk->next) != start_link);
212  if (lnk != start_link)
213  printf("Unused_links aren't connected\n");
214  }
215 
216  if (count != unused)
217  printf("Unused_links (%d) doesn't match table_def_cache: %d\n", count,
218  unused);
219 }
220 #endif
221 
222 
231 {
233  for (uint i= 0; i < table_cache_instances; i++)
234  {
235  if (m_table_cache[i].init())
236  {
237  for (uint j= 0; j < i; j++)
238  m_table_cache[i].destroy();
239  return true;
240  }
241  }
242 
243  return false;
244 }
245 
246 
250 {
251  for (uint i= 0; i < table_cache_instances; i++)
252  m_table_cache[i].destroy();
253 }
254 
255 
264 {
265  uint result= 0;
266 
267  for (uint i= 0; i < table_cache_instances; i++)
268  result+= m_table_cache[i].cached_tables();
269 
270  return result;
271 }
272 
273 
280 {
281  for (uint i= 0; i < table_cache_instances; i++)
282  m_table_cache[i].lock();
283 
285 }
286 
287 
294 {
296 
297  for (uint i= 0; i < table_cache_instances; i++)
298  m_table_cache[i].unlock();
299 }
300 
301 
307 {
308  for (uint i= 0; i < table_cache_instances; i++)
309  m_table_cache[i].assert_owner();
310 }
311 
312 
319 {
321 
323 }
324 
325 
338  enum_tdc_remove_table_type remove_type,
339  TABLE_SHARE *share)
340 {
342 
344 
345  /*
346  Freeing last TABLE instance for the share will destroy the share
347  and corresponding TABLE_SHARE::cache_element[] array. To make
348  iteration over this array safe, even when share is destroyed in
349  the middle of iteration, we create copy of this array on the stack
350  and iterate over it.
351  */
352  memcpy(&cache_el, share->cache_element,
353  table_cache_instances * sizeof(Table_cache_element *));
354 
355  for (uint i= 0; i < table_cache_instances; i++)
356  {
357  if (cache_el[i])
358  {
359  Table_cache_element::TABLE_list::Iterator it(cache_el[i]->free_tables);
360  TABLE *table;
361 
362 #ifndef DBUG_OFF
363  if (remove_type == TDC_RT_REMOVE_ALL)
364  DBUG_ASSERT(cache_el[i]->used_tables.is_empty());
365  else if (remove_type == TDC_RT_REMOVE_NOT_OWN ||
366  remove_type == TDC_RT_REMOVE_NOT_OWN_KEEP_SHARE)
367  {
368  Table_cache_element::TABLE_list::Iterator it2(cache_el[i]->used_tables);
369  while ((table= it2++))
370  {
371  if (table->in_use != thd)
372  DBUG_ASSERT(0);
373  }
374  }
375 #endif
376 
377  while ((table= it++))
378  {
379  m_table_cache[i].remove_table(table);
380  intern_close_table(table);
381  }
382  }
383  }
384 }
385 
386 
390 {
392 
393  for (uint i= 0; i < table_cache_instances; i++)
394  m_table_cache[i].free_all_unused_tables();
395 }
396 
397 
398 #ifndef DBUG_OFF
399 
404 {
405  puts("DB Table Version Thread Open Lock");
406 
407  for (uint i= 0; i < table_cache_instances; i++)
408  m_table_cache[i].print_tables();
409 }
410 #endif
411