1 /***************** Filter C++ Class Filter Code (.CPP) *****************/
2 /*  Name: FILTER.CPP  Version 4.0                                      */
3 /*                                                                     */
4 /*  (C) Copyright to the author Olivier BERTRAND          1998-2017    */
5 /*                                                                     */
6 /*  This file contains the class FILTER function code.                 */
7 /***********************************************************************/
8 
9 /***********************************************************************/
10 /*  Include relevant MariaDB header file.                              */
11 /***********************************************************************/
12 #include "my_global.h"
13 //#include "sql_class.h"
14 //#include "sql_time.h"
15 
16 #if defined(_WIN32)
17 //#include <windows.h>
18 #else   // !_WIN32
19 #include <string.h>
20 #include <sys/types.h>
21 #include <sys/stat.h>
22 #endif  // !_WIN32
23 
24 
25 /***********************************************************************/
26 /*  Include required application header files                          */
27 /*  global.h    is header containing all global Plug declarations.     */
28 /*  plgdbsem.h  is header containing the DB applic. declarations.      */
29 /*  xobject.h   is header containing the XOBJECT derived classes dcls. */
30 /***********************************************************************/
31 #include "global.h"
32 #include "plgdbsem.h"
33 #include "tabcol.h"
34 #include "xtable.h"
35 #include "array.h"
36 #include "filter.h"
37 #include "xindex.h"
38 
39 /***********************************************************************/
40 /*  Utility routines.                                                  */
41 /***********************************************************************/
42 void  PlugConvertConstant(PGLOBAL, void* &, short&);
43 //void *PlugCopyDB(PTABS, void*, INT);
44 void  NewPointer(PTABS, void*, void*);
45 void  AddPointer(PTABS, void*);
46 
MakeParm(PGLOBAL g,PXOB xp)47 static PPARM MakeParm(PGLOBAL g, PXOB xp)
48   {
49   PPARM pp = (PPARM)PlugSubAlloc(g, NULL, sizeof(PARM));
50   pp->Type = TYPE_XOBJECT;
51   pp->Value = xp;
52   pp->Domain = 0;
53   pp->Next = NULL;
54   return pp;
55   } // end of MakeParm
56 
57 /***********************************************************************/
58 /*  Routines called internally/externally by FILTER functions.         */
59 /***********************************************************************/
60 bool   PlugEvalLike(PGLOBAL, LPCSTR, LPCSTR, bool);
61 //bool  ReadSubQuery(PGLOBAL, PSUBQ);
62 //PSUBQ OpenSubQuery(PGLOBAL, PSQL);
63 //void  PlugCloseDB(PGLOBAL, PSQL);
64 BYTE   OpBmp(PGLOBAL g, OPVAL opc);
65 PARRAY MakeValueArray(PGLOBAL g, PPARM pp);
66 
67 /***********************************************************************/
68 /*  Returns the bitmap representing the conditions that must not be    */
69 /*  met when returning from TestValue for a given operator.            */
70 /*  Bit one is EQ, bit 2 is LT, and bit 3 is GT.                       */
71 /***********************************************************************/
OpBmp(PGLOBAL g,OPVAL opc)72 BYTE OpBmp(PGLOBAL g, OPVAL opc)
73   {
74   BYTE bt;
75 
76   switch (opc) {
77     case OP_IN:
78     case OP_EQ: bt = 0x06; break;
79     case OP_NE: bt = 0x01; break;
80     case OP_GT: bt = 0x03; break;
81     case OP_GE: bt = 0x02; break;
82     case OP_LT: bt = 0x05; break;
83     case OP_LE: bt = 0x04; break;
84     case OP_EXIST: bt = 0x00; break;
85     default:
86       sprintf(g->Message, MSG(BAD_FILTER_OP), opc);
87 			throw (int)TYPE_FILTER;
88 	} // endswitch opc
89 
90   return bt;
91   } // end of OpBmp
92 
93 /***********************************************************************/
94 /*  Routines called externally by CondFilter.                          */
95 /***********************************************************************/
MakeFilter(PGLOBAL g,PFIL fp1,OPVAL vop,PFIL fp2)96 PFIL MakeFilter(PGLOBAL g, PFIL fp1, OPVAL vop, PFIL fp2)
97   {
98   PFIL filp = new(g) FILTER(g, vop);
99 
100   filp->Arg(0) = fp1;
101   filp->Arg(1) = (fp2) ? fp2 : pXVOID;
102 
103   if (filp->Convert(g, false))
104     return NULL;
105 
106   return filp;
107   } // end of MakeFilter
108 
MakeFilter(PGLOBAL g,PCOL * colp,POPER pop,PPARM pfirst,bool neg)109 PFIL MakeFilter(PGLOBAL g, PCOL *colp, POPER pop, PPARM pfirst, bool neg)
110 {
111   PPARM parmp, pp[2];
112   PFIL  fp1, fp2, filp = NULL;
113 
114   if (pop->Val == OP_IN) {
115     PARRAY par = MakeValueArray(g, pfirst);
116 
117     if (par) {
118       pp[0] = MakeParm(g, colp[0]);
119       pp[1] = MakeParm(g, par);
120       fp1 = new(g) FILTER(g, pop, pp);
121 
122       if (fp1->Convert(g, false))
123         return NULL;
124 
125       filp = (neg) ? MakeFilter(g, fp1, OP_NOT, NULL) : fp1;
126       } // endif par
127 
128   } else if (pop->Val == OP_XX) {    // BETWEEN
129     if (pfirst && pfirst->Next) {
130       pp[0] = MakeParm(g, colp[0]);
131       pp[1] = pfirst;
132       fp1 = new(g) FILTER(g, neg ? OP_LT : OP_GE, pp);
133 
134       if (fp1->Convert(g, false))
135         return NULL;
136 
137       pp[1] = pfirst->Next;
138       fp2 = new(g) FILTER(g, neg ? OP_GT : OP_LE, pp);
139 
140       if (fp2->Convert(g, false))
141         return NULL;
142 
143       filp = MakeFilter(g, fp1, neg ? OP_OR : OP_AND, fp2);
144       } // endif parmp
145 
146   } else {
147     parmp = pfirst;
148 
149     for (int i = 0; i < 2; i++)
150       if (colp[i]) {
151         pp[i] = MakeParm(g, colp[i]);
152       } else {
153         if (!parmp || parmp->Domain != i)
154           return NULL;        // Logical error, should never happen
155 
156         pp[i] = parmp;
157         parmp = parmp->Next;
158       } // endif colp
159 
160     filp = new(g) FILTER(g, pop, pp);
161 
162     if (filp->Convert(g, false))
163       return NULL;
164 
165   } // endif's Val
166 
167   return filp;
168 } // end of MakeFilter
169 
170 /* --------------------------- Class FILTER -------------------------- */
171 
172 /***********************************************************************/
173 /*  FILTER public constructors.                                        */
174 /***********************************************************************/
FILTER(PGLOBAL g,POPER pop,PPARM * tp)175 FILTER::FILTER(PGLOBAL g, POPER pop, PPARM *tp)
176   {
177   Constr(g, pop->Val, pop->Mod, tp);
178   } // end of FILTER constructor
179 
FILTER(PGLOBAL g,OPVAL opc,PPARM * tp)180 FILTER::FILTER(PGLOBAL g, OPVAL opc, PPARM *tp)
181   {
182   Constr(g, opc, 0, tp);
183   } // end of FILTER constructor
184 
Constr(PGLOBAL g,OPVAL opc,int opm,PPARM * tp)185 void FILTER::Constr(PGLOBAL g, OPVAL opc, int opm, PPARM *tp)
186   {
187   Next = NULL;
188   Opc = opc;
189   Opm = opm;
190   Bt = 0x00;
191 
192   for (int i = 0; i < 2; i++) {
193     Test[i].B_T = TYPE_VOID;
194 
195     if (tp && tp[i]) {
196       PlugConvertConstant(g, tp[i]->Value, tp[i]->Type);
197 #if defined(_DEBUG)
198       assert(tp[i]->Type == TYPE_XOBJECT);
199 #endif
200       Arg(i) = (PXOB)tp[i]->Value;
201     } else
202       Arg(i) = pXVOID;
203 
204     Val(i) = NULL;
205     Test[i].Conv = FALSE;
206     } // endfor i
207 
208   } // end of Constr
209 
210 /***********************************************************************/
211 /*  FILTER copy constructor.                                           */
212 /***********************************************************************/
FILTER(PFIL fil1)213 FILTER::FILTER(PFIL fil1)
214   {
215   Next = NULL;
216   Opc = fil1->Opc;
217   Opm = fil1->Opm;
218   Test[0] = fil1->Test[0];
219   Test[1] = fil1->Test[1];
220   } // end of FILTER copy constructor
221 
222 #if 0
223 /***********************************************************************/
224 /*  Linearize:  Does the linearization of the filter tree:             */
225 /*    Independent filters (not implied in OR/NOT) will be separated    */
226 /*    from others and filtering operations will be automated by        */
227 /*    making a list of filter operations in polish operation style.    */
228 /*  Returned value points to the first filter of the list, which ends  */
229 /*  with the filter that was pointed by the first call argument,       */
230 /*  except for separators, in which case a loop is needed to find it.  */
231 /*  Note: a loop is used now in all cases (was not for OP_NOT) to be   */
232 /*    able to handle the case of filters whose arguments are already   */
233 /*    linearized, as it is done in LNA semantic routines. Indeed for   */
234 /*    already linearized chains, the first filter is never an OP_AND,  */
235 /*    OP_OR or OP_NOT filter, so this function just returns 'this'.    */
236 /***********************************************************************/
237 PFIL FILTER::Linearize(bool nosep)
238   {
239   int  i;
240   PFIL lfp[2], ffp[2] = {NULL,NULL};
241 
242   switch (Opc) {
243     case OP_NOT:
244       if (GetArgType(0) == TYPE_FILTER) {
245         lfp[0] = (PFIL)Arg(0);
246         ffp[0] = lfp[0]->Linearize(TRUE);
247         } /* endif */
248 
249       if (!ffp[0])
250         return NULL;
251 
252       while (lfp[0]->Next)         // See Note above
253         lfp[0] = lfp[0]->Next;
254 
255       Arg(0) = lfp[0];
256       lfp[0]->Next = this;
257       break;
258     case OP_OR:
259       nosep = TRUE;
260     case OP_AND:
261       for (i = 0; i < 2; i++) {
262         if (GetArgType(i) == TYPE_FILTER) {
263           lfp[i] = (PFIL)Arg(i);
264           ffp[i] = lfp[i]->Linearize(nosep);
265           } /* endif */
266 
267         if (!ffp[i])
268           return NULL;
269 
270         while (lfp[i]->Next)
271           lfp[i] = lfp[i]->Next;
272 
273         Arg(i) = lfp[i];
274         } /* endfor i */
275 
276       if (nosep) {
277         lfp[0]->Next = ffp[1];
278         lfp[1]->Next = this;
279       } else {
280         lfp[0]->Next = this;
281         Opc = OP_SEP;
282         Arg(1) = pXVOID;
283         Next = ffp[1];
284       } /* endif */
285 
286       break;
287     default:
288       ffp[0] = this;
289     } /* endswitch */
290 
291   return (ffp[0]);
292   } // end of Linearize
293 
294 /***********************************************************************/
295 /*  Link the fil2 filter chain to the fil1(this) filter chain.         */
296 /***********************************************************************/
297 PFIL FILTER::Link(PGLOBAL g, PFIL fil2)
298   {
299   PFIL fil1;
300 
301   if (trace(1))
302     htrc("Linking filter %p with op=%d... to filter %p with op=%d\n",
303                   this, Opc, fil2, (fil2) ? fil2->Opc : 0);
304 
305   for (fil1 = this; fil1->Next; fil1 = fil1->Next) ;
306 
307   if (fil1->Opc == OP_SEP)
308     fil1->Next = fil2;              // Separator already exists
309   else {
310     // Create a filter separator and insert it between the chains
311     PFIL filp = new(g) FILTER(g, OP_SEP);
312 
313     filp->Arg(0) = fil1;
314     filp->Next = fil2;
315     fil1->Next = filp;
316     } // endelse
317 
318   return (this);
319   } // end of Link
320 
321 /***********************************************************************/
322 /*  Remove eventual last separator from a filter chain.                */
323 /***********************************************************************/
324 PFIL FILTER::RemoveLastSep(void)
325   {
326   PFIL filp, gfp = NULL;
327 
328   // Find last filter block (filp) and previous one (gfp).
329   for (filp = this; filp->Next; filp = filp->Next)
330     gfp = filp;
331 
332   // If last filter is a separator, remove it
333   if (filp->Opc == OP_SEP)
334     if (gfp)
335       gfp->Next = NULL;
336     else
337       return NULL;       // chain is now empty
338 
339   return this;
340   } // end of RemoveLastSep
341 
342 /***********************************************************************/
343 /*  CheckColumn: Checks references to Columns in the filter and change */
344 /*  them into references to Col Blocks.                                */
345 /*  Returns the number of column references or -1 in case of column    */
346 /*  not found and -2 in case of unrecoverable error.                   */
347 /*  WHERE  filters are called with *aggreg == AGG_NO.                  */
348 /*  HAVING filters are called with *aggreg == AGG_ANY.                 */
349 /***********************************************************************/
350 int FILTER::CheckColumn(PGLOBAL g, PSQL sqlp, PXOB &p, int &ag)
351   {
352   char errmsg[MAX_STR] = "";
353   int  agg, k, n = 0;
354 
355   if (trace(1))
356     htrc("FILTER CheckColumn: sqlp=%p ag=%d\n", sqlp, ag);
357 
358   switch (Opc) {
359     case OP_SEP:
360     case OP_AND:
361     case OP_OR:
362     case OP_NOT:
363       return 0;  // This because we are called for a linearized filter
364     default:
365       break;
366     } // endswitch Opc
367 
368   // Check all arguments even in case of error for when we are called
369   // from CheckHaving, where references to an alias raise an error but
370   // we must have all other arguments to be set.
371   for (int i = 0; i < 2; i++) {
372     if (GetArgType(i) == TYPE_FILTER)      // Should never happen in
373       return 0;                            // current implementation
374 
375     agg = ag;
376 
377     if ((k = Arg(i)->CheckColumn(g, sqlp, Arg(i), agg)) < -1) {
378       return k;
379     } else if (k < 0) {
380       if (!*errmsg)                        // Keep first error message
381         strcpy(errmsg, g->Message);
382 
383     } else
384       n += k;
385 
386     } // endfor i
387 
388   if (*errmsg) {
389     strcpy(g->Message, errmsg);
390     return -1;
391   } else
392     return n;
393 
394   } // end of CheckColumn
395 
396 /***********************************************************************/
397 /*  RefNum: Find the number of references correlated sub-queries make  */
398 /*  to the columns of the outer query (pointed by sqlp).               */
399 /***********************************************************************/
400 int FILTER::RefNum(PSQL sqlp)
401   {
402   int n = 0;
403 
404   for (int i = 0; i < 2; i++)
405     n += Arg(i)->RefNum(sqlp);
406 
407   return n;
408   } // end of RefNum
409 
410 /***********************************************************************/
411 /*  CheckSubQuery: see SUBQUERY::CheckSubQuery for comment.            */
412 /***********************************************************************/
413 PXOB FILTER::CheckSubQuery(PGLOBAL g, PSQL sqlp)
414   {
415   switch (Opc) {
416     case OP_SEP:
417     case OP_AND:
418     case OP_OR:
419     case OP_NOT:
420       break;
421     default:
422       for (int i = 0; i < 2; i++)
423         if (!(Arg(i) = (PXOB)Arg(i)->CheckSubQuery(g, sqlp)))
424           return NULL;
425 
426       break;
427     } // endswitch Opc
428 
429   return this;
430   } // end of CheckSubQuery
431 
432 /***********************************************************************/
433 /*  SortJoin: function that places ahead of the list the 'good' groups */
434 /*  for join filtering.  These are groups with only one filter that    */
435 /*  specify equality between two different table columns, at least     */
436 /*  one is a table key column.  Doing so the join filter will be in    */
437 /*  general compatible with linearization of the joined table tree.    */
438 /*  This function has been added a further sorting on column indexing. */
439 /***********************************************************************/
440 PFIL FILTER::SortJoin(PGLOBAL g)
441   {
442   int     k;
443   PCOL    cp1, cp2;
444   PTDBASE tp1, tp2;
445   PFIL    fp, filp, gfp, filstart = this, filjoin = NULL, lfp = NULL;
446   bool    join = TRUE, key = TRUE;
447 
448   // This routine requires that the chain ends with a separator
449   // So check for it and eventually add one if necessary
450   for (filp = this; filp->Next; filp = filp->Next) ;
451 
452   if (filp->Opc != OP_SEP)
453     filp->Next = new(g) FILTER(g, OP_SEP);
454 
455  again:
456   for (k = (key) ? 0 : MAX_MULT_KEY; k <= MAX_MULT_KEY; k++)
457     for (gfp = NULL, fp = filp = filstart; filp; filp = filp->Next)
458       switch (filp->Opc) {
459         case OP_SEP:
460           if (join) {
461             // Put this filter group into the join filter group list.
462             if (!lfp)
463               filjoin = fp;
464             else
465               lfp->Next = fp;
466 
467             if (!gfp)
468               filstart = filp->Next;
469             else
470               gfp->Next = filp->Next;
471 
472             lfp = filp;              // last block of join filter list
473           } else
474             gfp = filp;              // last block of bad  filter list
475 
476           join = TRUE;
477           fp = filp->Next;
478           break;
479         case OP_LOJ:
480         case OP_ROJ:
481         case OP_DTJ:
482           join &= TRUE;
483           break;
484         case OP_EQ:
485           if (join && k > 0       // So specific join operators come first
486                    &&  filp->GetArgType(0) == TYPE_COLBLK
487                    &&  filp->GetArgType(1) == TYPE_COLBLK) {
488             cp1 = (PCOL)filp->Arg(0);
489             cp2 = (PCOL)filp->Arg(1);
490             tp1 = (PTDBASE)cp1->GetTo_Tdb();
491             tp2 = (PTDBASE)cp2->GetTo_Tdb();
492 
493             if (tp1->GetTdb_No() != tp2->GetTdb_No()) {
494               if (key)
495                 join &= (cp1->GetKey() == k || cp2->GetKey() == k);
496               else
497                 join &= (tp1->GetColIndex(cp1) || tp2->GetColIndex(cp2));
498 
499             } else
500               join = FALSE;
501 
502           } else
503             join = FALSE;
504 
505           break;
506         default:
507           join = FALSE;
508         } // endswitch filp->Opc
509 
510   if (key) {
511     key = FALSE;
512     goto again;
513     } // endif key
514 
515   if (filjoin) {
516     lfp->Next = filstart;
517     filstart = filjoin;
518     } // endif filjoin
519 
520   // Removing last separator is perhaps unuseful, but it was so
521   return filstart->RemoveLastSep();
522   } // end of SortJoin
523 
524 /***********************************************************************/
525 /*  Check that this filter is a good join filter.                      */
526 /*  If so the opj block will be set accordingly.                       */
527 /*  opj points to the join block, fprec to the filter block to which   */
528 /*  the rest of the chain must be linked in case of success.           */
529 /*  teq, tek and tk2 indicates the severity of the tests:              */
530 /*  tk2 == TRUE means both columns must be primary keys.               */
531 /*  tc2 == TRUE means both args must be columns (not expression).      */
532 /*  tek == TRUE means at least one column must be a primary key.       */
533 /*  teq == TRUE means the filter operator must be OP_EQ.               */
534 /*  tix == TRUE means at least one column must be a simple index key.  */
535 /*  thx == TRUE means at least one column must be a leading index key. */
536 /***********************************************************************/
537 bool FILTER::FindJoinFilter(POPJOIN opj, PFIL fprec, bool teq, bool tek,
538                             bool tk2, bool tc2, bool tix, bool thx)
539   {
540   if (trace(1))
541     htrc("FindJoinFilter: opj=%p fprec=%p tests=(%d,%d,%d,%d)\n",
542                           opj, fprec, teq, tek, tk2, tc2);
543 
544   // Firstly check that this filter is an independent filter
545   // meaning that it is the only one in its own group.
546   if (Next && Next->Opc != OP_SEP)
547     return (Opc < 0);
548 
549   // Keep only equi-joins and specific joins (Outer and Distinct)
550   // Normally specific join operators comme first because they have
551   // been placed first by SortJoin.
552   if (teq && Opc > OP_EQ)
553     return FALSE;
554 
555   // We have a candidate for join filter, now check that it
556   // fulfil the requirement about its operands, to point to
557   // columns of respectively the two TDB's of that join.
558   int    col1 = 0, col2 = 0;
559   bool   key = tk2;
560   bool   idx = FALSE, ihx = FALSE;
561   PIXDEF pdx;
562 
563   for (int i = 0; i < 2; i++)
564     if (GetArgType(i) == TYPE_COLBLK) {
565       PCOL colp = (PCOL)Arg(i);
566 
567       if (tk2)
568         key &= (colp->IsKey());
569       else
570         key |= (colp->IsKey());
571 
572       pdx = ((PTDBASE)colp->GetTo_Tdb())->GetColIndex(colp);
573       idx |= (pdx && pdx->GetNparts() == 1);
574       ihx |= (pdx != NULL);
575 
576       if      (colp->VerifyColumn(opj->GetTbx1()))
577         col1 = i + 1;
578       else if (colp->VerifyColumn(opj->GetTbx2()))
579         col2 = i + 1;
580 
581     } else if (!tc2 && GetArgType(i) != TYPE_CONST) {
582       PXOB xp = Arg(i);
583 
584       if      (xp->VerifyColumn(opj->GetTbx1()))
585         col1 = i + 1;
586       else if (xp->VerifyColumn(opj->GetTbx2()))
587         col2 = i + 1;
588 
589     } else
590       return (Opc < 0);
591 
592   if (col1 == 0 || col2 == 0)
593     return (Opc < 0);
594 
595   if (((tek && !key) || (tix && !idx) || (thx && !ihx)) && Opc != OP_DTJ)
596     return FALSE;
597 
598   // This is the join filter, set the join block.
599   if (col1 == 1) {
600     opj->SetCol1(Arg(0));
601     opj->SetCol2(Arg(1));
602   } else {
603     opj->SetCol1(Arg(1));
604     opj->SetCol2(Arg(0));
605 
606     switch (Opc) {
607 //    case OP_GT:  Opc = OP_LT;  break;
608 //    case OP_LT:  Opc = OP_GT;  break;
609 //    case OP_GE:  Opc = OP_LE;  break;
610 //    case OP_LE:  Opc = OP_GE;  break;
611       case OP_LOJ:
612       case OP_ROJ:
613       case OP_DTJ:
614         // For expended join operators, the filter must indicate
615         // the way the join should be done, and not the order of
616         // appearance of tables in the table list (which is kept
617         // because tables are sorted in AddTdb). Therefore the
618         // join is inversed, not the filter.
619         opj->InverseJoin();
620       default: break;
621       } // endswitch Opc
622 
623   } // endif col1
624 
625   if (Opc < 0) {
626     // For join operators, special processing is needed
627     int  knum = 0;
628     PFIL fp;
629 
630     switch (Opc) {
631       case OP_LOJ:
632         opj->SetJtype(JT_LEFT);
633         knum = opj->GetCol2()->GetKey();
634         break;
635       case OP_ROJ:
636         opj->SetJtype(JT_RIGHT);
637         knum = opj->GetCol1()->GetKey();
638         break;
639       case OP_DTJ:
640         for (knum = 1, fp = this->Next; fp; fp = fp->Next)
641           if (fp->Opc == OP_DTJ)
642             knum++;
643           else if (fp->Opc != OP_SEP)
644             break;
645 
646         opj->SetJtype(JT_DISTINCT);
647         opj->GetCol2()->SetKey(knum);
648         break;
649       default:
650         break;
651       } // endswitch Opc
652 
653     if (knum > 1) {
654       // Lets take care of a multiple key join
655       // We do a minimum of checking here as it will done later
656       int   k = 1;
657       OPVAL op;
658       BYTE  tmp[sizeof(Test[0])];
659 
660       for (fp = this->Next; k < knum && fp; fp = fp->Next) {
661         switch (op = fp->Opc) {
662           case OP_SEP:
663             continue;
664           case OP_LOJ:
665             if (Opc == OP_ROJ) {
666               op = Opc;
667               memcpy(tmp, &fp->Test[0], sizeof(Test[0]));
668               fp->Test[0] = fp->Test[1];
669               memcpy(&fp->Test[1], tmp, sizeof(Test[0]));
670               } // endif Opc
671 
672             k++;
673             break;
674           case OP_ROJ:
675             if (Opc == OP_LOJ) {
676               op = Opc;
677               memcpy(tmp, &fp->Test[0], sizeof(Test[0]));
678               fp->Test[0] = fp->Test[1];
679               memcpy(&fp->Test[1], tmp, sizeof(Test[0]));
680               } // endif Opc
681 
682             k++;
683             break;
684           case OP_DTJ:
685             if (op == Opc && fp->GetArgType(1) == TYPE_COLBLK)
686               ((PCOL)fp->Arg(1))->SetKey(knum);
687 
688             k++;
689             break;
690           default:
691             break;
692           } // endswitch op
693 
694         if (op != Opc)
695           return TRUE;
696 
697         fp->Opc = OP_EQ;
698         } // endfor fp
699 
700       } // endif k
701 
702     Opc = OP_EQ;
703     } // endif Opc
704 
705   // Set the join filter operator
706   opj->SetOpc(Opc);
707 
708   // Now mark the columns involved in the join filter because
709   // this information will be used by the linearize program.
710   // Note: this should be replaced in the future by something
711   // enabling to mark tables as Parent or Child.
712   opj->GetCol1()->MarkCol(U_J_EXT);
713   opj->GetCol2()->MarkCol(U_J_EXT);
714 
715   // Remove the filter from the filter chain.  If the filter is
716   // not last in the chain, also remove the SEP filter after it.
717   if (Next)                 // Next->Opc == OP_SEP
718     Next = Next->Next;
719 
720   if (!fprec)
721     opj->SetFilter(Next);
722   else
723     fprec->Next = Next;
724 
725   return FALSE;
726   } // end of FindJoinFilter
727 
728 /***********************************************************************/
729 /*  CheckHaving: check and process a filter of an HAVING clause.       */
730 /*  Check references to Columns and Functions in the filter.           */
731 /*  All these references can correspond to items existing in the       */
732 /*  SELECT list, else if it is a function, allocate a SELECT block     */
733 /*  to be added to the To_Sel list (non projected blocks).             */
734 /***********************************************************************/
735 bool FILTER::CheckHaving(PGLOBAL g, PSQL sqlp)
736   {
737   int  agg = AGG_ANY;
738   PXOB xp;
739 
740 //sqlp->SetOk(TRUE);  // Ok to look into outer queries for filters
741 
742   switch (Opc) {
743     case OP_SEP:
744     case OP_AND:
745     case OP_OR:
746     case OP_NOT:
747       return FALSE;
748     default:
749       if (CheckColumn(g, sqlp, xp, agg) < -1)
750         return TRUE;       // Unrecovable error
751 
752       break;
753     } // endswitch Opc
754 
755   sqlp->SetOk(TRUE);  // Ok to look into outer queries for filters
756 
757   for (int i = 0; i < 2; i++)
758     if (!(xp = Arg(i)->SetSelect(g, sqlp, TRUE)))
759       return TRUE;
760     else if (xp != Arg(i)) {
761       Arg(i) = xp;
762       Val(i) = Arg(i)->GetValue();
763       } // endif
764 
765   sqlp->SetOk(FALSE);
766   return FALSE;
767   } // end of CheckHaving
768 
769 /***********************************************************************/
770 /*  Used while building a table index. This function split the filter  */
771 /*  attached to the tdbp table into the local and not local part.      */
772 /*  The local filter is used to restrict the size of the index and the */
773 /*  not local part remains to be executed later. This has been added   */
774 /*  recently and not only to improve the performance but chiefly to    */
775 /*  avoid loosing rows when processing distinct joins.                 */
776 /*  Returns:                                                           */
777 /*    0: the whole filter is local (both arguments are)                */
778 /*    1: the whole filter is not local                                 */
779 /*    2: the filter was split in local (attached to fp[0]) and         */
780 /*                           not local (attached to fp[1]).            */
781 /***********************************************************************/
782 int FILTER::SplitFilter(PFIL *fp)
783   {
784   int i, rc[2];
785 
786   if (Opc == OP_AND) {
787     for (i = 0; i < 2; i++)
788       rc[i] = ((PFIL)Arg(i))->SplitFilter(fp);
789 
790     // Filter first argument should never be split because of the
791     // algorithm used to de-linearize the filter.
792     assert(rc[0] != 2);
793 
794     if (rc[0] != rc[1]) {
795       // Splitting to be done
796       if (rc[1] == 2) {
797         // 2nd argument already split, add 1st to the proper filter
798         assert(fp[*rc]);
799         Arg(1) = fp[*rc];
800         Val(1) = fp[*rc]->GetValue();
801         fp[*rc] = this;
802       } else for (i = 0; i < 2; i++) {
803         //  Split the filter arguments
804         assert(!fp[rc[i]]);
805         fp[rc[i]] = (PFIL)Arg(i);
806         } // endfor i
807 
808       *rc = 2;
809       } // endif rc
810 
811   } else
812     *rc = (CheckLocal(NULL)) ? 0 : 1;
813 
814   return *rc;
815   } // end of SplitFilter
816 
817 /***********************************************************************/
818 /*  This function is called when making a Kindex after the filter was  */
819 /*  split in local and nolocal part in the case of many to many joins. */
820 /*  Indeed the whole filter must be reconstructed to take care of next */
821 /*  same values when doing the explosive join. In addition, the link   */
822 /*  must be done respecting the way filters are de-linearized, no AND  */
823 /*  filter in the first argument of an AND filter, because this is     */
824 /*  expected to be true if SplitFilter is used again on this filter.   */
825 /***********************************************************************/
826 PFIL FILTER::LinkFilter(PGLOBAL g, PFIL fp2)
827   {
828   PFIL fp1, filp, filand = NULL;
829 
830   assert(fp2);           // Test must be made by caller
831 
832   // Find where the new AND filter must be attached
833   for (fp1 = this; fp1->Opc == OP_AND; fp1 = (PFIL)fp1->Arg(1))
834     filand = fp1;
835 
836   filp = new(g) FILTER(g, OP_AND);
837   filp->Arg(0) = fp1;
838   filp->Val(0) = fp1->GetValue();
839   filp->Test[0].B_T = TYPE_INT;
840   filp->Test[0].Conv = FALSE;
841   filp->Arg(1) = fp2;
842   filp->Val(1) = fp2->GetValue();
843   filp->Test[1].B_T = TYPE_INT;
844   filp->Test[1].Conv = FALSE;
845   filp->Value = AllocateValue(g, TYPE_INT);
846 
847   if (filand) {
848     // filp must be inserted here
849     filand->Arg(1) = filp;
850     filand->Val(1) = filp->GetValue();
851     filp = this;
852     } // endif filand
853 
854   return filp;
855   } // end of LinkFilter
856 
857 /***********************************************************************/
858 /*  Checks whether filter contains reference to a previous table that  */
859 /*  is not logically joined to the currently opened table, or whether */
860 /*  it is a Sub-Select filter.  In any case, local is set to FALSE.    */
861 /*  Note: This function is now applied to de-linearized filters.       */
862 /***********************************************************************/
863 bool FILTER::CheckLocal(PTDB tdbp)
864   {
865   bool local = TRUE;
866 
867   if (trace(1)) {
868     if (tdbp)
869       htrc("CheckLocal: filp=%p R%d\n", this, tdbp->GetTdb_No());
870     else
871       htrc("CheckLocal: filp=%p\n", this);
872     } // endif trace
873 
874   for (int i = 0; local && i < 2; i++)
875     local = Arg(i)->CheckLocal(tdbp);
876 
877   if (trace(1))
878     htrc("FCL: returning %d\n", local);
879 
880   return (local);
881   } // end of CheckLocal
882 
883 /***********************************************************************/
884 /*  This routine is used to split the filter attached to the tdbp      */
885 /*  table into the local and not local part where "local" means that   */
886 /*  it applies "locally" to the FILEID special column with crit = 2    */
887 /*  and to the SERVID and/or TABID special columns with crit = 3.      */
888 /*  Returns:                                                           */
889 /*    0: the whole filter is local (both arguments are)                */
890 /*    1: the whole filter is not local                                 */
891 /*    2: the filter was split in local (attached to fp[0]) and         */
892 /*                           not local (attached to fp[1]).            */
893 /*  Note: "Locally" means that the "local" filter can be evaluated     */
894 /*  before opening the table. This implies that the special column be  */
895 /*  compared only with constants and that this filter not to be or'ed  */
896 /*  with a non "local" filter.                                         */
897 /***********************************************************************/
898 int FILTER::SplitFilter(PFIL *fp, PTDB tp, int crit)
899   {
900   int i, rc[2];
901 
902   if (Opc == OP_AND) {
903     for (i = 0; i < 2; i++)
904       rc[i] = ((PFIL)Arg(i))->SplitFilter(fp, tp, crit);
905 
906     // Filter first argument should never be split because of the
907     // algorithm used to de-linearize the filter.
908     assert(rc[0] != 2);
909 
910     if (rc[0] != rc[1]) {
911       // Splitting to be done
912       if (rc[1] == 2) {
913         // 2nd argument already split, add 1st to the proper filter
914         assert(fp[*rc]);
915         Arg(1) = fp[*rc];
916         Val(1) = fp[*rc]->GetValue();
917         fp[*rc] = this;
918       } else for (i = 0; i < 2; i++) {
919         //  Split the filter arguments
920         assert(!fp[rc[i]]);
921         fp[rc[i]] = (PFIL)Arg(i);
922         } // endfor i
923 
924       *rc = 2;
925       } // endif rc
926 
927   } else
928     *rc = (CheckSpcCol(tp, crit) == 1) ? 0 : 1;
929 
930   return *rc;
931   } // end of SplitFilter
932 
933 /***********************************************************************/
934 /*  Checks whether filter contains only references to FILEID, SERVID,  */
935 /*  or TABID with constants or pseudo constants.                       */
936 /***********************************************************************/
937 int FILTER::CheckSpcCol(PTDB tdbp, int n)
938   {
939   int n1 = Arg(0)->CheckSpcCol(tdbp, n);
940   int n2 = Arg(1)->CheckSpcCol(tdbp, n);
941 
942   return max(n1, n2);
943   } // end of CheckSpcCol
944 #endif // 0
945 
946 /***********************************************************************/
947 /*  Reset the filter arguments to non evaluated yet.                   */
948 /***********************************************************************/
Reset(void)949 void FILTER::Reset(void)
950   {
951   for (int i = 0; i < 2; i++)
952     Arg(i)->Reset();
953 
954   } // end of Reset
955 
956 /***********************************************************************/
957 /*  Init: called when reinitializing a query (Correlated subqueries)   */
958 /***********************************************************************/
Init(PGLOBAL g)959 bool FILTER::Init(PGLOBAL g)
960   {
961   for (int i = 0; i < 2; i++)
962     Arg(i)->Init(g);
963 
964   return FALSE;
965   } // end of Init
966 
967 /***********************************************************************/
968 /*  Convert:  does all filter setting and conversions.                 */
969 /*   (having = TRUE for Having Clauses, FALSE for Where Clauses)       */
970 /*      Note: hierarchy of types is implied by the ConvertType         */
971 /*      function, currently FLOAT, int, STRING and TOKEN.              */
972 /*  Returns FALSE if successful or TRUE in case of error.              */
973 /*  Note on result type for filters:                                   */
974 /*    Currently the result type is of TYPE_INT (should be TYPE_BOOL).  */
975 /*    This avoids to introduce a new type and perhaps will permit      */
976 /*    conversions. However the boolean operators will result in a      */
977 /*    boolean int result, meaning that result shall be only 0 or 1  .  */
978 /***********************************************************************/
Convert(PGLOBAL g,bool having)979 bool FILTER::Convert(PGLOBAL g, bool having)
980   {
981   int i, comtype = TYPE_ERROR;
982 
983   if (trace(1))
984     htrc("converting(?) %s %p opc=%d\n",
985           (having) ? "having" : "filter", this, Opc);
986 
987   for (i = 0; i < 2; i++) {
988     switch (GetArgType(i)) {
989       case TYPE_COLBLK:
990         if (((PCOL)Arg(i))->InitValue(g))
991           return TRUE;
992 
993         break;
994       case TYPE_ARRAY:
995         if ((Opc != OP_IN && !Opm) || i == 0) {
996           strcpy(g->Message, MSG(BAD_ARRAY_OPER));
997           return TRUE;
998           } // endif
999 
1000         if (((PARRAY)Arg(i))->Sort(g))  // Sort the array
1001           return TRUE;                  // Error
1002 
1003         break;
1004       case TYPE_VOID:
1005         if (i == 1) {
1006           Val(0) = Arg(0)->GetValue();
1007           goto TEST;             // Filter has only one argument
1008           } // endif i
1009 
1010         strcpy(g->Message, MSG(VOID_FIRST_ARG));
1011         return TRUE;
1012       } // endswitch
1013 
1014     if (trace(1))
1015       htrc("Filter(%d): Arg type=%d\n", i, GetArgType(i));
1016 
1017     // Set default values
1018     Test[i].B_T = Arg(i)->GetResultType();
1019     Test[i].Conv = FALSE;
1020 
1021     // Special case of the LIKE operator.
1022     if (Opc == OP_LIKE) {
1023       if (!IsTypeChar((int)Test[i].B_T)) {
1024         sprintf(g->Message, MSG(BAD_TYPE_LIKE), i, Test[i].B_T);
1025         return TRUE;
1026         } // endif
1027 
1028       comtype = TYPE_STRING;
1029     } else {
1030       // Set the common type for both (eventually converted) arguments
1031       int argtyp = Test[i].B_T;
1032 
1033       if (GetArgType(i) == TYPE_CONST && argtyp == TYPE_INT) {
1034         // If possible, downcast the type to smaller types to avoid
1035         // convertion as much as possible.
1036         int n = Arg(i)->GetValue()->GetIntValue();
1037 
1038         if (n >= INT_MIN8 && n <= INT_MAX8)
1039           argtyp = TYPE_TINY;
1040         else if (n >= INT_MIN16 && n <= INT_MAX16)
1041           argtyp = TYPE_SHORT;
1042 
1043       } else if (GetArgType(i) == TYPE_ARRAY) {
1044         // If possible, downcast int arrays target type to TYPE_SHORT
1045         // to take  care of filters written like shortcol in (34,35,36).
1046         if (((PARRAY)Arg(i))->CanBeShort())
1047           argtyp = TYPE_SHORT;
1048 
1049       } // endif TYPE_CONST
1050 
1051       comtype = ConvertType(comtype, argtyp, CNV_ANY);
1052     } // endif Opc
1053 
1054     if (comtype == TYPE_ERROR) {
1055       strcpy(g->Message, MSG(ILL_FILTER_CONV));
1056       return TRUE;
1057       } // endif
1058 
1059     if (trace(1))
1060       htrc(" comtype=%d, B_T(%d)=%d Val(%d)=%p\n",
1061              comtype, i, Test[i].B_T, i, Val(i));
1062 
1063     } // endfor i
1064 
1065   // Set or allocate the filter argument values and buffers
1066   for (i = 0; i < 2; i++) {
1067     if (trace(1))
1068       htrc(" conv type %d ? i=%d B_T=%d comtype=%d\n",
1069             GetArgType(i), i, Test[i].B_T, comtype);
1070 
1071     if (Test[i].B_T == comtype) {
1072       // No conversion, set Value to argument Value
1073       Val(i) = Arg(i)->GetValue();
1074 #if defined(_DEBUG)
1075       assert (Val(i) && Val(i)->GetType() == Test[i].B_T);
1076 #endif
1077     } else {
1078       //  Conversion between filter arguments to be done.
1079       //  Note that the argument must be converted, not only the
1080       //  buffer and buffer type, so GetArgType() returns the new type.
1081       switch (GetArgType(i)) {
1082         case TYPE_CONST:
1083           if (comtype == TYPE_DATE && Test[i].B_T == TYPE_STRING) {
1084             // Convert according to the format of the other argument
1085             Val(i) = AllocateValue(g, comtype, Arg(i)->GetLength());
1086 
1087             if (((DTVAL*)Val(i))->SetFormat(g, Val(1-i)))
1088               return TRUE;
1089 
1090             Val(i)->SetValue_psz(Arg(i)->GetValue()->GetCharValue());
1091           } else {
1092             ((PCONST)Arg(i))->Convert(g, comtype);
1093             Val(i) = Arg(i)->GetValue();
1094           } // endif comtype
1095 
1096           break;
1097         case TYPE_ARRAY:
1098           // Conversion PSZ or int array to int or double FLOAT.
1099           if (((PARRAY)Arg(i))->Convert(g, comtype, Val(i-1)) == TYPE_ERROR)
1100             return TRUE;
1101 
1102           break;
1103         case TYPE_FILTER:
1104           strcpy(g->Message, MSG(UNMATCH_FIL_ARG));
1105           return TRUE;
1106         default:
1107           // Conversion from Column, Select/Func, Expr, Scalfnc...
1108           // The argument requires conversion during Eval
1109           // A separate Value block must be allocated.
1110           // Note: the test on comtype is to prevent unnecessary
1111           // domain initialization and get the correct length in
1112           // case of Token -> numeric conversion.
1113           Val(i) = AllocateValue(g, comtype, (comtype == TYPE_STRING)
1114                  ? Arg(i)->GetLengthEx() : Arg(i)->GetLength());
1115 
1116           if (comtype == TYPE_DATE && Test[i].B_T == TYPE_STRING)
1117             // Convert according to the format of the other argument
1118             if (((DTVAL*)Val(i))->SetFormat(g, Val(1 - i)))
1119               return TRUE;
1120 
1121           Test[i].Conv = TRUE;
1122           break;
1123         } // endswitch GetType
1124 
1125       Test[i].B_T = comtype;
1126     } // endif comtype
1127 
1128     } // endfor i
1129 
1130   //  Last check to be sure all is correct.
1131   if (Test[0].B_T != Test[1].B_T) {
1132     sprintf(g->Message, MSG(BAD_FILTER_CONV), Test[0].B_T, Test[1].B_T);
1133     return TRUE;
1134 //} else if (Test[0].B_T == TYPE_LIST &&
1135 //          ((LSTVAL*)Val(0))->GetN() != ((LSTVAL*)Val(1))->GetN()) {
1136 //  sprintf(g->Message, MSG(ROW_ARGNB_ERR),
1137 //          ((LSTVAL*)Val(0))->GetN(), ((LSTVAL*)Val(1))->GetN());
1138 //  return TRUE;
1139   } // endif's B_T
1140 
1141 
1142  TEST: // Test for possible Eval optimization
1143 
1144   if (trace(1))
1145     htrc("Filp %p op=%d argtypes=(%d,%d)\n",
1146           this, Opc, GetArgType(0), GetArgType(1));
1147 
1148   // Check whether we have a "simple" filter and in that case
1149   // change its class so an optimized Eval function will be used
1150   if (!Test[0].Conv && !Test[1].Conv) {
1151     if (Opm) switch (Opc) {
1152       case OP_EQ:
1153       case OP_NE:
1154       case OP_GT:
1155       case OP_GE:
1156       case OP_LT:
1157       case OP_LE:
1158         if (GetArgType(1) != TYPE_ARRAY)
1159           break;      // On subquery, do standard processing
1160 
1161         // Change the FILTER class to FILTERIN
1162         new(this) FILTERIN;
1163         break;
1164       default:
1165         break;
1166       } // endswitch Opc
1167 
1168     else switch (Opc) {
1169 #if 0
1170       case OP_EQ:  new(this) FILTEREQ;  break;
1171       case OP_NE:  new(this) FILTERNE;  break;
1172       case OP_GT:  new(this) FILTERGT;  break;
1173       case OP_GE:  new(this) FILTERGE;  break;
1174       case OP_LT:  new(this) FILTERLT;  break;
1175       case OP_LE:  new(this) FILTERLE;  break;
1176 #endif // 0
1177       case OP_EQ:
1178       case OP_NE:
1179       case OP_GT:
1180       case OP_GE:
1181       case OP_LT:
1182       case OP_LE:  new(this) FILTERCMP(g); break;
1183       case OP_AND: new(this) FILTERAND; break;
1184       case OP_OR:  new(this) FILTEROR;  break;
1185       case OP_NOT: new(this) FILTERNOT; break;
1186       case OP_EXIST:
1187         if (GetArgType(1) == TYPE_VOID) {
1188           // For EXISTS it is the first argument that should be null
1189           Arg(1) = Arg(0);
1190           Arg(0) = pXVOID;
1191           } // endif void
1192 
1193         // fall through
1194       case OP_IN:
1195         // For IN operator do optimize if operand is an array
1196         if (GetArgType(1) != TYPE_ARRAY)
1197           break;      // IN on subquery, do standard processing
1198 
1199         // Change the FILTER class to FILTERIN
1200         new(this) FILTERIN;
1201         break;
1202       default:
1203         break;
1204       } // endswitch Opc
1205 
1206     } // endif Conv
1207 
1208   // The result value (should be TYPE_BOOL ???)
1209   Value = AllocateValue(g, TYPE_INT);
1210   return FALSE;
1211   } // end of Convert
1212 
1213 /***********************************************************************/
1214 /*  Eval: Compute filter result value.                                 */
1215 /*  New algorithm: evaluation is now done from the root for each group */
1216 /*  so Eval is now a recursive process for FILTER operands.            */
1217 /***********************************************************************/
Eval(PGLOBAL g)1218 bool FILTER::Eval(PGLOBAL g)
1219   {
1220   int     i; // n = 0;
1221 //PSUBQ   subp = NULL;
1222   PARRAY  ap = NULL;
1223 
1224   (void) PlgGetUser(g);
1225 
1226   if (Opc <= OP_XX)
1227   {
1228     for (i = 0; i < 2; i++)
1229     {
1230       // Evaluate the object and eventually convert it.
1231       if (Arg(i)->Eval(g))
1232         return TRUE;
1233       else if (Test[i].Conv)
1234         Val(i)->SetValue_pval(Arg(i)->GetValue());
1235     }
1236   }
1237 
1238   if (trace(1))
1239     htrc(" Filter: op=%d type=%d %d B_T=%d %d val=%p %p\n",
1240           Opc, GetArgType(0), GetArgType(1), Test[0].B_T, Test[1].B_T,
1241           Val(0), Val(1));
1242 
1243   // Main switch on filtering according to operator type.
1244   switch (Opc) {
1245     case OP_EQ:
1246     case OP_NE:
1247     case OP_GT:
1248     case OP_GE:
1249     case OP_LT:
1250     case OP_LE:
1251       if (!Opm) {
1252         //  Comparison boolean operators.
1253 #if defined(_DEBUG)
1254         if (Val(0)->GetType() != Val(1)->GetType())
1255           goto FilterError;
1256 #endif
1257         // Compare the two arguments
1258         // New algorithm to take care of TYPE_LIST
1259         Bt = OpBmp(g, Opc);
1260         Value->SetValue_bool(!(Val(0)->TestValue(Val(1)) & Bt));
1261         break;
1262         } // endif Opm
1263 
1264       // For modified operators, pass thru
1265       /* fall through */
1266     case OP_IN:
1267     case OP_EXIST:
1268       // For IN operations, special processing is done here
1269       switch (GetArgType(1)) {
1270         case TYPE_ARRAY:
1271           ap = (PARRAY)Arg(1);
1272           break;
1273         default:
1274           strcpy(g->Message, MSG(IN_WITHOUT_SUB));
1275           goto FilterError;
1276         } // endswitch Type
1277 
1278       if (trace(1)) {
1279         htrc(" IN filtering: ap=%p\n", ap);
1280 
1281         if (ap)
1282           htrc(" Array: type=%d size=%d other_type=%d\n",
1283                 ap->GetType(), ap->GetSize(), Test[0].B_T);
1284 
1285         } // endif trace
1286 
1287       /*****************************************************************/
1288       /*  Implementation note: The Find function is now able to do a   */
1289       /*  conversion but limited to SHORT, int, and FLOAT arrays.     */
1290       /*****************************************************************/
1291 //    Value->SetValue_bool(ap->Find(g, Val(0)));
1292 
1293       if (ap)
1294         Value->SetValue_bool(ap->FilTest(g, Val(0), Opc, Opm));
1295 
1296       break;
1297 
1298     case OP_LIKE:
1299 #if defined(_DEBUG)
1300       if (!IsTypeChar((int)Test[0].B_T) || !IsTypeChar((int)Test[1].B_T))
1301         goto FilterError;
1302 #endif
1303       if (Arg(0)->Eval(g))
1304         return TRUE;
1305 
1306       Value->SetValue_bool(PlugEvalLike(g, Val(0)->GetCharValue(),
1307                                            Val(1)->GetCharValue(),
1308                                            Val(0)->IsCi()));
1309       break;
1310 
1311     case OP_AND:
1312 #if defined(_DEBUG)
1313       if (Test[0].B_T != TYPE_INT || Test[1].B_T != TYPE_INT)
1314         goto FilterError;
1315 #endif
1316 
1317       if (Arg(0)->Eval(g))
1318         return TRUE;
1319 
1320       Value->SetValue(Val(0)->GetIntValue());
1321 
1322       if (!Value->GetIntValue())
1323         return FALSE;   // No need to evaluate 2nd argument
1324 
1325       if (Arg(1)->Eval(g))
1326         return TRUE;
1327 
1328       Value->SetValue(Val(1)->GetIntValue());
1329       break;
1330 
1331     case OP_OR:
1332 #if defined(_DEBUG)
1333       if (Test[0].B_T != TYPE_INT || Test[1].B_T != TYPE_INT)
1334         goto FilterError;
1335 #endif
1336 
1337       if (Arg(0)->Eval(g))
1338         return TRUE;
1339 
1340       Value->SetValue(Val(0)->GetIntValue());
1341 
1342       if (Value->GetIntValue())
1343         return FALSE;   // No need to evaluate 2nd argument
1344 
1345       if (Arg(1)->Eval(g))
1346         return TRUE;
1347 
1348       Value->SetValue(Val(1)->GetIntValue());
1349       break;
1350 
1351     case OP_NOT:
1352 #if defined(_DEBUG)
1353       if (Test[0].B_T != TYPE_INT)      // Should be type bool ???
1354         goto FilterError;
1355 #endif
1356 
1357       if (Arg(0)->Eval(g))
1358         return TRUE;
1359 
1360       Value->SetValue_bool(!Val(0)->GetIntValue());
1361       break;
1362 
1363     case OP_SEP:   // No more used while evaluating
1364     default:
1365       goto FilterError;
1366     } // endswitch Opc
1367 
1368   if (trace(1))
1369     htrc("Eval: filter %p Opc=%d result=%d\n",
1370                 this, Opc, Value->GetIntValue());
1371 
1372   return FALSE;
1373 
1374  FilterError:
1375   sprintf(g->Message, MSG(BAD_FILTER),
1376           Opc, Test[0].B_T, Test[1].B_T, GetArgType(0), GetArgType(1));
1377   return TRUE;
1378   } // end of Eval
1379 
1380 #if 0
1381 /***********************************************************************/
1382 /*  Called by PlugCopyDB to make a copy of a (linearized) filter chain.*/
1383 /***********************************************************************/
1384 PFIL FILTER::Copy(PTABS t)
1385   {
1386   int  i;
1387   PFIL fil1, fil2, newfilchain = NULL, fprec = NULL;
1388 
1389   for (fil1 = this; fil1; fil1 = fil1->Next) {
1390     fil2 = new(t->G) FILTER(fil1);
1391 
1392     if (!fprec)
1393       newfilchain = fil2;
1394     else
1395       fprec->Next = fil2;
1396 
1397     NewPointer(t, fil1, fil2);
1398 
1399     for (i = 0; i < 2; i++)
1400       if (fil1->GetArgType(i) == TYPE_COLBLK ||
1401           fil1->GetArgType(i) == TYPE_FILTER)
1402         AddPointer(t, &fil2->Arg(i));
1403 
1404     fprec = fil2;
1405     } /* endfor fil1 */
1406 
1407   return newfilchain;
1408   } // end of Copy
1409 #endif // 0
1410 
1411 /*********************************************************************/
1412 /*  Make file output of FILTER contents.                             */
1413 /*********************************************************************/
Printf(PGLOBAL g,FILE * f,uint n)1414 void FILTER::Printf(PGLOBAL g, FILE *f, uint n)
1415   {
1416   char m[64];
1417 
1418   memset(m, ' ', n);                    // Make margin string
1419   m[n] = '\0';
1420 
1421   bool lin = (Next != NULL);            // lin == TRUE if linearized
1422 
1423   for (PFIL fp = this; fp; fp = fp->Next) {
1424     fprintf(f, "%sFILTER: at %p opc=%d lin=%d result=%d\n",
1425             m, fp, fp->Opc, lin,
1426             (Value) ? Value->GetIntValue() : 0);
1427 
1428     for (int i = 0; i < 2; i++) {
1429       fprintf(f, "%s Arg(%d) type=%d value=%p B_T=%d val=%p\n",
1430               m, i, fp->GetArgType(i), fp->Arg(i),
1431                     fp->Test[i].B_T, fp->Val(i));
1432 
1433       if (lin && fp->GetArgType(i) == TYPE_FILTER)
1434         fprintf(f, "%s  Filter at %p\n", m, fp->Arg(i));
1435       else
1436         fp->Arg(i)->Printf(g, f, n + 2);
1437 
1438       } // endfor i
1439 
1440     } // endfor fp
1441 
1442   } // end of Printf
1443 
1444 /***********************************************************************/
1445 /*  Make string output of TABLE contents (z should be checked).        */
1446 /***********************************************************************/
Prints(PGLOBAL g,char * ps,uint z)1447 void FILTER::Prints(PGLOBAL g, char *ps, uint z)
1448   {
1449   #define FLEN 100
1450 
1451   typedef struct _bc {
1452     struct _bc *Next;
1453     char   Cold[FLEN+1];
1454     } BC, *PBC;
1455 
1456   char *p;
1457   int   n;
1458   PFIL  fp;
1459   PBC   bxp, bcp = NULL;
1460 
1461   *ps = '\0';
1462 
1463   for (fp = this; fp && z > 0; fp = fp->Next) {
1464     if (fp->Opc < OP_CNC || fp->Opc == OP_IN || fp->Opc == OP_NULL
1465                          || fp->Opc == OP_LIKE || fp->Opc == OP_EXIST) {
1466       if (!(bxp = new BC)) {
1467         strncat(ps, "Filter(s)", z);
1468         return;
1469         } /* endif */
1470 
1471       bxp->Next = bcp;
1472       bcp = bxp;
1473       p = bcp->Cold;
1474       n = FLEN;
1475       fp->Arg(0)->Prints(g, p, n);
1476       n = FLEN - strlen(p);
1477 
1478       switch (fp->Opc) {
1479         case OP_EQ:
1480           strncat(bcp->Cold, "=", n);
1481           break;
1482         case OP_NE:
1483           strncat(bcp->Cold, "!=", n);
1484           break;
1485         case OP_GT:
1486           strncat(bcp->Cold, ">", n);
1487           break;
1488         case OP_GE:
1489           strncat(bcp->Cold, ">=", n);
1490           break;
1491         case OP_LT:
1492           strncat(bcp->Cold, "<", n);
1493           break;
1494         case OP_LE:
1495           strncat(bcp->Cold, "<=", n);
1496           break;
1497         case OP_IN:
1498           strncat(bcp->Cold, " in ", n);
1499           break;
1500         case OP_NULL:
1501           strncat(bcp->Cold, " is null", n);
1502           break;
1503         case OP_LIKE:
1504           strncat(bcp->Cold, " like ", n);
1505           break;
1506         case OP_EXIST:
1507           strncat(bcp->Cold, " exists ", n);
1508           break;
1509         case OP_AND:
1510           strncat(bcp->Cold, " and ", n);
1511           break;
1512         case OP_OR:
1513           strncat(bcp->Cold, " or ", n);
1514           break;
1515         default:
1516           strncat(bcp->Cold, "?", n);
1517         } // endswitch Opc
1518 
1519       n = FLEN - strlen(p);
1520       p += strlen(p);
1521       fp->Arg(1)->Prints(g, p, n);
1522     } else
1523       if (!bcp) {
1524         strncat(ps, "???", z);
1525         z -= 3;
1526       } else
1527         switch (fp->Opc) {
1528           case OP_SEP:                    // Filter list separator
1529             strncat(ps, bcp->Cold, z);
1530             z -= strlen(bcp->Cold);
1531             strncat(ps, ";", z--);
1532             bxp = bcp->Next;
1533             delete bcp;
1534             bcp = bxp;
1535             break;
1536           case OP_NOT:                    // Filter NOT operator
1537             for (n = MY_MIN((int)strlen(bcp->Cold), FLEN-3); n >= 0; n--)
1538               bcp->Cold[n+2] = bcp->Cold[n];
1539             bcp->Cold[0] = '^';
1540             bcp->Cold[1] = '(';
1541             strcat(bcp->Cold, ")");
1542             break;
1543           default:
1544             for (n = MY_MIN((int)strlen(bcp->Cold), FLEN-4); n >= 0; n--)
1545               bcp->Cold[n+3] = bcp->Cold[n];
1546             bcp->Cold[0] = ')';
1547             switch (fp->Opc) {
1548               case OP_AND: bcp->Cold[1] = '&'; break;
1549               case OP_OR:  bcp->Cold[1] = '|'; break;
1550               default: bcp->Cold[1] = '?';
1551               } // endswitch
1552             bcp->Cold[2] = '(';
1553             strcat(bcp->Cold, ")");
1554             bxp = bcp->Next;
1555             for (n = MY_MIN((int)strlen(bxp->Cold), FLEN-1); n >= 0; n--)
1556               bxp->Cold[n+1] = bxp->Cold[n];
1557             bxp->Cold[0] = '(';
1558             strncat(bxp->Cold, bcp->Cold, FLEN-strlen(bxp->Cold));
1559             delete bcp;
1560             bcp = bxp;
1561           } // endswitch
1562 
1563     } // endfor fp
1564 
1565   n = 0;
1566 
1567   if (!bcp)
1568     strncat(ps, "Null-Filter", z);
1569   else do {
1570     if (z > 0) {
1571       if (n++ > 0) {
1572         strncat(ps, "*?*", z);
1573         z = MY_MAX(0, (int)z-3);
1574         } // endif
1575       strncat(ps, bcp->Cold, z);
1576       z -= strlen(bcp->Cold);
1577       } // endif
1578 
1579     bxp = bcp->Next;
1580     delete bcp;
1581     bcp = bxp;
1582     } while (bcp); // enddo
1583 
1584   } // end of Prints
1585 
1586 
1587 /* -------------------- Derived Classes Functions -------------------- */
1588 
1589 /***********************************************************************/
1590 /*  FILTERCMP constructor.                                             */
1591 /***********************************************************************/
FILTERCMP(PGLOBAL g)1592 FILTERCMP::FILTERCMP(PGLOBAL g)
1593   {
1594   Bt = OpBmp(g, Opc);
1595   } // end of FILTERCMP constructor
1596 
1597 /***********************************************************************/
1598 /*  Eval: Compute result value for comparison operators.               */
1599 /***********************************************************************/
Eval(PGLOBAL g)1600 bool FILTERCMP::Eval(PGLOBAL g)
1601   {
1602   if (Arg(0)->Eval(g) || Arg(1)->Eval(g))
1603     return TRUE;
1604 
1605   Value->SetValue_bool(!(Val(0)->TestValue(Val(1)) & Bt));
1606   return FALSE;
1607   } // end of Eval
1608 
1609 /***********************************************************************/
1610 /*  Eval: Compute result value for AND filters.                        */
1611 /***********************************************************************/
Eval(PGLOBAL g)1612 bool FILTERAND::Eval(PGLOBAL g)
1613   {
1614   if (Arg(0)->Eval(g))
1615     return TRUE;
1616 
1617   Value->SetValue(Val(0)->GetIntValue());
1618 
1619   if (!Value->GetIntValue())
1620     return FALSE;   // No need to evaluate 2nd argument
1621 
1622   if (Arg(1)->Eval(g))
1623     return TRUE;
1624 
1625   Value->SetValue(Val(1)->GetIntValue());
1626   return FALSE;
1627   } // end of Eval
1628 
1629 /***********************************************************************/
1630 /*  Eval: Compute result value for OR filters.                         */
1631 /***********************************************************************/
Eval(PGLOBAL g)1632 bool FILTEROR::Eval(PGLOBAL g)
1633   {
1634   if (Arg(0)->Eval(g))
1635     return TRUE;
1636 
1637   Value->SetValue(Val(0)->GetIntValue());
1638 
1639   if (Value->GetIntValue())
1640     return FALSE;   // No need to evaluate 2nd argument
1641 
1642   if (Arg(1)->Eval(g))
1643     return TRUE;
1644 
1645   Value->SetValue(Val(1)->GetIntValue());
1646   return FALSE;
1647   } // end of Eval
1648 
1649 /***********************************************************************/
1650 /*  Eval: Compute result value for NOT filters.                        */
1651 /***********************************************************************/
Eval(PGLOBAL g)1652 bool FILTERNOT::Eval(PGLOBAL g)
1653   {
1654   if (Arg(0)->Eval(g))
1655     return TRUE;
1656 
1657   Value->SetValue_bool(!Val(0)->GetIntValue());
1658   return FALSE;
1659   } // end of Eval
1660 
1661 /***********************************************************************/
1662 /*  Eval: Compute result value for IN filters.                         */
1663 /***********************************************************************/
Eval(PGLOBAL g)1664 bool FILTERIN::Eval(PGLOBAL g)
1665   {
1666   if (Arg(0)->Eval(g))
1667     return TRUE;
1668 
1669   Value->SetValue_bool(((PARRAY)Arg(1))->FilTest(g, Val(0), Opc, Opm));
1670   return FALSE;
1671   } // end of Eval
1672 
1673 /***********************************************************************/
1674 /*  FILTERTRUE does nothing and returns TRUE.                          */
1675 /***********************************************************************/
Reset(void)1676 void FILTERTRUE::Reset(void)
1677   {
1678   } // end of Reset
1679 
Eval(PGLOBAL)1680 bool FILTERTRUE::Eval(PGLOBAL)
1681   {
1682   return FALSE;
1683   } // end of Eval
1684 
1685 /* ------------------------- Friend Functions ------------------------ */
1686 
1687 #if 0
1688 /***********************************************************************/
1689 /*  Prepare: prepare a filter for execution. This implies two things:  */
1690 /*  1) de-linearize the filter to be able to evaluate it recursively.  */
1691 /*     This permit to conditionally evaluate only the first argument   */
1692 /*     of OP_OR and OP_AND filters without having to pass by an        */
1693 /*     intermediate Apply function (as this has a performance cost).   */
1694 /*  2) do all the necessary conversion for all filter block arguments. */
1695 /***********************************************************************/
1696 PFIL PrepareFilter(PGLOBAL g, PFIL fp, bool having)
1697   {
1698   PFIL filp = NULL;
1699 
1700   if (trace(1))
1701     htrc("PrepareFilter: fp=%p having=%d\n", fp, having);
1702 
1703   while (fp) {
1704     if (fp->Opc == OP_SEP)
1705       // If separator is not last transform it into an AND filter
1706       if (fp->Next) {
1707         filp = PrepareFilter(g, fp->Next, having);
1708         fp->Arg(1) = filp;
1709         fp->Opc = OP_AND;
1710         fp->Next = NULL;     // This will end the loop
1711       } else
1712         break;  // Remove eventual ending separator(s)
1713 
1714 //  if (fp->Convert(g, having))
1715 //			throw (int)TYPE_FILTER;
1716 
1717     filp = fp;
1718     fp = fp->Next;
1719     filp->Next = NULL;
1720     } // endwhile
1721 
1722   if (trace(1))
1723     htrc(" returning filp=%p\n", filp);
1724 
1725   return filp;
1726   } // end of PrepareFilter
1727 #endif // 0
1728 
1729 /***********************************************************************/
1730 /*  ApplyFilter: Apply filtering for a table (where or having clause). */
1731 /*  New algorithm: evaluate from the root a de-linearized filter so    */
1732 /*  AND/OR clauses can be optimized throughout the whole tree.         */
1733 /***********************************************************************/
ApplyFilter(PGLOBAL g,PFIL filp)1734 DllExport bool ApplyFilter(PGLOBAL g, PFIL filp)
1735   {
1736   if (!filp)
1737     return TRUE;
1738 
1739   // Must be done for null tables
1740   filp->Reset();
1741 
1742 //if (tdbp && tdbp->IsNull())
1743 //  return TRUE;
1744 
1745   if (filp->Eval(g))
1746 		throw (int)TYPE_FILTER;
1747 
1748   if (trace(2))
1749     htrc("PlugFilter filp=%p result=%d\n",
1750                      filp, filp->GetResult());
1751 
1752   return filp->GetResult();
1753   } // end of ApplyFilter
1754