mirror of
				https://github.com/MariaDB/server.git
				synced 2025-10-31 02:46:29 +01:00 
			
		
		
		
	 740d3e7a74
			
		
	
	
	740d3e7a74
	
	
	
		
			
			- Removed not used variable 'file' from MYSQL_BIN_LOG::open() - Assigned not initialized variable in connect/tabext.cpp
		
			
				
	
	
		
			813 lines
		
	
	
	
		
			22 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			813 lines
		
	
	
	
		
			22 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /************* Tabext C++ Functions Source Code File (.CPP) ************/
 | |
| /*  Name: TABEXT.CPP  Version 1.1                                      */
 | |
| /*                                                                     */
 | |
| /*  (C) Copyright to the author Olivier BERTRAND          2017 - 2019  */
 | |
| /*                                                                     */
 | |
| /*  This file contains the TBX, TDB and OPJOIN classes functions.      */
 | |
| /***********************************************************************/
 | |
| 
 | |
| /***********************************************************************/
 | |
| /*  Include relevant MariaDB header file.                              */
 | |
| /***********************************************************************/
 | |
| #define MYSQL_SERVER 1
 | |
| #include "my_global.h"
 | |
| #include "sql_class.h"
 | |
| #include "sql_servers.h"
 | |
| #include "sql_string.h"
 | |
| #if !defined(_WIN32)
 | |
| #include "osutil.h"
 | |
| #endif
 | |
| 
 | |
| /***********************************************************************/
 | |
| /*  Include required application header files                          */
 | |
| /*  global.h    is header containing all global Plug declarations.     */
 | |
| /*  plgdbsem.h  is header containing the DB applic. declarations.      */
 | |
| /*  xobject.h   is header containing XOBJECT derived classes declares. */
 | |
| /***********************************************************************/
 | |
| #include "global.h"
 | |
| #include "plgdbsem.h"
 | |
| #include "xtable.h"
 | |
| #include "tabext.h"
 | |
| #include "ha_connect.h"
 | |
| 
 | |
| /* -------------------------- Class CONDFIL -------------------------- */
 | |
| 
 | |
| /***********************************************************************/
 | |
| /*  CONDFIL Constructor.                                               */
 | |
| /***********************************************************************/
 | |
| CONDFIL::CONDFIL(uint idx, AMT type)
 | |
| {
 | |
| //Cond = cond; 
 | |
| 	Idx = idx; 
 | |
| 	Type = type;
 | |
| 	Op = OP_XX;
 | |
| 	Cmds = NULL;
 | |
| 	Alist = NULL;
 | |
| 	All = true;
 | |
| 	Bd = false;
 | |
| 	Hv = false;
 | |
| 	Body = NULL, 
 | |
| 	Having = NULL;
 | |
| }	// end of CONDFIL constructor
 | |
| 
 | |
| /***********************************************************************/
 | |
| /*  Make and allocate the alias list.                                  */
 | |
| /***********************************************************************/
 | |
| int CONDFIL::Init(PGLOBAL g, PHC hc)
 | |
| {
 | |
| 	PTOS  options = hc->GetTableOptionStruct();
 | |
| 	char *p, *cn, *cal, *alt = NULL;
 | |
| 	int   rc = RC_OK;
 | |
| 	bool  h;
 | |
| 
 | |
| 	if (options)
 | |
|     alt = (char*)GetListOption(g, "Alias", options->oplist, NULL);
 | |
| 
 | |
| 	while (alt) {
 | |
| 		if (!(p = strchr(alt, '='))) {
 | |
|       safe_strcpy(g->Message, sizeof(g->Message), "Invalid alias list");
 | |
| 			rc = RC_FX;
 | |
| 			break;
 | |
| 		}	// endif !p
 | |
| 
 | |
| 		cal = alt;				 // Alias
 | |
| 		*p++ = 0;
 | |
| 
 | |
| 		if ((h = *p == '*')) {
 | |
| 			rc = RC_INFO;
 | |
| 			p++;
 | |
| 		}	// endif h
 | |
| 
 | |
| 		cn = p;						// Remote column name
 | |
| 
 | |
| 		if ((alt = strchr(p, ';')))
 | |
| 			*alt++ = 0;
 | |
| 
 | |
| 		if (*cn == 0)
 | |
| 			cn = alt;
 | |
| 
 | |
| 		Alist = new(g) ALIAS(Alist, cn, cal, h);
 | |
| 	}	// endwhile alt
 | |
| 
 | |
| 	return rc;
 | |
| }	// end of Init
 | |
| 
 | |
| /***********************************************************************/
 | |
| /*  Make and allocate the alias list.                                  */
 | |
| /***********************************************************************/
 | |
| const char *CONDFIL::Chk(const char *fln, bool *h)
 | |
