diff options
Diffstat (limited to 'storage/connect/tabmysql.cpp')
-rw-r--r-- | storage/connect/tabmysql.cpp | 1755 |
1 files changed, 1755 insertions, 0 deletions
diff --git a/storage/connect/tabmysql.cpp b/storage/connect/tabmysql.cpp new file mode 100644 index 00000000..2c6ffa8b --- /dev/null +++ b/storage/connect/tabmysql.cpp @@ -0,0 +1,1755 @@ +/************* TabMySQL C++ Program Source Code File (.CPP) *************/ +/* PROGRAM NAME: TABMYSQL */ +/* ------------- */ +/* Version 2.0 */ +/* */ +/* AUTHOR: */ +/* ------- */ +/* Olivier BERTRAND 2007-2017 */ +/* */ +/* 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 - TABMYSQL 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 */ +/* */ +/************************************************************************/ +#define MYSQL_SERVER 1 +#include "my_global.h" +#include "sql_class.h" +#include "sql_servers.h" +#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 "tabext.h" +#include "tabcol.h" +#include "colblk.h" +//#include "reldef.h" +#include "tabmysql.h" +#include "valblk.h" +#include "tabutil.h" +#include "ha_connect.h" + +#if defined(_CONSOLE) +void PrintResult(PGLOBAL, PSEM, PQRYRES); +#endif // _CONSOLE + +// Used to check whether a MYSQL table is created on itself +bool CheckSelf(PGLOBAL g, TABLE_SHARE *s, PCSZ host, PCSZ db, + PCSZ tab, PCSZ src, int port); + +/***********************************************************************/ +/* External function. */ +/***********************************************************************/ +bool ExactInfo(void); + +/* -------------- Implementation of the MYSQLDEF class --------------- */ + +/***********************************************************************/ +/* Constructor. */ +/***********************************************************************/ +MYSQLDEF::MYSQLDEF(void) + { + Pseudo = 2; // SERVID is Ok but not ROWID + Hostname = NULL; +//Tabschema = NULL; +//Tabname = NULL; +//Srcdef = NULL; +//Username = NULL; +//Password = NULL; + Portnumber = 0; + Isview = false; + Bind = false; + Delayed = false; + Ignored = false; +//Xsrc = false; + Huge = false; + } // end of MYSQLDEF constructor + +/***********************************************************************/ +/* 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!")); + snprintf(g->Message, sizeof(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 */ + snprintf(g->Message, sizeof(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 %p", + server)); + + // TODO: We need to examine which of these can really be NULL + Hostname = PlugDup(g, server->host); + Tabschema = 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 + +/***********************************************************************/ +/* 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)) */ +/* ENGINE="CONNECT" TABLE_TYPE="MYSQL" */ +/* 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) */ +/* ) ENGINE="CONNECT" TABLE_TYPE="MYSQL" */ +/* CONNECTION="my_conn"; (NIY) */ +/* */ +/* 'password' and 'port' are both optional. */ +/* */ +/* RETURN VALUE */ +/* false success */ +/* true error */ +/* */ +/***********************************************************************/ +bool MYSQLDEF::ParseURL(PGLOBAL g, char *url, bool b) + { + char *tabn, *pwd, *schema; + + if ((!strstr(url, "://") && (!strchr(url, '@')))) { + // No :// or @ in connection string. Must be a straight + // connection name of either "server" or "server/table" + // ok, so we do a little parsing, but not completely! + if ((tabn= strchr(url, '/'))) { + // If there is a single '/' in the connection string, + // this means the user is specifying a table name + *tabn++= '\0'; + + // there better not be any more '/'s ! + if (strchr(tabn, '/')) + return true; + + Tabname = tabn; + } else + // Otherwise, straight server name, + Tabname = (b) ? GetStringCatInfo(g, "Tabname", Name) : NULL; + + if (trace(1)) + htrc("server: %s TableName: %s", url, Tabname); + + Server = url; + return GetServerInfo(g, url); + } else { + // URL, parse it + char *sport, *scheme = url; + + if (!(Username = strstr(url, "://"))) { + snprintf(g->Message, sizeof(g->Message), "Connection is not an URL"); + return true; + } // endif User + + scheme[Username - scheme] = 0; + + if (stricmp(scheme, "mysql")) { + snprintf(g->Message, sizeof(g->Message), "scheme must be mysql"); + return true; + } // endif scheme + + Username += 3; + + if (!(Hostname = (char*)strchr(Username, '@'))) { + snprintf(g->Message, sizeof(g->Message), "No host specified in URL"); + return true; + } else { + *Hostname++ = 0; // End Username + Server = Hostname; + } // endif Hostname + + if ((pwd = (char*)strchr(Username, ':'))) { + *pwd++ = 0; // End username + + // Make sure there isn't an extra / + if (strchr(pwd, '/')) { + snprintf(g->Message, sizeof(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 (pwd[0] == 0) + Password = NULL; + else + Password = pwd; + + } // endif password + + // Make sure there isn't an extra / or @ */ + if ((strchr(Username, '/')) || (strchr(Hostname, '@'))) { + snprintf(g->Message, sizeof(g->Message), "Syntax error in URL"); + return true; + } // endif + + if ((schema = strchr(Hostname, '/'))) { + *schema++ = 0; + + if ((tabn = strchr(schema, '/'))) { + *tabn++ = 0; + + // Make sure there's not an extra / + if ((strchr(tabn, '/'))) { + snprintf(g->Message, sizeof(g->Message), "Syntax error in URL"); + return true; + } // endif / + + Tabname = tabn; + } // endif TableName + + Tabschema = schema; + } // endif database + + if ((sport = strchr(Hostname, ':'))) + *sport++ = 0; + + // 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) + : (b) ? GetIntCatInfo("Port", GetDefaultPort()) : 0; + + if (Username[0] == 0) + Username = (b) ? GetStringCatInfo(g, "User", "*") : NULL; + + if (Hostname[0] == 0) + Hostname = (b) ? GetStringCatInfo(g, "Host", "localhost") : NULL; + + if (!Tabschema || !*Tabschema) + Tabschema = (b) ? GetStringCatInfo(g, "Database", "*") : NULL; + + if (!Tabname || !*Tabname) + Tabname = (b) ? GetStringCatInfo(g, "Tabname", Name) : NULL; + + if (!Password) + Password = (b) ? GetStringCatInfo(g, "Password", NULL) : NULL; + } // 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 + +/***********************************************************************/ +/* DefineAM: define specific AM block values from XCV file. */ +/***********************************************************************/ +bool MYSQLDEF::DefineAM(PGLOBAL g, LPCSTR am, int) + { + char *url; + + Desc = "MySQL Table"; + + Delayed = !!GetIntCatInfo("Delayed", 0); + Ignored = !!GetIntCatInfo("Ignored", 0); + + if (stricmp(am, "MYPRX")) { + // Normal case of specific MYSQL table + url = GetStringCatInfo(g, "Connect", NULL); + + if (!url || !*url) { + // Not using the connection URL + Hostname = GetStringCatInfo(g, "Host", "localhost"); + Tabschema = 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()); + Server = Hostname; + } else if (ParseURL(g, url)) + return true; + + Bind = !!GetIntCatInfo("Bind", 0); + } else { + // MYSQL access from a PROXY table + TABLE_SHARE* s; + + Tabschema = GetStringCatInfo(g, "Database", Tabschema ? Tabschema : PlugDup(g, "*")); + Isview = GetBoolCatInfo("View", false); + + // We must get other connection parms from the calling table + s = Remove_tshp(Cat); + url = GetStringCatInfo(g, "Connect", NULL); + + if (!url || !*url) { + Hostname = GetStringCatInfo(g, "Host", "localhost"); + Username = GetStringCatInfo(g, "User", "*"); + Password = GetStringCatInfo(g, "Password", NULL); + Portnumber = GetIntCatInfo("Port", GetDefaultPort()); + Server = Hostname; + } else { + PCSZ locdb = Tabschema; + + if (ParseURL(g, url)) + return true; + + Tabschema = locdb; + } // endif url + + Tabname = Name; + + // Needed for column description + Restore_tshp(Cat, s); + } // endif am + + if ((Srcdef = GetStringCatInfo(g, "Srcdef", NULL))) { + Read_Only = true; + Isview = true; + } else if (CheckSelf(g, Hc->GetTable()->s, Hostname, Tabschema, + Tabname, Srcdef, Portnumber)) + return true; + + // Used for Update and Delete + Qrystr = GetStringCatInfo(g, "Query_String", "?"); + Quoted = GetIntCatInfo("Quoted", 0); + + // Specific for command executing tables + Xsrc = GetBoolCatInfo("Execsrc", false); + Maxerr = GetIntCatInfo("Maxerr", 0); + Huge = GetBoolCatInfo("Huge", false); + return false; + } // end of DefineAM + +/***********************************************************************/ +/* GetTable: makes a new TDB of the proper type. */ +/***********************************************************************/ +PTDB MYSQLDEF::GetTable(PGLOBAL g, MODE) + { + if (Xsrc) + return new(g) TDBMYEXC(this); + else if (Catfunc == FNC_COL) + return new(g) TDBMCL(this); + else + return new(g) TDBMYSQL(this); + + } // end of GetTable + +/* ------------------------------------------------------------------- */ + +/***********************************************************************/ +/* Implementation of the TDBMYSQL class. */ +/***********************************************************************/ +TDBMYSQL::TDBMYSQL(PMYDEF tdp) : TDBEXT(tdp) + { + if (tdp) { + Host = tdp->Hostname; +// Schema = tdp->Tabschema; +// TableName = tdp->Tabname; +// Srcdef = tdp->Srcdef; +// User = tdp->Username; +// Pwd = tdp->Password; + Server = tdp->Server; +// Qrystr = tdp->Qrystr; + Quoted = MY_MAX(0, tdp->Quoted); + Port = tdp->Portnumber; + Isview = tdp->Isview; + Prep = tdp->Bind; + Delayed = tdp->Delayed; + Ignored = tdp->Ignored; + Myc.m_Use = tdp->Huge; + } else { + Host = NULL; +// Schema = NULL; +// TableName = NULL; +// Srcdef = NULL; +// User = NULL; +// Pwd = NULL; + Server = NULL; +// Qrystr = NULL; +// Quoted = 0; + Port = 0; + Isview = false; + Prep = false; + Delayed = false; + Ignored = false; + } // endif tdp + + Bind = NULL; +//Query = NULL; + Fetched = false; + m_Rc = RC_FX; +//AftRows = 0; + N = -1; +//Nparm = 0; + } // end of TDBMYSQL constructor + +TDBMYSQL::TDBMYSQL(PTDBMY tdbp) : TDBEXT(tdbp) + { + Host = tdbp->Host; +//Schema = tdbp->Schema; +//TableName = tdbp->TableName; +//Srcdef = tdbp->Srcdef; +//User = tdbp->User; +//Pwd = tdbp->Pwd; +//Qrystr = tdbp->Qrystr; +//Quoted = tdbp->Quoted; + Server = tdbp->Server; + Port = tdbp->Port; + Isview = tdbp->Isview; + Prep = tdbp->Prep; + Delayed = tdbp->Delayed; + Ignored = tdbp->Ignored; + Bind = NULL; +//Query = tdbp->Query; + 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 ??? --> Yes for UPDATE +PTDB TDBMYSQL::Clone(PTABS t) + { + PTDB tp; + PCOL cp1, cp2; + PGLOBAL g = t->G; + + tp = new(g) TDBMYSQL(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 Clone + +/***********************************************************************/ +/* 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. */ +/***********************************************************************/ +bool TDBMYSQL::MakeSelect(PGLOBAL g, bool mx) +{ +//char *tk = "`"; + char tk = '`'; + int len = 0, rank = 0; + bool b = false; + PCOL colp; +//PDBUSER dup = PlgGetUser(g); + + if (Query) + return false; // already done + + if (Srcdef) + return MakeSrcdef(g); + + // Allocate the string used to contain Query + Query = new(g) STRING(g, 1023, "SELECT "); + + if (Columns) { + for (colp = Columns; colp; colp = colp->GetNext()) + if (!colp->IsSpecial()) { + if (b) + Query->Append(", "); + else + b = true; + + Query->Append(tk); + Query->Append(colp->GetName()); + Query->Append(tk); + ((PMYCOL)colp)->Rank = rank++; + } // endif colp + + } else { + // ncol == 0 can occur for views or queries such as + // Query count(*) from... for which we will count the rows from + // Query '*' from... + // (the use of a char constant minimize the result storage) + if (Isview) + Query->Append('*'); + else + Query->Append("'*'"); + + } // endif ncol + + Query->Append(" FROM "); + Query->Append(tk); + Query->Append(TableName); + Query->Append(tk); + len = Query->GetLength(); + + if (To_CondFil) { + if (!mx) { + Query->Append(" WHERE "); + Query->Append(To_CondFil->Body); + len = Query->GetLength() + 1; + } else + len += (strlen(To_CondFil->Body) + 256); + + } else + len += (mx ? 256 : 1); + + if (Query->IsTruncated() || Query->Resize(len)) { + snprintf(g->Message, sizeof(g->Message), "MakeSelect: Out of memory"); + return true; + } // endif Query + + if (trace(33)) + htrc("Query=%s\n", Query->GetStr()); + + return false; +} // end of MakeSelect + +/***********************************************************************/ +/* MakeInsert: make the Insert statement used with MySQL connection. */ +/***********************************************************************/ +bool TDBMYSQL::MakeInsert(PGLOBAL g) + { + const char *tk = "`"; + uint len = 0; + bool oom, b = false; + PCOL colp; + + if (Query) + return false; // already done + + if (Prep) { +#if !defined(MYSQL_PREPARED_STATEMENTS) + snprintf(g->Message, sizeof(g->Message), "Prepared statements not used (not supported)"); + PushWarning(g, this); + Prep = false; +#endif // !MYSQL_PREPARED_STATEMENTS + } // endif Prep + + for (colp = Columns; colp; colp = colp->GetNext()) + if (colp->IsSpecial()) { + snprintf(g->Message, sizeof(g->Message), MSG(NO_SPEC_COL)); + return true; + } else { + len += (strlen(colp->GetName()) + 4); + + // Parameter marker + if (!Prep) { + if (colp->GetResultType() == TYPE_DATE) + len += 20; + else + len += colp->GetLength(); + + } else + len += 2; + + ((PMYCOL)colp)->Rank = Nparm++; + } // endif colp + + // Below 40 is enough to contain the fixed part of the query + len += (strlen(TableName) + 40); + Query = new(g) STRING(g, len); + + Query->Set("INSERT "); + if (Delayed) + Query->Append("DELAYED "); + if (Ignored) + Query->Append("IGNORE "); + + Query->Append("INTO "); + Query->Append(tk); + Query->Append(TableName); + Query->Append("` ("); + + for (colp = Columns; colp; colp = colp->GetNext()) { + if (b) + Query->Append(", "); + else + b = true; + + Query->Append(tk); + Query->Append(colp->GetName()); + Query->Append(tk); + } // endfor colp + + Query->Append(") VALUES ("); + +#if defined(MYSQL_PREPARED_STATEMENTS) + if (Prep) { + for (int i = 0; i < Nparm; i++) + Query->Append("?,"); + + Query->RepLast(')'); + Query->Trim(); + } // endif Prep +#endif // MYSQL_PREPARED_STATEMENTS + + if ((oom = Query->IsTruncated())) + snprintf(g->Message, sizeof(g->Message), "MakeInsert: Out of memory"); + + return oom; + } // end of MakeInsert + +/***********************************************************************/ +/* MakeCommand: make the Update or Delete statement to send to the */ +/* MySQL server. Limited to remote values and filtering. */ +/***********************************************************************/ +bool TDBMYSQL::MakeCommand(PGLOBAL g) + { + Query = new(g) STRING(g, strlen(Qrystr) + 64); + + if (Quoted > 0 || stricmp(Name, TableName)) { + 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) + 5); + 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))) { + Query->Set(Qrystr, (uint)(p - qrystr)); + + if (qtd && *(p-1) == ' ') { + Query->Append('`'); + Query->Append(TableName); + Query->Append('`'); + } else + Query->Append(TableName); + + Query->Append(Qrystr + (p - qrystr) + strlen(name)); + + if (Query->IsTruncated()) { + snprintf(g->Message, sizeof(g->Message), "MakeCommand: Out of memory"); + return true; + } else + strlwr(strcpy(qrystr, Query->GetStr())); + + } else { + snprintf(g->Message, sizeof(g->Message), "Cannot use this %s command", + (Mode == MODE_UPDATE) ? "UPDATE" : "DELETE"); + return true; + } // endif p + + } else + (void)Query->Set(Qrystr); + + return false; + } // end of MakeCommand + +#if 0 +/***********************************************************************/ +/* MakeUpdate: make the Update statement use with MySQL connection. */ +/* Limited to remote values and filtering. */ +/***********************************************************************/ +int TDBMYSQL::MakeUpdate(PGLOBAL g) + { + char *qc, cmd[8], tab[96], end[1024]; + + Query = (char*)PlugSubAlloc(g, NULL, strlen(Qrystr) + 64); + memset(end, 0, sizeof(end)); + + if (sscanf(Qrystr, "%s `%[^`]`%1023c", cmd, tab, end) > 2 || + sscanf(Qrystr, "%s \"%[^\"]\"%1023c", cmd, tab, end) > 2) + qc = "`"; + else if (sscanf(Qrystr, "%s %s%1023c", cmd, tab, end) > 2 + && !stricmp(tab, Name)) + qc = (Quoted) ? "`" : ""; + else { + snprintf(g->Message, sizeof(g->Message), "Cannot use this UPDATE command"); + return RC_FX; + } // endif sscanf + + assert(!stricmp(cmd, "update")); + strcat(strcat(strcat(strcpy(Query, "UPDATE "), qc), TableName), qc); + strcat(Query, end); + return RC_OK; + } // end of MakeUpdate + +/***********************************************************************/ +/* MakeDelete: make the Delete statement used with MySQL connection. */ +/* Limited to remote filtering. */ +/***********************************************************************/ +int TDBMYSQL::MakeDelete(PGLOBAL g) + { + char *qc, cmd[8], from[8], tab[96], end[512]; + + Query = (char*)PlugSubAlloc(g, NULL, strlen(Qrystr) + 64); + memset(end, 0, sizeof(end)); + + 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 { + snprintf(g->Message, sizeof(g->Message), "Cannot use this DELETE command"); + return RC_FX; + } // endif sscanf + + assert(!stricmp(cmd, "delete") && !stricmp(from, "from")); + strcat(strcat(strcat(strcpy(Query, "DELETE FROM "), qc), TableName), qc); + + if (*end) + strcat(Query, end); + + return RC_OK; + } // end of MakeDelete +#endif // 0 + +/***********************************************************************/ +/* MYSQL Cardinality: returns the number of rows in the table. */ +/***********************************************************************/ +int TDBMYSQL::Cardinality(PGLOBAL g) +{ + if (!g) + return (Mode == MODE_ANY && !Srcdef) ? 1 : 0; + + if (Cardinal < 0 && Mode == MODE_ANY && !Srcdef && ExactInfo()) { + // Info command, we must return the exact table row number + char query[96]; + MYSQLC myc; + + if (myc.Open(g, Host, Schema, User, Pwd, Port, csname)) + return -1; + + strcpy(query, "SELECT COUNT(*) FROM "); + + if (Quoted > 0) + strcat(strcat(strcat(query, "`"), TableName), "`"); + else + strcat(query, TableName); + + Cardinal = myc.GetTableSize(g, query); + myc.Close(); + } else + Cardinal = 10; // To make MySQL happy + + return Cardinal; +} // end of Cardinality + +#if 0 +/***********************************************************************/ +/* MYSQL GetMaxSize: returns the maximum number of rows in the table. */ +/***********************************************************************/ +int TDBMYSQL::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 +#endif // 0 + +/***********************************************************************/ +/* This a fake routine as ROWID does not exist in MySQL. */ +/***********************************************************************/ +int TDBMYSQL::RowNumber(PGLOBAL, bool) + { + return N + 1; + } // end of RowNumber + +/***********************************************************************/ +/* Return 0 in mode UPDATE to tell that the update is done. */ +/***********************************************************************/ +int TDBMYSQL::GetProgMax(PGLOBAL g) + { + return (Mode == MODE_UPDATE) ? 0 : GetMaxSize(g); + } // end of GetProgMax + +/***********************************************************************/ +/* MySQL Bind Parameter function. */ +/***********************************************************************/ +int TDBMYSQL::BindColumns(PGLOBAL g __attribute__((unused))) + { +#if defined(MYSQL_PREPARED_STATEMENTS) + 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); + } // endif prep +#endif // MYSQL_PREPARED_STATEMENTS + + return RC_OK; + } // 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. */ + /*******************************************************************/ + if (Myc.Rewind(g, (Mode == MODE_READX) ? Query->GetStr() : NULL) != RC_OK) + return true; + + N = -1; + return false; + } // 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, Schema, User, Pwd, Port, csname)) + return true; + + } // endif Connected + + /*********************************************************************/ + /* 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); + + /*********************************************************************/ + /* Allocate whatever is used for getting results. */ + /*********************************************************************/ + if (Mode == MODE_READ || Mode == MODE_READX) { + MakeSelect(g, Mode == MODE_READX); + if (Mode == MODE_READ && !Query) + { + Myc.Close(); + return true; + } + m_Rc = (Mode == MODE_READ) + ? Myc.ExecSQL(g, Query->GetStr()) : RC_OK; + +#if 0 + if (!Myc.m_Res || !Myc.m_Fields) { + snprintf(g->Message, sizeof(g->Message), "%s result", (Myc.m_Res) ? "Void" : "No"); + Myc.Close(); + return true; + } // endif m_Res +#endif // 0 + + if (!m_Rc && Srcdef) + if (SetColumnRanks(g)) + return true; + + } else if (Mode == MODE_INSERT) { + if (Srcdef) { + snprintf(g->Message, sizeof(g->Message), "No insert into anonym views"); + Myc.Close(); + return true; + } // endif Srcdef + + if (!MakeInsert(g)) { +#if defined(MYSQL_PREPARED_STATEMENTS) + int n = (Prep) + ? Myc.PrepareSQL(g, Query->GetCharValue()) : Nparm; + + if (Nparm != n) { + if (n >= 0) // Other errors return negative values + snprintf(g->Message, sizeof(g->Message), MSG(BAD_PARM_COUNT)); + + } else +#endif // MYSQL_PREPARED_STATEMENTS + m_Rc = BindColumns(g); + + } // endif MakeInsert + + } else +// m_Rc = (Mode == MODE_DELETE) ? MakeDelete(g) : MakeUpdate(g); + m_Rc = (MakeCommand(g)) ? RC_FX : RC_OK; + + if (m_Rc == RC_FX) { + Myc.Close(); + return true; + } // endif rc + + Use = USE_OPEN; + return false; + } // end of OpenDB + +/***********************************************************************/ +/* 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) + snprintf(g->Message, sizeof(g->Message), "Column %s is not in view", name); + + return colp; + } // end of MakeFieldColumn + +/***********************************************************************/ +/* 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 + +/***********************************************************************/ +/* Send an UPDATE or DELETE command to the remote server. */ +/***********************************************************************/ +int TDBMYSQL::SendCommand(PGLOBAL g) + { + int w; + + if (Myc.ExecSQLcmd(g, Query->GetStr(), &w) == RC_NF) { + AftRows = Myc.m_Afrw; + snprintf(g->Message, sizeof(g->Message), "%s: %d affected rows", TableName, AftRows); + PushWarning(g, this, 0); // 0 means a Note + + if (trace(1)) + 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) { + snprintf(g->Message, sizeof(g->Message), "%s: (%s) %s", TableName, + 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 + +/***********************************************************************/ +/* Data Base indexed read routine for MYSQL access method. */ +/***********************************************************************/ +bool TDBMYSQL::ReadKey(PGLOBAL g, OPVAL op, const key_range *kr) +{ + int oldlen = Query->GetLength(); + PHC hc = To_Def->GetHandler(); + + if (!(kr || hc->end_range) || op == OP_NEXT || + Mode == MODE_UPDATE || Mode == MODE_DELETE) { + if (!kr && Mode == MODE_READX) { + // This is a false indexed read + m_Rc = Myc.ExecSQL(g, Query->GetStr()); + Mode = MODE_READ; + return (m_Rc == RC_FX) ? true : false; + } // endif key + + return false; + } else { + if (Myc.m_Res) + Myc.FreeResult(); + + if (hc->MakeKeyWhere(g, Query, op, '`', kr)) + return true; + + if (To_CondFil) { + if (To_CondFil->Idx != hc->active_index) { + To_CondFil->Idx = hc->active_index; + To_CondFil->Body= (char*)PlugSubAlloc(g, NULL, 0); + *To_CondFil->Body= 0; + + if ((To_CondFil = hc->CheckCond(g, To_CondFil, Cond))) + PlugSubAlloc(g, NULL, strlen(To_CondFil->Body) + 1); + + } // endif active_index + + if (To_CondFil) + if (Query->Append(" AND ") || Query->Append(To_CondFil->Body)) { + snprintf(g->Message, sizeof(g->Message), "Readkey: Out of memory"); + return true; + } // endif Append + + } // endif To_Condfil + + Mode = MODE_READ; + } // endif's op + + if (trace(33)) + htrc("MYSQL ReadKey: Query=%s\n", Query->GetStr()); + + m_Rc = Myc.ExecSQL(g, Query->GetStr()); + Query->Truncate(oldlen); + return (m_Rc == RC_FX) ? true : false; +} // end of ReadKey + +/***********************************************************************/ +/* Data Base read routine for MYSQL access method. */ +/***********************************************************************/ +int TDBMYSQL::ReadDB(PGLOBAL g) + { + int rc; + + if (trace(2)) + htrc("MySQL ReadDB: R%d Mode=%d\n", GetTdb_No(), Mode); + + if (Mode == MODE_UPDATE || Mode == MODE_DELETE) + return SendCommand(g); + + /*********************************************************************/ + /* Now start the reading process. */ + /* Here is the place to fetch the line. */ + /*********************************************************************/ + N++; + Fetched = ((rc = Myc.Fetch(g, -1)) == RC_OK); + + if (trace(2)) + 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) + { +#if defined(MYSQL_PREPARED_STATEMENTS) + if (Prep) + return Myc.ExecStmt(g); +#endif // MYSQL_PREPARED_STATEMENTS + + // Statement was not prepared, we must construct and execute + // an insert query for each line to insert + int rc; + uint len = Query->GetLength(); + char buf[64]; + + // Make the Insert command value list + for (PCOL colp = Columns; colp; colp = colp->GetNext()) { + if (!colp->GetValue()->IsNull()) { + if (colp->GetResultType() == TYPE_STRING || + colp->GetResultType() == TYPE_DATE) + Query->Append_quoted(colp->GetValue()->GetCharString(buf)); + else + Query->Append(colp->GetValue()->GetCharString(buf)); + + } else + Query->Append("NULL"); + + Query->Append(','); + } // endfor colp + + if (unlikely(Query->IsTruncated())) { + snprintf(g->Message, sizeof(g->Message), "WriteDB: Out of memory"); + rc = RC_FX; + } else { + Query->RepLast(')'); + Myc.m_Rows = -1; // To execute the query + rc = Myc.ExecSQL(g, Query->GetStr()); + Query->Truncate(len); // Restore query + } // endif Query + + return (rc == RC_NF) ? RC_OK : rc; // RC_NF is Ok + } // end of WriteDB + +/***********************************************************************/ +/* Data Base delete all routine for MYSQL access methods. */ +/***********************************************************************/ +int TDBMYSQL::DeleteDB(PGLOBAL g, int irc) + { + 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 + + } // end of DeleteDB + +/***********************************************************************/ +/* Data Base close routine for MySQL access method. */ +/***********************************************************************/ +void TDBMYSQL::CloseDB(PGLOBAL g) + { + if (Myc.Connected()) { + + Myc.Close(); + } // endif Myc + + if (trace(1)) + 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, PCSZ am) + : COLBLK(cdp, tdbp, i) + { + if (cprec) { + Next = cprec->GetNext(); + cprec->SetNext(this); + } else { + Next = tdbp->GetColumns(); + tdbp->SetColumns(this); + } // endif cprec + + // Set additional MySQL access method information for column. + Precision = Long = cdp->GetLong(); + Bind = NULL; + To_Val = NULL; + Slen = 0; + Rank = -1; // Not known yet + + if (trace(1)) + htrc(" making new %sCOL C%d %s at %p\n", am, Index, Name, this); + + } // end of MYSQLCOL constructor + +/***********************************************************************/ +/* MYSQLCOL public constructor. */ +/***********************************************************************/ +MYSQLCOL::MYSQLCOL(MYSQL_FIELD *fld, PTDB tdbp, int i, PCSZ am) + : COLBLK(NULL, tdbp, i) + { +//const char *chset = get_charset_name(fld->charsetnr); +//char v = (!strcmp(chset, "binary")) ? 'B' : 0; + char v = 0; + + Name = fld->name; + Opt = 0; + Precision = Long = fld->length; + Buf_Type = MYSQLtoPLG(fld->type, &v); + 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(1)) + htrc(" making new %sCOL C%d %s at %p\n", am, Index, Name, this); + + } // end of MYSQLCOL constructor + +/***********************************************************************/ +/* 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 + +/***********************************************************************/ +/* 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 + + snprintf(g->Message, sizeof(g->Message), "Column %s not in result set", Name); + return true; +} // end of FindRank + +/***********************************************************************/ +/* SetBuffer: prepare a column block for write operation. */ +/***********************************************************************/ +bool MYSQLCOL::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 + +/***********************************************************************/ +/* 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) { + Bind->buffer_type = PLGtoMYSQL(TYPE_STRING, false); + Bind->buffer = (char *)PlugSubAlloc(g,NULL, 20); + Bind->buffer_length = 20; + Bind->length = &Slen; + } else { + Bind->buffer_type = PLGtoMYSQL(Buf_Type, false); + 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) + { + char *p, *buf, tim[20]; + 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) + snprintf(g->Message, sizeof(g->Message), MSG(INV_DEF_READ), rc); + + throw 11; + } else + tdbp->Fetched = true; + } + if ((buf = ((PTDBMY)To_Tdb)->Myc.GetCharField(Rank))) { + if (trace(2)) + htrc("MySQL ReadColumn: name=%s buf=%s\n", Name, buf); + + // TODO: have a true way to differenciate temporal values + if (Buf_Type == TYPE_DATE && strlen(buf) == 8) + // This is a TIME value + p = strcat(strcpy(tim, "1970-01-01 "), buf); + else + p = buf; + + if (Value->SetValue_char(p, strlen(p))) { + snprintf(g->Message, sizeof(g->Message), "Out of range value for column %s at row %d", + Name, tdbp->RowNumber(g)); + PushWarning(g, tdbp); + } // endif SetValue_char + + } else { + if (Nullable) + Value->SetNull(true); + + Value->Reset(); // Null value + } // endif buf + + } // end of ReadColumn + +/***********************************************************************/ +/* WriteColumn: make sure the bind buffer is updated. */ +/***********************************************************************/ +void MYSQLCOL::WriteColumn(PGLOBAL) + { + /*********************************************************************/ + /* Do convert the column value if necessary. */ + /*********************************************************************/ + if (Value != To_Val) + Value->SetValue_pval(To_Val, false); // Convert the inserted value + +#if defined(MYSQL_PREPARED_STATEMENTS) + if (((PTDBMY)To_Tdb)->Prep) { + if (Buf_Type == TYPE_DATE) { + Value->ShowValue((char *)Bind->buffer, (int)Bind->buffer_length); + Slen = strlen((char *)Bind->buffer); + } else if (IsTypeChar(Buf_Type)) + Slen = strlen(Value->GetCharValue()); + + } // endif Prep +#endif // MYSQL_PREPARED_STATEMENTS + + } // end of WriteColumn + +/* ------------------------------------------------------------------- */ + +/***********************************************************************/ +/* Implementation of the TDBMYEXC class. */ +/***********************************************************************/ +TDBMYEXC::TDBMYEXC(PMYDEF tdp) : TDBMYSQL(tdp) +{ + Cmdlist = NULL; + Cmdcol = NULL; + Shw = false; + Havew = false; + Isw = false; + Warnings = 0; + Mxr = tdp->Maxerr; + Nerr = 0; +} // end of TDBMYEXC constructor + +TDBMYEXC::TDBMYEXC(PTDBMYX tdbp) : TDBMYSQL(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 + +// Is this really useful ??? +PTDB TDBMYEXC::Clone(PTABS t) + { + PTDB tp; + PCOL cp1, cp2; + PGLOBAL g = t->G; + + tp = new(g) TDBMYEXC(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 Clone + +/***********************************************************************/ +/* 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. */ +/***********************************************************************/ +PCMD TDBMYEXC::MakeCMD(PGLOBAL g) + { + PCMD xcmd = NULL; + + if (To_CondFil) { + if (Cmdcol) { + if (!stricmp(Cmdcol, To_CondFil->Body) && + (To_CondFil->Op == OP_EQ || To_CondFil->Op == OP_IN)) { + xcmd = To_CondFil->Cmds; + } else + snprintf(g->Message, sizeof(g->Message), "Invalid command specification filter"); + + } else + snprintf(g->Message, sizeof(g->Message), "No command column in select list"); + + } else if (!Srcdef) + snprintf(g->Message, sizeof(g->Message), "No Srcdef default command"); + else + xcmd = new(g) CMD(g, Srcdef); + + return xcmd; + } // end of MakeCMD + +/***********************************************************************/ +/* EXC GetMaxSize: returns the maximum number of rows in the table. */ +/***********************************************************************/ +int TDBMYEXC::GetMaxSize(PGLOBAL) + { + if (MaxSize < 0) { + MaxSize = 10; // a guess + } // endif MaxSize + + return MaxSize; + } // end of GetMaxSize + +/***********************************************************************/ +/* MySQL Exec Access Method opening routine. */ +/***********************************************************************/ +bool TDBMYEXC::OpenDB(PGLOBAL g) + { + if (Use == USE_OPEN) { + snprintf(g->Message, sizeof(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, Schema, User, Pwd, Port)) + return true; + + Use = USE_OPEN; // Do it now in case we are recursively called + + if (Mode != MODE_READ && Mode != MODE_READX) { + snprintf(g->Message, sizeof(g->Message), "No INSERT/DELETE/UPDATE of MYSQL EXEC tables"); + return true; + } // endif Mode + + /*********************************************************************/ + /* Get the command to execute. */ + /*********************************************************************/ + if (!(Cmdlist = MakeCMD(g))) { + // Next lines commented out because of CHECK TABLE + //Myc.Close(); + //return true; + } // endif Cmdlist + + return false; + } // end of OpenDB + +/***********************************************************************/ +/* Data Base read routine for MYSQL access method. */ +/***********************************************************************/ +int TDBMYEXC::ReadDB(PGLOBAL g) + { + 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 { + if (Query) + Query->Set(Cmdlist->Cmd); + else + Query = new(g) STRING(g, 0, Cmdlist->Cmd); + + switch (rc = Myc.ExecSQLcmd(g, Query->GetStr(), &Warnings)) { + case RC_NF: + AftRows = Myc.m_Afrw; + snprintf(g->Message, sizeof(g->Message), "Affected rows"); + break; + case RC_OK: + AftRows = Myc.m_Fields; + snprintf(g->Message, sizeof(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 { + PushWarning(g, this, 1); + return RC_EF; + } // endif Cmdlist + + } // end of ReadDB + +/***********************************************************************/ +/* WriteDB: Data Base write routine for Exec MYSQL access methods. */ +/***********************************************************************/ +int TDBMYEXC::WriteDB(PGLOBAL g) + { + snprintf(g->Message, sizeof(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, PCSZ 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, PCSZ am) + : MYSQLCOL(fld, tdbp, i, am) + { + if (trace(1)) + 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; + + 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->GetStr()); 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 + + } // end of ReadColumn + +/***********************************************************************/ +/* WriteColumn: should never be called. */ +/***********************************************************************/ +void MYXCOL::WriteColumn(PGLOBAL) + { + assert(false); + } // end of WriteColumn + +/* ---------------------------TDBMCL class --------------------------- */ + +/***********************************************************************/ +/* TDBMCL class constructor. */ +/***********************************************************************/ +TDBMCL::TDBMCL(PMYDEF tdp) : TDBCAT(tdp) + { + Host = tdp->Hostname; + Db = tdp->Tabschema; + 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) + { + return MyColumns(g, NULL, Host, Db, User, Pwd, Tab, NULL, Port, false); + } // end of GetResult |