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