| {
 | |
| 	for (PAL pal = Alist; pal; pal = pal->Next)
 | |
| 		if (!stricmp(fln, pal->Alias)) {
 | |
| 			*h = pal->Having;
 | |
| 			return pal->Name;
 | |
| 		}	// endif fln
 | |
| 
 | |
| 	*h = false;
 | |
| 	return fln;
 | |
| }	// end of Chk
 | |
| 
 | |
| /* --------------------------- Class EXTDEF -------------------------- */
 | |
| 
 | |
| /***********************************************************************/
 | |
| /*  EXTDEF Constructor.                                                */
 | |
| /***********************************************************************/
 | |
| EXTDEF::EXTDEF(void)
 | |
| {
 | |
| 	Tabname = Tabschema = Username = Password = Tabcat = Tabtyp = NULL;
 | |
| 	Colpat = Srcdef = Qchar = Qrystr = Sep = Phpos = NULL;
 | |
| 	Options = Cto = Qto = Quoted = Maxerr = Maxres = Memory = 0;
 | |
| 	Scrollable = Xsrc = false;
 | |
| } // end of EXTDEF constructor
 | |
| 
 | |
| /***********************************************************************/
 | |
| /*  DefineAM: define specific AM block values from XDB file.           */
 | |
| /***********************************************************************/
 | |
| bool EXTDEF::DefineAM(PGLOBAL g, LPCSTR am, int poff)
 | |
| {
 | |
| 	if (g->Createas) {
 | |
| 		safe_strcpy(g->Message, sizeof(g->Message),
 | |
| 			"Multiple-table UPDATE/DELETE commands are not supported");
 | |
| 		return true;
 | |
| 	}	// endif multi
 | |
| 
 | |
| 	Desc = NULL;
 | |
| 	Tabname = GetStringCatInfo(g, "Name",
 | |
| 		(Catfunc & (FNC_TABLE | FNC_COL)) ? NULL : Name);
 | |
| 	Tabname = GetStringCatInfo(g, "Tabname", Tabname);
 | |
| 	Tabschema = GetStringCatInfo(g, "Dbname", NULL);
 | |
| 	Tabschema = GetStringCatInfo(g, "Schema", Tabschema);
 | |
| 	Tabcat = GetStringCatInfo(g, "Qualifier", NULL);
 | |
| 	Tabcat = GetStringCatInfo(g, "Catalog", Tabcat);
 | |
| 	Username = GetStringCatInfo(g, "User", NULL);
 | |
| 	Password = GetStringCatInfo(g, "Password", NULL);
 | |
| 
 | |
| 	// Memory was Boolean, it is now integer
 | |
| 	if (!(Memory = GetIntCatInfo("Memory", 0)))
 | |
| 		Memory = GetBoolCatInfo("Memory", false) ? 1 : 0;
 | |
| 
 | |
| 	if ((Srcdef = GetStringCatInfo(g, "Srcdef", NULL))) {
 | |
| 		Read_Only = true;
 | |
| 		if (Memory == 2) Memory = 1;
 | |
| 	}	// endif Srcdef
 | |
| 
 | |
| 	Qrystr = GetStringCatInfo(g, "Query_String", "?");
 | |
| 	Sep = GetStringCatInfo(g, "Separator", NULL);
 | |
| //Alias = GetStringCatInfo(g, "Alias", NULL);
 | |
| 	Phpos = GetStringCatInfo(g, "Phpos", NULL);
 | |
| 	Xsrc = GetBoolCatInfo("Execsrc", FALSE);
 | |
| 	Maxerr = GetIntCatInfo("Maxerr", 0);
 | |
| 	Maxres = GetIntCatInfo("Maxres", 0);
 | |
| 	Quoted = GetIntCatInfo("Quoted", 0);
 | |
| 	Qchar = GetStringCatInfo(g,"Qchar", NULL);
 | |
|   if (Qchar && !Quoted)
 | |
|     Quoted = 1;
 | |
| 	Options = 0;
 | |
| 	Cto = 0;
 | |
| 	Qto = 0;
 | |
| 
 | |
| 	if ((Scrollable = GetBoolCatInfo("Scrollable", false)) && !Elemt)
 | |
| 		Elemt = 1;     // Cannot merge SQLFetch and SQLExtendedFetch
 | |
| 
 | |
| 	if (Catfunc == FNC_COL)
 | |
| 		Colpat = GetStringCatInfo(g, "Colpat", NULL);
 | |
| 
 | |
| 	if (Catfunc == FNC_TABLE)
 | |
| 		Tabtyp = GetStringCatInfo(g, "Tabtype", NULL);
 | |
| 
 | |
| 	Pseudo = 2;    // FILID is Ok but not ROWID
 | |
| 	return false;
 | |
| } // end of DefineAM
 | |
| 
 | |
| /* ---------------------------TDBEXT class --------------------------- */
 | |
| 
 | |
| /***********************************************************************/
 | |
| /*  Implementation of the TDBEXT class.                                */
 | |
| /***********************************************************************/
 | |
| TDBEXT::TDBEXT(EXTDEF *tdp) : TDB(tdp)
 | |
