mirror of
https://github.com/MariaDB/server.git
synced 2025-01-16 12:02:42 +01:00
1614 lines
51 KiB
C++
1614 lines
51 KiB
C++
/********** PlgDBUtl Fpe C++ Program Source Code File (.CPP) ***********/
|
|
/* PROGRAM NAME: PLGDBUTL */
|
|
/* ------------- */
|
|
/* Version 4.1 */
|
|
/* */
|
|
/* COPYRIGHT: */
|
|
/* ---------- */
|
|
/* (C) Copyright to the author Olivier BERTRAND 1998-2020 */
|
|
/* */
|
|
/* WHAT THIS PROGRAM DOES: */
|
|
/* ----------------------- */
|
|
/* Utility functions used by DB semantic routines. */
|
|
/* */
|
|
/* WHAT YOU NEED TO COMPILE THIS PROGRAM: */
|
|
/* -------------------------------------- */
|
|
/* */
|
|
/* REQUIRED FILES: */
|
|
/* --------------- */
|
|
/* See Readme.C for a list and description of required SYSTEM files. */
|
|
/* */
|
|
/* PLGDBUTL.C - Source code */
|
|
/* GLOBAL.H - Global declaration file */
|
|
/* PLGDBSEM.H - DB application declaration file */
|
|
/* */
|
|
/* REQUIRED LIBRARIES: */
|
|
/* ------------------- */
|
|
/* OS2.LIB - OS2 libray */
|
|
/* LLIBCE.LIB - Protect mode/standard combined large model C */
|
|
/* library */
|
|
/* */
|
|
/* REQUIRED PROGRAMS: */
|
|
/* ------------------ */
|
|
/* IBM, MS, Borland or GNU C++ Compiler */
|
|
/* IBM, MS, Borland or GNU Linker */
|
|
/***********************************************************************/
|
|
|
|
/***********************************************************************/
|
|
/* Include relevant MariaDB header file. */
|
|
/***********************************************************************/
|
|
#include "my_global.h"
|
|
#include "my_pthread.h"
|
|
#if defined(_WIN32)
|
|
#include <io.h>
|
|
#include <fcntl.h>
|
|
#include <errno.h>
|
|
#define BIGMEM 1048576 // 1 Megabyte
|
|
#else // !_WIN32
|
|
#include <unistd.h>
|
|
#include <fcntl.h>
|
|
//#if defined(THREAD)
|
|
#include <pthread.h>
|
|
//#endif // THREAD
|
|
#include <stdarg.h>
|
|
#define BIGMEM 2147483647 // Max int value
|
|
#endif // !_WIN32
|
|
#include <locale.h>
|
|
|
|
/***********************************************************************/
|
|
/* Include application header files */
|
|
/***********************************************************************/
|
|
#include "global.h" // header containing all global declarations.
|
|
#include "plgdbsem.h" // header containing the DB applic. declarations.
|
|
#include "preparse.h" // For DATPAR
|
|
#include "osutil.h"
|
|
#include "maputil.h"
|
|
#include "catalog.h"
|
|
#include "colblk.h"
|
|
#include "xtable.h" // header of TBX, TDB and TDBASE classes
|
|
#include "tabcol.h" // header of XTAB and COLUMN classes
|
|
#include "valblk.h"
|
|
#include "rcmsg.h"
|
|
#ifdef ZIP_SUPPORT
|
|
#include "filamzip.h"
|
|
#endif // ZIP_SUPPORT
|
|
#ifdef JAVA_SUPPORT
|
|
#include "javaconn.h"
|
|
#endif // JAVA_SUPPORT
|
|
#ifdef CMGO_SUPPORT
|
|
#include "cmgoconn.h"
|
|
#endif // JAVA_SUPPORT
|
|
|
|
/***********************************************************************/
|
|
/* DB static variables. */
|
|
/***********************************************************************/
|
|
bool Initdone = false;
|
|
bool plugin = false; // True when called by the XDB plugin handler
|
|
|
|
extern "C" {
|
|
extern char version[];
|
|
} // extern "C"
|
|
|
|
//#if defined(_WIN32)
|
|
//extern CRITICAL_SECTION parsec; // Used calling the Flex parser
|
|
//#else // !_WIN32
|
|
extern pthread_mutex_t parmut;
|
|
//#endif // !_WIN32
|
|
|
|
// The debug trace used by the main thread
|
|
FILE *pfile = NULL;
|
|
|
|
MBLOCK Nmblk = {NULL, false, 0, false, NULL}; // Used to init MBLOCK's
|
|
|
|
/***********************************************************************/
|
|
/* Routines called externally and internally by utility routines. */
|
|
/***********************************************************************/
|
|
bool PlugEvalLike(PGLOBAL, LPCSTR, LPCSTR, bool);
|
|
bool EvalLikePattern(LPCSTR, LPCSTR);
|
|
void PlugConvertConstant(PGLOBAL, void* &, short&);
|
|
|
|
#ifdef DOMDOC_SUPPORT
|
|
void CloseXMLFile(PGLOBAL, PFBLOCK, bool);
|
|
#endif // DOMDOC_SUPPORT
|
|
|
|
#ifdef LIBXML2_SUPPORT
|
|
#include "libdoc.h"
|
|
#endif // LIBXML2_SUPPORT
|
|
|
|
#ifdef ODBC_SUPPORT
|
|
void OdbcClose(PGLOBAL g, PFBLOCK fp);
|
|
#endif // ODBC_SUPPORT
|
|
|
|
/***********************************************************************/
|
|
/* Routines for file IO with error reporting to g->Message */
|
|
/* Note: errno and strerror must be called before the message file */
|
|
/* is read in the case of XMSG compile. */
|
|
/***********************************************************************/
|
|
static void global_open_error_msg(GLOBAL *g, int msgid, const char *path,
|
|
const char *mode)
|
|
{
|
|
int len, rno= (int)errno;
|
|
char errmsg[256]= "";
|
|
|
|
strncat(errmsg, strerror(errno), 255);
|
|
|
|
switch (msgid)
|
|
{
|
|
case MSGID_CANNOT_OPEN:
|
|
len= snprintf(g->Message, sizeof(g->Message) - 1,
|
|
MSG(CANNOT_OPEN), // Cannot open %s
|
|
path);
|
|
break;
|
|
|
|
case MSGID_OPEN_MODE_ERROR:
|
|
len= snprintf(g->Message, sizeof(g->Message) - 1,
|
|
MSG(OPEN_MODE_ERROR), // "Open(%s) error %d on %s"
|
|
mode, rno, path);
|
|
break;
|
|
|
|
case MSGID_OPEN_MODE_STRERROR:
|
|
{char fmt[256];
|
|
strcat(strcpy(fmt, MSG(OPEN_MODE_ERROR)), ": %s");
|
|
len= snprintf(g->Message, sizeof(g->Message) - 1,
|
|
fmt, // Open(%s) error %d on %s: %s
|
|
mode, rno, path, errmsg);
|
|
}break;
|
|
|
|
case MSGID_OPEN_STRERROR:
|
|
len= snprintf(g->Message, sizeof(g->Message) - 1,
|
|
MSG(OPEN_STRERROR), // "open error: %s"
|
|
errmsg);
|
|
break;
|
|
|
|
case MSGID_OPEN_ERROR_AND_STRERROR:
|
|
len= snprintf(g->Message, sizeof(g->Message) - 1,
|
|
//OPEN_ERROR does not work, as it wants mode %d (not %s)
|
|
//MSG(OPEN_ERROR) "%s",// "Open error %d in mode %d on %s: %s"
|
|
"Open error %d in mode %s on %s: %s",
|
|
rno, mode, path, errmsg);
|
|
break;
|
|
|
|
case MSGID_OPEN_EMPTY_FILE:
|
|
len= snprintf(g->Message, sizeof(g->Message) - 1,
|
|
MSG(OPEN_EMPTY_FILE), // "Opening empty file %s: %s"
|
|
path, errmsg);
|
|
break;
|
|
|
|
default:
|
|
DBUG_ASSERT(0);
|
|
/* Fall through*/
|
|
case 0:
|
|
len= 0;
|
|
}
|
|
g->Message[len]= '\0';
|
|
}
|
|
|
|
|
|
FILE *global_fopen(GLOBAL *g, int msgid, const char *path, const char *mode)
|
|
{
|
|
FILE *f;
|
|
if (!(f= fopen(path, mode)))
|
|
global_open_error_msg(g, msgid, path, mode);
|
|
return f;
|
|
}
|
|
|
|
|
|
int global_open(GLOBAL *g, int msgid, const char *path, int flags)
|
|
{
|
|
int h;
|
|
if ((h= open(path, flags)) <= 0)
|
|
global_open_error_msg(g, msgid, path, "");
|
|
return h;
|
|
}
|
|
|
|
|
|
int global_open(GLOBAL *g, int msgid, const char *path, int flags, int mode)
|
|
{
|
|
int h;
|
|
if ((h= open(path, flags, mode)) <= 0)
|
|
{
|
|
char modestr[64];
|
|
snprintf(modestr, sizeof(modestr), "%d", mode);
|
|
global_open_error_msg(g, msgid, path, modestr);
|
|
}
|
|
return h;
|
|
}
|
|
|
|
DllExport void SetTrc(void)
|
|
{
|
|
// If tracing is on, debug must be initialized.
|
|
debug = pfile;
|
|
} // end of SetTrc
|
|
|
|
/**************************************************************************/
|
|
/* SubAllocate the result structure that will contain result data. */
|
|
/**************************************************************************/
|
|
PQRYRES PlgAllocResult(PGLOBAL g, int ncol, int maxres, int ids,
|
|
int *buftyp, XFLD *fldtyp,
|
|
unsigned int *length, bool blank, bool nonull)
|
|
{
|
|
char cname[NAM_LEN+1];
|
|
int i;
|
|
PCOLRES *pcrp, crp;
|
|
PQRYRES qrp;
|
|
|
|
try {
|
|
/**********************************************************************/
|
|
/* Allocate the structure used to contain the result set. */
|
|
/**********************************************************************/
|
|
qrp = (PQRYRES)PlugSubAlloc(g, NULL, sizeof(QRYRES));
|
|
pcrp = &qrp->Colresp;
|
|
qrp->Continued = false;
|
|
qrp->Truncated = false;
|
|
qrp->Info = false;
|
|
qrp->Suball = true;
|
|
qrp->Maxres = maxres;
|
|
qrp->Maxsize = 0;
|
|
qrp->Nblin = 0;
|
|
qrp->Nbcol = 0; // will be ncol
|
|
qrp->Cursor = 0;
|
|
qrp->BadLines = 0;
|
|
|
|
for (i = 0; i < ncol; i++) {
|
|
*pcrp = (PCOLRES)PlugSubAlloc(g, NULL, sizeof(COLRES));
|
|
crp = *pcrp;
|
|
pcrp = &crp->Next;
|
|
memset(crp, 0, sizeof(COLRES));
|
|
crp->Colp = NULL;
|
|
crp->Ncol = ++qrp->Nbcol;
|
|
crp->Type = buftyp[i];
|
|
crp->Length = length[i];
|
|
crp->Clen = GetTypeSize(crp->Type, length[i]);
|
|
crp->Prec = 0;
|
|
|
|
if (ids > 0) {
|
|
#if defined(XMSG)
|
|
// Get header from message file
|
|
strncpy(cname, PlugReadMessage(g, ids + crp->Ncol, NULL), NAM_LEN);
|
|
cname[NAM_LEN] = 0; // for truncated long names
|
|
#else // !XMSG
|
|
GetRcString(ids + crp->Ncol, cname, sizeof(cname));
|
|
#endif // !XMSG
|
|
crp->Name = (PSZ)PlugDup(g, cname);
|
|
} else
|
|
crp->Name = NULL; // Will be set by caller
|
|
|
|
if (fldtyp)
|
|
crp->Fld = fldtyp[i];
|
|
else
|
|
crp->Fld = FLD_NO;
|
|
|
|
// Allocate the Value Block that will contain data
|
|
if (crp->Length || nonull)
|
|
crp->Kdata = AllocValBlock(g, NULL, crp->Type, maxres,
|
|
crp->Length, 0, true, blank, false);
|
|
else
|
|
crp->Kdata = NULL;
|
|
|
|
if (trace(1))
|
|
htrc("Column(%d) %s type=%d len=%d value=%p\n",
|
|
crp->Ncol, crp->Name, crp->Type, crp->Length, crp->Kdata);
|
|
|
|
} // endfor i
|
|
|
|
*pcrp = NULL;
|
|
|
|
} catch (int n) {
|
|
htrc("Exception %d: %s\n", n, g->Message);
|
|
qrp = NULL;
|
|
} catch (const char *msg) {
|
|
strcpy(g->Message, msg);
|
|
htrc("%s\n", g->Message);
|
|
qrp = NULL;
|
|
} // end catch
|
|
|
|
return qrp;
|
|
} // end of PlgAllocResult
|
|
|
|
/***********************************************************************/
|
|
/* Allocate and initialize the new DB User Block. */
|
|
/***********************************************************************/
|
|
PDBUSER PlgMakeUser(PGLOBAL g)
|
|
{
|
|
PDBUSER dbuserp;
|
|
|
|
if (!(dbuserp = (PDBUSER)malloc(sizeof(DBUSERBLK)))) {
|
|
snprintf(g->Message, sizeof(g->Message), MSG(MALLOC_ERROR), "PlgMakeUser");
|
|
return NULL;
|
|
} // endif dbuserp
|
|
|
|
memset(dbuserp, 0, sizeof(DBUSERBLK));
|
|
dbuserp->Maxbmp = MAXBMP;
|
|
//dbuserp->UseTemp = TMP_AUTO;
|
|
dbuserp->Check = CHK_ALL;
|
|
strcpy(dbuserp->Server, "CONNECT");
|
|
return dbuserp;
|
|
} // end of PlgMakeUser
|
|
|
|
/***********************************************************************/
|
|
/* PlgGetUser: returns DBUSER block pointer. */
|
|
/***********************************************************************/
|
|
PDBUSER PlgGetUser(PGLOBAL g)
|
|
{
|
|
PDBUSER dup = (PDBUSER)((g->Activityp) ? g->Activityp->Aptr : NULL);
|
|
|
|
if (!dup)
|
|
strcpy(g->Message, MSG(APPL_NOT_INIT));
|
|
|
|
return dup;
|
|
} // end of PlgGetUser
|
|
|
|
/***********************************************************************/
|
|
/* PlgGetCatalog: returns CATALOG class pointer. */
|
|
/***********************************************************************/
|
|
PCATLG PlgGetCatalog(PGLOBAL g, bool jump)
|
|
{
|
|
PDBUSER dbuserp = PlgGetUser(g);
|
|
PCATLG cat = (dbuserp) ? dbuserp->Catalog : NULL;
|
|
|
|
if (!cat && jump) {
|
|
// Raise exception so caller doesn't have to check return value
|
|
strcpy(g->Message, MSG(NO_ACTIVE_DB));
|
|
throw 1;
|
|
} // endif cat
|
|
|
|
return cat;
|
|
} // end of PlgGetCatalog
|
|
|
|
#if 0
|
|
/***********************************************************************/
|
|
/* PlgGetDataPath: returns the default data path. */
|
|
/***********************************************************************/
|
|
char *PlgGetDataPath(PGLOBAL g)
|
|
{
|
|
PCATLG cat = PlgGetCatalog(g, false);
|
|
|
|
return (cat) ? cat->GetDataPath() : NULL;
|
|
} // end of PlgGetDataPath
|
|
#endif // 0
|
|
|
|
/***********************************************************************/
|
|
/* This function returns a database path. */
|
|
/***********************************************************************/
|
|
char *SetPath(PGLOBAL g, const char *path)
|
|
{
|
|
char *buf= NULL;
|
|
|
|
if (path) {
|
|
size_t len = strlen(path) + (*path != '.' ? 4 : 1);
|
|
|
|
if (!(buf = (char*)PlgDBSubAlloc(g, NULL, len)))
|
|
return NULL;
|
|
|
|
if (PlugIsAbsolutePath(path)) {
|
|
snprintf(buf, len, "%s", path);
|
|
return buf;
|
|
} // endif path
|
|
|
|
if (*path != '.') {
|
|
#if defined(_WIN32)
|
|
const char *s = "\\";
|
|
#else // !_WIN32
|
|
const char *s = "/";
|
|
#endif // !_WIN32
|
|
snprintf(buf, len, ".%s%s%s", s, path, s);
|
|
} else
|
|
snprintf(buf, len, "%s", path);
|
|
|
|
} // endif path
|
|
|
|
return buf;
|
|
} // end of SetPath
|
|
|
|
/***********************************************************************/
|
|
/* Extract from a path name the required component. */
|
|
/* This function assumes there is enough space in the buffer. */
|
|
/***********************************************************************/
|
|
char *ExtractFromPath(PGLOBAL g, char *pBuff, char *FileName, OPVAL op)
|
|
{
|
|
char *drive = NULL, *direc = NULL, *fname = NULL, *ftype = NULL;
|
|
|
|
switch (op) { // Determine which part to extract
|
|
#if defined(_WIN32)
|
|
case OP_FDISK: drive = pBuff; break;
|
|
#endif // !UNIX
|
|
case OP_FPATH: direc = pBuff; break;
|
|
case OP_FNAME: fname = pBuff; break;
|
|
case OP_FTYPE: ftype = pBuff; break;
|
|
default:
|
|
snprintf(g->Message, sizeof(g->Message), MSG(INVALID_OPER), op, "ExtractFromPath");
|
|
return NULL;
|
|
} // endswitch op
|
|
|
|
// Now do the extraction
|
|
_splitpath(FileName, drive, direc, fname, ftype);
|
|
return pBuff;
|
|
} // end of PlgExtractFromPath
|
|
|
|
|
|
#ifdef NOT_USED
|
|
/***********************************************************************/
|
|
/* Check the occurrence and matching of a pattern against a string. */
|
|
/* Because this function is only used for catalog name checking, */
|
|
/* it must be case insensitive. */
|
|
/***********************************************************************/
|
|
static bool PlugCheckPattern(PGLOBAL g, LPCSTR string, LPCSTR pat)
|
|
{
|
|
if (pat && strlen(pat)) {
|
|
// This leaves 2048 bytes (MAX_STR / 2) for each components
|
|
LPSTR name = g->Message + MAX_STR / 2;
|
|
|
|
strlwr(strcpy(name, string));
|
|
strlwr(strcpy(g->Message, pat)); // Can be modified by Eval
|
|
return EvalLikePattern(name, g->Message);
|
|
} else
|
|
return true;
|
|
|
|
} // end of PlugCheckPattern
|
|
#endif /* NOT_USED */
|
|
|
|
/***********************************************************************/
|
|
/* PlugEvalLike: evaluates a LIKE clause. */
|
|
/* Syntaxe: M like P escape C. strg->M, pat->P, C not implemented yet */
|
|
/***********************************************************************/
|
|
bool PlugEvalLike(PGLOBAL g, LPCSTR strg, LPCSTR pat, bool ci)
|
|
{
|
|
char *tp, *sp;
|
|
bool b;
|
|
|
|
if (trace(2))
|
|
htrc("LIKE: strg='%s' pattern='%s'\n", strg, pat);
|
|
|
|
if (ci) { /* Case insensitive test */
|
|
if (strlen(pat) + strlen(strg) + 1 < MAX_STR)
|
|
tp = g->Message;
|
|
else if (!(tp = new char[strlen(pat) + strlen(strg) + 2])) {
|
|
strcpy(g->Message, MSG(NEW_RETURN_NULL));
|
|
throw (int)OP_LIKE;
|
|
} /* endif tp */
|
|
|
|
sp = tp + strlen(pat) + 1;
|
|
strlwr(strcpy(tp, pat)); /* Make a lower case copy of pat */
|
|
strlwr(strcpy(sp, strg)); /* Make a lower case copy of strg */
|
|
} else { /* Case sensitive test */
|
|
if (strlen(pat) < MAX_STR) /* In most of the case for small pat */
|
|
tp = g->Message; /* Use this as temporary work space. */
|
|
else if (!(tp = new char[strlen(pat) + 1])) {
|
|
strcpy(g->Message, MSG(NEW_RETURN_NULL));
|
|
throw (int)OP_LIKE;
|
|
} /* endif tp */
|
|
|
|
strcpy(tp, pat); /* Make a copy to be worked into */
|
|
sp = (char*)strg;
|
|
} /* endif ci */
|
|
|
|
b = EvalLikePattern(sp, tp);
|
|
|
|
if (tp != g->Message) /* If working space was obtained */
|
|
delete [] tp; /* by the use of new, delete it. */
|
|
|
|
return (b);
|
|
} /* end of PlugEvalLike */
|
|
|
|
/***********************************************************************/
|
|
/* M and P are variable length character string. If M and P are zero */
|
|
/* length strings then the Like predicate is true. */
|
|
/* */
|
|
/* The Like predicate is true if: */
|
|
/* */
|
|
/* 1- A subtring of M is a sequence of 0 or more contiguous <CR> of M */
|
|
/* and each <CR> of M is part of exactly one substring. */
|
|
/* */
|
|
/* 2- If the i-th <subtring-specifyer> of P is an <arbitrary-char- */
|
|
/* specifier>, the i-th subtring of M is any single <CR>. */
|
|
/* */
|
|
/* 3- If the i-th <subtring-specifyer> of P is an <arbitrary-string- */
|
|
/* specifier>, then the i-th subtring of M is any sequence of zero */
|
|
/* or more <CR>. */
|
|
/* */
|
|
/* 4- If the i-th <subtring-specifyer> of P is neither an <arbitrary- */
|
|
/* character-specifier> nor an <arbitrary-string-specifier>, then */
|
|
/* the i-th substring of M is equal to that <substring-specifier> */
|
|
/* according to the collating sequence of the <like-predicate>, */
|
|
/* without the appending of <space-character>, and has the same */
|
|
/* length as that <substring-specifier>. */
|
|
/* */
|
|
/* 5- The number of substrings of M is equal to the number of */
|
|
/* <subtring-specifiers> of P. */
|
|
/* */
|
|
/* Otherwise M like P is false. */
|
|
/***********************************************************************/
|
|
bool EvalLikePattern(LPCSTR sp, LPCSTR tp)
|
|
{
|
|
LPSTR p;
|
|
char c;
|
|
ssize_t n;
|
|
bool b, t = false;
|
|
|
|
if (trace(2))
|
|
htrc("Eval Like: sp=%s tp=%s\n",
|
|
(sp) ? sp : "Null", (tp) ? tp : "Null");
|
|
|
|
/********************************************************************/
|
|
/* If pattern is void, Like is true only if string is also void. */
|
|
/********************************************************************/
|
|
if (!*tp)
|
|
return (!*sp);
|
|
|
|
/********************************************************************/
|
|
/* Analyse eventual arbitrary specifications ahead of pattern. */
|
|
/********************************************************************/
|
|
for (p = (LPSTR)tp; p;)
|
|
switch (*p) { /* it can contain % and/or _ */
|
|
case '%': /* An % has been found */
|
|
t = true; /* Note eventual character skip */
|
|
p++;
|
|
break;
|
|
case '_': /* An _ has been found */
|
|
if (*sp) { /* If more character in string */
|
|
sp++; /* skip it */
|
|
p++;
|
|
} else
|
|
return false; /* Like condition is not met */
|
|
|
|
break;
|
|
default:
|
|
tp = p; /* Point to rest of template */
|
|
p = NULL; /* To stop For loop */
|
|
break;
|
|
} /* endswitch */
|
|
|
|
if ((p = (LPSTR)strpbrk(tp, "%_"))) /* Get position of next % or _ */
|
|
n = p - tp;
|
|
else
|
|
n = strlen(tp); /* Get length of pattern head */
|
|
|
|
if (trace(2))
|
|
htrc(" testing: t=%d sp=%s tp=%s p=%p\n", t, sp, tp, p);
|
|
|
|
if (n > (signed)strlen(sp)) /* If head is longer than strg */
|
|
b = false; /* Like condition is not met */
|
|
else if (n == 0) /* If void <substring-specifier> */
|
|
b = (t || !*sp); /* true if % or void strg. */
|
|
else if (!t) {
|
|
/*******************************************************************/
|
|
/* No character to skip, check occurrence of <subtring-specifier> */
|
|
/* at the very beginning of remaining string. */
|
|
/*******************************************************************/
|
|
if (p) {
|
|
if ((b = !strncmp(sp, tp, n)))
|
|
b = EvalLikePattern(sp + n, p);
|
|
|
|
} else
|
|
b = !strcmp(sp, tp); /* strg and tmp heads match */
|
|
|
|
} else
|
|
if (p)
|
|
/*****************************************************************/
|
|
/* Here is the case explaining why we need a recursive routine. */
|
|
/* The test must be done not only against the first occurrence */
|
|
/* of the <substring-specifier> in the remaining string, */
|
|
/* but also with all eventual succeeding ones. */
|
|
/*****************************************************************/
|
|
for (b = false, c = *p; !b && (signed)strlen(sp) >= n; sp++) {
|
|
*p = '\0'; /* Separate pattern header */
|
|
|
|
if ((sp = strstr(sp, tp))) {
|
|
*p = c;
|
|
b = EvalLikePattern(sp + n, p);
|
|
} else {
|
|
*p = c;
|
|
b = false;
|
|
break;
|
|
} /* endif s */
|
|
|
|
} /* endfor b, sp */
|
|
|
|
else {
|
|
sp += (strlen(sp) - n);
|
|
b = !strcmp(sp, tp);
|
|
} /* endif p */
|
|
|
|
if (trace(2))
|
|
htrc(" done: b=%d n=%d sp=%s tp=%s\n",
|
|
b, n, (sp) ? sp : "Null", tp);
|
|
|
|
return (b);
|
|
} /* end of EvalLikePattern */
|
|
|
|
/***********************************************************************/
|
|
/* MakeEscape: Escape some characters in a string. */
|
|
/***********************************************************************/
|
|
char *MakeEscape(PGLOBAL g, char* str, char q)
|
|
{
|
|
char *bufp;
|
|
int i, k, n = 0, len = (int)strlen(str);
|
|
|
|
for (i = 0; i < len; i++)
|
|
if (str[i] == q || str[i] == '\\')
|
|
n++;
|
|
|
|
if (!n)
|
|
return str;
|
|
else
|
|
bufp = (char*)PlugSubAlloc(g, NULL, len + n + 1);
|
|
|
|
for (i = k = 0; i < len; i++) {
|
|
if (str[i] == q || str[i] == '\\')
|
|
bufp[k++] = '\\';
|
|
|
|
bufp[k++] = str[i];
|
|
} // endfor i
|
|
|
|
bufp[k] = 0;
|
|
return bufp;
|
|
} /* end of MakeEscape */
|
|
|
|
/***********************************************************************/
|
|
/* PlugConvertConstant: convert a Plug constant to an Xobject. */
|
|
/***********************************************************************/
|
|
void PlugConvertConstant(PGLOBAL g, void* & value, short& type)
|
|
{
|
|
if (trace(1))
|
|
htrc("PlugConvertConstant: value=%p type=%hd\n", value, type);
|
|
|
|
if (type != TYPE_XOBJECT) {
|
|
value = new(g) CONSTANT(g, value, type);
|
|
type = TYPE_XOBJECT;
|
|
} // endif type
|
|
|
|
} // end of PlugConvertConstant
|
|
|
|
/***********************************************************************/
|
|
/* Call the Flex preparser to convert a date format to a sscanf input */
|
|
/* format and a Strftime output format. Flag if not 0 indicates that */
|
|
/* non quoted blanks are not included in the output format. */
|
|
/***********************************************************************/
|
|
PDTP MakeDateFormat(PGLOBAL g, PCSZ dfmt, bool in, bool out, int flag)
|
|
{
|
|
int rc;
|
|
PDTP pdp = (PDTP)PlugSubAlloc(g, NULL, sizeof(DATPAR));
|
|
|
|
if (trace(1))
|
|
htrc("MakeDateFormat: dfmt=%s\n", dfmt);
|
|
|
|
memset(pdp, 0, sizeof(DATPAR));
|
|
pdp->Format = pdp->Curp = PlugDup(g, dfmt);
|
|
pdp->Outsize = 2 * strlen(dfmt) + 1;
|
|
|
|
if (in)
|
|
pdp->InFmt = (char*)PlugSubAlloc(g, NULL, pdp->Outsize);
|
|
|
|
if (out)
|
|
pdp->OutFmt = (char*)PlugSubAlloc(g, NULL, pdp->Outsize);
|
|
|
|
pdp->Flag = flag;
|
|
|
|
/*********************************************************************/
|
|
/* Call the FLEX generated parser. In multi-threading mode the next */
|
|
/* instruction is protected by mutex fmdflex using static variables. */
|
|
/*********************************************************************/
|
|
pthread_mutex_lock(&parmut);
|
|
rc = fmdflex(pdp);
|
|
pthread_mutex_unlock(&parmut);
|
|
|
|
if (trace(1))
|
|
htrc("Done: in=%s out=%s rc=%d\n", SVP(pdp->InFmt), SVP(pdp->OutFmt), rc);
|
|
|
|
return pdp;
|
|
} // end of MakeDateFormat
|
|
|
|
/***********************************************************************/
|
|
/* Extract the date from a formatted string according to format. */
|
|
/***********************************************************************/
|
|
int ExtractDate(char *dts, PDTP pdp, int defy, int val[6])
|
|
{
|
|
PCSZ fmt;
|
|
char c, d, e, W[8][12];
|
|
int i, k, m, numval;
|
|
int n, y = 30;
|
|
bool b = true; // true for null dates
|
|
|
|
if (pdp)
|
|
fmt = pdp->InFmt;
|
|
else // assume standard MySQL date format
|
|
fmt = "%4d-%2d-%2d %2d:%2d:%2d";
|
|
|
|
if (trace(2))
|
|
htrc("ExtractDate: dts=%s fmt=%s defy=%d\n", dts, fmt, defy);
|
|
|
|
// Set default values for time only use
|
|
if (defy) {
|
|
// This may be a default value for year
|
|
y = defy;
|
|
val[0] = y;
|
|
y = (y < 100) ? y : 30;
|
|
} else
|
|
val[0] = 70;
|
|
|
|
val[1] = 1;
|
|
val[2] = 1;
|
|
|
|
for (i = 3; i < 6; i++)
|
|
val[i] = 0;
|
|
|
|
numval = 0;
|
|
|
|
// Get the date field parse it with derived input format
|
|
m = sscanf(dts, fmt, W[0], W[1], W[2], W[3], W[4], W[5], W[6], W[7]);
|
|
|
|
if (m > pdp->Num)
|
|
m = pdp->Num;
|
|
|
|
for (i = 0; i < m; i++) {
|
|
if ((n = *(int*)W[i]))
|
|
b = false;
|
|
|
|
switch (k = pdp->Index[i]) {
|
|
case 0:
|
|
if (n < y)
|
|
n += 100;
|
|
|
|
val[0] = n;
|
|
numval = MY_MAX(numval, 1);
|
|
break;
|
|
case 1:
|
|
case 2:
|
|
case 3:
|
|
case 4:
|
|
case 5:
|
|
val[k] = n;
|
|
numval = MY_MAX(numval, k + 1);
|
|
break;
|
|
case -1:
|
|
c = toupper(W[i][0]);
|
|
d = toupper(W[i][1]);
|
|
e = toupper(W[i][2]);
|
|
|
|
switch (c) {
|
|
case 'J':
|
|
n = (d == 'A') ? 1
|
|
: (e == 'N') ? 6 : 7; break;
|
|
case 'F': n = 2; break;
|
|
case 'M':
|
|
n = (e == 'R') ? 3 : 5; break;
|
|
case 'A':
|
|
n = (d == 'P') ? 4 : 8; break;
|
|
break;
|
|
case 'S': n = 9; break;
|
|
case 'O': n = 10; break;
|
|
case 'N': n = 11; break;
|
|
case 'D': n = 12; break;
|
|
} /* endswitch c */
|
|
|
|
val[1] = n;
|
|
numval = MY_MAX(numval, 2);
|
|
break;
|
|
case -6:
|
|
c = toupper(W[i][0]);
|
|
n = val[3] % 12;
|
|
|
|
if (c == 'P')
|
|
n += 12;
|
|
|
|
val[3] = n;
|
|
break;
|
|
} // endswitch Plugpar
|
|
|
|
} // endfor i
|
|
|
|
if (trace(2))
|
|
htrc("numval=%d val=(%d,%d,%d,%d,%d,%d)\n",
|
|
numval, val[0], val[1], val[2], val[3], val[4], val[5]);
|
|
|
|
return (b) ? 0 : numval;
|
|
} // end of ExtractDate
|
|
|
|
/***********************************************************************/
|
|
/* Open file routine: the purpose of this routine is to make a list */
|
|
/* of all open file so they can be closed in SQLINIT on error jump. */
|
|
/***********************************************************************/
|
|
FILE *PlugOpenFile(PGLOBAL g, LPCSTR fname, LPCSTR ftype)
|
|
{
|
|
FILE *fop;
|
|
PFBLOCK fp;
|
|
PDBUSER dbuserp = (PDBUSER)g->Activityp->Aptr;
|
|
|
|
if (trace(1)) {
|
|
htrc("PlugOpenFile: fname=%s ftype=%s\n", fname, ftype);
|
|
htrc("dbuserp=%p\n", dbuserp);
|
|
} // endif trace
|
|
|
|
if ((fop= global_fopen(g, MSGID_OPEN_MODE_STRERROR, fname, ftype)) != NULL) {
|
|
if (trace(1))
|
|
htrc(" fop=%p\n", fop);
|
|
|
|
fp = (PFBLOCK)PlugSubAlloc(g, NULL, sizeof(FBLOCK));
|
|
|
|
if (trace(1))
|
|
htrc(" fp=%p\n", fp);
|
|
|
|
// fname may be in volatile memory such as stack
|
|
fp->Fname = PlugDup(g, fname);
|
|
fp->Count = 1;
|
|
fp->Type = TYPE_FB_FILE;
|
|
fp->File = fop;
|
|
fp->Mode = MODE_ANY; // ???
|
|
fp->Next = dbuserp->Openlist;
|
|
dbuserp->Openlist = fp;
|
|
} /* endif fop */
|
|
|
|
if (trace(1))
|
|
htrc(" returning fop=%p\n", fop);
|
|
|
|
return (fop);
|
|
} // end of PlugOpenFile
|
|
|
|
/***********************************************************************/
|
|
/* Close file routine: the purpose of this routine is to avoid */
|
|
/* double closing that freeze the system on some Unix platforms. */
|
|
/***********************************************************************/
|
|
FILE *PlugReopenFile(PGLOBAL g, PFBLOCK fp, LPCSTR md)
|
|
{
|
|
FILE *fop;
|
|
|
|
if ((fop = global_fopen(g, MSGID_OPEN_MODE_STRERROR, fp->Fname, md))) {
|
|
fp->Count = 1;
|
|
fp->Type = TYPE_FB_FILE;
|
|
fp->File = fop;
|
|
} /* endif fop */
|
|
|
|
return (fop);
|
|
} // end of PlugOpenFile
|
|
|
|
/***********************************************************************/
|
|
/* Close file routine: the purpose of this routine is to avoid */
|
|
/* double closing that freeze the system on some Unix platforms. */
|
|
/***********************************************************************/
|
|
int PlugCloseFile(PGLOBAL g, PFBLOCK fp, bool all)
|
|
{
|
|
int rc = 0;
|
|
|
|
if (trace(1))
|
|
htrc("PlugCloseFile: fp=%p count=%hd type=%hd\n",
|
|
fp, ((fp) ? fp->Count : 0), ((fp) ? fp->Type : 0));
|
|
|
|
if (!fp || !fp->Count)
|
|
return rc;
|
|
|
|
switch (fp->Type) {
|
|
case TYPE_FB_FILE:
|
|
if (fclose((FILE *)fp->File) == EOF)
|
|
rc = errno;
|
|
|
|
fp->File = NULL;
|
|
fp->Mode = MODE_ANY;
|
|
fp->Count = 0;
|
|
break;
|
|
case TYPE_FB_MAP:
|
|
if ((fp->Count = (all) ? 0 : fp->Count - 1))
|
|
break;
|
|
|
|
if (CloseMemMap(fp->Memory, fp->Length))
|
|
rc = (int)GetLastError();
|
|
|
|
fp->Memory = NULL;
|
|
fp->Mode = MODE_ANY;
|
|
// fall through
|
|
case TYPE_FB_HANDLE:
|
|
if (fp->Handle && fp->Handle != INVALID_HANDLE_VALUE)
|
|
if (CloseFileHandle(fp->Handle))
|
|
rc = (rc) ? rc : (int)GetLastError();
|
|
|
|
fp->Handle = INVALID_HANDLE_VALUE;
|
|
fp->Mode = MODE_ANY;
|
|
fp->Count = 0;
|
|
break;
|
|
#ifdef DOMDOC_SUPPORT
|
|
case TYPE_FB_XML:
|
|
CloseXMLFile(g, fp, all);
|
|
break;
|
|
#endif // DOMDOC_SUPPORT
|
|
#ifdef LIBXML2_SUPPORT
|
|
case TYPE_FB_XML2:
|
|
CloseXML2File(g, fp, all);
|
|
break;
|
|
#endif // LIBXML2_SUPPORT
|
|
#ifdef ODBC_SUPPORT
|
|
case TYPE_FB_ODBC:
|
|
OdbcClose(g, fp);
|
|
fp->Count = 0;
|
|
fp->File = NULL;
|
|
break;
|
|
#endif // ODBC_SUPPORT
|
|
#ifdef ZIP_SUPPORT
|
|
case TYPE_FB_ZIP:
|
|
if (fp->Mode == MODE_INSERT)
|
|
((ZIPUTIL*)fp->File)->close();
|
|
else
|
|
((UNZIPUTL*)fp->File)->close();
|
|
|
|
fp->Memory = NULL;
|
|
fp->Mode = MODE_ANY;
|
|
fp->Count = 0;
|
|
fp->File = NULL;
|
|
break;
|
|
#endif // ZIP_SUPPORT
|
|
#ifdef JAVA_SUPPORT
|
|
case TYPE_FB_JAVA:
|
|
((JAVAConn*)fp->File)->Close();
|
|
fp->Count = 0;
|
|
fp->File = NULL;
|
|
break;
|
|
#endif // JAVA_SUPPORT
|
|
#ifdef CMGO_SUPPORT
|
|
case TYPE_FB_MONGO:
|
|
((CMgoConn*)fp->File)->Close();
|
|
fp->Count = 0;
|
|
fp->File = NULL;
|
|
break;
|
|
#endif // JAVA_SUPPORT
|
|
default:
|
|
rc = RC_FX;
|
|
} // endswitch Type
|
|
|
|
return rc;
|
|
} // end of PlugCloseFile
|
|
|
|
/***********************************************************************/
|
|
/* PlugCleanup: Cleanup remaining items of a SQL query. */
|
|
/***********************************************************************/
|
|
void PlugCleanup(PGLOBAL g, bool dofree)
|
|
{
|
|
PCATLG cat;
|
|
PDBUSER dbuserp = (PDBUSER)g->Activityp->Aptr;
|
|
|
|
// The test on Catalog is to avoid a Windows bug that can make
|
|
// LoadString in PlugGetMessage to fail in some case
|
|
if (!dbuserp || !(cat = dbuserp->Catalog))
|
|
return;
|
|
|
|
/*********************************************************************/
|
|
/* Close eventually still open/mapped files. */
|
|
/*********************************************************************/
|
|
for (PFBLOCK fp = dbuserp->Openlist; fp; fp = fp->Next)
|
|
PlugCloseFile(g, fp, true);
|
|
|
|
dbuserp->Openlist = NULL;
|
|
|
|
if (dofree) {
|
|
/*******************************************************************/
|
|
/* Cleanup any non suballocated memory still not freed. */
|
|
/*******************************************************************/
|
|
for (PMBLOCK mp = dbuserp->Memlist; mp; mp = mp->Next)
|
|
PlgDBfree(*mp);
|
|
|
|
dbuserp->Memlist = NULL;
|
|
|
|
/*******************************************************************/
|
|
/* If not using permanent storage catalog, reset volatile values. */
|
|
/*******************************************************************/
|
|
cat->Reset();
|
|
|
|
/*******************************************************************/
|
|
/* This is the place to reset the pointer on domains. */
|
|
/*******************************************************************/
|
|
dbuserp->Subcor = false;
|
|
dbuserp->Step = "New query"; // was STEP(PARSING_QUERY);
|
|
dbuserp->ProgMax = dbuserp->ProgCur = dbuserp->ProgSav = 0;
|
|
} // endif dofree
|
|
|
|
} // end of PlugCleanup
|
|
|
|
#if 0
|
|
/***********************************************************************/
|
|
/* That stupid Windows 98 does not provide this function. */
|
|
/***********************************************************************/
|
|
bool WritePrivateProfileInt(LPCSTR sec, LPCSTR key, int n, LPCSTR ini)
|
|
{
|
|
char buf[12];
|
|
|
|
sprintf(buf, "%d", n);
|
|
return WritePrivateProfileString(sec, key, buf, ini);
|
|
} // end of WritePrivateProfileInt
|
|
|
|
/***********************************************************************/
|
|
/* Retrieve a size from an INI file with eventual K or M following. */
|
|
/***********************************************************************/
|
|
int GetIniSize(char *section, char *key, char *def, char *ini)
|
|
{
|
|
char c, buff[32];
|
|
int i;
|
|
int n = 0;
|
|
|
|
GetPrivateProfileString(section, key, def, buff, sizeof(buff), ini);
|
|
|
|
if ((i = sscanf(buff, " %d %c ", &n, &c)) == 2)
|
|
switch (toupper(c)) {
|
|
case 'M':
|
|
n *= 1024;
|
|
case 'K':
|
|
n *= 1024;
|
|
} // endswitch c
|
|
|
|
if (trace(1))
|
|
htrc("GetIniSize: key=%s buff=%s i=%d n=%d\n", key, buff, i, n);
|
|
|
|
return n;
|
|
} // end of GetIniSize
|
|
|
|
/***********************************************************************/
|
|
/* Allocate a string retrieved from an INI file and return its address */
|
|
/***********************************************************************/
|
|
DllExport PSZ GetIniString(PGLOBAL g, void *mp, LPCSTR sec, LPCSTR key,
|
|
LPCSTR def, LPCSTR ini)
|
|
{
|
|
char buff[_MAX_PATH];
|
|
PSZ p;
|
|
int n, m = sizeof(buff);
|
|
char *buf = buff;
|
|
|
|
#if defined(_DEBUG)
|
|
assert (sec && key);
|
|
#endif
|
|
|
|
again:
|
|
n = GetPrivateProfileString(sec, key, def, buf, m, ini);
|
|
|
|
if (n == m - 1) {
|
|
// String may have been truncated, make sure to have all
|
|
if (buf != buff)
|
|
delete [] buf;
|
|
|
|
m *= 2;
|
|
buf = new char[m];
|
|
goto again;
|
|
} // endif n
|
|
|
|
p = (PSZ)PlugSubAlloc(g, mp, n + 1);
|
|
|
|
if (trace(1))
|
|
htrc("GetIniString: sec=%s key=%s buf=%s\n", sec, key, buf);
|
|
|
|
strcpy(p, buf);
|
|
|
|
if (buf != buff)
|
|
delete [] buf;
|
|
|
|
return p;
|
|
} // end of GetIniString
|
|
#endif // 0
|
|
|
|
/***********************************************************************/
|
|
/* GetAmName: return the name correponding to an AM code. */
|
|
/***********************************************************************/
|
|
char *GetAmName(PGLOBAL g, AMT am, void *memp)
|
|
{
|
|
char *amn= (char*)PlugSubAlloc(g, memp, 16);
|
|
|
|
switch (am) {
|
|
case TYPE_AM_ERROR: strcpy(amn, "ERROR"); break;
|
|
case TYPE_AM_ROWID: strcpy(amn, "ROWID"); break;
|
|
case TYPE_AM_FILID: strcpy(amn, "FILID"); break;
|
|
case TYPE_AM_VIEW: strcpy(amn, "VIEW"); break;
|
|
case TYPE_AM_COUNT: strcpy(amn, "COUNT"); break;
|
|
case TYPE_AM_DCD: strcpy(amn, "DCD"); break;
|
|
case TYPE_AM_CMS: strcpy(amn, "CMS"); break;
|
|
case TYPE_AM_MAP: strcpy(amn, "MAP"); break;
|
|
case TYPE_AM_FMT: strcpy(amn, "FMT"); break;
|
|
case TYPE_AM_CSV: strcpy(amn, "CSV"); break;
|
|
case TYPE_AM_MCV: strcpy(amn, "MCV"); break;
|
|
case TYPE_AM_DOS: strcpy(amn, "DOS"); break;
|
|
case TYPE_AM_FIX: strcpy(amn, "FIX"); break;
|
|
case TYPE_AM_BIN: strcpy(amn, "BIN"); break;
|
|
case TYPE_AM_VCT: strcpy(amn, "VEC"); break;
|
|
case TYPE_AM_VMP: strcpy(amn, "VMP"); break;
|
|
case TYPE_AM_DBF: strcpy(amn, "DBF"); break;
|
|
case TYPE_AM_QRY: strcpy(amn, "QRY"); break;
|
|
case TYPE_AM_SQL: strcpy(amn, "SQL"); break;
|
|
case TYPE_AM_PLG: strcpy(amn, "PLG"); break;
|
|
case TYPE_AM_PLM: strcpy(amn, "PLM"); break;
|
|
case TYPE_AM_DOM: strcpy(amn, "DOM"); break;
|
|
case TYPE_AM_DIR: strcpy(amn, "DIR"); break;
|
|
case TYPE_AM_ODBC: strcpy(amn, "ODBC"); break;
|
|
case TYPE_AM_JDBC: strcpy(amn, "JDBC"); break;
|
|
case TYPE_AM_MAC: strcpy(amn, "MAC"); break;
|
|
case TYPE_AM_OEM: strcpy(amn, "OEM"); break;
|
|
case TYPE_AM_OUT: strcpy(amn, "OUT"); break;
|
|
default: sprintf(amn, "OEM(%d)", am);
|
|
} // endswitch am
|
|
|
|
return amn;
|
|
} // end of GetAmName
|
|
|
|
#if defined(SE_CATCH)
|
|
/***********************************************************************/
|
|
/* GetExceptionDesc: return the description of an exception code. */
|
|
/***********************************************************************/
|
|
char *GetExceptionDesc(PGLOBAL g, unsigned int e)
|
|
{
|
|
char *p;
|
|
|
|
switch (e) {
|
|
case EXCEPTION_GUARD_PAGE:
|
|
p = MSG(GUARD_PAGE);
|
|
break;
|
|
case EXCEPTION_DATATYPE_MISALIGNMENT:
|
|
p = MSG(DATA_MISALIGN);
|
|
break;
|
|
case EXCEPTION_BREAKPOINT:
|
|
p = MSG(BREAKPOINT);
|
|
break;
|
|
case EXCEPTION_SINGLE_STEP:
|
|
p = MSG(SINGLE_STEP);
|
|
break;
|
|
case EXCEPTION_ACCESS_VIOLATION:
|
|
p = MSG(ACCESS_VIOLATN);
|
|
break;
|
|
case EXCEPTION_IN_PAGE_ERROR:
|
|
p = MSG(PAGE_ERROR);
|
|
break;
|
|
case EXCEPTION_INVALID_HANDLE:
|
|
p = MSG(INVALID_HANDLE);
|
|
break;
|
|
case EXCEPTION_ILLEGAL_INSTRUCTION:
|
|
p = MSG(ILLEGAL_INSTR);
|
|
break;
|
|
case EXCEPTION_NONCONTINUABLE_EXCEPTION:
|
|
p = MSG(NONCONT_EXCEPT);
|
|
break;
|
|
case EXCEPTION_INVALID_DISPOSITION:
|
|
p = MSG(INVALID_DISP);
|
|
break;
|
|
case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
|
|
p = MSG(ARRAY_BNDS_EXCD);
|
|
break;
|
|
case EXCEPTION_FLT_DENORMAL_OPERAND:
|
|
p = MSG(FLT_DENORMAL_OP);
|
|
break;
|
|
case EXCEPTION_FLT_DIVIDE_BY_ZERO:
|
|
p = MSG(FLT_ZERO_DIVIDE);
|
|
break;
|
|
case EXCEPTION_FLT_INEXACT_RESULT:
|
|
p = MSG(FLT_BAD_RESULT);
|
|
break;
|
|
case EXCEPTION_FLT_INVALID_OPERATION:
|
|
p = MSG(FLT_INVALID_OP);
|
|
break;
|
|
case EXCEPTION_FLT_OVERFLOW:
|
|
p = MSG(FLT_OVERFLOW);
|
|
break;
|
|
case EXCEPTION_FLT_STACK_CHECK:
|
|
p = MSG(FLT_STACK_CHECK);
|
|
break;
|
|
case EXCEPTION_FLT_UNDERFLOW:
|
|
p = MSG(FLT_UNDERFLOW);
|
|
break;
|
|
case EXCEPTION_INT_DIVIDE_BY_ZERO:
|
|
p = MSG(INT_ZERO_DIVIDE);
|
|
break;
|
|
case EXCEPTION_INT_OVERFLOW:
|
|
p = MSG(INT_OVERFLOW);
|
|
break;
|
|
case EXCEPTION_PRIV_INSTRUCTION:
|
|
p = MSG(PRIV_INSTR);
|
|
break;
|
|
case EXCEPTION_STACK_OVERFLOW:
|
|
p = MSG(STACK_OVERFLOW);
|
|
break;
|
|
case CONTROL_C_EXIT:
|
|
p = MSG(CONTROL_C_EXIT);
|
|
break;
|
|
case STATUS_NO_MEMORY:
|
|
p = MSG(NO_MEMORY);
|
|
break;
|
|
default:
|
|
p = MSG(UNKNOWN_EXCPT);
|
|
break;
|
|
} // endswitch nSE
|
|
|
|
return p;
|
|
} // end of GetExceptionDesc
|
|
#endif // SE_CATCH
|
|
|
|
/***********************************************************************/
|
|
/* PlgDBalloc: allocates or suballocates memory conditionally. */
|
|
/* If mp.Sub is true at entry, this forces suballocation. */
|
|
/* If the memory is allocated, makes an entry in an allocation list */
|
|
/* so it can be freed at the normal or error query completion. */
|
|
/***********************************************************************/
|
|
void *PlgDBalloc(PGLOBAL g, void *area, MBLOCK& mp)
|
|
{
|
|
//bool b;
|
|
size_t maxsub, minsub;
|
|
void *arp = (area) ? area : g->Sarea;
|
|
PPOOLHEADER pph = (PPOOLHEADER)arp;
|
|
|
|
if (mp.Memp) {
|
|
// This is a reallocation. If this block is not suballocated, it
|
|
// was already placed in the chain of memory blocks and we must
|
|
// not do it again as it can trigger a loop when freeing them.
|
|
// Note: this works if blocks can be reallocated only once.
|
|
// Otherwise a new boolean must be added to the block that
|
|
// indicate that it is chained, or a test on the whole chain be
|
|
// done to check whether the block is already there.
|
|
// b = mp.Sub;
|
|
mp.Sub = false; // Restrict suballocation to one quarter
|
|
} // endif Memp
|
|
|
|
// Suballoc when possible if mp.Sub is initially true, but leaving
|
|
// a minimum amount of storage for future operations such as the
|
|
// optimize recalculation after insert; otherwise
|
|
// suballoc only if size is smaller than one quarter of free mem.
|
|
minsub = (pph->FreeBlk + pph->To_Free + 524248) >> 2;
|
|
maxsub = (pph->FreeBlk < minsub) ? 0 : pph->FreeBlk - minsub;
|
|
mp.Sub = mp.Size <= ((mp.Sub) ? maxsub : (maxsub >> 2));
|
|
|
|
if (trace(2))
|
|
htrc("PlgDBalloc: in %p size=%zd used=%zd free=%zd sub=%d\n",
|
|
arp, mp.Size, pph->To_Free, pph->FreeBlk, mp.Sub);
|
|
|
|
if (!mp.Sub) {
|
|
// For allocations greater than one fourth of remaining storage
|
|
// in the area, do allocate from virtual storage.
|
|
const char*v = "malloc";
|
|
#if defined(_WIN32)
|
|
if (mp.Size >= BIGMEM) {
|
|
v = "VirtualAlloc";
|
|
mp.Memp = VirtualAlloc(NULL, mp.Size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
|
|
} else
|
|
#endif
|
|
mp.Memp = malloc(mp.Size);
|
|
|
|
if (trace(8))
|
|
htrc("PlgDBalloc: %s(%zd) at %p\n", v, mp.Size, mp.Memp);
|
|
|
|
if (!mp.Inlist && mp.Memp) {
|
|
// New allocated block, put it in the memory block chain.
|
|
PDBUSER dbuserp = (PDBUSER)g->Activityp->Aptr;
|
|
|
|
mp.Next = dbuserp->Memlist;
|
|
dbuserp->Memlist = ∓
|
|
mp.Inlist = true;
|
|
} // endif mp
|
|
|
|
} else
|
|
// Suballocating is Ok.
|
|
mp.Memp = PlugSubAlloc(g, area, mp.Size);
|
|
|
|
return mp.Memp;
|
|
} // end of PlgDBalloc
|
|
|
|
/***********************************************************************/
|
|
/* PlgDBrealloc: reallocates memory conditionally. */
|
|
/* Note that this routine can fail only when block size is increased */
|
|
/* because otherwise we keep the old storage on failure. */
|
|
/***********************************************************************/
|
|
void *PlgDBrealloc(PGLOBAL g, void *area, MBLOCK& mp, size_t newsize)
|
|
{
|
|
MBLOCK m;
|
|
|
|
#if defined(_DEBUG)
|
|
// assert (mp.Memp != NULL);
|
|
#endif
|
|
|
|
if (trace(2))
|
|
htrc("PlgDBrealloc: %p size=%zd sub=%d\n", mp.Memp, mp.Size, mp.Sub);
|
|
|
|
if (newsize == mp.Size)
|
|
return mp.Memp; // Nothing to do
|
|
else
|
|
m = mp;
|
|
|
|
if (!mp.Sub && mp.Size < BIGMEM && newsize < BIGMEM) {
|
|
// Allocation was done by malloc, try to use realloc but
|
|
// suballoc if newsize is smaller than one quarter of free mem.
|
|
size_t maxsub;
|
|
PPOOLHEADER pph = (PPOOLHEADER)((area) ? area : g->Sarea);
|
|
|
|
maxsub = (pph->FreeBlk < 131072) ? 0 : pph->FreeBlk - 131072;
|
|
|
|
if ((mp.Sub = (newsize <= (maxsub >> 2)))) {
|
|
mp.Memp = PlugSubAlloc(g, area, newsize);
|
|
memcpy(mp.Memp, m.Memp, MY_MIN(m.Size, newsize));
|
|
PlgDBfree(m); // Free the old block
|
|
} else {
|
|
if (!(mp.Memp = realloc(mp.Memp, newsize))) {
|
|
mp = m; // Possible only if newsize > Size
|
|
return NULL; // Failed
|
|
} else if (trace(8))
|
|
htrc("PlgDBrealloc: realloc(%ld) at %p\n", newsize, mp.Memp);
|
|
|
|
} // endif's
|
|
|
|
mp.Size = newsize;
|
|
} else if (!mp.Sub || newsize > mp.Size) {
|
|
// Was suballocated or Allocation was done by VirtualAlloc
|
|
// Make a new allocation and copy the useful part
|
|
// Note: DO NOT reset Memp and Sub so we know that this
|
|
// is a reallocation in PlgDBalloc
|
|
mp.Size = newsize;
|
|
|
|
if (PlgDBalloc(g, area, mp)) {
|
|
memcpy(mp.Memp, m.Memp, MY_MIN(m.Size, newsize));
|
|
PlgDBfree(m); // Free the old block
|
|
} else {
|
|
mp = m; // No space to realloc, do nothing
|
|
|
|
if (newsize > m.Size)
|
|
return NULL; // Failed
|
|
|
|
} // endif PlgDBalloc
|
|
|
|
} // endif's
|
|
|
|
if (trace(8))
|
|
htrc(" newsize=%zd newp=%p sub=%d\n", mp.Size, mp.Memp, mp.Sub);
|
|
|
|
return mp.Memp;
|
|
} // end of PlgDBrealloc
|
|
|
|
/***********************************************************************/
|
|
/* PlgDBfree: free memory if not suballocated. */
|
|
/***********************************************************************/
|
|
void PlgDBfree(MBLOCK& mp)
|
|
{
|
|
if (!mp.Sub && mp.Memp) {
|
|
const char*v = "free";
|
|
#if defined(_WIN32)
|
|
if (mp.Size >= BIGMEM) {
|
|
v = "VirtualFree";
|
|
VirtualFree(mp.Memp, 0, MEM_RELEASE);
|
|
} else
|
|
#endif
|
|
free(mp.Memp);
|
|
|
|
if (trace(8))
|
|
htrc("PlgDBfree: %s(%p) size=%d\n", v, mp.Memp, mp.Size);
|
|
|
|
} // endif mp
|
|
|
|
// Do not reset Next to avoid cutting the Mblock chain
|
|
mp.Memp = NULL;
|
|
mp.Sub = false;
|
|
mp.Size = 0;
|
|
} // end of PlgDBfree
|
|
|
|
/***********************************************************************/
|
|
/* Program for sub-allocating one item in a storage area. */
|
|
/* Note: This function is equivalent to PlugSubAlloc except that in */
|
|
/* case of insufficient memory, it returns NULL instead of doing a */
|
|
/* throw. The caller must test the return value for error. */
|
|
/***********************************************************************/
|
|
void *PlgDBSubAlloc(PGLOBAL g, void *memp, size_t size)
|
|
{
|
|
PPOOLHEADER pph; // Points on area header.
|
|
|
|
if (!memp)
|
|
/*******************************************************************/
|
|
/* Allocation is to be done in the Sarea. */
|
|
/*******************************************************************/
|
|
memp = g->Sarea;
|
|
|
|
//size = ((size + 3) / 4) * 4; /* Round up size to multiple of 4 */
|
|
size = ((size + 7) / 8) * 8; /* Round up size to multiple of 8 */
|
|
pph = (PPOOLHEADER)memp;
|
|
|
|
if (trace(16))
|
|
htrc("PlgDBSubAlloc: memp=%p size=%zd used=%zd free=%zd\n",
|
|
memp, size, pph->To_Free, pph->FreeBlk);
|
|
|
|
if (size > pph->FreeBlk) { /* Not enough memory left in pool */
|
|
snprintf(g->Message, sizeof(g->Message),
|
|
"Not enough memory in Work area for request of %zd (used=%zd free=%zd)",
|
|
size, pph->To_Free, pph->FreeBlk);
|
|
|
|
if (trace(1))
|
|
htrc("%s\n", g->Message);
|
|
|
|
return NULL;
|
|
} // endif size
|
|
|
|
/*********************************************************************/
|
|
/* Do the suballocation the simplest way. */
|
|
/*********************************************************************/
|
|
memp = MakePtr(memp, pph->To_Free); // Points to suballocated block
|
|
pph->To_Free += size; // New offset of pool free block
|
|
pph->FreeBlk -= size; // New size of pool free block
|
|
|
|
if (trace(16))
|
|
htrc("Done memp=%p used=%zd free=%zd\n",
|
|
memp, pph->To_Free, pph->FreeBlk);
|
|
|
|
return (memp);
|
|
} // end of PlgDBSubAlloc
|
|
|
|
/***********************************************************************/
|
|
/* Program for sub-allocating and copying a string in a storage area. */
|
|
/***********************************************************************/
|
|
char *PlgDBDup(PGLOBAL g, const char *str)
|
|
{
|
|
if (str) {
|
|
char *sm = (char*)PlgDBSubAlloc(g, NULL, strlen(str) + 1);
|
|
|
|
if (sm)
|
|
strcpy(sm, str);
|
|
|
|
return sm;
|
|
} else
|
|
return NULL;
|
|
|
|
} // end of PlgDBDup
|
|
|
|
/***********************************************************************/
|
|
/* PUTOUT: Plug DB object typing routine. */
|
|
/***********************************************************************/
|
|
void PlugPutOut(PGLOBAL g, FILE *f, short t, void *v, uint n)
|
|
{
|
|
char m[64];
|
|
|
|
if (trace(1))
|
|
htrc("PUTOUT: f=%p t=%d v=%p n=%d\n", f, t, v, n);
|
|
|
|
if (!v)
|
|
return;
|
|
|
|
memset(m, ' ', n); /* Make margin string */
|
|
m[n] = '\0';
|
|
n += 2; /* Increase margin */
|
|
|
|
switch (t) {
|
|
case TYPE_ERROR:
|
|
fprintf(f, "--> %s\n", (PSZ)v);
|
|
break;
|
|
|
|
case TYPE_STRING:
|
|
case TYPE_PSZ:
|
|
fprintf(f, "%s%s\n", m, (PSZ)v);
|
|
break;
|
|
|
|
case TYPE_DOUBLE:
|
|
fprintf(f, "%s%lf\n", m, *(double *)v);
|
|
break;
|
|
|
|
case TYPE_LIST:
|
|
case TYPE_COLIST:
|
|
case TYPE_COL:
|
|
{PPARM p;
|
|
|
|
if (t == TYPE_LIST)
|
|
fprintf(f, "%s%s\n", m, MSG(LIST));
|
|
else
|
|
fprintf(f, "%s%s\n", m, "Colist:");
|
|
|
|
for (p = (PPARM)v; p; p = p->Next)
|
|
PlugPutOut(g, f, p->Type, p->Value, n);
|
|
|
|
} break;
|
|
|
|
case TYPE_INT:
|
|
fprintf(f, "%s%d\n", m, *(int *)v);
|
|
break;
|
|
|
|
case TYPE_SHORT:
|
|
fprintf(f, "%s%hd\n", m, *(short *)v);
|
|
break;
|
|
|
|
case TYPE_TINY:
|
|
fprintf(f, "%s%d\n", m, (int)*(char *)v);
|
|
break;
|
|
|
|
case TYPE_VOID:
|
|
break;
|
|
|
|
case TYPE_SQL:
|
|
case TYPE_TABLE:
|
|
case TYPE_TDB:
|
|
case TYPE_XOBJECT:
|
|
((PBLOCK)v)->Printf(g, f, n-2);
|
|
break;
|
|
|
|
default:
|
|
fprintf(f, "%s%s %d\n", m, MSG(ANSWER_TYPE), t);
|
|
} /* endswitch */
|
|
|
|
return;
|
|
} /* end of PlugPutOut */
|
|
|
|
/***********************************************************************/
|
|
/* NewPointer: makes a table of pointer values to be changed later. */
|
|
/***********************************************************************/
|
|
DllExport void NewPointer(PTABS t, void *oldv, void *newv)
|
|
{
|
|
PTABPTR tp;
|
|
|
|
if (!oldv) /* error ?????????? */
|
|
return;
|
|
|
|
if (!t->P1 || t->P1->Num == 50)
|
|
{
|
|
if (!(tp = new TABPTR)) {
|
|
PGLOBAL g = t->G;
|
|
|
|
snprintf(g->Message, sizeof(g->Message), "NewPointer: %s", MSG(MEM_ALLOC_ERROR));
|
|
throw 3;
|
|
} else {
|
|
tp->Next = t->P1;
|
|
tp->Num = 0;
|
|
t->P1 = tp;
|
|
} /* endif tp */
|
|
}
|
|
|
|
t->P1->Old[t->P1->Num] = oldv;
|
|
t->P1->New[t->P1->Num++] = newv;
|
|
} /* end of NewPointer */
|
|
|
|
#if 0
|
|
/***********************************************************************/
|
|
/* Compare two files and return 0 if they are identical, else 1. */
|
|
/***********************************************************************/
|
|
int FileComp(PGLOBAL g, char *file1, char *file2)
|
|
{
|
|
char *fn[2], *bp[2], buff1[4096], buff2[4096];
|
|
int i, k, n[2], h[2] = {-1,-1};
|
|
int len[2], rc = -1;
|
|
|
|
fn[0] = file1; fn[1] = file2;
|
|
bp[0] = buff1; bp[1] = buff2;
|
|
|
|
for (i = 0; i < 2; i++) {
|
|
#if defined(_WIN32)
|
|
h[i]= global_open(g, MSGID_NONE, fn[i], _O_RDONLY | _O_BINARY);
|
|
#else // !_WIN32
|
|
h[i]= global_open(g, MSGOD_NONE, fn[i], O_RDONLY);
|
|
#endif // !_WIN32
|
|
|
|
if (h[i] == -1) {
|
|
// if (errno != ENOENT) {
|
|
snprintf(g->Message, sizeof(g->Message), MSG(OPEN_MODE_ERROR),
|
|
"rb", (int)errno, fn[i]);
|
|
strcat(strcat(g->Message, ": "), strerror(errno));
|
|
throw 666;
|
|
// } else
|
|
// len[i] = 0; // File does not exist yet
|
|
|
|
} else {
|
|
if ((len[i] = _filelength(h[i])) < 0) {
|
|
snprintf(g->Message, sizeof(g->Message), MSG(FILELEN_ERROR), "_filelength", fn[i]);
|
|
throw 666;
|
|
} // endif len
|
|
|
|
} // endif h
|
|
|
|
} // endfor i
|
|
|
|
if (len[0] != len[1])
|
|
rc = 1;
|
|
|
|
while (rc == -1) {
|
|
for (i = 0; i < 2; i++)
|
|
if ((n[i] = read(h[i], bp[i], 4096)) < 0) {
|
|
snprintf(g->Message, sizeof(g->Message), MSG(READ_ERROR), fn[i], strerror(errno));
|
|
goto fin;
|
|
} // endif n
|
|
|
|
if (n[0] != n[1])
|
|
rc = 1;
|
|
else if (*n == 0)
|
|
rc = 0;
|
|
else for (k = 0; k < *n; k++)
|
|
if (*(bp[0] + k) != *(bp[1] + k)) {
|
|
rc = 1;
|
|
goto fin;
|
|
} // endif bp
|
|
|
|
} // endwhile
|
|
|
|
fin:
|
|
for (i = 0; i < 2; i++)
|
|
if (h[i] != -1)
|
|
close(h[i]);
|
|
|
|
return rc;
|
|
} // end of FileComp
|
|
#endif // 0
|