mirror of
				https://github.com/MariaDB/server.git
				synced 2025-10-31 19:06:14 +01:00 
			
		
		
		
	 50c8ef01fc
			
		
	
	
	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 ----------------------- */
 |