| {
 | |
| 	Qrp = NULL;
 | |
| 
 | |
| 	if (tdp) {
 | |
| 		TableName = tdp->Tabname;
 | |
| 		Schema = tdp->Tabschema;
 | |
| 		User = tdp->Username;
 | |
| 		Pwd = tdp->Password;
 | |
| 		Catalog = tdp->Tabcat;
 | |
| 		Srcdef = tdp->Srcdef;
 | |
| 		Qrystr = tdp->Qrystr;
 | |
| 		Sep = tdp->GetSep();
 | |
| 		Options = tdp->Options;
 | |
| 		Cto = tdp->Cto;
 | |
| 		Qto = tdp->Qto;
 | |
| 		Quoted = MY_MAX(0, tdp->GetQuoted());
 | |
| 		Quote = tdp->GetQchar();
 | |
| 		Rows = tdp->GetElemt();
 | |
| 		Memory = tdp->Memory;
 | |
| 		Scrollable = tdp->Scrollable;
 | |
| 	} else {
 | |
| 		TableName = NULL;
 | |
| 		Schema = NULL;
 | |
| 		User = NULL;
 | |
| 		Pwd = NULL;
 | |
| 		Catalog = NULL;
 | |
| 		Srcdef = NULL;
 | |
| 		Qrystr = NULL;
 | |
| 		Sep = 0;
 | |
| 		Options = 0;
 | |
| 		Cto = 0;
 | |
| 		Qto = 0;
 | |
| 		Quoted = 0;
 | |
| 		Quote = NULL;
 | |
| 		Rows = 0;
 | |
| 		Memory = 0;
 | |
| 		Scrollable = false;
 | |
| 	} // endif tdp
 | |
| 
 | |
| 	Query = NULL;
 | |
| 	Count = NULL;
 | |
| 	//Where = NULL;
 | |
| 	MulConn = NULL;
 | |
| 	DBQ = NULL;
 | |
| 	Qrp = NULL;
 | |
| 	Fpos = 0;
 | |
| 	Curpos = 0;
 | |
| 	AftRows = 0;
 | |
| 	CurNum = 0;
 | |
| 	Rbuf = 0;
 | |
| 	BufSize = 0;
 | |
| 	Nparm = 0;
 | |
| 	Ncol = 0;
 | |
| 	Placed = false;
 | |
| } // end of TDBEXT constructor
 | |
| 
 | |
| TDBEXT::TDBEXT(PTDBEXT tdbp) : TDB(tdbp)
 | |
| {
 | |
| 	Qrp = tdbp->Qrp;
 | |
| 	TableName = tdbp->TableName;
 | |
| 	Schema = tdbp->Schema;
 | |
| 	User = tdbp->User;
 | |
| 	Pwd = tdbp->Pwd;
 | |
| 	Catalog = tdbp->Catalog;
 | |
| 	Srcdef = tdbp->Srcdef;
 | |
| 	Qrystr = tdbp->Qrystr;
 | |
| 	Sep = tdbp->Sep;
 | |
| 	Options = tdbp->Options;
 | |
| 	Cto = tdbp->Cto;
 | |
| 	Qto = tdbp->Qto;
 | |
| 	Quoted = tdbp->Quoted;
 | |
| 	Quote = tdbp->Quote;
 | |
| 	Rows = tdbp->Rows;
 | |
| 	Memory = tdbp->Memory;
 | |
| 	Scrollable = tdbp->Scrollable;
 | |
| 	Quote = tdbp->Quote;
 | |
| 	Query = tdbp->Query;
 | |
| 	Count = tdbp->Count;
 | |
| 	//Where = tdbp->Where;
 | |
| 	MulConn = tdbp->MulConn;
 | |
| 	DBQ = tdbp->DBQ;
 | |
| 	Fpos = 0;
 | |
| 	Curpos = 0;
 | |
| 	AftRows = 0;
 | |
| 	CurNum = 0;
 | |
| 	Rbuf = 0;
 | |
| 	BufSize = tdbp->BufSize;
 | |
| 	Nparm = tdbp->Nparm;
 | |
| 	Ncol = tdbp->Ncol;
 | |
| 	Placed = false;
 | |
| } // end of TDBEXT copy constructor
 | |
| 
 | |
| /******************************************************************/
 | |
| /*  Convert an UTF-8 string to latin characters.                  */
 | |
| /******************************************************************/
 | |
| int TDBEXT::Decode(PCSZ txt, char *buf, size_t n)
 | |
| {
 | |
| 	uint   dummy_errors;
 | |
| 	uint32 len = copy_and_convert(buf, n, &my_charset_latin1,
 | |
| 		txt, strlen(txt),
 | |
| 		&my_charset_utf8mb3_general_ci,
 | |
| 		&dummy_errors);
 | |
| 	buf[len] = '\0';
 | |
| 	return 0;
 | |
| } // end of Decode
 | |
