1 /*
2    Copyright (c) 2011, 2013, 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, version 2.0,
6    as published by the Free Software Foundation.
7 
8    This program is also distributed with certain software (including
9    but not limited to OpenSSL) that is licensed under separate terms,
10    as designated in a particular file or component or in included license
11    documentation.  The authors of MySQL hereby grant you an additional
12    permission to link the program and your derivative works with the
13    separately licensed software that they have included with MySQL.
14 
15    This program is distributed in the hope that it will be useful,
16    but WITHOUT ANY WARRANTY; without even the implied warranty of
17    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18    GNU General Public License, version 2.0, for more details.
19 
20    You should have received a copy of the GNU General Public License
21    along with this program; if not, write to the Free Software
22    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
23 */
24 
25 #include <NDBT_Test.hpp>
26 #include <NDBT_ReturnCodes.h>
27 #include <HugoTransactions.hpp>
28 #include <UtilTransactions.hpp>
29 #include <NdbRestarter.hpp>
30 #include <signaldata/DictTabInfo.hpp>
31 #include <Bitmask.hpp>
32 #include <random.h>
33 #include <HugoQueryBuilder.hpp>
34 #include <HugoQueries.hpp>
35 #include <NdbSchemaCon.hpp>
36 #include <ndb_version.h>
37 
38 static int faultToInject = 0;
39 
40 enum faultsToInject {
41   FI_START = 17001,
42   FI_END = 17521
43 };
44 
45 int
runLoadTable(NDBT_Context * ctx,NDBT_Step * step)46 runLoadTable(NDBT_Context* ctx, NDBT_Step* step)
47 {
48   int records = ctx->getNumRecords();
49   HugoTransactions hugoTrans(*ctx->getTab());
50   if (hugoTrans.loadTable(GETNDB(step), records) != 0){
51     return NDBT_FAILED;
52   }
53   return NDBT_OK;
54 }
55 
56 int
runClearTable(NDBT_Context * ctx,NDBT_Step * step)57 runClearTable(NDBT_Context* ctx, NDBT_Step* step)
58 {
59   UtilTransactions utilTrans(*ctx->getTab());
60   if (utilTrans.clearTable(GETNDB(step)) != 0){
61     return NDBT_FAILED;
62   }
63   return NDBT_OK;
64 }
65 
66 static
67 void
addMask(NDBT_Context * ctx,Uint32 val,const char * name)68 addMask(NDBT_Context* ctx, Uint32 val, const char * name)
69 {
70   Uint32 oldValue = 0;
71   do
72   {
73     oldValue = ctx->getProperty(name);
74     Uint32 newValue = oldValue | val;
75     if (ctx->casProperty(name, oldValue, newValue) == oldValue)
76       return;
77     NdbSleep_MilliSleep(5);
78   } while (true);
79 }
80 
81 int
runLookupJoin(NDBT_Context * ctx,NDBT_Step * step)82 runLookupJoin(NDBT_Context* ctx, NDBT_Step* step){
83   int loops = ctx->getNumLoops();
84   int joinlevel = ctx->getProperty("JoinLevel", 3);
85   int records = ctx->getNumRecords();
86   int queries = records/joinlevel;
87   int until_stopped = ctx->getProperty("UntilStopped", (Uint32)0);
88   Uint32 stepNo = step->getStepNo();
89 
90   int i = 0;
91   HugoQueryBuilder qb(GETNDB(step), ctx->getTab(), HugoQueryBuilder::O_LOOKUP);
92   qb.setJoinLevel(joinlevel);
93   const NdbQueryDef * query = qb.createQuery();
94   HugoQueries hugoTrans(*query);
95   while ((i<loops || until_stopped) && !ctx->isTestStopped())
96   {
97     g_info << i << ": ";
98     if (hugoTrans.runLookupQuery(GETNDB(step), queries))
99     {
100       g_info << endl;
101       return NDBT_FAILED;
102     }
103     addMask(ctx, (1 << stepNo), "Running");
104     i++;
105   }
106   g_info << endl;
107   return NDBT_OK;
108 }
109 
110 int
runLookupJoinError(NDBT_Context * ctx,NDBT_Step * step)111 runLookupJoinError(NDBT_Context* ctx, NDBT_Step* step){
112   int loops = ctx->getNumLoops();
113   int joinlevel = ctx->getProperty("JoinLevel", 8);
114   int records = ctx->getNumRecords();
115   int queries = records/joinlevel;
116   int until_stopped = ctx->getProperty("UntilStopped", (Uint32)0);
117   Uint32 stepNo = step->getStepNo();
118 
119   int i = 0;
120   HugoQueryBuilder qb(GETNDB(step), ctx->getTab(), HugoQueryBuilder::O_LOOKUP);
121   qb.setJoinLevel(joinlevel);
122   const NdbQueryDef * query = qb.createQuery();
123   HugoQueries hugoTrans(*query);
124 
125   NdbRestarter restarter;
126   int lookupFaults[] = {
127       5078,        // Pack TCKEYREF in ROUTE_ORD and send it via SPJ.
128       7240,        // DIGETNODESREQ returns error
129       17001, 17005, 17006, 17008,
130       17012, // testing abort in :execDIH_SCAN_TAB_CONF
131       17013, // Simulate DbspjErr::InvalidRequest
132       17020, 17021, 17022, // lookup_send() encounter dead node -> NodeFailure
133       17030, 17031, 17032, // LQHKEYREQ reply is LQHKEYREF('Invalid..')
134       17040, 17041, 17042, // lookup_parent_row -> OutOfQueryMemory
135       17050, 17051, 17052, 17053, // parseDA -> outOfSectionMem
136       17060, 17061, 17062, 17063, // scanIndex_parent_row -> outOfSectionMem
137       17070, 17071, 17072, // lookup_send.dupsec -> outOfSectionMem
138       17080, 17081, 17082, // lookup_parent_row -> OutOfQueryMemory
139       17120, 17121, // execTRANSID_AI -> OutOfRowMemory
140       17130,        // sendSignal(DIH_SCAN_GET_NODES_REQ)  -> import() failed
141       7234,         // sendSignal(DIH_SCAN_GET_NODES_CONF) -> import() failed (DIH)
142       17510,        // random failure when allocating section memory
143       17520, 17521  // failure (+random) from ::checkTableError()
144   };
145   loops =  faultToInject ? 1 : sizeof(lookupFaults)/sizeof(int);
146 
147   while ((i<loops || until_stopped) && !ctx->isTestStopped())
148   {
149     g_info << i << ": ";
150 
151     int inject_err = faultToInject ? faultToInject : lookupFaults[i];
152     int randomId = rand() % restarter.getNumDbNodes();
153     int nodeId = restarter.getDbNodeId(randomId);
154 
155     ndbout << "LookupJoinError: Injecting error "<<  inject_err <<
156       " in node " << nodeId << " loop "<< i << endl;
157 
158     if (restarter.getNodeStatus(nodeId) != NDB_MGM_NODE_STATUS_STARTED ||
159         restarter.insertErrorInNode(nodeId, inject_err) != 0)
160     {
161       ndbout << "Could not insert error in node "<< nodeId <<endl;
162       g_info << endl;
163       return NDBT_FAILED;
164     }
165 
166     // It'd be better if test could differentiates failures from
167     // fault injection and others.
168     // We expect to fail, and it's a failure if we don't
169     if (!hugoTrans.runLookupQuery(GETNDB(step), queries))
170     {
171       g_info << "LookUpJoinError didn't fail as expected."<< endl;
172       // return NDBT_FAILED;
173     }
174 
175     addMask(ctx, (1 << stepNo), "Running");
176     i++;
177   }
178   g_info << endl;
179   return NDBT_OK;
180 }
181 
182 int
runScanJoin(NDBT_Context * ctx,NDBT_Step * step)183 runScanJoin(NDBT_Context* ctx, NDBT_Step* step){
184   int loops = ctx->getNumLoops();
185   int joinlevel = ctx->getProperty("JoinLevel", 3);
186   int until_stopped = ctx->getProperty("UntilStopped", (Uint32)0);
187   Uint32 stepNo = step->getStepNo();
188 
189   int i = 0;
190   HugoQueryBuilder qb(GETNDB(step), ctx->getTab(), HugoQueryBuilder::O_SCAN);
191   qb.setJoinLevel(joinlevel);
192   const NdbQueryDef * query = qb.createQuery();
193   HugoQueries hugoTrans(* query);
194   while ((i<loops || until_stopped) && !ctx->isTestStopped())
195   {
196     g_info << i << ": ";
197     if (hugoTrans.runScanQuery(GETNDB(step)))
198     {
199       g_info << endl;
200       return NDBT_FAILED;
201     }
202     addMask(ctx, (1 << stepNo), "Running");
203     i++;
204   }
205   g_info << endl;
206   return NDBT_OK;
207 }
208 
209 int
runScanJoinError(NDBT_Context * ctx,NDBT_Step * step)210 runScanJoinError(NDBT_Context* ctx, NDBT_Step* step){
211   int loops = ctx->getNumLoops();
212   int joinlevel = ctx->getProperty("JoinLevel", 3);
213   int until_stopped = ctx->getProperty("UntilStopped", (Uint32)0);
214   Uint32 stepNo = step->getStepNo();
215 
216   int i = 0;
217   HugoQueryBuilder qb(GETNDB(step), ctx->getTab(), HugoQueryBuilder::O_SCAN);
218   qb.setJoinLevel(joinlevel);
219   const NdbQueryDef * query = qb.createQuery();
220   HugoQueries hugoTrans(* query);
221 
222   NdbRestarter restarter;
223   int scanFaults[] = {
224       7240,        // DIGETNODESREQ returns error
225       17002, 17004, 17005, 17006, 17008,
226       17012, // testing abort in :execDIH_SCAN_TAB_CONF
227       17013, // Simulate DbspjErr::InvalidRequest
228       17020, 17021, 17022, // lookup_send() encounter dead node -> NodeFailure
229       17030, 17031, 17032, // LQHKEYREQ reply is LQHKEYREF('Invalid..')
230       17040, 17041, 17042, // lookup_parent_row -> OutOfQueryMemory
231       17050, 17051, 17052, 17053, // parseDA -> outOfSectionMem
232       17060, 17061, 17062, 17063, // scanIndex_parent_row -> outOfSectionMem
233       17070, 17071, 17072, // lookup_send.dupsec -> outOfSectionMem
234       17080, 17081, 17082, // lookup_parent_row -> OutOfQueryMemory
235       17090, 17091, 17092, 17093, // scanIndex_send -> OutOfQueryMemory
236       17100, // scanFrag_sends invalid schema version, to get a SCAN_FRAGREF
237       17110, 17111, 17112, // scanIndex_sends invalid schema version, to get a SCAN_FRAGREF
238       17120, 17121, // execTRANSID_AI -> OutOfRowMemory
239       17510,        // random failure when allocating section memory
240       17520, 17521  // failure (+random) from TableRecord::checkTableError()
241   };
242   loops =  faultToInject ? 1 : sizeof(scanFaults)/sizeof(int);
243 
244   while ((i<loops || until_stopped) && !ctx->isTestStopped())
245   {
246     g_info << i << ": ";
247 
248     int inject_err = faultToInject ? faultToInject : scanFaults[i];
249     int randomId = rand() % restarter.getNumDbNodes();
250     int nodeId = restarter.getDbNodeId(randomId);
251 
252     ndbout << "ScanJoin: Injecting error "<<  inject_err <<
253               " in node " << nodeId << " loop "<< i<< endl;
254 
255     if (restarter.insertErrorInNode(nodeId, inject_err) != 0)
256     {
257       ndbout << "Could not insert error in node "<< nodeId <<endl;
258       return NDBT_FAILED;
259     }
260 
261     // It'd be better if test could differentiates failures from
262     // fault injection and others.
263     // We expect to fail, and it's a failure if we don't
264     if (!hugoTrans.runScanQuery(GETNDB(step)))
265     {
266       g_info << "ScanJoinError didn't fail as expected."<< endl;
267       // return NDBT_FAILED;
268     }
269 
270     addMask(ctx, (1 << stepNo), "Running");
271     i++;
272   }
273 
274   g_info << endl;
275   return NDBT_OK;
276 }
277 
278 int
runJoin(NDBT_Context * ctx,NDBT_Step * step)279 runJoin(NDBT_Context* ctx, NDBT_Step* step){
280   int loops = ctx->getNumLoops();
281   int joinlevel = ctx->getProperty("JoinLevel", 3);
282   int records = ctx->getNumRecords();
283   int queries = records/joinlevel;
284   int until_stopped = ctx->getProperty("UntilStopped", (Uint32)0);
285   int inject_err = ctx->getProperty("ErrorCode");
286   Uint32 stepNo = step->getStepNo();
287 
288   int i = 0;
289   HugoQueryBuilder qb1(GETNDB(step), ctx->getTab(), HugoQueryBuilder::O_SCAN);
290   HugoQueryBuilder qb2(GETNDB(step), ctx->getTab(), HugoQueryBuilder::O_LOOKUP);
291   qb1.setJoinLevel(joinlevel);
292   qb2.setJoinLevel(joinlevel);
293   const NdbQueryDef * q1 = qb1.createQuery();
294   const NdbQueryDef * q2 = qb2.createQuery();
295   HugoQueries hugoTrans1(* q1);
296   HugoQueries hugoTrans2(* q2);
297   NdbRestarter restarter;
298 
299   if (inject_err)
300   {
301     ndbout << "insertErrorInAllNodes("<<inject_err<<")"<<endl;
302     if (restarter.insertErrorInAllNodes(inject_err) != 0){
303       g_info << "Could not insert error in all nodes "<<endl;
304       return NDBT_FAILED;
305     }
306   }
307   while ((i<loops || until_stopped) && !ctx->isTestStopped())
308   {
309     g_info << i << ": ";
310     if (hugoTrans1.runScanQuery(GETNDB(step)))
311     {
312       g_info << endl;
313       return NDBT_FAILED;
314     }
315     if (hugoTrans2.runLookupQuery(GETNDB(step), queries))
316     {
317       g_info << endl;
318       return NDBT_FAILED;
319     }
320     i++;
321     addMask(ctx, (1 << stepNo), "Running");
322   }
323   g_info << endl;
324   restarter.insertErrorInAllNodes(0);
325   return NDBT_OK;
326 }
327 
328 int
runRestarter(NDBT_Context * ctx,NDBT_Step * step)329 runRestarter(NDBT_Context* ctx, NDBT_Step* step)
330 {
331   int result = NDBT_OK;
332   int loops = ctx->getNumLoops();
333   int waitprogress = ctx->getProperty("WaitProgress", (unsigned)0);
334   int randnode = ctx->getProperty("RandNode", (unsigned)0);
335   NdbRestarter restarter;
336   int i = 0;
337   int lastId = 0;
338 
339   if (restarter.getNumDbNodes() < 2){
340     ctx->stopTest();
341     return NDBT_OK;
342   }
343 
344   if(restarter.waitClusterStarted() != 0){
345     g_err << "Cluster failed to start" << endl;
346     return NDBT_FAILED;
347   }
348 
349   loops *= (restarter.getNumDbNodes() > 2 ? 2 : restarter.getNumDbNodes());
350   if (loops < restarter.getNumDbNodes())
351     loops = restarter.getNumDbNodes();
352 
353   NdbSleep_MilliSleep(200);
354   Uint32 running = ctx->getProperty("Running", (Uint32)0);
355   while (running == 0 && !ctx->isTestStopped())
356   {
357     NdbSleep_MilliSleep(100);
358     running = ctx->getProperty("Running", (Uint32)0);
359   }
360 
361   if (ctx->isTestStopped())
362     return NDBT_FAILED;
363 
364   while(i<loops && result != NDBT_FAILED && !ctx->isTestStopped()){
365 
366     int id = lastId % restarter.getNumDbNodes();
367     if (randnode == 1)
368     {
369       id = rand() % restarter.getNumDbNodes();
370     }
371     int nodeId = restarter.getDbNodeId(id);
372     ndbout << "Restart node " << nodeId << endl;
373 
374     if(restarter.restartOneDbNode(nodeId, false, true, true) != 0){
375       g_err << "Failed to restartNextDbNode" << endl;
376       result = NDBT_FAILED;
377       break;
378     }
379 
380     if (restarter.waitNodesNoStart(&nodeId, 1))
381     {
382       g_err << "Failed to waitNodesNoStart" << endl;
383       result = NDBT_FAILED;
384       break;
385     }
386 
387     if (waitprogress)
388     {
389       Uint32 maxwait = 60;
390       ndbout_c("running: 0x%.8x", running);
391       for (Uint32 checks = 0; checks < 3 && !ctx->isTestStopped(); checks++)
392       {
393         ctx->setProperty("Running", (Uint32)0);
394         for (; maxwait != 0 && !ctx->isTestStopped(); maxwait--)
395         {
396           if ((ctx->getProperty("Running", (Uint32)0) & running) == running)
397             goto ok;
398           NdbSleep_SecSleep(1);
399         }
400 
401         if (ctx->isTestStopped())
402         {
403           g_err << "Test stopped while waiting for progress!" << endl;
404           return NDBT_FAILED;
405         }
406 
407         g_err << "No progress made!!" << endl;
408         return NDBT_FAILED;
409     ok:
410         g_err << "Progress made!! " << endl;
411       }
412     }
413 
414     if (restarter.startNodes(&nodeId, 1))
415     {
416       g_err << "Failed to start node" << endl;
417       result = NDBT_FAILED;
418       break;
419     }
420 
421     if(restarter.waitClusterStarted() != 0){
422       g_err << "Cluster failed to start" << endl;
423       result = NDBT_FAILED;
424       break;
425     }
426 
427     if (waitprogress)
428     {
429       Uint32 maxwait = 60;
430       ndbout_c("running: 0x%.8x", running);
431       for (Uint32 checks = 0; checks < 3 && !ctx->isTestStopped(); checks++)
432       {
433         ctx->setProperty("Running", (Uint32)0);
434         for (; maxwait != 0 && !ctx->isTestStopped(); maxwait--)
435         {
436           if ((ctx->getProperty("Running", (Uint32)0) & running) == running)
437             goto ok2;
438           NdbSleep_SecSleep(1);
439         }
440 
441         if (ctx->isTestStopped())
442         {
443           g_err << "Test stopped while waiting for progress!" << endl;
444           return NDBT_FAILED;
445         }
446 
447         g_err << "No progress made!!" << endl;
448         return NDBT_FAILED;
449     ok2:
450         g_err << "Progress made!! " << endl;
451         ctx->setProperty("Running", (Uint32)0);
452       }
453     }
454 
455     lastId++;
456     i++;
457   }
458 
459   ctx->stopTest();
460 
461   return result;
462 }
463 
464 static const int nt2StrLen = 20;
465 
466 static int
createNegativeSchema(NDBT_Context * ctx,NDBT_Step * step)467 createNegativeSchema(NDBT_Context* ctx, NDBT_Step* step)
468 {
469   for (int i = 0; i<2; i++)
470   {
471     NdbDictionary::Column::Type type = NdbDictionary::Column::Undefined;
472     Uint32 arraySize = 0;
473     const char* tabName = NULL;
474     const char* ordIdxName = NULL;
475     const char* unqIdxName = NULL;
476     switch (i)
477     {
478     case 0:
479       type = NdbDictionary::Column::Int;
480       arraySize = 1;
481       tabName = "nt1";
482       ordIdxName = "nt1_oix";
483       unqIdxName = "nt1_uix";
484       break;
485     case 1:
486       type = NdbDictionary::Column::Varchar;
487       arraySize = nt2StrLen;
488       tabName = "nt2";
489       ordIdxName = "nt2_oix";
490       unqIdxName = "nt2_uix";
491       break;
492     }
493 
494     /****************************************************************
495      *	Create table nt1 and attributes.
496      ***************************************************************/
497     NDBT_Attribute pk1("pk1", type, arraySize, true);
498     NDBT_Attribute pk2("pk2", type, arraySize, true);
499     NDBT_Attribute oi1("oi1", type, arraySize);
500     NDBT_Attribute oi2("oi2", type, arraySize);
501     NDBT_Attribute ui1("ui1", type, arraySize);
502     NDBT_Attribute ui2("ui2", type, arraySize);
503 
504     NdbDictionary::Column* columns[] = {&pk1, &pk2, &oi1, &oi2, &ui1, &ui2};
505 
506     const NDBT_Table tabDef(tabName, sizeof columns/sizeof columns[0], columns);
507 
508     Ndb* const ndb = step->getNdb();
509 
510     NdbDictionary::Dictionary* const dictionary = ndb->getDictionary();
511 
512     dictionary->dropTable(tabName);
513     require(dictionary->createTable(tabDef) == 0);
514 
515     // Create ordered index on oi1,oi2.
516     NdbDictionary::Index ordIdx(ordIdxName);
517     require(ordIdx.setTable(tabName) == 0);
518     ordIdx.setType(NdbDictionary::Index::OrderedIndex);
519     ordIdx.setLogging(false);
520     require(ordIdx.addColumn(oi1) == 0);
521     require(ordIdx.addColumn(oi2) == 0);
522     require(dictionary->createIndex(ordIdx, tabDef) == 0);
523 
524     // Create unique index on ui1,ui2.
525     NdbDictionary::Index unqIdx(unqIdxName);
526     require(unqIdx.setTable(tabName) == 0);
527     unqIdx.setType(NdbDictionary::Index::UniqueHashIndex);
528     unqIdx.setLogging(true);
529     require(unqIdx.addColumn(ui1) == 0);
530     require(unqIdx.addColumn(ui2) == 0);
531     require(dictionary->createIndex(unqIdx, tabDef) == 0);
532   } // for (...
533   return NDBT_OK;
534 }
535 
536 /* Query-related error codes. Used for negative testing. */
537 #define QRY_TOO_FEW_KEY_VALUES 4801
538 #define QRY_TOO_MANY_KEY_VALUES 4802
539 #define QRY_OPERAND_HAS_WRONG_TYPE 4803
540 #define QRY_CHAR_OPERAND_TRUNCATED 4804
541 #define QRY_NUM_OPERAND_RANGE 4805
542 #define QRY_MULTIPLE_PARENTS 4806
543 #define QRY_UNKNOWN_PARENT 4807
544 #define QRY_UNRELATED_INDEX 4809
545 #define QRY_WRONG_INDEX_TYPE 4810
546 #define QRY_DEFINITION_TOO_LARGE 4812
547 #define QRY_RESULT_ROW_ALREADY_DEFINED 4814
548 #define QRY_HAS_ZERO_OPERATIONS 4815
549 #define QRY_ILLEGAL_STATE 4817
550 #define QRY_WRONG_OPERATION_TYPE 4820
551 #define QRY_MULTIPLE_SCAN_SORTED 4824
552 #define QRY_EMPTY_PROJECTION 4826
553 
554 /* Various error codes that are not specific to NdbQuery. */
555 static const int Err_FunctionNotImplemented = 4003;
556 static const int Err_UnknownColumn = 4004;
557 static const int Err_WrongFieldLength = 4209;
558 static const int Err_InvalidRangeNo = 4286;
559 static const int Err_DifferentTabForKeyRecAndAttrRec = 4287;
560 static const int Err_KeyIsNULL = 4316;
561 
562 /**
563  * Context data for negative tests of api extensions.
564  */
565 class NegativeTest
566 {
567 public:
568   // Static wrapper for each test case.
keyTest(NDBT_Context * ctx,NDBT_Step * step)569   static int keyTest(NDBT_Context* ctx, NDBT_Step* step)
570   { return NegativeTest(ctx, step).runKeyTest();}
571 
graphTest(NDBT_Context * ctx,NDBT_Step * step)572   static int graphTest(NDBT_Context* ctx, NDBT_Step* step)
573   { return NegativeTest(ctx, step).runGraphTest();}
574 
setBoundTest(NDBT_Context * ctx,NDBT_Step * step)575   static int setBoundTest(NDBT_Context* ctx, NDBT_Step* step)
576   { return NegativeTest(ctx, step).runSetBoundTest();}
577 
valueTest(NDBT_Context * ctx,NDBT_Step * step)578   static int valueTest(NDBT_Context* ctx, NDBT_Step* step)
579   { return NegativeTest(ctx, step).runValueTest();}
580 
featureDisabledTest(NDBT_Context * ctx,NDBT_Step * step)581   static int featureDisabledTest(NDBT_Context* ctx, NDBT_Step* step)
582   { return NegativeTest(ctx, step).runFeatureDisabledTest();}
583 
584 private:
585   Ndb* m_ndb;
586   NdbDictionary::Dictionary* m_dictionary;
587   const NdbDictionary::Table* m_nt1Tab;
588   const NdbDictionary::Index* m_nt1OrdIdx;
589   const NdbDictionary::Index* m_nt1UnqIdx;
590   const NdbDictionary::Table* m_nt2Tab;
591   const NdbDictionary::Index* m_nt2OrdIdx;
592   const NdbDictionary::Index* m_nt2UnqIdx;
593 
594   NegativeTest(NDBT_Context* ctx, NDBT_Step* step);
595 
596   // Tests
597   int runKeyTest() const;
598   int runGraphTest() const;
599   int runSetBoundTest() const;
600   int runValueTest() const;
601   int runFeatureDisabledTest() const;
602   // No copy.
603   NegativeTest(const NegativeTest&);
604   NegativeTest& operator=(const NegativeTest&);
605 };
606 
NegativeTest(NDBT_Context * ctx,NDBT_Step * step)607 NegativeTest::NegativeTest(NDBT_Context* ctx, NDBT_Step* step)
608 {
609   m_ndb = step->getNdb();
610   m_dictionary = m_ndb->getDictionary();
611 
612   m_nt1Tab = m_dictionary->getTable("nt1");
613   require(m_nt1Tab != NULL);
614 
615   m_nt1OrdIdx = m_dictionary->getIndex("nt1_oix", "nt1");
616   require(m_nt1OrdIdx != NULL);
617 
618   m_nt1UnqIdx = m_dictionary->getIndex("nt1_uix", "nt1");
619   require(m_nt1UnqIdx != NULL);
620 
621   m_nt2Tab = m_dictionary->getTable("nt2");
622   require(m_nt2Tab != NULL);
623 
624   m_nt2OrdIdx = m_dictionary->getIndex("nt2_oix", "nt2");
625   require(m_nt2OrdIdx != NULL);
626 
627   m_nt2UnqIdx = m_dictionary->getIndex("nt2_uix", "nt2");
628   require(m_nt2UnqIdx != NULL);
629 }
630 
631 int
runKeyTest() const632 NegativeTest::runKeyTest() const
633 {
634   // Make key with too long strings
635   {
636     NdbQueryBuilder* const builder = NdbQueryBuilder::create();
637     const char* longTxt= "x012345678901234567890123456789";
638     const NdbQueryOperand* const keyOperands[] =
639       {builder->constValue(longTxt), builder->constValue(longTxt), NULL};
640 
641     if (builder->readTuple(m_nt2Tab, keyOperands) != NULL ||
642         builder->getNdbError().code != QRY_CHAR_OPERAND_TRUNCATED)
643     {
644       g_err << "Lookup with truncated char values gave unexpected result.";
645       builder->destroy();
646       return NDBT_FAILED;
647     }
648     builder->destroy();
649   }
650 
651   // Make key with integer value outside column range.
652   if (false) // Temporarily disabled.
653   {
654     NdbQueryBuilder* const builder = NdbQueryBuilder::create();
655     const NdbQueryOperand* const keyOperands[] =
656       {builder->constValue(1ull), builder->constValue(~0ull), NULL};
657 
658     if (builder->readTuple(m_nt1Tab, keyOperands) != NULL ||
659         builder->getNdbError().code != QRY_NUM_OPERAND_RANGE)
660     {
661       g_err << "Lookup with integer value outside column range gave unexpected result.";
662       builder->destroy();
663       return NDBT_FAILED;
664     }
665     builder->destroy();
666   }
667 
668   // Make key with too few fields
669   {
670     NdbQueryBuilder* const builder = NdbQueryBuilder::create();
671     const NdbQueryOperand* const keyOperands[] =
672       {builder->constValue(1), NULL};
673 
674     if (builder->readTuple(m_nt1Tab, keyOperands) != NULL ||
675         builder->getNdbError().code != QRY_TOO_FEW_KEY_VALUES)
676     {
677       g_err << "Read with too few key values gave unexpected result.";
678       builder->destroy();
679       return NDBT_FAILED;
680     }
681     builder->destroy();
682   }
683 
684   // Make key with too many fields
685   {
686     NdbQueryBuilder* const builder = NdbQueryBuilder::create();
687     const NdbQueryOperand* const keyOperands[] =
688       {builder->constValue(1), builder->constValue(1), builder->constValue(1), NULL};
689 
690     if (builder->readTuple(m_nt1Tab, keyOperands) != NULL ||
691         builder->getNdbError().code != QRY_TOO_MANY_KEY_VALUES)
692     {
693       g_err << "Read with too many key values gave unexpected result.";
694       builder->destroy();
695       return NDBT_FAILED;
696     }
697     builder->destroy();
698   }
699 
700   // Make key with fields of wrong type.
701   {
702     NdbQueryBuilder* const builder = NdbQueryBuilder::create();
703     const NdbQueryOperand* const keyOperands[] =
704       {builder->constValue(1), builder->constValue("xxx"), NULL};
705 
706     if (builder->readTuple(m_nt1Tab, keyOperands) != NULL ||
707         builder->getNdbError().code != QRY_OPERAND_HAS_WRONG_TYPE)
708     {
709       g_err << "Read with key values of wrong type gave unexpected result.";
710       builder->destroy();
711       return NDBT_FAILED;
712     }
713     builder->destroy();
714   }
715 
716   // Make key with unknown column. Try preparing failed NdbQueryBuilder.
717   {
718     NdbQueryBuilder* const builder = NdbQueryBuilder::create();
719     const NdbQueryOperand* const keyOperands[] =
720       {builder->constValue(1), builder->constValue(1), NULL};
721 
722     const NdbQueryLookupOperationDef* parentOperation
723       = builder->readTuple(m_nt1Tab, keyOperands);
724     require(parentOperation != NULL);
725 
726     if (builder->linkedValue(parentOperation, "unknown_col") != NULL ||
727         builder->getNdbError().code != Err_UnknownColumn)
728     {
729       g_err << "Link to unknown column gave unexpected result.";
730       builder->destroy();
731       return NDBT_FAILED;
732     }
733     if (builder->prepare() != NULL)
734     {
735       g_err << "prepare() on failed query gave non-NULL result.";
736       builder->destroy();
737       return NDBT_FAILED;
738     }
739     builder->destroy();
740   }
741 
742   // Give too few parameter values.
743   {
744     NdbQueryBuilder* const builder = NdbQueryBuilder::create();
745     const NdbQueryOperand* const keyOperands[] =
746       {builder->paramValue(), builder->paramValue(), NULL};
747 
748     require(builder->readTuple(m_nt1Tab, keyOperands) != NULL);
749     const NdbQueryDef* const queryDef = builder->prepare();
750     require(queryDef != NULL);
751     builder->destroy();
752 
753     const NdbQueryParamValue params[] = {
754       Uint32(1),
755       NdbQueryParamValue()
756     };
757 
758     NdbTransaction* const trans = m_ndb->startTransaction();
759     NdbQuery* const query = trans->createQuery(queryDef, params);
760 
761     if (query != NULL || trans->getNdbError().code != Err_KeyIsNULL)
762     {
763       g_err << "Read with too few parameter values gave unexpected result.";
764       m_ndb->closeTransaction(trans);
765       queryDef->destroy();
766       return NDBT_FAILED;
767     }
768     m_ndb->closeTransaction(trans);
769     queryDef->destroy();
770   }
771 
772   /**
773    * Check for too many parameter values currently not possible. Must decide if
774    * NdbQueryParamValue with m_type==Type_NULL should be mandatory end marker or
775    * used for specifying actual null values.
776    */
777   return NDBT_OK;
778 } // NegativeTest::runKeyTest()
779 
780 
781 int
runGraphTest() const782 NegativeTest::runGraphTest() const
783 {
784   // Try preparing empty NdbQueryBuilder
785   {
786     NdbQueryBuilder* const builder = NdbQueryBuilder::create();
787     if (builder->prepare() != NULL ||
788         builder->getNdbError().code != QRY_HAS_ZERO_OPERATIONS)
789     {
790       g_err << "prepare() on empty query gave non-NULL result.";
791       builder->destroy();
792       return NDBT_FAILED;
793     }
794     builder->destroy();
795   }
796 
797   // Make query with too many operations.
798   {
799     NdbQueryBuilder* const builder = NdbQueryBuilder::create();
800     const NdbQueryOperand* const keyOperands[] =
801       {builder->constValue(1), builder->constValue(1), NULL};
802 
803     const NdbQueryLookupOperationDef* const parentOperation
804       = builder->readTuple(m_nt1Tab, keyOperands);
805     require(parentOperation != NULL);
806 
807     const NdbQueryOperand* const childOperands[] =
808       {builder->linkedValue(parentOperation, "ui1"),
809        builder->linkedValue(parentOperation, "oi1"),
810       NULL};
811 
812     for (Uint32 i = 0; i<32; i++)
813     {
814       const NdbQueryLookupOperationDef* const childOperation
815         = builder->readTuple(m_nt1Tab, childOperands);
816       if (i < 31)
817       {
818         require(childOperation != NULL);
819       }
820       else if (childOperation != NULL &&
821                builder->getNdbError().code != QRY_DEFINITION_TOO_LARGE)
822       {
823         g_err << "Building query with too many operations gave unexpected "
824           "result.";
825         builder->destroy();
826         return NDBT_FAILED;
827       }
828     }
829     builder->destroy();
830   }
831 
832   // Make query with two root operations.
833   {
834     NdbQueryBuilder* const builder = NdbQueryBuilder::create();
835     const NdbQueryOperand* const keyOperands[] =
836       {builder->constValue(1), builder->constValue(1), NULL};
837 
838     const NdbQueryLookupOperationDef* const root1
839       = builder->readTuple(m_nt1Tab, keyOperands);
840     require(root1 != NULL);
841 
842     if (builder->readTuple(m_nt1Tab, keyOperands)!= NULL ||
843         builder->getNdbError().code != QRY_UNKNOWN_PARENT)
844     {
845       g_err << "Query with two root operations gave unexpected result.";
846       builder->destroy();
847       return NDBT_FAILED;
848     };
849     builder->destroy();
850   }
851 
852   // Try lookup on ordered index.
853   {
854     NdbQueryBuilder* const builder = NdbQueryBuilder::create();
855     const NdbQueryOperand* const keyOperands[] =
856       {builder->constValue(1), builder->constValue(1), NULL};
857 
858     if (builder->readTuple(m_nt1OrdIdx, m_nt1Tab, keyOperands) != NULL ||
859         builder->getNdbError().code != QRY_WRONG_INDEX_TYPE)
860     {
861       g_err << "Lookup on ordered index gave unexpected result.";
862       builder->destroy();
863       return NDBT_FAILED;
864     }
865     builder->destroy();
866   }
867 
868   // Try lookup on index on wrong table.
869   {
870     NdbQueryBuilder* const builder = NdbQueryBuilder::create();
871     const NdbQueryOperand* const keyOperands[] =
872       {builder->constValue(1), builder->constValue(1), NULL};
873 
874     if (builder->readTuple(m_nt2OrdIdx, m_nt1Tab, keyOperands) != NULL ||
875         builder->getNdbError().code != QRY_UNRELATED_INDEX)
876     {
877       g_err << "Lookup on unrelated index gave unexpected result.";
878       builder->destroy();
879       return NDBT_FAILED;
880     }
881     builder->destroy();
882   }
883 
884   // Try scanning unique index.
885   {
886     NdbQueryBuilder* const builder = NdbQueryBuilder::create();
887     const NdbQueryOperand* const boundOperands[] =
888       {builder->constValue(1), NULL};
889     const NdbQueryIndexBound bound(boundOperands);
890 
891     if (builder->scanIndex(m_nt1UnqIdx, m_nt1Tab, &bound) != NULL ||
892         builder->getNdbError().code != QRY_WRONG_INDEX_TYPE)
893     {
894       g_err << "Scan of unique index gave unexpected result.";
895       builder->destroy();
896       return NDBT_FAILED;
897     }
898     builder->destroy();
899   }
900 
901   // Try scanning index on wrong table.
902   {
903     NdbQueryBuilder* const builder = NdbQueryBuilder::create();
904     const NdbQueryOperand* const boundOperands[] =
905       {builder->constValue(1), NULL};
906     const NdbQueryIndexBound bound(boundOperands);
907 
908     if (builder->scanIndex(m_nt2OrdIdx, m_nt1Tab, &bound) != NULL ||
909         builder->getNdbError().code != QRY_UNRELATED_INDEX)
910     {
911       g_err << "Scan of unrelated index gave unexpected result.";
912       builder->destroy();
913       return NDBT_FAILED;
914     }
915     builder->destroy();
916   }
917 
918   // Try adding a scan child to a lookup root.
919   {
920     NdbQueryBuilder* const builder = NdbQueryBuilder::create();
921     const NdbQueryOperand* const keyOperands[] =
922       {builder->constValue(1), builder->constValue(1), NULL};
923 
924     const NdbQueryLookupOperationDef* parentOperation
925       = builder->readTuple(m_nt1Tab, keyOperands);
926     require(parentOperation != NULL);
927 
928     const NdbQueryOperand* const childOperands[] =
929       {builder->linkedValue(parentOperation, "ui1"),
930        builder->linkedValue(parentOperation, "oi1"),
931       NULL};
932     const NdbQueryIndexBound bound(childOperands);
933 
934     if (builder->scanIndex(m_nt1OrdIdx, m_nt1Tab, &bound) != NULL ||
935         builder->getNdbError().code != QRY_WRONG_OPERATION_TYPE)
936     {
937       g_err << "Lookup with scan child gave unexpected result.";
938       builder->destroy();
939       return NDBT_FAILED;
940     }
941     builder->destroy();
942   }
943 
944   // Try adding a sorted child scan to a query.
945   {
946     NdbQueryBuilder* const builder = NdbQueryBuilder::create();
947 
948     const NdbQueryTableScanOperationDef* parentOperation
949       = builder->scanTable(m_nt1Tab);
950     require(parentOperation != NULL);
951 
952     const NdbQueryOperand* const childOperands[] =
953       {builder->linkedValue(parentOperation, "ui1"),
954       NULL};
955     const NdbQueryIndexBound bound(childOperands);
956     NdbQueryOptions childOptions;
957     childOptions.setOrdering(NdbQueryOptions::ScanOrdering_ascending);
958 
959     if (builder->scanIndex(m_nt1OrdIdx, m_nt1Tab, &bound, &childOptions) != NULL ||
960         builder->getNdbError().code != QRY_MULTIPLE_SCAN_SORTED)
961     {
962       g_err << "Query with sorted child scan gave unexpected result.";
963       builder->destroy();
964       return NDBT_FAILED;
965     }
966     builder->destroy();
967   }
968 
969   /**
970    * Try adding a child operation with two parents that are not descendants of each
971    * other (i.e. a diamond-shaped query graph).
972    */
973   {
974     NdbQueryBuilder* const builder = NdbQueryBuilder::create();
975     const NdbQueryOperand* const rootKey[] =
976       {builder->constValue(1), builder->constValue(1), NULL};
977 
978     const NdbQueryLookupOperationDef* rootOperation
979       = builder->readTuple(m_nt1Tab, rootKey);
980     require(rootOperation != NULL);
981 
982     const NdbQueryOperand* const leftKey[] =
983       {builder->linkedValue(rootOperation, "ui1"), builder->constValue(1), NULL};
984 
985     const NdbQueryLookupOperationDef* leftOperation
986       = builder->readTuple(m_nt1Tab, leftKey);
987     require(leftOperation != NULL);
988 
989     const NdbQueryOperand* const rightKey[] =
990       {builder->linkedValue(rootOperation, "ui1"), builder->constValue(1), NULL};
991 
992     const NdbQueryLookupOperationDef* rightOperation
993       = builder->readTuple(m_nt1Tab, rightKey);
994     require(rightOperation != NULL);
995 
996     const NdbQueryOperand* const bottomKey[] =
997       {builder->linkedValue(leftOperation, "ui1"),
998        builder->linkedValue(rightOperation, "oi1"),
999        NULL};
1000 
1001     if (builder->readTuple(m_nt1Tab, bottomKey) != NULL ||
1002         builder->getNdbError().code != QRY_MULTIPLE_PARENTS)
1003     {
1004       g_err << "Diamond-shaped query graph gave unexpected result.";
1005       builder->destroy();
1006       return NDBT_FAILED;
1007     }
1008     builder->destroy();
1009   }
1010 
1011   return NDBT_OK;
1012 } // NegativeTest::runGraphTest()
1013 
1014 
1015 int
runSetBoundTest() const1016 NegativeTest::runSetBoundTest() const
1017 {
1018   // Test NdbQueryOperation::setBound() with too long string value.
1019   {
1020     NdbQueryBuilder* const builder = NdbQueryBuilder::create();
1021 
1022     const NdbQueryIndexScanOperationDef* parentOperation
1023       = builder->scanIndex(m_nt2OrdIdx, m_nt2Tab);
1024     require(parentOperation != NULL);
1025 
1026     const NdbQueryDef* const queryDef = builder->prepare();
1027     require(queryDef != NULL);
1028     builder->destroy();
1029 
1030     NdbTransaction* const trans = m_ndb->startTransaction();
1031     NdbQuery* const query = trans->createQuery(queryDef);
1032 
1033     // Make bound with too long string.
1034     const NdbDictionary::RecordSpecification ordIdxRecSpec[] =
1035       {{m_nt2Tab->getColumn("oi1"), 0, 0, 0, 0}};
1036 
1037     const NdbRecord* const ordIdxRecord =
1038       m_dictionary->createRecord(m_nt2OrdIdx, ordIdxRecSpec,
1039                                  sizeof ordIdxRecSpec/sizeof ordIdxRecSpec[0],
1040                                  sizeof(NdbDictionary::RecordSpecification));
1041     require(ordIdxRecord != NULL);
1042 
1043     struct { Uint8 len; char data[nt2StrLen + 10]; } boundRow;
1044     memset(boundRow.data, 'x', sizeof(boundRow.data));
1045     // Set string length field.
1046     boundRow.len = nt2StrLen + 10;
1047 
1048     NdbIndexScanOperation::IndexBound
1049       bound = {reinterpret_cast<const char*>(&boundRow), 1, true,
1050                reinterpret_cast<const char*>(&boundRow), 1, true, 0};
1051 
1052     if (query->setBound(ordIdxRecord, &bound) == 0 ||
1053         query->getNdbError().code != Err_WrongFieldLength)
1054     {
1055       g_err << "Scan bound with too long string value gave unexpected result.";
1056       m_ndb->closeTransaction(trans);
1057       queryDef->destroy();
1058       return NDBT_FAILED;
1059     }
1060 
1061     // Set correct string lengh.
1062     boundRow.len = nt2StrLen;
1063     bound.range_no = 1;
1064     if (query->setBound(ordIdxRecord, &bound) == 0 ||
1065         query->getNdbError().code != QRY_ILLEGAL_STATE)
1066     {
1067       g_err << "setBound() in failed state gave unexpected result.";
1068       m_ndb->closeTransaction(trans);
1069       queryDef->destroy();
1070       return NDBT_FAILED;
1071     }
1072 
1073     m_ndb->closeTransaction(trans);
1074     queryDef->destroy();
1075   }
1076 
1077   // Test NdbQueryOperation::setBound() with wrong bound no.
1078   {
1079     NdbQueryBuilder* const builder = NdbQueryBuilder::create();
1080 
1081     const NdbQueryIndexScanOperationDef* parentOperation
1082       = builder->scanIndex(m_nt1OrdIdx, m_nt1Tab);
1083     require(parentOperation != NULL);
1084 
1085     const NdbQueryDef* const queryDef = builder->prepare();
1086     require(queryDef != NULL);
1087     builder->destroy();
1088 
1089     NdbTransaction* const trans = m_ndb->startTransaction();
1090     NdbQuery* const query = trans->createQuery(queryDef);
1091 
1092     const int boundRow[] = {1, 1};
1093 
1094     // Make bound with wrong bound no.
1095     NdbIndexScanOperation::IndexBound
1096       bound = {reinterpret_cast<const char*>(boundRow), 1, true,
1097                reinterpret_cast<const char*>(boundRow), 1, true, 1/*Should be 0.*/};
1098 
1099     if (query->setBound(m_nt1OrdIdx->getDefaultRecord(), &bound) == 0 ||
1100         query->getNdbError().code != Err_InvalidRangeNo)
1101     {
1102       g_err << "Scan bound with wrong range no gave unexpected result.";
1103       m_ndb->closeTransaction(trans);
1104       queryDef->destroy();
1105       return NDBT_FAILED;
1106     }
1107 
1108     m_ndb->closeTransaction(trans);
1109     queryDef->destroy();
1110   }
1111 
1112   // Test NdbQueryOperation::setBound() on table scan.
1113   {
1114     NdbQueryBuilder* const builder = NdbQueryBuilder::create();
1115 
1116     const NdbQueryTableScanOperationDef* parentOperation
1117       = builder->scanTable(m_nt1Tab);
1118     require(parentOperation != NULL);
1119 
1120     const NdbQueryDef* const queryDef = builder->prepare();
1121     require(queryDef != NULL);
1122     builder->destroy();
1123 
1124     NdbTransaction* const trans = m_ndb->startTransaction();
1125     NdbQuery* const query = trans->createQuery(queryDef);
1126 
1127     const int boundRow[] = {1, 1};
1128 
1129     NdbIndexScanOperation::IndexBound
1130       bound = {reinterpret_cast<const char*>(boundRow), 1, true,
1131                reinterpret_cast<const char*>(boundRow), 1, true, 0};
1132 
1133     if (query->setBound(m_nt1OrdIdx->getDefaultRecord(), &bound) == 0 ||
1134         query->getNdbError().code != QRY_WRONG_OPERATION_TYPE)
1135     {
1136       g_err << "Scan bound on table scan gave unexpected result.";
1137       m_ndb->closeTransaction(trans);
1138       queryDef->destroy();
1139       return NDBT_FAILED;
1140     }
1141 
1142     m_ndb->closeTransaction(trans);
1143     queryDef->destroy();
1144   }
1145 
1146   // Test NdbQueryOperation::setBound() in executed query.
1147   {
1148     NdbQueryBuilder* const builder = NdbQueryBuilder::create();
1149 
1150     const NdbQueryIndexScanOperationDef* parentOperation
1151       = builder->scanIndex(m_nt1OrdIdx, m_nt1Tab);
1152     require(parentOperation != NULL);
1153 
1154     const NdbQueryDef* const queryDef = builder->prepare();
1155     require(queryDef != NULL);
1156     builder->destroy();
1157 
1158     NdbTransaction* const trans = m_ndb->startTransaction();
1159     NdbQuery* const query = trans->createQuery(queryDef);
1160 
1161     const char* resultRow;
1162 
1163     require(
1164       query->getQueryOperation(0u)->setResultRowRef(
1165         m_nt1Tab->getDefaultRecord(),
1166         resultRow,
1167         NULL) == 0);
1168 
1169     require(trans->execute(NoCommit)==0);
1170 
1171     const int boundRow[] = {1, 1};
1172 
1173     // Add bound now.
1174     NdbIndexScanOperation::IndexBound
1175       bound = {reinterpret_cast<const char*>(boundRow), 1, true,
1176                reinterpret_cast<const char*>(boundRow), 1, true, 0};
1177 
1178     if (query->setBound(m_nt1OrdIdx->getDefaultRecord(), &bound) == 0 ||
1179         query->getNdbError().code != QRY_ILLEGAL_STATE)
1180     {
1181       g_err << "Adding scan bound to executed query gave unexpected result.";
1182       m_ndb->closeTransaction(trans);
1183       queryDef->destroy();
1184       return NDBT_FAILED;
1185     }
1186 
1187     m_ndb->closeTransaction(trans);
1188     queryDef->destroy();
1189   }
1190 
1191   return NDBT_OK;
1192 } // NegativeTest::runSetBoundTest()
1193 
1194 
1195 int
runValueTest() const1196 NegativeTest::runValueTest() const
1197 {
1198   // Test NdbQueryOperation::getValue() on an unknown column.
1199   {
1200     NdbQueryBuilder* const builder = NdbQueryBuilder::create();
1201 
1202     const NdbQueryTableScanOperationDef* parentOperation
1203       = builder->scanTable(m_nt1Tab);
1204     require(parentOperation != NULL);
1205 
1206     const NdbQueryDef* const queryDef = builder->prepare();
1207     require(queryDef != NULL);
1208     builder->destroy();
1209 
1210     NdbTransaction* const trans = m_ndb->startTransaction();
1211     NdbQuery* const query = trans->createQuery(queryDef);
1212 
1213     if (query->getQueryOperation(0u)->getValue("unknownCol") != NULL ||
1214         query->getNdbError().code != Err_UnknownColumn)
1215     {
1216       g_err << "NdbQueryOperation::getValue() on unknown column gave unexpected result.";
1217       m_ndb->closeTransaction(trans);
1218       queryDef->destroy();
1219       return NDBT_FAILED;
1220     }
1221 
1222     m_ndb->closeTransaction(trans);
1223     queryDef->destroy();
1224   }
1225 
1226   // Try fetching results with an NdbRecord for a different table.
1227   {
1228     NdbQueryBuilder* const builder = NdbQueryBuilder::create();
1229 
1230     const NdbQueryTableScanOperationDef* parentOperation
1231       = builder->scanTable(m_nt1Tab);
1232     require(parentOperation != NULL);
1233 
1234     const NdbQueryDef* const queryDef = builder->prepare();
1235     require(queryDef != NULL);
1236     builder->destroy();
1237 
1238     NdbTransaction* const trans = m_ndb->startTransaction();
1239     NdbQuery* const query = trans->createQuery(queryDef);
1240 
1241     const char* resultRow;
1242 
1243     if (query->getQueryOperation(0u)->setResultRowRef(m_nt2Tab->getDefaultRecord(),
1244                                                       resultRow, NULL) == 0 ||
1245         query->getNdbError().code != Err_DifferentTabForKeyRecAndAttrRec)
1246     {
1247       g_err << "NdbQueryOperation::setResultRowRef() on wrong table gave unexpected "
1248         "result.";
1249       m_ndb->closeTransaction(trans);
1250       queryDef->destroy();
1251       return NDBT_FAILED;
1252     }
1253 
1254     m_ndb->closeTransaction(trans);
1255     queryDef->destroy();
1256   }
1257 
1258   // Try defining result row twice.
1259   {
1260     NdbQueryBuilder* const builder = NdbQueryBuilder::create();
1261 
1262     const NdbQueryTableScanOperationDef* parentOperation
1263       = builder->scanTable(m_nt1Tab);
1264     require(parentOperation != NULL);
1265 
1266     const NdbQueryDef* const queryDef = builder->prepare();
1267     require(queryDef != NULL);
1268     builder->destroy();
1269 
1270     NdbTransaction* const trans = m_ndb->startTransaction();
1271     NdbQuery* const query = trans->createQuery(queryDef);
1272 
1273     const char* resultRow;
1274 
1275     require(
1276       query->getQueryOperation(0u)->setResultRowRef(
1277         m_nt1Tab->getDefaultRecord(),
1278         resultRow,
1279         NULL) == 0);
1280 
1281     if (query->getQueryOperation(0u)->setResultRowRef(m_nt1Tab->getDefaultRecord(),
1282                                                       resultRow, NULL) == 0 ||
1283         query->getNdbError().code != QRY_RESULT_ROW_ALREADY_DEFINED)
1284     {
1285       g_err << "Defining result row twice gave unexpected result.";
1286       m_ndb->closeTransaction(trans);
1287       queryDef->destroy();
1288       return NDBT_FAILED;
1289     }
1290 
1291     m_ndb->closeTransaction(trans);
1292     queryDef->destroy();
1293   }
1294 
1295   // Test operation with empty projection.
1296   {
1297     NdbQueryBuilder* const builder = NdbQueryBuilder::create();
1298 
1299     const NdbQueryIndexScanOperationDef* parentOperation
1300       = builder->scanIndex(m_nt1OrdIdx, m_nt1Tab);
1301     require(parentOperation != NULL);
1302 
1303     const NdbQueryDef* const queryDef = builder->prepare();
1304     require(queryDef != NULL);
1305     builder->destroy();
1306 
1307     NdbTransaction* const trans = m_ndb->startTransaction();
1308     NdbQuery* const query = trans->createQuery(queryDef);
1309 
1310     // Execute without defining a projection.
1311     if (trans->execute(NoCommit) == 0 ||
1312         query->getNdbError().code != QRY_EMPTY_PROJECTION)
1313     {
1314       g_err << "Having operation with empty projection gave unexpected result.";
1315       m_ndb->closeTransaction(trans);
1316       queryDef->destroy();
1317       return NDBT_FAILED;
1318     }
1319 
1320     m_ndb->closeTransaction(trans);
1321     queryDef->destroy();
1322   }
1323   return NDBT_OK;
1324 } // NegativeTest::runValueBoundTest()
1325 
1326 /**
1327  * Check that query pushdown is disabled in older versions of the code
1328  * (even if the API extensions are present in the code).
1329  */
1330 int
runFeatureDisabledTest() const1331 NegativeTest::runFeatureDisabledTest() const
1332 {
1333   NdbQueryBuilder* const builder = NdbQueryBuilder::create();
1334 
1335   const NdbQueryTableScanOperationDef* const parentOperation
1336     = builder->scanTable(m_nt1Tab);
1337 
1338   int result = NDBT_OK;
1339 
1340   if (ndb_join_pushdown(ndbGetOwnVersion()))
1341   {
1342     if (parentOperation == NULL)
1343     {
1344       g_err << "scanTable() failed: " << builder->getNdbError()
1345             << endl;
1346       result = NDBT_FAILED;
1347     }
1348     else
1349     {
1350       g_info << "scanTable() succeeded in version "
1351              << ndbGetOwnVersionString() << " as expected." << endl;
1352     }
1353   }
1354   else
1355   {
1356     // Query pushdown should not be enabled in this version.
1357     if (parentOperation != NULL)
1358     {
1359       g_err << "Succeeded with creating scan operation, which should not be "
1360         "possible in version " << ndbGetOwnVersionString() << endl;
1361       result = NDBT_FAILED;
1362     }
1363     else if (builder->getNdbError().code != Err_FunctionNotImplemented)
1364     {
1365       g_err << "scanTable() failed with unexpected error: "
1366             << builder->getNdbError() << endl;
1367       result = NDBT_FAILED;
1368     }
1369     else
1370     {
1371       g_info << "scanTable() failed in version "
1372              << ndbGetOwnVersionString() << " as expected with error: "
1373              << builder->getNdbError() << endl;
1374     }
1375   }
1376 
1377   builder->destroy();
1378   return result;
1379 } // NegativeTest::runFeatureDisabledTest()
1380 
1381 static int
dropNegativeSchema(NDBT_Context * ctx,NDBT_Step * step)1382 dropNegativeSchema(NDBT_Context* ctx, NDBT_Step* step)
1383 {
1384   NdbDictionary::Dictionary* const dictionary
1385     = step->getNdb()->getDictionary();
1386 
1387   if (dictionary->dropTable("nt1") != 0)
1388   {
1389     g_err << "Failed to drop table nt1." << endl;
1390     return NDBT_FAILED;
1391   }
1392   if (dictionary->dropTable("nt2") != 0)
1393   {
1394     g_err << "Failed to drop table nt2." << endl;
1395     return NDBT_FAILED;
1396   }
1397   return NDBT_OK;
1398 }
1399 
1400 NDBT_TESTSUITE(testSpj);
1401 TESTCASE("NegativeJoin", ""){
1402   INITIALIZER(createNegativeSchema);
1403   INITIALIZER(NegativeTest::keyTest);
1404   INITIALIZER(NegativeTest::graphTest);
1405   INITIALIZER(NegativeTest::setBoundTest);
1406   INITIALIZER(NegativeTest::valueTest);
1407   FINALIZER(dropNegativeSchema);
1408 }
1409 TESTCASE("FeatureDisabled", ""){
1410   INITIALIZER(createNegativeSchema);
1411   INITIALIZER(NegativeTest::featureDisabledTest);
1412   FINALIZER(dropNegativeSchema);
1413 }
1414 TESTCASE("LookupJoin", ""){
1415   INITIALIZER(runLoadTable);
1416   STEP(runLookupJoin);
1417   VERIFIER(runClearTable);
1418 }
1419 TESTCASE("ScanJoin", ""){
1420   INITIALIZER(runLoadTable);
1421   STEP(runScanJoin);
1422   FINALIZER(runClearTable);
1423 }
1424 TESTCASE("MixedJoin", ""){
1425   INITIALIZER(runLoadTable);
1426   STEPS(runJoin, 6);
1427   FINALIZER(runClearTable);
1428 }
1429 TESTCASE("MixedJoinDiskWait", "Simulate disk wait during pushed joins"){
1430   INITIALIZER(runLoadTable);
1431   TC_PROPERTY("ErrorCode", 4035);
1432   STEPS(runJoin, 4);
1433   FINALIZER(runClearTable);
1434 }
1435 TESTCASE("NF_Join", ""){
1436   TC_PROPERTY("UntilStopped", 1);
1437   TC_PROPERTY("WaitProgress", 20);
1438   INITIALIZER(runLoadTable);
1439   //STEPS(runScanJoin, 6);
1440   //STEPS(runLookupJoin, 6);
1441   STEPS(runJoin, 6);
1442   STEP(runRestarter);
1443   FINALIZER(runClearTable);
1444 }
1445 
1446 TESTCASE("LookupJoinError", ""){
1447   INITIALIZER(runLoadTable);
1448   STEP(runLookupJoinError);
1449   VERIFIER(runClearTable);
1450 }
1451 TESTCASE("ScanJoinError", ""){
1452   INITIALIZER(runLoadTable);
1453   TC_PROPERTY("NodeNumber", 2);
1454   STEP(runScanJoinError);
1455   FINALIZER(runClearTable);
1456 }
1457 NDBT_TESTSUITE_END(testSpj);
1458 
1459 
main(int argc,const char ** argv)1460 int main(int argc, const char** argv){
1461   ndb_init();
1462 
1463   /* To inject a single fault, for testing fault injection.
1464      Add the required fault number at the end
1465      of the command line. */
1466 
1467   if (argc > 0) sscanf(argv[argc-1], "%d",  &faultToInject);
1468   if (faultToInject && (faultToInject < FI_START || faultToInject > FI_END))
1469   {
1470     ndbout_c("Illegal fault to inject: %d. Legal range is between %d and %d",
1471              faultToInject, FI_START, FI_END);
1472     exit(1);
1473   }
1474 
1475   NDBT_TESTSUITE_INSTANCE(testSpj);
1476   return testSpj.execute(argc, argv);
1477 }
1478