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