| 
 | |
| /*
 | |
|   Count number of %s placeholders in string.
 | |
|   Returns -1 if other sprintf placeholders are found, .g %d
 | |
| */
 | |
| static int count_placeholders(const char *fmt)
 | |
| {
 | |
|   int cnt= 0;
 | |
|   for (const char *p=fmt; *p; p++)
 | |
|   {
 | |
|     if (*p == '%')
 | |
|     {
 | |
|       switch (p[1])
 | |
|       {
 | |
|       case 's':
 | |
|         /* %s found */
 | |
|         cnt++;
 | |
|         p++;
 | |
|         break;
 | |
|       case '%':
 | |
|         /* masking char for % found */
 | |
|         p++;
 | |
|         break;
 | |
|       default:
 | |
|         /* some other placeholder found */
 | |
|         return -1;
 | |
|       }
 | |
|     }
 | |
|   }
 | |
|   return cnt;
 | |
| }
 | |
| 
 | |
| /***********************************************************************/
 | |
| /*  MakeSrcdef: make the SQL statement from SRDEF option.              */
 | |
| /***********************************************************************/
 | |
| bool TDBEXT::MakeSrcdef(PGLOBAL g)
 | |
| {
 | |
| 	char *catp = strstr(Srcdef, "%s");
 | |
| 
 | |
| 	if (catp) {
 | |
| 		char *fil1 = 0, *fil2= 0;
 | |
| 		PCSZ  ph = ((EXTDEF*)To_Def)->Phpos;
 | |
| 
 | |
| 		if (!ph)
 | |
| 			ph = (strstr(catp + 2, "%s")) ? "WH" : "W";
 | |
| 
 | |
| 		if (stricmp(ph, "H")) {
 | |
| 			fil1 = (To_CondFil && *To_CondFil->Body)
 | |
| 				? To_CondFil->Body : PlugDup(g, "1=1");
 | |
| 		} // endif ph
 | |
| 
 | |
| 		if (stricmp(ph, "W")) {
 | |
| 			fil2 = (To_CondFil && To_CondFil->Having && *To_CondFil->Having)
 | |
| 				? To_CondFil->Having : PlugDup(g, "1=1");
 | |
| 		} // endif ph
 | |
| 
 | |
| 		int n_placeholders = count_placeholders(Srcdef);
 | |
| 		if (n_placeholders < 0)
 | |
| 		{
 | |
| 			safe_strcpy(g->Message, sizeof(g->Message), "MakeSQL: Wrong place holders specification");
 | |
| 			return true;
 | |
| 		}
 | |
| 
 | |
| 		if (!stricmp(ph, "W") && n_placeholders <= 1) {
 | |
| 			Query = new(g)STRING(g, strlen(Srcdef) + strlen(fil1));
 | |
| 			Query->SetLength(sprintf(Query->GetStr(), Srcdef, fil1));
 | |
| 		}
 | |
| 		else if (!stricmp(ph, "WH") && n_placeholders <= 2)
 | |
| 		{
 | |
| 			Query = new(g)STRING(g, strlen(Srcdef) + strlen(fil1) + strlen(fil2));
 | |
| 			Query->SetLength(sprintf(Query->GetStr(), Srcdef, fil1, fil2));
 | |
| 		}
 | |
| 		else if (!stricmp(ph, "H") && n_placeholders <= 1)
 | |
| 		{
 | |
| 			Query = new(g)STRING(g, strlen(Srcdef) + strlen(fil2));
 | |
| 			Query->SetLength(sprintf(Query->GetStr(), Srcdef, fil2));
 | |
| 		}
 | |
| 		else if (!stricmp(ph, "HW") && n_placeholders <= 2)
 | |
| 		{
 | |
| 			Query = new(g)STRING(g, strlen(Srcdef) + strlen(fil1) + strlen(fil2));
 | |
| 			Query->SetLength(sprintf(Query->GetStr(), Srcdef, fil2, fil1));
 | |
| 		} else {
 | |
| 			safe_strcpy(g->Message, sizeof(g->Message), "MakeSQL: Wrong place holders specification");
 | |
| 			return true;
 | |
| 		} // endif's ph
 | |
| 
 | |
| 	} else
 | |
| 		Query = new(g)STRING(g, 0, Srcdef);
 | |
| 
 | |
| 	return false;
 | |
| } // end of MakeSrcdef
 | |
| 
 | |
| 	/***********************************************************************/
 | |
| 	/*  MakeSQL: make the SQL statement use with remote connection.        */
 | |
| 	/*  TODO: when implementing remote filtering, column only used in      */
 | |
| 	/*  local filter should be removed from column list.                   */
 | |
| 	/***********************************************************************/
 | |
| bool TDBEXT::MakeSQL(PGLOBAL g, bool cnt)
 | |
