MySQL 5.6.14 Source Code Document
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
testPartitioning.cpp
1 /*
2  Copyright (c) 2004, 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 <NDBT_Test.hpp>
19 #include <NDBT_ReturnCodes.h>
20 #include <HugoTransactions.hpp>
21 #include <UtilTransactions.hpp>
22 #include <NdbRestarter.hpp>
23 
24 static Uint32 max_dks = 0;
25 static const Uint32 MAX_FRAGS=48 * 8 * 4; // e.g. 48 nodes, 8 frags/node, 4 replicas
26 static Uint32 frag_ng_mappings[MAX_FRAGS];
27 static const char* DistTabName= "DistTest";
28 static const char* DistTabDKeyCol= "DKey";
29 static const char* DistTabPKey2Col= "PKey2";
30 static const char* DistTabResultCol= "Result";
31 static const char* DistIdxName= "ResultIndex";
32 
33 static
34 int
35 run_drop_table(NDBT_Context* ctx, NDBT_Step* step)
36 {
37  NdbDictionary::Dictionary* dict = GETNDB(step)->getDictionary();
38  dict->dropTable(ctx->getTab()->getName());
39  return 0;
40 }
41 
42 static
43 int
44 setNativePartitioning(Ndb* ndb, NdbDictionary::Table& tab, int when, void* arg)
45 {
46  switch(when){
47  case 0: // Before
48  break;
49  case 1: // After
50  return 0;
51  default:
52  return 0;
53  }
54 
55  /* Use rand to choose one of the native partitioning schemes */
56  const Uint32 rType= rand() % 3;
57  Uint32 fragType= -1;
58  switch(rType)
59  {
60  case 0 :
61  fragType = NdbDictionary::Object::DistrKeyHash;
62  break;
63  case 1 :
64  fragType = NdbDictionary::Object::DistrKeyLin;
65  break;
66  case 2:
67  fragType = NdbDictionary::Object::HashMapPartition;
68  break;
69  }
70 
71  ndbout << "Setting fragment type to " << fragType << endl;
73  return 0;
74 }
75 
76 
77 static
78 int
79 add_distribution_key(Ndb* ndb, NdbDictionary::Table& tab, int when, void* arg)
80 {
81  switch(when){
82  case 0: // Before
83  break;
84  case 1: // After
85  return 0;
86  default:
87  return 0;
88  }
89 
90  /* Choose a partitioning type */
91  setNativePartitioning(ndb, tab, when, arg);
92 
93  int keys = tab.getNoOfPrimaryKeys();
94  Uint32 dks = (2 * keys + 2) / 3; dks = (dks > max_dks ? max_dks : dks);
95 
96  for(int i = 0; i<tab.getNoOfColumns(); i++)
97  if(tab.getColumn(i)->getPrimaryKey() &&
98  tab.getColumn(i)->getCharset() != 0)
99  keys--;
100 
101  Uint32 max = NDB_MAX_NO_OF_ATTRIBUTES_IN_KEY - tab.getNoOfPrimaryKeys();
102 
103  if(max_dks < max)
104  max = max_dks;
105 
106  if(keys <= 1 && max > 0)
107  {
108  dks = 1 + (rand() % max);
109  ndbout_c("%s pks: %d dks: %d", tab.getName(), keys, dks);
110  while(dks--)
111  {
114  name.assfmt("PK_DK_%d", dks);
115  col.setName(name.c_str());
116  if((rand() % 100) > 50)
117  {
119  col.setLength(1);
120  }
121  else
122  {
124  col.setLength(1+(rand() % 25));
125  }
126  col.setNullable(false);
127  col.setPrimaryKey(true);
128  col.setDistributionKey(true);
129  tab.addColumn(col);
130  }
131  }
132  else
133  {
134  for(int i = 0; i<tab.getNoOfColumns(); i++)
135  {
136  NdbDictionary::Column* col = tab.getColumn(i);
137  if(col->getPrimaryKey() && col->getCharset() == 0)
138  {
139  if((int)dks >= keys || (rand() % 100) > 50)
140  {
141  col->setDistributionKey(true);
142  dks--;
143  }
144  keys--;
145  }
146  }
147  }
148 
149  ndbout << (NDBT_Table&)tab << endl;
150 
151  return 0;
152 }
153 
154 
155 static
156 int
157 setupUDPartitioning(Ndb* ndb, NdbDictionary::Table& tab)
158 {
159  /* Following should really be taken from running test system : */
160  const Uint32 numNodes= ndb->get_ndb_cluster_connection().no_db_nodes();
161  const Uint32 numReplicas= 2; // Assumption
162  const Uint32 guessNumNgs= numNodes/2;
163  const Uint32 numNgs= guessNumNgs?guessNumNgs : 1;
164  const Uint32 numFragsPerNode= 2 + (rand() % 3);
165  const Uint32 numPartitions= numReplicas * numNgs * numFragsPerNode;
166 
167  tab.setFragmentType(NdbDictionary::Table::UserDefined);
168  tab.setFragmentCount(numPartitions);
169  for (Uint32 i=0; i<numPartitions; i++)
170  {
171  frag_ng_mappings[i]= i % numNgs;
172  }
173  tab.setFragmentData(frag_ng_mappings, numPartitions);
174 
175  return 0;
176 }
177 
178 static
179 int
180 setUserDefPartitioning(Ndb* ndb, NdbDictionary::Table& tab, int when, void* arg)
181 {
182  switch(when){
183  case 0: // Before
184  break;
185  case 1: // After
186  return 0;
187  default:
188  return 0;
189  }
190 
191  setupUDPartitioning(ndb, tab);
192 
193  ndbout << (NDBT_Table&)tab << endl;
194 
195  return 0;
196 }
197 
198 static
199 int
200 one_distribution_key(Ndb* ndb, NdbDictionary::Table& tab, int when, void* arg)
201 {
202  switch(when){
203  case 0: // Before
204  break;
205  case 1: // After
206  return 0;
207  default:
208  return 0;
209  }
210 
211  setNativePartitioning(ndb, tab, when, arg);
212 
213  int keys = tab.getNoOfPrimaryKeys();
214  int dist_key_no = rand()% keys;
215 
216  for(int i = 0; i<tab.getNoOfColumns(); i++)
217  {
218  if(tab.getColumn(i)->getPrimaryKey())
219  {
220  if (dist_key_no-- == 0)
221  {
222  tab.getColumn(i)->setDistributionKey(true);
223  }
224  else
225  {
226  tab.getColumn(i)->setDistributionKey(false);
227  }
228  }
229  }
230  ndbout << (NDBT_Table&)tab << endl;
231 
232  return 0;
233 }
234 
235 static
237 create_dist_table(Ndb* pNdb,
238  bool userDefined)
239 {
241 
242  do {
244  tab.setName(DistTabName);
245 
246  if (userDefined)
247  {
248  setupUDPartitioning(pNdb, tab);
249  }
250  else
251  {
252  setNativePartitioning(pNdb, tab, 0, 0);
253  }
254 
256  dk.setName(DistTabDKeyCol);
258  dk.setLength(1);
259  dk.setNullable(false);
260  dk.setPrimaryKey(true);
261  dk.setPartitionKey(true);
262  tab.addColumn(dk);
263 
265  pk2.setName(DistTabPKey2Col);
267  pk2.setLength(1);
268  pk2.setNullable(false);
269  pk2.setPrimaryKey(true);
270  pk2.setPartitionKey(false);
271  tab.addColumn(pk2);
272 
273  NdbDictionary::Column result;
274  result.setName(DistTabResultCol);
276  result.setLength(1);
277  result.setNullable(true);
278  result.setPrimaryKey(false);
279  tab.addColumn(result);
280 
281  dict->dropTable(tab.getName());
282  if(dict->createTable(tab) == 0)
283  {
284  ndbout << (NDBT_Table&)tab << endl;
285 
286  do {
287  /* Primary key index */
290  idx.setLogging(false);
291  idx.setTable(DistTabName);
292  idx.setName("PRIMARY");
293  idx.addColumnName(DistTabDKeyCol);
294  idx.addColumnName(DistTabPKey2Col);
295 
296  dict->dropIndex("PRIMARY",
297  tab.getName());
298 
299  if (dict->createIndex(idx) == 0)
300  {
301  ndbout << "Primary Index created successfully" << endl;
302  break;
303  }
304  ndbout << "Primary Index create failed with " <<
305  dict->getNdbError().code <<
306  " retrying " << endl;
307  } while (0);
308 
309  do {
310  /* Now the index on the result column */
313  idx.setLogging(false);
314  idx.setTable(DistTabName);
315  idx.setName(DistIdxName);
316  idx.addColumnName(DistTabResultCol);
317 
318  dict->dropIndex(idx.getName(),
319  tab.getName());
320 
321  if (dict->createIndex(idx) == 0)
322  {
323  ndbout << "Index on Result created successfully" << endl;
324  return dict->getTable(tab.getName());
325  }
326  ndbout << "Index create failed with " <<
327  dict->getNdbError().code << endl;
328  } while (0);
329  }
330  } while (0);
331  return 0;
332 };
333 
334 static int
335 run_create_table(NDBT_Context* ctx, NDBT_Step* step)
336 {
337  /* Create table, optionally with extra distribution keys
338  * or UserDefined partitioning
339  */
340  max_dks = ctx->getProperty("distributionkey", (unsigned)0);
341  bool userDefined = ctx->getProperty("UserDefined", (unsigned) 0);
342 
343  if(NDBT_Tables::createTable(GETNDB(step),
344  ctx->getTab()->getName(),
345  false, false,
346  max_dks?
347  add_distribution_key:
348  userDefined?
349  setUserDefPartitioning :
350  setNativePartitioning) == NDBT_OK)
351  {
352  return NDBT_OK;
353  }
354 
355  if(GETNDB(step)->getDictionary()->getNdbError().code == 745)
356  return NDBT_OK;
357 
358  return NDBT_FAILED;
359 }
360 
361 static int
362 run_create_table_smart_scan(NDBT_Context* ctx, NDBT_Step* step)
363 {
364  if(NDBT_Tables::createTable(GETNDB(step),
365  ctx->getTab()->getName(),
366  false, false,
367  one_distribution_key) == NDBT_OK)
368  {
369  return NDBT_OK;
370  }
371 
372  if(GETNDB(step)->getDictionary()->getNdbError().code == 745)
373  return NDBT_OK;
374 
375  return NDBT_FAILED;
376 }
377 
378 static int
379 run_create_pk_index(NDBT_Context* ctx, NDBT_Step* step){
380  bool orderedIndex = ctx->getProperty("OrderedIndex", (unsigned)0);
381 
382  Ndb* pNdb = GETNDB(step);
383  const NdbDictionary::Table *pTab =
384  pNdb->getDictionary()->getTable(ctx->getTab()->getName());
385 
386  if(!pTab)
387  return NDBT_OK;
388 
389  bool logged = ctx->getProperty("LoggedIndexes", orderedIndex ? 0 : 1);
390 
392  name.assfmt("IND_%s_PK_%c", pTab->getName(), orderedIndex ? 'O' : 'U');
393 
394  // Create index
395  if (orderedIndex)
396  ndbout << "Creating " << ((logged)?"logged ": "temporary ") << "ordered index "
397  << name.c_str() << " (";
398  else
399  ndbout << "Creating " << ((logged)?"logged ": "temporary ") << "unique index "
400  << name.c_str() << " (";
401 
402  NdbDictionary::Index pIdx(name.c_str());
403  pIdx.setTable(pTab->getName());
404  if (orderedIndex)
406  else
408  for (int c = 0; c< pTab->getNoOfColumns(); c++){
409  const NdbDictionary::Column * col = pTab->getColumn(c);
410  if(col->getPrimaryKey()){
411  pIdx.addIndexColumn(col->getName());
412  ndbout << col->getName() <<" ";
413  }
414  }
415 
416  pIdx.setStoredIndex(logged);
417  ndbout << ") ";
418  if (pNdb->getDictionary()->createIndex(pIdx) != 0){
419  ndbout << "FAILED!" << endl;
420  const NdbError err = pNdb->getDictionary()->getNdbError();
421  ERR(err);
422  return NDBT_FAILED;
423  }
424 
425  ndbout << "OK!" << endl;
426  return NDBT_OK;
427 }
428 
429 static int run_create_pk_index_drop(NDBT_Context* ctx, NDBT_Step* step){
430  bool orderedIndex = ctx->getProperty("OrderedIndex", (unsigned)0);
431 
432  Ndb* pNdb = GETNDB(step);
433  const NdbDictionary::Table *pTab =
434  pNdb->getDictionary()->getTable(ctx->getTab()->getName());
435 
436  if(!pTab)
437  return NDBT_OK;
438 
440  name.assfmt("IND_%s_PK_%c", pTab->getName(), orderedIndex ? 'O' : 'U');
441 
442  ndbout << "Dropping index " << name.c_str() << " ";
443  if (pNdb->getDictionary()->dropIndex(name.c_str(), pTab->getName()) != 0){
444  ndbout << "FAILED!" << endl;
445  ERR(pNdb->getDictionary()->getNdbError());
446  return NDBT_FAILED;
447  } else {
448  ndbout << "OK!" << endl;
449  }
450 
451  return NDBT_OK;
452 }
453 
454 static int
455 run_create_dist_table(NDBT_Context* ctx, NDBT_Step* step)
456 {
457  bool userDefined = ctx->getProperty("UserDefined", (unsigned)0);
458  if(create_dist_table(GETNDB(step),
459  userDefined))
460  return NDBT_OK;
461 
462  return NDBT_FAILED;
463 }
464 
465 static int
466 run_drop_dist_table(NDBT_Context* ctx, NDBT_Step* step)
467 {
468  GETNDB(step)->getDictionary()->dropTable(DistTabName);
469  return NDBT_OK;
470 }
471 
472 static int
473 run_tests(Ndb* p_ndb, HugoTransactions& hugoTrans, int records, Uint32 batchSize = 1)
474 {
475  if (hugoTrans.loadTable(p_ndb, records, batchSize) != 0)
476  {
477  return NDBT_FAILED;
478  }
479 
480  if(hugoTrans.pkReadRecords(p_ndb, records, batchSize) != 0)
481  {
482  return NDBT_FAILED;
483  }
484 
485  if(hugoTrans.pkUpdateRecords(p_ndb, records, batchSize) != 0)
486  {
487  return NDBT_FAILED;
488  }
489 
490  if(hugoTrans.pkDelRecords(p_ndb, records, batchSize) != 0)
491  {
492  return NDBT_FAILED;
493  }
494 
495  if (hugoTrans.loadTable(p_ndb, records, batchSize) != 0)
496  {
497  return NDBT_FAILED;
498  }
499 
500  if(hugoTrans.scanUpdateRecords(p_ndb, records) != 0)
501  {
502  return NDBT_FAILED;
503  }
504 
505  Uint32 abort = 23;
506  for(Uint32 j = 0; j<5; j++){
507  Uint32 parallelism = (j == 1 ? 1 : j * 3);
508  ndbout_c("parallelism: %d", parallelism);
509  if (hugoTrans.scanReadRecords(p_ndb, records, abort, parallelism,
510  NdbOperation::LM_Read) != 0)
511  {
512  return NDBT_FAILED;
513  }
514  if (hugoTrans.scanReadRecords(p_ndb, records, abort, parallelism,
516  {
517  return NDBT_FAILED;
518  }
519  if (hugoTrans.scanReadRecords(p_ndb, records, abort, parallelism,
521  {
522  return NDBT_FAILED;
523  }
524  }
525 
526  if(hugoTrans.clearTable(p_ndb, records) != 0)
527  {
528  return NDBT_FAILED;
529  }
530 
531  return 0;
532 }
533 
534 static int
535 run_pk_dk(NDBT_Context* ctx, NDBT_Step* step)
536 {
537  Ndb* p_ndb = GETNDB(step);
538  int records = ctx->getNumRecords();
539  const NdbDictionary::Table *tab =
540  p_ndb->getDictionary()->getTable(ctx->getTab()->getName());
541 
542  if(!tab)
543  return NDBT_OK;
544 
545  HugoTransactions hugoTrans(*tab);
546 
547  Uint32 batchSize= ctx->getProperty("BatchSize", (unsigned) 1);
548 
549  return run_tests(p_ndb, hugoTrans, records, batchSize);
550 }
551 
552 int
553 run_index_dk(NDBT_Context* ctx, NDBT_Step* step)
554 {
555  Ndb* p_ndb = GETNDB(step);
556  int records = ctx->getNumRecords();
557  const NdbDictionary::Table *pTab =
558  p_ndb->getDictionary()->getTable(ctx->getTab()->getName());
559 
560  if(!pTab)
561  return NDBT_OK;
562 
563  bool orderedIndex = ctx->getProperty("OrderedIndex", (unsigned)0);
564 
566  name.assfmt("IND_%s_PK_%c", pTab->getName(), orderedIndex ? 'O' : 'U');
567 
568  const NdbDictionary::Index * idx =
569  p_ndb->getDictionary()->getIndex(name.c_str(), pTab->getName());
570 
571  if(!idx)
572  {
573  ndbout << "Failed to retreive index: " << name.c_str() << endl;
574  return NDBT_FAILED;
575  }
576  Uint32 batchSize= ctx->getProperty("BatchSize", (unsigned) 1);
577 
578  HugoTransactions hugoTrans(*pTab, idx);
579 
580  return run_tests(p_ndb, hugoTrans, records, batchSize);
581 }
582 
583 static int
584 run_startHint(NDBT_Context* ctx, NDBT_Step* step)
585 {
586  Ndb* p_ndb = GETNDB(step);
587  int records = ctx->getNumRecords();
588  const NdbDictionary::Table *tab =
589  p_ndb->getDictionary()->getTable(ctx->getTab()->getName());
590 
591  if(!tab)
592  return NDBT_OK;
593 
594  HugoTransactions hugoTrans(*tab);
595  if (hugoTrans.loadTable(p_ndb, records) != 0)
596  {
597  return NDBT_FAILED;
598  }
599 
600  NdbRestarter restarter;
601  if(restarter.insertErrorInAllNodes(8050) != 0)
602  return NDBT_FAILED;
603 
604  HugoCalculator dummy(*tab);
605  int result = NDBT_OK;
606  for(int i = 0; i<records && result == NDBT_OK; i++)
607  {
608  char buffer[NDB_MAX_TUPLE_SIZE];
609  char* start= buffer + (rand() & 7);
610  char* pos= start;
611 
612  int k = 0;
613  Ndb::Key_part_ptr ptrs[NDB_MAX_NO_OF_ATTRIBUTES_IN_KEY+1];
614  for(int j = 0; j<tab->getNoOfColumns(); j++)
615  {
616  if(tab->getColumn(j)->getPartitionKey())
617  {
618  //ndbout_c(tab->getColumn(j)->getName());
619  int sz = tab->getColumn(j)->getSizeInBytes();
620  Uint32 real_size;
621  dummy.calcValue(i, j, 0, pos, sz, &real_size);
622  ptrs[k].ptr = pos;
623  ptrs[k++].len = real_size;
624  pos += (real_size + 3) & ~3;
625  }
626  }
627  ptrs[k].ptr = 0;
628 
629  // Now we have the pk
630  NdbTransaction* pTrans= p_ndb->startTransaction(tab, ptrs);
631  HugoOperations ops(*tab);
632  ops.setTransaction(pTrans);
633  if(ops.pkReadRecord(p_ndb, i, 1) != NDBT_OK)
634  {
635  result = NDBT_FAILED;
636  break;
637  }
638 
639  if(ops.execute_Commit(p_ndb) != 0)
640  {
641  result = NDBT_FAILED;
642  break;
643  }
644 
645  ops.closeTransaction(p_ndb);
646  }
647  restarter.insertErrorInAllNodes(0);
648  return result;
649 }
650 
651 static int
652 run_startHint_ordered_index(NDBT_Context* ctx, NDBT_Step* step)
653 {
654  Ndb* p_ndb = GETNDB(step);
655  int records = ctx->getNumRecords();
656  const NdbDictionary::Table *tab =
657  p_ndb->getDictionary()->getTable(ctx->getTab()->getName());
658 
659  if(!tab)
660  return NDBT_OK;
661 
663  name.assfmt("IND_%s_PK_O", tab->getName());
664 
665  const NdbDictionary::Index * idx =
666  p_ndb->getDictionary()->getIndex(name.c_str(), tab->getName());
667 
668  if(!idx)
669  {
670  ndbout << "Failed to retreive index: " << name.c_str() << endl;
671  return NDBT_FAILED;
672  }
673 
674  HugoTransactions hugoTrans(*tab, idx);
675  if (hugoTrans.loadTable(p_ndb, records) != 0)
676  {
677  return NDBT_FAILED;
678  }
679 
680  NdbRestarter restarter;
681  if(restarter.insertErrorInAllNodes(8050) != 0)
682  return NDBT_FAILED;
683 
684  HugoCalculator dummy(*tab);
685  int result = NDBT_OK;
686  for(int i = 0; i<records && result == NDBT_OK; i++)
687  {
688  char buffer[NDB_MAX_TUPLE_SIZE];
689  NdbTransaction* pTrans= NULL;
690 
691  char* start= buffer + (rand() & 7);
692  char* pos= start;
693 
694  int k = 0;
695  Ndb::Key_part_ptr ptrs[NDB_MAX_NO_OF_ATTRIBUTES_IN_KEY+1];
696  for(int j = 0; j<tab->getNoOfColumns(); j++)
697  {
698  if(tab->getColumn(j)->getPartitionKey())
699  {
700  //ndbout_c(tab->getColumn(j)->getName());
701  int sz = tab->getColumn(j)->getSizeInBytes();
702  Uint32 real_size;
703  dummy.calcValue(i, j, 0, pos, sz, &real_size);
704  ptrs[k].ptr = pos;
705  ptrs[k++].len = real_size;
706  pos += (real_size + 3) & ~3;
707  }
708  }
709  ptrs[k].ptr = 0;
710 
711  // Now we have the pk, start a hinted transaction
712  pTrans= p_ndb->startTransaction(tab, ptrs);
713 
714  // Because we pass an Ordered index here, pkReadRecord will
715  // use an index scan on the Ordered index
716  HugoOperations ops(*tab, idx);
717  ops.setTransaction(pTrans);
718  /* Despite it's name, it will actually perform index scans
719  * as there is an index.
720  * Error 8050 will cause an NDBD assertion failure in
721  * Dbtc::execDIGETPRIMCONF() if TC needs to scan a fragment
722  * which is not on the TC node
723  * So for this TC to pass with no failures we need transaction
724  * hinting and scan partition pruning on equal() to work
725  * correctly.
726  * TODO : Get coverage of Index scan which is equal on dist
727  * key cols, but has an inequality on some other column.
728  */
729  if(ops.pkReadRecord(p_ndb, i, 1) != NDBT_OK)
730  {
731  result = NDBT_FAILED;
732  break;
733  }
734 
735  if(ops.execute_Commit(p_ndb) != 0)
736  {
737  result = NDBT_FAILED;
738  break;
739  }
740 
741  ops.closeTransaction(p_ndb);
742  }
743  restarter.insertErrorInAllNodes(0);
744  return result;
745 }
746 
747 #define CHECK(x, y) {int res= (x); \
748  if (res != 0) { ndbout << "Assert failed at " \
749  << __LINE__ << endl \
750  << res << endl \
751  << " error : " \
752  << (y)->getNdbError().code \
753  << endl; \
754  return NDBT_FAILED; } }
755 
756 #define CHECKNOTNULL(x, y) { \
757  if ((x) == NULL) { ndbout << "Assert failed at line " \
758  << __LINE__ << endl \
759  << " with " \
760  << (y)->getNdbError().code \
761  << endl; \
762  return NDBT_FAILED; } }
763 
764 
765 static int
766 load_dist_table(Ndb* pNdb, int records, int parts)
767 {
768  const NdbDictionary::Table* tab= pNdb->getDictionary()->getTable(DistTabName);
769  bool userDefined= (tab->getFragmentType() ==
770  NdbDictionary::Object::UserDefined);
771 
772  const NdbRecord* distRecord= tab->getDefaultRecord();
773  CHECKNOTNULL(distRecord, pNdb);
774 
775  char* buf= (char*) malloc(NdbDictionary::getRecordRowLength(distRecord));
776 
777  CHECKNOTNULL(buf, pNdb);
778 
779  /* We insert a number of records with a constrained number of
780  * values for the distribution key column
781  */
782  for (int r=0; r < records; r++)
783  {
784  NdbTransaction* trans= pNdb->startTransaction();
785  CHECKNOTNULL(trans, pNdb);
786 
787  {
788  const int dKeyVal= r % parts;
789  const Uint32 dKeyAttrid= tab->getColumn(DistTabDKeyCol)->getAttrId();
790  memcpy(NdbDictionary::getValuePtr(distRecord, buf,
791  dKeyAttrid),
792  &dKeyVal, sizeof(dKeyVal));
793  }
794 
795  {
796  const int pKey2Val= r;
797  const Uint32 pKey2Attrid= tab->getColumn(DistTabPKey2Col)->getAttrId();
798  memcpy(NdbDictionary::getValuePtr(distRecord, buf,
799  pKey2Attrid),
800  &pKey2Val, sizeof(pKey2Val));
801  }
802 
803  {
804  const int resultVal= r*r;
805  const Uint32 resultValAttrid=
806  tab->getColumn(DistTabResultCol)->getAttrId();
807  memcpy(NdbDictionary::getValuePtr(distRecord, buf,
808  resultValAttrid),
809  &resultVal, sizeof(resultVal));
810 
811  // set not NULL
812  NdbDictionary::setNull(distRecord, buf, resultValAttrid, false);
813  }
814 
815 
817  opts.optionsPresent= 0;
818 
819  if (userDefined)
820  {
821  /* For user-defined partitioning, we set the partition id
822  * to be the distribution key value modulo the number
823  * of partitions in the table
824  */
825  opts.optionsPresent= NdbOperation::OperationOptions::OO_PARTITION_ID;
826  opts.partitionId= (r%parts) % tab->getFragmentCount();
827  }
828 
829  CHECKNOTNULL(trans->insertTuple(distRecord, buf,
830  NULL, &opts, sizeof(opts)), trans);
831 
832  if (trans->execute(NdbTransaction::Commit) != 0)
833  {
834  NdbError err = trans->getNdbError();
835  if (err.status == NdbError::TemporaryError)
836  {
837  ndbout << err << endl;
838  NdbSleep_MilliSleep(50);
839  r--; // just retry
840  }
841  else
842  {
843  CHECK(-1, trans);
844  }
845  }
846  trans->close();
847  }
848 
849  free(buf);
850 
851  return NDBT_OK;
852 };
853 
854 struct PartInfo
855 {
856  NdbTransaction* trans;
858  int dKeyVal;
859  int valCount;
860 };
861 
862 class Ap
863 {
864 public:
865  void* ptr;
866 
867  Ap(void* _ptr) : ptr(_ptr)
868  {};
869  ~Ap()
870  {
871  if (ptr != 0)
872  {
873  free(ptr);
874  ptr= 0;
875  }
876  }
877 };
878 
879 static int
880 dist_scan_body(Ndb* pNdb, int records, int parts, PartInfo* partInfo, bool usePrimary)
881 {
882  const NdbDictionary::Table* tab= pNdb->getDictionary()->getTable(DistTabName);
883  CHECKNOTNULL(tab, pNdb->getDictionary());
884  const char* indexName= usePrimary ? "PRIMARY" : DistIdxName;
885  const NdbDictionary::Index* idx= pNdb->getDictionary()->getIndex(indexName,
886  DistTabName);
887  CHECKNOTNULL(idx, pNdb->getDictionary());
888  const NdbRecord* tabRecord= tab->getDefaultRecord();
889  const NdbRecord* idxRecord= idx->getDefaultRecord();
890  bool userDefined= (tab->getFragmentType() ==
891  NdbDictionary::Object::UserDefined);
892 
893  char* boundBuf= (char*) malloc(NdbDictionary::getRecordRowLength(idx->getDefaultRecord()));
894 
895  if (usePrimary)
896  ndbout << "Checking MRR indexscan distribution awareness when distribution key part of bounds" << endl;
897  else
898  ndbout << "Checking MRR indexscan distribution awareness when distribution key provided explicitly" << endl;
899 
900  if (userDefined)
901  ndbout << "User Defined Partitioning scheme" << endl;
902  else
903  ndbout << "Native Partitioning scheme" << endl;
904 
905  Ap boundAp(boundBuf);
906 
907  for (int r=0; r < records; r++)
908  {
909  int partValue= r % parts;
910  PartInfo& pInfo= partInfo[partValue];
911 
912  if (pInfo.trans == NULL)
913  {
914  /* Provide the partition key as a hint for this transaction */
915  if (!userDefined)
916  {
917  Ndb::Key_part_ptr keyParts[2];
918  keyParts[0].ptr= &partValue;
919  keyParts[0].len= sizeof(partValue);
920  keyParts[1].ptr= NULL;
921  keyParts[1].len= 0;
922 
923  /* To test that bad hinting causes failure, uncomment */
924  // int badPartVal= partValue+1;
925  // keyParts[0].ptr= &badPartVal;
926 
927  CHECKNOTNULL(pInfo.trans= pNdb->startTransaction(tab, keyParts),
928  pNdb);
929  }
930  else
931  {
932  /* User Defined partitioning */
933  Uint32 partId= partValue % tab->getFragmentCount();
934  CHECKNOTNULL(pInfo.trans= pNdb->startTransaction(tab,
935  partId),
936  pNdb);
937  }
938  pInfo.valCount= 0;
939  pInfo.dKeyVal= partValue;
940 
942  opts.optionsPresent= NdbScanOperation::ScanOptions::SO_SCANFLAGS;
943  opts.scan_flags= NdbScanOperation::SF_MultiRange;
944 
945  // Define the scan operation for this partition.
946  CHECKNOTNULL(pInfo.op= pInfo.trans->scanIndex(idx->getDefaultRecord(),
947  tab->getDefaultRecord(),
949  NULL,
950  NULL,
951  &opts,
952  sizeof(opts)),
953  pInfo.trans);
954  }
955 
956  NdbIndexScanOperation* op= pInfo.op;
957 
958  if (usePrimary)
959  {
960  {
961  int dKeyVal= partValue;
962  int pKey2Val= r;
963  /* Scanning the primary index, set bound on the pk */
964  memcpy(NdbDictionary::getValuePtr(idxRecord,
965  boundBuf,
966  tab->getColumn(DistTabDKeyCol)->getAttrId()),
967  &dKeyVal,
968  sizeof(dKeyVal));
969  memcpy(NdbDictionary::getValuePtr(idxRecord,
970  boundBuf,
971  tab->getColumn(DistTabPKey2Col)->getAttrId()),
972  &pKey2Val,
973  sizeof(pKey2Val));
974 
975  }
976 
978  ib.low_key= boundBuf;
979  ib.low_key_count= 2;
980  ib.low_inclusive= true;
981  ib.high_key= ib.low_key;
982  ib.high_key_count= ib.low_key_count;
983  ib.high_inclusive= true;
984  ib.range_no= pInfo.valCount++;
985 
986  /* No partitioning info for native, PK index scan
987  * NDBAPI can determine it from PK */
988  Ndb::PartitionSpec pSpec;
989  pSpec.type= Ndb::PartitionSpec::PS_NONE;
990 
991  if (userDefined)
992  {
993  /* We'll provide partition info */
994  pSpec.type= Ndb::PartitionSpec::PS_USER_DEFINED;
995  pSpec.UserDefined.partitionId= partValue % tab->getFragmentCount();
996  }
997 
998  CHECK(op->setBound(idxRecord,
999  ib,
1000  &pSpec,
1001  sizeof(pSpec)),
1002  op);
1003  }
1004  else
1005  {
1006  Uint32 resultValAttrId= tab->getColumn(DistTabResultCol)->getAttrId();
1007  /* Scanning the secondary index, set bound on the result */
1008  {
1009  int resultVal= r*r;
1010  memcpy(NdbDictionary::getValuePtr(idxRecord,
1011  boundBuf,
1012  resultValAttrId),
1013  &resultVal,
1014  sizeof(resultVal));
1015  }
1016 
1017  NdbDictionary::setNull(idxRecord,
1018  boundBuf,
1019  resultValAttrId,
1020  false);
1021 
1023  ib.low_key= boundBuf;
1024  ib.low_key_count= 1;
1025  ib.low_inclusive= true;
1026  ib.high_key= ib.low_key;
1027  ib.high_key_count= ib.low_key_count;
1028  ib.high_inclusive= true;
1029  ib.range_no= pInfo.valCount++;
1030 
1031  Ndb::Key_part_ptr keyParts[2];
1032  keyParts[0].ptr= &partValue;
1033  keyParts[0].len= sizeof(partValue);
1034  keyParts[1].ptr= NULL;
1035  keyParts[1].len= 0;
1036 
1037  /* To test that bad hinting causes failure, uncomment */
1038  //int badPartVal= partValue+1;
1039  //keyParts[0].ptr= &badPartVal;
1040 
1041  Ndb::PartitionSpec pSpec;
1042  char* tabRow= NULL;
1043 
1044  if (userDefined)
1045  {
1046  /* We'll provide partition info */
1047  pSpec.type= Ndb::PartitionSpec::PS_USER_DEFINED;
1048  pSpec.UserDefined.partitionId= partValue % tab->getFragmentCount();
1049  }
1050  else
1051  {
1052  /* Can set either using an array of Key parts, or a KeyRecord
1053  * structure. Let's test both
1054  */
1055  if (rand() % 2)
1056  {
1057  //ndbout << "Using Key Parts to set range partition info" << endl;
1058  pSpec.type= Ndb::PartitionSpec::PS_DISTR_KEY_PART_PTR;
1059  pSpec.KeyPartPtr.tableKeyParts= keyParts;
1060  pSpec.KeyPartPtr.xfrmbuf= NULL;
1061  pSpec.KeyPartPtr.xfrmbuflen= 0;
1062  }
1063  else
1064  {
1065  //ndbout << "Using KeyRecord to set range partition info" << endl;
1066 
1067  /* Setup a row in NdbRecord format with the distkey value set */
1068  tabRow= (char*)malloc(NdbDictionary::getRecordRowLength(tabRecord));
1069  int& dKeyVal= *((int*) NdbDictionary::getValuePtr(tabRecord,
1070  tabRow,
1071  tab->getColumn(DistTabDKeyCol)->getAttrId()));
1072  dKeyVal= partValue;
1073  // dKeyVal= partValue + 1; // Test failue case
1074 
1075  pSpec.type= Ndb::PartitionSpec::PS_DISTR_KEY_RECORD;
1076  pSpec.KeyRecord.keyRecord= tabRecord;
1077  pSpec.KeyRecord.keyRow= tabRow;
1078  pSpec.KeyRecord.xfrmbuf= 0;
1079  pSpec.KeyRecord.xfrmbuflen= 0;
1080  }
1081  }
1082 
1083  CHECK(op->setBound(idxRecord,
1084  ib,
1085  &pSpec,
1086  sizeof(pSpec)),
1087  op);
1088 
1089  if (tabRow)
1090  free(tabRow);
1091  tabRow= NULL;
1092 
1093  }
1094  }
1095 
1096  for (int p=0; p < parts; p++)
1097  {
1098  PartInfo& pInfo= partInfo[p];
1099  //ndbout << "D-key val " << p << " has " << pInfo.valCount
1100  // << " ranges specified. " << endl;
1101  //ndbout << "Is Pruned? " << pInfo.op->getPruned() << endl;
1102  if (! pInfo.op->getPruned())
1103  {
1104  ndbout << "MRR Scan Operation should have been pruned, but was not." << endl;
1105  return NDBT_FAILED;
1106  }
1107 
1108  CHECK(pInfo.trans->execute(NdbTransaction::NoCommit), pInfo.trans);
1109 
1110  int resultCount=0;
1111 
1112  const char* resultPtr;
1113  int rc= 0;
1114 
1115  while ((rc= pInfo.op->nextResult(&resultPtr, true, true)) == 0)
1116  {
1117  int dKeyVal;
1118  memcpy(&dKeyVal, NdbDictionary::getValuePtr(tabRecord,
1119  resultPtr,
1120  tab->getColumn(DistTabDKeyCol)->getAttrId()),
1121  sizeof(dKeyVal));
1122 
1123  int pKey2Val;
1124  memcpy(&pKey2Val, NdbDictionary::getValuePtr(tabRecord,
1125  resultPtr,
1126  tab->getColumn(DistTabPKey2Col)->getAttrId()),
1127  sizeof(pKey2Val));
1128 
1129  int resultVal;
1130  memcpy(&resultVal, NdbDictionary::getValuePtr(tabRecord,
1131  resultPtr,
1132  tab->getColumn(DistTabResultCol)->getAttrId()),
1133  sizeof(resultVal));
1134 
1135  if ((dKeyVal != pInfo.dKeyVal) ||
1136  (resultVal != (pKey2Val * pKey2Val)))
1137  {
1138  ndbout << "Got bad values. Dkey : " << dKeyVal
1139  << " Pkey2 : " << pKey2Val
1140  << " Result : " << resultVal
1141  << endl;
1142  return NDBT_FAILED;
1143  }
1144  resultCount++;
1145  }
1146 
1147  if (rc != 1)
1148  {
1149  ndbout << "Got bad scan rc " << rc << endl;
1150  ndbout << "Error : " << pInfo.op->getNdbError().code << endl;
1151  ndbout << "Trans Error : " << pInfo.trans->getNdbError().code << endl;
1152  return NDBT_FAILED;
1153  }
1154 
1155  if (resultCount != pInfo.valCount)
1156  {
1157  ndbout << "Error resultCount was " << resultCount << endl;
1158  return NDBT_FAILED;
1159  }
1160  CHECK(pInfo.trans->execute(NdbTransaction::Commit), pInfo.trans);
1161  pInfo.trans->close();
1162  };
1163 
1164  ndbout << "Success" << endl;
1165 
1166  return NDBT_OK;
1167 }
1168 
1169 static int
1170 dist_scan(Ndb* pNdb, int records, int parts, bool usePk)
1171 {
1172  PartInfo* partInfo= new PartInfo[parts];
1173 
1174  NdbRestarter restarter;
1175  if(restarter.insertErrorInAllNodes(8050) != 0)
1176  {
1177  delete[] partInfo;
1178  return NDBT_FAILED;
1179  }
1180 
1181  for (int p=0; p<parts; p++)
1182  {
1183  partInfo[p].trans= NULL;
1184  partInfo[p].op= NULL;
1185  partInfo[p].dKeyVal= 0;
1186  partInfo[p].valCount= 0;
1187  }
1188 
1189  int result= dist_scan_body(pNdb,
1190  records,
1191  parts,
1192  partInfo,
1193  usePk);
1194 
1195  restarter.insertErrorInAllNodes(0);
1196  delete[] partInfo;
1197 
1198  return result;
1199 }
1200 
1201 static int
1202 run_dist_test(NDBT_Context* ctx, NDBT_Step* step)
1203 {
1204  int records= ctx->getNumRecords();
1205 
1206  /* Choose an interesting number of discrete
1207  * distribution key values to work with
1208  */
1209  int numTabPartitions= GETNDB(step)
1210  ->getDictionary()
1211  ->getTable(DistTabName)
1212  ->getFragmentCount();
1213  int numDkeyValues= 2*numTabPartitions + (rand() % 6);
1214  if (numDkeyValues > records)
1215  {
1216  // limit number of distributions keys to number of records
1217  numDkeyValues = records;
1218  }
1219 
1220  ndbout << "Table has " << numTabPartitions
1221  << " physical partitions" << endl;
1222  ndbout << "Testing with " << numDkeyValues
1223  << " discrete distribution key values " << endl;
1224 
1225  if (load_dist_table(GETNDB(step), records, numDkeyValues) != NDBT_OK)
1226  return NDBT_FAILED;
1227 
1228  /* Test access via PK ordered index (including Dkey) */
1229  if (dist_scan(GETNDB(step), records, numDkeyValues, true) != NDBT_OK)
1230  return NDBT_FAILED;
1231 
1232  /* Test access via secondary ordered index (not including Dkey) */
1233  if (dist_scan(GETNDB(step), records, numDkeyValues, false) != NDBT_OK)
1234  return NDBT_FAILED;
1235 
1236  return NDBT_OK;
1237 }
1238 
1239 
1240 
1241 NDBT_TESTSUITE(testPartitioning);
1242 TESTCASE("pk_dk",
1243  "Primary key operations with distribution key")
1244 {
1245  TC_PROPERTY("distributionkey", ~0);
1246  INITIALIZER(run_drop_table);
1247  INITIALIZER(run_create_table);
1248  INITIALIZER(run_pk_dk);
1249  INITIALIZER(run_drop_table);
1250 }
1251 TESTCASE("hash_index_dk",
1252  "Unique index operations with distribution key")
1253 {
1254  TC_PROPERTY("distributionkey", ~0);
1255  TC_PROPERTY("OrderedIndex", (unsigned)0);
1256  INITIALIZER(run_drop_table);
1257  INITIALIZER(run_create_table);
1258  INITIALIZER(run_create_pk_index);
1259  INITIALIZER(run_index_dk);
1260  INITIALIZER(run_create_pk_index_drop);
1261  INITIALIZER(run_drop_table);
1262 }
1263 TESTCASE("ordered_index_dk",
1264  "Ordered index operations with distribution key")
1265 {
1266  TC_PROPERTY("distributionkey", (unsigned)1);
1267  TC_PROPERTY("OrderedIndex", (unsigned)1);
1268  INITIALIZER(run_drop_table);
1269  INITIALIZER(run_create_table);
1270  INITIALIZER(run_create_pk_index);
1271  INITIALIZER(run_index_dk);
1272  INITIALIZER(run_create_pk_index_drop);
1273  INITIALIZER(run_drop_table);
1274 }
1275 TESTCASE("smart_scan",
1276  "Ordered index operations with distribution key")
1277 {
1278  TC_PROPERTY("OrderedIndex", (unsigned)1);
1279  INITIALIZER(run_drop_table);
1280  INITIALIZER(run_create_table_smart_scan);
1281  INITIALIZER(run_create_pk_index);
1282  INITIALIZER(run_index_dk);
1283  INITIALIZER(run_create_pk_index_drop);
1284  INITIALIZER(run_drop_table);
1285 }
1286 TESTCASE("startTransactionHint",
1287  "Test startTransactionHint wo/ distribution key")
1288 {
1289  /* If hint is incorrect, node failure occurs */
1290  TC_PROPERTY("distributionkey", (unsigned)0);
1291  INITIALIZER(run_drop_table);
1292  INITIALIZER(run_create_table);
1293  INITIALIZER(run_startHint);
1294  INITIALIZER(run_drop_table);
1295 }
1296 TESTCASE("startTransactionHint_dk",
1297  "Test startTransactionHint with distribution key")
1298 {
1299  /* If hint is incorrect, node failure occurs */
1300  TC_PROPERTY("distributionkey", (unsigned)~0);
1301  INITIALIZER(run_drop_table);
1302  INITIALIZER(run_create_table);
1303  INITIALIZER(run_startHint);
1304  INITIALIZER(run_drop_table);
1305 }
1306 TESTCASE("startTransactionHint_orderedIndex",
1307  "Test startTransactionHint and ordered index reads")
1308 {
1309  /* If hint is incorrect, node failure occurs */
1310  TC_PROPERTY("distributionkey", (unsigned)0);
1311  TC_PROPERTY("OrderedIndex", (unsigned)1);
1312  INITIALIZER(run_drop_table);
1313  INITIALIZER(run_create_table);
1314  INITIALIZER(run_create_pk_index);
1315  INITIALIZER(run_startHint_ordered_index);
1316  INITIALIZER(run_create_pk_index_drop);
1317  INITIALIZER(run_drop_table);
1318 }
1319 TESTCASE("startTransactionHint_orderedIndex_dk",
1320  "Test startTransactionHint and ordered index reads with distribution key")
1321 {
1322  /* If hint is incorrect, node failure occurs */
1323  TC_PROPERTY("distributionkey", (unsigned)~0);
1324  TC_PROPERTY("OrderedIndex", (unsigned)1);
1325  INITIALIZER(run_drop_table);
1326  INITIALIZER(run_create_table);
1327  INITIALIZER(run_create_pk_index);
1328  INITIALIZER(run_startHint_ordered_index);
1329  INITIALIZER(run_create_pk_index_drop);
1330  INITIALIZER(run_drop_table);
1331 }
1332 TESTCASE("startTransactionHint_orderedIndex_mrr_native",
1333  "Test hinting and MRR Ordered Index Scans for native partitioned table")
1334 {
1335  TC_PROPERTY("UserDefined", (unsigned)0);
1336  INITIALIZER(run_create_dist_table);
1337  INITIALIZER(run_dist_test);
1338  INITIALIZER(run_drop_dist_table);
1339 }
1340 TESTCASE("pk_userDefined",
1341  "Test primary key operations on table with user-defined partitioning")
1342 {
1343  /* Check PK ops against user-defined partitioned table */
1344  TC_PROPERTY("UserDefined", (unsigned) 1);
1345  INITIALIZER(run_drop_table);
1346  INITIALIZER(run_create_table);
1347  INITIALIZER(run_create_pk_index);
1348  INITIALIZER(run_pk_dk);
1349  INITIALIZER(run_create_pk_index_drop);
1350  INITIALIZER(run_drop_table);
1351 };
1352 TESTCASE("hash_index_userDefined",
1353  "Unique index operations on table with user-defined partitioning")
1354 {
1355  /* Check hash index ops against user-defined partitioned table */
1356  TC_PROPERTY("OrderedIndex", (unsigned)0);
1357  TC_PROPERTY("UserDefined", (unsigned)1);
1358  INITIALIZER(run_drop_table);
1359  INITIALIZER(run_create_table);
1360  INITIALIZER(run_create_pk_index);
1361  INITIALIZER(run_index_dk);
1362  INITIALIZER(run_create_pk_index_drop);
1363  INITIALIZER(run_drop_table);
1364 }
1365 TESTCASE("ordered_index_userDefined",
1366  "Ordered index operations on table with user-defined partitioning")
1367 {
1368  /* Check ordered index operations against user-defined partitioned table */
1369  TC_PROPERTY("OrderedIndex", (unsigned)1);
1370  TC_PROPERTY("UserDefined", (unsigned)1);
1371  INITIALIZER(run_drop_table);
1372  INITIALIZER(run_create_table);
1373  INITIALIZER(run_create_pk_index);
1374  INITIALIZER(run_index_dk);
1375  INITIALIZER(run_create_pk_index_drop);
1376  INITIALIZER(run_drop_table);
1377 }
1378 TESTCASE("startTransactionHint_orderedIndex_mrr_userDefined",
1379  "Test hinting and MRR Ordered Index Scans for user defined partitioned table")
1380 {
1381  TC_PROPERTY("UserDefined", (unsigned)1);
1382  INITIALIZER(run_create_dist_table);
1383  INITIALIZER(run_dist_test);
1384  INITIALIZER(run_drop_dist_table);
1385 }
1386 
1387 NDBT_TESTSUITE_END(testPartitioning);
1388 
1389 int main(int argc, const char** argv){
1390  ndb_init();
1391  NDBT_TESTSUITE_INSTANCE(testPartitioning);
1392  testPartitioning.setCreateTable(false);
1393  return testPartitioning.execute(argc, argv);
1394 }
1395 
1396 
1397