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(__WIN__)
17//#include <windows.h>
18#else // !__WIN__
19#include <string.h>
20#include <sys/types.h>
21#include <sys/stat.h>
22#endif // !__WIN__
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/***********************************************************************/
42void PlugConvertConstant(PGLOBAL, void* &, short&);
43//void *PlugCopyDB(PTABS, void*, INT);
44void NewPointer(PTABS, void*, void*);
45void AddPointer(PTABS, void*);
46
47static 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/***********************************************************************/
60bool PlugEvalLike(PGLOBAL, LPCSTR, LPCSTR, bool);
61//bool ReadSubQuery(PGLOBAL, PSUBQ);
62//PSUBQ OpenSubQuery(PGLOBAL, PSQL);
63//void PlugCloseDB(PGLOBAL, PSQL);
64BYTE OpBmp(PGLOBAL g, OPVAL opc);
65PARRAY 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/***********************************************************************/
72BYTE 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/***********************************************************************/
96PFIL 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
109PFIL 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/***********************************************************************/
175FILTER::FILTER(PGLOBAL g, POPER pop, PPARM *tp)
176 {
177 Constr(g, pop->Val, pop->Mod, tp);
178 } // end of FILTER constructor
179
180FILTER::FILTER(PGLOBAL g, OPVAL opc, PPARM *tp)
181 {
182 Constr(g, opc, 0, tp);
183 } // end of FILTER constructor
184
185void 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/***********************************************************************/
213FILTER::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/***********************************************************************/
237PFIL 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/***********************************************************************/
297PFIL 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/***********************************************************************/
324PFIL 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/***********************************************************************/
350int 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/***********************************************************************/
400int 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/***********************************************************************/
413PXOB 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/***********************************************************************/
440PFIL 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/***********************************************************************/
537bool 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/***********************************************************************/
735bool 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/***********************************************************************/
782int 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/***********************************************************************/
826PFIL 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 openned 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/***********************************************************************/
863bool 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/***********************************************************************/
898int 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/***********************************************************************/
937int 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/***********************************************************************/
949void 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/***********************************************************************/
959bool 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/***********************************************************************/
979bool 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/***********************************************************************/
1218bool FILTER::Eval(PGLOBAL g)
1219 {
1220 int i; // n = 0;
1221//PSUBQ subp = NULL;
1222 PARRAY ap = NULL;
1223 PDBUSER dup = PlgGetUser(g);
1224
1225 if (Opc <= OP_XX)
1226 for (i = 0; i < 2; i++)
1227 // Evaluate the object and eventually convert it.
1228 if (Arg(i)->Eval(g))
1229 return TRUE;
1230 else if (Test[i].Conv)
1231 Val(i)->SetValue_pval(Arg(i)->GetValue());
1232
1233 if (trace(1))
1234 htrc(" Filter: op=%d type=%d %d B_T=%d %d val=%p %p\n",
1235 Opc, GetArgType(0), GetArgType(1), Test[0].B_T, Test[1].B_T,
1236 Val(0), Val(1));
1237
1238 // Main switch on filtering according to operator type.
1239 switch (Opc) {
1240 case OP_EQ:
1241 case OP_NE:
1242 case OP_GT:
1243 case OP_GE:
1244 case OP_LT:
1245 case OP_LE:
1246 if (!Opm) {
1247 // Comparison boolean operators.
1248#if defined(_DEBUG)
1249 if (Val(0)->GetType() != Val(1)->GetType())
1250 goto FilterError;
1251#endif
1252 // Compare the two arguments
1253 // New algorithm to take care of TYPE_LIST
1254 Bt = OpBmp(g, Opc);
1255 Value->SetValue_bool(!(Val(0)->TestValue(Val(1)) & Bt));
1256 break;
1257 } // endif Opm
1258
1259 // For modified operators, pass thru
1260 /* fall through */
1261 case OP_IN:
1262 case OP_EXIST:
1263 // For IN operations, special processing is done here
1264 switch (GetArgType(1)) {
1265 case TYPE_ARRAY:
1266 ap = (PARRAY)Arg(1);
1267 break;
1268 default:
1269 strcpy(g->Message, MSG(IN_WITHOUT_SUB));
1270 goto FilterError;
1271 } // endswitch Type
1272
1273 if (trace(1)) {
1274 htrc(" IN filtering: ap=%p\n", ap);
1275
1276 if (ap)
1277 htrc(" Array: type=%d size=%d other_type=%d\n",
1278 ap->GetType(), ap->GetSize(), Test[0].B_T);
1279
1280 } // endif trace
1281
1282 /*****************************************************************/
1283 /* Implementation note: The Find function is now able to do a */
1284 /* conversion but limited to SHORT, int, and FLOAT arrays. */
1285 /*****************************************************************/
1286// Value->SetValue_bool(ap->Find(g, Val(0)));
1287
1288 if (ap)
1289 Value->SetValue_bool(ap->FilTest(g, Val(0), Opc, Opm));
1290
1291 break;
1292
1293 case OP_LIKE:
1294#if defined(_DEBUG)
1295 if (!IsTypeChar((int)Test[0].B_T) || !IsTypeChar((int)Test[1].B_T))
1296 goto FilterError;
1297#endif
1298 if (Arg(0)->Eval(g))
1299 return TRUE;
1300
1301 Value->SetValue_bool(PlugEvalLike(g, Val(0)->GetCharValue(),
1302 Val(1)->GetCharValue(),
1303 Val(0)->IsCi()));
1304 break;
1305
1306 case OP_AND:
1307#if defined(_DEBUG)
1308 if (Test[0].B_T != TYPE_INT || Test[1].B_T != TYPE_INT)
1309 goto FilterError;
1310#endif
1311
1312 if (Arg(0)->Eval(g))
1313 return TRUE;
1314
1315 Value->SetValue(Val(0)->GetIntValue());
1316
1317 if (!Value->GetIntValue())
1318 return FALSE; // No need to evaluate 2nd argument
1319
1320 if (Arg(1)->Eval(g))
1321 return TRUE;
1322
1323 Value->SetValue(Val(1)->GetIntValue());
1324 break;
1325
1326 case OP_OR:
1327#if defined(_DEBUG)
1328 if (Test[0].B_T != TYPE_INT || Test[1].B_T != TYPE_INT)
1329 goto FilterError;
1330#endif
1331
1332 if (Arg(0)->Eval(g))
1333 return TRUE;
1334
1335 Value->SetValue(Val(0)->GetIntValue());
1336
1337 if (Value->GetIntValue())
1338 return FALSE; // No need to evaluate 2nd argument
1339
1340 if (Arg(1)->Eval(g))
1341 return TRUE;
1342
1343 Value->SetValue(Val(1)->GetIntValue());
1344 break;
1345
1346 case OP_NOT:
1347#if defined(_DEBUG)
1348 if (Test[0].B_T != TYPE_INT) // Should be type bool ???
1349 goto FilterError;
1350#endif
1351
1352 if (Arg(0)->Eval(g))
1353 return TRUE;
1354
1355 Value->SetValue_bool(!Val(0)->GetIntValue());
1356 break;
1357
1358 case OP_SEP: // No more used while evaluating
1359 default:
1360 goto FilterError;
1361 } // endswitch Opc
1362
1363 if (trace(1))
1364 htrc("Eval: filter %p Opc=%d result=%d\n",
1365 this, Opc, Value->GetIntValue());
1366
1367 return FALSE;
1368
1369 FilterError:
1370 sprintf(g->Message, MSG(BAD_FILTER),
1371 Opc, Test[0].B_T, Test[1].B_T, GetArgType(0), GetArgType(1));
1372 return TRUE;
1373 } // end of Eval
1374
1375#if 0
1376/***********************************************************************/
1377/* Called by PlugCopyDB to make a copy of a (linearized) filter chain.*/
1378/***********************************************************************/
1379PFIL FILTER::Copy(PTABS t)
1380 {
1381 int i;
1382 PFIL fil1, fil2, newfilchain = NULL, fprec = NULL;
1383
1384 for (fil1 = this; fil1; fil1 = fil1->Next) {
1385 fil2 = new(t->G) FILTER(fil1);
1386
1387 if (!fprec)
1388 newfilchain = fil2;
1389 else
1390 fprec->Next = fil2;
1391
1392 NewPointer(t, fil1, fil2);
1393
1394 for (i = 0; i < 2; i++)
1395 if (fil1->GetArgType(i) == TYPE_COLBLK ||
1396 fil1->GetArgType(i) == TYPE_FILTER)
1397 AddPointer(t, &fil2->Arg(i));
1398
1399 fprec = fil2;
1400 } /* endfor fil1 */
1401
1402 return newfilchain;
1403 } // end of Copy
1404#endif // 0
1405
1406/*********************************************************************/
1407/* Make file output of FILTER contents. */
1408/*********************************************************************/
1409void FILTER::Printf(PGLOBAL g, FILE *f, uint n)
1410 {
1411 char m[64];
1412
1413 memset(m, ' ', n); // Make margin string
1414 m[n] = '\0';
1415
1416 bool lin = (Next != NULL); // lin == TRUE if linearized
1417
1418 for (PFIL fp = this; fp; fp = fp->Next) {
1419 fprintf(f, "%sFILTER: at %p opc=%d lin=%d result=%d\n",
1420 m, fp, fp->Opc, lin,
1421 (Value) ? Value->GetIntValue() : 0);
1422
1423 for (int i = 0; i < 2; i++) {
1424 fprintf(f, "%s Arg(%d) type=%d value=%p B_T=%d val=%p\n",
1425 m, i, fp->GetArgType(i), fp->Arg(i),
1426 fp->Test[i].B_T, fp->Val(i));
1427
1428 if (lin && fp->GetArgType(i) == TYPE_FILTER)
1429 fprintf(f, "%s Filter at %p\n", m, fp->Arg(i));
1430 else
1431 fp->Arg(i)->Printf(g, f, n + 2);
1432
1433 } // endfor i
1434
1435 } // endfor fp
1436
1437 } // end of Printf
1438
1439/***********************************************************************/
1440/* Make string output of TABLE contents (z should be checked). */
1441/***********************************************************************/
1442void FILTER::Prints(PGLOBAL g, char *ps, uint z)
1443 {
1444 #define FLEN 100
1445
1446 typedef struct _bc {
1447 struct _bc *Next;
1448 char Cold[FLEN+1];
1449 } BC, *PBC;
1450
1451 char *p;
1452 int n;
1453 PFIL fp;
1454 PBC bxp, bcp = NULL;
1455
1456 *ps = '\0';
1457
1458 for (fp = this; fp && z > 0; fp = fp->Next) {
1459 if (fp->Opc < OP_CNC || fp->Opc == OP_IN || fp->Opc == OP_NULL
1460 || fp->Opc == OP_LIKE || fp->Opc == OP_EXIST) {
1461 if (!(bxp = new BC)) {
1462 strncat(ps, "Filter(s)", z);
1463 return;
1464 } /* endif */
1465
1466 bxp->Next = bcp;
1467 bcp = bxp;
1468 p = bcp->Cold;
1469 n = FLEN;
1470 fp->Arg(0)->Prints(g, p, n);
1471 n = FLEN - strlen(p);
1472
1473 switch (fp->Opc) {
1474 case OP_EQ:
1475 strncat(bcp->Cold, "=", n);
1476 break;
1477 case OP_NE:
1478 strncat(bcp->Cold, "!=", n);
1479 break;
1480 case OP_GT:
1481 strncat(bcp->Cold, ">", n);
1482 break;
1483 case OP_GE:
1484 strncat(bcp->Cold, ">=", n);
1485 break;
1486 case OP_LT:
1487 strncat(bcp->Cold, "<", n);
1488 break;
1489 case OP_LE:
1490 strncat(bcp->Cold, "<=", n);
1491 break;
1492 case OP_IN:
1493 strncat(bcp->Cold, " in ", n);
1494 break;
1495 case OP_NULL:
1496 strncat(bcp->Cold, " is null", n);
1497 break;
1498 case OP_LIKE:
1499 strncat(bcp->Cold, " like ", n);
1500 break;
1501 case OP_EXIST:
1502 strncat(bcp->Cold, " exists ", n);
1503 break;
1504 case OP_AND:
1505 strncat(bcp->Cold, " and ", n);
1506 break;
1507 case OP_OR:
1508 strncat(bcp->Cold, " or ", n);
1509 break;
1510 default:
1511 strncat(bcp->Cold, "?", n);
1512 } // endswitch Opc
1513
1514 n = FLEN - strlen(p);
1515 p += strlen(p);
1516 fp->Arg(1)->Prints(g, p, n);
1517 } else
1518 if (!bcp) {
1519 strncat(ps, "???", z);
1520 z -= 3;
1521 } else
1522 switch (fp->Opc) {
1523 case OP_SEP: // Filter list separator
1524 strncat(ps, bcp->Cold, z);
1525 z -= strlen(bcp->Cold);
1526 strncat(ps, ";", z--);
1527 bxp = bcp->Next;
1528 delete bcp;
1529 bcp = bxp;
1530 break;
1531 case OP_NOT: // Filter NOT operator
1532 for (n = MY_MIN((int)strlen(bcp->Cold), FLEN-3); n >= 0; n--)
1533 bcp->Cold[n+2] = bcp->Cold[n];
1534 bcp->Cold[0] = '^';
1535 bcp->Cold[1] = '(';
1536 strcat(bcp->Cold, ")");
1537 break;
1538 default:
1539 for (n = MY_MIN((int)strlen(bcp->Cold), FLEN-4); n >= 0; n--)
1540 bcp->Cold[n+3] = bcp->Cold[n];
1541 bcp->Cold[0] = ')';
1542 switch (fp->Opc) {
1543 case OP_AND: bcp->Cold[1] = '&'; break;
1544 case OP_OR: bcp->Cold[1] = '|'; break;
1545 default: bcp->Cold[1] = '?';
1546 } // endswitch
1547 bcp->Cold[2] = '(';
1548 strcat(bcp->Cold, ")");
1549 bxp = bcp->Next;
1550 for (n = MY_MIN((int)strlen(bxp->Cold), FLEN-1); n >= 0; n--)
1551 bxp->Cold[n+1] = bxp->Cold[n];
1552 bxp->Cold[0] = '(';
1553 strncat(bxp->Cold, bcp->Cold, FLEN-strlen(bxp->Cold));
1554 delete bcp;
1555 bcp = bxp;
1556 } // endswitch
1557
1558 } // endfor fp
1559
1560 n = 0;
1561
1562 if (!bcp)
1563 strncat(ps, "Null-Filter", z);
1564 else do {
1565 if (z > 0) {
1566 if (n++ > 0) {
1567 strncat(ps, "*?*", z);
1568 z = MY_MAX(0, (int)z-3);
1569 } // endif
1570 strncat(ps, bcp->Cold, z);
1571 z -= strlen(bcp->Cold);
1572 } // endif
1573
1574 bxp = bcp->Next;
1575 delete bcp;
1576 bcp = bxp;
1577 } while (bcp); // enddo
1578
1579 } // end of Prints
1580
1581
1582/* -------------------- Derived Classes Functions -------------------- */
1583
1584/***********************************************************************/
1585/* FILTERCMP constructor. */
1586/***********************************************************************/
1587FILTERCMP::FILTERCMP(PGLOBAL g)
1588 {
1589 Bt = OpBmp(g, Opc);
1590 } // end of FILTERCMP constructor
1591
1592/***********************************************************************/
1593/* Eval: Compute result value for comparison operators. */
1594/***********************************************************************/
1595bool FILTERCMP::Eval(PGLOBAL g)
1596 {
1597 if (Arg(0)->Eval(g) || Arg(1)->Eval(g))
1598 return TRUE;
1599
1600 Value->SetValue_bool(!(Val(0)->TestValue(Val(1)) & Bt));
1601 return FALSE;
1602 } // end of Eval
1603
1604/***********************************************************************/
1605/* Eval: Compute result value for AND filters. */
1606/***********************************************************************/
1607bool FILTERAND::Eval(PGLOBAL g)
1608 {
1609 if (Arg(0)->Eval(g))
1610 return TRUE;
1611
1612 Value->SetValue(Val(0)->GetIntValue());
1613
1614 if (!Value->GetIntValue())
1615 return FALSE; // No need to evaluate 2nd argument
1616
1617 if (Arg(1)->Eval(g))
1618 return TRUE;
1619
1620 Value->SetValue(Val(1)->GetIntValue());
1621 return FALSE;
1622 } // end of Eval
1623
1624/***********************************************************************/
1625/* Eval: Compute result value for OR filters. */
1626/***********************************************************************/
1627bool FILTEROR::Eval(PGLOBAL g)
1628 {
1629 if (Arg(0)->Eval(g))
1630 return TRUE;
1631
1632 Value->SetValue(Val(0)->GetIntValue());
1633
1634 if (Value->GetIntValue())
1635 return FALSE; // No need to evaluate 2nd argument
1636
1637 if (Arg(1)->Eval(g))
1638 return TRUE;
1639
1640 Value->SetValue(Val(1)->GetIntValue());
1641 return FALSE;
1642 } // end of Eval
1643
1644/***********************************************************************/
1645/* Eval: Compute result value for NOT filters. */
1646/***********************************************************************/
1647bool FILTERNOT::Eval(PGLOBAL g)
1648 {
1649 if (Arg(0)->Eval(g))
1650 return TRUE;
1651
1652 Value->SetValue_bool(!Val(0)->GetIntValue());
1653 return FALSE;
1654 } // end of Eval
1655
1656/***********************************************************************/
1657/* Eval: Compute result value for IN filters. */
1658/***********************************************************************/
1659bool FILTERIN::Eval(PGLOBAL g)
1660 {
1661 if (Arg(0)->Eval(g))
1662 return TRUE;
1663
1664 Value->SetValue_bool(((PARRAY)Arg(1))->FilTest(g, Val(0), Opc, Opm));
1665 return FALSE;
1666 } // end of Eval
1667
1668/***********************************************************************/
1669/* FILTERTRUE does nothing and returns TRUE. */
1670/***********************************************************************/
1671void FILTERTRUE::Reset(void)
1672 {
1673 } // end of Reset
1674
1675bool FILTERTRUE::Eval(PGLOBAL)
1676 {
1677 return FALSE;
1678 } // end of Eval
1679
1680/* ------------------------- Friend Functions ------------------------ */
1681
1682#if 0
1683/***********************************************************************/
1684/* Prepare: prepare a filter for execution. This implies two things: */
1685/* 1) de-linearize the filter to be able to evaluate it recursively. */
1686/* This permit to conditionally evaluate only the first argument */
1687/* of OP_OR and OP_AND filters without having to pass by an */
1688/* intermediate Apply function (as this has a performance cost). */
1689/* 2) do all the necessary conversion for all filter block arguments. */
1690/***********************************************************************/
1691PFIL PrepareFilter(PGLOBAL g, PFIL fp, bool having)
1692 {
1693 PFIL filp = NULL;
1694
1695 if (trace(1))
1696 htrc("PrepareFilter: fp=%p having=%d\n", fp, having);
1697
1698 while (fp) {
1699 if (fp->Opc == OP_SEP)
1700 // If separator is not last transform it into an AND filter
1701 if (fp->Next) {
1702 filp = PrepareFilter(g, fp->Next, having);
1703 fp->Arg(1) = filp;
1704 fp->Opc = OP_AND;
1705 fp->Next = NULL; // This will end the loop
1706 } else
1707 break; // Remove eventual ending separator(s)
1708
1709// if (fp->Convert(g, having))
1710// throw (int)TYPE_FILTER;
1711
1712 filp = fp;
1713 fp = fp->Next;
1714 filp->Next = NULL;
1715 } // endwhile
1716
1717 if (trace(1))
1718 htrc(" returning filp=%p\n", filp);
1719
1720 return filp;
1721 } // end of PrepareFilter
1722#endif // 0
1723
1724/***********************************************************************/
1725/* ApplyFilter: Apply filtering for a table (where or having clause). */
1726/* New algorithm: evaluate from the root a de-linearized filter so */
1727/* AND/OR clauses can be optimized throughout the whole tree. */
1728/***********************************************************************/
1729DllExport bool ApplyFilter(PGLOBAL g, PFIL filp)
1730 {
1731 if (!filp)
1732 return TRUE;
1733
1734 // Must be done for null tables
1735 filp->Reset();
1736
1737//if (tdbp && tdbp->IsNull())
1738// return TRUE;
1739
1740 if (filp->Eval(g))
1741 throw (int)TYPE_FILTER;
1742
1743 if (trace(2))
1744 htrc("PlugFilter filp=%p result=%d\n",
1745 filp, filp->GetResult());
1746
1747 return filp->GetResult();
1748 } // end of ApplyFilter
1749