| {
 | |
| 	PCSZ   schmp = NULL;
 | |
| 	char  *catp = NULL, buf[NAM_LEN * 3];
 | |
| 	int    len;
 | |
| 	bool   first = true;
 | |
| 	PCOL   colp;
 | |
| 	char *res= NULL, *my_schema_table= NULL;
 | |
| 	size_t my_len= 0;
 | |
| 
 | |
| 	if (Srcdef)
 | |
| 		return MakeSrcdef(g);
 | |
| 
 | |
| 	// Allocate the string used to contain the Query
 | |
| 	Query = new(g)STRING(g, 1023, "SELECT ");
 | |
| 
 | |
| 	if (!cnt) {
 | |
| 		if (Columns) {
 | |
| 			// Normal SQL statement to retrieve results
 | |
| 			for (colp = Columns; colp; colp = colp->GetNext())
 | |
| 				if (!colp->IsSpecial()) {
 | |
| 					if (!first)
 | |
| 						Query->Append(", ");
 | |
| 					else
 | |
| 						first = false;
 | |
| 
 | |
| 					// Column name can be encoded in UTF-8
 | |
| 					Decode(colp->GetName(), buf, sizeof(buf));
 | |
| 
 | |
| 					if (Quote) {
 | |
| 						// Put column name between identifier quotes in case in contains blanks
 | |
| 						Query->Append(Quote);
 | |
| 						Query->Append(buf);
 | |
| 						Query->Append(Quote);
 | |
| 					} else
 | |
| 						Query->Append(buf);
 | |
| 
 | |
| 					((PEXTCOL)colp)->SetRank(++Ncol);
 | |
| 				} // endif colp
 | |
| 
 | |
| 		} else
 | |
| 			// !Columns can occur for queries such that sql count(*) from...
 | |
| 			// for which we will count the rows from sql * from...
 | |
| 			Query->Append('*');
 | |
| 
 | |
| 	} else
 | |
| 		// SQL statement used to retrieve the size of the result
 | |
| 		Query->Append("count(*)");
 | |
| 
 | |
| 	Query->Append(" FROM ");
 | |
| 
 | |
| 	if (Catalog && *Catalog)
 | |
| 		catp = Catalog;
 | |
| 
 | |
| 	//if (tablep->GetSchema())
 | |
| 	//	schmp = (char*)tablep->GetSchema();
 | |
| 	//else 
 | |
| 	if (Schema && *Schema)
 | |
| 		schmp = Schema;
 | |
| 
 | |
| 	if (catp) {
 | |
| 		Query->Append(catp);
 | |
| 
 | |
| 		if (schmp) {
 | |
| 			Query->Append('.');
 | |
| 			Query->Append(schmp);
 | |
| 		} // endif schmp
 | |
| 
 | |
| 		Query->Append('.');
 | |
| 	} else if (schmp) {
 | |
| 		Query->Append(schmp);
 | |
| 		Query->Append('.');
 | |
| 	} // endif schmp
 | |
| 
 | |
| 	// Table name can be encoded in UTF-8
 | |
| 	Decode(TableName, buf, sizeof(buf));
 | |
| 
 | |
| 	if (Quote) {
 | |
| 		// Tabname can have both database and table identifiers, we need to parse
 | |
| 		if ((res= strstr(buf, ".")))
 | |
| 		{
 | |
| 			// Parse schema
 | |
| 			my_len= res - buf + 1;
 | |
| 			my_schema_table= (char *) malloc(my_len);
 | |
| 			memcpy(my_schema_table, buf, my_len - 1);
 | |
| 			my_schema_table[my_len - 1] = 0;
 | |
| 			Query->Append(Quote);
 | |
| 			Query->Append(my_schema_table);
 | |
| 			Query->Append(Quote);
 | |
| 			free(my_schema_table);
 | |
| 			Query->Append(".");
 | |
| 			// Parse table
 | |
| 			my_len= strlen(buf) - my_len + 1;
 | |
| 			my_schema_table= (char *) malloc(my_len + 1);
 | |
| 			memcpy(my_schema_table, ++res, my_len);
 | |
| 			my_schema_table[my_len] = 0;
 | |
| 			Query->Append(Quote);
 | |
| 			Query->Append(my_schema_table);
 | |
| 			Query->Append(Quote);
 | |
| 			free(my_schema_table);
 | |
| 		}
 | |
| 		else
 | |
| 		{
 | |
| 			// Put table name between identifier quotes in case in contains blanks
 | |
| 			Query->Append(Quote);
 | |
| 			Query->Append(buf);
 | |
| 			Query->Append(Quote);
 | |
| 		}
 | |
| 
 | |
| 	} else
 | |
| 		Query->Append(buf);
 | |
| 
 | |
| 	len = Query->GetLength();
 | |
| 
 | |
| 	if (To_CondFil) {
 | |
| 		if (Mode == MODE_READ) {
 | |
| 			Query->Append(" WHERE ");
 | |
| 			Query->Append(To_CondFil->Body);
 | |
| 			len = Query->GetLength() + 1;
 | |
| 		} else
 | |
| 			len += (strlen(To_CondFil->Body) + 256);
 | |
| 
 | |
| 	} else
 | |
| 		len += ((Mode == MODE_READX) ? 256 : 1);
 | |
| 
 | |
| 	if (Query->IsTruncated()) {
 | |
| 		safe_strcpy(g->Message, sizeof(g->Message), "MakeSQL: Out of memory");
 | |
| 		return true;
 | |
| 	} else
 | |
| 		Query->Resize(len);
 | |
| 
 | |
| 	if (trace(33))
 | |
| 		htrc("Query=%s\n", Query->GetStr());
 | |
| 
 | |
| 	return false;
 | |
| } // end of MakeSQL
 | |
