MySQL 5.6.14 Source Code Document
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
HugoQueryBuilder.cpp
1 /*
2  Copyright (c) 2011, 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 <HugoQueryBuilder.hpp>
19 
20 static
21 bool
22 isScan(const NdbQueryOperationDef* def)
23 {
24  return
25  def->getType() == NdbQueryOperationDef::TableScan ||
27 }
28 
29 NdbOut&
30 operator<<(NdbOut& out, const HugoQueryBuilder::Op& op)
31 {
32  out << "[" << op.m_idx << " : " << op.m_op->getTable()->getName() << ": ";
33  switch(op.m_op->getType()){
35  out << "table-scan";
36  break;
38  out << "index-scan";
39  break;
40  default:
41  out << "lookup";
42  }
43 
44  out << " : parent: " << op.m_parent << " ]";
45  return out;
46 }
47 
48 void
49 HugoQueryBuilder::init()
50 {
51  m_options = 0;
52  setMinJoinLevel(2);
53  setMaxJoinLevel(4);
54 }
55 
56 HugoQueryBuilder::~HugoQueryBuilder()
57 {
58  for (size_t i = 0; i<m_queries.size(); i++)
59  m_queries[i]->destroy();
60 }
61 
62 void
63 HugoQueryBuilder::fixOptions()
64 {
65  setOption(O_PK_INDEX);
66  setOption(O_UNIQUE_INDEX);
67  setOption(O_TABLE_SCAN);
68  setOption(O_ORDERED_INDEX);
69  if (testOption(O_LOOKUP))
70  {
71  clearOption(O_TABLE_SCAN);
72  clearOption(O_ORDERED_INDEX);
73  }
74 }
75 
76 void
77 HugoQueryBuilder::addTable(Ndb* ndb, const NdbDictionary::Table* tab)
78 {
79  for (size_t i = 0; i<m_tables.size(); i++)
80  {
81  if (m_tables[i].m_table == tab)
82  return;
83  }
84 
85  TableDef def;
86  def.m_table = tab;
87 
90  int res = pDict->listIndexes(l, tab->getName());
91  if (res == 0)
92  {
93  for (unsigned i = 0; i<l.count; i++)
94  {
95  const NdbDictionary::Index * idx = pDict->getIndex(l.elements[i].name,
96  tab->getName());
97  if (idx)
98  {
100  {
101  def.m_unique_indexes.push_back(idx);
102  }
103  else if (idx->getType() == NdbDictionary::Index::OrderedIndex)
104  {
105  def.m_ordered_indexes.push_back(idx);
106  }
107  }
108  }
109  }
110 
111  m_tables.push_back(def);
112 }
113 
114 int
115 HugoQueryBuilder::getJoinLevel() const
116 {
117  int m0 = m_joinLevel[0]; // min
118  int m1 = m_joinLevel[1]; // max
119  if (m0 > m1)
120  {
121  int m = m0;
122  m0 = m1;
123  m1 = m;
124  }
125 
126  int d = m1 - m0;
127  if (d == 0)
128  return m0;
129  else
130  return m0 + (rand() % d);
131 }
132 
133 void
134 HugoQueryBuilder::removeTable(const NdbDictionary::Table* tab)
135 {
136  for (size_t i = 0; i<m_tables.size(); i++)
137  {
138  if (m_tables[i].m_table == tab)
139  {
140  m_tables.erase(i);
141  return;
142  }
143  }
144 }
145 
146 HugoQueryBuilder::TableDef
147 HugoQueryBuilder::getTable() const
148 {
149  int i = rand() % m_tables.size();
150  return m_tables[i];
151 }
152 
153 HugoQueryBuilder::OpIdx
154 HugoQueryBuilder::getOp() const
155 {
156  OpIdx oi;
157  TableDef tab = getTable();
158  oi.m_index = 0;
159  oi.m_table = tab.m_table;
160 
161  OptionMask save = m_options;
162  if (tab.m_unique_indexes.size() == 0)
163  {
164  clearOption(O_UNIQUE_INDEX);
165  }
166  if (tab.m_ordered_indexes.size() == 0)
167  {
168  clearOption(O_ORDERED_INDEX);
169  }
170 
171 loop:
172  switch(rand() % 4){
173  case 0:
174  if (testOption(O_PK_INDEX))
175  {
177  goto found;
178  }
179  break;
180  case 1:
181  if (testOption(O_TABLE_SCAN))
182  {
184  goto found;
185  }
186  break;
187  case 2:
188  if (testOption(O_UNIQUE_INDEX))
189  {
191  int cnt = tab.m_unique_indexes.size();
192  oi.m_index = tab.m_unique_indexes[rand() % cnt];
193  goto found;
194  }
195  break;
196  case 3:
197  if (testOption(O_ORDERED_INDEX))
198  {
200  int cnt = tab.m_ordered_indexes.size();
201  oi.m_index = tab.m_ordered_indexes[rand() % cnt];
202  goto found;
203  }
204  break;
205  }
206  goto loop;
207 
208 found:
209  m_options = save;
210  return oi;
211 }
212 
213 bool
214 HugoQueryBuilder::checkBindable(Vector<const NdbDictionary::Column*> cols,
215  Vector<Op> ops,
216  bool allow_bind_nullable)
217 {
218  for (size_t c = 0; c < cols.size(); c++)
219  {
220  const NdbDictionary::Column * col = cols[c];
221  bool found = false;
222  for (size_t t = 0; !found && t<ops.size(); t++)
223  {
224  const NdbDictionary::Table * tab = ops[t].m_op->getTable();
225  if (tab)
226  {
227  for (int i = 0; i<tab->getNoOfColumns(); i++)
228  {
229  if (!allow_bind_nullable && tab->getColumn(i)->getNullable())
230  continue;
231  else if (col->isBindable(* tab->getColumn(i)) == 0)
232  {
233  found = true;
234  break;
235  }
236  }
237  }
238  }
239  if (!found)
240  return false;
241  }
242  return true;
243 }
244 
245 bool
246 HugoQueryBuilder::isAncestor(const Op& parent, const Op& child) const
247 {
248  int pi = parent.m_idx;
249  int ci = child.m_idx;
250  assert(ci != pi);
251 
252  while (ci != 0)
253  {
254  if (m_query[ci].m_parent == pi)
255  return true;
256  assert(m_query[ci].m_parent != -1);
257  ci = m_query[m_query[ci].m_parent].m_idx;
258  }
259  return false;
260 }
261 
262 bool
263 HugoQueryBuilder::checkBusyScan(Op op) const
264 {
268  while (op.m_parent != -1)
269  {
270  if (isScan(op.m_op))
271  {
272  break;
273  }
274  op = m_query[op.m_parent];
275  }
276 
277  for (size_t i = op.m_idx + 1; i < m_query.size(); i++)
278  if (isAncestor(op, m_query[i]) && isScan(m_query[i].m_op))
279  return true;
280 
281  return false;
282 }
283 
285 HugoQueryBuilder::getParents(OpIdx oi)
286 {
290  bool allow_bind_nullable = false;
291  bool check_bushy_scan = false;
293  if (oi.m_index == 0)
294  {
295  for (int i = 0; i<oi.m_table->getNoOfColumns(); i++)
296  if (oi.m_table->getColumn(i)->getPrimaryKey())
297  cols.push_back(oi.m_table->getColumn(i));
298  }
299  else if (oi.m_index->getType() == NdbDictionary::Index::UniqueHashIndex)
300  {
301  for (unsigned i = 0; i < oi.m_index->getNoOfColumns(); i++)
302  cols.push_back(oi.m_table->getColumn
303  (oi.m_index->getColumn(i)->getName()));
304  }
305  else if (oi.m_index->getType() == NdbDictionary::Index::OrderedIndex)
306  {
310  allow_bind_nullable = true;
311  check_bushy_scan = true;
312  unsigned cnt = oi.m_index->getNoOfColumns();
313  unsigned val = cnt; // cnt > 1 ? (1 + (rand() % (cnt - 1))) : cnt;
314  for (unsigned i = 0; i < val; i++)
315  cols.push_back(oi.m_table->getColumn
316  (oi.m_index->getColumn(i)->getName()));
317  }
318 
319  int r = rand() % m_query.size();
320  int cnt = (int)m_query.size();
321  for (int i = 0; i < cnt; i++)
322  {
323  Vector<Op> set;
324  Op op = m_query[(i + r) % cnt];
325  if (check_bushy_scan && checkBusyScan(op))
326  continue;
327  set.push_back(op);
328 
329 #if 0
330 
333  if (testOption(O_MULTI_PARENT))
334  {
335  while (op.m_parent != -1)
336  {
337  op = m_query[op.m_parent];
338  set.push_back(op);
339  }
340  }
341 #endif
342 
343  if (checkBindable(cols, set, allow_bind_nullable))
344  return set;
345  }
346 
347  Vector<Op> ret;
348  return ret;
349 }
350 
352 HugoQueryBuilder::createLink(NdbQueryBuilder& builder,
353  const NdbDictionary::Column* pCol,
354  Vector<Op> & parents,
355  bool allow_bind_nullable)
356 {
357  int cnt = (int)parents.size();
358  int r = rand();
359 
360  Op op;
361  const NdbDictionary::Column* col = 0;
362 
366  for (int i = 0; i<cnt; i++)
367  {
368  op = parents[(i + r) % cnt];
369  const NdbDictionary::Table* tab = op.m_op->getTable();
370 
371  int cntpk = tab->getNoOfPrimaryKeys();
372  int rpk = rand();
373  for (int j = 0; j<cntpk; j++)
374  {
375  col = tab->getColumn(tab->getPrimaryKey((j + rpk) % cntpk));
376  if (pCol->isBindable(* col) == 0)
377  {
378  goto found;
379  }
380  }
381  }
382 
386  r = rand();
387  for (int i = 0; i<cnt; i++)
388  {
389  op = parents[(i + r) % cnt];
390  const NdbDictionary::Table* tab = op.m_op->getTable();
391 
392  int cntcol = tab->getNoOfColumns();
393  int rcol = rand();
394  for (int j = 0; j<cntcol; j++)
395  {
396  col = tab->getColumn((j + rcol) % cntcol);
397  if (col->getPrimaryKey())
398  {
399  // already checked
400  continue;
401  }
402  if (!allow_bind_nullable && col->getNullable())
403  continue;
404 
405  if (pCol->isBindable(* col) == 0)
406  {
407  goto found;
408  }
409  }
410  }
411  return 0;
412 
413 found:
414  NdbQueryOperand * ret = builder.linkedValue(op.m_op, col->getName());
415  assert(ret);
416  return ret;
417 }
418 
420 HugoQueryBuilder::createOp(NdbQueryBuilder& builder)
421 {
422  struct Op op;
423  op.m_parent = -1;
424  op.m_op = 0;
425  op.m_idx = m_query.size();
426 
427  if (m_query.size() == 0)
428  {
432  OpIdx oi = getOp();
433  switch(oi.m_type){
435  int opNo = 0;
436  NdbQueryOperand * operands[NDB_MAX_NO_OF_ATTRIBUTES_IN_KEY + 1];
437  for (int a = 0; a<oi.m_table->getNoOfColumns(); a++)
438  {
439  if (oi.m_table->getColumn(a)->getPrimaryKey())
440  {
441  operands[opNo++] = builder.paramValue();
442  }
443  }
444  operands[opNo] = 0;
445  op.m_op = builder.readTuple(oi.m_table, operands);
446  break;
447  }
449  op.m_op = builder.scanTable(oi.m_table);
450  break;
452  int opNo = 0;
453  NdbQueryOperand * operands[NDB_MAX_NO_OF_ATTRIBUTES_IN_KEY + 1];
454  for (unsigned a = 0; a<oi.m_index->getNoOfColumns(); a++)
455  {
456  operands[opNo++] = builder.paramValue();
457  }
458  operands[opNo] = 0;
459  NdbQueryIndexBound bounds(operands);
460  op.m_op = builder.scanIndex(oi.m_index, oi.m_table, &bounds);
461  break;
462  }
464  int opNo = 0;
465  NdbQueryOperand * operands[NDB_MAX_NO_OF_ATTRIBUTES_IN_KEY + 1];
466  for (unsigned a = 0; a<oi.m_index->getNoOfColumns(); a++)
467  {
468  operands[opNo++] = builder.paramValue();
469  }
470  operands[opNo] = 0;
471  op.m_op = builder.readTuple(oi.m_index, oi.m_table, operands);
472  break;
473  }
474  }
475  else
476  {
477 loop:
478  OpIdx oi = getOp();
479  Vector<Op> parents = getParents(oi);
480  if (parents.size() == 0)
481  {
482  // no possible parents found for pTab...try another
483  goto loop;
484  }
485  switch(oi.m_type){
487  int opNo = 0;
488  NdbQueryOperand * operands[NDB_MAX_NO_OF_ATTRIBUTES_IN_KEY + 1];
489  for (int a = 0; a<oi.m_table->getNoOfColumns(); a++)
490  {
491  if (oi.m_table->getColumn(a)->getPrimaryKey())
492  {
493  operands[opNo++] = createLink(builder, oi.m_table->getColumn(a),
494  parents, false);
495  }
496  }
497  operands[opNo] = 0;
498 
499  op.m_parent = parents[0].m_idx;
500  op.m_op = builder.readTuple(oi.m_table, operands);
501  break;
502  }
504  int opNo = 0;
505  NdbQueryOperand * operands[NDB_MAX_NO_OF_ATTRIBUTES_IN_KEY + 1];
506  for (unsigned a = 0; a<oi.m_index->getNoOfColumns(); a++)
507  {
508  operands[opNo++] =
509  createLink(builder,
510  oi.m_table->getColumn(oi.m_index->getColumn(a)->getName()),
511  parents, false);
512  }
513  operands[opNo] = 0;
514 
515  op.m_parent = parents[0].m_idx;
516  op.m_op = builder.readTuple(oi.m_index, oi.m_table, operands);
517  break;
518  }
520  // not supported
521  abort();
523  int opNo = 0;
524  NdbQueryOperand * operands[NDB_MAX_NO_OF_ATTRIBUTES_IN_KEY + 1];
525  for (unsigned a = 0; a<oi.m_index->getNoOfColumns(); a++)
526  {
527  operands[opNo++] =
528  createLink(builder,
529  oi.m_table->getColumn(oi.m_index->getColumn(a)->getName()),
530  parents, true);
531  }
532  operands[opNo] = 0;
533 
534  op.m_parent = parents[0].m_idx;
535  NdbQueryIndexBound bounds(operands); // Only EQ for now
536  op.m_op = builder.scanIndex(oi.m_index, oi.m_table, &bounds);
537  if (op.m_op == 0)
538  {
539  ndbout << "Failed to add to " << endl;
540  for (size_t i = 0; i<m_query.size(); i++)
541  ndbout << m_query[i] << endl;
542 
543  ndbout << "Parents: " << endl;
544  for (size_t i = 0; i<parents.size(); i++)
545  ndbout << parents[i].m_idx << " ";
546  ndbout << endl;
547  }
548  break;
549  }
550  }
551  }
552 
553  if (op.m_op == 0)
554  {
555  NdbError err = builder.getNdbError();
556  ndbout << err << endl;
557  return 0;
558  }
559 
560  m_query.push_back(op);
561  return op.m_op;
562 }
563 
564 const NdbQueryDef *
565 HugoQueryBuilder::createQuery(bool takeOwnership)
566 {
567  NdbQueryBuilder* const builder = NdbQueryBuilder::create();
568  if (builder == NULL)
569  {
570  ndbout << "Failed to create NdbQueryBuilder." << endl;
571  return 0;
572  }
573 
574  {
575  OptionMask save = m_options;
576  if (testOption(O_SCAN))
577  {
578  clearOption(O_PK_INDEX);
579  clearOption(O_UNIQUE_INDEX);
580  }
581  const NdbQueryOperationDef * rootOp = createOp(*builder);
582  assert(rootOp != 0);
583  m_options = save;
584  }
585 
589  OptionMask save = m_options;
590  clearOption(O_TABLE_SCAN);
591 
596  if (!isScan(m_query[0].m_op))
597  {
598  clearOption(O_ORDERED_INDEX);
599  }
600 
601  int levels = getJoinLevel();
602  while (levels --)
603  {
604  createOp(*builder);
605  }
606 
607  m_options = save;
608 
609  const NdbQueryDef * def = builder->prepare();
610  builder->destroy();
611  if (def != 0 && !takeOwnership)
612  {
613  m_queries.push_back(def);
614  }
615 
616  m_query.clear();
617 
618  return def;
619 }
620 
621 template class Vector<const NdbQueryDef*>;
622 template class Vector<HugoQueryBuilder::Op>;