MySQL 5.6.14 Source Code Document
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
dynarray-t.cc
1 /* Copyright (c) 2011, 2013 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 Street, Fifth Floor, Boston, MA 02110-1301, USA */
15 
16 // First include (the generated) my_config.h, to get correct platform defines.
17 #include "my_config.h"
18 #include <gtest/gtest.h>
19 
20 #include <algorithm>
21 #include <functional>
22 #include <vector>
23 
24 #include "sql_select.h"
25 #include "mem_root_array.h"
26 
37 /*
38  Rewrite of sort_keyuse() to comparison operator for use by std::less<>
39  It is a template argument, so static rather than in unnamed namespace.
40 */
41 static inline bool operator<(const Key_use &a, const Key_use &b)
42 {
43  if (a.table->tablenr != b.table->tablenr)
44  return a.table->tablenr < b.table->tablenr;
45  if (a.key != b.key)
46  return a.key < b.key;
47  if (a.keypart != b.keypart)
48  return a.keypart < b.keypart;
49  const bool atab = test((a.used_tables & ~OUTER_REF_TABLE_BIT));
50  const bool btab = test((b.used_tables & ~OUTER_REF_TABLE_BIT));
51  if (atab != btab)
52  return atab < btab;
53  return
54  ((a.optimize & KEY_OPTIMIZE_REF_OR_NULL) <
55  (b.optimize & KEY_OPTIMIZE_REF_OR_NULL));
56 }
57 
58 
59 /*
60  Compare for equality.
61  It is a template argument, so static rather than in unnamed namespace.
62 */
63 static inline bool operator==(const Key_use &lhs, const Key_use &rhs)
64 {
65  return
66  lhs.table->tablenr == rhs.table->tablenr &&
67  lhs.key == rhs.key &&
68  lhs.keypart == rhs.keypart &&
69  test((lhs.used_tables & ~OUTER_REF_TABLE_BIT))
70  ==
71  test((rhs.used_tables & ~OUTER_REF_TABLE_BIT)) &&
72  (lhs.optimize & KEY_OPTIMIZE_REF_OR_NULL)
73  ==
74  (rhs.optimize & KEY_OPTIMIZE_REF_OR_NULL);
75 }
76 
77 
78 static inline std::ostream &operator<<(std::ostream &s, const Key_use &v)
79 {
80  return s << "{"
81  << v.table->tablenr << ", "
82  << v.key << ", "
83  << v.keypart << ", "
84  << v.used_tables << ", "
85  << v.optimize
86  << "}"
87  ;
88 }
89 
90 
91 namespace dynarray_unittest {
92 
93 /*
94  Cut'n paste this function from sql_select.cc,
95  to avoid linking in the entire server for this unit test.
96 */
97 inline int sort_keyuse(Key_use *a, Key_use *b)
98 {
99  int res;
100  if (a->table->tablenr != b->table->tablenr)
101  return (int) (a->table->tablenr - b->table->tablenr);
102  if (a->key != b->key)
103  return (int) (a->key - b->key);
104  if (a->keypart != b->keypart)
105  return (int) (a->keypart - b->keypart);
106  // Place const values before other ones
107  if ((res= test((a->used_tables & ~OUTER_REF_TABLE_BIT)) -
108  test((b->used_tables & ~OUTER_REF_TABLE_BIT))))
109  return res;
110  /* Place rows that are not 'OPTIMIZE_REF_OR_NULL' first */
111  return (int) ((a->optimize & KEY_OPTIMIZE_REF_OR_NULL) -
112  (b->optimize & KEY_OPTIMIZE_REF_OR_NULL));
113 }
114 
115 
116 // We generate some random data at startup, for testing of sorting.
117 void generate_test_data(Key_use *keys, TABLE *tables, int n)
118 {
119  int ix;
120  for (ix= 0; ix < n; ++ix)
121  {
122  tables[ix].tablenr= ix % 3;
123  keys[ix]=
124  Key_use(&tables[ix],
125  NULL, // Item *val
126  0, // table_map used_tables
127  ix % 4, // uint key
128  ix % 2, // uint keypart
129  0, // uint optimize
130  0, // keypart_map
131  0, // ha_rows ref_table_rows
132  true, // bool null_rejecting
133  NULL, // bool *cond_guard
134  0 // uint sj_pred_no
135  );
136  }
137  std::random_shuffle(&keys[0], &keys[n]);
138 }
139 
140 
141 // Play around with these constants to see std::sort speedup vs. my_qsort.
142 const int num_elements= 200;
143 const int num_iterations= 1000;
144 
145 /*
146  This class is used for comparing performance of
147  std::vector<> and std::sort()
148  vs
149  DYNAMIC_ARRAY and my_qsort()
150  */
151 class DynArrayTest : public ::testing::Test
152 {
153 public:
154  DynArrayTest() {}
155 
156  static void SetUpTestCase()
157  {
158  generate_test_data(test_data, table_list, num_elements);
159  }
160 
161  virtual void SetUp()
162  {
163  my_init_dynamic_array(&m_keyuse_dyn, sizeof(Key_use), num_elements, 64);
164  m_keyuse_vec.reserve(num_elements);
165  }
166 
167  virtual void TearDown()
168  {
169  delete_dynamic(&m_keyuse_dyn);
170  }
171 
172  void insert_and_sort_dynamic()
173  {
174  reset_dynamic(&m_keyuse_dyn);
175  for (int ix= 0; ix < num_elements; ++ix)
176  {
177  insert_dynamic(&m_keyuse_dyn, &test_data[ix]);
178  }
179  my_qsort(m_keyuse_dyn.buffer, m_keyuse_dyn.elements, sizeof(Key_use),
180  reinterpret_cast<qsort_cmp>(sort_keyuse));
181  }
182 
183  void insert_and_sort_vector()
184  {
185  m_keyuse_vec.clear();
186  for (int ix= 0; ix < num_elements; ++ix)
187  {
188  m_keyuse_vec.push_back(test_data[ix]);
189  }
190  std::sort(m_keyuse_vec.begin(), m_keyuse_vec.end(), std::less<Key_use>());
191  }
192 
193  DYNAMIC_ARRAY m_keyuse_dyn;
194  std::vector<Key_use> m_keyuse_vec;
195 private:
196  static Key_use test_data[num_elements];
197  static TABLE table_list[num_elements];
198 
199  GTEST_DISALLOW_COPY_AND_ASSIGN_(DynArrayTest);
200 };
201 
202 Key_use DynArrayTest::test_data[num_elements];
203 TABLE DynArrayTest::table_list[num_elements];
204 
205 
206 // Test insert_dynamic() and my_qsort().
207 TEST_F(DynArrayTest, DynArray)
208 {
209  for (int ix= 0; ix < num_iterations; ++ix)
210  insert_and_sort_dynamic();
211 }
212 
213 
214 // Test vector::push_back() and std::sort()
215 TEST_F(DynArrayTest, Vector)
216 {
217  for (int ix= 0; ix < num_iterations; ++ix)
218  insert_and_sort_vector();
219 }
220 
221 
222 /*
223  This class is for unit testing of Mem_root_array.
224  */
225 class MemRootTest : public ::testing::Test
226 {
227 protected:
228  MemRootTest()
229  : m_mem_root_p(&m_mem_root),
230  m_array_mysys(m_mem_root_p),
231  m_array_std(m_mem_root_p)
232  {}
233 
234  virtual void SetUp()
235  {
236  init_sql_alloc(&m_mem_root, 1024, 0);
237  ASSERT_EQ(0, my_pthread_setspecific_ptr(THR_MALLOC, &m_mem_root_p));
238  MEM_ROOT *root= *my_pthread_getspecific_ptr(MEM_ROOT**, THR_MALLOC);
239  ASSERT_EQ(root, m_mem_root_p);
240 
241  m_array_mysys.reserve(num_elements);
242  m_array_std.reserve(num_elements);
243  }
244 
245  virtual void TearDown()
246  {
247  free_root(&m_mem_root, MYF(0));
248  }
249 
250  static void SetUpTestCase()
251  {
252  generate_test_data(test_data, table_list, num_elements);
253  ASSERT_EQ(0, pthread_key_create(&THR_THD, NULL));
254  ASSERT_EQ(0, pthread_key_create(&THR_MALLOC, NULL));
255  }
256 
257  static void TearDownTestCase()
258  {
259  pthread_key_delete(THR_THD);
260  pthread_key_delete(THR_MALLOC);
261  }
262 
263  void insert_and_sort_mysys()
264  {
265  m_array_mysys.clear();
266  for (int ix= 0; ix < num_elements; ++ix)
267  {
268  m_array_mysys.push_back(test_data[ix]);
269  }
270  my_qsort(m_array_mysys.begin(), m_array_mysys.size(),
271  m_array_mysys.element_size(),
272  reinterpret_cast<qsort_cmp>(sort_keyuse));
273  }
274 
275  void insert_and_sort_std()
276  {
277  m_array_std.clear();
278  for (int ix= 0; ix < num_elements; ++ix)
279  {
280  m_array_std.push_back(test_data[ix]);
281  }
282  std::sort(m_array_std.begin(), m_array_std.end(), std::less<Key_use>());
283  }
284 
285  MEM_ROOT m_mem_root;
286  MEM_ROOT *m_mem_root_p;
287  Key_use_array m_array_mysys;
288  Key_use_array m_array_std;
289 private:
290  static Key_use test_data[num_elements];
291  static TABLE table_list[num_elements];
292 
293  GTEST_DISALLOW_COPY_AND_ASSIGN_(MemRootTest);
294 };
295 
296 Key_use MemRootTest::test_data[num_elements];
297 TABLE MemRootTest::table_list[num_elements];
298 
299 
300 // Test Mem_root_array::push_back() and my_qsort()
301 TEST_F(MemRootTest, KeyUseMysys)
302 {
303  for (int ix= 0; ix < num_iterations; ++ix)
304  insert_and_sort_mysys();
305 }
306 
307 
308 // Test Mem_root_array::push_back() and std::sort()
309 TEST_F(MemRootTest, KeyUseStd)
310 {
311  for (int ix= 0; ix < num_iterations; ++ix)
312  insert_and_sort_std();
313 }
314 
315 
316 // Test that my_qsort() and std::sort() generate same order.
317 TEST_F(MemRootTest, KeyUseCompare)
318 {
319  insert_and_sort_mysys();
320  insert_and_sort_std();
321  for (int ix= 0; ix < num_elements; ++ix)
322  {
323  Key_use k1= m_array_mysys.at(ix);
324  Key_use k2= m_array_std.at(ix);
325  EXPECT_EQ(k1, k2);
326  }
327 }
328 
329 
330 // Test that Mem_root_array re-expanding works.
331 TEST_F(MemRootTest, Reserve)
332 {
333  Mem_root_array<uint, true> intarr(m_mem_root_p);
334  intarr.reserve(2);
335  const uint num_pushes= 20;
336  for (uint ix=0; ix < num_pushes; ++ix)
337  {
338  EXPECT_EQ(ix, intarr.size());
339  EXPECT_FALSE(intarr.push_back(ix));
340  EXPECT_EQ(ix, intarr.at(ix));
341  }
342  for (uint ix=0; ix < num_pushes; ++ix)
343  {
344  EXPECT_EQ(ix, intarr.at(ix));
345  }
346  EXPECT_EQ(sizeof(uint), intarr.element_size());
347  EXPECT_EQ(num_pushes, intarr.size());
348  EXPECT_LE(num_pushes, intarr.capacity());
349 }
350 
351 
353 {
354 public:
355  DestroyCounter(const DestroyCounter &rhs) : p_counter(rhs.p_counter) {}
356  DestroyCounter(size_t *p) : p_counter(p) {}
357  ~DestroyCounter() { (*p_counter)+= 1; }
358 private:
359  size_t *p_counter;
360 };
361 
362 
363 // Test chop() and clear() and that destructors are executed.
364 TEST_F(MemRootTest, ChopAndClear)
365 {
366  Mem_root_array<DestroyCounter, false> array(m_mem_root_p);
367  const size_t nn= 4;
368  array.reserve(nn);
369  size_t counter= 0;
370  DestroyCounter foo(&counter);
371  for (size_t ix= 0; ix < array.capacity(); ++ix)
372  array.push_back(foo);
373 
374  EXPECT_EQ(0U, counter);
375  array.chop(nn / 2);
376  EXPECT_EQ(nn / 2, counter);
377  EXPECT_EQ(nn / 2, array.size());
378 
379  array.clear();
380  EXPECT_EQ(nn, counter);
381 }
382 
383 
384 // Test that elements are destroyed if push_back() needs to call reserve().
385 TEST_F(MemRootTest, ReserveDestroy)
386 {
387  Mem_root_array<DestroyCounter, false> array(m_mem_root_p);
388  const size_t nn= 4;
389  array.reserve(nn / 2);
390  size_t counter= 0;
391  DestroyCounter foo(&counter);
392  for (size_t ix= 0; ix < nn; ++ix)
393  array.push_back(foo);
394 
395  EXPECT_EQ(nn / 2, counter);
396  EXPECT_EQ(nn, array.size());
397 
398  counter= 0;
399  array.clear();
400  EXPECT_EQ(nn, counter);
401 }
402 
403 
404 }