mariadb/storage/connect/plugutil.cpp
Olivier Bertrand dc3a693b70 - Inline MakePtr and MakeOff with OFFSET as size_t
Also add a new member Saved_Size in the Global structure.
  modified:   storage/connect/global.h
  modified:   storage/connect/plugutil.cpp
  modified:   storage/connect/user_connect.cc
  modified:   storage/connect/jsonudf.cpp

- Add session variables json_all_path and default_depth
  modified:   storage/connect/ha_connect.cc
  modified:   storage/connect/mongo.cpp
  modified:   storage/connect/tabjson.cpp
  modified:   storage/connect/tabxml.cpp

- ADD column options JPATH and XPATH
  Work as FIELD_FORMAT but are more readable
  modified:   storage/connect/ha_connect.cc
  modified:   storage/connect/ha_connect.h
  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

- Handle negative numbes in the option list
  modified:   storage/connect/ha_connect.cc

- Fix Json parse that could crash the server.
  Was because it could use THROW out of the TRY block.
  Also handle all error by THROW.
  It is now done by a new class JSON.
  modified:   storage/connect/json.cpp
  modified:   storage/connect/json.h

- Add a new UDF function jfile_translate.
  It translate a Json file to pretty = 0.
  Fast because it does not a real parse of the file.
  modified:   storage/connect/jsonudf.cpp
  modified:   storage/connect/jsonudf.h

- Add a now options JSIZE and STRINGIFY to Json tables.
  STRINGIFY makes Objects or Arrays to be returned by their
  json representation instead of by their concatenated values.
  JSIZE allows to specify the LRECL (was 256) defaults to 1024.
  Also fix a bug about locating the sub-table by its path.
  modified:   storage/connect/tabjson.cpp
  modified:   storage/connect/tabjson.h
2020-10-18 17:20:44 +02:00

609 lines
20 KiB
C++

