mirror of
https://github.com/MariaDB/server.git
synced 2025-01-16 12:02:42 +01:00
50c8ef01fc
- agressively -> aggressively - exising -> existing - occured -> occurred - releated -> related - seperated -> separated - sucess -> success - use use -> use All new code of the whole pull request, including one or several files that are either new files or modified ones, are contributed under the BSD-new license. I am contributing on behalf of my employer Amazon Web Services, Inc.
364 lines
11 KiB
C++
364 lines
11 KiB
C++
/************** tabrest C++ Program Source Code File (.CPP) ************/
|
|
/* PROGRAM NAME: tabrest Version 2.1 */
|
|
/* (C) Copyright to the author Olivier BERTRAND 2018 - 2021 */
|
|
/* This program is the REST Web API support for MariaDB. */
|
|
/* The way Connect handles NOSQL data returned by REST queries is */
|
|
/* just by retrieving it as a file and then leave the existing data */
|
|
/* type tables (JSON, XML or CSV) process it as usual. */
|
|
/***********************************************************************/
|
|
|
|
/***********************************************************************/
|
|
/* Definitions needed by the included files. */
|
|
/***********************************************************************/
|
|
#include <my_global.h> // All MariaDB stuff
|
|
#include <mysqld.h>
|
|
#include <mysqld_error.h>
|
|
#include <sql_error.h>
|
|
#if !defined(_WIN32) && !defined(_WINDOWS)
|
|
#include <sys/types.h>
|
|
#include <sys/wait.h>
|
|
#endif // !_WIN32 && !_WINDOWS
|
|
|
|
/***********************************************************************/
|
|
/* Include application header files: */
|
|
/* global.h is header containing all global declarations. */
|
|
/* plgdbsem.h is header containing the DB application declarations. */
|
|
/***********************************************************************/
|
|
#include "global.h"
|
|
#include "plgdbsem.h"
|
|
#include "xtable.h"
|
|
#include "filamtxt.h"
|
|
#include "tabdos.h"
|
|
#include "plgxml.h"
|
|
#if defined(XML_SUPPORT)
|
|
#include "tabxml.h"
|
|
#endif // XML_SUPPORT
|
|
#include "tabjson.h"
|
|
#include "tabfmt.h"
|
|
#include "tabrest.h"
|
|
|
|
#if defined(connect_EXPORTS)
|
|
#define PUSH_WARNING(M) push_warning(current_thd, Sql_condition::WARN_LEVEL_NOTE, ER_UNKNOWN_ERROR, M)
|
|
#else
|
|
#define PUSH_WARNING(M) htrc(M)
|
|
#endif
|
|
|
|
static XGETREST getRestFnc = NULL;
|
|
static int Xcurl(PGLOBAL g, PCSZ Http, PCSZ Uri, PCSZ filename);
|
|
|
|
/***********************************************************************/
|
|
/* Xcurl: retrieve the REST answer by executing cURL. */
|
|
/***********************************************************************/
|
|
int Xcurl(PGLOBAL g, PCSZ Http, PCSZ Uri, PCSZ filename)
|
|
{
|
|
char buf[512];
|
|
int rc = 0;
|
|
|
|
if (strchr(filename, '"')) {
|
|
strcpy(g->Message, "Invalid file name");
|
|
return 1;
|
|
} // endif filename
|
|
|
|
if (Uri) {
|
|
if (*Uri == '/' || Http[strlen(Http) - 1] == '/')
|
|
my_snprintf(buf, sizeof(buf)-1, "%s%s", Http, Uri);
|
|
else
|
|
my_snprintf(buf, sizeof(buf)-1, "%s/%s", Http, Uri);
|
|
|
|
} else
|
|
my_snprintf(buf, sizeof(buf)-1, "%s", Http);
|
|
|
|
#if defined(_WIN32)
|
|
char cmd[1024];
|
|
STARTUPINFO si;
|
|
PROCESS_INFORMATION pi;
|
|
|
|
sprintf(cmd, "curl \"%s\" -o \"%s\"", buf, filename);
|
|
|
|
ZeroMemory(&si, sizeof(si));
|
|
si.cb = sizeof(si);
|
|
ZeroMemory(&pi, sizeof(pi));
|
|
|
|
// Start the child process.
|
|
if (CreateProcess(NULL, cmd, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi)) {
|
|
// Wait until child process exits.
|
|
WaitForSingleObject(pi.hProcess, INFINITE);
|
|
|
|
// Close process and thread handles.
|
|
CloseHandle(pi.hProcess);
|
|
CloseHandle(pi.hThread);
|
|
} else {
|
|
snprintf(g->Message, sizeof(g->Message), "CreateProcess curl failed (%d)", GetLastError());
|
|
rc = 1;
|
|
} // endif CreateProcess
|
|
#else // !_WIN32
|
|
char fn[600];
|
|
pid_t pID;
|
|
|
|
// Check if curl package is availabe by executing subprocess
|
|
FILE *f= popen("command -v curl", "r");
|
|
|
|
if (!f) {
|
|
strcpy(g->Message, "Problem in allocating memory.");
|
|
return 1;
|
|
} else {
|
|
char temp_buff[50];
|
|
size_t len = fread(temp_buff,1, 50, f);
|
|
|
|
if(!len) {
|
|
strcpy(g->Message, "Curl not installed.");
|
|
return 1;
|
|
} else
|
|
pclose(f);
|
|
|
|
} // endif f
|
|
|
|
#ifdef HAVE_VFORK
|
|
pID = vfork();
|
|
#else
|
|
pID = fork();
|
|
#endif
|
|
sprintf(fn, "-o%s", filename);
|
|
|
|
if (pID == 0) {
|
|
// Code executed by child process
|
|
execlp("curl", "curl", buf, fn, (char*)NULL);
|
|
|
|
// If execlp() is successful, we should not reach this next line.
|
|
strcpy(g->Message, "Unsuccessful execlp from vfork()");
|
|
exit(1);
|
|
} else if (pID < 0) {
|
|
// failed to fork
|
|
strcpy(g->Message, "Failed to fork");
|
|
rc = 1;
|
|
} else {
|
|
// Parent process
|
|
wait(NULL); // Wait for the child to terminate
|
|
} // endif pID
|
|
#endif // !_WIN32
|
|
|
|
return rc;
|
|
} // end of Xcurl
|
|
|
|
/***********************************************************************/
|
|
/* GetREST: load the Rest lib and get the Rest function. */
|
|
/***********************************************************************/
|
|
XGETREST GetRestFunction(PGLOBAL g)
|
|
{
|
|
if (getRestFnc)
|
|
return getRestFnc;
|
|
|
|
#if !defined(REST_SOURCE)
|
|
if (trace(515))
|
|
htrc("Looking for GetRest library\n");
|
|
|
|
#if defined(_WIN32) || defined(_WINDOWS)
|
|
HANDLE Hdll;
|
|
const char* soname = "GetRest.dll"; // Module name
|
|
|
|
if (!(Hdll = LoadLibrary(soname))) {
|
|
char buf[256];
|
|
DWORD rc = GetLastError();
|
|
|
|
snprintf(g->Message, sizeof(g->Message), MSG(DLL_LOAD_ERROR), rc, soname);
|
|
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
|
|
FORMAT_MESSAGE_IGNORE_INSERTS, NULL, rc, 0,
|
|
(LPTSTR)buf, sizeof(buf), NULL);
|
|
strcat(strcat(g->Message, ": "), buf);
|
|
return NULL;
|
|
} // endif Hdll
|
|
|
|
// Get the function returning an instance of the external DEF class
|
|
if (!(getRestFnc = (XGETREST)GetProcAddress((HINSTANCE)Hdll, "restGetFile"))) {
|
|
char buf[256];
|
|
DWORD rc = GetLastError();
|
|
|
|
snprintf(g->Message, sizeof(g->Message), MSG(PROCADD_ERROR), rc, "restGetFile");
|
|
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
|
|
FORMAT_MESSAGE_IGNORE_INSERTS, NULL, rc, 0,
|
|
(LPTSTR)buf, sizeof(buf), NULL);
|
|
strcat(strcat(g->Message, ": "), buf);
|
|
FreeLibrary((HMODULE)Hdll);
|
|
return NULL;
|
|
} // endif getRestFnc
|
|
#else // !_WIN32
|
|
void* Hso;
|
|
const char* error = NULL;
|
|
const char* soname = "GetRest.so"; // Module name
|
|
|
|
// Load the desired shared library
|
|
if (!(Hso = dlopen(soname, RTLD_LAZY))) {
|
|
error = dlerror();
|
|
snprintf(g->Message, sizeof(g->Message), MSG(SHARED_LIB_ERR), soname, SVP(error));
|
|
return NULL;
|
|
} // endif Hdll
|
|
|
|
// Get the function returning an instance of the external DEF class
|
|
if (!(getRestFnc = (XGETREST)dlsym(Hso, "restGetFile"))) {
|
|
error = dlerror();
|
|
snprintf(g->Message, sizeof(g->Message), MSG(GET_FUNC_ERR), "restGetFile", SVP(error));
|
|
dlclose(Hso);
|
|
return NULL;
|
|
} // endif getdef
|
|
#endif // !_WIN32
|
|
#else // REST_SOURCE
|
|
getRestFnc = restGetFile;
|
|
#endif // REST_SOURCE
|
|
|
|
return getRestFnc;
|
|
} // end of GetRestFunction
|
|
|
|
/***********************************************************************/
|
|
/* Return the columns definition to MariaDB. */
|
|
/***********************************************************************/
|
|
PQRYRES RESTColumns(PGLOBAL g, PTOS tp, char *tab, char *db, bool info)
|
|
{
|
|
PQRYRES qrp= NULL;
|
|
char filename[_MAX_PATH + 1]; // MAX PATH ???
|
|
int rc;
|
|
PCSZ http, uri, fn, ftype;
|
|
XGETREST grf = NULL;
|
|
bool curl = GetBooleanTableOption(g, tp, "Curl", false);
|
|
|
|
if (!curl && !(grf = GetRestFunction(g)))
|
|
curl = true;
|
|
|
|
http = GetStringTableOption(g, tp, "Http", NULL);
|
|
uri = GetStringTableOption(g, tp, "Uri", NULL);
|
|
ftype = GetStringTableOption(g, tp, "Type", "JSON");
|
|
fn = GetStringTableOption(g, tp, "Filename", NULL);
|
|
|
|
if (!fn) {
|
|
int n, m = strlen(ftype) + 1;
|
|
|
|
strcat(strcpy(filename, tab), ".");
|
|
n = strlen(filename);
|
|
|
|
// Fold ftype to lower case
|
|
for (int i = 0; i < m; i++)
|
|
filename[n + i] = tolower(ftype[i]);
|
|
|
|
fn = filename;
|
|
tp->subtype = PlugDup(g, fn);
|
|
snprintf(g->Message, sizeof(g->Message), "No file name. Table will use %s", fn);
|
|
PUSH_WARNING(g->Message);
|
|
} // endif fn
|
|
|
|
// We used the file name relative to recorded datapath
|
|
PlugSetPath(filename, fn, db);
|
|
remove(filename);
|
|
|
|
// Retrieve the file from the web and copy it locally
|
|
if (curl)
|
|
rc = Xcurl(g, http, uri, filename);
|
|
else
|
|
rc = grf(g->Message, trace(515), http, uri, filename);
|
|
|
|
if (rc) {
|
|
strcpy(g->Message, "Cannot access to curl nor casablanca");
|
|
return NULL;
|
|
} else if (!stricmp(ftype, "JSON"))
|
|
qrp = JSONColumns(g, db, NULL, tp, info);
|
|
else if (!stricmp(ftype, "CSV"))
|
|
qrp = CSVColumns(g, NULL, tp, info);
|
|
#if defined(XML_SUPPORT)
|
|
else if (!stricmp(ftype, "XML"))
|
|
qrp = XMLColumns(g, db, tab, tp, info);
|
|
#endif // XML_SUPPORT
|
|
else
|
|
snprintf(g->Message, sizeof(g->Message), "Usupported file type %s", ftype);
|
|
|
|
return qrp;
|
|
} // end of RESTColumns
|
|
|
|
/* -------------------------- Class RESTDEF -------------------------- */
|
|
|
|
/***********************************************************************/
|
|
/* DefineAM: define specific AM block values. */
|
|
/***********************************************************************/
|
|
bool RESTDEF::DefineAM(PGLOBAL g, LPCSTR am, int poff)
|
|
{
|
|
char filename[_MAX_PATH + 1];
|
|
int rc = 0, n;
|
|
bool xt = trace(515);
|
|
LPCSTR ftype;
|
|
XGETREST grf = NULL;
|
|
bool curl = GetBoolCatInfo("Curl", false);
|
|
|
|
if (!curl && !(grf = GetRestFunction(g)))
|
|
curl = true;
|
|
|
|
ftype = GetStringCatInfo(g, "Type", "JSON");
|
|
|
|
if (xt)
|
|
htrc("ftype = %s am = %s\n", ftype, SVP(am));
|
|
|
|
n = (!stricmp(ftype, "JSON")) ? 1
|
|
#if defined(XML_SUPPORT)
|
|
: (!stricmp(ftype, "XML")) ? 2
|
|
#endif // XML_SUPPORT
|
|
: (!stricmp(ftype, "CSV")) ? 3 : 0;
|
|
|
|
if (n == 0) {
|
|
htrc("DefineAM: Unsupported REST table type %s\n", ftype);
|
|
snprintf(g->Message, sizeof(g->Message), "Unsupported REST table type %s", ftype);
|
|
return true;
|
|
} // endif n
|
|
|
|
Http = GetStringCatInfo(g, "Http", NULL);
|
|
Uri = GetStringCatInfo(g, "Uri", NULL);
|
|
Fn = GetStringCatInfo(g, "Filename", NULL);
|
|
|
|
// We used the file name relative to recorded datapath
|
|
PlugSetPath(filename, Fn, GetPath());
|
|
remove(filename);
|
|
|
|
// Retrieve the file from the web and copy it locally
|
|
if (curl) {
|
|
rc = Xcurl(g, Http, Uri, filename);
|
|
xtrc(515, "Return from Xcurl: rc=%d\n", rc);
|
|
} else {
|
|
rc = grf(g->Message, xt, Http, Uri, filename);
|
|
xtrc(515, "Return from restGetFile: rc=%d\n", rc);
|
|
} // endelse
|
|
|
|
if (rc) {
|
|
// strcpy(g->Message, "Cannot access to curl nor casablanca");
|
|
return true;
|
|
} else switch (n) {
|
|
case 1: Tdp = new (g) JSONDEF; break;
|
|
#if defined(XML_SUPPORT)
|
|
case 2: Tdp = new (g) XMLDEF; break;
|
|
#endif // XML_SUPPORT
|
|
case 3: Tdp = new (g) CSVDEF; break;
|
|
default: Tdp = NULL;
|
|
} // endswitch n
|
|
|
|
// Do make the table/view definition
|
|
if (Tdp && Tdp->Define(g, Cat, Name, Schema, "REST"))
|
|
Tdp = NULL; // Error occurred
|
|
|
|
if (xt)
|
|
htrc("Tdp defined\n", rc);
|
|
|
|
// Return true in case of error
|
|
return (Tdp == NULL);
|
|
} // end of DefineAM
|
|
|
|
/***********************************************************************/
|
|
/* GetTable: makes a new Table Description Block. */
|
|
/***********************************************************************/
|
|
PTDB RESTDEF::GetTable(PGLOBAL g, MODE m)
|
|
{
|
|
if (trace(515))
|
|
htrc("REST GetTable mode=%d\n", m);
|
|
|
|
if (m != MODE_READ && m != MODE_READX && m != MODE_ANY) {
|
|
strcpy(g->Message, "REST tables are currently read only");
|
|
return NULL;
|
|
} // endif m
|
|
|
|
return Tdp->GetTable(g, m); // Leave file type do the job
|
|
} // end of GetTable
|
|
|
|
/* ---------------------- End of Class RESTDEF ----------------------- */
|