MySQL 5.6.14 Source Code Document
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
DbtupVarAlloc.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 #define DBTUP_C
19 #define DBTUP_VAR_ALLOC_CPP
20 #include "Dbtup.hpp"
21 
22 void Dbtup::init_list_sizes(void)
23 {
24  c_min_list_size[0]= 200;
25  c_max_list_size[0]= 499;
26 
27  c_min_list_size[1]= 500;
28  c_max_list_size[1]= 999;
29 
30  c_min_list_size[2]= 1000;
31  c_max_list_size[2]= 4079;
32 
33  c_min_list_size[3]= 4080;
34  c_max_list_size[3]= 8159;
35 
36  c_min_list_size[4]= 0;
37  c_max_list_size[4]= 199;
38 }
39 
40 /*
41  Allocator for variable sized segments
42  Part of the external interface for variable sized segments
43 
44  This method is used to allocate and free variable sized tuples and
45  parts of tuples. This part can be used to implement variable sized
46  attributes without wasting memory. It can be used to support small
47  BLOB's attached to the record. It can also be used to support adding
48  and dropping attributes without the need to copy the entire table.
49 
50  SYNOPSIS
51  fragPtr A pointer to the fragment description
52  tabPtr A pointer to the table description
53  alloc_size Size of the allocated record
54  signal The signal object to be used if a signal needs to
55  be sent
56  RETURN VALUES
57  Returns true if allocation was successful otherwise false
58 
59  page_offset Page offset of allocated record
60  page_index Page index of allocated record
61  page_ptr The i and p value of the page where the record was
62  allocated
63 */
64 Uint32* Dbtup::alloc_var_rec(Uint32 * err,
65  Fragrecord* fragPtr,
66  Tablerec* tabPtr,
67  Uint32 alloc_size,
68  Local_key* key,
69  Uint32 * out_frag_page_id)
70 {
74  Uint32 *ptr = alloc_fix_rec(err, fragPtr, tabPtr, key, out_frag_page_id);
75  if (unlikely(ptr == 0))
76  {
77  return 0;
78  }
79 
80  Local_key varref;
81  Tuple_header* tuple = (Tuple_header*)ptr;
82  Var_part_ref* dst = tuple->get_var_part_ref_ptr(tabPtr);
83  if (alloc_size)
84  {
85  if (likely(alloc_var_part(err, fragPtr, tabPtr, alloc_size, &varref) != 0))
86  {
87  dst->assign(&varref);
88  return ptr;
89  }
90  }
91  else
92  {
93  varref.m_page_no = RNIL;
94  dst->assign(&varref);
95  return ptr;
96  }
97 
98  PagePtr pagePtr;
99  c_page_pool.getPtr(pagePtr, key->m_page_no);
100  free_fix_rec(fragPtr, tabPtr, key, (Fix_page*)pagePtr.p);
101  return 0;
102 }
103 
104 Uint32*
105 Dbtup::alloc_var_part(Uint32 * err,
106  Fragrecord* fragPtr,
107  Tablerec* tabPtr,
108  Uint32 alloc_size,
109  Local_key* key)
110 {
111  PagePtr pagePtr;
112  pagePtr.i= get_alloc_page(fragPtr, (alloc_size + 1));
113  if (pagePtr.i == RNIL) {
114  jam();
115  if ((pagePtr.i= get_empty_var_page(fragPtr)) == RNIL) {
116  jam();
117  * err = ZMEM_NOMEM_ERROR;
118  return 0;
119  }
120  c_page_pool.getPtr(pagePtr);
121  ((Var_page*)pagePtr.p)->init();
122  pagePtr.p->list_index = MAX_FREE_LIST - 1;
123  LocalDLList<Page> list(c_page_pool,
124  fragPtr->free_var_page_array[MAX_FREE_LIST-1]);
125  list.add(pagePtr);
126  } else {
127  c_page_pool.getPtr(pagePtr);
128  jam();
129  }
130  Uint32 idx= ((Var_page*)pagePtr.p)
131  ->alloc_record(alloc_size, (Var_page*)ctemp_page, Var_page::CHAIN);
132 
133  key->m_page_no = pagePtr.i;
134  key->m_page_idx = idx;
135 
136  update_free_page_list(fragPtr, pagePtr);
137  return ((Var_page*)pagePtr.p)->get_ptr(idx);
138 }
139 
140 /*
141  free_var_part is used to free the variable length storage associated
142  with the passed local key.
143  It is not assumed that there is a corresponding fixed-length part.
144  // TODO : Any need for tabPtr?
145 */
146 void Dbtup::free_var_part(Fragrecord* fragPtr,
147  Tablerec* tabPtr,
148  Local_key* key)
149 {
150  Ptr<Page> pagePtr;
151  if (key->m_page_no != RNIL)
152  {
153  c_page_pool.getPtr(pagePtr, key->m_page_no);
154  ((Var_page*)pagePtr.p)->free_record(key->m_page_idx, Var_page::CHAIN);
155 
156  ndbassert(pagePtr.p->free_space <= Var_page::DATA_WORDS);
157  if (pagePtr.p->free_space == Var_page::DATA_WORDS - 1)
158  {
159  jam();
160  Uint32 idx = pagePtr.p->list_index;
161  LocalDLList<Page> list(c_page_pool, fragPtr->free_var_page_array[idx]);
162  list.remove(pagePtr);
163  returnCommonArea(pagePtr.i, 1);
164  fragPtr->noOfVarPages --;
165  } else {
166  jam();
167  update_free_page_list(fragPtr, pagePtr);
168  }
169  }
170  return;
171 }
172 
173 /*
174  Deallocator for variable sized segments
175  Part of the external interface for variable sized segments
176 
177  SYNOPSIS
178  fragPtr A pointer to the fragment description
179  tabPtr A pointer to the table description
180  signal The signal object to be used if a signal needs to
181  be sent
182  page_ptr A reference to the page of the variable sized
183  segment
184  free_page_index Page index on page of variable sized segment
185  which is freed
186  RETURN VALUES
187  Returns true if deallocation was successful otherwise false
188 */
189 void Dbtup::free_var_rec(Fragrecord* fragPtr,
190  Tablerec* tabPtr,
191  Local_key* key,
192  Ptr<Page> pagePtr)
193 {
197  Uint32 *ptr = ((Fix_page*)pagePtr.p)->get_ptr(key->m_page_idx, 0);
198  Tuple_header* tuple = (Tuple_header*)ptr;
199 
200  Local_key ref;
201  Var_part_ref * varref = tuple->get_var_part_ref_ptr(tabPtr);
202  varref->copyout(&ref);
203 
204  free_fix_rec(fragPtr, tabPtr, key, (Fix_page*)pagePtr.p);
205 
206  if (ref.m_page_no != RNIL)
207  {
208  jam();
209  c_page_pool.getPtr(pagePtr, ref.m_page_no);
210  free_var_part(fragPtr, pagePtr, ref.m_page_idx);
211  }
212  return;
213 }
214 
215 void
216 Dbtup::free_var_part(Fragrecord* fragPtr, PagePtr pagePtr, Uint32 page_idx)
217 {
218  ((Var_page*)pagePtr.p)->free_record(page_idx, Var_page::CHAIN);
219 
220  ndbassert(pagePtr.p->free_space <= Var_page::DATA_WORDS);
221  if (pagePtr.p->free_space == Var_page::DATA_WORDS - 1)
222  {
223  jam();
224  Uint32 idx = pagePtr.p->list_index;
225  LocalDLList<Page> list(c_page_pool, fragPtr->free_var_page_array[idx]);
226  list.remove(pagePtr);
227  returnCommonArea(pagePtr.i, 1);
228  fragPtr->noOfVarPages --;
229  }
230  else
231  {
232  jam();
233  update_free_page_list(fragPtr, pagePtr);
234  }
235 }
236 
237 Uint32 *
238 Dbtup::realloc_var_part(Uint32 * err,
239  Fragrecord* fragPtr, Tablerec* tabPtr, PagePtr pagePtr,
240  Var_part_ref* refptr, Uint32 oldsz, Uint32 newsz)
241 {
242  Uint32 add = newsz - oldsz;
243  Uint32 *new_var_ptr;
244  Var_page* pageP = (Var_page*)pagePtr.p;
245  Local_key oldref;
246  refptr->copyout(&oldref);
247 
248  ndbassert(newsz);
249  ndbassert(add);
250 
251  if (oldsz && pageP->free_space >= add)
252  {
253  jam();
254  new_var_ptr= pageP->get_ptr(oldref.m_page_idx);
255  if(!pageP->is_space_behind_entry(oldref.m_page_idx, add))
256  {
257  if(0) printf("extra reorg");
258  jam();
266  Uint32* copyBuffer= cinBuffer;
267  memcpy(copyBuffer, new_var_ptr, 4*oldsz);
268  pageP->set_entry_len(oldref.m_page_idx, 0);
269  pageP->free_space += oldsz;
270  pageP->reorg((Var_page*)ctemp_page);
271  new_var_ptr= pageP->get_free_space_ptr();
272  memcpy(new_var_ptr, copyBuffer, 4*oldsz);
273  pageP->set_entry_offset(oldref.m_page_idx, pageP->insert_pos);
274  add += oldsz;
275  }
276  pageP->grow_entry(oldref.m_page_idx, add);
277  update_free_page_list(fragPtr, pagePtr);
278  }
279  else
280  {
281  jam();
282  Local_key newref;
283  new_var_ptr = alloc_var_part(err, fragPtr, tabPtr, newsz, &newref);
284  if (unlikely(new_var_ptr == 0))
285  return NULL;
286 
287  if (oldsz)
288  {
289  jam();
290  Uint32 *src = pageP->get_ptr(oldref.m_page_idx);
291  ndbassert(oldref.m_page_no != newref.m_page_no);
292  ndbassert(pageP->get_entry_len(oldref.m_page_idx) == oldsz);
293  memcpy(new_var_ptr, src, 4*oldsz);
294  free_var_part(fragPtr, pagePtr, oldref.m_page_idx);
295  }
296 
297  refptr->assign(&newref);
298  }
299 
300  return new_var_ptr;
301 }
302 
303 void
304 Dbtup::move_var_part(Fragrecord* fragPtr, Tablerec* tabPtr, PagePtr pagePtr,
305  Var_part_ref* refptr, Uint32 size)
306 {
307  jam();
308 
309  ndbassert(size);
310  Var_page* pageP = (Var_page*)pagePtr.p;
311  Local_key oldref;
312  refptr->copyout(&oldref);
313 
317  Uint32 new_index = calculate_free_list_impl(size);
318 
323  if (new_index > pageP->list_index)
324  {
325  jam();
326  return;
327  }
328 
329  PagePtr new_pagePtr;
330  new_pagePtr.i = get_alloc_page(fragPtr, size + 1);
331 
332  if (new_pagePtr.i == RNIL)
333  {
334  jam();
335  return;
336  }
337 
341  if (new_pagePtr.i == pagePtr.i)
342  {
343  jam();
344  return;
345  }
346 
347  c_page_pool.getPtr(new_pagePtr);
348 
349  Uint32 idx= ((Var_page*)new_pagePtr.p)
350  ->alloc_record(size,(Var_page*)ctemp_page, Var_page::CHAIN);
351 
355  update_free_page_list(fragPtr, new_pagePtr);
356 
357  Uint32 *dst = ((Var_page*)new_pagePtr.p)->get_ptr(idx);
358  const Uint32 *src = pageP->get_ptr(oldref.m_page_idx);
359 
363  memcpy(dst, src, 4*size);
364 
368  free_var_part(fragPtr, pagePtr, oldref.m_page_idx);
369 
373  Local_key newref;
374  newref.m_page_no = new_pagePtr.i;
375  newref.m_page_idx = idx;
376  refptr->assign(&newref);
377 }
378 
379 /* ------------------------------------------------------------------------ */
380 // Get a page from one of free lists. If the desired free list is empty we
381 // try with the next until we have tried all possible lists.
382 /* ------------------------------------------------------------------------ */
383 Uint32
384 Dbtup::get_alloc_page(Fragrecord* fragPtr, Uint32 alloc_size)
385 {
386  Uint32 i, start_index, loop= 0;
387  PagePtr pagePtr;
388 
389  start_index= calculate_free_list_impl(alloc_size);
390  if (start_index == (MAX_FREE_LIST - 1))
391  {
392  jam();
393  }
394  else
395  {
396  jam();
397  ndbrequire(start_index < (MAX_FREE_LIST - 1));
398  start_index++;
399  }
400  for (i= start_index; i < MAX_FREE_LIST; i++)
401  {
402  jam();
403  if (!fragPtr->free_var_page_array[i].isEmpty())
404  {
405  jam();
406  return fragPtr->free_var_page_array[i].firstItem;
407  }
408  }
409  ndbrequire(start_index > 0);
410  i= start_index - 1;
411  LocalDLList<Page> list(c_page_pool, fragPtr->free_var_page_array[i]);
412  for(list.first(pagePtr); !pagePtr.isNull() && loop < 16; )
413  {
414  jam();
415  if (pagePtr.p->free_space >= alloc_size) {
416  jam();
417  return pagePtr.i;
418  }
419  loop++;
420  list.next(pagePtr);
421  }
422  return RNIL;
423 }
424 
425 Uint32
426 Dbtup::get_empty_var_page(Fragrecord* fragPtr)
427 {
428  PagePtr ptr;
429  Uint32 cnt;
430  allocConsPages(1, cnt, ptr.i);
431  fragPtr->noOfVarPages+= cnt;
432  if (unlikely(cnt == 0))
433  {
434  return RNIL;
435  }
436 
437  c_page_pool.getPtr(ptr);
438  ptr.p->physical_page_id = ptr.i;
439  ptr.p->page_state = ~0;
440  ptr.p->nextList = RNIL;
441  ptr.p->prevList = RNIL;
442  ptr.p->frag_page_id = RNIL;
443 
444  return ptr.i;
445 }
446 
447 /* ------------------------------------------------------------------------ */
448 // Check if the page needs to go to a new free page list.
449 /* ------------------------------------------------------------------------ */
450 void Dbtup::update_free_page_list(Fragrecord* fragPtr,
451  Ptr<Page> pagePtr)
452 {
453  Uint32 free_space, list_index;
454  free_space= pagePtr.p->free_space;
455  list_index= pagePtr.p->list_index;
456  if ((free_space < c_min_list_size[list_index]) ||
457  (free_space > c_max_list_size[list_index])) {
458  Uint32 new_list_index= calculate_free_list_impl(free_space);
459 
460  {
465  list(c_page_pool, fragPtr->free_var_page_array[list_index]);
466  list.remove(pagePtr);
467  }
468  if (free_space < c_min_list_size[new_list_index])
469  {
470  /*
471  We have not sufficient amount of free space to put it into any
472  free list. Thus the page will not be available for new inserts.
473  This can only happen for the free list with least guaranteed
474  free space.
475 
476  Put in on MAX_FREE_LIST-list (i.e full pages)
477  */
478  jam();
479  ndbrequire(new_list_index == 0);
480  new_list_index = MAX_FREE_LIST;
481  }
482 
483  {
484  LocalDLList<Page> list(c_page_pool,
485  fragPtr->free_var_page_array[new_list_index]);
486  list.add(pagePtr);
487  pagePtr.p->list_index = new_list_index;
488  }
489  }
490 }
491 
492 /* ------------------------------------------------------------------------ */
493 // Given size of free space, calculate the free list to put it into
494 /* ------------------------------------------------------------------------ */
495 Uint32 Dbtup::calculate_free_list_impl(Uint32 free_space_size) const
496 {
497  Uint32 i;
498  for (i = 0; i < MAX_FREE_LIST; i++) {
499  jam();
500  if (free_space_size <= c_max_list_size[i]) {
501  jam();
502  return i;
503  }
504  }
505  ndbrequire(false);
506  return 0;
507 }
508 
509 Uint64 Dbtup::calculate_used_var_words(Fragrecord* fragPtr)
510 {
511  /* Loop over all VarSize pages in this fragment, summing
512  * their used space
513  */
514  Uint64 totalUsed= 0;
515  for (Uint32 freeList= 0; freeList <= MAX_FREE_LIST; freeList++)
516  {
517  LocalDLList<Page> list(c_page_pool,
518  fragPtr->free_var_page_array[freeList]);
519  Ptr<Page> pagePtr;
520 
521  if (list.first(pagePtr))
522  {
523  do
524  {
525  totalUsed+= (Tup_varsize_page::DATA_WORDS - pagePtr.p->free_space);
526  } while (list.next(pagePtr));
527  };
528  };
529 
530  return totalUsed;
531 }
532 
533 Uint32*
534 Dbtup::alloc_var_rowid(Uint32 * err,
535  Fragrecord* fragPtr,
536  Tablerec* tabPtr,
537  Uint32 alloc_size,
538  Local_key* key,
539  Uint32 * out_frag_page_id)
540 {
541  Uint32 *ptr = alloc_fix_rowid(err, fragPtr, tabPtr, key, out_frag_page_id);
542  if (unlikely(ptr == 0))
543  {
544  return 0;
545  }
546 
547  Local_key varref;
548  Tuple_header* tuple = (Tuple_header*)ptr;
549  Var_part_ref* dst = (Var_part_ref*)tuple->get_var_part_ref_ptr(tabPtr);
550 
551  if (alloc_size)
552  {
553  if (likely(alloc_var_part(err, fragPtr, tabPtr, alloc_size, &varref) != 0))
554  {
555  dst->assign(&varref);
556  return ptr;
557  }
558  }
559  else
560  {
561  varref.m_page_no = RNIL;
562  dst->assign(&varref);
563  return ptr;
564  }
565 
566  PagePtr pagePtr;
567  c_page_pool.getPtr(pagePtr, key->m_page_no);
568  free_fix_rec(fragPtr, tabPtr, key, (Fix_page*)pagePtr.p);
569  return 0;
570 }