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 | /***********************************************************************/ |
42 | void PlugConvertConstant(PGLOBAL, void* &, short&); |
43 | //void *PlugCopyDB(PTABS, void*, INT); |
44 | void NewPointer(PTABS, void*, void*); |
45 | void AddPointer(PTABS, void*); |
46 | |
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 | /***********************************************************************/ |
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 | /***********************************************************************/ |
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 | |
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 | /***********************************************************************/ |
175 | FILTER::FILTER(PGLOBAL g, POPER pop, PPARM *tp) |
176 | { |
177 | Constr(g, pop->Val, pop->Mod, tp); |
178 | } // end of FILTER constructor |
179 | |
180 | FILTER::FILTER(PGLOBAL g, OPVAL opc, PPARM *tp) |
181 | { |
182 | Constr(g, opc, 0, tp); |
183 | } // end of FILTER constructor |
184 | |
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 | /***********************************************************************/ |
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 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 | /***********************************************************************/ |
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 | /***********************************************************************/ |
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 | /***********************************************************************/ |
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 | /***********************************************************************/ |
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 | /***********************************************************************/ |
1218 | bool 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 | /***********************************************************************/ |
1379 | PFIL 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 | /*********************************************************************/ |
1409 | void 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 | /***********************************************************************/ |
1442 | void 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 | /***********************************************************************/ |
1587 | FILTERCMP::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 | /***********************************************************************/ |
1595 | bool 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 | /***********************************************************************/ |
1607 | bool 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 | /***********************************************************************/ |
1627 | bool 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 | /***********************************************************************/ |
1647 | bool 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 | /***********************************************************************/ |
1659 | bool 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 | /***********************************************************************/ |
1671 | void FILTERTRUE::Reset(void) |
1672 | { |
1673 | } // end of Reset |
1674 | |
1675 | bool 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 | /***********************************************************************/ |
1691 | PFIL 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 | /***********************************************************************/ |
1729 | DllExport 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 | |