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