MySQL 5.6.14 Source Code Document
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
tuppage.cpp
1 /*
2  Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
3 
4  This program is free software; you can redistribute it and/or modify
5  it under the terms of the GNU General Public License as published by
6  the Free Software Foundation; version 2 of the License.
7 
8  This program is distributed in the hope that it will be useful,
9  but WITHOUT ANY WARRANTY; without even the implied warranty of
10  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11  GNU General Public License for more details.
12 
13  You should have received a copy of the GNU General Public License
14  along with this program; if not, write to the Free Software
15  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
16 */
17 
18 #include <ndb_global.h>
19 #include "tuppage.hpp"
20 
35 Uint32
37 {
38  assert(free_space);
39  Uint32 page_idx = next_free_index;
40  assert(page_idx + 1 < DATA_WORDS);
41 
42  Uint32 prev = m_data[page_idx] >> 16;
43  Uint32 next = m_data[page_idx] & 0xFFFF;
44 
45  assert(prev == 0xFFFF);
46  assert(m_data[page_idx + 1] == FREE_RECORD);
47 
48  m_data[page_idx + 1] = 0;
49  if (next != 0xFFFF)
50  {
51  assert(free_space > 1);
52  Uint32 nextP = m_data[next];
53  assert((nextP >> 16) == page_idx);
54  m_data[next] = 0xFFFF0000 | (nextP & 0xFFFF);
55  }
56  else
57  {
58  assert(free_space == 1);
59  }
60 
61  next_free_index = next;
62  free_space--;
63  return page_idx;
64 }
65 
66 Uint32
67 Tup_fixsize_page::alloc_record(Uint32 page_idx)
68 {
69  assert(page_idx + 1 < DATA_WORDS);
70  if (likely(free_space && m_data[page_idx + 1] == FREE_RECORD))
71  {
72  Uint32 prev = m_data[page_idx] >> 16;
73  Uint32 next = m_data[page_idx] & 0xFFFF;
74 
75  assert(prev != 0xFFFF || (next_free_index == page_idx));
76  if (prev == 0xFFFF)
77  {
78  next_free_index = next;
79  }
80  else
81  {
82  Uint32 prevP = m_data[prev];
83  m_data[prev] = (prevP & 0xFFFF0000) | next;
84  }
85 
86  if (next != 0xFFFF)
87  {
88  Uint32 nextP = m_data[next];
89  m_data[next] = (prev << 16) | (nextP & 0xFFFF);
90  }
91  free_space --;
92  m_data[page_idx + 1] = 0;
93  return page_idx;
94  }
95  return ~0;
96 }
97 
98 Uint32
99 Tup_fixsize_page::free_record(Uint32 page_idx)
100 {
101  Uint32 next = next_free_index;
102 
103  assert(page_idx + 1 < DATA_WORDS);
104  assert(m_data[page_idx + 1] != FREE_RECORD);
105 
106  if (next == 0xFFFF)
107  {
108  assert(free_space == 0);
109  }
110  else
111  {
112  assert(free_space);
113  assert(next + 1 < DATA_WORDS);
114  Uint32 nextP = m_data[next];
115  assert((nextP >> 16) == 0xFFFF);
116  m_data[next] = (page_idx << 16) | (nextP & 0xFFFF);
117  assert(m_data[next + 1] == FREE_RECORD);
118  }
119 
120  next_free_index = page_idx;
121  m_data[page_idx] = 0xFFFF0000 | next;
122  m_data[page_idx + 1] = FREE_RECORD;
123 
124  return ++free_space;
125 }
126 
127 void
128 Tup_varsize_page::init()
129 {
130  free_space= DATA_WORDS - 1;
131  high_index= 1;
132  insert_pos= 0;
133  next_free_index= END_OF_FREE_LIST;
134  m_page_header.m_page_type = File_formats::PT_Tup_varsize_page;
135 }
136 
137 Uint32
138 Tup_varsize_page::alloc_record(Uint32 page_idx, Uint32 alloc_size,
140 {
141  assert(page_idx); // 0 is not allowed
142  Uint32 free = free_space;
143  Uint32 largest_size= DATA_WORDS - (insert_pos + high_index);
144  Uint32 free_list = next_free_index;
145 
146  if (page_idx < high_index)
147  {
148  Uint32 *ptr = get_index_ptr(page_idx);
149  Uint32 word = *ptr;
150 
151  if (unlikely((free < alloc_size) || ! (word & FREE)))
152  {
153  return ~0;
154  }
155 
156  if (alloc_size >= largest_size)
157  {
158  /*
159  We can't fit this segment between the insert position and the end of
160  the index entries. We will pack the page so that all free space
161  exists between the insert position and the end of the index entries.
162  */
163  reorg(temp);
164  }
165 
166  Uint32 next = (word & NEXT_MASK) >> NEXT_SHIFT;
167  Uint32 prev = (word & PREV_MASK) >> PREV_SHIFT;
168 
169  if (next != END_OF_FREE_LIST)
170  {
171  Uint32 * next_ptr = get_index_ptr(next);
172  Uint32 next_word = * next_ptr;
173  * next_ptr = (next_word & ~PREV_MASK) | (prev << PREV_SHIFT);
174  }
175 
176  if (prev != END_OF_FREE_LIST)
177  {
178  Uint32 * prev_ptr = get_index_ptr(prev);
179  Uint32 prev_word = * prev_ptr;
180  * prev_ptr = (prev_word & ~NEXT_MASK) | (next << NEXT_SHIFT);
181  }
182  else
183  {
184  assert(next_free_index == page_idx);
185  next_free_index = next;
186  }
187 
188  * ptr = insert_pos + (alloc_size << LEN_SHIFT);
189  free -= alloc_size;
190  }
191  else
192  {
196  Uint32 hi = high_index;
197  Uint32 expand = (page_idx + 1 - hi);
198  Uint32 size = alloc_size + expand;
199  if (unlikely(size > free))
200  {
201  return ~0;
202  }
203 
204  if (size >= largest_size)
205  {
206  /*
207  We can't fit this segment between the insert position and the end of
208  the index entries. We will pack the page so that all free space
209  exists between the insert position and the end of the index entries.
210  */
211  reorg(temp);
212  }
213 
214  Uint32 *ptr = m_data + DATA_WORDS - hi;
215  if (page_idx == hi)
216  {
217  * ptr = insert_pos + (alloc_size << LEN_SHIFT);
218  }
219  else
220  {
221  if (free_list != END_OF_FREE_LIST)
222  {
223  Uint32 * prev_ptr = get_index_ptr(free_list);
224  Uint32 prev_word = * prev_ptr;
225  * prev_ptr = (prev_word & ~PREV_MASK) | (hi << PREV_SHIFT);
226  }
227 
228  for (; hi < page_idx;)
229  {
230  * ptr-- = FREE | (free_list << NEXT_SHIFT) | ((hi+1) << PREV_SHIFT);
231  free_list = hi++;
232  }
233 
234  * ptr++ = insert_pos + (alloc_size << LEN_SHIFT);
235  * ptr = ((* ptr) & ~PREV_MASK) | (END_OF_FREE_LIST << PREV_SHIFT);
236 
237  next_free_index = hi - 1;
238  }
239  high_index = hi + 1;
240  free -= size;
241  }
242 
243  free_space = free;
244  insert_pos += alloc_size;
245 
246  return page_idx;
247 }
248 
249 Uint32
251  Tup_varsize_page* temp, Uint32 chain)
252 {
253  assert(free_space >= alloc_size);
254  Uint32 largest_size= DATA_WORDS - (insert_pos + high_index);
255  if (alloc_size >= largest_size) {
256  /*
257  We can't fit this segment between the insert position and the end of
258  the index entries. We will pack the page so that all free space
259  exists between the insert position and the end of the index entries.
260  */
261  reorg(temp);
262  largest_size= DATA_WORDS - (insert_pos + high_index);
263  }
264  assert(largest_size > alloc_size);
265 
266  Uint32 page_idx;
267  if (next_free_index == END_OF_FREE_LIST) {
268  /*
269  We are out of free index slots. We will extend the array of free
270  slots
271  */
272  page_idx= high_index++;
273  free_space--;
274  } else {
275  // Pick an empty slot among the index entries
276  page_idx= next_free_index;
277  assert((get_index_word(page_idx) & FREE) == FREE);
278  assert(((get_index_word(page_idx) & PREV_MASK) >> PREV_SHIFT) ==
279  END_OF_FREE_LIST);
280  next_free_index= (get_index_word(page_idx) & NEXT_MASK) >> NEXT_SHIFT;
281  assert(next_free_index);
282  if (next_free_index != END_OF_FREE_LIST)
283  {
284  Uint32 *ptr = get_index_ptr(next_free_index);
285  Uint32 word = *ptr;
286  * ptr = (word & ~PREV_MASK) | (END_OF_FREE_LIST << PREV_SHIFT);
287  }
288  }
289 
290  assert(chain == 0 || chain == CHAIN);
291  * get_index_ptr(page_idx) = insert_pos + chain + (alloc_size << LEN_SHIFT);
292 
293  insert_pos += alloc_size;
294  free_space -= alloc_size;
295  //ndbout_c("%p->alloc_record(%d%s) -> %d", this,alloc_size, (chain ? " CHAIN" : ""),page_idx);
296  return page_idx;
297 }
298 
299 Uint32
300 Tup_varsize_page::free_record(Uint32 page_idx, Uint32 chain)
301 {
302  //ndbout_c("%p->free_record(%d%s)", this, page_idx, (chain ? " CHAIN": ""));
303  Uint32 *index_ptr= get_index_ptr(page_idx);
304  Uint32 index_word= * index_ptr;
305  Uint32 entry_pos= (index_word & POS_MASK) >> POS_SHIFT;
306  Uint32 entry_len= (index_word & LEN_MASK) >> LEN_SHIFT;
307  assert(chain == 0 || chain == CHAIN);
308  assert((index_word & CHAIN) == chain);
309 #ifdef VM_TRACE
310  memset(m_data + entry_pos, 0xF2, 4*entry_len);
311 #endif
312  if (page_idx + 1 == high_index) {
313  /*
314  We are removing the last in the entry list. We could potentially
315  have several free entries also before this. To take that into account
316  we will rebuild the free list and thus compress it and update the
317  free space accordingly.
318  */
319  rebuild_index(index_ptr);
320  } else {
321  if (next_free_index != END_OF_FREE_LIST)
322  {
323  Uint32 *ptr = get_index_ptr(next_free_index);
324  Uint32 word = *ptr;
325  assert(((word & PREV_MASK) >> PREV_SHIFT) == END_OF_FREE_LIST);
326  * ptr = (word & ~PREV_MASK) | (page_idx << PREV_SHIFT);
327  }
328  * index_ptr= FREE | next_free_index | (END_OF_FREE_LIST << PREV_SHIFT);
329  next_free_index= page_idx;
330  assert(next_free_index);
331  }
332 
333  free_space+= entry_len;
334  // If we're the "last" entry, decrease insert_pos
335  insert_pos -= (entry_pos + entry_len == insert_pos ? entry_len : 0);
336 
337  return free_space;
338 }
339 
340 void
342 {
343  Uint32 empty= 1;
344  Uint32 *end= m_data + DATA_WORDS;
345 
349  for(index_ptr++; index_ptr < end; index_ptr++)
350  if((* index_ptr) & FREE)
351  empty++;
352  else
353  break;
354 
355  if(index_ptr == end)
356  {
357  // Totally free page
358  high_index = 1;
359  free_space += empty;
360  next_free_index = END_OF_FREE_LIST;
361  return;
362  }
363 
364  Uint32 next= END_OF_FREE_LIST;
365  Uint32 dummy;
366  Uint32 *prev_ptr = &dummy;
367  for(index_ptr++; index_ptr < end; index_ptr++)
368  {
369  if ((* index_ptr) & FREE)
370  {
371  * index_ptr= FREE | next;
372  next= Uint32(end - index_ptr);
373  * prev_ptr |= (next << PREV_SHIFT);
374  prev_ptr = index_ptr;
375  }
376  }
377 
378  * prev_ptr |= (END_OF_FREE_LIST << PREV_SHIFT);
379 
380  high_index -= empty;
381  free_space += empty;
382  next_free_index= next;
383  assert(next_free_index);
384 }
385 
386 void
387 Tup_varsize_page::reorg(Tup_varsize_page* copy_page)
388 {
389  Uint32 new_insert_pos= 0;
390  Uint32 old_insert_pos= insert_pos;
391 
392  // Copy key data part of page to a temporary page.
393  memcpy(copy_page->m_data, m_data, 4*old_insert_pos);
394  assert(high_index > 0);
395  Uint32* index_ptr= get_index_ptr(high_index-1);
396  Uint32 *end_of_page= m_data + DATA_WORDS;
397  for (; index_ptr < end_of_page; index_ptr++)
398  {
399  Uint32 index_word= * index_ptr;
400  Uint32 entry_len= (index_word & LEN_MASK) >> LEN_SHIFT;
401  if (!(index_word & FREE) && entry_len)
402  {
403  /*
404  We found an index item that needs to be packed.
405  We will update the index entry and copy the data to the page.
406  */
407  Uint32 entry_pos= (index_word & POS_MASK) >> POS_SHIFT;
408  assert(entry_pos + entry_len <= old_insert_pos);
409  assert(new_insert_pos + entry_len <= old_insert_pos);
410  * index_ptr= (new_insert_pos << POS_SHIFT) + (index_word & ~POS_MASK);
411  memcpy(m_data+new_insert_pos, copy_page->m_data+entry_pos, 4*entry_len);
412 
413  new_insert_pos += entry_len;
414  }
415  }
416  insert_pos= new_insert_pos;
417 }
418 
419 NdbOut&
420 operator<< (NdbOut& out, const Tup_varsize_page& page)
421 {
422  out << "[ Varpage " << &page << ": free: " << page.free_space
423  << " (" << (page.DATA_WORDS - (page.insert_pos + page.high_index + 1)) << ")"
424  << " insert_pos: " << page.insert_pos
425  << " high_index: " << page.high_index
426  << " index: " << flush;
427 
428  const Uint32 *index_ptr= page.m_data+page.DATA_WORDS-1;
429  for(Uint32 i = 1; i<page.high_index; i++, index_ptr--)
430  {
431  out << " [ " << i;
432  if(! (*index_ptr & page.FREE))
433  out << " pos: " << ((* index_ptr & page.POS_MASK) >> page.POS_SHIFT)
434  << " len: " << ((* index_ptr & page.LEN_MASK) >> page.LEN_SHIFT)
435  << ((* index_ptr & page.CHAIN) ? " CHAIN " : " ")
436  << "]" << flush;
437  else
438  out << " FREE ]" << flush;
439  }
440 
441  out << " free list: " << flush;
442  Uint32 next= page.next_free_index;
443  while(next != page.END_OF_FREE_LIST)
444  {
445  out << next << " " << flush;
446  next= ((* (page.m_data+page.DATA_WORDS-next)) & page.NEXT_MASK) >> page.NEXT_SHIFT;
447  }
448  out << "]";
449  return out;
450 }
451 
452 NdbOut&
453 operator<< (NdbOut& out, const Tup_fixsize_page& page)
454 {
455  out << "[ Fixpage " << &page
456  << ": frag_page: " << page.frag_page_id
457  << " page_no: " << page.m_page_no
458  << " file_no: " << page.m_file_no
459  << " table: " << page.m_table_id
460  << " fragment: " << page.m_fragment_id
461  << " uncommitted_used_space: " << page.uncommitted_used_space
462  << " free: " << page.free_space;
463 
464  out << " free list: " << hex << page.next_free_index << " " << flush;
465 #if 0
466  Uint32 startTuple = page.next_free_index >> 16;
467  Uint32 cnt = 0;
468  Uint32 next= startTuple;
469  while((next & 0xFFFF) != 0xFFFF)
470  {
471  cnt++;
472  out << dec << "(" << (next & 0xFFFF) << " " << hex << next << ") " << flush;
473  assert(page.m_data[(next & 0xFFFF) + 1] == Dbtup::Tuple_header::FREE);
474  next= * (page.m_data + ( next & 0xFFFF ));
475  }
476  assert(cnt == page.free_space);
477 #endif
478  out << "]";
479  return out;
480 }