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