| 
 | |
| /***********************************************************************/
 | |
| /*  Remove the NAME_CONST functions that are added by procedures.      */
 | |
| /***********************************************************************/
 | |
| void TDBEXT::RemoveConst(PGLOBAL g, char *stmt)
 | |
| {
 | |
| 	char *p, *p2;
 | |
| 	char  val[1025], nval[1025];
 | |
| 	int   n, nc;
 | |
| 
 | |
| 	while ((p = strstr(stmt, "NAME_CONST")))
 | |
|         {
 | |
| 		if ((n = sscanf(p, "%*[^,],%1024[^)])%n", val, &nc))) {
 | |
| 			if (trace(33))
 | |
| 				htrc("p=%s\nn=%d val=%s nc=%d\n", p, n, val, nc);
 | |
| 
 | |
| 			*p = 0;
 | |
| 
 | |
| 			if ((p2 = strstr(val, "'"))) {
 | |
| 				if ((n = sscanf(p2, "%*['\\]%1024[^'\\]", nval))) {
 | |
| 					if (trace(33))
 | |
| 						htrc("p2=%s\nn=%d nval=%s\n", p2, n, nval);
 | |
| 
 | |
| 					strcat(strcat(strcat(strcat(stmt, "'"), nval), "'"), p + nc);
 | |
| 				} else
 | |
| 					break;
 | |
| 
 | |
| 			} else
 | |
| 				strcat(strcat(strcat(strcat(stmt, "("), val), ")"), p + nc);
 | |
| 
 | |
| 			if (trace(33))
 | |
| 				htrc("stmt=%s\n", stmt);
 | |
| 
 | |
| 		} else
 | |
| 			break;
 | |
|         }
 | |
| 	return;
 | |
| } // end of RemoveConst
 | |
| 
 | |
| /***********************************************************************/
 | |
| /*  MakeCommand: make the Update or Delete statement to send to the    */
 | |
| /*  MySQL server. Limited to remote values and filtering.              */
 | |
| /***********************************************************************/
 | |
| bool TDBEXT::MakeCommand(PGLOBAL g)
 | |
