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/***********************************************************************/
57extern MBLOCK Nmblk; /* Used to initialize MBLOCK's */
58
59/***********************************************************************/
60/* External functions. */
61/***********************************************************************/
62BYTE OpBmp(PGLOBAL g, OPVAL opc);
63void EncodeValue(int *lp, char *strp, int n);
64PARRAY MakeValueArray(PGLOBAL g, PPARM pp); // avoid gcc warning
65
66/***********************************************************************/
67/* MakeValueArray: Makes a value array from a value list. */
68/***********************************************************************/
69PARRAY 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/***********************************************************************/
143ARRAY::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/***********************************************************************/
199ARRAY::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/***********************************************************************/
239ARRAY::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/***********************************************************************/
272void 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/***********************************************************************/
283bool 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/***********************************************************************/
302bool 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/***********************************************************************/
319bool 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/***********************************************************************/
338bool 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/***********************************************************************/
357bool 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/***********************************************************************/
375bool 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/***********************************************************************/
394bool 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/***********************************************************************/
412void 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/***********************************************************************/
421bool 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/***********************************************************************/
439int 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/***********************************************************************/
448char *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/***********************************************************************/
458bool 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/***********************************************************************/
490BYTE 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/***********************************************************************/
500bool 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/***********************************************************************/
565bool 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/***********************************************************************/
583int 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/***********************************************************************/
650void 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/***********************************************************************/
658void 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/***********************************************************************/
666void 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/***********************************************************************/
674int 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/***********************************************************************/
682void 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/***********************************************************************/
713bool 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/***********************************************************************/
809void *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/***********************************************************************/
853int 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/***********************************************************************/
975PSZ 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/***********************************************************************/
1015void 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/***********************************************************************/
1043void 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/***********************************************************************/
1057MULAR::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/***********************************************************************/
1066int 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/***********************************************************************/
1082bool 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