1 | /************* Array C++ Functions Source Code File (.CPP) *************/ |
2 | /* Name: ARRAY.CPP Version 2.3 */ |
3 | /* */ |
4 | /* (C) Copyright to the author Olivier BERTRAND 2005-2017 */ |
5 | /* */ |
6 | /* This file contains the XOBJECT derived class ARRAY functions. */ |
7 | /* ARRAY is used for elaborate type of processing, such as sorting */ |
8 | /* and dichotomic search (Find). This new version does not use sub */ |
9 | /* classes anymore for the different types but relies entirely on the */ |
10 | /* functionalities provided by the VALUE and VALBLK classes. */ |
11 | /* Currently the only supported types are STRING, SHORT, int, DATE, */ |
12 | /* TOKEN, DOUBLE, and Compressed Strings. */ |
13 | /***********************************************************************/ |
14 | |
15 | /***********************************************************************/ |
16 | /* Include relevant MariaDB header file. */ |
17 | /***********************************************************************/ |
18 | #include "my_global.h" |
19 | #include "sql_class.h" |
20 | //#include "sql_time.h" |
21 | |
22 | #if defined(__WIN__) |
23 | //#include <windows.h> |
24 | #else // !__WIN__ |
25 | #include <string.h> |
26 | #include <sys/types.h> |
27 | #include <sys/stat.h> |
28 | #include <stdint.h> // for uintprt_h |
29 | #endif // !__WIN__ |
30 | |
31 | /***********************************************************************/ |
32 | /* Include required application header files */ |
33 | /* global.h is header containing all global Plug declarations. */ |
34 | /* plgdbsem.h is header containing the DB applic. declarations. */ |
35 | /* xobject.h is header containing XOBJECT derived classes declares. */ |
36 | /***********************************************************************/ |
37 | #include "global.h" |
38 | #include "plgdbsem.h" |
39 | #include "xtable.h" |
40 | #include "array.h" |
41 | //#include "select.h" |
42 | //#include "query.h" |
43 | //#include "token.h" |
44 | |
45 | /***********************************************************************/ |
46 | /* Macro definitions. */ |
47 | /***********************************************************************/ |
48 | #if defined(_DEBUG) |
49 | #define ASSERT(B) assert(B); |
50 | #else |
51 | #define ASSERT(B) |
52 | #endif |
53 | |
54 | /***********************************************************************/ |
55 | /* DB static external variables. */ |
56 | /***********************************************************************/ |
57 | extern MBLOCK Nmblk; /* Used to initialize MBLOCK's */ |
58 | |
59 | /***********************************************************************/ |
60 | /* External functions. */ |
61 | /***********************************************************************/ |
62 | BYTE OpBmp(PGLOBAL g, OPVAL opc); |
63 | void EncodeValue(int *lp, char *strp, int n); |
64 | PARRAY MakeValueArray(PGLOBAL g, PPARM pp); // avoid gcc warning |
65 | |
66 | /***********************************************************************/ |
67 | /* MakeValueArray: Makes a value array from a value list. */ |
68 | /***********************************************************************/ |
69 | PARRAY MakeValueArray(PGLOBAL g, PPARM pp) |
70 | { |
71 | int n, valtyp = 0; |
72 | size_t len = 0; |
73 | PARRAY par; |
74 | PPARM parmp; |
75 | |
76 | if (!pp) |
77 | return NULL; |
78 | |
79 | /*********************************************************************/ |
80 | /* New version with values coming in a list. */ |
81 | /*********************************************************************/ |
82 | if ((valtyp = pp->Type) != TYPE_STRING) |
83 | len = 1; |
84 | |
85 | if (trace(1)) |
86 | htrc("valtyp=%d len=%d\n" , valtyp, len); |
87 | |
88 | /*********************************************************************/ |
89 | /* Firstly check the list and count the number of values in it. */ |
90 | /*********************************************************************/ |
91 | for (n = 0, parmp = pp; parmp; n++, parmp = parmp->Next) |
92 | if (parmp->Type != valtyp) { |
93 | sprintf(g->Message, MSG(BAD_PARAM_TYPE), "MakeValueArray" , parmp->Type); |
94 | return NULL; |
95 | } else if (valtyp == TYPE_STRING) |
96 | len = MY_MAX(len, strlen((char*)parmp->Value)); |
97 | |
98 | /*********************************************************************/ |
99 | /* Make an array object with one block of the proper size. */ |
100 | /*********************************************************************/ |
101 | par = new(g) ARRAY(g, valtyp, n, (int)len); |
102 | |
103 | if (par->GetResultType() == TYPE_ERROR) |
104 | return NULL; // Memory allocation error in ARRAY |
105 | |
106 | /*********************************************************************/ |
107 | /* All is right now, fill the array block. */ |
108 | /*********************************************************************/ |
109 | for (parmp = pp; parmp; parmp = parmp->Next) |
110 | switch (valtyp) { |
111 | case TYPE_STRING: |
112 | par->AddValue(g, (PSZ)parmp->Value); |
113 | break; |
114 | case TYPE_SHORT: |
115 | par->AddValue(g, *(short*)parmp->Value); |
116 | break; |
117 | case TYPE_INT: |
118 | par->AddValue(g, *(int*)parmp->Value); |
119 | break; |
120 | case TYPE_DOUBLE: |
121 | par->AddValue(g, *(double*)parmp->Value); |
122 | break; |
123 | case TYPE_PCHAR: |
124 | par->AddValue(g, parmp->Value); |
125 | break; |
126 | case TYPE_VOID: |
127 | // Integer stored inside pp->Value |
128 | par->AddValue(g, parmp->Intval); |
129 | break; |
130 | } // endswitch valtyp |
131 | |
132 | /*********************************************************************/ |
133 | /* Send back resulting array. */ |
134 | /*********************************************************************/ |
135 | return par; |
136 | } // end of MakeValueArray |
137 | |
138 | /* -------------------------- Class ARRAY ---------------------------- */ |
139 | |
140 | /***********************************************************************/ |
141 | /* ARRAY public constructor. */ |
142 | /***********************************************************************/ |
143 | ARRAY::ARRAY(PGLOBAL g, int type, int size, int length, int prec) |
144 | : CSORT(false) |
145 | { |
146 | Nval = 0; |
147 | Ndif = 0; |
148 | Bot = 0; |
149 | Top = 0; |
150 | Size = size; |
151 | Type = type; |
152 | Xsize = -1; |
153 | Len = 1; |
154 | |
155 | switch (type) { |
156 | case TYPE_STRING: |
157 | Len = length; |
158 | /* fall through */ |
159 | case TYPE_SHORT: |
160 | case TYPE_INT: |
161 | case TYPE_DOUBLE: |
162 | case TYPE_PCHAR: |
163 | Type = type; |
164 | break; |
165 | case TYPE_VOID: |
166 | Type = TYPE_INT; |
167 | break; |
168 | #if 0 |
169 | case TYPE_TOKEN: |
170 | break; |
171 | case TYPE_LIST: |
172 | Len = 0; |
173 | prec = length; |
174 | break; |
175 | #endif // 0 |
176 | default: // This is illegal an causes an ill formed array building |
177 | sprintf(g->Message, MSG(BAD_ARRAY_TYPE), type); |
178 | Type = TYPE_ERROR; |
179 | return; |
180 | } // endswitch type |
181 | |
182 | Valblk = new(g) MBVALS; |
183 | |
184 | if (!(Vblp = Valblk->Allocate(g, Type, Len, prec, Size))) |
185 | Type = TYPE_ERROR; |
186 | else if (!Valblk->GetMemp() && Type != TYPE_LIST) |
187 | // The error message was built by PlgDBalloc |
188 | Type = TYPE_ERROR; |
189 | else if (type != TYPE_PCHAR) |
190 | Value = AllocateValue(g, type, Len, prec); |
191 | |
192 | Constant = true; |
193 | } // end of ARRAY constructor |
194 | |
195 | #if 0 |
196 | /***********************************************************************/ |
197 | /* ARRAY public constructor from a QUERY. */ |
198 | /***********************************************************************/ |
199 | ARRAY::ARRAY(PGLOBAL g, PQUERY qryp) : CSORT(false) |
200 | { |
201 | Type = qryp->GetColType(0); |
202 | Nval = qryp->GetNblin(); |
203 | Ndif = 0; |
204 | Bot = 0; |
205 | Top = 0; |
206 | Size = Nval; |
207 | Xsize = -1; |
208 | Len = qryp->GetColLength(0); |
209 | X = Inf = Sup = 0; |
210 | Correlated = false; |
211 | |
212 | switch (Type) { |
213 | case TYPE_STRING: |
214 | case TYPE_SHORT: |
215 | case TYPE_INT: |
216 | case TYPE_DATE: |
217 | case TYPE_DOUBLE: |
218 | // case TYPE_TOKEN: |
219 | // case TYPE_LIST: |
220 | // Valblk = qryp->GetCol(0)->Result; |
221 | // Vblp = qryp->GetColBlk(0); |
222 | // Value = qryp->GetColValue(0); |
223 | // break; |
224 | default: // This is illegal an causes an ill formed array building |
225 | sprintf(g->Message, MSG(BAD_ARRAY_TYPE), Type); |
226 | Type = TYPE_ERROR; |
227 | } // endswitch type |
228 | |
229 | if (!Valblk || (!Valblk->GetMemp() && Type != TYPE_LIST)) |
230 | // The error message was built by ??? |
231 | Type = TYPE_ERROR; |
232 | |
233 | Constant = true; |
234 | } // end of ARRAY constructor |
235 | |
236 | /***********************************************************************/ |
237 | /* ARRAY constructor from a TYPE_LIST subarray. */ |
238 | /***********************************************************************/ |
239 | ARRAY::ARRAY(PGLOBAL g, PARRAY par, int k) : CSORT(false) |
240 | { |
241 | int prec; |
242 | LSTBLK *lp; |
243 | |
244 | if (par->Type != TYPE_LIST) { |
245 | Type = TYPE_ERROR; |
246 | return; |
247 | } // endif Type |
248 | |
249 | lp = (LSTBLK*)par->Vblp; |
250 | |
251 | Nval = par->Nval; |
252 | Ndif = 0; |
253 | Bot = 0; |
254 | Top = 0; |
255 | Size = par->Size; |
256 | Xsize = -1; |
257 | |
258 | Valblk = lp->Mbvk[k]; |
259 | Vblp = Valblk->Vblk; |
260 | Type = Vblp->GetType(); |
261 | Len = (Type == TYPE_STRING) ? Vblp->GetVlen() : 0; |
262 | prec = (Type == TYPE_FLOAT) ? 2 : 0; |
263 | Value = AllocateValue(g, Type, Len, prec, NULL); |
264 | Constant = true; |
265 | } // end of ARRAY constructor |
266 | |
267 | /***********************************************************************/ |
268 | /* Empty: reset the array for a new use (correlated queries). */ |
269 | /* Note: this is temporary as correlated queries will not use arrays */ |
270 | /* anymore with future optimized algorithms. */ |
271 | /***********************************************************************/ |
272 | void ARRAY::Empty(void) |
273 | { |
274 | assert(Correlated); |
275 | Nval = Ndif = 0; |
276 | Bot = Top = X = Inf = Sup = 0; |
277 | } // end of Empty |
278 | #endif // 0 |
279 | |
280 | /***********************************************************************/ |
281 | /* Add a string element to an array. */ |
282 | /***********************************************************************/ |
283 | bool ARRAY::AddValue(PGLOBAL g, PSZ strp) |
284 | { |
285 | if (Type != TYPE_STRING) { |
286 | sprintf(g->Message, MSG(ADD_BAD_TYPE), GetTypeName(Type), "CHAR" ); |
287 | return true; |
288 | } // endif Type |
289 | |
290 | if (trace(1)) |
291 | htrc(" adding string(%d): '%s'\n" , Nval, strp); |
292 | |
293 | //Value->SetValue_psz(strp); |
294 | //Vblp->SetValue(valp, Nval++); |
295 | Vblp->SetValue(strp, Nval++); |
296 | return false; |
297 | } // end of AddValue |
298 | |
299 | /***********************************************************************/ |
300 | /* Add a char pointer element to an array. */ |
301 | /***********************************************************************/ |
302 | bool ARRAY::AddValue(PGLOBAL g, void *p) |
303 | { |
304 | if (Type != TYPE_PCHAR) { |
305 | sprintf(g->Message, MSG(ADD_BAD_TYPE), GetTypeName(Type), "PCHAR" ); |
306 | return true; |
307 | } // endif Type |
308 | |
309 | if (trace(1)) |
310 | htrc(" adding pointer(%d): %p\n" , Nval, p); |
311 | |
312 | Vblp->SetValue((PSZ)p, Nval++); |
313 | return false; |
314 | } // end of AddValue |
315 | |
316 | /***********************************************************************/ |
317 | /* Add a short integer element to an array. */ |
318 | /***********************************************************************/ |
319 | bool ARRAY::AddValue(PGLOBAL g, short n) |
320 | { |
321 | if (Type != TYPE_SHORT) { |
322 | sprintf(g->Message, MSG(ADD_BAD_TYPE), GetTypeName(Type), "SHORT" ); |
323 | return true; |
324 | } // endif Type |
325 | |
326 | if (trace(1)) |
327 | htrc(" adding SHORT(%d): %hd\n" , Nval, n); |
328 | |
329 | //Value->SetValue(n); |
330 | //Vblp->SetValue(valp, Nval++); |
331 | Vblp->SetValue(n, Nval++); |
332 | return false; |
333 | } // end of AddValue |
334 | |
335 | /***********************************************************************/ |
336 | /* Add an integer element to an array. */ |
337 | /***********************************************************************/ |
338 | bool ARRAY::AddValue(PGLOBAL g, int n) |
339 | { |
340 | if (Type != TYPE_INT) { |
341 | sprintf(g->Message, MSG(ADD_BAD_TYPE), GetTypeName(Type), "INTEGER" ); |
342 | return true; |
343 | } // endif Type |
344 | |
345 | if (trace(1)) |
346 | htrc(" adding int(%d): %d\n" , Nval, n); |
347 | |
348 | //Value->SetValue(n); |
349 | //Vblp->SetValue(valp, Nval++); |
350 | Vblp->SetValue(n, Nval++); |
351 | return false; |
352 | } // end of AddValue |
353 | |
354 | /***********************************************************************/ |
355 | /* Add a double float element to an array. */ |
356 | /***********************************************************************/ |
357 | bool ARRAY::AddValue(PGLOBAL g, double d) |
358 | { |
359 | if (Type != TYPE_DOUBLE) { |
360 | sprintf(g->Message, MSG(ADD_BAD_TYPE), GetTypeName(Type), "DOUBLE" ); |
361 | return true; |
362 | } // endif Type |
363 | |
364 | if (trace(1)) |
365 | htrc(" adding float(%d): %lf\n" , Nval, d); |
366 | |
367 | Value->SetValue(d); |
368 | Vblp->SetValue(Value, Nval++); |
369 | return false; |
370 | } // end of AddValue |
371 | |
372 | /***********************************************************************/ |
373 | /* Add the value of a XOBJECT block to an array. */ |
374 | /***********************************************************************/ |
375 | bool ARRAY::AddValue(PGLOBAL g, PXOB xp) |
376 | { |
377 | if (Type != xp->GetResultType()) { |
378 | sprintf(g->Message, MSG(ADD_BAD_TYPE), |
379 | GetTypeName(xp->GetResultType()), GetTypeName(Type)); |
380 | return true; |
381 | } // endif Type |
382 | |
383 | if (trace(1)) |
384 | htrc(" adding (%d) from xp=%p\n" , Nval, xp); |
385 | |
386 | //AddValue(xp->GetValue()); |
387 | Vblp->SetValue(xp->GetValue(), Nval++); |
388 | return false; |
389 | } // end of AddValue |
390 | |
391 | /***********************************************************************/ |
392 | /* Add a value to an array. */ |
393 | /***********************************************************************/ |
394 | bool ARRAY::AddValue(PGLOBAL g, PVAL vp) |
395 | { |
396 | if (Type != vp->GetType()) { |
397 | sprintf(g->Message, MSG(ADD_BAD_TYPE), |
398 | GetTypeName(vp->GetType()), GetTypeName(Type)); |
399 | return true; |
400 | } // endif Type |
401 | |
402 | if (trace(1)) |
403 | htrc(" adding (%d) from vp=%p\n" , Nval, vp); |
404 | |
405 | Vblp->SetValue(vp, Nval++); |
406 | return false; |
407 | } // end of AddValue |
408 | |
409 | /***********************************************************************/ |
410 | /* Retrieve the nth value of the array. */ |
411 | /***********************************************************************/ |
412 | void ARRAY::GetNthValue(PVAL valp, int n) |
413 | { |
414 | valp->SetValue_pvblk(Vblp, n); |
415 | } // end of GetNthValue |
416 | |
417 | #if 0 |
418 | /***********************************************************************/ |
419 | /* Retrieve the nth subvalue of a list array. */ |
420 | /***********************************************************************/ |
421 | bool ARRAY::GetSubValue(PGLOBAL g, PVAL valp, int *kp) |
422 | { |
423 | PVBLK vblp; |
424 | |
425 | if (Type != TYPE_LIST) { |
426 | sprintf(g->Message, MSG(NO_SUB_VAL), Type); |
427 | return true; |
428 | } // endif Type |
429 | |
430 | vblp = ((LSTBLK*)Vblp)->Mbvk[kp[0]]->Vblk; |
431 | valp->SetValue_pvblk(vblp, kp[1]); |
432 | return false; |
433 | } // end of GetSubValue |
434 | #endif // 0 |
435 | |
436 | /***********************************************************************/ |
437 | /* Return the nth value of an integer array. */ |
438 | /***********************************************************************/ |
439 | int ARRAY::GetIntValue(int n) |
440 | { |
441 | assert (Type == TYPE_INT); |
442 | return Vblp->GetIntValue(n); |
443 | } // end of GetIntValue |
444 | |
445 | /***********************************************************************/ |
446 | /* Return the nth value of a STRING array. */ |
447 | /***********************************************************************/ |
448 | char *ARRAY::GetStringValue(int n) |
449 | { |
450 | assert (Type == TYPE_STRING || Type == TYPE_PCHAR); |
451 | return Vblp->GetCharValue(n); |
452 | } // end of GetStringValue |
453 | |
454 | /***********************************************************************/ |
455 | /* Find whether a value is in an array. */ |
456 | /* Provide a conversion limited to the Value limitation. */ |
457 | /***********************************************************************/ |
458 | bool ARRAY::Find(PVAL valp) |
459 | { |
460 | register int n; |
461 | PVAL vp; |
462 | |
463 | if (Type != valp->GetType()) { |
464 | Value->SetValue_pval(valp); |
465 | vp = Value; |
466 | } else |
467 | vp = valp; |
468 | |
469 | Inf = Bot, Sup = Top; |
470 | |
471 | while (Sup - Inf > 1) { |
472 | X = (Inf + Sup) >> 1; |
473 | n = Vblp->CompVal(vp, X); |
474 | |
475 | if (n < 0) |
476 | Sup = X; |
477 | else if (n > 0) |
478 | Inf = X; |
479 | else |
480 | return true; |
481 | |
482 | } // endwhile |
483 | |
484 | return false; |
485 | } // end of Find |
486 | |
487 | /***********************************************************************/ |
488 | /* ARRAY: Compare routine for a list of values. */ |
489 | /***********************************************************************/ |
490 | BYTE ARRAY::Vcompare(PVAL vp, int n) |
491 | { |
492 | Value->SetValue_pvblk(Vblp, n); |
493 | return vp->TestValue(Value); |
494 | } // end of Vcompare |
495 | |
496 | /***********************************************************************/ |
497 | /* Test a filter condition on an array depending on operator and mod. */ |
498 | /* Modificator values are 1: ANY (or SOME) and 2: ALL. */ |
499 | /***********************************************************************/ |
500 | bool ARRAY::FilTest(PGLOBAL g, PVAL valp, OPVAL opc, int opm) |
501 | { |
502 | int i; |
503 | PVAL vp; |
504 | BYTE bt = OpBmp(g, opc); |
505 | int top = Nval - 1; |
506 | |
507 | if (top < 0) // Array is empty |
508 | // Return true for ALL because it means that there are no item that |
509 | // does not verify the condition, which is true indeed. |
510 | // Return false for ANY because true means that there is at least |
511 | // one item that verifies the condition, which is false. |
512 | return opm == 2; |
513 | |
514 | if (valp) { |
515 | if (Type != valp->GetType()) { |
516 | Value->SetValue_pval(valp); |
517 | vp = Value; |
518 | } else |
519 | vp = valp; |
520 | |
521 | } else if (opc != OP_EXIST) { |
522 | sprintf(g->Message, MSG(MISSING_ARG), opc); |
523 | throw (int)TYPE_ARRAY; |
524 | } else // OP_EXIST |
525 | return Nval > 0; |
526 | |
527 | if (opc == OP_IN || (opc == OP_EQ && opm == 1)) |
528 | return Find(vp); |
529 | else if (opc == OP_NE && opm == 2) |
530 | return !Find(vp); |
531 | else if (opc == OP_EQ && opm == 2) |
532 | return (Ndif == 1) ? !(Vcompare(vp, 0) & bt) : false; |
533 | else if (opc == OP_NE && opm == 1) |
534 | return (Ndif == 1) ? !(Vcompare(vp, 0) & bt) : true; |
535 | |
536 | if (Type != TYPE_LIST) { |
537 | if (opc == OP_GT || opc == OP_GE) |
538 | return !(Vcompare(vp, (opm == 1) ? 0 : top) & bt); |
539 | else |
540 | return !(Vcompare(vp, (opm == 2) ? 0 : top) & bt); |
541 | |
542 | } // endif Type |
543 | |
544 | // Case of TYPE_LIST |
545 | if (opm == 2) { |
546 | for (i = 0; i < Nval; i++) |
547 | if (Vcompare(vp, i) & bt) |
548 | return false; |
549 | |
550 | return true; |
551 | } else { // opm == 1 |
552 | for (i = 0; i < Nval; i++) |
553 | if (!(Vcompare(vp, i) & bt)) |
554 | return true; |
555 | |
556 | return false; |
557 | } // endif opm |
558 | |
559 | } // end of FilTest |
560 | |
561 | /***********************************************************************/ |
562 | /* Test whether this array can be converted to TYPE_SHORT. */ |
563 | /* Must be called after the array is sorted. */ |
564 | /***********************************************************************/ |
565 | bool ARRAY::CanBeShort(void) |
566 | { |
567 | int* To_Val = (int*)Valblk->GetMemp(); |
568 | |
569 | if (Type != TYPE_INT || !Ndif) |
570 | return false; |
571 | |
572 | // Because the array is sorted, this is true if all the array |
573 | // int values are in the range of SHORT values |
574 | return (To_Val[0] >= -32768 && To_Val[Nval-1] < 32768); |
575 | } // end of CanBeShort |
576 | |
577 | /***********************************************************************/ |
578 | /* Convert an array to new numeric type k. */ |
579 | /* Note: conversion is always made in ascending order from STRING to */ |
580 | /* short to int to double so no precision is lost in the conversion. */ |
581 | /* One exception is converting from int to short compatible arrays. */ |
582 | /***********************************************************************/ |
583 | int ARRAY::Convert(PGLOBAL g, int k, PVAL vp) |
584 | { |
585 | int i, prec = 0; |
586 | bool b = false; |
587 | PMBV ovblk = Valblk; |
588 | PVBLK ovblp = Vblp; |
589 | |
590 | Type = k; // k is the new type |
591 | Valblk = new(g) MBVALS; |
592 | |
593 | switch (Type) { |
594 | case TYPE_DOUBLE: |
595 | prec = 2; |
596 | /* fall through */ |
597 | case TYPE_SHORT: |
598 | case TYPE_INT: |
599 | case TYPE_DATE: |
600 | Len = 1; |
601 | break; |
602 | default: |
603 | sprintf(g->Message, MSG(BAD_CONV_TYPE), Type); |
604 | return TYPE_ERROR; |
605 | } // endswitch k |
606 | |
607 | Size = Nval; |
608 | Nval = 0; |
609 | Vblp = Valblk->Allocate(g, Type, Len, prec, Size); |
610 | |
611 | if (!Valblk->GetMemp()) |
612 | // The error message was built by PlgDBalloc |
613 | return TYPE_ERROR; |
614 | else |
615 | Value = AllocateValue(g, Type, Len, prec); |
616 | |
617 | /*********************************************************************/ |
618 | /* Converting STRING to DATE can be done according to date format. */ |
619 | /*********************************************************************/ |
620 | if (Type == TYPE_DATE && ovblp->GetType() == TYPE_STRING && vp) |
621 | if (((DTVAL*)Value)->SetFormat(g, vp)) |
622 | return TYPE_ERROR; |
623 | else |
624 | b = true; // Sort the new array on date internal values |
625 | |
626 | /*********************************************************************/ |
627 | /* Do the actual conversion. */ |
628 | /*********************************************************************/ |
629 | for (i = 0; i < Size; i++) { |
630 | Value->SetValue_pvblk(ovblp, i); |
631 | |
632 | if (AddValue(g, Value)) |
633 | return TYPE_ERROR; |
634 | |
635 | } // endfor i |
636 | |
637 | /*********************************************************************/ |
638 | /* For sorted arrays, get the initial find values. */ |
639 | /*********************************************************************/ |
640 | if (b) |
641 | Sort(g); |
642 | |
643 | ovblk->Free(); |
644 | return Type; |
645 | } // end of Convert |
646 | |
647 | /***********************************************************************/ |
648 | /* ARRAY Save: save value at i (used while rordering). */ |
649 | /***********************************************************************/ |
650 | void ARRAY::Save(int i) |
651 | { |
652 | Value->SetValue_pvblk(Vblp, i); |
653 | } // end of Save |
654 | |
655 | /***********************************************************************/ |
656 | /* ARRAY Restore: restore value to j (used while rordering). */ |
657 | /***********************************************************************/ |
658 | void ARRAY::Restore(int j) |
659 | { |
660 | Vblp->SetValue(Value, j); |
661 | } // end of Restore |
662 | |
663 | /***********************************************************************/ |
664 | /* ARRAY Move: move value from k to j (used while rordering). */ |
665 | /***********************************************************************/ |
666 | void ARRAY::Move(int j, int k) |
667 | { |
668 | Vblp->Move(k, j); // VALBLK does the opposite !!! |
669 | } // end of Move |
670 | |
671 | /***********************************************************************/ |
672 | /* ARRAY: Compare routine for one LIST value (ascending only). */ |
673 | /***********************************************************************/ |
674 | int ARRAY::Qcompare(int *i1, int *i2) |
675 | { |
676 | return Vblp->CompVal(*i1, *i2); |
677 | } // end of Qcompare |
678 | |
679 | /***********************************************************************/ |
680 | /* Mainly meant to set the character arrays case sensitiveness. */ |
681 | /***********************************************************************/ |
682 | void ARRAY::SetPrecision(PGLOBAL g, int p) |
683 | { |
684 | if (Vblp == NULL) { |
685 | strcpy(g->Message, MSG(PREC_VBLP_NULL)); |
686 | throw (int)TYPE_ARRAY; |
687 | } // endif Vblp |
688 | |
689 | bool was = Vblp->IsCi(); |
690 | |
691 | if (was && !p) { |
692 | strcpy(g->Message, MSG(BAD_SET_CASE)); |
693 | throw (int)TYPE_ARRAY; |
694 | } // endif Vblp |
695 | |
696 | if (was || !p) |
697 | return; |
698 | else |
699 | Vblp->SetPrec(p); |
700 | |
701 | if (!was && Type == TYPE_STRING) |
702 | // Must be resorted to eliminate duplicate strings |
703 | if (Sort(g)) |
704 | throw (int)TYPE_ARRAY; |
705 | |
706 | } // end of SetPrecision |
707 | |
708 | /***********************************************************************/ |
709 | /* Sort and eliminate distinct values from an array. */ |
710 | /* Note: this is done by making a sorted index on distinct values. */ |
711 | /* Returns false if Ok or true in case of error. */ |
712 | /***********************************************************************/ |
713 | bool ARRAY::Sort(PGLOBAL g) |
714 | { |
715 | int i, j, k; |
716 | |
717 | // This is to avoid multiply allocating for correlated subqueries |
718 | if (Nval > Xsize) { |
719 | if (Xsize >= 0) { |
720 | // Was already allocated |
721 | PlgDBfree(Index); |
722 | PlgDBfree(Offset); |
723 | } // endif Xsize |
724 | |
725 | // Prepare non conservative sort with offet values |
726 | Index.Size = Nval * sizeof(int); |
727 | |
728 | if (!PlgDBalloc(g, NULL, Index)) |
729 | goto error; |
730 | |
731 | Offset.Size = (Nval + 1) * sizeof(int); |
732 | |
733 | if (!PlgDBalloc(g, NULL, Offset)) |
734 | goto error; |
735 | |
736 | Xsize = Nval; |
737 | } // endif Nval |
738 | |
739 | // Call the sort program, it returns the number of distinct values |
740 | Ndif = Qsort(g, Nval); |
741 | |
742 | if (Ndif < 0) |
743 | goto error; |
744 | |
745 | // Use the sort index to reorder the data in storage so it will |
746 | // be physically sorted and Index can be removed. |
747 | for (i = 0; i < Nval; i++) { |
748 | if (Pex[i] == i || Pex[i] == Nval) |
749 | // Already placed or already moved |
750 | continue; |
751 | |
752 | Save(i); |
753 | |
754 | for (j = i;; j = k) { |
755 | k = Pex[j]; |
756 | Pex[j] = Nval; // Mark position as set |
757 | |
758 | if (k == i) { |
759 | Restore(j); |
760 | break; // end of loop |
761 | } else |
762 | Move(j, k); |
763 | |
764 | } // endfor j |
765 | |
766 | } // endfor i |
767 | |
768 | // Reduce the size of the To_Val array if Ndif < Nval |
769 | if (Ndif < Nval) { |
770 | for (i = 1; i < Ndif; i++) |
771 | if (i != Pof[i]) |
772 | break; |
773 | |
774 | for (; i < Ndif; i++) |
775 | Move(i, Pof[i]); |
776 | |
777 | Nval = Ndif; |
778 | } // endif ndif |
779 | |
780 | //if (!Correlated) { |
781 | if (Size > Nval) { |
782 | Size = Nval; |
783 | Valblk->ReAllocate(g, Size); |
784 | } // endif Size |
785 | |
786 | // Index and Offset are not used anymore |
787 | PlgDBfree(Index); |
788 | PlgDBfree(Offset); |
789 | Xsize = -1; |
790 | // } // endif Correlated |
791 | |
792 | Bot = -1; // For non optimized search |
793 | Top = Ndif; // Find searches the whole array. |
794 | return false; |
795 | |
796 | error: |
797 | Nval = Ndif = 0; |
798 | Valblk->Free(); |
799 | PlgDBfree(Index); |
800 | PlgDBfree(Offset); |
801 | return true; |
802 | } // end of Sort |
803 | |
804 | /***********************************************************************/ |
805 | /* Sort and return the sort index. */ |
806 | /* Note: This is meant if the array contains unique values. */ |
807 | /* Returns Index.Memp if Ok or NULL in case of error. */ |
808 | /***********************************************************************/ |
809 | void *ARRAY::GetSortIndex(PGLOBAL g) |
810 | { |
811 | // Prepare non conservative sort with offet values |
812 | Index.Size = Nval * sizeof(int); |
813 | |
814 | if (!PlgDBalloc(g, NULL, Index)) |
815 | goto error; |
816 | |
817 | Offset.Size = (Nval + 1) * sizeof(int); |
818 | |
819 | if (!PlgDBalloc(g, NULL, Offset)) |
820 | goto error; |
821 | |
822 | // Call the sort program, it returns the number of distinct values |
823 | Ndif = Qsort(g, Nval); |
824 | |
825 | if (Ndif < 0) |
826 | goto error; |
827 | |
828 | if (Ndif < Nval) |
829 | goto error; |
830 | |
831 | PlgDBfree(Offset); |
832 | return Index.Memp; |
833 | |
834 | error: |
835 | Nval = Ndif = 0; |
836 | Valblk->Free(); |
837 | PlgDBfree(Index); |
838 | PlgDBfree(Offset); |
839 | return NULL; |
840 | } // end of GetSortIndex |
841 | |
842 | /***********************************************************************/ |
843 | /* Block filter testing for IN operator on Column/Array operands. */ |
844 | /* Here we call Find that returns true if the value is in the array */ |
845 | /* with X equal to the index of the found value in the array, or */ |
846 | /* false if the value is not in the array with Inf and Sup being the */ |
847 | /* indexes of the array values that are immediately below and over */ |
848 | /* the not found value. This enables to restrict the array to the */ |
849 | /* values that are between the min and max block values and to return */ |
850 | /* the indication of whether the Find will be always true, always not */ |
851 | /* true or other. */ |
852 | /***********************************************************************/ |
853 | int ARRAY::BlockTest(PGLOBAL, int opc, int opm, |
854 | void *minp, void *maxp, bool s) |
855 | { |
856 | bool bin, bax, pin, pax, veq, all = (opm == 2); |
857 | |
858 | if (Ndif == 0) // Array is empty |
859 | // Return true for ALL because it means that there are no item that |
860 | // does not verify the condition, which is true indeed. |
861 | // Return false for ANY because true means that there is at least |
862 | // one item that verifies the condition, which is false. |
863 | return (all) ? 2 : -2; |
864 | else if (opc == OP_EQ && all && Ndif > 1) |
865 | return -2; |
866 | else if (opc == OP_NE && !all && Ndif > 1) |
867 | return 2; |
868 | // else if (Ndif == 1) |
869 | // all = false; |
870 | |
871 | // veq is true when all values in the block are equal |
872 | switch (Type) { |
873 | case TYPE_STRING: veq = (Vblp->IsCi()) |
874 | ? !stricmp((char*)minp, (char*)maxp) |
875 | : !strcmp((char*)minp, (char*)maxp); break; |
876 | case TYPE_SHORT: veq = *(short*)minp == *(short*)maxp; break; |
877 | case TYPE_INT: veq = *(int*)minp == *(int*)maxp; break; |
878 | case TYPE_DOUBLE: veq = *(double*)minp == *(double*)maxp; break; |
879 | default: veq = false; // Error ? |
880 | } // endswitch type |
881 | |
882 | if (!s) |
883 | Bot = -1; |
884 | |
885 | Top = Ndif; // Reset Top at top of list |
886 | Value->SetBinValue(maxp); |
887 | Top = (bax = Find(Value)) ? X + 1 : Sup; |
888 | |
889 | if (bax) { |
890 | if (opc == OP_EQ) |
891 | return (veq) ? 1 : 0; |
892 | else if (opc == OP_NE) |
893 | return (veq) ? -1 : 0; |
894 | |
895 | if (X == 0) switch (opc) { |
896 | // Max value is equal to min list value |
897 | case OP_LE: return 1; break; |
898 | case OP_LT: return (veq) ? -1 : 0; break; |
899 | case OP_GE: return (veq) ? 1 : 0; break; |
900 | case OP_GT: return -1; break; |
901 | } // endswitch opc |
902 | |
903 | pax = (opc == OP_GE) ? (X < Ndif - 1) : true; |
904 | } else if (Inf == Bot) { |
905 | // Max value is smaller than min list value |
906 | return (opc == OP_LT || opc == OP_LE || opc == OP_NE) ? 1 : -1; |
907 | } else |
908 | pax = (Sup < Ndif); // True if max value is inside the list value |
909 | |
910 | if (!veq) { |
911 | Value->SetBinValue(minp); |
912 | bin = Find(Value); |
913 | } else |
914 | bin = bax; |
915 | |
916 | Bot = (bin) ? X - 1 : Inf; |
917 | |
918 | if (bin) { |
919 | if (opc == OP_EQ || opc == OP_NE) |
920 | return 0; |
921 | |
922 | if (X == Ndif - 1) switch (opc) { |
923 | case OP_GE: return (s) ? 2 : 1; break; |
924 | case OP_GT: return (veq) ? -1 : 0; break; |
925 | case OP_LE: return (veq) ? 1 : 0; break; |
926 | case OP_LT: return (s) ? -2 : -1; break; |
927 | } // endswitch opc |
928 | |
929 | pin = (opc == OP_LE) ? (X > 0) : true; |
930 | } else if (Sup == Ndif) { |
931 | // Min value is greater than max list value |
932 | if (opc == OP_GT || opc == OP_GE || opc == OP_NE) |
933 | return (s) ? 2 : 1; |
934 | else |
935 | return (s) ? -2 : -1; |
936 | |
937 | } else |
938 | pin = (Inf >= 0); // True if min value is inside the list value |
939 | |
940 | if (Top - Bot <= 1) { |
941 | // No list item between min and max value |
942 | #if defined(_DEBUG) |
943 | assert (!bin && !bax); |
944 | #endif |
945 | switch (opc) { |
946 | case OP_EQ: return -1; break; |
947 | case OP_NE: return 1; break; |
948 | default: return (all) ? -1 : 1; break; |
949 | } // endswitch opc |
950 | |
951 | } // endif |
952 | |
953 | #if defined(_DEBUG) |
954 | assert (Ndif > 1); // if Ndif = 1 we should have returned already |
955 | #endif |
956 | |
957 | // At this point, if there are no logical errors in the algorithm, |
958 | // the only possible overlaps between the array and the block are: |
959 | // Array: +-------+ +-------+ +-------+ +-----+ |
960 | // Block: +-----+ +---+ +------+ +--------+ |
961 | // true: pax pin pax pin |
962 | if (all) switch (opc) { |
963 | case OP_GT: |
964 | case OP_GE: return (pax) ? -1 : 0; break; |
965 | case OP_LT: |
966 | case OP_LE: return (pin) ? -1 : 0; break; |
967 | } // endswitch opc |
968 | |
969 | return 0; |
970 | } // end of BlockTest |
971 | |
972 | /***********************************************************************/ |
973 | /* MakeArrayList: Makes a value list from an SQL IN array (in work). */ |
974 | /***********************************************************************/ |
975 | PSZ ARRAY::MakeArrayList(PGLOBAL g) |
976 | { |
977 | char *p, *tp; |
978 | int i; |
979 | size_t z, len = 2; |
980 | |
981 | if (Type == TYPE_LIST) |
982 | return (PSZ)("(?" "?" "?)" ); // To be implemented |
983 | |
984 | z = MY_MAX(24, GetTypeSize(Type, Len) + 4); |
985 | tp = (char*)PlugSubAlloc(g, NULL, z); |
986 | |
987 | for (i = 0; i < Nval; i++) { |
988 | Value->SetValue_pvblk(Vblp, i); |
989 | Value->Prints(g, tp, z); |
990 | len += strlen(tp); |
991 | } // enfor i |
992 | |
993 | if (trace(1)) |
994 | htrc("Arraylist: len=%d\n" , len); |
995 | |
996 | p = (char *)PlugSubAlloc(g, NULL, len); |
997 | strcpy(p, "(" ); |
998 | |
999 | for (i = 0; i < Nval;) { |
1000 | Value->SetValue_pvblk(Vblp, i); |
1001 | Value->Prints(g, tp, z); |
1002 | strcat(p, tp); |
1003 | strcat(p, (++i == Nval) ? ")" : "," ); |
1004 | } // enfor i |
1005 | |
1006 | if (trace(1)) |
1007 | htrc("Arraylist: newlen=%d\n" , strlen(p)); |
1008 | |
1009 | return p; |
1010 | } // end of MakeArrayList |
1011 | |
1012 | /***********************************************************************/ |
1013 | /* Make file output of ARRAY contents. */ |
1014 | /***********************************************************************/ |
1015 | void ARRAY::Printf(PGLOBAL g, FILE *f, uint n) |
1016 | { |
1017 | char m[64]; |
1018 | int lim = MY_MIN(Nval,10); |
1019 | |
1020 | memset(m, ' ', n); // Make margin string |
1021 | m[n] = '\0'; |
1022 | fprintf(f, "%sARRAY: type=%d\n" , m, Type); |
1023 | memset(m, ' ', n + 2); // Make margin string |
1024 | m[n] = '\0'; |
1025 | |
1026 | if (Type != TYPE_LIST) { |
1027 | fprintf(f, "%sblock=%p numval=%d\n" , m, Valblk->GetMemp(), Nval); |
1028 | |
1029 | if (Vblp) |
1030 | for (int i = 0; i < lim; i++) { |
1031 | Value->SetValue_pvblk(Vblp, i); |
1032 | Value->Printf(g, f, n+4); |
1033 | } // endfor i |
1034 | |
1035 | } else |
1036 | fprintf(f, "%sVALLST: numval=%d\n" , m, Nval); |
1037 | |
1038 | } // end of Printf |
1039 | |
1040 | /***********************************************************************/ |
1041 | /* Make string output of ARRAY contents. */ |
1042 | /***********************************************************************/ |
1043 | void ARRAY::Prints(PGLOBAL, char *ps, uint z) |
1044 | { |
1045 | if (z < 16) |
1046 | return; |
1047 | |
1048 | sprintf(ps, "ARRAY: type=%d\n" , Type); |
1049 | // More to be implemented later |
1050 | } // end of Prints |
1051 | |
1052 | /* -------------------------- Class MULAR ---------------------------- */ |
1053 | |
1054 | /***********************************************************************/ |
1055 | /* MULAR public constructor. */ |
1056 | /***********************************************************************/ |
1057 | MULAR::MULAR(PGLOBAL g, int n) : CSORT(false) |
1058 | { |
1059 | Narray = n; |
1060 | Pars = (PARRAY*)PlugSubAlloc(g, NULL, n * sizeof(PARRAY)); |
1061 | } // end of MULAR constructor |
1062 | |
1063 | /***********************************************************************/ |
1064 | /* MULAR: Compare routine multiple arrays. */ |
1065 | /***********************************************************************/ |
1066 | int MULAR::Qcompare(int *i1, int *i2) |
1067 | { |
1068 | register int i, n = 0; |
1069 | |
1070 | for (i = 0; i < Narray; i++) |
1071 | if ((n = Pars[i]->Qcompare(i1, i2))) |
1072 | break; |
1073 | |
1074 | return n; |
1075 | } // end of Qcompare |
1076 | |
1077 | /***********************************************************************/ |
1078 | /* Sort and eliminate distinct values from multiple arrays. */ |
1079 | /* Note: this is done by making a sorted index on distinct values. */ |
1080 | /* Returns false if Ok or true in case of error. */ |
1081 | /***********************************************************************/ |
1082 | bool MULAR::Sort(PGLOBAL g) |
1083 | { |
1084 | int i, j, k, n, nval, ndif; |
1085 | |
1086 | // All arrays must have the same number of values |
1087 | nval = Pars[0]->Nval; |
1088 | |
1089 | for (n = 1; n < Narray; n++) |
1090 | if (Pars[n]->Nval != nval) { |
1091 | strcpy(g->Message, MSG(BAD_ARRAY_VAL)); |
1092 | return true; |
1093 | } // endif nval |
1094 | |
1095 | // Prepare non conservative sort with offet values |
1096 | Index.Size = nval * sizeof(int); |
1097 | |
1098 | if (!PlgDBalloc(g, NULL, Index)) |
1099 | goto error; |
1100 | |
1101 | Offset.Size = (nval + 1) * sizeof(int); |
1102 | |
1103 | if (!PlgDBalloc(g, NULL, Offset)) |
1104 | goto error; |
1105 | |
1106 | // Call the sort program, it returns the number of distinct values |
1107 | ndif = Qsort(g, nval); |
1108 | |
1109 | if (ndif < 0) |
1110 | goto error; |
1111 | |
1112 | // Use the sort index to reorder the data in storage so it will |
1113 | // be physically sorted and Index can be removed. |
1114 | for (i = 0; i < nval; i++) { |
1115 | if (Pex[i] == i || Pex[i] == nval) |
1116 | // Already placed or already moved |
1117 | continue; |
1118 | |
1119 | for (n = 0; n < Narray; n++) |
1120 | Pars[n]->Save(i); |
1121 | |
1122 | for (j = i;; j = k) { |
1123 | k = Pex[j]; |
1124 | Pex[j] = nval; // Mark position as set |
1125 | |
1126 | if (k == i) { |
1127 | for (n = 0; n < Narray; n++) |
1128 | Pars[n]->Restore(j); |
1129 | |
1130 | break; // end of loop |
1131 | } else |
1132 | for (n = 0; n < Narray; n++) |
1133 | Pars[n]->Move(j, k); |
1134 | |
1135 | } // endfor j |
1136 | |
1137 | } // endfor i |
1138 | |
1139 | // Reduce the size of the To_Val array if ndif < nval |
1140 | if (ndif < nval) { |
1141 | for (i = 1; i < ndif; i++) |
1142 | if (i != Pof[i]) |
1143 | break; |
1144 | |
1145 | for (; i < ndif; i++) |
1146 | for (n = 0; n < Narray; n++) |
1147 | Pars[n]->Move(i, Pof[i]); |
1148 | |
1149 | for (n = 0; n < Narray; n++) { |
1150 | Pars[n]->Nval = ndif; |
1151 | Pars[n]->Size = ndif; |
1152 | Pars[n]->Valblk->ReAllocate(g, ndif); |
1153 | } // endfor n |
1154 | |
1155 | } // endif ndif |
1156 | |
1157 | // Index and Offset are not used anymore |
1158 | PlgDBfree(Index); |
1159 | PlgDBfree(Offset); |
1160 | |
1161 | for (n = 0; n < Narray; n++) { |
1162 | Pars[n]->Bot = -1; // For non optimized search |
1163 | Pars[n]->Top = ndif; // Find searches the whole array. |
1164 | } // endfor n |
1165 | |
1166 | return false; |
1167 | |
1168 | error: |
1169 | PlgDBfree(Index); |
1170 | PlgDBfree(Offset); |
1171 | return true; |
1172 | } // end of Sort |
1173 | |