1 /*
2    Copyright (c) 2009, 2011, Oracle and/or its affiliates. All rights reserved.
3 
4    This program is free software; you can redistribute it and/or modify
5    it under the terms of the GNU General Public License, 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 <new>
26 #include <assert.h>
27 #include <mysql.h>
28 #include <mysqld_error.h>
29 
30 #include <ndb_global.h>
31 #include <ndb_opts.h>
32 #include <NDBT.hpp>
33 #include <NdbApi.hpp>
34 #include "../../src/ndbapi/NdbQueryBuilder.hpp"
35 #include "../../src/ndbapi/NdbQueryOperation.hpp"
36 
37 
38 /* TODO:
39  - RecAttr and setResultRowBuff result retrieval.
40  - Parameter operands.
41  - Add another table type (e.g. with CHAR() fields.)
42 */
43 
44 #ifdef NDEBUG
45 // Some asserts have side effects, and there is no other error handling anyway.
46 #define ASSERT_ALWAYS(cond) if(!(cond)){abort();}
47 #else
48 #define ASSERT_ALWAYS assert
49 #endif
50 /* Query-related error codes. Used for negative testing. */
51 #define QRY_REQ_ARG_IS_NULL 4800
52 #define QRY_TOO_FEW_KEY_VALUES 4801
53 #define QRY_TOO_MANY_KEY_VALUES 4802
54 #define QRY_OPERAND_HAS_WRONG_TYPE 4803
55 #define QRY_CHAR_OPERAND_TRUNCATED 4804
56 #define QRY_NUM_OPERAND_RANGE 4805
57 #define QRY_MULTIPLE_PARENTS 4806
58 #define QRY_UNKONWN_PARENT 4807
59 #define QRY_UNKNOWN_COLUMN 4808
60 #define QRY_UNRELATED_INDEX 4809
61 #define QRY_WRONG_INDEX_TYPE 4810
62 #define QRY_OPERAND_ALREADY_BOUND 4811
63 #define QRY_DEFINITION_TOO_LARGE 4812
64 #define QRY_SEQUENTIAL_SCAN_SORTED 4813
65 #define QRY_RESULT_ROW_ALREADY_DEFINED 4814
66 #define QRY_HAS_ZERO_OPERATIONS 4815
67 #define QRY_IN_ERROR_STATE 4816
68 #define QRY_ILLEGAL_STATE 4817
69 #define QRY_WRONG_OPERATION_TYPE 4820
70 #define QRY_SCAN_ORDER_ALREADY_SET 4821
71 #define QRY_PARAMETER_HAS_WRONG_TYPE 4822
72 #define QRY_CHAR_PARAMETER_TRUNCATED 4823
73 #define QRY_MULTIPLE_SCAN_BRANCHES 4824
74 #define QRY_MULTIPLE_SCAN_SORTED 4825
75 
76 
77 namespace SPJSanityTest{
78 
resetError(const NdbError & err)79   static void resetError(const NdbError& err)
80   {
81     new (&const_cast<NdbError&>(err)) NdbError;
82   }
83 
84   class IntField{
85   public:
getType()86     static const char* getType(){
87       return "INT";
88     }
89 
IntField(int i=0)90     IntField(int i=0):
91       m_val(i)
92     {}
93 
toStr(char * buff) const94     const char* toStr(char* buff) const {
95       sprintf(buff, "%d", m_val);
96       return buff;
97     }
98 
compare(const IntField & other) const99     int compare(const IntField& other) const{
100       if (m_val > other.m_val)
101         return 1;
102       else if (m_val == other.m_val)
103         return 0;
104       else
105         return -1;
106     }
107 
getValue() const108     Uint64 getValue() const{
109       return m_val;
110     }
111 
getSize() const112     Uint32 getSize() const{
113       return sizeof m_val;
114    }
115 
116   private:
117     uint m_val;
118   };
119 
120   class StrField{
121   public:
getType()122     static const char* getType(){
123       return "VARCHAR(10)";
124     }
125 
StrField(int i=0)126     StrField(int i=0):
127       m_len(6){
128       // bzero(m_val, sizeof m_val);
129       sprintf(m_val, "c%5d", i);
130     }
131 
toStr(char * buff) const132     const char* toStr(char* buff) const {
133       sprintf(buff, "'%s'", getValue());
134       return buff;
135     }
136 
compare(const StrField & other) const137     int compare(const StrField& other) const{
138       return strcmp(getValue(), other.getValue());
139     }
140 
getValue() const141     const char* getValue() const{
142       m_val[m_len] = '\0';
143       return m_val;
144     }
145 
getSize() const146     Uint32 getSize() const{
147       m_val[m_len] = '\0';
148       return strlen(m_val);
149     }
150 
151   private:
152     Uint8 m_len;
153     mutable char m_val[10];
154   };
155 
156 
157   /* Key class.*/
158   template <typename FieldType>
159   class GenericKey{
160   public:
161     static const int size = 2;
162     FieldType m_values[size];
163 
makeConstOperand(NdbQueryBuilder & builder,int fieldNo) const164     NdbConstOperand* makeConstOperand(NdbQueryBuilder& builder,
165                                       int fieldNo) const {
166       ASSERT_ALWAYS(fieldNo<size);
167       //return builder.constValue(m_values[fieldNo]);
168       return builder.constValue(m_values[fieldNo].getValue());
169     }
170   };
171 
172   /* Concrete Row class.*/
173   template <typename FieldType>
174   class GenericRow{
175   public:
176     static const int size = 4;
177 
178     FieldType m_values[size];
179 
180     explicit GenericRow<FieldType>(int rowNo){
181       /* Attribute values are chosen such that rows are sorted on
182        * all attribtes, and that any pair of consecutive columns can be
183        * used as a foreign key to the table itself.*/
184       for(int i = 0; i<size; i++){
185         m_values[i] = FieldType(i+rowNo);
186       }
187     }
188 
getType(int colNo)189     static const char *getType(int colNo){
190       //return "INT";
191       return FieldType::getType();
192     }
193 
makeSQLValues(char * buffer,int rowNo)194     static void makeSQLValues(char* buffer, int rowNo){
195       const GenericRow<FieldType> row(rowNo);
196       sprintf(buffer, "values(");
197       char* tail = buffer+strlen(buffer);
198       for(int i = 0; i<size; i++){
199         char tmp[11];
200         if(i<size-1){
201           // sprintf(tail, "%d,", row.m_values[i].toStr(tmp));
202           sprintf(tail, "%s,", row.m_values[i].toStr(tmp));
203         }else{
204           sprintf(tail, "%s)", row.m_values[i].toStr(tmp));
205         }
206         tail = buffer+strlen(buffer);
207       }
208     }
209 
210     GenericKey<FieldType> getPrimaryKey() const;
211 
212     GenericKey<FieldType> getIndexKey() const;
213 
214     GenericKey<FieldType> getForeignKey(int keyNo) const;
215 
makeLessThanCond(NdbScanFilter & scanFilter)216     void makeLessThanCond(NdbScanFilter& scanFilter){
217       //ASSERT_ALWAYS(scanFilter.lt(0, m_values[0].getValue())==0);
218       ASSERT_ALWAYS(scanFilter.cmp(NdbScanFilter::COND_LT, 0, m_values, m_values[0].getSize())==0);
219     }
220 
221     /** Get the row column number that corresponds to the n'th column
222      * of the index.*/
223     static int getIndexKeyColNo(int indexCol);
224 
225     /** Get the row column number that corresponds to the n'th column
226      * of the m'th foreign key..*/
227     static int getForeignKeyColNo(int keyNo, int keyCol);
228   };
229 
230   template <typename FieldType>
getPrimaryKey() const231   GenericKey<FieldType> GenericRow<FieldType>::getPrimaryKey() const {
232     GenericKey<FieldType> key;
233     for(int i = 0; i<GenericKey<FieldType>::size; i++){
234       key.m_values[i] = m_values[i];
235     }
236     return key;
237   }
238 
239   template <typename FieldType>
getIndexKey() const240   GenericKey<FieldType> GenericRow<FieldType>::getIndexKey() const {
241     return getForeignKey(1);
242   }
243 
244   template <typename FieldType>
getForeignKey(int keyNo) const245   GenericKey<FieldType> GenericRow<FieldType>::getForeignKey(int keyNo) const {
246     ASSERT_ALWAYS(keyNo<=1);
247     GenericKey<FieldType> key;
248     for(int i = 0; i<GenericKey<FieldType>::size; i++){
249       key.m_values[i] = m_values[getForeignKeyColNo(keyNo,i)];
250     }
251     return key;
252   }
253 
254   template <typename FieldType>
getIndexKeyColNo(int indexCol)255   int GenericRow<FieldType>::getIndexKeyColNo(int indexCol){
256     return getForeignKeyColNo(1, indexCol);
257   }
258 
259   template <typename FieldType>
getForeignKeyColNo(int keyNo,int keyCol)260   int GenericRow<FieldType>::getForeignKeyColNo(int keyNo, int keyCol){
261     ASSERT_ALWAYS(keyNo<GenericRow<FieldType>::size-GenericKey<FieldType>::size);
262     ASSERT_ALWAYS(keyCol<GenericKey<FieldType>::size);
263     return size-GenericKey<FieldType>::size-keyNo+keyCol;
264   }
265 
266   template <typename FieldType>
operator ==(const GenericRow<FieldType> & a,const GenericRow<FieldType> & b)267   static bool operator==(const GenericRow<FieldType>& a, const GenericRow<FieldType>& b){
268     for(int i = 0; i<GenericRow<FieldType>::size; i++){
269       if(a.m_values[i].compare(b.m_values[i]) != 0){
270         return false;
271       }
272     }
273     return true;
274   }
275 
276   template <typename FieldType>
operator ==(const GenericKey<FieldType> & a,const GenericKey<FieldType> & b)277   static bool operator==(const GenericKey<FieldType>& a, const GenericKey<FieldType>& b){
278     for(int i = 0; i<GenericKey<FieldType>::size; i++){
279       if(a.m_values[i].compare(b.m_values[i]) != 0){
280         return false;
281       }
282     }
283     return true;
284   }
285 
286   /** Returns true if key of a <= key of b.*/
287   template <typename FieldType>
lessOrEqual(const GenericRow<FieldType> & a,const GenericRow<FieldType> & b)288   static bool lessOrEqual(const GenericRow<FieldType>& a, const GenericRow<FieldType>& b){
289     for(int i = 0; i<GenericKey<FieldType>::size; i++){
290       if(a.m_values[i].compare(b.m_values[i]) == 1){
291         return false;
292       }
293     }
294     return true;
295   }
296 
297   template <typename FieldType>
operator <<(NdbOut & out,const GenericRow<FieldType> & row)298   static NdbOut& operator<<(NdbOut& out, const GenericRow<FieldType>& row){
299     char buff[11];
300     out << "{";
301     for(int i = 0; i<GenericRow<FieldType>::size; i++){
302       out << row.m_values[i].toStr(buff);
303       if(i<GenericRow<FieldType>::size-1){
304         out << ", ";
305       }
306     }
307     out << "}";
308     return out;
309   }
310 
311   //typedef GenericRow<IntField> Row;
312   //typedef GenericKey<IntField> Key;
313   typedef GenericRow<StrField> Row;
314   typedef GenericKey<StrField> Key;
315 
316 
colName(int colNo)317   static const char* colName(int colNo){
318     static const char* names[] = {
319       "c1", "c2", "c3", "c4", "c5", "c6", "c7", "c8", "c9", "c10"
320     };
321     ASSERT_ALWAYS(static_cast<unsigned int>(colNo)< sizeof names/sizeof names[0]);
322     return names[colNo];
323   };
324 
printMySQLError(MYSQL & mysql,const char * before=NULL)325   static void printMySQLError(MYSQL& mysql, const char* before=NULL){
326     if(before!=NULL){
327       ndbout << before;
328     }
329     ndbout << mysql_error(&mysql) << endl;
330     exit(-1);
331   }
332 
mySQLExec(MYSQL & mysql,const char * stmt)333   static void mySQLExec(MYSQL& mysql, const char* stmt){
334     ndbout << stmt << ";" << endl;
335     if(mysql_query(&mysql, stmt) != 0){
336       ndbout << "Error executing '" << stmt << "' : ";
337       printMySQLError(mysql);
338     }
339   }
340 
341   class Query;
342 
343   /** Class representing a single NdbQueryOperation. 'Row'
344    * is a template argument, to allow different table defintions.*/
345   class Operation{
346   public:
347 
348     explicit Operation(class Query& query, Operation* parent);
349 
~Operation()350     virtual ~Operation(){}
351 
352     //protected: FIXME
353   public:
354     friend class Query;
355     /** Enclosing NdbQuery.*/
356     Query& m_query;
357     /** Optional parent operation.*/
358     const Operation* m_parent;
359     Vector<Operation*> m_children;
360     const NdbQueryOperationDef* m_operationDef;
361     // For now, only setResultRowRef() style result retrieval is tested.
362     union
363     {
364       const Row* m_resultPtr;
365       // Use union to avoid strict-aliasing problems.
366       const char* m_resultCharPtr;
367     };
368     // Corresponds to NdbQueryOperationDef operation numbering
369     Uint32 m_operationId;
370     // Number among siblings.
371     const Uint32 m_childNo;
372 
373     /** Check that result of this op and descendans is ok.*/
374     void verifyRow();
375 
376     /** Check that result of this op is ok.*/
377     virtual void verifyOwnRow() = 0;
378 
379     /** Build operation definition.*/
380     virtual void buildThis(NdbQueryBuilder& builder,
381                            const NdbDictionary::Table& tab) = 0;
382 
383     /** Build this and descendants.*/
384     void build(NdbQueryBuilder& builder,
385                const NdbDictionary::Table& tab);
386     /** Set up result retrieval before execution.*/
387     virtual void submit()=0;
388 
389     void compareRows(const char* text,
390                      const Row* expected,
391                      const Row* actual) const;
392   };
393 
394   class Query{
395   public:
396 
397     explicit Query(Ndb& ndb);
398 
~Query()399     ~Query(){
400       m_builder->destroy();
401       if (m_queryDef != NULL)
402       {
403         m_queryDef->destroy();
404       }
405     }
406 
407     /** Build query definition.*/
408     void build(const NdbDictionary::Table& tab, int tableSize);
409 
410     /** Execute within transaction.*/
411     void submit(NdbTransaction& transaction);
412 
413     void submitOperation(Operation& operation) const;
414 
setRoot(Operation & root)415     void setRoot(Operation& root){ m_root = &root;}
416 
nextResult()417     NdbQuery::NextResultOutcome nextResult(){
418       return m_query->nextResult(true, false);
419     }
420 
421     /** Verify current row for all operations.*/
verifyRow() const422     void verifyRow() const {
423       m_root->verifyRow();
424     }
425 
allocOperationId()426     Uint32 allocOperationId(){
427       return m_operationCount++;
428     }
429 
getOperation(Uint32 ident) const430     NdbQueryOperation* getOperation(Uint32 ident) const {
431       return m_query->getQueryOperation(ident);
432     }
433 
getTableSize() const434     int getTableSize() const {
435       return m_tableSize;
436     }
437 
getNdbRecord() const438     const NdbRecord* getNdbRecord() const {
439       return m_ndbRecord;
440     }
441 
getDictionary() const442     const NdbDictionary::Dictionary* getDictionary() const {
443       return m_ndb.getDictionary();
444     }
445 
close(bool forceSend=false)446     void close(bool forceSend = false){
447       m_query->close(forceSend);
448     }
449   private:
450     Ndb& m_ndb;
451     NdbQueryBuilder* const m_builder;
452     Operation* m_root;
453     const NdbQueryDef* m_queryDef;
454     NdbQuery* m_query;
455     Uint32 m_operationCount;
456     int m_tableSize;
457     const NdbRecord* m_ndbRecord;
458   };
459 
460 
461   class LookupOperation: public Operation{
462   public:
463     explicit LookupOperation(Query& query,
464                              Operation* parent = NULL);
465     virtual void verifyOwnRow();
466 
467     /** Set up result retrieval before execution.*/
468     virtual void submit();
469   protected:
470     virtual void buildThis(NdbQueryBuilder& builder,
471                            const NdbDictionary::Table& tab);
472   };
473 
474   class IndexLookupOperation: public Operation{
475   public:
476     explicit IndexLookupOperation(Query& query,
477                                   const char* indexName,
478                                   Operation* parent = NULL);
479     virtual void verifyOwnRow();
480 
481     /** Set up result retrieval before execution.*/
482     virtual void submit();
483   protected:
484     virtual void buildThis(NdbQueryBuilder& builder,
485                            const NdbDictionary::Table& tab);
486   private:
487     const char* const m_indexName;
488   };
489 
490   class TableScanOperation: public Operation{
491   public:
492 
493     explicit TableScanOperation(Query& query, int lessThanRow=-1);
494 
~TableScanOperation()495     virtual ~TableScanOperation() {
496       delete[] m_rowFound;
497     }
498 
499     virtual void verifyOwnRow();
500 
501     /** Set up result retrieval before execution.*/
502     virtual void submit();
503 
504   protected:
505     virtual void buildThis(NdbQueryBuilder& builder,
506                            const NdbDictionary::Table& tab);
507   private:
508     bool* m_rowFound;
509     int m_lessThanRow;
510   };
511 
512   class IndexScanOperation: public Operation{
513   public:
514 
515     explicit IndexScanOperation(Query& query,
516                                 const char* indexName,
517                                 int lowerBoundRowNo,
518                                 int upperBoundRowNo,
519                                 NdbQueryOptions::ScanOrdering ordering);
520 
~IndexScanOperation()521     virtual ~IndexScanOperation() {
522       delete[] m_rowFound;
523     }
524 
525     virtual void verifyOwnRow();
526 
527     /** Set up result retrieval before execution.*/
528     virtual void submit();
529 
530   protected:
531     virtual void buildThis(NdbQueryBuilder& builder,
532                            const NdbDictionary::Table& tab);
533   private:
534     const char* const m_indexName;
535     /** Number of table row from which get key to use as lower bound.*/
536     const int m_lowerBoundRowNo;
537     /** Number of table row from which get key to use as upper bound.*/
538     const int m_upperBoundRowNo;
539     /** An entry per row. True if row has been seen in the result stream.*/
540     bool* m_rowFound;
541     /** Ordering of results.*/
542     NdbQueryOptions::ScanOrdering m_ordering;
543     /** Previous row, for verifying ordering.*/
544     Row m_previousRow;
545     /** True from the second row and onwards.*/
546     bool m_hasPreviousRow;
547   };
548 
549 
550   // Query methods.
551 
Query(Ndb & ndb)552   Query::Query(Ndb& ndb):
553     m_ndb(ndb),
554     m_builder(NdbQueryBuilder::create()),
555     m_root(NULL),
556     m_queryDef(NULL),
557     m_query(NULL),
558     m_operationCount(0),
559     m_tableSize(-1),
560     m_ndbRecord(NULL)
561   {
562     ASSERT_ALWAYS(m_builder != NULL);
563   }
564 
build(const NdbDictionary::Table & tab,int tableSize)565   void Query::build(const NdbDictionary::Table& tab, int tableSize){
566     m_tableSize = tableSize;
567     m_root->build(*m_builder, tab);
568     m_queryDef = m_builder->prepare();
569     m_ndbRecord = tab.getDefaultRecord();
570   }
571 
submit(NdbTransaction & transaction)572   void Query::submit(NdbTransaction& transaction){
573     m_query = transaction.createQuery(m_queryDef);
574     ASSERT_ALWAYS(m_query!=NULL);
575     submitOperation(*m_root);
576   }
577 
submitOperation(Operation & operation) const578   void Query::submitOperation(Operation& operation) const{
579     // Do a depth first traversal of the operations graph.
580     operation.submit();
581     for(Uint32 i = 0; i<operation.m_children.size(); i++){
582       submitOperation(*operation.m_children[i]);
583     }
584   }
585 
586   // Operation methods.
Operation(class Query & query,Operation * parent)587   Operation::Operation(class Query& query,
588                                  Operation* parent):
589     m_query(query),
590     m_parent(parent),
591     m_operationDef(NULL),
592     m_resultPtr(NULL),
593     m_childNo(parent == NULL ? 0 : parent->m_children.size())
594   {
595     if(parent==NULL){
596       query.setRoot(*this);
597     }else{
598       parent->m_children.push_back(this);
599     }
600   }
601 
build(NdbQueryBuilder & builder,const NdbDictionary::Table & tab)602   void Operation::build(NdbQueryBuilder& builder,
603                                   const NdbDictionary::Table& tab){
604     m_operationId = m_query.allocOperationId();
605     buildThis(builder, tab);
606     ASSERT_ALWAYS(builder.getNdbError().code==0);
607     for(Uint32 i = 0; i<m_children.size(); i++){
608       m_children[i]->build(builder, tab);
609     }
610   }
611 
verifyRow()612   void Operation::verifyRow(){
613     verifyOwnRow();
614     for(Uint32 i = 0; i<m_children.size(); i++){
615       m_children[i]->verifyRow();
616     }
617   }
618 
619   typedef const char* constCharPtr;
620 
compareRows(const char * text,const Row * expected,const Row * actual) const621   void Operation::compareRows(const char* text,
622                                    const Row* expected,
623                                    const Row* actual) const{
624     if(expected==NULL){
625       if(actual==NULL){
626         ndbout << text << " operationId=" << m_operationId
627                << " expected NULL and got it." << endl;
628       }else{
629         ndbout << text << " operationId=" << m_operationId
630                << " expected NULL but got." << *actual
631                << endl;
632         ASSERT_ALWAYS(false);
633       }
634     }else{
635       if(actual==NULL){
636         ndbout << text << " operationId=" << m_operationId
637                << " expected: " << *expected
638                << " but got NULL." << endl;
639         ASSERT_ALWAYS(false);
640       }else{
641         ndbout << text << " operationId=" << m_operationId
642                << " expected: " << *expected;
643         if(*expected == *actual){
644           ndbout << " and got it." << endl;
645         }else{
646           ndbout << " but got: " << *actual;
647           ASSERT_ALWAYS(false);
648         }
649       }
650     }
651   };
652 
653   // LookupOperation methods.
654 
655   LookupOperation
LookupOperation(Query & query,Operation * parent)656   ::LookupOperation(Query& query,
657                     Operation* parent):
658     Operation(query, parent){
659   }
660 
buildThis(NdbQueryBuilder & builder,const NdbDictionary::Table & tab)661   void LookupOperation::buildThis(NdbQueryBuilder& builder,
662                                             const NdbDictionary::Table& tab){
663     NdbQueryOperand* keyOperands[Key::size+2];
664     if(m_parent==NULL){
665       const Key key = Row(0).getPrimaryKey();
666       for(int i = 0; i<Key::size; i++){
667         keyOperands[i] = key.makeConstOperand(builder, i);
668       }
669     }else{
670       // Negative testing
671       ASSERT_ALWAYS(builder.linkedValue(m_parent->m_operationDef,
672                                         "unknown_col") == NULL);
673       ASSERT_ALWAYS(builder.getNdbError().code == QRY_UNKNOWN_COLUMN);
674 
675       for(int i = 0; i<Key::size; i++){
676         keyOperands[i] =
677           builder.linkedValue(m_parent->m_operationDef,
678                               colName(Row::getForeignKeyColNo(
679                                         m_childNo, i)));
680         ASSERT_ALWAYS(keyOperands[i]!=NULL);
681         /*Row::makeLinkedKey(builder, keyOperands,
682                          Operation::m_parent->m_operationDef,
683                          Operation::m_childNo);*/
684       }
685     }
686     // Negative testing
687     keyOperands[Key::size] = keyOperands[0];
688     keyOperands[Key::size+1] = NULL;
689     ASSERT_ALWAYS(builder.readTuple(&tab, keyOperands)== NULL);
690     ASSERT_ALWAYS(builder.getNdbError().code == QRY_TOO_MANY_KEY_VALUES);
691     resetError(builder.getNdbError());
692 
693     keyOperands[Key::size] = NULL;
694     m_operationDef = builder.readTuple(&tab, keyOperands);
695     ASSERT_ALWAYS(m_operationDef != NULL);
696 
697     // Negative testing
698     keyOperands[Key::size-1] = builder.constValue(0x1fff1fff);
699     ASSERT_ALWAYS(keyOperands[Key::size-1] != NULL);
700     ASSERT_ALWAYS(builder.readTuple(&tab, keyOperands) == NULL);
701     ASSERT_ALWAYS(builder.getNdbError().code == QRY_OPERAND_HAS_WRONG_TYPE);
702 
703     // Negative testing
704     keyOperands[Key::size-1] = NULL;
705     ASSERT_ALWAYS(builder.readTuple(&tab, keyOperands) == NULL);
706     ASSERT_ALWAYS(builder.getNdbError().code == QRY_TOO_FEW_KEY_VALUES);
707     resetError(builder.getNdbError());
708   }
709 
submit()710   void LookupOperation::submit(){
711     NdbQueryOperation* queryOp
712       = m_query.getOperation(m_operationId);
713     // Negative testing
714     ASSERT_ALWAYS(queryOp->setResultRowRef(NULL,
715                                            m_resultCharPtr,
716                                            NULL) == -1);
717     ASSERT_ALWAYS(queryOp->getQuery().getNdbError().code ==
718                   QRY_REQ_ARG_IS_NULL);
719     ASSERT_ALWAYS(queryOp->setOrdering(NdbQueryOptions::ScanOrdering_ascending)
720                   == -1);
721     ASSERT_ALWAYS(queryOp->getQuery().getNdbError().code ==
722                   QRY_WRONG_OPERATION_TYPE);
723 
724     ASSERT_ALWAYS(queryOp->setResultRowRef(m_query.getNdbRecord(),
725                                            m_resultCharPtr,
726                                            NULL) == 0);
727     // Negative testing
728     ASSERT_ALWAYS(queryOp->setResultRowRef(m_query.getNdbRecord(),
729                                            m_resultCharPtr,
730                                            NULL) == -1);
731     ASSERT_ALWAYS(queryOp->getQuery().getNdbError().code ==
732                   QRY_RESULT_ROW_ALREADY_DEFINED);
733   }
734 
verifyOwnRow()735   void LookupOperation::verifyOwnRow(){
736     if(m_parent==NULL){
737       const Row expected(0);
738       compareRows("lookup root operation",
739                                   &expected,
740                                   m_resultPtr);
741     }else{
742       NdbQueryOperation* queryOp
743         = m_query
744         .getOperation(m_operationId);
745       if(!queryOp->getParentOperation(0)->isRowNULL()){
746         const Key key =
747           m_parent->m_resultPtr
748           ->getForeignKey(m_childNo);
749         bool found = false;
750         for(int i = 0; i<m_query.getTableSize(); i++){
751           const Row row(i);
752           if(row.getPrimaryKey() == key){
753             found = true;
754             compareRows("lookup child operation",
755                                         &row,
756                                         m_resultPtr);
757           }
758         }
759         if(!found && !queryOp->isRowNULL()){
760           compareRows("lookup child operation",
761                                       NULL,
762                                       m_resultPtr);
763         }
764       }
765     }
766   }
767 
768   // IndexLookupOperation methods.
769 
770   IndexLookupOperation
IndexLookupOperation(Query & query,const char * indexName,Operation * parent)771   ::IndexLookupOperation(Query& query,
772                          const char* indexName,
773                          Operation* parent):
774     Operation(query, parent),
775     m_indexName(indexName){
776   }
777 
778   void IndexLookupOperation
buildThis(NdbQueryBuilder & builder,const NdbDictionary::Table & tab)779   ::buildThis(NdbQueryBuilder& builder,
780               const NdbDictionary::Table& tab){
781     const NdbDictionary::Dictionary* const dict
782       = m_query.getDictionary();
783     char fullName[200];
784     sprintf(fullName, "%s$unique", m_indexName);
785     const NdbDictionary::Index* const index
786       = dict->getIndex(fullName, tab.getName());
787     ASSERT_ALWAYS(index!=NULL);
788 
789     NdbQueryOperand* keyOperands[Key::size+1];
790     if(m_parent==NULL){
791       const Key key = Row(0).getIndexKey();
792       for(int i = 0; i<Key::size; i++){
793         keyOperands[i] = key.makeConstOperand(builder, i);
794       }
795     }else{
796       for(int i = 0; i<Key::size; i++){
797         keyOperands[i] =
798           builder.linkedValue(m_parent->m_operationDef,
799                               colName(Row::getForeignKeyColNo(
800                                         m_childNo, i)));
801         ASSERT_ALWAYS(keyOperands[i]!=NULL);
802       }
803       /*Row::makeLinkedKey(builder, keyOperands,
804                          Operation::m_parent->m_operationDef,
805                          Operation::m_childNo);*/
806     }
807     keyOperands[Key::size] = NULL;
808     m_operationDef = builder.readTuple(index, &tab, keyOperands);
809 
810     // Negative testing
811     const NdbDictionary::Index* const orderedIndex
812       = dict->getIndex(m_indexName, tab.getName());
813     ASSERT_ALWAYS(orderedIndex != NULL);
814     ASSERT_ALWAYS(builder.readTuple(orderedIndex, &tab, keyOperands) == NULL);
815     ASSERT_ALWAYS(builder.getNdbError().code == QRY_WRONG_INDEX_TYPE);
816     resetError(builder.getNdbError());
817   }
818 
submit()819   void IndexLookupOperation::submit(){
820     NdbQueryOperation* queryOp
821       = m_query.getOperation(m_operationId);
822     queryOp->setResultRowRef(m_query.getNdbRecord(),
823                              m_resultCharPtr,
824                              NULL);
825   }
826 
verifyOwnRow()827   void IndexLookupOperation::verifyOwnRow(){
828     if(m_parent==NULL){
829       const Row expected(0);
830       compareRows("index lookup root operation",
831                                   &expected,
832                                   m_resultPtr);
833     }else{
834       NdbQueryOperation* queryOp
835         = m_query
836         .getOperation(m_operationId);
837       if(!queryOp->getParentOperation(0)->isRowNULL()){
838         const Key key =
839           m_parent->m_resultPtr
840           ->getForeignKey(m_childNo);
841         bool found = false;
842         for(int i = 0; i<m_query.getTableSize(); i++){
843           const Row row(i);
844           if(row.getIndexKey() == key){
845             found = true;
846             compareRows("index lookup child operation",
847                                         &row,
848                                         m_resultPtr);
849           }
850         }
851         if(!found && !queryOp->isRowNULL()){
852           compareRows("index lookup child operation",
853                                       NULL,
854                                       m_resultPtr);
855         }
856       }
857     }
858   }
859 
860   // TableScanOperation methods.
861 
862   TableScanOperation
TableScanOperation(Query & query,int lessThanRow)863   ::TableScanOperation(Query& query, int lessThanRow):
864     Operation(query, NULL),
865     m_rowFound(NULL),
866     m_lessThanRow(lessThanRow){
867   }
868 
buildThis(NdbQueryBuilder & builder,const NdbDictionary::Table & tab)869   void TableScanOperation::buildThis(NdbQueryBuilder& builder,
870                                           const NdbDictionary::Table& tab){
871     m_operationDef = builder.scanTable(&tab);
872     m_rowFound = new bool[m_query.getTableSize()];
873     for(int i = 0; i<m_query.getTableSize(); i++){
874       m_rowFound[i] = false;
875     }
876   }
877 
submit()878   void TableScanOperation::submit(){
879     NdbQueryOperation* queryOp
880       = m_query.getOperation(m_operationId);
881     queryOp->setResultRowRef(m_query.getNdbRecord(),
882                              m_resultCharPtr,
883                              NULL);
884     if(m_lessThanRow!=-1){
885       NdbInterpretedCode code(queryOp->getQueryOperationDef().getTable());
886       NdbScanFilter filter(&code);
887       ASSERT_ALWAYS(filter.begin()==0);
888       Row(m_lessThanRow).makeLessThanCond(filter);
889       ASSERT_ALWAYS(filter.end()==0);
890       ASSERT_ALWAYS(queryOp->setInterpretedCode(code)==0);
891     }
892   }
893 
verifyOwnRow()894   void TableScanOperation::verifyOwnRow(){
895     bool found = false;
896     const int upperBound =
897       m_lessThanRow==-1 ?
898       m_query.getTableSize() :
899       m_lessThanRow;
900     for(int i = 0; i<upperBound; i++){
901       //const Row row(i);
902       if(Row(i) == *m_resultPtr){
903         found = true;
904         if(m_rowFound[i]){
905           ndbout << "Root table scan operation: "
906                  << *m_resultPtr
907                  << "appeared twice." << endl;
908           ASSERT_ALWAYS(false);
909         }
910         m_rowFound[i] = true;
911       }
912     }
913     if(!found){
914       ndbout << "Root table scan operation. Unexpected row: "
915              << *m_resultPtr << endl;
916       ASSERT_ALWAYS(false);
917     }else{
918       ndbout << "Root table scan operation. Got row: "
919              << *m_resultPtr
920              << " as expected." << endl;
921     }
922   }
923 
924     // IndexScanOperation methods.
925 
926   IndexScanOperation
IndexScanOperation(Query & query,const char * indexName,int lowerBoundRowNo,int upperBoundRowNo,NdbQueryOptions::ScanOrdering ordering)927   ::IndexScanOperation(Query& query,
928                        const char* indexName,
929                        int lowerBoundRowNo,
930                        int upperBoundRowNo,
931                        NdbQueryOptions::ScanOrdering ordering):
932     Operation(query, NULL),
933     m_indexName(indexName),
934     m_lowerBoundRowNo(lowerBoundRowNo),
935     m_upperBoundRowNo(upperBoundRowNo),
936     m_ordering(ordering),
937     m_previousRow(0),
938     m_hasPreviousRow(false){
939   }
940 
buildThis(NdbQueryBuilder & builder,const NdbDictionary::Table & tab)941   void IndexScanOperation::buildThis(NdbQueryBuilder& builder,
942                                                const NdbDictionary::Table& tab){
943     const NdbDictionary::Dictionary* const dict
944       = m_query.getDictionary();
945     const NdbDictionary::Index* const index
946       = dict->getIndex(m_indexName, tab.getName());
947     ASSERT_ALWAYS(index!=NULL);
948 
949     const NdbQueryOperand* low[Key::size+1];
950     const NdbQueryOperand* high[Key::size+1];
951     // Code below assume that we use primary key index.
952     ASSERT_ALWAYS(strcmp(m_indexName, "PRIMARY")==0);
953     /* Tables are alway sorted on all columns. Using these bounds,
954      we therefore get m_upperBoundRowNo - m_lowerBoundRowNo +1 rows.*/
955     const Key& lowKey = *new Key(Row(m_lowerBoundRowNo).getPrimaryKey());
956     const Key& highKey = *new Key(Row(m_upperBoundRowNo).getPrimaryKey());
957 
958     for(int i = 0; i<Key::size; i++){
959       low[i] = lowKey.makeConstOperand(builder, i);
960       high[i] = highKey.makeConstOperand(builder, i);
961     }
962     low[Key::size] = NULL;
963     high[Key::size] = NULL;
964 
965     NdbQueryOptions options;
966     options.setOrdering(m_ordering);
967     const NdbQueryIndexBound bound(low, high);
968     const NdbQueryIndexScanOperationDef* opDef
969       = builder.scanIndex(index, &tab, &bound, &options);
970     m_operationDef = opDef;
971     ASSERT_ALWAYS(m_operationDef!=NULL);
972     m_rowFound = new bool[m_query.getTableSize()];
973     for(int i = 0; i<m_query.getTableSize(); i++){
974       m_rowFound[i] = false;
975     }
976   }
977 
submit()978   void IndexScanOperation::submit(){
979     NdbQueryOperation* queryOp
980       = m_query.getOperation(m_operationId);
981     queryOp->setResultRowRef(m_query.getNdbRecord(),
982                              m_resultCharPtr,
983                              NULL);
984 
985     // Negative testing.
986     if (m_ordering != NdbQueryOptions::ScanOrdering_unordered){
987       ASSERT_ALWAYS(queryOp
988                     ->setOrdering(NdbQueryOptions::ScanOrdering_ascending) != 0);
989       ASSERT_ALWAYS(queryOp->getQuery().getNdbError().code ==
990                     QRY_SCAN_ORDER_ALREADY_SET);
991 
992       ASSERT_ALWAYS(queryOp->setParallelism(1) != 0);
993       ASSERT_ALWAYS(queryOp->getQuery().getNdbError().code ==
994                     QRY_SEQUENTIAL_SCAN_SORTED);
995     }
996   }
997 
verifyOwnRow()998   void IndexScanOperation::verifyOwnRow(){
999     bool found = false;
1000     for(int i = m_lowerBoundRowNo; i<=m_upperBoundRowNo; i++){
1001       const Row row(i);
1002       if(row == *m_resultPtr){
1003         found = true;
1004         if(m_rowFound[i]){
1005           ndbout << "Root index scan operation: "
1006                  << *m_resultPtr
1007                  << "appeared twice." << endl;
1008           ASSERT_ALWAYS(false);
1009         }
1010         m_rowFound[i] = true;
1011       }
1012     }
1013     if(!found){
1014       ndbout << "Root index scan operation. Unexpected row: "
1015              << *m_resultPtr << endl;
1016       ASSERT_ALWAYS(false);
1017     }else{
1018       if(m_hasPreviousRow){
1019         switch(m_ordering){
1020         case NdbQueryOptions::ScanOrdering_ascending:
1021           if(!lessOrEqual(m_previousRow, *m_resultPtr)){
1022             ndbout << "Error in result ordering. Did not expect row "
1023                    <<  *m_resultPtr
1024                    << " now." << endl;
1025             ASSERT_ALWAYS(false);
1026           }
1027           break;
1028         case NdbQueryOptions::ScanOrdering_descending:
1029           if(lessOrEqual(m_previousRow, *m_resultPtr)){
1030             ndbout << "Error in result ordering. Did not expect row "
1031                    <<  *m_resultPtr
1032                    << " now." << endl;
1033             ASSERT_ALWAYS(false);
1034           }
1035           break;
1036         case NdbQueryOptions::ScanOrdering_unordered:
1037           break;
1038         default:
1039           ASSERT_ALWAYS(false);
1040         }
1041       }
1042       m_hasPreviousRow = true;
1043       m_previousRow = *m_resultPtr;
1044       ndbout << "Root index scan operation. Got row: "
1045              << *m_resultPtr
1046              << " as expected." << endl;
1047     }
1048   }
1049 
1050   // Misc. functions.
1051 
1052   /** Make and populate SQL table.*/
makeTable(MYSQL & mysql,const char * name,int rowCount)1053   void makeTable(MYSQL& mysql, const char* name, int rowCount){
1054     char cmd[500];
1055     char piece[500];
1056     sprintf(cmd, "drop table if exists %s", name);
1057     mySQLExec(mysql, cmd);
1058     sprintf(cmd, "create table %s (\n", name);
1059     for(int i = 0; i<Row::size; i++){
1060       sprintf(piece, "   %s %s NOT NULL,\n", colName(i), Row::getType(i));
1061       strcat(cmd, piece);
1062     }
1063     strcat(cmd, "   PRIMARY KEY(");
1064     for(int i = 0; i<Key::size; i++){
1065       strcat(cmd, colName(i));
1066       if(i<Key::size - 1){
1067         strcat(cmd, ",");
1068       }else{
1069         strcat(cmd, "),\n");
1070       }
1071     }
1072     strcat(cmd, "   UNIQUE KEY UIX (");
1073     for(int i = 0; i<Key::size; i++){
1074       strcat(cmd, colName(Row::getIndexKeyColNo(i)));
1075       if(i<Key::size - 1){
1076         strcat(cmd, ",");
1077       }else{
1078         strcat(cmd, "))\n");
1079       }
1080     }
1081     strcat(cmd, "ENGINE=NDB");
1082     mySQLExec(mysql, cmd);
1083     for(int i = 0; i<rowCount; i++){
1084       Row::makeSQLValues(piece, i);
1085       sprintf(cmd, "insert into %s %s", name, piece);
1086       mySQLExec(mysql, cmd);
1087     }
1088   };
1089 
1090 
1091   /* Execute a test for a give operation graph.*/
runCase(MYSQL & mysql,Ndb & ndb,Query & query,const char * tabName,int tabSize,int rowCount)1092   void runCase(MYSQL& mysql,
1093                Ndb& ndb,
1094                Query& query,
1095                const char* tabName,
1096                int tabSize,
1097                int rowCount){
1098     // Populate test table.
1099     makeTable(mysql, tabName, tabSize);
1100     NdbDictionary::Dictionary*  const dict = ndb.getDictionary();
1101     const NdbDictionary::Table* const tab = dict->getTable(tabName);
1102     ASSERT_ALWAYS(tab!=NULL);
1103     // Build generic query definition.
1104     query.build(*tab, tabSize);
1105     NdbTransaction* trans = ndb.startTransaction();
1106     ASSERT_ALWAYS(trans!=NULL);
1107     // instantiate query within transaction.
1108     query.submit(*trans);
1109     ASSERT_ALWAYS(trans->execute(NoCommit)==0);
1110     // Verify each row and total number of rows.
1111     for(int i = 0; i<rowCount; i++){
1112       ASSERT_ALWAYS(query.nextResult() ==  NdbQuery::NextResult_gotRow);
1113       query.verifyRow();
1114       if(false && i>3){
1115         // Enable to test close of incomplete scan.
1116         query.close();
1117         ndb.closeTransaction(trans);
1118         return;
1119       }
1120     }
1121     ASSERT_ALWAYS(query.nextResult() ==  NdbQuery::NextResult_scanComplete);
1122     ndb.closeTransaction(trans);
1123   }
1124 
1125   /** Run a set of test cases.*/
runTestSuite(MYSQL & mysql,Ndb & ndb)1126   void runTestSuite(MYSQL& mysql, Ndb& ndb){
1127     for(int caseNo = 0; caseNo<7; caseNo++){
1128       ndbout << endl << "Running test case " << caseNo << endl;
1129 
1130       char tabName[20];
1131       sprintf(tabName, "t%d", caseNo);
1132       Query query(ndb);
1133 
1134       switch(caseNo){
1135       case 0:
1136         {
1137           LookupOperation root(query);
1138           LookupOperation child(query, &root);
1139           LookupOperation child2(query, &root);
1140           runCase(mysql, ndb, query, tabName, 1, 1);
1141         }
1142         break;
1143       case 1:
1144         {
1145           IndexLookupOperation root(query, "UIX");
1146           IndexLookupOperation child(query, "UIX", &root);
1147           runCase(mysql, ndb, query, tabName, 5, 1);
1148         }
1149         break;
1150       case 2:
1151         {
1152           IndexScanOperation root(query, "PRIMARY", 2, 4,
1153                                    NdbQueryOptions::ScanOrdering_unordered);
1154           LookupOperation child(query, &root);
1155           IndexLookupOperation child2(query, "UIX", &child);
1156           LookupOperation child3(query, &child);
1157           runCase(mysql, ndb, query, tabName, 5, 3);
1158         }
1159         break;
1160       case 3:
1161         {
1162           TableScanOperation root(query);
1163           LookupOperation child(query, &root);
1164           runCase(mysql, ndb, query, tabName, 5, 5);
1165         }
1166         break;
1167       case 4:
1168         {
1169           TableScanOperation root(query);
1170           IndexLookupOperation child1(query, "UIX", &root);
1171           LookupOperation child2(query, &child1);
1172           IndexLookupOperation child3(query, "UIX", &child2);
1173           LookupOperation child1_2(query, &root);
1174           LookupOperation child2_2(query, &child1_2);
1175           runCase(mysql, ndb, query, tabName, 10, 10);
1176         }
1177         break;
1178       case 5:
1179         {
1180           IndexScanOperation root(query, "PRIMARY", 0, 10,
1181                                  NdbQueryOptions::ScanOrdering_descending);
1182           LookupOperation child(query, &root);
1183           runCase(mysql, ndb, query, tabName, 10, 10);
1184         }
1185         break;
1186       case 6:
1187         {
1188           TableScanOperation root(query, 3);
1189           LookupOperation child(query, &root);
1190           runCase(mysql, ndb, query, tabName, 5, 3);
1191         }
1192         break;
1193 #if 0
1194       default:
1195         //case 6:
1196         {
1197           IndexScanOperation root(query, "PRIMARY", 0, 1000,
1198                                  NdbQueryOptions::ScanOrdering_descending);
1199           LookupOperation child(query, &root);
1200           runCase(mysql, ndb, query, tabName, 10*(caseNo-6), 10*(caseNo-6));
1201         }
1202         break;
1203 #endif
1204       }
1205     }
1206   }
1207 
1208 };
1209 
1210 
1211 using namespace SPJSanityTest;
1212 
main(int argc,char * argv[])1213 int main(int argc, char* argv[]){
1214   if(argc!=4){
1215     ndbout << "Usage: " << argv[0]
1216            << " <mysql IP address> <mysql port> <cluster connect string>"
1217            << endl;
1218     return NDBT_ProgramExit(NDBT_FAILED);
1219   }
1220   const char* const host=argv[1];
1221   const int port = atoi(argv[2]);
1222   const char* const connectString = argv[3];
1223 
1224   NDB_INIT(argv[0]);
1225   MYSQL mysql;
1226   ASSERT_ALWAYS(mysql_init(&mysql));
1227   if(!mysql_real_connect(&mysql, host, "root", "", "",
1228                          port, NULL, 0)){
1229     printMySQLError(mysql, "mysql_real_connect() failed:");
1230     return NDBT_ProgramExit(NDBT_FAILED);
1231   }
1232   mySQLExec(mysql, "create database if not exists CK_DB");
1233   mySQLExec(mysql, "use CK_DB");
1234   {
1235     Ndb_cluster_connection con(connectString);
1236     if(con.connect(12, 5, 1) != 0){
1237       ndbout << "Unable to connect to management server." << endl;
1238       return NDBT_ProgramExit(NDBT_FAILED);
1239     }
1240 
1241     int res = con.wait_until_ready(30,30);
1242     if (res != 0){
1243       ndbout << "Cluster nodes not ready in 30 seconds." << endl;
1244       return NDBT_ProgramExit(NDBT_FAILED);
1245     }
1246     Ndb ndb(&con, "CK_DB");
1247     if(ndb.init() != 0){
1248       ERR(ndb.getNdbError());
1249       return NDBT_ProgramExit(NDBT_FAILED);
1250     }
1251     runTestSuite(mysql, ndb);
1252   } // Must call ~Ndb_cluster_connection() before ndb_end().
1253   ndb_end(0);
1254   return 0;
1255 }
1256 
1257 // Explicit template instantiations.
1258 template class Vector<Operation*>;
1259 template class GenericRow<IntField>;
1260 template class GenericKey<IntField>;
1261 template class GenericRow<StrField>;
1262 template class GenericKey<StrField>;
1263