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