mirror of
https://github.com/MariaDB/server.git
synced 2025-01-16 12:02:42 +01:00
5ae2656b69
commit c545d544378483bf68c4fb713cd4ec872d0df2c4 Author: Olivier Bertrand <bertrandop@gmail.com> Date: Tue Mar 27 23:13:14 2018 +0200 - Fix MDEV-15577 CONNECT engine JDBC remote index prevents UPDATE Fixed in TDBJDBC::OpenDB because query can be null for updates modified: storage/connect/tabjdbc.cpp commit c4f1fe2e8e74566e9c3296f435448d9f5c718b23 Author: Olivier Bertrand <bertrandop@gmail.com> Date: Wed Mar 14 12:21:17 2018 +0100 - Fix MDEV-15429 CONNECT engine JDBC handling Postgresql UUID type Also handle Postgresql sending type VARCHAR for TEXT column and setting length to b x7FFFFFF when the length is unknown. modified: storage/connect/Client.java modified: storage/connect/JavaWrappers.jar modified: storage/connect/JdbcInterface.java modified: storage/connect/PostgresqlInterface.java modified: storage/connect/global.h modified: storage/connect/ha_connect.cc modified: storage/connect/jdbconn.cpp modified: storage/connect/jdbconn.h modified: storage/connect/mysql-test/connect/r/jdbc_postgresql.result modified: storage/connect/mysql-test/connect/t/jdbc_postgresql.test modified: storage/connect/mysql-test/connect/t/jdbconn.inc modified: storage/connect/plgdbsem.h modified: storage/connect/tabjdbc.cpp modified: storage/connect/tabjdbc.h added: storage/connect/mysql-test/connect/std_data/JavaWrappers.jar commit 3068bb4882a316c6c32c624609db2bf24ae3ad40 Author: Olivier Bertrand <bertrandop@gmail.com> Date: Mon Feb 26 19:40:27 2018 +0100 Revert to old cmake file to avoid compile error commit da8c1c0bd6a327fd03bd321be3170f468122ef67 Author: Olivier Bertrand <bertrandop@gmail.com> Date: Mon Feb 26 16:13:39 2018 +0100 - Remove warning on not used tabtyp variable in connect_assisted_discovery modified: storage/connect/ha_connect.cc - Fix a bug causing CONNECT to loop when expanding a JSON column when the expanded column value is null or void array. - Adding the FullArray option to JSON tables. - Skipping expanded JSON lines when the expanded column value is null. modified: storage/connect/connect.cc modified: storage/connect/tabdos.h modified: storage/connect/tabjson.cpp modified: storage/connect/tabjson.h - Fix MDEV-13353 No file privilege for ODBC, JDBC, MONGO, MAC, WMI file types. modified: storage/connect/ha_connect.cc - Make some Json UDFs to accept a non JSON item as 1st parameter. modified: storage/connect/json.cpp modified: storage/connect/json.h modified: storage/connect/jsonudf.cpp modified: storage/connect/jsonudf.h - Update Json UDF tests to cope with above changes. modified: storage/connect/mysql-test/connect/r/json_udf.result modified: storage/connect/mysql-test/connect/r/json_udf_bin.result modified: storage/connect/mysql-test/connect/r/vcol.result modified: storage/connect/mysql-test/connect/t/json_udf.test modified: storage/connect/mysql-test/connect/t/vcol.test - Fix some compiler warning treated as error PlugSubAlloc no more exported because it does throw. modified: storage/connect/global.h modified: storage/connect/ha_connect.cc modified: storage/connect/jsonudf.cpp modified: storage/connect/tabjson.cpp modified: storage/connect/tabjson.h - Other files modified (?) when going back to wrong merge modified: storage/connect/CMakeLists.txt modified: storage/connect/array.cpp modified: storage/connect/colblk.cpp modified: storage/connect/connect.cc modified: storage/connect/csort.cpp modified: storage/connect/domdoc.cpp modified: storage/connect/filamap.cpp modified: storage/connect/filamgz.cpp modified: storage/connect/filamtxt.cpp modified: storage/connect/filamzip.cpp modified: storage/connect/filter.cpp modified: storage/connect/fmdlex.c modified: storage/connect/jdbconn.cpp modified: storage/connect/macutil.cpp modified: storage/connect/myconn.cpp modified: storage/connect/odbconn.cpp modified: storage/connect/plgdbutl.cpp modified: storage/connect/plugutil.cpp modified: storage/connect/preparse.h modified: storage/connect/rcmsg.c modified: storage/connect/rcmsg.h modified: storage/connect/reldef.cpp modified: storage/connect/tabdos.cpp modified: storage/connect/tabfmt.cpp modified: storage/connect/tabmac.cpp modified: storage/connect/tabmul.cpp modified: storage/connect/tabmysql.cpp modified: storage/connect/tabmysql.h modified: storage/connect/tabodbc.cpp modified: storage/connect/tabtbl.cpp modified: storage/connect/tabxml.cpp modified: storage/connect/value.cpp modified: storage/connect/xobject.cpp commit 5095628b31ed8c94bd7c794c6b0162894a9040b1 Author: Olivier Bertrand <bertrandop@gmail.com> Date: Tue Feb 13 14:29:00 2018 +0100 - Fix a bug causing CONNECT to loop when expanding a JSON column when the expanded column value is null or void array. - Adding the FullArray option to JSON tables. - Skipping expanded JSON lines when the expanded column value is null. modified: storage/connect/connect.cc modified: storage/connect/tabdos.h modified: storage/connect/tabjson.cpp modified: storage/connect/tabjson.h commit 9896174328da42016735630c893fd09eb128b48e Author: Olivier Bertrand <bertrandop@gmail.com> Date: Fri Feb 2 15:48:57 2018 +0100 - Remove warning on not used tabtyp variable in connect_assisted_discovery modified: storage/connect/ha_connect.cc commit d7e254c05f88173eb04d8dafc813013eb42ccdbe Author: Olivier Bertrand <bertrandop@gmail.com> Date: Wed Jan 31 14:42:45 2018 +0100 - Use delayed load for the MongoDB C Drive on Windows modified: storage/connect/CMakeLists.txt modified: storage/connect/cmgoconn.cpp modified: storage/connect/ha_connect.cc - Add FORCE to the connect_type_conv enum values This will translate binary values to TYPE_STRING modified: storage/connect/checklvl.h modified: storage/connect/ha_connect.cc modified: storage/connect/odbconn.cpp - Change the connect_xtrace variable to from int to set modified: storage/connect/array.cpp modified: storage/connect/blkfil.cpp modified: storage/connect/block.h modified: storage/connect/cmgoconn.cpp modified: storage/connect/colblk.cpp modified: storage/connect/connect.cc modified: storage/connect/filamap.cpp modified: storage/connect/filamdbf.cpp modified: storage/connect/filamfix.cpp modified: storage/connect/filamgz.cpp modified: storage/connect/filamtxt.cpp modified: storage/connect/filamvct.cpp modified: storage/connect/filamzip.cpp modified: storage/connect/filter.cpp modified: storage/connect/global.h modified: storage/connect/ha_connect.cc modified: storage/connect/inihandl.cpp modified: storage/connect/javaconn.cpp modified: storage/connect/jdbconn.cpp modified: storage/connect/jmgfam.cpp modified: storage/connect/jmgoconn.cpp modified: storage/connect/json.cpp modified: storage/connect/jsonudf.cpp modified: storage/connect/libdoc.cpp modified: storage/connect/mongo.cpp modified: storage/connect/mycat.cc modified: storage/connect/myconn.cpp modified: storage/connect/odbconn.cpp modified: storage/connect/plgdbutl.cpp modified: storage/connect/plugutil.cpp modified: storage/connect/reldef.cpp modified: storage/connect/tabcol.cpp modified: storage/connect/tabdos.cpp modified: storage/connect/tabext.cpp modified: storage/connect/tabfix.cpp modified: storage/connect/tabfmt.cpp modified: storage/connect/tabjdbc.cpp modified: storage/connect/tabjson.cpp modified: storage/connect/table.cpp modified: storage/connect/tabmul.cpp modified: storage/connect/tabmysql.cpp modified: storage/connect/tabodbc.cpp modified: storage/connect/tabpivot.cpp modified: storage/connect/tabsys.cpp modified: storage/connect/tabtbl.cpp modified: storage/connect/tabutil.cpp modified: storage/connect/tabvct.cpp modified: storage/connect/tabwmi.cpp modified: storage/connect/tabxml.cpp modified: storage/connect/user_connect.cc modified: storage/connect/valblk.cpp modified: storage/connect/value.cpp modified: storage/connect/xindex.cpp - Restore connect_enable_mongo variable (but undocumented) modified: storage/connect/ha_connect.cc modified: storage/connect/mycat.cc modified: storage/connect/mysql-test/connect/r/json_java_2.result modified: storage/connect/mysql-test/connect/r/json_java_3.result modified: storage/connect/mysql-test/connect/r/json_mongo_c.result modified: storage/connect/mysql-test/connect/r/mongo_c.result modified: storage/connect/mysql-test/connect/r/mongo_java_2.result modified: storage/connect/mysql-test/connect/r/mongo_java_3.result modified: storage/connect/mysql-test/connect/r/tbl_thread.result modified: storage/connect/mysql-test/connect/t/mongo.inc modified: storage/connect/mysql-test/connect/t/mongo_test.inc modified: storage/connect/mysql-test/connect/t/tbl_thread.test
1172 lines
36 KiB
C++
1172 lines
36 KiB
C++
/************* Array C++ Functions Source Code File (.CPP) *************/
|
|
/* Name: ARRAY.CPP Version 2.3 */
|
|
/* */
|
|
/* (C) Copyright to the author Olivier BERTRAND 2005-2017 */
|
|
/* */
|
|
/* This file contains the XOBJECT derived class ARRAY functions. */
|
|
/* ARRAY is used for elaborate type of processing, such as sorting */
|
|
/* and dichotomic search (Find). This new version does not use sub */
|
|
/* classes anymore for the different types but relies entirely on the */
|
|
/* functionalities provided by the VALUE and VALBLK classes. */
|
|
/* Currently the only supported types are STRING, SHORT, int, DATE, */
|
|
/* TOKEN, DOUBLE, and Compressed Strings. */
|
|
/***********************************************************************/
|
|
|
|
/***********************************************************************/
|
|
/* Include relevant MariaDB header file. */
|
|
/***********************************************************************/
|
|
#include "my_global.h"
|
|
#include "sql_class.h"
|
|
//#include "sql_time.h"
|
|
|
|
#if defined(__WIN__)
|
|
//#include <windows.h>
|
|
#else // !__WIN__
|
|
#include <string.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <stdint.h> // for uintprt_h
|
|
#endif // !__WIN__
|
|
|
|
/***********************************************************************/
|
|
/* Include required application header files */
|
|
/* global.h is header containing all global Plug declarations. */
|
|
/* plgdbsem.h is header containing the DB applic. declarations. */
|
|
/* xobject.h is header containing XOBJECT derived classes declares. */
|
|
/***********************************************************************/
|
|
#include "global.h"
|
|
#include "plgdbsem.h"
|
|
#include "xtable.h"
|
|
#include "array.h"
|
|
//#include "select.h"
|
|
//#include "query.h"
|
|
//#include "token.h"
|
|
|
|
/***********************************************************************/
|
|
/* Macro definitions. */
|
|
/***********************************************************************/
|
|
#if defined(_DEBUG)
|
|
#define ASSERT(B) assert(B);
|
|
#else
|
|
#define ASSERT(B)
|
|
#endif
|
|
|
|
/***********************************************************************/
|
|
/* DB static external variables. */
|
|
/***********************************************************************/
|
|
extern MBLOCK Nmblk; /* Used to initialize MBLOCK's */
|
|
|
|
/***********************************************************************/
|
|
/* External functions. */
|
|
/***********************************************************************/
|
|
BYTE OpBmp(PGLOBAL g, OPVAL opc);
|
|
void EncodeValue(int *lp, char *strp, int n);
|
|
PARRAY MakeValueArray(PGLOBAL g, PPARM pp); // avoid gcc warning
|
|
|
|
/***********************************************************************/
|
|
/* MakeValueArray: Makes a value array from a value list. */
|
|
/***********************************************************************/
|
|
PARRAY MakeValueArray(PGLOBAL g, PPARM pp)
|
|
{
|
|
int n, valtyp = 0;
|
|
size_t len = 0;
|
|
PARRAY par;
|
|
PPARM parmp;
|
|
|
|
if (!pp)
|
|
return NULL;
|
|
|
|
/*********************************************************************/
|
|
/* New version with values coming in a list. */
|
|
/*********************************************************************/
|
|
if ((valtyp = pp->Type) != TYPE_STRING)
|
|
len = 1;
|
|
|
|
if (trace(1))
|
|
htrc("valtyp=%d len=%d\n", valtyp, len);
|
|
|
|
/*********************************************************************/
|
|
/* Firstly check the list and count the number of values in it. */
|
|
/*********************************************************************/
|
|
for (n = 0, parmp = pp; parmp; n++, parmp = parmp->Next)
|
|
if (parmp->Type != valtyp) {
|
|
sprintf(g->Message, MSG(BAD_PARAM_TYPE), "MakeValueArray", parmp->Type);
|
|
return NULL;
|
|
} else if (valtyp == TYPE_STRING)
|
|
len = MY_MAX(len, strlen((char*)parmp->Value));
|
|
|
|
/*********************************************************************/
|
|
/* Make an array object with one block of the proper size. */
|
|
/*********************************************************************/
|
|
par = new(g) ARRAY(g, valtyp, n, (int)len);
|
|
|
|
if (par->GetResultType() == TYPE_ERROR)
|
|
return NULL; // Memory allocation error in ARRAY
|
|
|
|
/*********************************************************************/
|
|
/* All is right now, fill the array block. */
|
|
/*********************************************************************/
|
|
for (parmp = pp; parmp; parmp = parmp->Next)
|
|
switch (valtyp) {
|
|
case TYPE_STRING:
|
|
par->AddValue(g, (PSZ)parmp->Value);
|
|
break;
|
|
case TYPE_SHORT:
|
|
par->AddValue(g, *(short*)parmp->Value);
|
|
break;
|
|
case TYPE_INT:
|
|
par->AddValue(g, *(int*)parmp->Value);
|
|
break;
|
|
case TYPE_DOUBLE:
|
|
par->AddValue(g, *(double*)parmp->Value);
|
|
break;
|
|
case TYPE_PCHAR:
|
|
par->AddValue(g, parmp->Value);
|
|
break;
|
|
case TYPE_VOID:
|
|
// Integer stored inside pp->Value
|
|
par->AddValue(g, parmp->Intval);
|
|
break;
|
|
} // endswitch valtyp
|
|
|
|
/*********************************************************************/
|
|
/* Send back resulting array. */
|
|
/*********************************************************************/
|
|
return par;
|
|
} // end of MakeValueArray
|
|
|
|
/* -------------------------- Class ARRAY ---------------------------- */
|
|
|
|
/***********************************************************************/
|
|
/* ARRAY public constructor. */
|
|
/***********************************************************************/
|
|
ARRAY::ARRAY(PGLOBAL g, int type, int size, int length, int prec)
|
|
: CSORT(false)
|
|
{
|
|
Nval = 0;
|
|
Ndif = 0;
|
|
Bot = 0;
|
|
Top = 0;
|
|
Size = size;
|
|
Type = type;
|
|
Xsize = -1;
|
|
Len = 1;
|
|
|
|
switch (type) {
|
|
case TYPE_STRING:
|
|
Len = length;
|
|
/* fall through */
|
|
case TYPE_SHORT:
|
|
case TYPE_INT:
|
|
case TYPE_DOUBLE:
|
|
case TYPE_PCHAR:
|
|
Type = type;
|
|
break;
|
|
case TYPE_VOID:
|
|
Type = TYPE_INT;
|
|
break;
|
|
#if 0
|
|
case TYPE_TOKEN:
|
|
break;
|
|
case TYPE_LIST:
|
|
Len = 0;
|
|
prec = length;
|
|
break;
|
|
#endif // 0
|
|
default: // This is illegal an causes an ill formed array building
|
|
sprintf(g->Message, MSG(BAD_ARRAY_TYPE), type);
|
|
Type = TYPE_ERROR;
|
|
return;
|
|
} // endswitch type
|
|
|
|
Valblk = new(g) MBVALS;
|
|
|
|
if (!(Vblp = Valblk->Allocate(g, Type, Len, prec, Size)))
|
|
Type = TYPE_ERROR;
|
|
else if (!Valblk->GetMemp() && Type != TYPE_LIST)
|
|
// The error message was built by PlgDBalloc
|
|
Type = TYPE_ERROR;
|
|
else if (type != TYPE_PCHAR)
|
|
Value = AllocateValue(g, type, Len, prec);
|
|
|
|
Constant = true;
|
|
} // end of ARRAY constructor
|
|
|
|
#if 0
|
|
/***********************************************************************/
|
|
/* ARRAY public constructor from a QUERY. */
|
|
/***********************************************************************/
|
|
ARRAY::ARRAY(PGLOBAL g, PQUERY qryp) : CSORT(false)
|
|
{
|
|
Type = qryp->GetColType(0);
|
|
Nval = qryp->GetNblin();
|
|
Ndif = 0;
|
|
Bot = 0;
|
|
Top = 0;
|
|
Size = Nval;
|
|
Xsize = -1;
|
|
Len = qryp->GetColLength(0);
|
|
X = Inf = Sup = 0;
|
|
Correlated = false;
|
|
|
|
switch (Type) {
|
|
case TYPE_STRING:
|
|
case TYPE_SHORT:
|
|
case TYPE_INT:
|
|
case TYPE_DATE:
|
|
case TYPE_DOUBLE:
|
|
// case TYPE_TOKEN:
|
|
// case TYPE_LIST:
|
|
// Valblk = qryp->GetCol(0)->Result;
|
|
// Vblp = qryp->GetColBlk(0);
|
|
// Value = qryp->GetColValue(0);
|
|
// break;
|
|
default: // This is illegal an causes an ill formed array building
|
|
sprintf(g->Message, MSG(BAD_ARRAY_TYPE), Type);
|
|
Type = TYPE_ERROR;
|
|
} // endswitch type
|
|
|
|
if (!Valblk || (!Valblk->GetMemp() && Type != TYPE_LIST))
|
|
// The error message was built by ???
|
|
Type = TYPE_ERROR;
|
|
|
|
Constant = true;
|
|
} // end of ARRAY constructor
|
|
|
|
/***********************************************************************/
|
|
/* ARRAY constructor from a TYPE_LIST subarray. */
|
|
/***********************************************************************/
|
|
ARRAY::ARRAY(PGLOBAL g, PARRAY par, int k) : CSORT(false)
|
|
{
|
|
int prec;
|
|
LSTBLK *lp;
|
|
|
|
if (par->Type != TYPE_LIST) {
|
|
Type = TYPE_ERROR;
|
|
return;
|
|
} // endif Type
|
|
|
|
lp = (LSTBLK*)par->Vblp;
|
|
|
|
Nval = par->Nval;
|
|
Ndif = 0;
|
|
Bot = 0;
|
|
Top = 0;
|
|
Size = par->Size;
|
|
Xsize = -1;
|
|
|
|
Valblk = lp->Mbvk[k];
|
|
Vblp = Valblk->Vblk;
|
|
Type = Vblp->GetType();
|
|
Len = (Type == TYPE_STRING) ? Vblp->GetVlen() : 0;
|
|
prec = (Type == TYPE_FLOAT) ? 2 : 0;
|
|
Value = AllocateValue(g, Type, Len, prec, NULL);
|
|
Constant = true;
|
|
} // end of ARRAY constructor
|
|
|
|
/***********************************************************************/
|
|
/* Empty: reset the array for a new use (correlated queries). */
|
|
/* Note: this is temporary as correlated queries will not use arrays */
|
|
/* anymore with future optimized algorithms. */
|
|
/***********************************************************************/
|
|
void ARRAY::Empty(void)
|
|
{
|
|
assert(Correlated);
|
|
Nval = Ndif = 0;
|
|
Bot = Top = X = Inf = Sup = 0;
|
|
} // end of Empty
|
|
#endif // 0
|
|
|
|
/***********************************************************************/
|
|
/* Add a string element to an array. */
|
|
/***********************************************************************/
|
|
bool ARRAY::AddValue(PGLOBAL g, PSZ strp)
|
|
{
|
|
if (Type != TYPE_STRING) {
|
|
sprintf(g->Message, MSG(ADD_BAD_TYPE), GetTypeName(Type), "CHAR");
|
|
return true;
|
|
} // endif Type
|
|
|
|
if (trace(1))
|
|
htrc(" adding string(%d): '%s'\n", Nval, strp);
|
|
|
|
//Value->SetValue_psz(strp);
|
|
//Vblp->SetValue(valp, Nval++);
|
|
Vblp->SetValue(strp, Nval++);
|
|
return false;
|
|
} // end of AddValue
|
|
|
|
/***********************************************************************/
|
|
/* Add a char pointer element to an array. */
|
|
/***********************************************************************/
|
|
bool ARRAY::AddValue(PGLOBAL g, void *p)
|
|
{
|
|
if (Type != TYPE_PCHAR) {
|
|
sprintf(g->Message, MSG(ADD_BAD_TYPE), GetTypeName(Type), "PCHAR");
|
|
return true;
|
|
} // endif Type
|
|
|
|
if (trace(1))
|
|
htrc(" adding pointer(%d): %p\n", Nval, p);
|
|
|
|
Vblp->SetValue((PSZ)p, Nval++);
|
|
return false;
|
|
} // end of AddValue
|
|
|
|
/***********************************************************************/
|
|
/* Add a short integer element to an array. */
|
|
/***********************************************************************/
|
|
bool ARRAY::AddValue(PGLOBAL g, short n)
|
|
{
|
|
if (Type != TYPE_SHORT) {
|
|
sprintf(g->Message, MSG(ADD_BAD_TYPE), GetTypeName(Type), "SHORT");
|
|
return true;
|
|
} // endif Type
|
|
|
|
if (trace(1))
|
|
htrc(" adding SHORT(%d): %hd\n", Nval, n);
|
|
|
|
//Value->SetValue(n);
|
|
//Vblp->SetValue(valp, Nval++);
|
|
Vblp->SetValue(n, Nval++);
|
|
return false;
|
|
} // end of AddValue
|
|
|
|
/***********************************************************************/
|
|
/* Add an integer element to an array. */
|
|
/***********************************************************************/
|
|
bool ARRAY::AddValue(PGLOBAL g, int n)
|
|
{
|
|
if (Type != TYPE_INT) {
|
|
sprintf(g->Message, MSG(ADD_BAD_TYPE), GetTypeName(Type), "INTEGER");
|
|
return true;
|
|
} // endif Type
|
|
|
|
if (trace(1))
|
|
htrc(" adding int(%d): %d\n", Nval, n);
|
|
|
|
//Value->SetValue(n);
|
|
//Vblp->SetValue(valp, Nval++);
|
|
Vblp->SetValue(n, Nval++);
|
|
return false;
|
|
} // end of AddValue
|
|
|
|
/***********************************************************************/
|
|
/* Add a double float element to an array. */
|
|
/***********************************************************************/
|
|
bool ARRAY::AddValue(PGLOBAL g, double d)
|
|
{
|
|
if (Type != TYPE_DOUBLE) {
|
|
sprintf(g->Message, MSG(ADD_BAD_TYPE), GetTypeName(Type), "DOUBLE");
|
|
return true;
|
|
} // endif Type
|
|
|
|
if (trace(1))
|
|
htrc(" adding float(%d): %lf\n", Nval, d);
|
|
|
|
Value->SetValue(d);
|
|
Vblp->SetValue(Value, Nval++);
|
|
return false;
|
|
} // end of AddValue
|
|
|
|
/***********************************************************************/
|
|
/* Add the value of a XOBJECT block to an array. */
|
|
/***********************************************************************/
|
|
bool ARRAY::AddValue(PGLOBAL g, PXOB xp)
|
|
{
|
|
if (Type != xp->GetResultType()) {
|
|
sprintf(g->Message, MSG(ADD_BAD_TYPE),
|
|
GetTypeName(xp->GetResultType()), GetTypeName(Type));
|
|
return true;
|
|
} // endif Type
|
|
|
|
if (trace(1))
|
|
htrc(" adding (%d) from xp=%p\n", Nval, xp);
|
|
|
|
//AddValue(xp->GetValue());
|
|
Vblp->SetValue(xp->GetValue(), Nval++);
|
|
return false;
|
|
} // end of AddValue
|
|
|
|
/***********************************************************************/
|
|
/* Add a value to an array. */
|
|
/***********************************************************************/
|
|
bool ARRAY::AddValue(PGLOBAL g, PVAL vp)
|
|
{
|
|
if (Type != vp->GetType()) {
|
|
sprintf(g->Message, MSG(ADD_BAD_TYPE),
|
|
GetTypeName(vp->GetType()), GetTypeName(Type));
|
|
return true;
|
|
} // endif Type
|
|
|
|
if (trace(1))
|
|
htrc(" adding (%d) from vp=%p\n", Nval, vp);
|
|
|
|
Vblp->SetValue(vp, Nval++);
|
|
return false;
|
|
} // end of AddValue
|
|
|
|
/***********************************************************************/
|
|
/* Retrieve the nth value of the array. */
|
|
/***********************************************************************/
|
|
void ARRAY::GetNthValue(PVAL valp, int n)
|
|
{
|
|
valp->SetValue_pvblk(Vblp, n);
|
|
} // end of GetNthValue
|
|
|
|
#if 0
|
|
/***********************************************************************/
|
|
/* Retrieve the nth subvalue of a list array. */
|
|
/***********************************************************************/
|
|
bool ARRAY::GetSubValue(PGLOBAL g, PVAL valp, int *kp)
|
|
{
|
|
PVBLK vblp;
|
|
|
|
if (Type != TYPE_LIST) {
|
|
sprintf(g->Message, MSG(NO_SUB_VAL), Type);
|
|
return true;
|
|
} // endif Type
|
|
|
|
vblp = ((LSTBLK*)Vblp)->Mbvk[kp[0]]->Vblk;
|
|
valp->SetValue_pvblk(vblp, kp[1]);
|
|
return false;
|
|
} // end of GetSubValue
|
|
#endif // 0
|
|
|
|
/***********************************************************************/
|
|
/* Return the nth value of an integer array. */
|
|
/***********************************************************************/
|
|
int ARRAY::GetIntValue(int n)
|
|
{
|
|
assert (Type == TYPE_INT);
|
|
return Vblp->GetIntValue(n);
|
|
} // end of GetIntValue
|
|
|
|
/***********************************************************************/
|
|
/* Return the nth value of a STRING array. */
|
|
/***********************************************************************/
|
|
char *ARRAY::GetStringValue(int n)
|
|
{
|
|
assert (Type == TYPE_STRING || Type == TYPE_PCHAR);
|
|
return Vblp->GetCharValue(n);
|
|
} // end of GetStringValue
|
|
|
|
/***********************************************************************/
|
|
/* Find whether a value is in an array. */
|
|
/* Provide a conversion limited to the Value limitation. */
|
|
/***********************************************************************/
|
|
bool ARRAY::Find(PVAL valp)
|
|
{
|
|
register int n;
|
|
PVAL vp;
|
|
|
|
if (Type != valp->GetType()) {
|
|
Value->SetValue_pval(valp);
|
|
vp = Value;
|
|
} else
|
|
vp = valp;
|
|
|
|
Inf = Bot, Sup = Top;
|
|
|
|
while (Sup - Inf > 1) {
|
|
X = (Inf + Sup) >> 1;
|
|
n = Vblp->CompVal(vp, X);
|
|
|
|
if (n < 0)
|
|
Sup = X;
|
|
else if (n > 0)
|
|
Inf = X;
|
|
else
|
|
return true;
|
|
|
|
} // endwhile
|
|
|
|
return false;
|
|
} // end of Find
|
|
|
|
/***********************************************************************/
|
|
/* ARRAY: Compare routine for a list of values. */
|
|
/***********************************************************************/
|
|
BYTE ARRAY::Vcompare(PVAL vp, int n)
|
|
{
|
|
Value->SetValue_pvblk(Vblp, n);
|
|
return vp->TestValue(Value);
|
|
} // end of Vcompare
|
|
|
|
/***********************************************************************/
|
|
/* Test a filter condition on an array depending on operator and mod. */
|
|
/* Modificator values are 1: ANY (or SOME) and 2: ALL. */
|
|
/***********************************************************************/
|
|
bool ARRAY::FilTest(PGLOBAL g, PVAL valp, OPVAL opc, int opm)
|
|
{
|
|
int i;
|
|
PVAL vp;
|
|
BYTE bt = OpBmp(g, opc);
|
|
int top = Nval - 1;
|
|
|
|
if (top < 0) // Array is empty
|
|
// Return true for ALL because it means that there are no item that
|
|
// does not verify the condition, which is true indeed.
|
|
// Return false for ANY because true means that there is at least
|
|
// one item that verifies the condition, which is false.
|
|
return opm == 2;
|
|
|
|
if (valp) {
|
|
if (Type != valp->GetType()) {
|
|
Value->SetValue_pval(valp);
|
|
vp = Value;
|
|
} else
|
|
vp = valp;
|
|
|
|
} else if (opc != OP_EXIST) {
|
|
sprintf(g->Message, MSG(MISSING_ARG), opc);
|
|
throw (int)TYPE_ARRAY;
|
|
} else // OP_EXIST
|
|
return Nval > 0;
|
|
|
|
if (opc == OP_IN || (opc == OP_EQ && opm == 1))
|
|
return Find(vp);
|
|
else if (opc == OP_NE && opm == 2)
|
|
return !Find(vp);
|
|
else if (opc == OP_EQ && opm == 2)
|
|
return (Ndif == 1) ? !(Vcompare(vp, 0) & bt) : false;
|
|
else if (opc == OP_NE && opm == 1)
|
|
return (Ndif == 1) ? !(Vcompare(vp, 0) & bt) : true;
|
|
|
|
if (Type != TYPE_LIST) {
|
|
if (opc == OP_GT || opc == OP_GE)
|
|
return !(Vcompare(vp, (opm == 1) ? 0 : top) & bt);
|
|
else
|
|
return !(Vcompare(vp, (opm == 2) ? 0 : top) & bt);
|
|
|
|
} // endif Type
|
|
|
|
// Case of TYPE_LIST
|
|
if (opm == 2) {
|
|
for (i = 0; i < Nval; i++)
|
|
if (Vcompare(vp, i) & bt)
|
|
return false;
|
|
|
|
return true;
|
|
} else { // opm == 1
|
|
for (i = 0; i < Nval; i++)
|
|
if (!(Vcompare(vp, i) & bt))
|
|
return true;
|
|
|
|
return false;
|
|
} // endif opm
|
|
|
|
} // end of FilTest
|
|
|
|
/***********************************************************************/
|
|
/* Test whether this array can be converted to TYPE_SHORT. */
|
|
/* Must be called after the array is sorted. */
|
|
/***********************************************************************/
|
|
bool ARRAY::CanBeShort(void)
|
|
{
|
|
int* To_Val = (int*)Valblk->GetMemp();
|
|
|
|
if (Type != TYPE_INT || !Ndif)
|
|
return false;
|
|
|
|
// Because the array is sorted, this is true if all the array
|
|
// int values are in the range of SHORT values
|
|
return (To_Val[0] >= -32768 && To_Val[Nval-1] < 32768);
|
|
} // end of CanBeShort
|
|
|
|
/***********************************************************************/
|
|
/* Convert an array to new numeric type k. */
|
|
/* Note: conversion is always made in ascending order from STRING to */
|
|
/* short to int to double so no precision is lost in the conversion. */
|
|
/* One exception is converting from int to short compatible arrays. */
|
|
/***********************************************************************/
|
|
int ARRAY::Convert(PGLOBAL g, int k, PVAL vp)
|
|
{
|
|
int i, prec = 0;
|
|
bool b = false;
|
|
PMBV ovblk = Valblk;
|
|
PVBLK ovblp = Vblp;
|
|
|
|
Type = k; // k is the new type
|
|
Valblk = new(g) MBVALS;
|
|
|
|
switch (Type) {
|
|
case TYPE_DOUBLE:
|
|
prec = 2;
|
|
/* fall through */
|
|
case TYPE_SHORT:
|
|
case TYPE_INT:
|
|
case TYPE_DATE:
|
|
Len = 1;
|
|
break;
|
|
default:
|
|
sprintf(g->Message, MSG(BAD_CONV_TYPE), Type);
|
|
return TYPE_ERROR;
|
|
} // endswitch k
|
|
|
|
Size = Nval;
|
|
Nval = 0;
|
|
Vblp = Valblk->Allocate(g, Type, Len, prec, Size);
|
|
|
|
if (!Valblk->GetMemp())
|
|
// The error message was built by PlgDBalloc
|
|
return TYPE_ERROR;
|
|
else
|
|
Value = AllocateValue(g, Type, Len, prec);
|
|
|
|
/*********************************************************************/
|
|
/* Converting STRING to DATE can be done according to date format. */
|
|
/*********************************************************************/
|
|
if (Type == TYPE_DATE && ovblp->GetType() == TYPE_STRING && vp)
|
|
if (((DTVAL*)Value)->SetFormat(g, vp))
|
|
return TYPE_ERROR;
|
|
else
|
|
b = true; // Sort the new array on date internal values
|
|
|
|
/*********************************************************************/
|
|
/* Do the actual conversion. */
|
|
/*********************************************************************/
|
|
for (i = 0; i < Size; i++) {
|
|
Value->SetValue_pvblk(ovblp, i);
|
|
|
|
if (AddValue(g, Value))
|
|
return TYPE_ERROR;
|
|
|
|
} // endfor i
|
|
|
|
/*********************************************************************/
|
|
/* For sorted arrays, get the initial find values. */
|
|
/*********************************************************************/
|
|
if (b)
|
|
Sort(g);
|
|
|
|
ovblk->Free();
|
|
return Type;
|
|
} // end of Convert
|
|
|
|
/***********************************************************************/
|
|
/* ARRAY Save: save value at i (used while rordering). */
|
|
/***********************************************************************/
|
|
void ARRAY::Save(int i)
|
|
{
|
|
Value->SetValue_pvblk(Vblp, i);
|
|
} // end of Save
|
|
|
|
/***********************************************************************/
|
|
/* ARRAY Restore: restore value to j (used while rordering). */
|
|
/***********************************************************************/
|
|
void ARRAY::Restore(int j)
|
|
{
|
|
Vblp->SetValue(Value, j);
|
|
} // end of Restore
|
|
|
|
/***********************************************************************/
|
|
/* ARRAY Move: move value from k to j (used while rordering). */
|
|
/***********************************************************************/
|
|
void ARRAY::Move(int j, int k)
|
|
{
|
|
Vblp->Move(k, j); // VALBLK does the opposite !!!
|
|
} // end of Move
|
|
|
|
/***********************************************************************/
|
|
/* ARRAY: Compare routine for one LIST value (ascending only). */
|
|
/***********************************************************************/
|
|
int ARRAY::Qcompare(int *i1, int *i2)
|
|
{
|
|
return Vblp->CompVal(*i1, *i2);
|
|
} // end of Qcompare
|
|
|
|
/***********************************************************************/
|
|
/* Mainly meant to set the character arrays case sensitiveness. */
|
|
/***********************************************************************/
|
|
void ARRAY::SetPrecision(PGLOBAL g, int p)
|
|
{
|
|
if (Vblp == NULL) {
|
|
strcpy(g->Message, MSG(PREC_VBLP_NULL));
|
|
throw (int)TYPE_ARRAY;
|
|
} // endif Vblp
|
|
|
|
bool was = Vblp->IsCi();
|
|
|
|
if (was && !p) {
|
|
strcpy(g->Message, MSG(BAD_SET_CASE));
|
|
throw (int)TYPE_ARRAY;
|
|
} // endif Vblp
|
|
|
|
if (was || !p)
|
|
return;
|
|
else
|
|
Vblp->SetPrec(p);
|
|
|
|
if (!was && Type == TYPE_STRING)
|
|
// Must be resorted to eliminate duplicate strings
|
|
if (Sort(g))
|
|
throw (int)TYPE_ARRAY;
|
|
|
|
} // end of SetPrecision
|
|
|
|
/***********************************************************************/
|
|
/* Sort and eliminate distinct values from an array. */
|
|
/* Note: this is done by making a sorted index on distinct values. */
|
|
/* Returns false if Ok or true in case of error. */
|
|
/***********************************************************************/
|
|
bool ARRAY::Sort(PGLOBAL g)
|
|
{
|
|
int i, j, k;
|
|
|
|
// This is to avoid multiply allocating for correlated subqueries
|
|
if (Nval > Xsize) {
|
|
if (Xsize >= 0) {
|
|
// Was already allocated
|
|
PlgDBfree(Index);
|
|
PlgDBfree(Offset);
|
|
} // endif Xsize
|
|
|
|
// Prepare non conservative sort with offet values
|
|
Index.Size = Nval * sizeof(int);
|
|
|
|
if (!PlgDBalloc(g, NULL, Index))
|
|
goto error;
|
|
|
|
Offset.Size = (Nval + 1) * sizeof(int);
|
|
|
|
if (!PlgDBalloc(g, NULL, Offset))
|
|
goto error;
|
|
|
|
Xsize = Nval;
|
|
} // endif Nval
|
|
|
|
// Call the sort program, it returns the number of distinct values
|
|
Ndif = Qsort(g, Nval);
|
|
|
|
if (Ndif < 0)
|
|
goto error;
|
|
|
|
// Use the sort index to reorder the data in storage so it will
|
|
// be physically sorted and Index can be removed.
|
|
for (i = 0; i < Nval; i++) {
|
|
if (Pex[i] == i || Pex[i] == Nval)
|
|
// Already placed or already moved
|
|
continue;
|
|
|
|
Save(i);
|
|
|
|
for (j = i;; j = k) {
|
|
k = Pex[j];
|
|
Pex[j] = Nval; // Mark position as set
|
|
|
|
if (k == i) {
|
|
Restore(j);
|
|
break; // end of loop
|
|
} else
|
|
Move(j, k);
|
|
|
|
} // endfor j
|
|
|
|
} // endfor i
|
|
|
|
// Reduce the size of the To_Val array if Ndif < Nval
|
|
if (Ndif < Nval) {
|
|
for (i = 1; i < Ndif; i++)
|
|
if (i != Pof[i])
|
|
break;
|
|
|
|
for (; i < Ndif; i++)
|
|
Move(i, Pof[i]);
|
|
|
|
Nval = Ndif;
|
|
} // endif ndif
|
|
|
|
//if (!Correlated) {
|
|
if (Size > Nval) {
|
|
Size = Nval;
|
|
Valblk->ReAllocate(g, Size);
|
|
} // endif Size
|
|
|
|
// Index and Offset are not used anymore
|
|
PlgDBfree(Index);
|
|
PlgDBfree(Offset);
|
|
Xsize = -1;
|
|
// } // endif Correlated
|
|
|
|
Bot = -1; // For non optimized search
|
|
Top = Ndif; // Find searches the whole array.
|
|
return false;
|
|
|
|
error:
|
|
Nval = Ndif = 0;
|
|
Valblk->Free();
|
|
PlgDBfree(Index);
|
|
PlgDBfree(Offset);
|
|
return true;
|
|
} // end of Sort
|
|
|
|
/***********************************************************************/
|
|
/* Sort and return the sort index. */
|
|
/* Note: This is meant if the array contains unique values. */
|
|
/* Returns Index.Memp if Ok or NULL in case of error. */
|
|
/***********************************************************************/
|
|
void *ARRAY::GetSortIndex(PGLOBAL g)
|
|
{
|
|
// Prepare non conservative sort with offet values
|
|
Index.Size = Nval * sizeof(int);
|
|
|
|
if (!PlgDBalloc(g, NULL, Index))
|
|
goto error;
|
|
|
|
Offset.Size = (Nval + 1) * sizeof(int);
|
|
|
|
if (!PlgDBalloc(g, NULL, Offset))
|
|
goto error;
|
|
|
|
// Call the sort program, it returns the number of distinct values
|
|
Ndif = Qsort(g, Nval);
|
|
|
|
if (Ndif < 0)
|
|
goto error;
|
|
|
|
if (Ndif < Nval)
|
|
goto error;
|
|
|
|
PlgDBfree(Offset);
|
|
return Index.Memp;
|
|
|
|
error:
|
|
Nval = Ndif = 0;
|
|
Valblk->Free();
|
|
PlgDBfree(Index);
|
|
PlgDBfree(Offset);
|
|
return NULL;
|
|
} // end of GetSortIndex
|
|
|
|
/***********************************************************************/
|
|
/* Block filter testing for IN operator on Column/Array operands. */
|
|
/* Here we call Find that returns true if the value is in the array */
|
|
/* with X equal to the index of the found value in the array, or */
|
|
/* false if the value is not in the array with Inf and Sup being the */
|
|
/* indexes of the array values that are immediately below and over */
|
|
/* the not found value. This enables to restrict the array to the */
|
|
/* values that are between the min and max block values and to return */
|
|
/* the indication of whether the Find will be always true, always not */
|
|
/* true or other. */
|
|
/***********************************************************************/
|
|
int ARRAY::BlockTest(PGLOBAL, int opc, int opm,
|
|
void *minp, void *maxp, bool s)
|
|
{
|
|
bool bin, bax, pin, pax, veq, all = (opm == 2);
|
|
|
|
if (Ndif == 0) // Array is empty
|
|
// Return true for ALL because it means that there are no item that
|
|
// does not verify the condition, which is true indeed.
|
|
// Return false for ANY because true means that there is at least
|
|
// one item that verifies the condition, which is false.
|
|
return (all) ? 2 : -2;
|
|
else if (opc == OP_EQ && all && Ndif > 1)
|
|
return -2;
|
|
else if (opc == OP_NE && !all && Ndif > 1)
|
|
return 2;
|
|
// else if (Ndif == 1)
|
|
// all = false;
|
|
|
|
// veq is true when all values in the block are equal
|
|
switch (Type) {
|
|
case TYPE_STRING: veq = (Vblp->IsCi())
|
|
? !stricmp((char*)minp, (char*)maxp)
|
|
: !strcmp((char*)minp, (char*)maxp); break;
|
|
case TYPE_SHORT: veq = *(short*)minp == *(short*)maxp; break;
|
|
case TYPE_INT: veq = *(int*)minp == *(int*)maxp; break;
|
|
case TYPE_DOUBLE: veq = *(double*)minp == *(double*)maxp; break;
|
|
default: veq = false; // Error ?
|
|
} // endswitch type
|
|
|
|
if (!s)
|
|
Bot = -1;
|
|
|
|
Top = Ndif; // Reset Top at top of list
|
|
Value->SetBinValue(maxp);
|
|
Top = (bax = Find(Value)) ? X + 1 : Sup;
|
|
|
|
if (bax) {
|
|
if (opc == OP_EQ)
|
|
return (veq) ? 1 : 0;
|
|
else if (opc == OP_NE)
|
|
return (veq) ? -1 : 0;
|
|
|
|
if (X == 0) switch (opc) {
|
|
// Max value is equal to min list value
|
|
case OP_LE: return 1; break;
|
|
case OP_LT: return (veq) ? -1 : 0; break;
|
|
case OP_GE: return (veq) ? 1 : 0; break;
|
|
case OP_GT: return -1; break;
|
|
} // endswitch opc
|
|
|
|
pax = (opc == OP_GE) ? (X < Ndif - 1) : true;
|
|
} else if (Inf == Bot) {
|
|
// Max value is smaller than min list value
|
|
return (opc == OP_LT || opc == OP_LE || opc == OP_NE) ? 1 : -1;
|
|
} else
|
|
pax = (Sup < Ndif); // True if max value is inside the list value
|
|
|
|
if (!veq) {
|
|
Value->SetBinValue(minp);
|
|
bin = Find(Value);
|
|
} else
|
|
bin = bax;
|
|
|
|
Bot = (bin) ? X - 1 : Inf;
|
|
|
|
if (bin) {
|
|
if (opc == OP_EQ || opc == OP_NE)
|
|
return 0;
|
|
|
|
if (X == Ndif - 1) switch (opc) {
|
|
case OP_GE: return (s) ? 2 : 1; break;
|
|
case OP_GT: return (veq) ? -1 : 0; break;
|
|
case OP_LE: return (veq) ? 1 : 0; break;
|
|
case OP_LT: return (s) ? -2 : -1; break;
|
|
} // endswitch opc
|
|
|
|
pin = (opc == OP_LE) ? (X > 0) : true;
|
|
} else if (Sup == Ndif) {
|
|
// Min value is greater than max list value
|
|
if (opc == OP_GT || opc == OP_GE || opc == OP_NE)
|
|
return (s) ? 2 : 1;
|
|
else
|
|
return (s) ? -2 : -1;
|
|
|
|
} else
|
|
pin = (Inf >= 0); // True if min value is inside the list value
|
|
|
|
if (Top - Bot <= 1) {
|
|
// No list item between min and max value
|
|
#if defined(_DEBUG)
|
|
assert (!bin && !bax);
|
|
#endif
|
|
switch (opc) {
|
|
case OP_EQ: return -1; break;
|
|
case OP_NE: return 1; break;
|
|
default: return (all) ? -1 : 1; break;
|
|
} // endswitch opc
|
|
|
|
} // endif
|
|
|
|
#if defined(_DEBUG)
|
|
assert (Ndif > 1); // if Ndif = 1 we should have returned already
|
|
#endif
|
|
|
|
// At this point, if there are no logical errors in the algorithm,
|
|
// the only possible overlaps between the array and the block are:
|
|
// Array: +-------+ +-------+ +-------+ +-----+
|
|
// Block: +-----+ +---+ +------+ +--------+
|
|
// true: pax pin pax pin
|
|
if (all) switch (opc) {
|
|
case OP_GT:
|
|
case OP_GE: return (pax) ? -1 : 0; break;
|
|
case OP_LT:
|
|
case OP_LE: return (pin) ? -1 : 0; break;
|
|
} // endswitch opc
|
|
|
|
return 0;
|
|
} // end of BlockTest
|
|
|
|
/***********************************************************************/
|
|
/* MakeArrayList: Makes a value list from an SQL IN array (in work). */
|
|
/***********************************************************************/
|
|
PSZ ARRAY::MakeArrayList(PGLOBAL g)
|
|
{
|
|
char *p, *tp;
|
|
int i;
|
|
size_t z, len = 2;
|
|
|
|
if (Type == TYPE_LIST)
|
|
return (PSZ)("(?" "?" "?)"); // To be implemented
|
|
|
|
z = MY_MAX(24, GetTypeSize(Type, Len) + 4);
|
|
tp = (char*)PlugSubAlloc(g, NULL, z);
|
|
|
|
for (i = 0; i < Nval; i++) {
|
|
Value->SetValue_pvblk(Vblp, i);
|
|
Value->Prints(g, tp, z);
|
|
len += strlen(tp);
|
|
} // enfor i
|
|
|
|
if (trace(1))
|
|
htrc("Arraylist: len=%d\n", len);
|
|
|
|
p = (char *)PlugSubAlloc(g, NULL, len);
|
|
strcpy(p, "(");
|
|
|
|
for (i = 0; i < Nval;) {
|
|
Value->SetValue_pvblk(Vblp, i);
|
|
Value->Prints(g, tp, z);
|
|
strcat(p, tp);
|
|
strcat(p, (++i == Nval) ? ")" : ",");
|
|
} // enfor i
|
|
|
|
if (trace(1))
|
|
htrc("Arraylist: newlen=%d\n", strlen(p));
|
|
|
|
return p;
|
|
} // end of MakeArrayList
|
|
|
|
/***********************************************************************/
|
|
/* Make file output of ARRAY contents. */
|
|
/***********************************************************************/
|
|
void ARRAY::Printf(PGLOBAL g, FILE *f, uint n)
|
|
{
|
|
char m[64];
|
|
int lim = MY_MIN(Nval,10);
|
|
|
|
memset(m, ' ', n); // Make margin string
|
|
m[n] = '\0';
|
|
fprintf(f, "%sARRAY: type=%d\n", m, Type);
|
|
memset(m, ' ', n + 2); // Make margin string
|
|
m[n] = '\0';
|
|
|
|
if (Type != TYPE_LIST) {
|
|
fprintf(f, "%sblock=%p numval=%d\n", m, Valblk->GetMemp(), Nval);
|
|
|
|
if (Vblp)
|
|
for (int i = 0; i < lim; i++) {
|
|
Value->SetValue_pvblk(Vblp, i);
|
|
Value->Printf(g, f, n+4);
|
|
} // endfor i
|
|
|
|
} else
|
|
fprintf(f, "%sVALLST: numval=%d\n", m, Nval);
|
|
|
|
} // end of Printf
|
|
|
|
/***********************************************************************/
|
|
/* Make string output of ARRAY contents. */
|
|
/***********************************************************************/
|
|
void ARRAY::Prints(PGLOBAL, char *ps, uint z)
|
|
{
|
|
if (z < 16)
|
|
return;
|
|
|
|
sprintf(ps, "ARRAY: type=%d\n", Type);
|
|
// More to be implemented later
|
|
} // end of Prints
|
|
|
|
/* -------------------------- Class MULAR ---------------------------- */
|
|
|
|
/***********************************************************************/
|
|
/* MULAR public constructor. */
|
|
/***********************************************************************/
|
|
MULAR::MULAR(PGLOBAL g, int n) : CSORT(false)
|
|
{
|
|
Narray = n;
|
|
Pars = (PARRAY*)PlugSubAlloc(g, NULL, n * sizeof(PARRAY));
|
|
} // end of MULAR constructor
|
|
|
|
/***********************************************************************/
|
|
/* MULAR: Compare routine multiple arrays. */
|
|
/***********************************************************************/
|
|
int MULAR::Qcompare(int *i1, int *i2)
|
|
{
|
|
register int i, n = 0;
|
|
|
|
for (i = 0; i < Narray; i++)
|
|
if ((n = Pars[i]->Qcompare(i1, i2)))
|
|
break;
|
|
|
|
return n;
|
|
} // end of Qcompare
|
|
|
|
/***********************************************************************/
|
|
/* Sort and eliminate distinct values from multiple arrays. */
|
|
/* Note: this is done by making a sorted index on distinct values. */
|
|
/* Returns false if Ok or true in case of error. */
|
|
/***********************************************************************/
|
|
bool MULAR::Sort(PGLOBAL g)
|
|
{
|
|
int i, j, k, n, nval, ndif;
|
|
|
|
// All arrays must have the same number of values
|
|
nval = Pars[0]->Nval;
|
|
|
|
for (n = 1; n < Narray; n++)
|
|
if (Pars[n]->Nval != nval) {
|
|
strcpy(g->Message, MSG(BAD_ARRAY_VAL));
|
|
return true;
|
|
} // endif nval
|
|
|
|
// Prepare non conservative sort with offet values
|
|
Index.Size = nval * sizeof(int);
|
|
|
|
if (!PlgDBalloc(g, NULL, Index))
|
|
goto error;
|
|
|
|
Offset.Size = (nval + 1) * sizeof(int);
|
|
|
|
if (!PlgDBalloc(g, NULL, Offset))
|
|
goto error;
|
|
|
|
// Call the sort program, it returns the number of distinct values
|
|
ndif = Qsort(g, nval);
|
|
|
|
if (ndif < 0)
|
|
goto error;
|
|
|
|
// Use the sort index to reorder the data in storage so it will
|
|
// be physically sorted and Index can be removed.
|
|
for (i = 0; i < nval; i++) {
|
|
if (Pex[i] == i || Pex[i] == nval)
|
|
// Already placed or already moved
|
|
continue;
|
|
|
|
for (n = 0; n < Narray; n++)
|
|
Pars[n]->Save(i);
|
|
|
|
for (j = i;; j = k) {
|
|
k = Pex[j];
|
|
Pex[j] = nval; // Mark position as set
|
|
|
|
if (k == i) {
|
|
for (n = 0; n < Narray; n++)
|
|
Pars[n]->Restore(j);
|
|
|
|
break; // end of loop
|
|
} else
|
|
for (n = 0; n < Narray; n++)
|
|
Pars[n]->Move(j, k);
|
|
|
|
} // endfor j
|
|
|
|
} // endfor i
|
|
|
|
// Reduce the size of the To_Val array if ndif < nval
|
|
if (ndif < nval) {
|
|
for (i = 1; i < ndif; i++)
|
|
if (i != Pof[i])
|
|
break;
|
|
|
|
for (; i < ndif; i++)
|
|
for (n = 0; n < Narray; n++)
|
|
Pars[n]->Move(i, Pof[i]);
|
|
|
|
for (n = 0; n < Narray; n++) {
|
|
Pars[n]->Nval = ndif;
|
|
Pars[n]->Size = ndif;
|
|
Pars[n]->Valblk->ReAllocate(g, ndif);
|
|
} // endfor n
|
|
|
|
} // endif ndif
|
|
|
|
// Index and Offset are not used anymore
|
|
PlgDBfree(Index);
|
|
PlgDBfree(Offset);
|
|
|
|
for (n = 0; n < Narray; n++) {
|
|
Pars[n]->Bot = -1; // For non optimized search
|
|
Pars[n]->Top = ndif; // Find searches the whole array.
|
|
} // endfor n
|
|
|
|
return false;
|
|
|
|
error:
|
|
PlgDBfree(Index);
|
|
PlgDBfree(Offset);
|
|
return true;
|
|
} // end of Sort
|