1 /* Copyright (c) 2003-2007 MySQL AB
2    Use is subject to license terms
3 
4    This program is free software; you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation; version 2 of the License.
7 
8    This program is distributed in the hope that it will be useful,
9    but WITHOUT ANY WARRANTY; without even the implied warranty of
10    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11    GNU General Public License for more details.
12 
13    You should have received a copy of the GNU General Public License
14    along with this program; if not, write to the Free Software
15    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA */
16 
17 #include <NdbScanFilter.hpp>
18 #include <Ndb.hpp>
19 #include <NdbOperation.hpp>
20 #include "NdbDictionaryImpl.hpp"
21 #include <Vector.hpp>
22 #include <NdbOut.hpp>
23 #include <Interpreter.hpp>
24 #include <signaldata/AttrInfo.hpp>
25 #include "NdbApiSignal.hpp"
26 #include "NdbUtil.hpp"
27 
28 #ifdef VM_TRACE
29 #include <NdbEnv.h>
30 #define INT_DEBUG(x) \
31   { const char* tmp = NdbEnv_GetEnv("INT_DEBUG", (char*)0, 0); \
32   if (tmp != 0 && strlen(tmp) != 0) { ndbout << "INT:"; ndbout_c x; } }
33 #else
34 #define INT_DEBUG(x)
35 #endif
36 
37 class NdbScanFilterImpl {
38 public:
NdbScanFilterImpl()39   NdbScanFilterImpl() {}
40   struct State {
41     NdbScanFilter::Group m_group;
42     Uint32 m_popCount;
43     Uint32 m_ownLabel;
44     Uint32 m_trueLabel;
45     Uint32 m_falseLabel;
46   };
47 
48   int m_label;
49   State m_current;
50   Uint32 m_negative;    //used for translating NAND/NOR to AND/OR, equal 0 or 1
51   Vector<State> m_stack;
52   Vector<Uint32> m_stack2;    //to store info of m_negative
53   NdbOperation * m_operation;
54   Uint32 m_latestAttrib;
55 
56   int cond_col(Interpreter::UnaryCondition, Uint32 attrId);
57 
58   int cond_col_const(Interpreter::BinaryCondition, Uint32 attrId,
59 		     const void * value, Uint32 len);
60 
61   bool m_abort_on_too_large;
62 
63   NdbOperation::OperationStatus m_initial_op_status;
64   Uint32 m_initial_AI_size;
65   Uint32 m_max_size;
66 
get_size()67   Uint32 get_size() {
68     assert(m_operation->theTotalCurrAI_Len >= m_initial_AI_size);
69     return m_operation->theTotalCurrAI_Len - m_initial_AI_size;
70   }
check_size()71   bool check_size() {
72     if (get_size() <= m_max_size)
73       return true;
74     handle_filter_too_large();
75     return false;
76   }
77   void handle_filter_too_large();
78 
79   NdbError m_error;
80 };
81 
82 const Uint32 LabelExit = ~0;
83 
84 
NdbScanFilter(class NdbOperation * op,bool abort_on_too_large,Uint32 max_size)85 NdbScanFilter::NdbScanFilter(class NdbOperation * op,
86                              bool abort_on_too_large,
87                              Uint32 max_size)
88   : m_impl(* new NdbScanFilterImpl())
89 {
90   DBUG_ENTER("NdbScanFilter::NdbScanFilter");
91   m_impl.m_current.m_group = (NdbScanFilter::Group)0;
92   m_impl.m_current.m_popCount = 0;
93   m_impl.m_current.m_ownLabel = 0;
94   m_impl.m_current.m_trueLabel = ~0;
95   m_impl.m_current.m_falseLabel = ~0;
96   m_impl.m_label = 0;
97   m_impl.m_latestAttrib = ~0;
98   m_impl.m_operation = op;
99   m_impl.m_negative = 0;
100 
101   DBUG_PRINT("info", ("op status: %d tot AI: %u in curr: %u",
102                       op->theStatus,
103                       op->theTotalCurrAI_Len, op->theAI_LenInCurrAI));
104 
105   m_impl.m_abort_on_too_large = abort_on_too_large;
106 
107   m_impl.m_initial_op_status = op->theStatus;
108   m_impl.m_initial_AI_size = op->theTotalCurrAI_Len;
109   if (max_size > NDB_MAX_SCANFILTER_SIZE_IN_WORDS)
110     max_size = NDB_MAX_SCANFILTER_SIZE_IN_WORDS;
111   m_impl.m_max_size = max_size;
112 
113   m_impl.m_error.code = 0;
114   DBUG_VOID_RETURN;
115 }
116 
~NdbScanFilter()117 NdbScanFilter::~NdbScanFilter(){
118   delete &m_impl;
119 }
120 
121 int
begin(Group group)122 NdbScanFilter::begin(Group group){
123 
124   if (m_impl.m_stack2.push_back(m_impl.m_negative))
125   {
126     m_impl.m_operation->setErrorCodeAbort(4000);
127     return -1;
128   }
129   switch(group){
130   case NdbScanFilter::AND:
131     INT_DEBUG(("Begin(AND)"));
132     if(m_impl.m_negative == 1){
133       group = NdbScanFilter::OR;
134     }
135     break;
136   case NdbScanFilter::OR:
137     INT_DEBUG(("Begin(OR)"));
138     if(m_impl.m_negative == 1){
139       group = NdbScanFilter::AND;
140     }
141     break;
142   case NdbScanFilter::NAND:
143     INT_DEBUG(("Begin(NAND)"));
144     if(m_impl.m_negative == 0){
145       group = NdbScanFilter::OR;
146       m_impl.m_negative = 1;
147     }else{
148       group = NdbScanFilter::AND;
149       m_impl.m_negative = 0;
150     }
151     break;
152   case NdbScanFilter::NOR:
153     INT_DEBUG(("Begin(NOR)"));
154     if(m_impl.m_negative == 0){
155       group = NdbScanFilter::AND;
156       m_impl.m_negative = 1;
157     }else{
158       group = NdbScanFilter::OR;
159       m_impl.m_negative = 0;
160     }
161     break;
162   }
163 
164   if(group == m_impl.m_current.m_group){
165     switch(group){
166     case NdbScanFilter::AND:
167     case NdbScanFilter::OR:
168       m_impl.m_current.m_popCount++;
169       return 0;
170     case NdbScanFilter::NOR:
171     case NdbScanFilter::NAND:
172       break;
173     }
174   }
175 
176   NdbScanFilterImpl::State tmp = m_impl.m_current;
177   if (m_impl.m_stack.push_back(m_impl.m_current))
178   {
179     m_impl.m_operation->setErrorCodeAbort(4000);
180     return -1;
181   }
182   m_impl.m_current.m_group = group;
183   m_impl.m_current.m_ownLabel = m_impl.m_label++;
184   m_impl.m_current.m_popCount = 0;
185 
186   switch(group){
187   case NdbScanFilter::AND:
188   case NdbScanFilter::NAND:
189     m_impl.m_current.m_falseLabel = m_impl.m_current.m_ownLabel;
190     m_impl.m_current.m_trueLabel = tmp.m_trueLabel;
191     break;
192   case NdbScanFilter::OR:
193   case NdbScanFilter::NOR:
194     m_impl.m_current.m_falseLabel = tmp.m_falseLabel;
195     m_impl.m_current.m_trueLabel = m_impl.m_current.m_ownLabel;
196     break;
197   default:
198     m_impl.m_operation->setErrorCodeAbort(4260);
199     return -1;
200   }
201 
202   return 0;
203 }
204 
205 int
end()206 NdbScanFilter::end(){
207 
208   if(m_impl.m_stack2.size() == 0){
209     m_impl.m_operation->setErrorCodeAbort(4259);
210     return -1;
211   }
212   m_impl.m_negative = m_impl.m_stack2.back();
213   m_impl.m_stack2.erase(m_impl.m_stack2.size() - 1);
214 
215   switch(m_impl.m_current.m_group){
216   case NdbScanFilter::AND:
217     INT_DEBUG(("End(AND pc=%d)", m_impl.m_current.m_popCount));
218     break;
219   case NdbScanFilter::OR:
220     INT_DEBUG(("End(OR pc=%d)", m_impl.m_current.m_popCount));
221     break;
222   case NdbScanFilter::NAND:
223     INT_DEBUG(("End(NAND pc=%d)", m_impl.m_current.m_popCount));
224     break;
225   case NdbScanFilter::NOR:
226     INT_DEBUG(("End(NOR pc=%d)", m_impl.m_current.m_popCount));
227     break;
228   }
229 
230   if(m_impl.m_current.m_popCount > 0){
231     m_impl.m_current.m_popCount--;
232     return 0;
233   }
234 
235   NdbScanFilterImpl::State tmp = m_impl.m_current;
236   if(m_impl.m_stack.size() == 0){
237     m_impl.m_operation->setErrorCodeAbort(4259);
238     return -1;
239   }
240   m_impl.m_current = m_impl.m_stack.back();
241   m_impl.m_stack.erase(m_impl.m_stack.size() - 1);
242 
243   switch(tmp.m_group){
244   case NdbScanFilter::AND:
245     if(tmp.m_trueLabel == (Uint32)~0){
246       if (m_impl.m_operation->interpret_exit_ok() == -1)
247         return -1;
248     } else {
249       if (m_impl.m_operation->branch_label(tmp.m_trueLabel) == -1)
250         return -1;
251     }
252     break;
253   case NdbScanFilter::NAND:
254     if(tmp.m_trueLabel == (Uint32)~0){
255       if (m_impl.m_operation->interpret_exit_nok() == -1)
256         return -1;
257     } else {
258       if (m_impl.m_operation->branch_label(tmp.m_falseLabel) == -1)
259         return -1;
260     }
261     break;
262   case NdbScanFilter::OR:
263     if(tmp.m_falseLabel == (Uint32)~0){
264       if (m_impl.m_operation->interpret_exit_nok() == -1)
265         return -1;
266     } else {
267       if (m_impl.m_operation->branch_label(tmp.m_falseLabel) == -1)
268         return -1;
269     }
270     break;
271   case NdbScanFilter::NOR:
272     if(tmp.m_falseLabel == (Uint32)~0){
273       if (m_impl.m_operation->interpret_exit_ok() == -1)
274         return -1;
275     } else {
276       if (m_impl.m_operation->branch_label(tmp.m_trueLabel) == -1)
277         return -1;
278     }
279     break;
280   default:
281     m_impl.m_operation->setErrorCodeAbort(4260);
282     return -1;
283   }
284 
285   if (m_impl.m_operation->def_label(tmp.m_ownLabel) == -1)
286     return -1;
287 
288   if(m_impl.m_stack.size() == 0){
289     switch(tmp.m_group){
290     case NdbScanFilter::AND:
291     case NdbScanFilter::NOR:
292       if (m_impl.m_operation->interpret_exit_nok() == -1)
293         return -1;
294       break;
295     case NdbScanFilter::OR:
296     case NdbScanFilter::NAND:
297       if (m_impl.m_operation->interpret_exit_ok() == -1)
298         return -1;
299       break;
300     default:
301       m_impl.m_operation->setErrorCodeAbort(4260);
302       return -1;
303     }
304   }
305 
306   if (!m_impl.check_size())
307     return -1;
308   return 0;
309 }
310 
311 int
istrue()312 NdbScanFilter::istrue(){
313   if(m_impl.m_current.m_group < NdbScanFilter::AND ||
314      m_impl.m_current.m_group > NdbScanFilter::NOR){
315     m_impl.m_operation->setErrorCodeAbort(4260);
316     return -1;
317   }
318 
319   if(m_impl.m_current.m_trueLabel == (Uint32)~0){
320     if (m_impl.m_operation->interpret_exit_ok() == -1)
321       return -1;
322   } else {
323     if (m_impl.m_operation->branch_label(m_impl.m_current.m_trueLabel) == -1)
324       return -1;
325   }
326 
327   if (!m_impl.check_size())
328     return -1;
329   return 0;
330 }
331 
332 int
isfalse()333 NdbScanFilter::isfalse(){
334   if(m_impl.m_current.m_group < NdbScanFilter::AND ||
335      m_impl.m_current.m_group > NdbScanFilter::NOR){
336     m_impl.m_operation->setErrorCodeAbort(4260);
337     return -1;
338   }
339 
340   if(m_impl.m_current.m_falseLabel == (Uint32)~0){
341     if (m_impl.m_operation->interpret_exit_nok() == -1)
342       return -1;
343   } else {
344     if (m_impl.m_operation->branch_label(m_impl.m_current.m_falseLabel) == -1)
345       return -1;
346   }
347 
348   if (!m_impl.check_size())
349     return -1;
350   return 0;
351 }
352 
353 NdbOperation *
getNdbOperation()354 NdbScanFilter::getNdbOperation(){
355   return m_impl.m_operation;
356 }
357 
358 #define action(x, y, z)
359 
360 
361 typedef int (NdbOperation:: * Branch1)(Uint32, Uint32 label);
362 typedef int (NdbOperation:: * StrBranch2)(Uint32, const void*, Uint32, bool, Uint32);
363 
364 struct tab2 {
365   Branch1 m_branches[5];
366 };
367 
368 static const tab2 table2[] = {
369   /**
370    * IS NULL
371    */
372   { { 0,
373       &NdbOperation::branch_col_ne_null,
374       &NdbOperation::branch_col_eq_null,
375       &NdbOperation::branch_col_eq_null,
376       &NdbOperation::branch_col_ne_null } }
377 
378   /**
379    * IS NOT NULL
380    */
381   ,{ { 0,
382        &NdbOperation::branch_col_eq_null,
383        &NdbOperation::branch_col_ne_null,
384        &NdbOperation::branch_col_ne_null,
385        &NdbOperation::branch_col_eq_null } }
386 };
387 
388 const int tab2_sz = sizeof(table2)/sizeof(table2[0]);
389 
390 int
cond_col(Interpreter::UnaryCondition op,Uint32 AttrId)391 NdbScanFilterImpl::cond_col(Interpreter::UnaryCondition op, Uint32 AttrId){
392 
393   if(op < 0 || op >= tab2_sz){
394     m_operation->setErrorCodeAbort(4262);
395     return -1;
396   }
397 
398   if(m_current.m_group < NdbScanFilter::AND ||
399      m_current.m_group > NdbScanFilter::NOR){
400     m_operation->setErrorCodeAbort(4260);
401     return -1;
402   }
403 
404   Branch1 branch = table2[op].m_branches[m_current.m_group];
405   if ((m_operation->* branch)(AttrId, m_current.m_ownLabel) == -1)
406     return -1;
407 
408   if (!check_size())
409     return -1;
410   return 0;
411 }
412 
413 int
isnull(int AttrId)414 NdbScanFilter::isnull(int AttrId){
415   if(m_impl.m_negative == 1)
416     return m_impl.cond_col(Interpreter::IS_NOT_NULL, AttrId);
417   else
418     return m_impl.cond_col(Interpreter::IS_NULL, AttrId);
419 }
420 
421 int
isnotnull(int AttrId)422 NdbScanFilter::isnotnull(int AttrId){
423   if(m_impl.m_negative == 1)
424     return m_impl.cond_col(Interpreter::IS_NULL, AttrId);
425   else
426     return m_impl.cond_col(Interpreter::IS_NOT_NULL, AttrId);
427 }
428 
429 struct tab3 {
430   StrBranch2 m_branches[5];
431 };
432 
433 static const tab3 table3[] = {
434   /**
435    * EQ (AND, OR, NAND, NOR)
436    */
437   { { 0,
438       &NdbOperation::branch_col_ne,
439       &NdbOperation::branch_col_eq,
440       &NdbOperation::branch_col_ne,
441       &NdbOperation::branch_col_eq } }
442 
443   /**
444    * NEQ
445    */
446   ,{ { 0,
447        &NdbOperation::branch_col_eq,
448        &NdbOperation::branch_col_ne,
449        &NdbOperation::branch_col_eq,
450        &NdbOperation::branch_col_ne } }
451 
452   /**
453    * LT
454    */
455   ,{ { 0,
456        &NdbOperation::branch_col_le,
457        &NdbOperation::branch_col_gt,
458        &NdbOperation::branch_col_le,
459        &NdbOperation::branch_col_gt } }
460 
461   /**
462    * LE
463    */
464   ,{ { 0,
465        &NdbOperation::branch_col_lt,
466        &NdbOperation::branch_col_ge,
467        &NdbOperation::branch_col_lt,
468        &NdbOperation::branch_col_ge } }
469 
470   /**
471    * GT
472    */
473   ,{ { 0,
474        &NdbOperation::branch_col_ge,
475        &NdbOperation::branch_col_lt,
476        &NdbOperation::branch_col_ge,
477        &NdbOperation::branch_col_lt } }
478 
479   /**
480    * GE
481    */
482   ,{ { 0,
483        &NdbOperation::branch_col_gt,
484        &NdbOperation::branch_col_le,
485        &NdbOperation::branch_col_gt,
486        &NdbOperation::branch_col_le } }
487 
488   /**
489    * LIKE
490    */
491   ,{ { 0,
492        &NdbOperation::branch_col_notlike,
493        &NdbOperation::branch_col_like,
494        &NdbOperation::branch_col_notlike,
495        &NdbOperation::branch_col_like } }
496 
497   /**
498    * NOT LIKE
499    */
500   ,{ { 0,
501        &NdbOperation::branch_col_like,
502        &NdbOperation::branch_col_notlike,
503        &NdbOperation::branch_col_like,
504        &NdbOperation::branch_col_notlike } }
505 };
506 
507 const int tab3_sz = sizeof(table3)/sizeof(table3[0]);
508 
509 int
cond_col_const(Interpreter::BinaryCondition op,Uint32 AttrId,const void * value,Uint32 len)510 NdbScanFilterImpl::cond_col_const(Interpreter::BinaryCondition op,
511 				  Uint32 AttrId,
512 				  const void * value, Uint32 len){
513   if(op < 0 || op >= tab3_sz){
514     m_operation->setErrorCodeAbort(4260);
515     return -1;
516   }
517 
518   if(m_current.m_group < NdbScanFilter::AND ||
519      m_current.m_group > NdbScanFilter::NOR){
520     m_operation->setErrorCodeAbort(4260);
521     return -1;
522   }
523 
524   StrBranch2 branch;
525   if(m_negative == 1){  //change NdbOperation to its negative
526     if(m_current.m_group == NdbScanFilter::AND)
527       branch = table3[op].m_branches[(Uint32)(m_current.m_group) + 1];
528     if(m_current.m_group == NdbScanFilter::OR)
529       branch = table3[op].m_branches[(Uint32)(m_current.m_group) - 1];
530   }else{
531     branch = table3[op].m_branches[(Uint32)(m_current.m_group)];
532   }
533 
534   const NdbDictionary::Column * col =
535     m_operation->m_currentTable->getColumn(AttrId);
536 
537   if(col == 0){
538     m_operation->setErrorCodeAbort(4261);
539     return -1;
540   }
541 
542   if ((m_operation->* branch)(AttrId, value, len, false, m_current.m_ownLabel) == -1)
543     return -1;
544 
545   if (!check_size())
546     return -1;
547   return 0;
548 }
549 
550 int
cmp(BinaryCondition cond,int ColId,const void * val,Uint32 len)551 NdbScanFilter::cmp(BinaryCondition cond, int ColId,
552 		   const void *val, Uint32 len)
553 {
554   switch(cond){
555   case COND_LE:
556     return m_impl.cond_col_const(Interpreter::LE, ColId, val, len);
557   case COND_LT:
558     return m_impl.cond_col_const(Interpreter::LT, ColId, val, len);
559   case COND_GE:
560     return m_impl.cond_col_const(Interpreter::GE, ColId, val, len);
561   case COND_GT:
562     return m_impl.cond_col_const(Interpreter::GT, ColId, val, len);
563   case COND_EQ:
564     return m_impl.cond_col_const(Interpreter::EQ, ColId, val, len);
565   case COND_NE:
566     return m_impl.cond_col_const(Interpreter::NE, ColId, val, len);
567   case COND_LIKE:
568     return m_impl.cond_col_const(Interpreter::LIKE, ColId, val, len);
569   case COND_NOT_LIKE:
570     return m_impl.cond_col_const(Interpreter::NOT_LIKE, ColId, val, len);
571   }
572   return -1;
573 }
574 
575 void
handle_filter_too_large()576 NdbScanFilterImpl::handle_filter_too_large()
577 {
578   DBUG_ENTER("NdbScanFilterImpl::handle_filter_too_large");
579 
580   NdbOperation* const op = m_operation;
581   m_error.code = NdbScanFilter::FilterTooLarge;
582   if (m_abort_on_too_large)
583     op->setErrorCodeAbort(m_error.code);
584 
585   /*
586    * Possible interpreted parts at this point are:
587    *
588    * 1. initial read
589    * 2. interpreted program
590    *
591    * It is assumed that NdbScanFilter has created all of 2
592    * so that we don't have to save interpreter state.
593    */
594 
595   const Uint32 size = get_size();
596   assert(size != 0);
597 
598   // new ATTRINFO size
599   const Uint32 new_size = m_initial_AI_size;
600 
601   // find last signal for new size
602   assert(op->theFirstATTRINFO != NULL);
603   NdbApiSignal* lastSignal = op->theFirstATTRINFO;
604   Uint32 n = 0;
605   while (n + AttrInfo::DataLength < new_size) {
606     lastSignal = lastSignal->next();
607     assert(lastSignal != NULL);
608     n += AttrInfo::DataLength;
609   }
610   assert(n < size);
611 
612   // release remaining signals
613   NdbApiSignal* tSignal = lastSignal->next();
614   op->theNdb->releaseSignalsInList(&tSignal);
615   lastSignal->next(NULL);
616 
617   // length of lastSignal
618   const Uint32 new_curr = AttrInfo::HeaderLength + new_size - n;
619   assert(new_curr <= 25);
620 
621   DBUG_PRINT("info", ("op status: %d->%d tot AI: %u->%u in curr: %u->%u",
622                       op->theStatus, m_initial_op_status,
623                       op->theTotalCurrAI_Len, new_size,
624                       op->theAI_LenInCurrAI, new_curr));
625 
626   // reset op state
627   op->theStatus = m_initial_op_status;
628 
629   // reset interpreter state to initial
630 
631   NdbBranch* tBranch = op->theFirstBranch;
632   while (tBranch != NULL) {
633     NdbBranch* tmp = tBranch;
634     tBranch = tBranch->theNext;
635     op->theNdb->releaseNdbBranch(tmp);
636   }
637   op->theFirstBranch = NULL;
638   op->theLastBranch = NULL;
639 
640   NdbLabel* tLabel = op->theFirstLabel;
641   while (tLabel != NULL) {
642     NdbLabel* tmp = tLabel;
643     tLabel = tLabel->theNext;
644     op->theNdb->releaseNdbLabel(tmp);
645   }
646   op->theFirstLabel = NULL;
647   op->theLastLabel = NULL;
648 
649   NdbCall* tCall = op->theFirstCall;
650   while (tCall != NULL) {
651     NdbCall* tmp = tCall;
652     tCall = tCall->theNext;
653     op->theNdb->releaseNdbCall(tmp);
654   }
655   op->theFirstCall = NULL;
656   op->theLastCall = NULL;
657 
658   NdbSubroutine* tSubroutine = op->theFirstSubroutine;
659   while (tSubroutine != NULL) {
660     NdbSubroutine* tmp = tSubroutine;
661     tSubroutine = tSubroutine->theNext;
662     op->theNdb->releaseNdbSubroutine(tmp);
663   }
664   op->theFirstSubroutine = NULL;
665   op->theLastSubroutine = NULL;
666 
667   op->theNoOfLabels = 0;
668   op->theNoOfSubroutines = 0;
669 
670   // reset AI size
671   op->theTotalCurrAI_Len = new_size;
672   op->theAI_LenInCurrAI = new_curr;
673 
674   // reset signal pointers
675   op->theCurrentATTRINFO = lastSignal;
676   op->theATTRINFOptr = &lastSignal->getDataPtrSend()[new_curr];
677 
678   // interpreter sizes are set later somewhere
679 
680   DBUG_VOID_RETURN;
681 }
682 
683 static void
update(const NdbError & _err)684 update(const NdbError & _err){
685   NdbError & error = (NdbError &) _err;
686   ndberror_struct ndberror = (ndberror_struct)error;
687   ndberror_update(&ndberror);
688   error = NdbError(ndberror);
689 }
690 
691 const NdbError &
getNdbError() const692 NdbScanFilter::getNdbError() const
693 {
694   update(m_impl.m_error);
695   return m_impl.m_error;
696 }
697 
698 
699 #if 0
700 int
701 main(void){
702   if(0)
703   {
704     ndbout << "a > 7 AND b < 9 AND c = 4" << endl;
705     NdbScanFilter f(0);
706     f.begin(NdbScanFilter::AND);
707     f.gt(0, 7);
708     f.lt(1, 9);
709     f.eq(2, 4);
710     f.end();
711     ndbout << endl;
712   }
713 
714   if(0)
715   {
716     ndbout << "a > 7 OR b < 9 OR c = 4" << endl;
717     NdbScanFilter f(0);
718     f.begin(NdbScanFilter::OR);
719     f.gt(0, 7);
720     f.lt(1, 9);
721     f.eq(2, 4);
722     f.end();
723     ndbout << endl;
724   }
725 
726   if(0)
727   {
728     ndbout << "a > 7 AND (b < 9 OR c = 4)" << endl;
729     NdbScanFilter f(0);
730     f.begin(NdbScanFilter::AND);
731     f.gt(0, 7);
732     f.begin(NdbScanFilter::OR);
733     f.lt(1, 9);
734     f.eq(2, 4);
735     f.end();
736     f.end();
737     ndbout << endl;
738   }
739 
740   if(0)
741   {
742     ndbout << "a > 7 AND (b < 9 AND c = 4)" << endl;
743     NdbScanFilter f(0);
744     f.begin(NdbScanFilter::AND);
745     f.gt(0, 7);
746     f.begin(NdbScanFilter::AND);
747     f.lt(1, 9);
748     f.eq(2, 4);
749     f.end();
750     f.end();
751     ndbout << endl;
752   }
753 
754   if(0)
755   {
756     ndbout << "(a > 7 AND b < 9) AND c = 4" << endl;
757     NdbScanFilter f(0);
758     f.begin(NdbScanFilter::AND);
759     f.begin(NdbScanFilter::AND);
760     f.gt(0, 7);
761     f.lt(1, 9);
762     f.end();
763     f.eq(2, 4);
764     f.end();
765     ndbout << endl;
766   }
767 
768   if(1)
769   {
770     ndbout << "(a > 7 OR b < 9) AND (c = 4 OR c = 5)" << endl;
771     NdbScanFilter f(0);
772     f.begin(NdbScanFilter::AND);
773     f.begin(NdbScanFilter::OR);
774     f.gt(0, 7);
775     f.lt(1, 9);
776     f.end();
777     f.begin(NdbScanFilter::OR);
778     f.eq(2, 4);
779     f.eq(2, 5);
780     f.end();
781     f.end();
782     ndbout << endl;
783   }
784 
785   if(1)
786   {
787     ndbout << "(a > 7 AND b < 9) OR (c = 4 AND c = 5)" << endl;
788     NdbScanFilter f(0);
789     f.begin(NdbScanFilter::OR);
790     f.begin(NdbScanFilter::AND);
791     f.gt(0, 7);
792     f.lt(1, 9);
793     f.end();
794     f.begin(NdbScanFilter::AND);
795     f.eq(2, 4);
796     f.eq(2, 5);
797     f.end();
798     f.end();
799     ndbout << endl;
800   }
801 
802   if(1)
803   {
804     ndbout <<
805       "((a > 7 AND b < 9) OR (c = 4 AND d = 5)) AND "
806       "((e > 6 AND f < 8) OR (g = 2 AND h = 3)) "  << endl;
807     NdbScanFilter f(0);
808     f.begin(NdbScanFilter::AND);
809     f.begin(NdbScanFilter::OR);
810     f.begin(NdbScanFilter::AND);
811     f.gt(0, 7);
812     f.lt(1, 9);
813     f.end();
814     f.begin(NdbScanFilter::AND);
815     f.eq(2, 4);
816     f.eq(3, 5);
817     f.end();
818     f.end();
819 
820     f.begin(NdbScanFilter::OR);
821     f.begin(NdbScanFilter::AND);
822     f.gt(4, 6);
823     f.lt(5, 8);
824     f.end();
825     f.begin(NdbScanFilter::AND);
826     f.eq(6, 2);
827     f.eq(7, 3);
828     f.end();
829     f.end();
830     f.end();
831   }
832 
833   return 0;
834 }
835 #endif
836 
837 template class Vector<NdbScanFilterImpl::State>;
838 
839