/************** PlugUtil C Program Source Code File (.C) ***************/
/* */
/* PROGRAM NAME: PLUGUTIL */
/* ------------- */
/* Version 3.1 */
/* */
/* COPYRIGHT: */
/* ---------- */
/* (C) Copyright to the author Olivier BERTRAND 1993-2020 */
/* */
/* WHAT THIS PROGRAM DOES: */
/* ----------------------- */
/* This program are initialization and utility Plug routines. */
/* */
/* WHAT YOU NEED TO COMPILE THIS PROGRAM: */
/* -------------------------------------- */
/* */
/* REQUIRED FILES: */
/* --------------- */
/* See Readme.C for a list and description of required SYSTEM files. */
/* */
/* PLUG.C - Source code */
/* GLOBAL.H - Global declaration file */
/* OPTION.H - Option declaration file */
/* */
/* REQUIRED LIBRARIES: */
/* ------------------- */
/* */
/* OS2.LIB - OS2 libray */
/* LLIBCE.LIB - Protect mode/standard combined large model C */
/* library */
/* */
/* REQUIRED PROGRAMS: */
/* ------------------ */
/* */
/* IBM C Compiler */
/* IBM Linker */
/* */
/***********************************************************************/
/***********************************************************************/
/* */
/* Include relevant MariaDB header file. */
/* */
/***********************************************************************/
#include "my_global.h"
#if defined(__WIN__)
//#include <windows.h>
#else
#if defined(UNIX) || defined(UNIV_LINUX)
#include <errno.h>
#include <unistd.h>
//#define __stdcall
#else
#include <dir.h>
#endif
#include <stdarg.h>
#endif
#if defined(WIN)
#include <alloc.h>
#endif
#include <errno.h> /* definitions of ERANGE ENOMEM */
#if !defined(UNIX) && !defined(UNIV_LINUX)
#include <direct.h> /* Directory management library */
#endif
/***********************************************************************/
/* */
/* Include application header files */
/* */
/* global.h is header containing all global declarations. */
/* */
/***********************************************************************/
#define STORAGE /* Initialize global variables */
#include "osutil.h"
#include "global.h"
#include "plgdbsem.h"
#if defined(NEWMSG)
#include "rcmsg.h"
#endif // NEWMSG
#if defined(__WIN__)
extern HINSTANCE s_hModule; /* Saved module handle */
#endif // __WIN__
#if defined(XMSG)
extern char *msg_path;
char *msglang(void);
#endif // XMSG
/***********************************************************************/
/* Local Definitions and static variables */
/***********************************************************************/
typedef struct {
ushort Segsize;
ushort Size;
} AREASIZE;
ACTIVITY defActivity = { /* Describes activity and language */
NULL, /* Points to user work area(s) */
"Unknown"}; /* Application name */
#if defined(XMSG) || defined(NEWMSG)
static char stmsg[200];
#endif // XMSG || NEWMSG
#if defined(UNIX) || defined(UNIV_LINUX)
#include "rcmsg.h"
#endif // UNIX
/**************************************************************************/
/* Conditional tracing output function. */
/**************************************************************************/
void xtrc(uint x, char const *fmt, ...)
{
if (GetTraceValue() & x) {
va_list ap;
va_start(ap, fmt);
vfprintf(stderr, fmt, ap);
va_end(ap);
} // endif x
} // end of xtrc
/**************************************************************************/
/* Tracing output function. */
/**************************************************************************/
void htrc(char const* fmt, ...)
{
va_list ap;
va_start(ap, fmt);
vfprintf(stderr, fmt, ap);
va_end(ap);
} // end of htrc
/***********************************************************************/
/* Plug initialization routine. */
/* Language points on initial language name and eventual path. */
/* Return value is the pointer to the Global structure. */
/***********************************************************************/
PGLOBAL PlugInit(LPCSTR Language, size_t worksize)
{
PGLOBAL g;
if (trace(2))
htrc("PlugInit: Language='%s'\n",
((!Language) ? "Null" : (char*)Language));
try {
g = new GLOBAL;
} catch (...) {
fprintf(stderr, MSG(GLOBAL_ERROR), (int)sizeof(GLOBAL));
return NULL;
} // end try/catch
g->Sarea = NULL;
g->Createas = false;
g->Alchecked = 0;
g->Mrr = 0;
g->Activityp = NULL;
g->Xchk = NULL;
g->N = 0;
g->More = 0;
g->Saved_Size = 0;
strcpy(g->Message, "");
/*******************************************************************/
/* Allocate the main work segment. */
/*******************************************************************/
if (worksize && AllocSarea(g, worksize)) {
char errmsg[MAX_STR];
snprintf(errmsg, sizeof(errmsg) - 1, MSG(WORK_AREA), g->Message);
strcpy(g->Message, errmsg);
} // endif Sarea
g->jump_level = -1; /* New setting to allow recursive call of Plug */
return(g);
} /* end of PlugInit */
/***********************************************************************/
/* PlugExit: Terminate Plug operations. */
/***********************************************************************/
int PlugExit(PGLOBAL g)
{
if (g) {
PDBUSER dup = PlgGetUser(g);
if (dup)
free(dup);
FreeSarea(g);
delete g;
} // endif g
return 0;
} // end of PlugExit
/***********************************************************************/
/* Remove the file type from a file name. */
/* Note: this routine is not really implemented for Unix. */
/***********************************************************************/
LPSTR PlugRemoveType(LPSTR pBuff, LPCSTR FileName)
{
#if defined(__WIN__)
char drive[_MAX_DRIVE];
#else
char *drive = NULL;
#endif
char direc[_MAX_DIR];
char fname[_MAX_FNAME];
char ftype[_MAX_EXT];
_splitpath(FileName, drive, direc, fname, ftype);
if (trace(2)) {
htrc("after _splitpath: FileName=%s\n", FileName);
htrc("drive=%s dir=%s fname=%s ext=%s\n",
SVP(drive), direc, fname, ftype);
} // endif trace
_makepath(pBuff, drive, direc, fname, "");
if (trace(2))
htrc("buff='%s'\n", pBuff);
return pBuff;
} // end of PlugRemoveType
BOOL PlugIsAbsolutePath(LPCSTR path)
{
#if defined(__WIN__)
return ((path[0] >= 'a' && path[0] <= 'z') ||
(path[0] >= 'A' && path[0] <= 'Z')) && path[1] == ':';
#else
return path[0] == '/';
#endif
}
/***********************************************************************/
/* Set the full path of a file relatively to a given path. */
/* Note: this routine is not really implemented for Unix. */
/***********************************************************************/
LPCSTR PlugSetPath(LPSTR pBuff, LPCSTR prefix, LPCSTR FileName, LPCSTR defpath)
{
char newname[_MAX_PATH];
char direc[_MAX_DIR], defdir[_MAX_DIR], tmpdir[_MAX_DIR];
char fname[_MAX_FNAME];
char ftype[_MAX_EXT];
#if defined(__WIN__)
char drive[_MAX_DRIVE], defdrv[_MAX_DRIVE];
#else
char *drive = NULL, *defdrv = NULL;
#endif
if (trace(2))
htrc("prefix=%s fn=%s path=%s\n", prefix, FileName, defpath);
if (!strncmp(FileName, "//", 2) || !strncmp(FileName, "\\\\", 2)) {
strcpy(pBuff, FileName); // Remote file
return pBuff;
} // endif
if (PlugIsAbsolutePath(FileName))
{
strcpy(pBuff, FileName); // FileName includes absolute path
return pBuff;
} // endif
#if !defined(__WIN__)
if (*FileName == '~') {
if (_fullpath(pBuff, FileName, _MAX_PATH)) {
if (trace(2))
htrc("pbuff='%s'\n", pBuff);
return pBuff;
} else
return FileName; // Error, return unchanged name
} // endif FileName
#endif // !__WIN__
if (prefix && strcmp(prefix, ".") && !PlugIsAbsolutePath(defpath))
{
char tmp[_MAX_PATH];
int len= snprintf(tmp, sizeof(tmp) - 1, "%s%s%s",
prefix, defpath, FileName);
memcpy(pBuff, tmp, (size_t) len);
pBuff[len]= '\0';
return pBuff;
}
_splitpath(FileName, drive, direc, fname, ftype);
if (defpath) {
char c = defpath[strlen(defpath) - 1];
strcpy(tmpdir, defpath);
if (c != '/' && c != '\\')
strcat(tmpdir, "/");
} else
strcpy(tmpdir, "./");
_splitpath(tmpdir, defdrv, defdir, NULL, NULL);
if (trace(2)) {
htrc("after _splitpath: FileName=%s\n", FileName);
#if defined(__WIN__)
htrc("drive=%s dir=%s fname=%s ext=%s\n", drive, direc, fname, ftype);
htrc("defdrv=%s defdir=%s\n", defdrv, defdir);
#else
htrc("dir=%s fname=%s ext=%s\n", direc, fname, ftype);
#endif
} // endif trace
if (drive && !*drive)
strcpy(drive, defdrv);
switch (*direc) {
case '\0':
strcpy(direc, defdir);
break;
case '\\':
case '/':
break;
default:
// This supposes that defdir ends with a SLASH
strcpy(direc, strcat(defdir, direc));
} // endswitch
_makepath(newname, drive, direc, fname, ftype);
if (trace(2))
htrc("newname='%s'\n", newname);
if (_fullpath(pBuff, newname, _MAX_PATH)) {
if (trace(2))
htrc("pbuff='%s'\n", pBuff);
return pBuff;
} else
return FileName; // Error, return unchanged name
} // end of PlugSetPath
#if defined(XMSG)
/***********************************************************************/
/* PlugGetMessage: get a message from the message file. */
/***********************************************************************/
char *PlugReadMessage(PGLOBAL g, int mid, char *m)
{
char msgfile[_MAX_PATH], msgid[32], buff[256];
char *msg;
FILE *mfile = NULL;
//GetPrivateProfileString("Message", msglang, "Message\\english.msg",
// msgfile, _MAX_PATH, plgini);
//strcat(strcat(strcpy(msgfile, msg_path), msglang()), ".msg");
strcat(strcpy(buff, msglang()), ".msg");
PlugSetPath(msgfile, NULL, buff, msg_path);
if (!(mfile = fopen(msgfile, "rt"))) {
sprintf(stmsg, "Fail to open message file %s", msgfile);
goto err;
} // endif mfile
for (;;)
if (!fgets(buff, 256, mfile)) {
sprintf(stmsg, "Cannot get message %d %s", mid, SVP(m));
goto fin;
} else
if (atoi(buff) == mid)
break;
if (sscanf(buff, " %*d %s \"%[^\"]", msgid, stmsg) < 2) {
// Old message file
if (!sscanf(buff, " %*d \"%[^\"]", stmsg)) {
sprintf(stmsg, "Bad message file for %d %s", mid, SVP(m));
goto fin;
} else
m = NULL;
} // endif sscanf
if (m && strcmp(m, msgid)) {
// Message file is out of date
strcpy(stmsg, m);
goto fin;
} // endif m
fin:
fclose(mfile);
err:
if (g) {
// Called by STEP
msg = PlugDup(g, stmsg);
} else // Called by MSG or PlgGetErrorMsg
msg = stmsg;
return msg;
} // end of PlugReadMessage
#elif defined(NEWMSG)
/***********************************************************************/
/* PlugGetMessage: get a message from the resource string table. */
/***********************************************************************/
char *PlugGetMessage(PGLOBAL g, int mid)
{
char *msg;
#if 0 // was !defined(UNIX) && !defined(UNIV_LINUX)
int n = LoadString(s_hModule, (uint)mid, (LPTSTR)stmsg, 200);
if (n == 0) {
DWORD rc = GetLastError();
msg = (char*)PlugSubAlloc(g, NULL, 512); // Extend buf allocation
n = sprintf(msg, "Message %d, rc=%d: ", mid, rc);
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS, NULL, rc, 0,
(LPTSTR)(msg + n), 512 - n, NULL);
return msg;
} // endif n
#else // ALL
if (!GetRcString(mid, stmsg, 200))
sprintf(stmsg, "Message %d not found", mid);
#endif // ALL
if (g) {
// Called by STEP
msg = PlugDup(g, stmsg);
} else // Called by MSG or PlgGetErrorMsg
msg = stmsg;
return msg;
} // end of PlugGetMessage
#endif // NEWMSG
#if defined(__WIN__)
/***********************************************************************/
/* Return the line length of the console screen buffer. */
/***********************************************************************/
short GetLineLength(PGLOBAL g)
{
CONSOLE_SCREEN_BUFFER_INFO coninfo;
HANDLE hcons = GetStdHandle(STD_OUTPUT_HANDLE);
BOOL b = GetConsoleScreenBufferInfo(hcons, &coninfo);
return (b) ? coninfo.dwSize.X : 0;
} // end of GetLineLength
#endif // __WIN__
/***********************************************************************/
/* Program for memory allocation of work and language areas. */
/***********************************************************************/
bool AllocSarea(PGLOBAL g, size_t size)
{
/*********************************************************************/
/* This is the allocation routine for the WIN32/UNIX/AIX version. */
/*********************************************************************/
#if defined(__WIN__)
if (size >= 1048576) // 1M
g->Sarea = VirtualAlloc(NULL, size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
else
#endif
g->Sarea = malloc(size);
if (!g->Sarea) {
sprintf(g->Message, MSG(MALLOC_ERROR), "malloc");
g->Sarea_Size = 0;
} else
g->Sarea_Size = size;
#if defined(DEVELOPMENT)
if (true) {
#else
if (trace(8)) {
#endif
if (g->Sarea)
htrc("Work area of %zd allocated at %p\n", size, g->Sarea);
else
htrc("SareaAlloc: %s\n", g->Message);
} // endif trace
return (!g->Sarea);
} // end of AllocSarea
/***********************************************************************/
/* Program for memory freeing the work area. */
/***********************************************************************/
void FreeSarea(PGLOBAL g)
{
if (g->Sarea) {
#if defined(__WIN__)
if (g->Sarea_Size >= 1048576) // 1M
VirtualFree(g->Sarea, 0, MEM_RELEASE);
else
#endif
free(g->Sarea);
#if defined(DEVELOPMENT)
if (true)
#else
if (trace(8))
#endif
htrc("Freeing Sarea at %p size = %zd\n", g->Sarea, g->Sarea_Size);
g->Sarea = NULL;
g->Sarea_Size = 0;
} // endif Sarea
return;
} // end of FreeSarea
/***********************************************************************/
/* Program for SubSet initialization of memory pools. */
/* Here there should be some verification done such as validity of */
/* the address and size not larger than memory size. */
/***********************************************************************/
BOOL PlugSubSet(void *memp, size_t size)
{
PPOOLHEADER pph = (PPOOLHEADER)memp;
pph->To_Free = (size_t)sizeof(POOLHEADER);
pph->FreeBlk = size - pph->To_Free;
return FALSE;
} /* end of PlugSubSet */
/***********************************************************************/
/* Use it to export a function that do throwing. */
/***********************************************************************/
static void *DoThrow(int n)
{
throw n;
} /* end of DoThrow */
/***********************************************************************/
/* Program for sub-allocating one item in a storage area. */
/* The simple way things are done here is based on the fact */
/* that no freeing of suballocated blocks is permitted in CONNECT. */
/***********************************************************************/
void *PlugSubAlloc(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 + 7) / 8) * 8; /* Round up size to multiple of 8 */
pph = (PPOOLHEADER)memp;
if (trace(16))
htrc("SubAlloc in %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 */
PCSZ pname = "Work";
sprintf(g->Message,
"Not enough memory in %s area for request of %zd (used=%zd free=%zd)",
pname, size, pph->To_Free, pph->FreeBlk);
if (trace(1))
htrc("PlugSubAlloc: %s\n", g->Message);
DoThrow(1234);
} /* endif size OS32 code */
/*********************************************************************/
/* 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 PlugSubAlloc */
/***********************************************************************/
/* Program for sub-allocating and copying a string in a storage area. */
/***********************************************************************/
char *PlugDup(PGLOBAL g, const char *str)
{
if (str) {
char *sm = (char*)PlugSubAlloc(g, NULL, strlen(str) + 1);
strcpy(sm, str);
return sm;
} else
return NULL;
} // end of PlugDup
/*--------------------- End of PLUGUTIL program -----------------------*/