| {
 | |
| 	PCSZ  schmp = NULL;
 | |
| 	char *p, *stmt, name[132], *body = NULL;
 | |
| 	char *qrystr = (char*)PlugSubAlloc(g, NULL, strlen(Qrystr) + 1);
 | |
| 	bool  qtd = Quoted > 0;
 | |
| 	char  q = qtd ? *Quote : ' ';
 | |
| 	int   i = 0, k = 0;
 | |
| 	size_t stmt_sz = 0;
 | |
| 
 | |
| 	// Make a lower case copy of the originale query and change
 | |
| 	// back ticks to the data source identifier quoting character
 | |
| 	do {
 | |
| 		qrystr[i] = (Qrystr[i] == '`') ? q : tolower(Qrystr[i]);
 | |
| 	} while (Qrystr[i++]);
 | |
| 
 | |
| 	if (To_CondFil && (p = strstr(qrystr, " where "))) {
 | |
| 		p[7] = 0;           // Remove where clause
 | |
| 		Qrystr[(p - qrystr) + 7] = 0;
 | |
| 		body = To_CondFil->Body;
 | |
| 		stmt_sz = strlen(qrystr) + strlen(body) + 64;
 | |
| 	} else
 | |
| 		stmt_sz = strlen(Qrystr) + 64;
 | |
| 	stmt = (char*)PlugSubAlloc(g, NULL, stmt_sz);
 | |
| 
 | |
| 	// Check whether the table name is equal to a keyword
 | |
| 	// If so, it must be quoted in the original query
 | |
| 	snprintf(name, sizeof(name), " %s ", Name);
 | |
| 	strlwr(name);
 | |
| 
 | |
| 	if (strstr(" update delete low_priority ignore quick from ", name)) {
 | |
| 		if (Quote) {
 | |
| 			snprintf(name, sizeof(name), "%s%s%s", Quote, Name, Quote);
 | |
| 			strlwr(name);
 | |
| 			k += 2;
 | |
| 		} else {
 | |
| 			safe_strcpy(g->Message, sizeof(g->Message), "Quoted must be specified");
 | |
| 			return true;
 | |
| 		}	// endif Quote
 | |
| 
 | |
| 	} else {
 | |
| 		safe_strcpy(name, sizeof(name), Name);     // Not a keyword
 | |
| 		strlwr(name);
 | |
| 	}
 | |
| 
 | |
| 	if ((p = strstr(qrystr, name))) {
 | |
| 		for (i = 0; i < p - qrystr; i++)
 | |
| 			stmt[i] = (Qrystr[i] == '`') ? q : Qrystr[i];
 | |
| 
 | |
| 		stmt[i] = 0;
 | |
| 
 | |
| 		k += i + (int)strlen(Name);
 | |
| 
 | |
| 		if (Schema && *Schema)
 | |
| 			schmp = Schema;
 | |
| 
 | |
| 		if (qtd && *(p - 1) == ' ') {
 | |
| 			if (schmp) {
 | |
| 				safe_strcat(stmt, stmt_sz, schmp);
 | |
| 				safe_strcat(stmt, stmt_sz, ".");
 | |
| 			}
 | |
| 
 | |
| 			safe_strcat(stmt, stmt_sz, Quote);
 | |
| 			safe_strcat(stmt, stmt_sz, TableName);
 | |
| 			safe_strcat(stmt, stmt_sz, Quote);
 | |
| 		} else {
 | |
| 			if (schmp) {
 | |
| 				if (qtd && *(p - 1) != ' ') {
 | |
| 					stmt[i - 1] = 0;
 | |
| 					safe_strcat(stmt, stmt_sz, schmp);
 | |
| 					safe_strcat(stmt, stmt_sz, ".");
 | |
| 					safe_strcat(stmt, stmt_sz, Quote);
 | |
| 				} else {
 | |
| 					safe_strcat(stmt, stmt_sz, schmp);
 | |
| 					safe_strcat(stmt, stmt_sz, ".");
 | |
| 				}
 | |
| 
 | |
| 			}	// endif schmp
 | |
| 
 | |
| 			safe_strcat(stmt, stmt_sz, TableName);
 | |
| 		} // endif's
 | |
| 
 | |
| 		i = (int)strlen(stmt);
 | |
| 
 | |
| 		do {
 | |
| 			stmt[i++] = (Qrystr[k] == '`') ? q : Qrystr[k];
 | |
| 		} while (Qrystr[k++]);
 | |
| 
 | |
| 		RemoveConst(g, stmt);
 | |
| 
 | |
| 		if (body)
 | |
| 			safe_strcat(stmt, stmt_sz, body);
 | |
| 
 | |
| 	} else {
 | |
| 		snprintf(g->Message, sizeof(g->Message), "Cannot use this %s command",
 | |
| 			(Mode == MODE_UPDATE) ? "UPDATE" : "DELETE");
 | |
| 		return true;
 | |
| 	} // endif p
 | |
| 
 | |
| 	if (trace(33))
 | |
| 		htrc("Command=%s\n", stmt);
 | |
| 
 | |
| 	Query = new(g)STRING(g, 0, stmt);
 | |
| 	return (!Query->GetSize());
 | |
| } // end of MakeCommand
 | |
| 
 | |
| /***********************************************************************/
 | |
| /*  GetRecpos: return the position of last read record.                */
 | |
| /***********************************************************************/
 | |
| int TDBEXT::GetRecpos(void)
 | |
| {
 | |
| 	return Fpos;
 | |
| } // end of GetRecpos
 | |
| 
 | |
| /***********************************************************************/
 | |
| /*  ODBC GetMaxSize: returns table size estimate in number of lines.   */
 | |
| /***********************************************************************/
 | |
| int TDBEXT::GetMaxSize(PGLOBAL g)
 | |
| {
 | |
| 	if (MaxSize < 0) {
 | |
| 		if (Mode == MODE_DELETE)
 | |
| 			// Return 0 in mode DELETE in case of delete all.
 | |
| 			MaxSize = 0;
 | |
| 		else if (!Cardinality(NULL))
 | |
| 			MaxSize = 10;   // To make MySQL happy
 | |
| 		else if ((MaxSize = Cardinality(g)) < 0)
 | |
| 			MaxSize = 12;   // So we can see an error occurred
 | |
| 
 | |
| 	} // endif MaxSize
 | |
| 
 | |
| 	return MaxSize;
 | |
| } // end of GetMaxSize
 | |
| 
 | |
| /***********************************************************************/
 | |
| /*  Return max size value.                                             */
 | |
| /***********************************************************************/
 | |
| int TDBEXT::GetProgMax(PGLOBAL g)
 | |
| {
 | |
| 	return GetMaxSize(g);
 | |
| } // end of GetProgMax
 | |
| 
 | |
| /* ---------------------------EXTCOL class --------------------------- */
 | |
| 
 | |
| /***********************************************************************/
 | |
| /*  EXTCOL public constructor.                                         */
 | |
| /***********************************************************************/
 | |
