2013-02-07 10:34:27 +01:00
|
|
|
/************* TabMySQL C++ Program Source Code File (.CPP) *************/
|
|
|
|
/* PROGRAM NAME: TABMYSQL */
|
|
|
|
/* ------------- */
|
2013-02-22 17:26:08 +01:00
|
|
|
/* Version 1.7 */
|
2013-02-07 10:34:27 +01:00
|
|
|
/* */
|
|
|
|
/* AUTHOR: */
|
|
|
|
/* ------- */
|
|
|
|
/* Olivier BERTRAND 2007-2013 */
|
|
|
|
/* */
|
|
|
|
/* WHAT THIS PROGRAM DOES: */
|
|
|
|
/* ----------------------- */
|
|
|
|
/* Implements a table type that are MySQL tables. */
|
|
|
|
/* It can optionally use the embedded MySQL library. */
|
|
|
|
/* */
|
|
|
|
/* WHAT YOU NEED TO COMPILE THIS PROGRAM: */
|
|
|
|
/* -------------------------------------- */
|
|
|
|
/* */
|
|
|
|
/* REQUIRED FILES: */
|
|
|
|
/* --------------- */
|
|
|
|
/* TABMYSQL.CPP - Source code */
|
|
|
|
/* PLGDBSEM.H - DB application declaration file */
|
|
|
|
/* TABMYSQL.H - TABODBC classes declaration file */
|
|
|
|
/* GLOBAL.H - Global declaration file */
|
|
|
|
/* */
|
|
|
|
/* REQUIRED LIBRARIES: */
|
|
|
|
/* ------------------- */
|
|
|
|
/* Large model C library */
|
|
|
|
/* */
|
|
|
|
/* REQUIRED PROGRAMS: */
|
|
|
|
/* ------------------ */
|
|
|
|
/* IBM, Borland, GNU or Microsoft C++ Compiler and Linker */
|
|
|
|
/* */
|
|
|
|
/************************************************************************/
|
2013-06-16 19:07:27 +02:00
|
|
|
#define MYSQL_SERVER 1
|
2013-02-07 10:34:27 +01:00
|
|
|
#include "my_global.h"
|
2013-06-16 19:07:27 +02:00
|
|
|
#include "sql_class.h"
|
|
|
|
#include "sql_servers.h"
|
2013-02-07 10:34:27 +01:00
|
|
|
#if defined(WIN32)
|
|
|
|
//#include <windows.h>
|
|
|
|
#else // !WIN32
|
|
|
|
//#include <fnmatch.h>
|
|
|
|
//#include <errno.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include "osutil.h"
|
|
|
|
//#include <io.h>
|
|
|
|
//#include <fcntl.h>
|
|
|
|
#endif // !WIN32
|
|
|
|
|
|
|
|
/***********************************************************************/
|
|
|
|
/* Include application header files: */
|
|
|
|
/***********************************************************************/
|
|
|
|
#include "global.h"
|
|
|
|
#include "plgdbsem.h"
|
|
|
|
#include "xtable.h"
|
|
|
|
#include "tabcol.h"
|
|
|
|
#include "colblk.h"
|
2013-02-12 12:34:14 +01:00
|
|
|
#include "mycat.h"
|
2013-02-07 10:34:27 +01:00
|
|
|
#include "reldef.h"
|
|
|
|
#include "tabmysql.h"
|
|
|
|
#include "valblk.h"
|
2013-05-13 14:48:03 +02:00
|
|
|
#include "tabutil.h"
|
2014-04-19 17:02:53 +02:00
|
|
|
#include "ha_connect.h"
|
2013-02-07 10:34:27 +01:00
|
|
|
|
|
|
|
#if defined(_CONSOLE)
|
|
|
|
void PrintResult(PGLOBAL, PSEM, PQRYRES);
|
|
|
|
#endif // _CONSOLE
|
|
|
|
|
|
|
|
extern "C" int trace;
|
|
|
|
|
|
|
|
/* -------------- Implementation of the MYSQLDEF class --------------- */
|
|
|
|
|
|
|
|
/***********************************************************************/
|
|
|
|
/* Constructor. */
|
|
|
|
/***********************************************************************/
|
|
|
|
MYSQLDEF::MYSQLDEF(void)
|
|
|
|
{
|
|
|
|
Pseudo = 2; // SERVID is Ok but not ROWID
|
|
|
|
Hostname = NULL;
|
|
|
|
Database = NULL;
|
|
|
|
Tabname = NULL;
|
2013-05-19 19:25:06 +02:00
|
|
|
Srcdef = NULL;
|
2013-02-07 10:34:27 +01:00
|
|
|
Username = NULL;
|
|
|
|
Password = NULL;
|
|
|
|
Portnumber = 0;
|
2013-05-19 19:25:06 +02:00
|
|
|
Isview = FALSE;
|
2013-02-07 10:34:27 +01:00
|
|
|
Bind = FALSE;
|
|
|
|
Delayed = FALSE;
|
2013-10-26 17:14:58 +02:00
|
|
|
Xsrc = FALSE;
|
2013-02-07 10:34:27 +01:00
|
|
|
} // end of MYSQLDEF constructor
|
|
|
|
|
2013-06-16 19:07:27 +02:00
|
|
|
/***********************************************************************/
|
|
|
|
/* Get connection info from the declared server. */
|
|
|
|
/***********************************************************************/
|
|
|
|
bool MYSQLDEF::GetServerInfo(PGLOBAL g, const char *server_name)
|
|
|
|
{
|
|
|
|
THD *thd= current_thd;
|
|
|
|
MEM_ROOT *mem= thd->mem_root;
|
|
|
|
FOREIGN_SERVER *server, server_buffer;
|
|
|
|
DBUG_ENTER("GetServerInfo");
|
|
|
|
DBUG_PRINT("info", ("server_name %s", server_name));
|
|
|
|
|
|
|
|
if (!server_name || !strlen(server_name)) {
|
|
|
|
DBUG_PRINT("info", ("server_name not defined!"));
|
|
|
|
strcpy(g->Message, "server_name not defined!");
|
|
|
|
DBUG_RETURN(true);
|
|
|
|
} // endif server_name
|
|
|
|
|
|
|
|
// get_server_by_name() clones the server if exists and allocates
|
|
|
|
// copies of strings in the supplied mem_root
|
|
|
|
if (!(server= get_server_by_name(mem, server_name, &server_buffer))) {
|
|
|
|
DBUG_PRINT("info", ("get_server_by_name returned > 0 error condition!"));
|
|
|
|
/* need to come up with error handling */
|
|
|
|
strcpy(g->Message, "get_server_by_name returned > 0 error condition!");
|
|
|
|
DBUG_RETURN(true);
|
|
|
|
} // endif server
|
|
|
|
|
|
|
|
DBUG_PRINT("info", ("get_server_by_name returned server at %lx",
|
|
|
|
(long unsigned int) server));
|
|
|
|
|
|
|
|
// TODO: We need to examine which of these can really be NULL
|
|
|
|
Hostname = PlugDup(g, server->host);
|
|
|
|
Database = PlugDup(g, server->db);
|
|
|
|
Username = PlugDup(g, server->username);
|
|
|
|
Password = PlugDup(g, server->password);
|
|
|
|
Portnumber = (server->port) ? server->port : GetDefaultPort();
|
|
|
|
|
|
|
|
DBUG_RETURN(false);
|
|
|
|
} // end of GetServerInfo
|
|
|
|
|
2013-02-22 17:26:08 +01:00
|
|
|
/***********************************************************************/
|
|
|
|
/* Parse connection string */
|
|
|
|
/* */
|
|
|
|
/* SYNOPSIS */
|
|
|
|
/* ParseURL() */
|
|
|
|
/* url The connection string to parse */
|
|
|
|
/* */
|
|
|
|
/* DESCRIPTION */
|
|
|
|
/* Populates the table with information about the connection */
|
|
|
|
/* to the foreign database that will serve as the data source. */
|
|
|
|
/* This string must be specified (currently) in the "CONNECTION" */
|
|
|
|
/* field, listed in the CREATE TABLE statement. */
|
|
|
|
/* */
|
|
|
|
/* This string MUST be in the format of any of these: */
|
|
|
|
/* */
|
|
|
|
/* CONNECTION="scheme://user:pwd@host:port/database/table" */
|
|
|
|
/* CONNECTION="scheme://user@host/database/table" */
|
|
|
|
/* CONNECTION="scheme://user@host:port/database/table" */
|
|
|
|
/* CONNECTION="scheme://user:pwd@host/database/table" */
|
|
|
|
/* */
|
|
|
|
/* _OR_ */
|
|
|
|
/* */
|
|
|
|
/* CONNECTION="connection name" (NIY) */
|
|
|
|
/* */
|
|
|
|
/* An Example: */
|
|
|
|
/* */
|
|
|
|
/* CREATE TABLE t1 (id int(32)) */
|
2013-02-24 01:23:18 +01:00
|
|
|
/* ENGINE="CONNECT" TABLE_TYPE="MYSQL" */
|
2013-02-22 17:26:08 +01:00
|
|
|
/* CONNECTION="mysql://joe:pwd@192.168.1.111:9308/dbname/tabname"; */
|
|
|
|
/* */
|
|
|
|
/* CREATE TABLE t2 ( */
|
|
|
|
/* id int(4) NOT NULL auto_increment, */
|
|
|
|
/* name varchar(32) NOT NULL, */
|
|
|
|
/* PRIMARY KEY(id) */
|
2013-02-24 01:23:18 +01:00
|
|
|
/* ) ENGINE="CONNECT" TABLE_TYPE="MYSQL" */
|
|
|
|
/* CONNECTION="my_conn"; (NIY) */
|
2013-02-22 17:26:08 +01:00
|
|
|
/* */
|
|
|
|
/* 'password' and 'port' are both optional. */
|
|
|
|
/* */
|
|
|
|
/* RETURN VALUE */
|
|
|
|
/* false success */
|
|
|
|
/* true error */
|
|
|
|
/* */
|
|
|
|
/***********************************************************************/
|
2013-10-27 10:37:12 +01:00
|
|
|
bool MYSQLDEF::ParseURL(PGLOBAL g, char *url, bool b)
|
2013-02-22 17:26:08 +01:00
|
|
|
{
|
|
|
|
if ((!strstr(url, "://") && (!strchr(url, '@')))) {
|
|
|
|
// No :// or @ in connection string. Must be a straight
|
|
|
|
// connection name of either "server" or "server/table"
|
2013-06-16 19:07:27 +02:00
|
|
|
// ok, so we do a little parsing, but not completely!
|
|
|
|
if ((Tabname= strchr(url, '/'))) {
|
|
|
|
// If there is a single '/' in the connection string,
|
|
|
|
// this means the user is specifying a table name
|
|
|
|
*Tabname++= '\0';
|
|
|
|
|
|
|
|
// there better not be any more '/'s !
|
|
|
|
if (strchr(Tabname, '/'))
|
|
|
|
return true;
|
|
|
|
|
|
|
|
} else
|
|
|
|
// Otherwise, straight server name,
|
|
|
|
// use tablename of federatedx table as remote table name
|
|
|
|
Tabname= Name;
|
|
|
|
|
|
|
|
if (trace)
|
|
|
|
htrc("server: %s Tabname: %s", url, Tabname);
|
|
|
|
|
2013-08-09 18:02:47 +02:00
|
|
|
Server = url;
|
2013-06-16 19:07:27 +02:00
|
|
|
return GetServerInfo(g, url);
|
2013-02-22 17:26:08 +01:00
|
|
|
} else {
|
|
|
|
// URL, parse it
|
|
|
|
char *sport, *scheme = url;
|
|
|
|
|
|
|
|
if (!(Username = strstr(url, "://"))) {
|
|
|
|
strcpy(g->Message, "Connection is not an URL");
|
|
|
|
return true;
|
|
|
|
} // endif User
|
|
|
|
|
|
|
|
scheme[Username - scheme] = 0;
|
|
|
|
|
|
|
|
if (stricmp(scheme, "mysql")) {
|
|
|
|
strcpy(g->Message, "scheme must be mysql");
|
|
|
|
return true;
|
|
|
|
} // endif scheme
|
|
|
|
|
|
|
|
Username += 3;
|
|
|
|
|
|
|
|
if (!(Hostname = strchr(Username, '@'))) {
|
|
|
|
strcpy(g->Message, "No host specified in URL");
|
|
|
|
return true;
|
2013-08-09 18:02:47 +02:00
|
|
|
} else {
|
2013-02-22 17:26:08 +01:00
|
|
|
*Hostname++ = 0; // End Username
|
2013-08-09 18:02:47 +02:00
|
|
|
Server = Hostname;
|
|
|
|
} // endif Hostname
|
2013-02-22 17:26:08 +01:00
|
|
|
|
|
|
|
if ((Password = strchr(Username, ':'))) {
|
|
|
|
*Password++ = 0; // End username
|
|
|
|
|
|
|
|
// Make sure there isn't an extra / or @
|
|
|
|
if ((strchr(Password, '/') || strchr(Hostname, '@'))) {
|
|
|
|
strcpy(g->Message, "Syntax error in URL");
|
|
|
|
return true;
|
|
|
|
} // endif
|
|
|
|
|
|
|
|
// Found that if the string is:
|
|
|
|
// user:@hostname:port/db/table
|
|
|
|
// Then password is a null string, so set to NULL
|
|
|
|
if ((Password[0] == 0))
|
|
|
|
Password = NULL;
|
|
|
|
|
|
|
|
} // endif password
|
|
|
|
|
|
|
|
// Make sure there isn't an extra / or @ */
|
|
|
|
if ((strchr(Username, '/')) || (strchr(Hostname, '@'))) {
|
|
|
|
strcpy(g->Message, "Syntax error in URL");
|
|
|
|
return true;
|
|
|
|
} // endif
|
|
|
|
|
|
|
|
if ((Database = strchr(Hostname, '/'))) {
|
|
|
|
*Database++ = 0;
|
|
|
|
|
2013-10-27 10:37:12 +01:00
|
|
|
if ((Tabname = strchr(Database, '/'))) {
|
2013-02-22 17:26:08 +01:00
|
|
|
*Tabname++ = 0;
|
|
|
|
|
2013-10-27 10:37:12 +01:00
|
|
|
// Make sure there's not an extra /
|
|
|
|
if ((strchr(Tabname, '/'))) {
|
|
|
|
strcpy(g->Message, "Syntax error in URL");
|
|
|
|
return true;
|
|
|
|
} // endif /
|
|
|
|
|
|
|
|
} // endif Tabname
|
2013-02-22 17:26:08 +01:00
|
|
|
|
|
|
|
} // endif database
|
|
|
|
|
|
|
|
if ((sport = strchr(Hostname, ':')))
|
|
|
|
*sport++ = 0;
|
|
|
|
|
2013-10-27 10:37:12 +01:00
|
|
|
// For unspecified values, get the values of old style options
|
|
|
|
// but only if called from MYSQLDEF, else set them to NULL
|
|
|
|
Portnumber = (sport && sport[0]) ? atoi(sport)
|
2014-04-14 14:26:48 +02:00
|
|
|
: (b) ? GetIntCatInfo("Port", GetDefaultPort()) : 0;
|
2013-04-29 13:50:20 +02:00
|
|
|
|
|
|
|
if (Username[0] == 0)
|
2014-04-14 14:26:48 +02:00
|
|
|
Username = (b) ? GetStringCatInfo(g, "User", "*") : NULL;
|
2013-02-22 17:26:08 +01:00
|
|
|
|
|
|
|
if (Hostname[0] == 0)
|
2014-04-14 14:26:48 +02:00
|
|
|
Hostname = (b) ? GetStringCatInfo(g, "Host", "localhost") : NULL;
|
2013-02-22 17:26:08 +01:00
|
|
|
|
|
|
|
if (!Database || !*Database)
|
2014-04-14 14:26:48 +02:00
|
|
|
Database = (b) ? GetStringCatInfo(g, "Database", "*") : NULL;
|
2013-02-22 17:26:08 +01:00
|
|
|
|
|
|
|
if (!Tabname || !*Tabname)
|
2014-04-14 14:26:48 +02:00
|
|
|
Tabname = (b) ? GetStringCatInfo(g, "Tabname", Name) : NULL;
|
2013-02-22 17:26:08 +01:00
|
|
|
|
2013-10-27 10:37:12 +01:00
|
|
|
if (!Password)
|
2014-04-14 14:26:48 +02:00
|
|
|
Password = (b) ? GetStringCatInfo(g, "Password", NULL) : NULL;
|
2013-02-22 17:26:08 +01:00
|
|
|
} // endif URL
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
if (!share->port)
|
|
|
|
if (!share->hostname || strcmp(share->hostname, my_localhost) == 0)
|
|
|
|
share->socket= (char *) MYSQL_UNIX_ADDR;
|
|
|
|
else
|
|
|
|
share->port= MYSQL_PORT;
|
|
|
|
#endif // 0
|
|
|
|
|
|
|
|
return false;
|
|
|
|
} // end of ParseURL
|
|
|
|
|
2013-02-07 10:34:27 +01:00
|
|
|
/***********************************************************************/
|
|
|
|
/* DefineAM: define specific AM block values from XCV file. */
|
|
|
|
/***********************************************************************/
|
|
|
|
bool MYSQLDEF::DefineAM(PGLOBAL g, LPCSTR am, int poff)
|
|
|
|
{
|
2013-06-16 19:07:27 +02:00
|
|
|
char *url;
|
2013-02-22 17:26:08 +01:00
|
|
|
|
2013-02-07 10:34:27 +01:00
|
|
|
Desc = "MySQL Table";
|
2013-02-22 17:26:08 +01:00
|
|
|
|
2013-05-09 16:16:45 +02:00
|
|
|
if (stricmp(am, "MYPRX")) {
|
2013-05-02 16:33:15 +02:00
|
|
|
// Normal case of specific MYSQL table
|
2014-04-14 14:26:48 +02:00
|
|
|
url = GetStringCatInfo(g, "Connect", NULL);
|
2013-06-16 19:07:27 +02:00
|
|
|
|
2013-05-02 16:33:15 +02:00
|
|
|
if (!url || !*url) {
|
|
|
|
// Not using the connection URL
|
2014-04-14 14:26:48 +02:00
|
|
|
Hostname = GetStringCatInfo(g, "Host", "localhost");
|
|
|
|
Database = GetStringCatInfo(g, "Database", "*");
|
|
|
|
Tabname = GetStringCatInfo(g, "Name", Name); // Deprecated
|
|
|
|
Tabname = GetStringCatInfo(g, "Tabname", Tabname);
|
|
|
|
Username = GetStringCatInfo(g, "User", "*");
|
|
|
|
Password = GetStringCatInfo(g, "Password", NULL);
|
|
|
|
Portnumber = GetIntCatInfo("Port", GetDefaultPort());
|
2013-08-09 18:02:47 +02:00
|
|
|
Server = Hostname;
|
2013-05-02 16:33:15 +02:00
|
|
|
} else if (ParseURL(g, url))
|
2013-11-11 18:30:36 +01:00
|
|
|
return true;
|
2013-04-30 16:16:32 +02:00
|
|
|
|
2014-04-14 14:26:48 +02:00
|
|
|
Bind = !!GetIntCatInfo("Bind", 0);
|
|
|
|
Delayed = !!GetIntCatInfo("Delayed", 0);
|
2013-05-02 16:33:15 +02:00
|
|
|
} else {
|
2013-06-16 19:07:27 +02:00
|
|
|
// MYSQL access from a PROXY table
|
2014-04-14 14:26:48 +02:00
|
|
|
Database = GetStringCatInfo(g, "Database", "*");
|
|
|
|
Isview = GetBoolCatInfo("View", FALSE);
|
2013-05-02 16:33:15 +02:00
|
|
|
|
2013-06-16 19:07:27 +02:00
|
|
|
// We must get other connection parms from the calling table
|
2013-05-02 16:33:15 +02:00
|
|
|
Remove_tshp(Cat);
|
2014-04-14 14:26:48 +02:00
|
|
|
url = GetStringCatInfo(g, "Connect", NULL);
|
2013-06-16 19:07:27 +02:00
|
|
|
|
|
|
|
if (!url || !*url) {
|
2014-04-14 14:26:48 +02:00
|
|
|
Hostname = GetStringCatInfo(g, "Host", "localhost");
|
|
|
|
Username = GetStringCatInfo(g, "User", "*");
|
|
|
|
Password = GetStringCatInfo(g, "Password", NULL);
|
|
|
|
Portnumber = GetIntCatInfo("Port", GetDefaultPort());
|
2013-08-09 18:02:47 +02:00
|
|
|
Server = Hostname;
|
2013-06-16 19:07:27 +02:00
|
|
|
} else {
|
|
|
|
char *locdb = Database;
|
|
|
|
|
|
|
|
if (ParseURL(g, url))
|
2013-11-11 18:30:36 +01:00
|
|
|
return true;
|
2013-06-16 19:07:27 +02:00
|
|
|
|
|
|
|
Database = locdb;
|
|
|
|
} // endif url
|
|
|
|
|
|
|
|
Tabname = Name;
|
2013-05-02 16:33:15 +02:00
|
|
|
} // endif am
|
2013-02-22 17:26:08 +01:00
|
|
|
|
2014-04-14 14:26:48 +02:00
|
|
|
if ((Srcdef = GetStringCatInfo(g, "Srcdef", NULL)))
|
2013-11-11 18:30:36 +01:00
|
|
|
Isview = true;
|
|
|
|
|
|
|
|
// Used for Update and Delete
|
2014-04-14 14:26:48 +02:00
|
|
|
Qrystr = GetStringCatInfo(g, "Query_String", "?");
|
|
|
|
Quoted = GetIntCatInfo("Quoted", 0);
|
2013-05-19 19:25:06 +02:00
|
|
|
|
2013-11-06 18:22:09 +01:00
|
|
|
// Specific for command executing tables
|
2014-04-14 14:26:48 +02:00
|
|
|
Xsrc = GetBoolCatInfo("Execsrc", false);
|
|
|
|
Mxr = GetIntCatInfo("Maxerr", 0);
|
2013-02-07 10:34:27 +01:00
|
|
|
return FALSE;
|
|
|
|
} // end of DefineAM
|
|
|
|
|
|
|
|
/***********************************************************************/
|
|
|
|
/* GetTable: makes a new TDB of the proper type. */
|
|
|
|
/***********************************************************************/
|
|
|
|
PTDB MYSQLDEF::GetTable(PGLOBAL g, MODE m)
|
|
|
|
{
|
2013-10-26 17:14:58 +02:00
|
|
|
if (Xsrc)
|
|
|
|
return new(g) TDBMYEXC(this);
|
|
|
|
else if (Catfunc == FNC_COL)
|
2013-02-11 00:31:03 +01:00
|
|
|
return new(g) TDBMCL(this);
|
|
|
|
else
|
|
|
|
return new(g) TDBMYSQL(this);
|
|
|
|
|
2013-02-07 10:34:27 +01:00
|
|
|
} // end of GetTable
|
|
|
|
|
|
|
|
/* ------------------------------------------------------------------- */
|
|
|
|
|
|
|
|
/***********************************************************************/
|
|
|
|
/* Implementation of the TDBMYSQL class. */
|
|
|
|
/***********************************************************************/
|
|
|
|
TDBMYSQL::TDBMYSQL(PMYDEF tdp) : TDBASE(tdp)
|
|
|
|
{
|
|
|
|
if (tdp) {
|
2013-08-09 18:02:47 +02:00
|
|
|
Host = tdp->Hostname;
|
|
|
|
Database = tdp->Database;
|
|
|
|
Tabname = tdp->Tabname;
|
|
|
|
Srcdef = tdp->Srcdef;
|
|
|
|
User = tdp->Username;
|
|
|
|
Pwd = tdp->Password;
|
|
|
|
Server = tdp->Server;
|
2013-11-11 18:30:36 +01:00
|
|
|
Qrystr = tdp->Qrystr;
|
2014-04-21 12:57:10 +02:00
|
|
|
Quoted = MY_MAX(0, tdp->Quoted);
|
2013-08-09 18:02:47 +02:00
|
|
|
Port = tdp->Portnumber;
|
2013-05-19 19:25:06 +02:00
|
|
|
Isview = tdp->Isview;
|
2013-02-07 10:34:27 +01:00
|
|
|
Prep = tdp->Bind;
|
|
|
|
Delayed = tdp->Delayed;
|
|
|
|
} else {
|
|
|
|
Host = NULL;
|
|
|
|
Database = NULL;
|
|
|
|
Tabname = NULL;
|
2013-05-19 19:25:06 +02:00
|
|
|
Srcdef = NULL;
|
2013-02-07 10:34:27 +01:00
|
|
|
User = NULL;
|
|
|
|
Pwd = NULL;
|
2013-08-09 18:02:47 +02:00
|
|
|
Server = NULL;
|
2013-11-11 18:30:36 +01:00
|
|
|
Qrystr = NULL;
|
|
|
|
Quoted = 0;
|
2013-02-07 10:34:27 +01:00
|
|
|
Port = 0;
|
2013-05-19 19:25:06 +02:00
|
|
|
Isview = FALSE;
|
2013-02-07 10:34:27 +01:00
|
|
|
Prep = FALSE;
|
|
|
|
Delayed = FALSE;
|
|
|
|
} // endif tdp
|
|
|
|
|
|
|
|
Bind = NULL;
|
|
|
|
Query = NULL;
|
|
|
|
Qbuf = NULL;
|
|
|
|
Fetched = FALSE;
|
|
|
|
m_Rc = RC_FX;
|
|
|
|
AftRows = 0;
|
|
|
|
N = -1;
|
|
|
|
Nparm = 0;
|
|
|
|
} // end of TDBMYSQL constructor
|
|
|
|
|
|
|
|
TDBMYSQL::TDBMYSQL(PGLOBAL g, PTDBMY tdbp) : TDBASE(tdbp)
|
|
|
|
{
|
|
|
|
Host = tdbp->Host;
|
|
|
|
Database = tdbp->Database;
|
|
|
|
Tabname = tdbp->Tabname;
|
2013-05-19 19:25:06 +02:00
|
|
|
Srcdef = tdbp->Srcdef;
|
2013-02-07 10:34:27 +01:00
|
|
|
User = tdbp->User;
|
|
|
|
Pwd = tdbp->Pwd;
|
2013-11-11 18:30:36 +01:00
|
|
|
Qrystr = tdbp->Qrystr;
|
|
|
|
Quoted = tdbp->Quoted;
|
2013-02-07 10:34:27 +01:00
|
|
|
Port = tdbp->Port;
|
2013-05-19 19:25:06 +02:00
|
|
|
Isview = tdbp->Isview;
|
2013-02-07 10:34:27 +01:00
|
|
|
Prep = tdbp->Prep;
|
|
|
|
Delayed = tdbp->Delayed;
|
|
|
|
Bind = NULL;
|
|
|
|
Query = tdbp->Query;
|
|
|
|
Qbuf = NULL;
|
|
|
|
Fetched = tdbp->Fetched;
|
|
|
|
m_Rc = tdbp->m_Rc;
|
|
|
|
AftRows = tdbp->AftRows;
|
|
|
|
N = tdbp->N;
|
|
|
|
Nparm = tdbp->Nparm;
|
|
|
|
} // end of TDBMYSQL copy constructor
|
|
|
|
|
|
|
|
// Is this really useful ???
|
|
|
|
PTDB TDBMYSQL::CopyOne(PTABS t)
|
|
|
|
{
|
|
|
|
PTDB tp;
|
|
|
|
PCOL cp1, cp2;
|
|
|
|
PGLOBAL g = t->G;
|
|
|
|
|
|
|
|
tp = new(g) TDBMYSQL(g, this);
|
|
|
|
|
|
|
|
for (cp1 = Columns; cp1; cp1 = cp1->GetNext()) {
|
|
|
|
cp2 = new(g) MYSQLCOL((PMYCOL)cp1, tp);
|
|
|
|
|
|
|
|
NewPointer(t, cp1, cp2);
|
|
|
|
} // endfor cp1
|
|
|
|
|
|
|
|
return tp;
|
|
|
|
} // end of CopyOne
|
|
|
|
|
|
|
|
/***********************************************************************/
|
|
|
|
/* Allocate MYSQL column description block. */
|
|
|
|
/***********************************************************************/
|
|
|
|
PCOL TDBMYSQL::MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n)
|
|
|
|
{
|
|
|
|
return new(g) MYSQLCOL(cdp, this, cprec, n);
|
|
|
|
} // end of MakeCol
|
|
|
|
|
|
|
|
/***********************************************************************/
|
|
|
|
/* MakeSelect: make the Select statement use with MySQL connection. */
|
|
|
|
/* Note: when implementing EOM filtering, column only used in local */
|
|
|
|
/* filter should be removed from column list. */
|
|
|
|
/***********************************************************************/
|
2014-04-19 17:02:53 +02:00
|
|
|
bool TDBMYSQL::MakeSelect(PGLOBAL g, bool mx)
|
2013-02-07 10:34:27 +01:00
|
|
|
{
|
|
|
|
char *tk = "`";
|
2014-04-19 17:02:53 +02:00
|
|
|
int len = 0, rank = 0;
|
|
|
|
bool b = false;
|
2013-02-07 10:34:27 +01:00
|
|
|
PCOL colp;
|
2013-05-19 19:25:06 +02:00
|
|
|
//PDBUSER dup = PlgGetUser(g);
|
2013-02-07 10:34:27 +01:00
|
|
|
|
|
|
|
if (Query)
|
2014-04-19 17:02:53 +02:00
|
|
|
return false; // already done
|
2013-02-07 10:34:27 +01:00
|
|
|
|
2013-05-19 19:25:06 +02:00
|
|
|
if (Srcdef) {
|
|
|
|
Query = Srcdef;
|
|
|
|
return false;
|
|
|
|
} // endif Srcdef
|
2013-02-07 10:34:27 +01:00
|
|
|
|
2013-05-19 19:25:06 +02:00
|
|
|
//Find the address of the suballocated query
|
|
|
|
Query = (char*)PlugSubAlloc(g, NULL, 0);
|
|
|
|
strcpy(Query, "SELECT ");
|
2013-02-07 10:34:27 +01:00
|
|
|
|
2013-05-19 19:25:06 +02:00
|
|
|
if (Columns) {
|
2013-02-07 10:34:27 +01:00
|
|
|
for (colp = Columns; colp; colp = colp->GetNext())
|
2013-08-09 18:02:47 +02:00
|
|
|
if (!colp->IsSpecial()) {
|
|
|
|
// if (colp->IsSpecial()) {
|
|
|
|
// strcpy(g->Message, MSG(NO_SPEC_COL));
|
2014-04-19 17:02:53 +02:00
|
|
|
// return true;
|
2013-08-09 18:02:47 +02:00
|
|
|
// } else {
|
2013-02-07 10:34:27 +01:00
|
|
|
if (b)
|
2013-05-19 19:25:06 +02:00
|
|
|
strcat(Query, ", ");
|
2013-02-07 10:34:27 +01:00
|
|
|
else
|
2014-04-19 17:02:53 +02:00
|
|
|
b = true;
|
2013-02-07 10:34:27 +01:00
|
|
|
|
2013-05-19 19:25:06 +02:00
|
|
|
strcat(strcat(strcat(Query, tk), colp->GetName()), tk);
|
2013-02-07 10:34:27 +01:00
|
|
|
((PMYCOL)colp)->Rank = rank++;
|
|
|
|
} // endif colp
|
|
|
|
|
|
|
|
} else {
|
2013-05-19 19:25:06 +02:00
|
|
|
// ncol == 0 can occur for views or queries such as
|
|
|
|
// Query count(*) from... for which we will count the rows from
|
|
|
|
// Query '*' from...
|
2013-02-07 10:34:27 +01:00
|
|
|
// (the use of a char constant minimize the result storage)
|
2013-05-19 19:25:06 +02:00
|
|
|
strcat(Query, (Isview) ? "*" : "'*'");
|
2013-02-07 10:34:27 +01:00
|
|
|
} // endif ncol
|
|
|
|
|
|
|
|
strcat(strcat(strcat(strcat(Query, " FROM "), tk), Tabname), tk);
|
2014-04-19 17:02:53 +02:00
|
|
|
len = strlen(Query);
|
|
|
|
|
|
|
|
if (To_CondFil) {
|
|
|
|
if (!mx) {
|
|
|
|
strcat(strcat(Query, " WHERE "), To_CondFil->Body);
|
|
|
|
len = strlen(Query) + 1;
|
|
|
|
} else
|
|
|
|
len += (strlen(To_CondFil->Body) + 256);
|
2013-02-07 10:34:27 +01:00
|
|
|
|
2014-04-19 17:02:53 +02:00
|
|
|
} else
|
|
|
|
len += (mx ? 256 : 1);
|
2013-02-07 10:34:27 +01:00
|
|
|
|
2013-10-02 19:58:49 +02:00
|
|
|
if (trace)
|
|
|
|
htrc("Query=%s\n", Query);
|
|
|
|
|
2013-05-19 19:25:06 +02:00
|
|
|
// Now we know how much to suballocate
|
2014-04-19 17:02:53 +02:00
|
|
|
PlugSubAlloc(g, NULL, len);
|
|
|
|
return false;
|
2013-02-07 10:34:27 +01:00
|
|
|
} // end of MakeSelect
|
|
|
|
|
|
|
|
/***********************************************************************/
|
|
|
|
/* MakeInsert: make the Insert statement used with MySQL connection. */
|
|
|
|
/***********************************************************************/
|
|
|
|
bool TDBMYSQL::MakeInsert(PGLOBAL g)
|
|
|
|
{
|
|
|
|
char *colist, *valist = NULL;
|
|
|
|
char *tk = "`";
|
|
|
|
int len = 0, qlen = 0;
|
|
|
|
bool b = FALSE;
|
|
|
|
PCOL colp;
|
|
|
|
|
|
|
|
if (Query)
|
|
|
|
return FALSE; // already done
|
|
|
|
|
|
|
|
for (colp = Columns; colp; colp = colp->GetNext())
|
2013-08-09 18:02:47 +02:00
|
|
|
if (!colp->IsSpecial()) {
|
|
|
|
// if (colp->IsSpecial()) {
|
|
|
|
// strcpy(g->Message, MSG(NO_SPEC_COL));
|
|
|
|
// return TRUE;
|
|
|
|
// } else {
|
2013-02-07 10:34:27 +01:00
|
|
|
len += (strlen(colp->GetName()) + 4);
|
|
|
|
((PMYCOL)colp)->Rank = Nparm++;
|
|
|
|
} // endif colp
|
|
|
|
|
|
|
|
colist = (char*)PlugSubAlloc(g, NULL, len);
|
|
|
|
*colist = '\0';
|
|
|
|
|
|
|
|
if (Prep) {
|
2013-02-20 16:57:38 +01:00
|
|
|
#if defined(MYSQL_PREPARED_STATEMENTS)
|
2013-02-07 10:34:27 +01:00
|
|
|
valist = (char*)PlugSubAlloc(g, NULL, 2 * Nparm);
|
|
|
|
*valist = '\0';
|
2013-02-20 16:57:38 +01:00
|
|
|
#else // !MYSQL_PREPARED_STATEMENTS
|
|
|
|
strcpy(g->Message, "Prepared statements not used (not supported)");
|
|
|
|
PushWarning(g, this);
|
|
|
|
Prep = FALSE;
|
|
|
|
#endif // !MYSQL_PREPARED_STATEMENTS
|
2013-02-07 10:34:27 +01:00
|
|
|
} // endif Prep
|
|
|
|
|
|
|
|
for (colp = Columns; colp; colp = colp->GetNext()) {
|
|
|
|
if (b) {
|
|
|
|
strcat(colist, ", ");
|
|
|
|
if (Prep) strcat(valist, ",");
|
|
|
|
} else
|
|
|
|
b = TRUE;
|
|
|
|
|
|
|
|
strcat(strcat(strcat(colist, tk), colp->GetName()), tk);
|
|
|
|
|
|
|
|
// Parameter marker
|
|
|
|
if (!Prep) {
|
|
|
|
if (colp->GetResultType() == TYPE_DATE)
|
|
|
|
qlen += 20;
|
|
|
|
else
|
|
|
|
qlen += colp->GetLength();
|
|
|
|
|
|
|
|
} // endif Prep
|
|
|
|
|
|
|
|
if (Prep)
|
|
|
|
strcat(valist, "?");
|
|
|
|
|
|
|
|
} // endfor colp
|
|
|
|
|
|
|
|
// Below 40 is enough to contain the fixed part of the query
|
|
|
|
len = (strlen(Tabname) + strlen(colist)
|
|
|
|
+ ((Prep) ? strlen(valist) : 0) + 40);
|
|
|
|
Query = (char*)PlugSubAlloc(g, NULL, len);
|
|
|
|
|
|
|
|
if (Delayed)
|
|
|
|
strcpy(Query, "INSERT DELAYED INTO ");
|
|
|
|
else
|
|
|
|
strcpy(Query, "INSERT INTO ");
|
|
|
|
|
|
|
|
strcat(strcat(strcat(Query, tk), Tabname), tk);
|
|
|
|
strcat(strcat(strcat(Query, " ("), colist), ") VALUES (");
|
|
|
|
|
|
|
|
if (Prep)
|
|
|
|
strcat(strcat(Query, valist), ")");
|
|
|
|
else {
|
|
|
|
qlen += (strlen(Query) + Nparm);
|
|
|
|
Qbuf = (char *)PlugSubAlloc(g, NULL, qlen);
|
|
|
|
} // endelse Prep
|
|
|
|
|
|
|
|
return FALSE;
|
|
|
|
} // end of MakeInsert
|
|
|
|
|
2013-11-13 00:15:38 +01:00
|
|
|
/***********************************************************************/
|
|
|
|
/* MakeCommand: make the Update or Delete statement to send to the */
|
|
|
|
/* MySQL server. Limited to remote values and filtering. */
|
|
|
|
/***********************************************************************/
|
|
|
|
int TDBMYSQL::MakeCommand(PGLOBAL g)
|
|
|
|
{
|
|
|
|
Query = (char*)PlugSubAlloc(g, NULL, strlen(Qrystr) + 64);
|
|
|
|
|
|
|
|
if (Quoted > 0 || stricmp(Name, Tabname)) {
|
|
|
|
char *p, *qrystr, name[68];
|
|
|
|
bool qtd = Quoted > 0;
|
|
|
|
|
|
|
|
|
|
|
|
// Make a lower case copy of the originale query
|
|
|
|
qrystr = (char*)PlugSubAlloc(g, NULL, strlen(Qrystr) + 1);
|
|
|
|
strlwr(strcpy(qrystr, Qrystr));
|
|
|
|
|
|
|
|
// Check whether the table name is equal to a keyword
|
|
|
|
// If so, it must be quoted in the original query
|
|
|
|
strlwr(strcat(strcat(strcpy(name, "`"), Name), "`"));
|
|
|
|
|
|
|
|
if (!strstr("`update`delete`low_priority`ignore`quick`from`", name))
|
|
|
|
strlwr(strcpy(name, Name)); // Not a keyword
|
|
|
|
|
|
|
|
if ((p = strstr(qrystr, name))) {
|
|
|
|
memcpy(Query, Qrystr, p - qrystr);
|
|
|
|
Query[p - qrystr] = 0;
|
|
|
|
|
|
|
|
if (qtd && *(p-1) == ' ')
|
|
|
|
strcat(strcat(strcat(Query, "`"), Tabname), "`");
|
|
|
|
else
|
|
|
|
strcat(Query, Tabname);
|
|
|
|
|
|
|
|
strcat(Query, Qrystr + (p - qrystr) + strlen(name));
|
|
|
|
} else {
|
|
|
|
sprintf(g->Message, "Cannot use this %s command",
|
|
|
|
(Mode == MODE_UPDATE) ? "UPDATE" : "DELETE");
|
|
|
|
return RC_FX;
|
|
|
|
} // endif p
|
|
|
|
|
|
|
|
} else
|
|
|
|
strcpy(Query, Qrystr);
|
|
|
|
|
|
|
|
return RC_OK;
|
|
|
|
} // end of MakeCommand
|
|
|
|
|
|
|
|
#if 0
|
2013-02-07 10:34:27 +01:00
|
|
|
/***********************************************************************/
|
|
|
|
/* MakeUpdate: make the Update statement use with MySQL connection. */
|
2013-11-11 18:30:36 +01:00
|
|
|
/* Limited to remote values and filtering. */
|
2013-02-07 10:34:27 +01:00
|
|
|
/***********************************************************************/
|
2013-11-11 18:30:36 +01:00
|
|
|
int TDBMYSQL::MakeUpdate(PGLOBAL g)
|
2013-02-07 10:34:27 +01:00
|
|
|
{
|
2013-11-11 18:30:36 +01:00
|
|
|
char *qc, cmd[8], tab[96], end[1024];
|
2013-02-07 10:34:27 +01:00
|
|
|
|
2013-11-11 18:30:36 +01:00
|
|
|
Query = (char*)PlugSubAlloc(g, NULL, strlen(Qrystr) + 64);
|
|
|
|
memset(end, 0, sizeof(end));
|
2013-02-07 10:34:27 +01:00
|
|
|
|
2013-11-11 18:30:36 +01:00
|
|
|
if (sscanf(Qrystr, "%s `%[^`]`%1023c", cmd, tab, end) > 2 ||
|
|
|
|
sscanf(Qrystr, "%s \"%[^\"]\"%1023c", cmd, tab, end) > 2)
|
|
|
|
qc = "`";
|
2013-11-13 00:15:38 +01:00
|
|
|
else if (sscanf(Qrystr, "%s %s%1023c", cmd, tab, end) > 2
|
|
|
|
&& !stricmp(tab, Name))
|
2013-11-11 18:30:36 +01:00
|
|
|
qc = (Quoted) ? "`" : "";
|
|
|
|
else {
|
|
|
|
strcpy(g->Message, "Cannot use this UPDATE command");
|
|
|
|
return RC_FX;
|
|
|
|
} // endif sscanf
|
2013-02-07 10:34:27 +01:00
|
|
|
|
2013-11-11 18:30:36 +01:00
|
|
|
assert(!stricmp(cmd, "update"));
|
|
|
|
strcat(strcat(strcat(strcpy(Query, "UPDATE "), qc), Tabname), qc);
|
|
|
|
strcat(Query, end);
|
|
|
|
return RC_OK;
|
2013-02-07 10:34:27 +01:00
|
|
|
} // end of MakeUpdate
|
|
|
|
|
|
|
|
/***********************************************************************/
|
2013-11-11 18:30:36 +01:00
|
|
|
/* MakeDelete: make the Delete statement used with MySQL connection. */
|
|
|
|
/* Limited to remote filtering. */
|
2013-02-07 10:34:27 +01:00
|
|
|
/***********************************************************************/
|
2013-11-11 18:30:36 +01:00
|
|
|
int TDBMYSQL::MakeDelete(PGLOBAL g)
|
2013-02-07 10:34:27 +01:00
|
|
|
{
|
2013-11-11 18:30:36 +01:00
|
|
|
char *qc, cmd[8], from[8], tab[96], end[512];
|
2013-02-07 10:34:27 +01:00
|
|
|
|
2013-11-11 18:30:36 +01:00
|
|
|
Query = (char*)PlugSubAlloc(g, NULL, strlen(Qrystr) + 64);
|
|
|
|
memset(end, 0, sizeof(end));
|
2013-02-07 10:34:27 +01:00
|
|
|
|
2013-11-11 18:30:36 +01:00
|
|
|
if (sscanf(Qrystr, "%s %s `%[^`]`%511c", cmd, from, tab, end) > 2 ||
|
|
|
|
sscanf(Qrystr, "%s %s \"%[^\"]\"%511c", cmd, from, tab, end) > 2)
|
|
|
|
qc = "`";
|
|
|
|
else if (sscanf(Qrystr, "%s %s %s%511c", cmd, from, tab, end) > 2)
|
|
|
|
qc = (Quoted) ? "`" : "";
|
|
|
|
else {
|
|
|
|
strcpy(g->Message, "Cannot use this DELETE command");
|
|
|
|
return RC_FX;
|
|
|
|
} // endif sscanf
|
2013-02-07 10:34:27 +01:00
|
|
|
|
2013-11-11 18:30:36 +01:00
|
|
|
assert(!stricmp(cmd, "delete") && !stricmp(from, "from"));
|
|
|
|
strcat(strcat(strcat(strcpy(Query, "DELETE FROM "), qc), Tabname), qc);
|
2013-02-07 10:34:27 +01:00
|
|
|
|
2013-11-11 18:30:36 +01:00
|
|
|
if (*end)
|
|
|
|
strcat(Query, end);
|
2013-02-07 10:34:27 +01:00
|
|
|
|
2013-11-11 18:30:36 +01:00
|
|
|
return RC_OK;
|
2013-02-07 10:34:27 +01:00
|
|
|
} // end of MakeDelete
|
2013-11-13 00:15:38 +01:00
|
|
|
#endif // 0
|
2013-02-07 10:34:27 +01:00
|
|
|
|
|
|
|
/***********************************************************************/
|
|
|
|
/* XCV GetMaxSize: returns the maximum number of rows in the table. */
|
|
|
|
/***********************************************************************/
|
|
|
|
int TDBMYSQL::GetMaxSize(PGLOBAL g)
|
|
|
|
{
|
|
|
|
if (MaxSize < 0) {
|
|
|
|
#if 0
|
|
|
|
if (MakeSelect(g))
|
|
|
|
return -2;
|
|
|
|
|
|
|
|
if (!Myc.Connected()) {
|
|
|
|
if (Myc.Open(g, Host, Database, User, Pwd, Port))
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
} // endif connected
|
|
|
|
|
|
|
|
if ((MaxSize = Myc.GetResultSize(g, Query)) < 0) {
|
|
|
|
Myc.Close();
|
|
|
|
return -3;
|
|
|
|
} // endif MaxSize
|
|
|
|
|
|
|
|
// FIXME: Columns should be known when Info calls GetMaxSize
|
|
|
|
if (!Columns)
|
|
|
|
Query = NULL; // Must be remade when columns are known
|
|
|
|
#endif // 0
|
|
|
|
|
2013-11-11 18:30:36 +01:00
|
|
|
// Return 0 in mode DELETE in case of delete all.
|
|
|
|
MaxSize = (Mode == MODE_DELETE) ? 0 : 10; // To make MySQL happy
|
2013-02-07 10:34:27 +01:00
|
|
|
} // endif MaxSize
|
|
|
|
|
|
|
|
return MaxSize;
|
|
|
|
} // end of GetMaxSize
|
|
|
|
|
|
|
|
/***********************************************************************/
|
|
|
|
/* This a fake routine as ROWID does not exist in MySQL. */
|
|
|
|
/***********************************************************************/
|
|
|
|
int TDBMYSQL::RowNumber(PGLOBAL g, bool b)
|
|
|
|
{
|
|
|
|
return N;
|
|
|
|
} // end of RowNumber
|
|
|
|
|
|
|
|
/***********************************************************************/
|
2013-11-11 18:30:36 +01:00
|
|
|
/* Return 0 in mode UPDATE to tell that the update is done. */
|
2013-02-07 10:34:27 +01:00
|
|
|
/***********************************************************************/
|
|
|
|
int TDBMYSQL::GetProgMax(PGLOBAL g)
|
|
|
|
{
|
2013-11-11 18:30:36 +01:00
|
|
|
return (Mode == MODE_UPDATE) ? 0 : GetMaxSize(g);
|
2013-02-07 10:34:27 +01:00
|
|
|
} // end of GetProgMax
|
|
|
|
|
|
|
|
/***********************************************************************/
|
|
|
|
/* MySQL Bind Parameter function. */
|
|
|
|
/***********************************************************************/
|
|
|
|
int TDBMYSQL::BindColumns(PGLOBAL g)
|
|
|
|
{
|
2013-02-20 16:57:38 +01:00
|
|
|
#if defined(MYSQL_PREPARED_STATEMENTS)
|
2013-02-07 10:34:27 +01:00
|
|
|
if (Prep) {
|
|
|
|
Bind = (MYSQL_BIND*)PlugSubAlloc(g, NULL, Nparm * sizeof(MYSQL_BIND));
|
|
|
|
|
|
|
|
for (PMYCOL colp = (PMYCOL)Columns; colp; colp = (PMYCOL)colp->Next)
|
|
|
|
colp->InitBind(g);
|
|
|
|
|
|
|
|
return Myc.BindParams(g, Bind);
|
2013-02-20 16:57:38 +01:00
|
|
|
} // endif prep
|
|
|
|
#endif // MYSQL_PREPARED_STATEMENTS
|
2013-02-07 10:34:27 +01:00
|
|
|
|
2013-02-20 16:57:38 +01:00
|
|
|
return RC_OK;
|
2013-02-07 10:34:27 +01:00
|
|
|
} // end of BindColumns
|
|
|
|
|
|
|
|
/***********************************************************************/
|
|
|
|
/* MySQL Access Method opening routine. */
|
|
|
|
/***********************************************************************/
|
|
|
|
bool TDBMYSQL::OpenDB(PGLOBAL g)
|
|
|
|
{
|
|
|
|
if (Use == USE_OPEN) {
|
|
|
|
/*******************************************************************/
|
|
|
|
/* Table already open, just replace it at its beginning. */
|
|
|
|
/*******************************************************************/
|
|
|
|
Myc.Rewind();
|
2013-05-19 19:25:06 +02:00
|
|
|
return false;
|
2013-02-07 10:34:27 +01:00
|
|
|
} // endif use
|
|
|
|
|
|
|
|
/*********************************************************************/
|
|
|
|
/* Open a MySQL connection for this table. */
|
|
|
|
/* Note: this may not be the proper way to do. Perhaps it is better */
|
|
|
|
/* to test whether a connection is already open for this server */
|
|
|
|
/* and if so to allocate just a new result set. But this only for */
|
|
|
|
/* servers allowing concurency in getting results ??? */
|
|
|
|
/*********************************************************************/
|
|
|
|
if (!Myc.Connected()) {
|
|
|
|
if (Myc.Open(g, Host, Database, User, Pwd, Port))
|
2013-05-19 19:25:06 +02:00
|
|
|
return true;
|
2013-02-07 10:34:27 +01:00
|
|
|
|
|
|
|
} // endif Connected
|
|
|
|
|
2013-06-26 19:42:28 +02:00
|
|
|
/*********************************************************************/
|
|
|
|
/* Take care of DATE columns. */
|
|
|
|
/*********************************************************************/
|
|
|
|
for (PMYCOL colp = (PMYCOL)Columns; colp; colp = (PMYCOL)colp->Next)
|
|
|
|
if (colp->Buf_Type == TYPE_DATE)
|
|
|
|
// Format must match DATETIME MySQL type
|
|
|
|
((DTVAL*)colp->GetValue())->SetFormat(g, "YYYY-MM-DD hh:mm:ss", 19);
|
|
|
|
|
2013-02-07 10:34:27 +01:00
|
|
|
/*********************************************************************/
|
|
|
|
/* Allocate whatever is used for getting results. */
|
|
|
|
/*********************************************************************/
|
2014-04-19 17:02:53 +02:00
|
|
|
if (Mode == MODE_READ || Mode == MODE_READX) {
|
|
|
|
MakeSelect(g, Mode == MODE_READX);
|
|
|
|
m_Rc = (Mode == MODE_READ) ? Myc.ExecSQL(g, Query) : RC_OK;
|
2013-02-07 10:34:27 +01:00
|
|
|
|
2013-05-19 19:25:06 +02:00
|
|
|
#if 0
|
|
|
|
if (!Myc.m_Res || !Myc.m_Fields) {
|
|
|
|
sprintf(g->Message, "%s result", (Myc.m_Res) ? "Void" : "No");
|
|
|
|
Myc.Close();
|
|
|
|
return true;
|
|
|
|
} // endif m_Res
|
|
|
|
#endif // 0
|
|
|
|
|
2013-05-24 00:19:26 +02:00
|
|
|
if (!m_Rc && Srcdef)
|
2013-05-19 19:25:06 +02:00
|
|
|
if (SetColumnRanks(g))
|
|
|
|
return true;
|
|
|
|
|
2013-02-07 10:34:27 +01:00
|
|
|
} else if (Mode == MODE_INSERT) {
|
2013-05-19 19:25:06 +02:00
|
|
|
if (Srcdef) {
|
|
|
|
strcpy(g->Message, "No insert into anonym views");
|
|
|
|
return true;
|
|
|
|
} // endif Srcdef
|
|
|
|
|
2013-02-07 10:34:27 +01:00
|
|
|
if (!MakeInsert(g)) {
|
2013-02-20 16:57:38 +01:00
|
|
|
#if defined(MYSQL_PREPARED_STATEMENTS)
|
2013-02-07 10:34:27 +01:00
|
|
|
int n = (Prep) ? Myc.PrepareSQL(g, Query) : Nparm;
|
|
|
|
|
|
|
|
if (Nparm != n) {
|
|
|
|
if (n >= 0) // Other errors return negative values
|
|
|
|
strcpy(g->Message, MSG(BAD_PARM_COUNT));
|
|
|
|
|
|
|
|
} else
|
2013-02-20 16:57:38 +01:00
|
|
|
#endif // MYSQL_PREPARED_STATEMENTS
|
2013-02-07 10:34:27 +01:00
|
|
|
m_Rc = BindColumns(g);
|
|
|
|
|
|
|
|
} // endif MakeInsert
|
|
|
|
|
|
|
|
if (m_Rc != RC_FX) {
|
|
|
|
char cmd[64];
|
|
|
|
int w;
|
|
|
|
|
|
|
|
sprintf(cmd, "ALTER TABLE `%s` DISABLE KEYS", Tabname);
|
|
|
|
m_Rc = Myc.ExecSQL(g, cmd, &w);
|
|
|
|
} // endif m_Rc
|
|
|
|
|
2013-11-11 18:30:36 +01:00
|
|
|
} else
|
2013-11-13 00:15:38 +01:00
|
|
|
// m_Rc = (Mode == MODE_DELETE) ? MakeDelete(g) : MakeUpdate(g);
|
|
|
|
m_Rc = MakeCommand(g);
|
2013-02-07 10:34:27 +01:00
|
|
|
|
|
|
|
if (m_Rc == RC_FX) {
|
|
|
|
Myc.Close();
|
2013-11-11 18:30:36 +01:00
|
|
|
return true;
|
2013-02-07 10:34:27 +01:00
|
|
|
} // endif rc
|
|
|
|
|
2013-11-11 18:30:36 +01:00
|
|
|
Use = USE_OPEN;
|
|
|
|
return false;
|
2013-02-07 10:34:27 +01:00
|
|
|
} // end of OpenDB
|
|
|
|
|
2013-05-19 19:25:06 +02:00
|
|
|
/***********************************************************************/
|
|
|
|
/* Set the rank of columns in the result set. */
|
|
|
|
/***********************************************************************/
|
|
|
|
bool TDBMYSQL::SetColumnRanks(PGLOBAL g)
|
|
|
|
{
|
|
|
|
for (PCOL colp = Columns; colp; colp = colp->GetNext())
|
|
|
|
if (((PMYCOL)colp)->FindRank(g))
|
|
|
|
return TRUE;
|
|
|
|
|
|
|
|
return FALSE;
|
|
|
|
} // end of SetColumnRanks
|
|
|
|
|
|
|
|
/***********************************************************************/
|
|
|
|
/* Called by Parent table to make the columns of a View. */
|
|
|
|
/***********************************************************************/
|
|
|
|
PCOL TDBMYSQL::MakeFieldColumn(PGLOBAL g, char *name)
|
|
|
|
{
|
|
|
|
int n;
|
|
|
|
MYSQL_FIELD *fld;
|
|
|
|
PCOL cp, colp = NULL;
|
|
|
|
|
|
|
|
for (n = 0; n < Myc.m_Fields; n++) {
|
|
|
|
fld = &Myc.m_Res->fields[n];
|
|
|
|
|
|
|
|
if (!stricmp(name, fld->name)) {
|
|
|
|
colp = new(g) MYSQLCOL(fld, this, n);
|
|
|
|
|
|
|
|
if (colp->InitValue(g))
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
if (!Columns)
|
|
|
|
Columns = colp;
|
|
|
|
else for (cp = Columns; cp; cp = cp->GetNext())
|
|
|
|
if (!cp->GetNext()) {
|
|
|
|
cp->SetNext(colp);
|
|
|
|
break;
|
|
|
|
} // endif Next
|
|
|
|
|
|
|
|
break;
|
|
|
|
} // endif name
|
|
|
|
|
|
|
|
} // endfor n
|
|
|
|
|
|
|
|
if (!colp)
|
|
|
|
sprintf(g->Message, "Column %s is not in view", name);
|
|
|
|
|
|
|
|
return colp;
|
|
|
|
} // end of MakeFieldColumn
|
|
|
|
|
2013-05-22 13:35:21 +02:00
|
|
|
/***********************************************************************/
|
|
|
|
/* Called by Pivot tables to find default column names in a View */
|
|
|
|
/* as the name of last field not equal to the passed name. */
|
|
|
|
/***********************************************************************/
|
|
|
|
char *TDBMYSQL::FindFieldColumn(char *name)
|
|
|
|
{
|
|
|
|
int n;
|
|
|
|
MYSQL_FIELD *fld;
|
|
|
|
char *cp = NULL;
|
|
|
|
|
|
|
|
for (n = Myc.m_Fields - 1; n >= 0; n--) {
|
|
|
|
fld = &Myc.m_Res->fields[n];
|
|
|
|
|
|
|
|
if (!name || stricmp(name, fld->name)) {
|
|
|
|
cp = fld->name;
|
|
|
|
break;
|
|
|
|
} // endif name
|
|
|
|
|
|
|
|
} // endfor n
|
|
|
|
|
|
|
|
return cp;
|
|
|
|
} // end of FindFieldColumn
|
|
|
|
|
2013-11-11 18:30:36 +01:00
|
|
|
/***********************************************************************/
|
|
|
|
/* Send an UPDATE or DELETE command to the remote server. */
|
|
|
|
/***********************************************************************/
|
|
|
|
int TDBMYSQL::SendCommand(PGLOBAL g)
|
|
|
|
{
|
|
|
|
int w;
|
|
|
|
|
|
|
|
if (Myc.ExecSQLcmd(g, Query, &w) == RC_NF) {
|
|
|
|
AftRows = Myc.m_Afrw;
|
|
|
|
sprintf(g->Message, "%s: %d affected rows", Tabname, AftRows);
|
|
|
|
PushWarning(g, this, 0); // 0 means a Note
|
|
|
|
|
|
|
|
if (trace)
|
|
|
|
htrc("%s\n", g->Message);
|
|
|
|
|
|
|
|
if (w && Myc.ExecSQL(g, "SHOW WARNINGS") == RC_OK) {
|
|
|
|
// We got warnings from the remote server
|
|
|
|
while (Myc.Fetch(g, -1) == RC_OK) {
|
|
|
|
sprintf(g->Message, "%s: (%s) %s", Tabname,
|
|
|
|
Myc.GetCharField(1), Myc.GetCharField(2));
|
|
|
|
PushWarning(g, this);
|
|
|
|
} // endwhile Fetch
|
|
|
|
|
|
|
|
Myc.FreeResult();
|
|
|
|
} // endif w
|
|
|
|
|
|
|
|
return RC_EF; // Nothing else to do
|
|
|
|
} else
|
|
|
|
return RC_FX; // Error
|
|
|
|
|
|
|
|
} // end of SendCommand
|
|
|
|
|
2014-04-19 17:02:53 +02:00
|
|
|
/***********************************************************************/
|
|
|
|
/* Data Base indexed read routine for MYSQL access method. */
|
|
|
|
/***********************************************************************/
|
|
|
|
bool TDBMYSQL::ReadKey(PGLOBAL g, OPVAL op, const void *key, int len)
|
|
|
|
{
|
|
|
|
int oldlen = strlen(Query);
|
|
|
|
|
|
|
|
if (op == OP_NEXT)
|
|
|
|
return false;
|
|
|
|
else if (op == OP_FIRST) {
|
|
|
|
if (To_CondFil)
|
|
|
|
strcat(strcat(Query, " WHERE "), To_CondFil->Body);
|
|
|
|
|
|
|
|
} else {
|
|
|
|
if (Myc.m_Res)
|
|
|
|
Myc.FreeResult();
|
|
|
|
|
2014-04-21 12:57:10 +02:00
|
|
|
To_Def->GetHandler()->MakeKeyWhere(g, Query, op, "`", key, len);
|
2014-04-19 17:02:53 +02:00
|
|
|
|
|
|
|
if (To_CondFil)
|
|
|
|
strcat(strcat(strcat(Query, " AND ("), To_CondFil->Body), ")");
|
|
|
|
|
|
|
|
} // endif's op
|
|
|
|
|
|
|
|
m_Rc = Myc.ExecSQL(g, Query);
|
|
|
|
Query[oldlen] = 0;
|
|
|
|
return false;
|
|
|
|
} // end of ReadKey
|
|
|
|
|
2013-02-07 10:34:27 +01:00
|
|
|
/***********************************************************************/
|
|
|
|
/* Data Base read routine for MYSQL access method. */
|
|
|
|
/***********************************************************************/
|
|
|
|
int TDBMYSQL::ReadDB(PGLOBAL g)
|
|
|
|
{
|
|
|
|
int rc;
|
|
|
|
|
|
|
|
if (trace > 1)
|
|
|
|
htrc("MySQL ReadDB: R%d Mode=%d key=%p link=%p Kindex=%p\n",
|
|
|
|
GetTdb_No(), Mode, To_Key_Col, To_Link, To_Kindex);
|
|
|
|
|
2013-11-11 18:30:36 +01:00
|
|
|
if (Mode == MODE_UPDATE || Mode == MODE_DELETE)
|
|
|
|
return SendCommand(g);
|
|
|
|
|
2013-02-07 10:34:27 +01:00
|
|
|
/*********************************************************************/
|
|
|
|
/* Now start the reading process. */
|
|
|
|
/* Here is the place to fetch the line. */
|
|
|
|
/*********************************************************************/
|
|
|
|
N++;
|
|
|
|
Fetched = ((rc = Myc.Fetch(g, -1)) == RC_OK);
|
|
|
|
|
|
|
|
if (trace > 1)
|
|
|
|
htrc(" Read: rc=%d\n", rc);
|
|
|
|
|
|
|
|
return rc;
|
|
|
|
} // end of ReadDB
|
|
|
|
|
|
|
|
/***********************************************************************/
|
|
|
|
/* WriteDB: Data Base write routine for MYSQL access methods. */
|
|
|
|
/***********************************************************************/
|
|
|
|
int TDBMYSQL::WriteDB(PGLOBAL g)
|
|
|
|
{
|
2013-02-20 16:57:38 +01:00
|
|
|
#if defined(MYSQL_PREPARED_STATEMENTS)
|
2013-02-07 10:34:27 +01:00
|
|
|
if (Prep)
|
|
|
|
return Myc.ExecStmt(g);
|
2013-02-20 16:57:38 +01:00
|
|
|
#endif // MYSQL_PREPARED_STATEMENTS
|
2013-02-07 10:34:27 +01:00
|
|
|
|
|
|
|
// Statement was not prepared, we must construct and execute
|
|
|
|
// an insert query for each line to insert
|
|
|
|
int rc;
|
2014-03-18 19:25:50 +01:00
|
|
|
char buf[64];
|
2013-02-07 10:34:27 +01:00
|
|
|
|
|
|
|
strcpy(Qbuf, Query);
|
|
|
|
|
|
|
|
// Make the Insert command value list
|
|
|
|
for (PCOL colp = Columns; colp; colp = colp->GetNext()) {
|
2013-02-25 22:44:42 +01:00
|
|
|
if (!colp->GetValue()->IsNull()) {
|
|
|
|
if (colp->GetResultType() == TYPE_STRING ||
|
|
|
|
colp->GetResultType() == TYPE_DATE)
|
|
|
|
strcat(Qbuf, "'");
|
2013-02-07 10:34:27 +01:00
|
|
|
|
2013-02-25 22:44:42 +01:00
|
|
|
strcat(Qbuf, colp->GetValue()->GetCharString(buf));
|
2013-02-07 10:34:27 +01:00
|
|
|
|
2013-02-25 22:44:42 +01:00
|
|
|
if (colp->GetResultType() == TYPE_STRING ||
|
|
|
|
colp->GetResultType() == TYPE_DATE)
|
|
|
|
strcat(Qbuf, "'");
|
|
|
|
|
|
|
|
} else
|
|
|
|
strcat(Qbuf, "NULL");
|
2013-02-07 10:34:27 +01:00
|
|
|
|
|
|
|
strcat(Qbuf, (colp->GetNext()) ? "," : ")");
|
|
|
|
} // endfor colp
|
|
|
|
|
|
|
|
Myc.m_Rows = -1; // To execute the query
|
|
|
|
rc = Myc.ExecSQL(g, Qbuf);
|
|
|
|
return (rc == RC_NF) ? RC_OK : rc; // RC_NF is Ok
|
|
|
|
} // end of WriteDB
|
|
|
|
|
|
|
|
/***********************************************************************/
|
2013-11-11 18:30:36 +01:00
|
|
|
/* Data Base delete all routine for MYSQL access methods. */
|
2013-02-07 10:34:27 +01:00
|
|
|
/***********************************************************************/
|
|
|
|
int TDBMYSQL::DeleteDB(PGLOBAL g, int irc)
|
|
|
|
{
|
2013-11-11 18:30:36 +01:00
|
|
|
if (irc == RC_FX)
|
|
|
|
// Send the DELETE (all) command to the remote table
|
|
|
|
return (SendCommand(g) == RC_FX) ? RC_FX : RC_OK;
|
|
|
|
else
|
|
|
|
return RC_OK; // Ignore
|
|
|
|
|
2013-02-07 10:34:27 +01:00
|
|
|
} // end of DeleteDB
|
|
|
|
|
|
|
|
/***********************************************************************/
|
|
|
|
/* Data Base close routine for MySQL access method. */
|
|
|
|
/***********************************************************************/
|
|
|
|
void TDBMYSQL::CloseDB(PGLOBAL g)
|
|
|
|
{
|
2013-10-11 13:57:56 +02:00
|
|
|
if (Myc.Connected()) {
|
|
|
|
if (Mode == MODE_INSERT) {
|
|
|
|
char cmd[64];
|
|
|
|
int w;
|
|
|
|
PDBUSER dup = PlgGetUser(g);
|
|
|
|
|
|
|
|
dup->Step = "Enabling indexes";
|
|
|
|
sprintf(cmd, "ALTER TABLE `%s` ENABLE KEYS", Tabname);
|
|
|
|
Myc.m_Rows = -1; // To execute the query
|
|
|
|
m_Rc = Myc.ExecSQL(g, cmd, &w);
|
|
|
|
} // endif m_Rc
|
|
|
|
|
|
|
|
Myc.Close();
|
|
|
|
} // endif Myc
|
2013-02-07 10:34:27 +01:00
|
|
|
|
|
|
|
if (trace)
|
|
|
|
htrc("MySQL CloseDB: closing %s rc=%d\n", Name, m_Rc);
|
|
|
|
|
|
|
|
} // end of CloseDB
|
|
|
|
|
|
|
|
// ------------------------ MYSQLCOL functions --------------------------
|
|
|
|
|
|
|
|
/***********************************************************************/
|
|
|
|
/* MYSQLCOL public constructor. */
|
|
|
|
/***********************************************************************/
|
|
|
|
MYSQLCOL::MYSQLCOL(PCOLDEF cdp, PTDB tdbp, PCOL cprec, int i, PSZ am)
|
|
|
|
: COLBLK(cdp, tdbp, i)
|
|
|
|
{
|
|
|
|
if (cprec) {
|
|
|
|
Next = cprec->GetNext();
|
|
|
|
cprec->SetNext(this);
|
|
|
|
} else {
|
|
|
|
Next = tdbp->GetColumns();
|
|
|
|
tdbp->SetColumns(this);
|
|
|
|
} // endif cprec
|
|
|
|
|
2013-05-19 19:25:06 +02:00
|
|
|
// Set additional MySQL access method information for column.
|
2013-12-28 15:46:49 +01:00
|
|
|
Precision = Long = cdp->GetLong();
|
2013-02-07 10:34:27 +01:00
|
|
|
Bind = NULL;
|
|
|
|
To_Val = NULL;
|
|
|
|
Slen = 0;
|
|
|
|
Rank = -1; // Not known yet
|
|
|
|
|
|
|
|
if (trace)
|
|
|
|
htrc(" making new %sCOL C%d %s at %p\n", am, Index, Name, this);
|
|
|
|
|
|
|
|
} // end of MYSQLCOL constructor
|
|
|
|
|
2013-05-19 19:25:06 +02:00
|
|
|
/***********************************************************************/
|
|
|
|
/* MYSQLCOL public constructor. */
|
|
|
|
/***********************************************************************/
|
|
|
|
MYSQLCOL::MYSQLCOL(MYSQL_FIELD *fld, PTDB tdbp, int i, PSZ am)
|
|
|
|
: COLBLK(NULL, tdbp, i)
|
|
|
|
{
|
2014-03-30 22:52:54 +02:00
|
|
|
const char *chset = get_charset_name(fld->charsetnr);
|
|
|
|
char v = (!strcmp(chset, "binary")) ? 'B' : 0;
|
|
|
|
|
2013-05-19 19:25:06 +02:00
|
|
|
Name = fld->name;
|
2013-12-28 15:46:49 +01:00
|
|
|
Precision = Long = fld->length;
|
2014-03-30 22:52:54 +02:00
|
|
|
Buf_Type = MYSQLtoPLG(fld->type, &v);
|
2013-05-19 19:25:06 +02:00
|
|
|
strcpy(Format.Type, GetFormatType(Buf_Type));
|
|
|
|
Format.Length = Long;
|
|
|
|
Format.Prec = fld->decimals;
|
|
|
|
ColUse = U_P;
|
|
|
|
Nullable = !IS_NOT_NULL(fld->flags);
|
|
|
|
|
|
|
|
// Set additional MySQL access method information for column.
|
|
|
|
Bind = NULL;
|
|
|
|
To_Val = NULL;
|
|
|
|
Slen = 0;
|
|
|
|
Rank = i;
|
|
|
|
|
|
|
|
if (trace)
|
|
|
|
htrc(" making new %sCOL C%d %s at %p\n", am, Index, Name, this);
|
|
|
|
|
|
|
|
} // end of MYSQLCOL constructor
|
|
|
|
|
2013-02-07 10:34:27 +01:00
|
|
|
/***********************************************************************/
|
|
|
|
/* MYSQLCOL constructor used for copying columns. */
|
|
|
|
/* tdbp is the pointer to the new table descriptor. */
|
|
|
|
/***********************************************************************/
|
|
|
|
MYSQLCOL::MYSQLCOL(MYSQLCOL *col1, PTDB tdbp) : COLBLK(col1, tdbp)
|
|
|
|
{
|
|
|
|
Long = col1->Long;
|
|
|
|
Bind = NULL;
|
|
|
|
To_Val = NULL;
|
|
|
|
Slen = col1->Slen;
|
|
|
|
Rank = col1->Rank;
|
|
|
|
} // end of MYSQLCOL copy constructor
|
|
|
|
|
2013-05-19 19:25:06 +02:00
|
|
|
/***********************************************************************/
|
|
|
|
/* FindRank: Find the rank of this column in the result set. */
|
|
|
|
/***********************************************************************/
|
|
|
|
bool MYSQLCOL::FindRank(PGLOBAL g)
|
|
|
|
{
|
|
|
|
int n;
|
|
|
|
MYSQLC myc = ((PTDBMY)To_Tdb)->Myc;
|
|
|
|
|
|
|
|
for (n = 0; n < myc.m_Fields; n++)
|
|
|
|
if (!stricmp(Name, myc.m_Res->fields[n].name)) {
|
|
|
|
Rank = n;
|
|
|
|
return false;
|
|
|
|
} // endif Name
|
|
|
|
|
|
|
|
sprintf(g->Message, "Column %s not in result set", Name);
|
|
|
|
return true;
|
|
|
|
} // end of FindRank
|
|
|
|
|
2013-02-07 10:34:27 +01:00
|
|
|
/***********************************************************************/
|
|
|
|
/* SetBuffer: prepare a column block for write operation. */
|
|
|
|
/***********************************************************************/
|
|
|
|
bool MYSQLCOL::SetBuffer(PGLOBAL g, PVAL value, bool ok, bool check)
|
|
|
|
{
|
|
|
|
if (!(To_Val = value)) {
|
|
|
|
sprintf(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;
|
|
|
|
|
2013-12-28 15:46:49 +01:00
|
|
|
} else if (Buf_Type == TYPE_DOUBLE)
|
2013-02-07 10:34:27 +01:00
|
|
|
// Float values must be written with the correct (column) precision
|
|
|
|
// Note: maybe this should be forced by ShowValue instead of this ?
|
2013-12-28 15:46:49 +01:00
|
|
|
value->SetPrec(GetScale());
|
2013-02-07 10:34:27 +01:00
|
|
|
|
|
|
|
Value = value; // Directly access the external value
|
|
|
|
} else {
|
|
|
|
// Values are not of the (good) column type
|
|
|
|
if (check) {
|
|
|
|
sprintf(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
|
|
|
|
|
|
|
|
/***********************************************************************/
|
|
|
|
/* InitBind: Initialize the bind structure according to type. */
|
|
|
|
/***********************************************************************/
|
|
|
|
void MYSQLCOL::InitBind(PGLOBAL g)
|
|
|
|
{
|
|
|
|
PTDBMY tdbp = (PTDBMY)To_Tdb;
|
|
|
|
|
|
|
|
assert(tdbp->Bind && Rank < tdbp->Nparm);
|
|
|
|
|
|
|
|
Bind = &tdbp->Bind[Rank];
|
|
|
|
memset(Bind, 0, sizeof(MYSQL_BIND));
|
|
|
|
|
|
|
|
if (Buf_Type == TYPE_DATE) {
|
2013-02-09 01:08:15 +01:00
|
|
|
Bind->buffer_type = PLGtoMYSQL(TYPE_STRING, false);
|
2013-02-07 10:34:27 +01:00
|
|
|
Bind->buffer = (char *)PlugSubAlloc(g,NULL, 20);
|
|
|
|
Bind->buffer_length = 20;
|
|
|
|
Bind->length = &Slen;
|
|
|
|
} else {
|
2013-02-09 01:08:15 +01:00
|
|
|
Bind->buffer_type = PLGtoMYSQL(Buf_Type, false);
|
2013-02-07 10:34:27 +01:00
|
|
|
Bind->buffer = (char *)Value->GetTo_Val();
|
|
|
|
Bind->buffer_length = Value->GetClen();
|
|
|
|
Bind->length = (IsTypeChar(Buf_Type)) ? &Slen : NULL;
|
|
|
|
} // endif Buf_Type
|
|
|
|
|
|
|
|
} // end of InitBind
|
|
|
|
|
|
|
|
/***********************************************************************/
|
|
|
|
/* ReadColumn: */
|
|
|
|
/***********************************************************************/
|
|
|
|
void MYSQLCOL::ReadColumn(PGLOBAL g)
|
|
|
|
{
|
2013-11-22 16:03:54 +01:00
|
|
|
char *p, *buf, tim[20];
|
2013-02-07 10:34:27 +01:00
|
|
|
int rc;
|
|
|
|
PTDBMY tdbp = (PTDBMY)To_Tdb;
|
|
|
|
|
|
|
|
/*********************************************************************/
|
|
|
|
/* If physical fetching of the line was deferred, do it now. */
|
|
|
|
/*********************************************************************/
|
|
|
|
if (!tdbp->Fetched)
|
|
|
|
if ((rc = tdbp->Myc.Fetch(g, tdbp->N)) != RC_OK) {
|
|
|
|
if (rc == RC_EF)
|
|
|
|
sprintf(g->Message, MSG(INV_DEF_READ), rc);
|
|
|
|
|
|
|
|
longjmp(g->jumper[g->jump_level], 11);
|
|
|
|
} else
|
|
|
|
tdbp->Fetched = TRUE;
|
|
|
|
|
2013-05-19 19:25:06 +02:00
|
|
|
if ((buf = ((PTDBMY)To_Tdb)->Myc.GetCharField(Rank))) {
|
2014-02-03 16:14:13 +01:00
|
|
|
if (trace > 1)
|
2013-05-19 19:25:06 +02:00
|
|
|
htrc("MySQL ReadColumn: name=%s buf=%s\n", Name, buf);
|
|
|
|
|
2013-11-22 16:03:54 +01:00
|
|
|
// TODO: have a true way to differenciate temporal values
|
2013-12-03 22:59:40 +01:00
|
|
|
if (Buf_Type == TYPE_DATE && strlen(buf) == 8)
|
2013-11-22 16:03:54 +01:00
|
|
|
// This is a TIME value
|
|
|
|
p = strcat(strcpy(tim, "1970-01-01 "), buf);
|
|
|
|
else
|
|
|
|
p = buf;
|
|
|
|
|
2013-12-03 22:59:40 +01:00
|
|
|
if (Value->SetValue_char(p, strlen(p))) {
|
|
|
|
sprintf(g->Message, "Out of range value for column %s at row %d",
|
|
|
|
Name, tdbp->RowNumber(g));
|
|
|
|
PushWarning(g, tdbp);
|
|
|
|
} // endif SetValue_char
|
|
|
|
|
2013-05-19 19:25:06 +02:00
|
|
|
} else {
|
2013-02-24 01:23:18 +01:00
|
|
|
if (Nullable)
|
|
|
|
Value->SetNull(true);
|
|
|
|
|
2013-02-07 10:34:27 +01:00
|
|
|
Value->Reset(); // Null value
|
2013-05-19 19:25:06 +02:00
|
|
|
} // endif buf
|
2013-02-07 10:34:27 +01:00
|
|
|
|
|
|
|
} // end of ReadColumn
|
|
|
|
|
|
|
|
/***********************************************************************/
|
|
|
|
/* WriteColumn: make sure the bind buffer is updated. */
|
|
|
|
/***********************************************************************/
|
|
|
|
void MYSQLCOL::WriteColumn(PGLOBAL g)
|
|
|
|
{
|
|
|
|
/*********************************************************************/
|
|
|
|
/* Do convert the column value if necessary. */
|
|
|
|
/*********************************************************************/
|
|
|
|
if (Value != To_Val)
|
|
|
|
Value->SetValue_pval(To_Val, FALSE); // Convert the inserted value
|
|
|
|
|
2013-02-20 16:57:38 +01:00
|
|
|
#if defined(MYSQL_PREPARED_STATEMENTS)
|
2013-02-07 10:34:27 +01:00
|
|
|
if (((PTDBMY)To_Tdb)->Prep) {
|
|
|
|
if (Buf_Type == TYPE_DATE) {
|
2013-06-26 19:42:28 +02:00
|
|
|
Value->ShowValue((char *)Bind->buffer, (int)Bind->buffer_length);
|
2013-02-07 10:34:27 +01:00
|
|
|
Slen = strlen((char *)Bind->buffer);
|
|
|
|
} else if (IsTypeChar(Buf_Type))
|
|
|
|
Slen = strlen(Value->GetCharValue());
|
|
|
|
|
|
|
|
} // endif Prep
|
2013-02-20 16:57:38 +01:00
|
|
|
#endif // MYSQL_PREPARED_STATEMENTS
|
2013-02-07 10:34:27 +01:00
|
|
|
|
|
|
|
} // end of WriteColumn
|
2013-02-11 00:31:03 +01:00
|
|
|
|
2013-10-26 17:14:58 +02:00
|
|
|
/* ------------------------------------------------------------------- */
|
|
|
|
|
|
|
|
/***********************************************************************/
|
2013-11-06 18:22:09 +01:00
|
|
|
/* Implementation of the TDBMYEXC class. */
|
2013-10-26 17:14:58 +02:00
|
|
|
/***********************************************************************/
|
2013-11-06 18:22:09 +01:00
|
|
|
TDBMYEXC::TDBMYEXC(PMYDEF tdp) : TDBMYSQL(tdp)
|
|
|
|
{
|
|
|
|
Cmdlist = NULL;
|
|
|
|
Cmdcol = NULL;
|
|
|
|
Shw = false;
|
|
|
|
Havew = false;
|
|
|
|
Isw = false;
|
|
|
|
Warnings = 0;
|
|
|
|
Mxr = tdp->Mxr;
|
|
|
|
Nerr = 0;
|
|
|
|
} // end of TDBMYEXC constructor
|
|
|
|
|
|
|
|
TDBMYEXC::TDBMYEXC(PGLOBAL g, PTDBMYX tdbp) : TDBMYSQL(g, tdbp)
|
|
|
|
{
|
|
|
|
Cmdlist = tdbp->Cmdlist;
|
|
|
|
Cmdcol = tdbp->Cmdcol;
|
|
|
|
Shw = tdbp->Shw;
|
|
|
|
Havew = tdbp->Havew;
|
|
|
|
Isw = tdbp->Isw;
|
|
|
|
Mxr = tdbp->Mxr;
|
|
|
|
Nerr = tdbp->Nerr;
|
|
|
|
} // end of TDBMYEXC copy constructor
|
2013-10-26 17:14:58 +02:00
|
|
|
|
|
|
|
// Is this really useful ???
|
|
|
|
PTDB TDBMYEXC::CopyOne(PTABS t)
|
|
|
|
{
|
|
|
|
PTDB tp;
|
|
|
|
PCOL cp1, cp2;
|
|
|
|
PGLOBAL g = t->G;
|
|
|
|
|
|
|
|
tp = new(g) TDBMYEXC(g, this);
|
|
|
|
|
|
|
|
for (cp1 = Columns; cp1; cp1 = cp1->GetNext()) {
|
|
|
|
cp2 = new(g) MYXCOL((PMYXCOL)cp1, tp);
|
|
|
|
|
|
|
|
NewPointer(t, cp1, cp2);
|
|
|
|
} // endfor cp1
|
|
|
|
|
|
|
|
return tp;
|
|
|
|
} // end of CopyOne
|
|
|
|
|
|
|
|
/***********************************************************************/
|
|
|
|
/* Allocate MYSQL column description block. */
|
|
|
|
/***********************************************************************/
|
|
|
|
PCOL TDBMYEXC::MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n)
|
|
|
|
{
|
|
|
|
PMYXCOL colp = new(g) MYXCOL(cdp, this, cprec, n);
|
|
|
|
|
|
|
|
if (!colp->Flag)
|
|
|
|
Cmdcol = colp->GetName();
|
|
|
|
|
|
|
|
return colp;
|
|
|
|
} // end of MakeCol
|
|
|
|
|
|
|
|
/***********************************************************************/
|
|
|
|
/* MakeCMD: make the SQL statement to send to MYSQL connection. */
|
|
|
|
/***********************************************************************/
|
2013-11-06 18:22:09 +01:00
|
|
|
PCMD TDBMYEXC::MakeCMD(PGLOBAL g)
|
2013-10-26 17:14:58 +02:00
|
|
|
{
|
2013-11-06 18:22:09 +01:00
|
|
|
PCMD xcmd = NULL;
|
2013-10-26 17:14:58 +02:00
|
|
|
|
2014-03-10 18:29:04 +01:00
|
|
|
if (To_CondFil) {
|
2013-10-26 17:14:58 +02:00
|
|
|
if (Cmdcol) {
|
2014-03-10 18:29:04 +01:00
|
|
|
if (!stricmp(Cmdcol, To_CondFil->Body) &&
|
|
|
|
(To_CondFil->Op == OP_EQ || To_CondFil->Op == OP_IN)) {
|
|
|
|
xcmd = To_CondFil->Cmds;
|
2013-10-26 17:14:58 +02:00
|
|
|
} else
|
|
|
|
strcpy(g->Message, "Invalid command specification filter");
|
|
|
|
|
|
|
|
} else
|
|
|
|
strcpy(g->Message, "No command column in select list");
|
|
|
|
|
|
|
|
} else if (!Srcdef)
|
|
|
|
strcpy(g->Message, "No Srcdef default command");
|
|
|
|
else
|
2013-11-06 18:22:09 +01:00
|
|
|
xcmd = new(g) CMD(g, Srcdef);
|
2013-10-26 17:14:58 +02:00
|
|
|
|
|
|
|
return xcmd;
|
|
|
|
} // end of MakeCMD
|
|
|
|
|
|
|
|
/***********************************************************************/
|
|
|
|
/* EXC GetMaxSize: returns the maximum number of rows in the table. */
|
|
|
|
/***********************************************************************/
|
|
|
|
int TDBMYEXC::GetMaxSize(PGLOBAL g)
|
|
|
|
{
|
|
|
|
if (MaxSize < 0) {
|
2013-11-06 18:22:09 +01:00
|
|
|
MaxSize = 10; // a guess
|
2013-10-26 17:14:58 +02:00
|
|
|
} // endif MaxSize
|
|
|
|
|
|
|
|
return MaxSize;
|
|
|
|
} // end of GetMaxSize
|
|
|
|
|
|
|
|
/***********************************************************************/
|
|
|
|
/* MySQL Exec Access Method opening routine. */
|
|
|
|
/***********************************************************************/
|
|
|
|
bool TDBMYEXC::OpenDB(PGLOBAL g)
|
|
|
|
{
|
|
|
|
if (Use == USE_OPEN) {
|
|
|
|
strcpy(g->Message, "Multiple execution is not allowed");
|
|
|
|
return true;
|
|
|
|
} // endif use
|
|
|
|
|
|
|
|
/*********************************************************************/
|
|
|
|
/* Open a MySQL connection for this table. */
|
|
|
|
/* Note: this may not be the proper way to do. Perhaps it is better */
|
|
|
|
/* to test whether a connection is already open for this server */
|
|
|
|
/* and if so to allocate just a new result set. But this only for */
|
|
|
|
/* servers allowing concurency in getting results ??? */
|
|
|
|
/*********************************************************************/
|
|
|
|
if (!Myc.Connected())
|
|
|
|
if (Myc.Open(g, Host, Database, User, Pwd, Port))
|
|
|
|
return true;
|
|
|
|
|
|
|
|
Use = USE_OPEN; // Do it now in case we are recursively called
|
|
|
|
|
2014-04-19 17:02:53 +02:00
|
|
|
if (Mode != MODE_READ && Mode != MODE_READX) {
|
2013-10-26 17:14:58 +02:00
|
|
|
strcpy(g->Message, "No INSERT/DELETE/UPDATE of MYSQL EXEC tables");
|
|
|
|
return true;
|
|
|
|
} // endif Mode
|
|
|
|
|
|
|
|
/*********************************************************************/
|
|
|
|
/* Get the command to execute. */
|
|
|
|
/*********************************************************************/
|
2013-11-06 18:22:09 +01:00
|
|
|
if (!(Cmdlist = MakeCMD(g))) {
|
2013-10-26 17:14:58 +02:00
|
|
|
Myc.Close();
|
|
|
|
return true;
|
|
|
|
} // endif Query
|
|
|
|
|
|
|
|
return false;
|
|
|
|
} // end of OpenDB
|
|
|
|
|
|
|
|
/***********************************************************************/
|
|
|
|
/* Data Base read routine for MYSQL access method. */
|
|
|
|
/***********************************************************************/
|
|
|
|
int TDBMYEXC::ReadDB(PGLOBAL g)
|
|
|
|
{
|
2013-11-06 18:22:09 +01:00
|
|
|
if (Havew) {
|
|
|
|
// Process result set from SHOW WARNINGS
|
|
|
|
if (Myc.Fetch(g, -1) != RC_OK) {
|
|
|
|
Myc.FreeResult();
|
|
|
|
Havew = Isw = false;
|
|
|
|
} else {
|
|
|
|
N++;
|
|
|
|
Isw = true;
|
|
|
|
return RC_OK;
|
|
|
|
} // endif Fetch
|
|
|
|
|
|
|
|
} // endif m_Res
|
|
|
|
|
|
|
|
if (Cmdlist) {
|
|
|
|
// Process query to send
|
|
|
|
int rc;
|
|
|
|
|
|
|
|
do {
|
|
|
|
Query = Cmdlist->Cmd;
|
|
|
|
|
|
|
|
switch (rc = Myc.ExecSQLcmd(g, Query, &Warnings)) {
|
|
|
|
case RC_NF:
|
|
|
|
AftRows = Myc.m_Afrw;
|
|
|
|
strcpy(g->Message, "Affected rows");
|
|
|
|
break;
|
|
|
|
case RC_OK:
|
|
|
|
AftRows = Myc.m_Fields;
|
|
|
|
strcpy(g->Message, "Result set columns");
|
|
|
|
break;
|
|
|
|
case RC_FX:
|
|
|
|
AftRows = Myc.m_Afrw;
|
|
|
|
Nerr++;
|
|
|
|
break;
|
|
|
|
case RC_INFO:
|
|
|
|
Shw = true;
|
|
|
|
} // endswitch rc
|
|
|
|
|
|
|
|
Cmdlist = (Nerr > Mxr) ? NULL : Cmdlist->Next;
|
|
|
|
} while (rc == RC_INFO);
|
|
|
|
|
|
|
|
if (Shw && Warnings)
|
|
|
|
Havew = (Myc.ExecSQL(g, "SHOW WARNINGS") == RC_OK);
|
|
|
|
|
|
|
|
++N;
|
|
|
|
return RC_OK;
|
|
|
|
} else
|
|
|
|
return RC_EF;
|
|
|
|
|
2013-10-26 17:14:58 +02:00
|
|
|
} // end of ReadDB
|
|
|
|
|
|
|
|
/***********************************************************************/
|
|
|
|
/* WriteDB: Data Base write routine for Exec MYSQL access methods. */
|
|
|
|
/***********************************************************************/
|
|
|
|
int TDBMYEXC::WriteDB(PGLOBAL g)
|
|
|
|
{
|
|
|
|
strcpy(g->Message, "EXEC MYSQL tables are read only");
|
|
|
|
return RC_FX;
|
|
|
|
} // end of WriteDB
|
|
|
|
|
|
|
|
// ------------------------- MYXCOL functions ---------------------------
|
|
|
|
|
|
|
|
/***********************************************************************/
|
|
|
|
/* MYXCOL public constructor. */
|
|
|
|
/***********************************************************************/
|
|
|
|
MYXCOL::MYXCOL(PCOLDEF cdp, PTDB tdbp, PCOL cprec, int i, PSZ am)
|
|
|
|
: MYSQLCOL(cdp, tdbp, cprec, i, am)
|
|
|
|
{
|
|
|
|
// Set additional EXEC MYSQL access method information for column.
|
|
|
|
Flag = cdp->GetOffset();
|
|
|
|
} // end of MYSQLCOL constructor
|
|
|
|
|
|
|
|
/***********************************************************************/
|
|
|
|
/* MYSQLCOL public constructor. */
|
|
|
|
/***********************************************************************/
|
|
|
|
MYXCOL::MYXCOL(MYSQL_FIELD *fld, PTDB tdbp, int i, PSZ am)
|
|
|
|
: MYSQLCOL(fld, tdbp, i, am)
|
|
|
|
{
|
|
|
|
if (trace)
|
|
|
|
htrc(" making new %sCOL C%d %s at %p\n", am, Index, Name, this);
|
|
|
|
|
|
|
|
} // end of MYSQLCOL constructor
|
|
|
|
|
|
|
|
/***********************************************************************/
|
|
|
|
/* MYXCOL constructor used for copying columns. */
|
|
|
|
/* tdbp is the pointer to the new table descriptor. */
|
|
|
|
/***********************************************************************/
|
|
|
|
MYXCOL::MYXCOL(MYXCOL *col1, PTDB tdbp) : MYSQLCOL(col1, tdbp)
|
|
|
|
{
|
|
|
|
Flag = col1->Flag;
|
|
|
|
} // end of MYXCOL copy constructor
|
|
|
|
|
|
|
|
/***********************************************************************/
|
|
|
|
/* ReadColumn: */
|
|
|
|
/***********************************************************************/
|
|
|
|
void MYXCOL::ReadColumn(PGLOBAL g)
|
|
|
|
{
|
|
|
|
PTDBMYX tdbp = (PTDBMYX)To_Tdb;
|
|
|
|
|
2013-11-06 18:22:09 +01:00
|
|
|
if (tdbp->Isw) {
|
|
|
|
char *buf = NULL;
|
|
|
|
|
|
|
|
if (Flag < 3) {
|
|
|
|
buf = tdbp->Myc.GetCharField(Flag);
|
|
|
|
Value->SetValue_psz(buf);
|
|
|
|
} else
|
|
|
|
Value->Reset();
|
|
|
|
|
|
|
|
} else
|
|
|
|
switch (Flag) {
|
|
|
|
case 0: Value->SetValue_psz(tdbp->Query); break;
|
|
|
|
case 1: Value->SetValue(tdbp->AftRows); break;
|
|
|
|
case 2: Value->SetValue_psz(g->Message); break;
|
|
|
|
case 3: Value->SetValue(tdbp->Warnings); break;
|
|
|
|
default: Value->SetValue_psz("Invalid Flag"); break;
|
|
|
|
} // endswitch Flag
|
2013-10-26 17:14:58 +02:00
|
|
|
|
|
|
|
} // end of ReadColumn
|
|
|
|
|
|
|
|
/***********************************************************************/
|
|
|
|
/* WriteColumn: should never be called. */
|
|
|
|
/***********************************************************************/
|
|
|
|
void MYXCOL::WriteColumn(PGLOBAL g)
|
|
|
|
{
|
|
|
|
assert(false);
|
|
|
|
} // end of WriteColumn
|
|
|
|
|
2013-02-11 00:31:03 +01:00
|
|
|
/* ---------------------------TDBMCL class --------------------------- */
|
|
|
|
|
|
|
|
/***********************************************************************/
|
|
|
|
/* TDBMCL class constructor. */
|
|
|
|
/***********************************************************************/
|
|
|
|
TDBMCL::TDBMCL(PMYDEF tdp) : TDBCAT(tdp)
|
|
|
|
{
|
|
|
|
Host = tdp->Hostname;
|
|
|
|
Db = tdp->Database;
|
|
|
|
Tab = tdp->Tabname;
|
|
|
|
User = tdp->Username;
|
|
|
|
Pwd = tdp->Password;
|
|
|
|
Port = tdp->Portnumber;
|
|
|
|
} // end of TDBMCL constructor
|
|
|
|
|
|
|
|
/***********************************************************************/
|
|
|
|
/* GetResult: Get the list the MYSQL table columns. */
|
|
|
|
/***********************************************************************/
|
|
|
|
PQRYRES TDBMCL::GetResult(PGLOBAL g)
|
|
|
|
{
|
2014-03-30 22:52:54 +02:00
|
|
|
return MyColumns(g, NULL, Host, Db, User, Pwd, Tab, NULL, Port, false);
|
2013-02-11 00:31:03 +01:00
|
|
|
} // end of GetResult
|