summaryrefslogtreecommitdiffstats
path: root/storage/connect/tabrest.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'storage/connect/tabrest.cpp')
-rw-r--r--storage/connect/tabrest.cpp359
1 files changed, 359 insertions, 0 deletions
diff --git a/storage/connect/tabrest.cpp b/storage/connect/tabrest.cpp
new file mode 100644
index 00000000..c66d8d76
--- /dev/null
+++ b/storage/connect/tabrest.cpp
@@ -0,0 +1,359 @@
+/************** 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 <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, 0, 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 {
+ sprintf(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
+
+ pID = vfork();
+ 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();
+
+ sprintf(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();
+
+ sprintf(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();
+ sprintf(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();
+ sprintf(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);
+ sprintf(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
+ sprintf(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);
+ sprintf(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 occured
+
+ 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 ----------------------- */