| EXTCOL::EXTCOL(PCOLDEF cdp, PTDB tdbp, PCOL cprec, int i, PCSZ am)
 | |
| 	: COLBLK(cdp, tdbp, i)
 | |
| {
 | |
| 	if (cprec) {
 | |
| 		Next = cprec->GetNext();
 | |
| 		cprec->SetNext(this);
 | |
| 	} else {
 | |
| 		Next = tdbp->GetColumns();
 | |
| 		tdbp->SetColumns(this);
 | |
| 	} // endif cprec
 | |
| 
 | |
| 	if (trace(1))
 | |
| 		htrc(" making new %sCOL C%d %s at %p\n", am, Index, Name, this);
 | |
| 
 | |
| 	// Set additional remote access method information for column.
 | |
| 	Crp = NULL;
 | |
| 	Long = Precision;
 | |
| 	To_Val = NULL;
 | |
| 	Bufp = NULL;
 | |
| 	Blkp = NULL;
 | |
| 	Rank = 0;           // Not known yet
 | |
| } // end of JDBCCOL constructor
 | |
| 
 | |
| /***********************************************************************/
 | |
| /*  EXTCOL private constructor.                                        */
 | |
| /***********************************************************************/
 | |
| EXTCOL::EXTCOL(void) : COLBLK()
 | |
| {
 | |
| 	Crp = NULL;
 | |
| 	Buf_Type = TYPE_INT;     // This is a count(*) column
 | |
| 
 | |
| 	// Set additional Dos access method information for column.
 | |
| 	Long = sizeof(int);
 | |
| 	To_Val = NULL;
 | |
| 	Bufp = NULL;
 | |
| 	Blkp = NULL;
 | |
| 	Rank = 1;
 | |
| } // end of EXTCOL constructor
 | |
| 
 | |
| /***********************************************************************/
 | |
| /*  EXTCOL constructor used for copying columns.                       */
 | |
| /*  tdbp is the pointer to the new table descriptor.                   */
 | |
| /***********************************************************************/
 | |
| EXTCOL::EXTCOL(PEXTCOL col1, PTDB tdbp) : COLBLK(col1, tdbp)
 | |
| {
 | |
| 	Crp = col1->Crp;
 | |
| 	Long = col1->Long;
 | |
| 	To_Val = col1->To_Val;
 | |
| 	Bufp = col1->Bufp;
 | |
| 	Blkp = col1->Blkp;
 | |
| 	Rank = col1->Rank;
 | |
| } // end of JDBCCOL copy constructor
 | |
| 
 | |
| /***********************************************************************/
 | |
| /*  SetBuffer: prepare a column block for write operation.             */
 | |
| /***********************************************************************/
 | |
| bool EXTCOL::SetBuffer(PGLOBAL g, PVAL value, bool ok, bool check)
 | |
| {
 | |
| 	if (!(To_Val = value)) {
 | |
| 		snprintf(g->Message, sizeof(g->Message), MSG(VALUE_ERROR), Name);
 | |
| 		return true;
 | |
| 	} else if (Buf_Type == value->GetType()) {
 | |
| 		// Values are of the (good) column type
 | |
| 		if (Buf_Type == TYPE_DATE) {
 | |
| 			// If any of the date values is formatted
 | |
| 			// output format must be set for the receiving table
 | |
| 			if (GetDomain() || ((DTVAL *)value)->IsFormatted())
 | |
| 				goto newval;          // This will make a new value;
 | |
| 
 | |
| 		} else if (Buf_Type == TYPE_DOUBLE)
 | |
| 			// Float values must be written with the correct (column) precision
 | |
| 			// Note: maybe this should be forced by ShowValue instead of this ?
 | |
| 			value->SetPrec(GetScale());
 | |
| 
 | |
| 		Value = value;            // Directly access the external value
 | |
| 	} else {
 | |
| 		// Values are not of the (good) column type
 | |
| 		if (check) {
 | |
| 			snprintf(g->Message, sizeof(g->Message), MSG(TYPE_VALUE_ERR), Name,
 | |
| 				GetTypeName(Buf_Type), GetTypeName(value->GetType()));
 | |
| 			return true;
 | |
| 		} // endif check
 | |
| 
 | |
| 	newval:
 | |
| 		if (InitValue(g))         // Allocate the matching value block
 | |
| 			return true;
 | |
| 
 | |
| 	} // endif's Value, Buf_Type
 | |
| 
 | |
| 	// Because Colblk's have been made from a copy of the original TDB in
 | |
| 	// case of Update, we must reset them to point to the original one.
 | |
| 	if (To_Tdb->GetOrig())
 | |
| 		To_Tdb = (PTDB)To_Tdb->GetOrig();
 | |
| 
 | |
| 	// Set the Column
 | |
| 	Status = (ok) ? BUF_EMPTY : BUF_NO;
 | |
| 	return false;
 | |
| } // end of SetBuffer
 | |
| 
 |