1 /************* TabMySQL C++ Program Source Code File (.CPP) *************/
2 /* PROGRAM NAME: TABMYSQL                                               */
3 /* -------------                                                        */
4 /*  Version 2.0                                                         */
5 /*                                                                      */
6 /* AUTHOR:                                                              */
7 /* -------                                                              */
8 /*  Olivier BERTRAND                                      2007-2017     */
9 /*                                                                      */
10 /* WHAT THIS PROGRAM DOES:                                              */
11 /* -----------------------                                              */
12 /*  Implements a table type that are MySQL tables.                      */
13 /*  It can optionally use the embedded MySQL library.                   */
14 /*                                                                      */
15 /* WHAT YOU NEED TO COMPILE THIS PROGRAM:                               */
16 /* --------------------------------------                               */
17 /*                                                                      */
18 /*  REQUIRED FILES:                                                     */
19 /*  ---------------                                                     */
20 /*    TABMYSQL.CPP   - Source code                                      */
21 /*    PLGDBSEM.H     - DB application declaration file                  */
22 /*    TABMYSQL.H     - TABMYSQL classes declaration file                */
23 /*    GLOBAL.H       - Global declaration file                          */
24 /*                                                                      */
25 /*  REQUIRED LIBRARIES:                                                 */
26 /*  -------------------                                                 */
27 /*    Large model C library                                             */
28 /*                                                                      */
29 /*  REQUIRED PROGRAMS:                                                  */
30 /*  ------------------                                                  */
31 /*    IBM, Borland, GNU or Microsoft C++ Compiler and Linker            */
32 /*                                                                      */
33 /************************************************************************/
34 #define MYSQL_SERVER 1
35 #include "my_global.h"
36 #include "sql_class.h"
37 #include "sql_servers.h"
38 #if defined(_WIN32)
39 //#include <windows.h>
40 #else   // !_WIN32
41 //#include <fnmatch.h>
42 //#include <errno.h>
43 #include <stdlib.h>
44 #include <stdio.h>
45 #include <string.h>
46 #include "osutil.h"
47 //#include <io.h>
48 //#include <fcntl.h>
49 #endif  // !_WIN32
50 
51 /***********************************************************************/
52 /*  Include application header files:                                  */
53 /***********************************************************************/
54 #include "global.h"
55 #include "plgdbsem.h"
56 #include "xtable.h"
57 #include "tabext.h"
58 #include "tabcol.h"
59 #include "colblk.h"
60 //#include "reldef.h"
61 #include "tabmysql.h"
62 #include "valblk.h"
63 #include "tabutil.h"
64 #include "ha_connect.h"
65 
66 #if defined(_CONSOLE)
67 void PrintResult(PGLOBAL, PSEM, PQRYRES);
68 #endif   // _CONSOLE
69 
70 // Used to check whether a MYSQL table is created on itself
71 bool CheckSelf(PGLOBAL g, TABLE_SHARE *s, PCSZ host, PCSZ db,
72 	                                        PCSZ tab, PCSZ src, int port);
73 
74 /***********************************************************************/
75 /*  External function.                                                 */
76 /***********************************************************************/
77 bool ExactInfo(void);
78 
79 /* -------------- Implementation of the MYSQLDEF class --------------- */
80 
81 /***********************************************************************/
82 /*  Constructor.                                                       */
83 /***********************************************************************/
MYSQLDEF(void)84 MYSQLDEF::MYSQLDEF(void)
85   {
86   Pseudo = 2;                            // SERVID is Ok but not ROWID
87   Hostname = NULL;
88 //Tabschema = NULL;
89 //Tabname = NULL;
90 //Srcdef = NULL;
91 //Username = NULL;
92 //Password = NULL;
93   Portnumber = 0;
94   Isview = false;
95   Bind = false;
96   Delayed = false;
97 //Xsrc = false;
98   Huge = false;
99   } // end of MYSQLDEF constructor
100 
101 /***********************************************************************/
102 /*  Get connection info from the declared server.                      */
103 /***********************************************************************/
GetServerInfo(PGLOBAL g,const char * server_name)104 bool MYSQLDEF::GetServerInfo(PGLOBAL g, const char *server_name)
105 {
106   THD      *thd= current_thd;
107   MEM_ROOT *mem= thd->mem_root;
108   FOREIGN_SERVER *server, server_buffer;
109   DBUG_ENTER("GetServerInfo");
110   DBUG_PRINT("info", ("server_name %s", server_name));
111 
112   if (!server_name || !strlen(server_name)) {
113     DBUG_PRINT("info", ("server_name not defined!"));
114     strcpy(g->Message, "server_name not defined!");
115     DBUG_RETURN(true);
116     } // endif server_name
117 
118   // get_server_by_name() clones the server if exists and allocates
119   // copies of strings in the supplied mem_root
120   if (!(server= get_server_by_name(mem, server_name, &server_buffer))) {
121     DBUG_PRINT("info", ("get_server_by_name returned > 0 error condition!"));
122     /* need to come up with error handling */
123     strcpy(g->Message, "get_server_by_name returned > 0 error condition!");
124     DBUG_RETURN(true);
125     } // endif server
126 
127   DBUG_PRINT("info", ("get_server_by_name returned server at %p",
128                      server));
129 
130   // TODO: We need to examine which of these can really be NULL
131   Hostname = PlugDup(g, server->host);
132   Tabschema = PlugDup(g, server->db);
133   Username = PlugDup(g, server->username);
134   Password = PlugDup(g, server->password);
135   Portnumber = (server->port) ? server->port : GetDefaultPort();
136 
137   DBUG_RETURN(false);
138 } // end of GetServerInfo
139 
140 /***********************************************************************/
141 /* Parse connection string                                             */
142 /*                                                                     */
143 /* SYNOPSIS                                                            */
144 /*   ParseURL()                                                        */
145 /*   url                 The connection string to parse                */
146 /*                                                                     */
147 /* DESCRIPTION                                                         */
148 /*   Populates the table with information about the connection         */
149 /*   to the foreign database that will serve as the data source.       */
150 /*   This string must be specified (currently) in the "CONNECTION"     */
151 /*   field, listed in the CREATE TABLE statement.                      */
152 /*                                                                     */
153 /*   This string MUST be in the format of any of these:                */
154 /*                                                                     */
155 /*   CONNECTION="scheme://user:pwd@host:port/database/table"           */
156 /*   CONNECTION="scheme://user@host/database/table"                    */
157 /*   CONNECTION="scheme://user@host:port/database/table"               */
158 /*   CONNECTION="scheme://user:pwd@host/database/table"                */
159 /*                                                                     */
160 /*   _OR_                                                              */
161 /*                                                                     */
162 /*   CONNECTION="connection name" (NIY)                                */
163 /*                                                                     */
164 /* An Example:                                                         */
165 /*                                                                     */
166 /* CREATE TABLE t1 (id int(32))                                        */
167 /*   ENGINE="CONNECT" TABLE_TYPE="MYSQL"                               */
168 /*   CONNECTION="mysql://joe:pwd@192.168.1.111:9308/dbname/tabname";   */
169 /*                                                                     */
170 /* CREATE TABLE t2 (                                                   */
171 /*   id int(4) NOT NULL auto_increment,                                */
172 /*   name varchar(32) NOT NULL,                                        */
173 /*   PRIMARY KEY(id)                                                   */
174 /*   ) ENGINE="CONNECT" TABLE_TYPE="MYSQL"                             */
175 /*   CONNECTION="my_conn";    (NIY)                                    */
176 /*                                                                     */
177 /*  'password' and 'port' are both optional.                           */
178 /*                                                                     */
179 /* RETURN VALUE                                                        */
180 /*   false       success                                               */
181 /*   true        error                                                 */
182 /*                                                                     */
183 /***********************************************************************/
ParseURL(PGLOBAL g,char * url,bool b)184 bool MYSQLDEF::ParseURL(PGLOBAL g, char *url, bool b)
185   {
186 	char *tabn, *pwd, *schema;
187 
188   if ((!strstr(url, "://") && (!strchr(url, '@')))) {
189     // No :// or @ in connection string. Must be a straight
190     // connection name of either "server" or "server/table"
191     // ok, so we do a little parsing, but not completely!
192     if ((tabn= strchr(url, '/'))) {
193       // If there is a single '/' in the connection string,
194       // this means the user is specifying a table name
195       *tabn++= '\0';
196 
197       // there better not be any more '/'s !
198       if (strchr(tabn, '/'))
199         return true;
200 
201 			Tabname = tabn;
202     } else
203       // Otherwise, straight server name,
204       Tabname = (b) ? GetStringCatInfo(g, "Tabname", Name) : NULL;
205 
206     if (trace(1))
207       htrc("server: %s  TableName: %s", url, Tabname);
208 
209     Server = url;
210     return GetServerInfo(g, url);
211   } else {
212     // URL, parse it
213     char *sport, *scheme = url;
214 
215     if (!(Username = strstr(url, "://"))) {
216       strcpy(g->Message, "Connection is not an URL");
217       return true;
218       } // endif User
219 
220     scheme[Username - scheme] = 0;
221 
222     if (stricmp(scheme, "mysql")) {
223       strcpy(g->Message, "scheme must be mysql");
224       return true;
225       } // endif scheme
226 
227     Username += 3;
228 
229     if (!(Hostname = (char*)strchr(Username, '@'))) {
230       strcpy(g->Message, "No host specified in URL");
231       return true;
232     } else {
233       *Hostname++ = 0;                   // End Username
234       Server = Hostname;
235     } // endif Hostname
236 
237     if ((pwd = (char*)strchr(Username, ':'))) {
238       *pwd++ = 0;                   // End username
239 
240       // Make sure there isn't an extra /
241       if (strchr(pwd, '/')) {
242         strcpy(g->Message, "Syntax error in URL");
243         return true;
244         } // endif
245 
246       // Found that if the string is:
247       // user:@hostname:port/db/table
248       // Then password is a null string, so set to NULL
249 			if ((pwd[0] == 0))
250 				Password = NULL;
251 			else
252 				Password = pwd;
253 
254       } // endif password
255 
256     // Make sure there isn't an extra / or @ */
257     if ((strchr(Username, '/')) || (strchr(Hostname, '@'))) {
258       strcpy(g->Message, "Syntax error in URL");
259       return true;
260       } // endif
261 
262     if ((schema = strchr(Hostname, '/'))) {
263       *schema++ = 0;
264 
265       if ((tabn = strchr(schema, '/'))) {
266         *tabn++ = 0;
267 
268         // Make sure there's not an extra /
269         if ((strchr(tabn, '/'))) {
270           strcpy(g->Message, "Syntax error in URL");
271           return true;
272           } // endif /
273 
274 				Tabname = tabn;
275         } // endif TableName
276 
277 			Tabschema = schema;
278 		} // endif database
279 
280     if ((sport = strchr(Hostname, ':')))
281       *sport++ = 0;
282 
283     // For unspecified values, get the values of old style options
284     // but only if called from MYSQLDEF, else set them to NULL
285     Portnumber = (sport && sport[0]) ? atoi(sport)
286                : (b) ? GetIntCatInfo("Port", GetDefaultPort()) : 0;
287 
288     if (Username[0] == 0)
289       Username = (b) ? GetStringCatInfo(g, "User", "*") : NULL;
290 
291     if (Hostname[0] == 0)
292       Hostname = (b) ? GetStringCatInfo(g, "Host", "localhost") : NULL;
293 
294     if (!Tabschema || !*Tabschema)
295       Tabschema = (b) ? GetStringCatInfo(g, "Database", "*") : NULL;
296 
297     if (!Tabname || !*Tabname)
298       Tabname = (b) ? GetStringCatInfo(g, "Tabname", Name) : NULL;
299 
300     if (!Password)
301       Password = (b) ? GetStringCatInfo(g, "Password", NULL) : NULL;
302     } // endif URL
303 
304 #if 0
305   if (!share->port)
306     if (!share->hostname || strcmp(share->hostname, my_localhost) == 0)
307       share->socket= (char *) MYSQL_UNIX_ADDR;
308     else
309       share->port= MYSQL_PORT;
310 #endif // 0
311 
312   return false;
313   } // end of ParseURL
314 
315 /***********************************************************************/
316 /*  DefineAM: define specific AM block values from XCV file.           */
317 /***********************************************************************/
DefineAM(PGLOBAL g,LPCSTR am,int)318 bool MYSQLDEF::DefineAM(PGLOBAL g, LPCSTR am, int)
319   {
320   char *url;
321 
322   Desc = "MySQL Table";
323 
324   if (stricmp(am, "MYPRX")) {
325     // Normal case of specific MYSQL table
326     url = GetStringCatInfo(g, "Connect", NULL);
327 
328     if (!url || !*url) {
329       // Not using the connection URL
330       Hostname = GetStringCatInfo(g, "Host", "localhost");
331       Tabschema = GetStringCatInfo(g, "Database", "*");
332       Tabname = GetStringCatInfo(g, "Name", Name); // Deprecated
333       Tabname = GetStringCatInfo(g, "Tabname", Tabname);
334       Username = GetStringCatInfo(g, "User", "*");
335       Password = GetStringCatInfo(g, "Password", NULL);
336       Portnumber = GetIntCatInfo("Port", GetDefaultPort());
337       Server = Hostname;
338     } else if (ParseURL(g, url))
339       return true;
340 
341     Bind = !!GetIntCatInfo("Bind", 0);
342     Delayed = !!GetIntCatInfo("Delayed", 0);
343   } else {
344     // MYSQL access from a PROXY table
345 		TABLE_SHARE* s;
346 
347 		Tabschema = GetStringCatInfo(g, "Database", Tabschema ? Tabschema : PlugDup(g, "*"));
348     Isview = GetBoolCatInfo("View", false);
349 
350     // We must get other connection parms from the calling table
351     s = Remove_tshp(Cat);
352     url = GetStringCatInfo(g, "Connect", NULL);
353 
354     if (!url || !*url) {
355       Hostname = GetStringCatInfo(g, "Host", "localhost");
356       Username = GetStringCatInfo(g, "User", "*");
357       Password = GetStringCatInfo(g, "Password", NULL);
358       Portnumber = GetIntCatInfo("Port", GetDefaultPort());
359       Server = Hostname;
360     } else {
361       PCSZ locdb = Tabschema;
362 
363       if (ParseURL(g, url))
364         return true;
365 
366       Tabschema = locdb;
367     } // endif url
368 
369     Tabname = Name;
370 
371 		// Needed for column description
372 		Restore_tshp(Cat, s);
373   } // endif am
374 
375   if ((Srcdef = GetStringCatInfo(g, "Srcdef", NULL))) {
376     Read_Only = true;
377     Isview = true;
378   } else if (CheckSelf(g, Hc->GetTable()->s, Hostname, Tabschema,
379                        Tabname, Srcdef, Portnumber))
380     return true;
381 
382   // Used for Update and Delete
383   Qrystr = GetStringCatInfo(g, "Query_String", "?");
384   Quoted = GetIntCatInfo("Quoted", 0);
385 
386   // Specific for command executing tables
387   Xsrc = GetBoolCatInfo("Execsrc", false);
388   Maxerr = GetIntCatInfo("Maxerr", 0);
389   Huge = GetBoolCatInfo("Huge", false);
390   return false;
391   } // end of DefineAM
392 
393 /***********************************************************************/
394 /*  GetTable: makes a new TDB of the proper type.                      */
395 /***********************************************************************/
GetTable(PGLOBAL g,MODE)396 PTDB MYSQLDEF::GetTable(PGLOBAL g, MODE)
397   {
398   if (Xsrc)
399     return new(g) TDBMYEXC(this);
400   else if (Catfunc == FNC_COL)
401     return new(g) TDBMCL(this);
402   else
403     return new(g) TDBMYSQL(this);
404 
405   } // end of GetTable
406 
407 /* ------------------------------------------------------------------- */
408 
409 /***********************************************************************/
410 /*  Implementation of the TDBMYSQL class.                              */
411 /***********************************************************************/
TDBMYSQL(PMYDEF tdp)412 TDBMYSQL::TDBMYSQL(PMYDEF tdp) : TDBEXT(tdp)
413   {
414   if (tdp) {
415     Host = tdp->Hostname;
416 //  Schema = tdp->Tabschema;
417 //  TableName = tdp->Tabname;
418 //  Srcdef = tdp->Srcdef;
419 //  User = tdp->Username;
420 //  Pwd = tdp->Password;
421     Server = tdp->Server;
422 //  Qrystr = tdp->Qrystr;
423     Quoted = MY_MAX(0, tdp->Quoted);
424     Port = tdp->Portnumber;
425     Isview = tdp->Isview;
426     Prep = tdp->Bind;
427     Delayed = tdp->Delayed;
428     Myc.m_Use = tdp->Huge;
429   } else {
430     Host = NULL;
431 //  Schema = NULL;
432 //  TableName = NULL;
433 //  Srcdef = NULL;
434 //  User = NULL;
435 //  Pwd = NULL;
436     Server = NULL;
437 //  Qrystr = NULL;
438 //  Quoted = 0;
439     Port = 0;
440     Isview = false;
441     Prep = false;
442     Delayed = false;
443   } // endif tdp
444 
445   Bind = NULL;
446 //Query = NULL;
447   Fetched = false;
448   m_Rc = RC_FX;
449 //AftRows = 0;
450   N = -1;
451 //Nparm = 0;
452   } // end of TDBMYSQL constructor
453 
TDBMYSQL(PTDBMY tdbp)454 TDBMYSQL::TDBMYSQL(PTDBMY tdbp) : TDBEXT(tdbp)
455   {
456   Host = tdbp->Host;
457 //Schema = tdbp->Schema;
458 //TableName = tdbp->TableName;
459 //Srcdef = tdbp->Srcdef;
460 //User = tdbp->User;
461 //Pwd =  tdbp->Pwd;
462 //Qrystr = tdbp->Qrystr;
463 //Quoted = tdbp->Quoted;
464 	Server = tdbp->Server;
465   Port = tdbp->Port;
466   Isview = tdbp->Isview;
467   Prep = tdbp->Prep;
468   Delayed = tdbp->Delayed;
469   Bind = NULL;
470 //Query = tdbp->Query;
471   Fetched = tdbp->Fetched;
472   m_Rc = tdbp->m_Rc;
473 //AftRows = tdbp->AftRows;
474   N = tdbp->N;
475 //Nparm = tdbp->Nparm;
476   } // end of TDBMYSQL copy constructor
477 
478 // Is this really useful ??? --> Yes for UPDATE
Clone(PTABS t)479 PTDB TDBMYSQL::Clone(PTABS t)
480   {
481   PTDB    tp;
482   PCOL    cp1, cp2;
483   PGLOBAL g = t->G;
484 
485   tp = new(g) TDBMYSQL(this);
486 
487   for (cp1 = Columns; cp1; cp1 = cp1->GetNext()) {
488     cp2 = new(g) MYSQLCOL((PMYCOL)cp1, tp);
489 
490     NewPointer(t, cp1, cp2);
491     } // endfor cp1
492 
493   return tp;
494   } // end of Clone
495 
496 /***********************************************************************/
497 /*  Allocate MYSQL column description block.                           */
498 /***********************************************************************/
MakeCol(PGLOBAL g,PCOLDEF cdp,PCOL cprec,int n)499 PCOL TDBMYSQL::MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n)
500   {
501   return new(g) MYSQLCOL(cdp, this, cprec, n);
502   } // end of MakeCol
503 
504 /***********************************************************************/
505 /*  MakeSelect: make the Select statement use with MySQL connection.   */
506 /*  Note: when implementing EOM filtering, column only used in local   */
507 /*  filter should be removed from column list.                         */
508 /***********************************************************************/
MakeSelect(PGLOBAL g,bool mx)509 bool TDBMYSQL::MakeSelect(PGLOBAL g, bool mx)
510 {
511 //char   *tk = "`";
512   char    tk = '`';
513   int     len = 0, rank = 0;
514   bool    b = false;
515   PCOL    colp;
516 //PDBUSER dup = PlgGetUser(g);
517 
518   if (Query)
519     return false;        // already done
520 
521 	if (Srcdef)
522 		return MakeSrcdef(g);
523 
524   // Allocate the string used to contain Query
525   Query = new(g) STRING(g, 1023, "SELECT ");
526 
527   if (Columns) {
528     for (colp = Columns; colp; colp = colp->GetNext())
529       if (!colp->IsSpecial()) {
530         if (b)
531           Query->Append(", ");
532         else
533           b = true;
534 
535         Query->Append(tk);
536         Query->Append(colp->GetName());
537         Query->Append(tk);
538         ((PMYCOL)colp)->Rank = rank++;
539       } // endif colp
540 
541   } else {
542     // ncol == 0 can occur for views or queries such as
543     // Query count(*) from... for which we will count the rows from
544     // Query '*' from...
545     // (the use of a char constant minimize the result storage)
546     if (Isview)
547       Query->Append('*');
548     else
549       Query->Append("'*'");
550 
551   } // endif ncol
552 
553   Query->Append(" FROM ");
554   Query->Append(tk);
555   Query->Append(TableName);
556   Query->Append(tk);
557   len = Query->GetLength();
558 
559   if (To_CondFil) {
560     if (!mx) {
561       Query->Append(" WHERE ");
562       Query->Append(To_CondFil->Body);
563       len = Query->GetLength() + 1;
564     } else
565       len += (strlen(To_CondFil->Body) + 256);
566 
567   } else
568     len += (mx ? 256 : 1);
569 
570   if (Query->IsTruncated() || Query->Resize(len)) {
571     strcpy(g->Message, "MakeSelect: Out of memory");
572     return true;
573   } // endif Query
574 
575   if (trace(33))
576     htrc("Query=%s\n", Query->GetStr());
577 
578   return false;
579 } // end of MakeSelect
580 
581 /***********************************************************************/
582 /*  MakeInsert: make the Insert statement used with MySQL connection.  */
583 /***********************************************************************/
MakeInsert(PGLOBAL g)584 bool TDBMYSQL::MakeInsert(PGLOBAL g)
585   {
586   const char *tk = "`";
587   uint  len = 0;
588   bool  oom, b = false;
589   PCOL  colp;
590 
591   if (Query)
592     return false;        // already done
593 
594   if (Prep) {
595 #if !defined(MYSQL_PREPARED_STATEMENTS)
596     strcpy(g->Message, "Prepared statements not used (not supported)");
597     PushWarning(g, this);
598     Prep = false;
599 #endif  // !MYSQL_PREPARED_STATEMENTS
600     } // endif Prep
601 
602   for (colp = Columns; colp; colp = colp->GetNext())
603     if (colp->IsSpecial()) {
604       strcpy(g->Message, MSG(NO_SPEC_COL));
605       return true;
606     } else {
607       len += (strlen(colp->GetName()) + 4);
608 
609       // Parameter marker
610       if (!Prep) {
611         if (colp->GetResultType() == TYPE_DATE)
612           len += 20;
613         else
614           len += colp->GetLength();
615 
616       } else
617         len += 2;
618 
619       ((PMYCOL)colp)->Rank = Nparm++;
620     } // endif colp
621 
622   // Below 40 is enough to contain the fixed part of the query
623   len += (strlen(TableName) + 40);
624   Query = new(g) STRING(g, len);
625 
626   if (Delayed)
627     Query->Set("INSERT DELAYED INTO ");
628   else
629     Query->Set("INSERT INTO ");
630 
631   Query->Append(tk);
632   Query->Append(TableName);
633   Query->Append("` (");
634 
635   for (colp = Columns; colp; colp = colp->GetNext()) {
636     if (b)
637       Query->Append(", ");
638     else
639       b = true;
640 
641     Query->Append(tk);
642     Query->Append(colp->GetName());
643     Query->Append(tk);
644     } // endfor colp
645 
646   Query->Append(") VALUES (");
647 
648 #if defined(MYSQL_PREPARED_STATEMENTS)
649   if (Prep) {
650     for (int i = 0; i < Nparm; i++)
651       Query->Append("?,");
652 
653     Query->RepLast(')');
654     Query->Trim();
655     }  // endif Prep
656 #endif  // MYSQL_PREPARED_STATEMENTS
657 
658   if ((oom = Query->IsTruncated()))
659     strcpy(g->Message, "MakeInsert: Out of memory");
660 
661   return oom;
662   } // end of MakeInsert
663 
664 /***********************************************************************/
665 /*  MakeCommand: make the Update or Delete statement to send to the    */
666 /*  MySQL server. Limited to remote values and filtering.              */
667 /***********************************************************************/
MakeCommand(PGLOBAL g)668 bool TDBMYSQL::MakeCommand(PGLOBAL g)
669   {
670   Query = new(g) STRING(g, strlen(Qrystr) + 64);
671 
672   if (Quoted > 0 || stricmp(Name, TableName)) {
673     char *p, *qrystr, name[68];
674     bool  qtd = Quoted > 0;
675 
676 
677     // Make a lower case copy of the originale query
678     qrystr = (char*)PlugSubAlloc(g, NULL, strlen(Qrystr) + 5);
679     strlwr(strcpy(qrystr, Qrystr));
680 
681     // Check whether the table name is equal to a keyword
682     // If so, it must be quoted in the original query
683     strlwr(strcat(strcat(strcpy(name, "`"), Name), "`"));
684 
685     if (!strstr("`update`delete`low_priority`ignore`quick`from`", name))
686       strlwr(strcpy(name, Name));     // Not a keyword
687 
688     if ((p = strstr(qrystr, name))) {
689       Query->Set(Qrystr, (uint)(p - qrystr));
690 
691       if (qtd && *(p-1) == ' ') {
692         Query->Append('`');
693         Query->Append(TableName);
694         Query->Append('`');
695       } else
696         Query->Append(TableName);
697 
698       Query->Append(Qrystr + (p - qrystr) + strlen(name));
699 
700       if (Query->IsTruncated()) {
701         strcpy(g->Message, "MakeCommand: Out of memory");
702         return true;
703       } else
704         strlwr(strcpy(qrystr, Query->GetStr()));
705 
706     } else {
707       sprintf(g->Message, "Cannot use this %s command",
708                    (Mode == MODE_UPDATE) ? "UPDATE" : "DELETE");
709       return true;
710     } // endif p
711 
712   } else
713     (void)Query->Set(Qrystr);
714 
715   return false;
716   } // end of MakeCommand
717 
718 #if 0
719 /***********************************************************************/
720 /*  MakeUpdate: make the Update statement use with MySQL connection.   */
721 /*  Limited to remote values and filtering.                            */
722 /***********************************************************************/
723 int TDBMYSQL::MakeUpdate(PGLOBAL g)
724   {
725   char *qc, cmd[8], tab[96], end[1024];
726 
727   Query = (char*)PlugSubAlloc(g, NULL, strlen(Qrystr) + 64);
728   memset(end, 0, sizeof(end));
729 
730   if (sscanf(Qrystr, "%s `%[^`]`%1023c", cmd, tab, end) > 2 ||
731       sscanf(Qrystr, "%s \"%[^\"]\"%1023c", cmd, tab, end) > 2)
732     qc = "`";
733   else if (sscanf(Qrystr, "%s %s%1023c", cmd, tab, end) > 2
734                   && !stricmp(tab, Name))
735     qc = (Quoted) ? "`" : "";
736   else {
737     strcpy(g->Message, "Cannot use this UPDATE command");
738     return RC_FX;
739   } // endif sscanf
740 
741   assert(!stricmp(cmd, "update"));
742   strcat(strcat(strcat(strcpy(Query, "UPDATE "), qc), TableName), qc);
743   strcat(Query, end);
744   return RC_OK;
745   } // end of MakeUpdate
746 
747 /***********************************************************************/
748 /*  MakeDelete: make the Delete statement used with MySQL connection.  */
749 /*  Limited to remote filtering.                                       */
750 /***********************************************************************/
751 int TDBMYSQL::MakeDelete(PGLOBAL g)
752   {
753   char *qc, cmd[8], from[8], tab[96], end[512];
754 
755   Query = (char*)PlugSubAlloc(g, NULL, strlen(Qrystr) + 64);
756   memset(end, 0, sizeof(end));
757 
758   if (sscanf(Qrystr, "%s %s `%[^`]`%511c", cmd, from, tab, end) > 2 ||
759       sscanf(Qrystr, "%s %s \"%[^\"]\"%511c", cmd, from, tab, end) > 2)
760     qc = "`";
761   else if (sscanf(Qrystr, "%s %s %s%511c", cmd, from, tab, end) > 2)
762     qc = (Quoted) ? "`" : "";
763   else {
764     strcpy(g->Message, "Cannot use this DELETE command");
765     return RC_FX;
766   } // endif sscanf
767 
768   assert(!stricmp(cmd, "delete") && !stricmp(from, "from"));
769   strcat(strcat(strcat(strcpy(Query, "DELETE FROM "), qc), TableName), qc);
770 
771   if (*end)
772     strcat(Query, end);
773 
774   return RC_OK;
775   } // end of MakeDelete
776 #endif // 0
777 
778 /***********************************************************************/
779 /*  MYSQL Cardinality: returns the number of rows in the table.        */
780 /***********************************************************************/
Cardinality(PGLOBAL g)781 int TDBMYSQL::Cardinality(PGLOBAL g)
782 {
783   if (!g)
784     return (Mode == MODE_ANY && !Srcdef) ? 1 : 0;
785 
786   if (Cardinal < 0 && Mode == MODE_ANY && !Srcdef && ExactInfo()) {
787     // Info command, we must return the exact table row number
788     char   query[96];
789     MYSQLC myc;
790 
791     if (myc.Open(g, Host, Schema, User, Pwd, Port, csname))
792       return -1;
793 
794     strcpy(query, "SELECT COUNT(*) FROM ");
795 
796     if (Quoted > 0)
797       strcat(strcat(strcat(query, "`"), TableName), "`");
798     else
799       strcat(query, TableName);
800 
801     Cardinal = myc.GetTableSize(g, query);
802     myc.Close();
803   } else
804     Cardinal = 10;    // To make MySQL happy
805 
806   return Cardinal;
807 } // end of Cardinality
808 
809 #if 0
810 /***********************************************************************/
811 /*  MYSQL GetMaxSize: returns the maximum number of rows in the table. */
812 /***********************************************************************/
813 int TDBMYSQL::GetMaxSize(PGLOBAL g)
814   {
815   if (MaxSize < 0) {
816     if (Mode == MODE_DELETE)
817       // Return 0 in mode DELETE in case of delete all.
818       MaxSize = 0;
819     else if (!Cardinality(NULL))
820       MaxSize = 10;   // To make MySQL happy
821     else if ((MaxSize = Cardinality(g)) < 0)
822       MaxSize = 12;   // So we can see an error occurred
823 
824     } // endif MaxSize
825 
826   return MaxSize;
827   } // end of GetMaxSize
828 #endif // 0
829 
830 /***********************************************************************/
831 /*  This a fake routine as ROWID does not exist in MySQL.              */
832 /***********************************************************************/
RowNumber(PGLOBAL,bool)833 int TDBMYSQL::RowNumber(PGLOBAL, bool)
834   {
835   return N + 1;
836   } // end of RowNumber
837 
838 /***********************************************************************/
839 /*  Return 0 in mode UPDATE to tell that the update is done.           */
840 /***********************************************************************/
GetProgMax(PGLOBAL g)841 int TDBMYSQL::GetProgMax(PGLOBAL g)
842   {
843   return (Mode == MODE_UPDATE) ? 0 : GetMaxSize(g);
844   } // end of GetProgMax
845 
846 /***********************************************************************/
847 /*  MySQL Bind Parameter function.                                     */
848 /***********************************************************************/
BindColumns(PGLOBAL g)849 int TDBMYSQL::BindColumns(PGLOBAL g __attribute__((unused)))
850   {
851 #if defined(MYSQL_PREPARED_STATEMENTS)
852   if (Prep) {
853     Bind = (MYSQL_BIND*)PlugSubAlloc(g, NULL, Nparm * sizeof(MYSQL_BIND));
854 
855     for (PMYCOL colp = (PMYCOL)Columns; colp; colp = (PMYCOL)colp->Next)
856       colp->InitBind(g);
857 
858     return Myc.BindParams(g, Bind);
859     } // endif prep
860 #endif   // MYSQL_PREPARED_STATEMENTS
861 
862   return RC_OK;
863   } // end of BindColumns
864 
865 /***********************************************************************/
866 /*  MySQL Access Method opening routine.                               */
867 /***********************************************************************/
OpenDB(PGLOBAL g)868 bool TDBMYSQL::OpenDB(PGLOBAL g)
869   {
870   if (Use == USE_OPEN) {
871     /*******************************************************************/
872     /*  Table already open, just replace it at its beginning.          */
873     /*******************************************************************/
874 		if (Myc.Rewind(g, (Mode == MODE_READX) ? Query->GetStr() : NULL) != RC_OK)
875 			return true;
876 
877     N = -1;
878     return false;
879     } // endif use
880 
881   /*********************************************************************/
882   /*  Open a MySQL connection for this table.                          */
883   /*  Note: this may not be the proper way to do. Perhaps it is better */
884   /*  to test whether a connection is already open for this server     */
885   /*  and if so to allocate just a new result set. But this only for   */
886   /*  servers allowing concurency in getting results ???               */
887   /*********************************************************************/
888   if (!Myc.Connected()) {
889     if (Myc.Open(g, Host, Schema, User, Pwd, Port, csname))
890       return true;
891 
892     } // endif Connected
893 
894   /*********************************************************************/
895   /*  Take care of DATE columns.                                       */
896   /*********************************************************************/
897   for (PMYCOL colp = (PMYCOL)Columns; colp; colp = (PMYCOL)colp->Next)
898     if (colp->Buf_Type == TYPE_DATE)
899       // Format must match DATETIME MySQL type
900       ((DTVAL*)colp->GetValue())->SetFormat(g, "YYYY-MM-DD hh:mm:ss", 19);
901 
902   /*********************************************************************/
903   /*  Allocate whatever is used for getting results.                   */
904   /*********************************************************************/
905   if (Mode == MODE_READ || Mode == MODE_READX) {
906     MakeSelect(g, Mode == MODE_READX);
907     if (Mode == MODE_READ && !Query)
908     {
909       Myc.Close();
910       return true;
911     }
912     m_Rc = (Mode == MODE_READ)
913          ? Myc.ExecSQL(g, Query->GetStr()) : RC_OK;
914 
915 #if 0
916     if (!Myc.m_Res || !Myc.m_Fields) {
917       sprintf(g->Message, "%s result", (Myc.m_Res) ? "Void" : "No");
918       Myc.Close();
919       return true;
920       } // endif m_Res
921 #endif // 0
922 
923     if (!m_Rc && Srcdef)
924       if (SetColumnRanks(g))
925         return true;
926 
927   } else if (Mode == MODE_INSERT) {
928     if (Srcdef) {
929       strcpy(g->Message, "No insert into anonym views");
930       Myc.Close();
931       return true;
932       } // endif Srcdef
933 
934     if (!MakeInsert(g)) {
935 #if defined(MYSQL_PREPARED_STATEMENTS)
936       int n = (Prep)
937             ? Myc.PrepareSQL(g, Query->GetCharValue()) : Nparm;
938 
939       if (Nparm != n) {
940         if (n >= 0)          // Other errors return negative values
941           strcpy(g->Message, MSG(BAD_PARM_COUNT));
942 
943       } else
944 #endif   // MYSQL_PREPARED_STATEMENTS
945         m_Rc = BindColumns(g);
946 
947       } // endif MakeInsert
948 
949     if (m_Rc != RC_FX) {
950       char cmd[64];
951       int  w;
952 
953       sprintf(cmd, "ALTER TABLE `%s` DISABLE KEYS", TableName);
954 
955       m_Rc = Myc.ExecSQL(g, cmd, &w);   // may fail for some engines
956       } // endif m_Rc
957 
958   } else
959 //  m_Rc = (Mode == MODE_DELETE) ? MakeDelete(g) : MakeUpdate(g);
960     m_Rc = (MakeCommand(g)) ? RC_FX : RC_OK;
961 
962   if (m_Rc == RC_FX) {
963     Myc.Close();
964     return true;
965     } // endif rc
966 
967   Use = USE_OPEN;
968   return false;
969   } // end of OpenDB
970 
971 /***********************************************************************/
972 /*  Set the rank of columns in the result set.                         */
973 /***********************************************************************/
SetColumnRanks(PGLOBAL g)974 bool TDBMYSQL::SetColumnRanks(PGLOBAL g)
975   {
976   for (PCOL colp = Columns; colp; colp = colp->GetNext())
977     if (((PMYCOL)colp)->FindRank(g))
978       return true;
979 
980   return false;
981   } // end of SetColumnRanks
982 
983 /***********************************************************************/
984 /*  Called by Parent table to make the columns of a View.              */
985 /***********************************************************************/
MakeFieldColumn(PGLOBAL g,char * name)986 PCOL TDBMYSQL::MakeFieldColumn(PGLOBAL g, char *name)
987   {
988   int          n;
989   MYSQL_FIELD *fld;
990   PCOL         cp, colp = NULL;
991 
992   for (n = 0; n < Myc.m_Fields; n++) {
993     fld = &Myc.m_Res->fields[n];
994 
995     if (!stricmp(name, fld->name)) {
996       colp = new(g) MYSQLCOL(fld, this, n);
997 
998       if (colp->InitValue(g))
999         return NULL;
1000 
1001       if (!Columns)
1002         Columns = colp;
1003       else for (cp = Columns; cp; cp = cp->GetNext())
1004         if (!cp->GetNext()) {
1005           cp->SetNext(colp);
1006           break;
1007           } // endif Next
1008 
1009       break;
1010       } // endif name
1011 
1012     } // endfor n
1013 
1014   if (!colp)
1015     sprintf(g->Message, "Column %s is not in view", name);
1016 
1017   return colp;
1018   } // end of MakeFieldColumn
1019 
1020 /***********************************************************************/
1021 /*  Called by Pivot tables to find default column names in a View      */
1022 /*  as the name of last field not equal to the passed name.            */
1023 /***********************************************************************/
FindFieldColumn(char * name)1024 char *TDBMYSQL::FindFieldColumn(char *name)
1025   {
1026   int          n;
1027   MYSQL_FIELD *fld;
1028   char        *cp = NULL;
1029 
1030   for (n = Myc.m_Fields - 1; n >= 0; n--) {
1031     fld = &Myc.m_Res->fields[n];
1032 
1033     if (!name || stricmp(name, fld->name)) {
1034       cp = fld->name;
1035       break;
1036       } // endif name
1037 
1038     } // endfor n
1039 
1040   return cp;
1041   } // end of FindFieldColumn
1042 
1043 /***********************************************************************/
1044 /*  Send an UPDATE or DELETE command to the remote server.             */
1045 /***********************************************************************/
SendCommand(PGLOBAL g)1046 int TDBMYSQL::SendCommand(PGLOBAL g)
1047   {
1048   int w;
1049 
1050   if (Myc.ExecSQLcmd(g, Query->GetStr(), &w) == RC_NF) {
1051     AftRows = Myc.m_Afrw;
1052     sprintf(g->Message, "%s: %d affected rows", TableName, AftRows);
1053     PushWarning(g, this, 0);    // 0 means a Note
1054 
1055     if (trace(1))
1056       htrc("%s\n", g->Message);
1057 
1058     if (w && Myc.ExecSQL(g, "SHOW WARNINGS") == RC_OK) {
1059       // We got warnings from the remote server
1060       while (Myc.Fetch(g, -1) == RC_OK) {
1061         sprintf(g->Message, "%s: (%s) %s", TableName,
1062                 Myc.GetCharField(1), Myc.GetCharField(2));
1063         PushWarning(g, this);
1064         } // endwhile Fetch
1065 
1066       Myc.FreeResult();
1067       } // endif w
1068 
1069     return RC_EF;               // Nothing else to do
1070   } else
1071     return RC_FX;               // Error
1072 
1073   } // end of SendCommand
1074 
1075 /***********************************************************************/
1076 /*  Data Base indexed read routine for MYSQL access method.            */
1077 /***********************************************************************/
ReadKey(PGLOBAL g,OPVAL op,const key_range * kr)1078 bool TDBMYSQL::ReadKey(PGLOBAL g, OPVAL op, const key_range *kr)
1079 {
1080   int  oldlen = Query->GetLength();
1081 	PHC  hc = To_Def->GetHandler();
1082 
1083 	if (!(kr || hc->end_range) || op == OP_NEXT ||
1084          Mode == MODE_UPDATE || Mode == MODE_DELETE) {
1085     if (!kr && Mode == MODE_READX) {
1086       // This is a false indexed read
1087       m_Rc = Myc.ExecSQL(g, Query->GetStr());
1088       Mode = MODE_READ;
1089       return (m_Rc == RC_FX) ? true : false;
1090       } // endif key
1091 
1092     return false;
1093   } else {
1094     if (Myc.m_Res)
1095       Myc.FreeResult();
1096 
1097 		if (hc->MakeKeyWhere(g, Query, op, '`', kr))
1098 			return true;
1099 
1100     if (To_CondFil) {
1101 			if (To_CondFil->Idx != hc->active_index) {
1102 				To_CondFil->Idx = hc->active_index;
1103 				To_CondFil->Body= (char*)PlugSubAlloc(g, NULL, 0);
1104 				*To_CondFil->Body= 0;
1105 
1106 				if ((To_CondFil = hc->CheckCond(g, To_CondFil, Cond)))
1107 					PlugSubAlloc(g, NULL, strlen(To_CondFil->Body) + 1);
1108 
1109 				} // endif active_index
1110 
1111 			if (To_CondFil)
1112 				if (Query->Append(" AND ") || Query->Append(To_CondFil->Body)) {
1113 				  strcpy(g->Message, "Readkey: Out of memory");
1114 					return true;
1115 					} // endif Append
1116 
1117 			} // endif To_Condfil
1118 
1119 		Mode = MODE_READ;
1120 	} // endif's op
1121 
1122 	if (trace(33))
1123 		htrc("MYSQL ReadKey: Query=%s\n", Query->GetStr());
1124 
1125 	m_Rc = Myc.ExecSQL(g, Query->GetStr());
1126   Query->Truncate(oldlen);
1127   return (m_Rc == RC_FX) ? true : false;
1128 } // end of ReadKey
1129 
1130 /***********************************************************************/
1131 /*  Data Base read routine for MYSQL access method.                    */
1132 /***********************************************************************/
ReadDB(PGLOBAL g)1133 int TDBMYSQL::ReadDB(PGLOBAL g)
1134   {
1135   int rc;
1136 
1137   if (trace(2))
1138     htrc("MySQL ReadDB: R%d Mode=%d\n", GetTdb_No(), Mode);
1139 
1140   if (Mode == MODE_UPDATE || Mode == MODE_DELETE)
1141     return SendCommand(g);
1142 
1143   /*********************************************************************/
1144   /*  Now start the reading process.                                   */
1145   /*  Here is the place to fetch the line.                             */
1146   /*********************************************************************/
1147   N++;
1148   Fetched = ((rc = Myc.Fetch(g, -1)) == RC_OK);
1149 
1150   if (trace(2))
1151     htrc(" Read: rc=%d\n", rc);
1152 
1153   return rc;
1154   } // end of ReadDB
1155 
1156 /***********************************************************************/
1157 /*  WriteDB: Data Base write routine for MYSQL access methods.         */
1158 /***********************************************************************/
WriteDB(PGLOBAL g)1159 int TDBMYSQL::WriteDB(PGLOBAL g)
1160   {
1161 #if defined(MYSQL_PREPARED_STATEMENTS)
1162   if (Prep)
1163     return Myc.ExecStmt(g);
1164 #endif   // MYSQL_PREPARED_STATEMENTS
1165 
1166   // Statement was not prepared, we must construct and execute
1167   // an insert query for each line to insert
1168   int  rc;
1169   uint len = Query->GetLength();
1170   char buf[64];
1171 
1172   // Make the Insert command value list
1173   for (PCOL colp = Columns; colp; colp = colp->GetNext()) {
1174     if (!colp->GetValue()->IsNull()) {
1175       if (colp->GetResultType() == TYPE_STRING ||
1176           colp->GetResultType() == TYPE_DATE)
1177         Query->Append_quoted(colp->GetValue()->GetCharString(buf));
1178       else
1179         Query->Append(colp->GetValue()->GetCharString(buf));
1180 
1181     } else
1182       Query->Append("NULL");
1183 
1184     Query->Append(',');
1185     } // endfor colp
1186 
1187   if (unlikely(Query->IsTruncated())) {
1188     strcpy(g->Message, "WriteDB: Out of memory");
1189     rc = RC_FX;
1190   } else {
1191     Query->RepLast(')');
1192     Myc.m_Rows = -1;          // To execute the query
1193     rc = Myc.ExecSQL(g, Query->GetStr());
1194     Query->Truncate(len);     // Restore query
1195   } // endif Query
1196 
1197   return (rc == RC_NF) ? RC_OK : rc;      // RC_NF is Ok
1198   } // end of WriteDB
1199 
1200 /***********************************************************************/
1201 /*  Data Base delete all routine for MYSQL access methods.             */
1202 /***********************************************************************/
DeleteDB(PGLOBAL g,int irc)1203 int TDBMYSQL::DeleteDB(PGLOBAL g, int irc)
1204   {
1205   if (irc == RC_FX)
1206     // Send the DELETE (all) command to the remote table
1207     return (SendCommand(g) == RC_FX) ? RC_FX : RC_OK;
1208   else
1209     return RC_OK;                 // Ignore
1210 
1211   } // end of DeleteDB
1212 
1213 /***********************************************************************/
1214 /*  Data Base close routine for MySQL access method.                   */
1215 /***********************************************************************/
CloseDB(PGLOBAL g)1216 void TDBMYSQL::CloseDB(PGLOBAL g)
1217   {
1218   if (Myc.Connected()) {
1219     if (Mode == MODE_INSERT) {
1220       char cmd[64];
1221       int  w;
1222       PDBUSER dup = PlgGetUser(g);
1223 
1224       dup->Step = "Enabling indexes";
1225       sprintf(cmd, "ALTER TABLE `%s` ENABLE KEYS", TableName);
1226       Myc.m_Rows = -1;      // To execute the query
1227       m_Rc = Myc.ExecSQL(g, cmd, &w);  // May fail for some engines
1228       } // endif m_Rc
1229 
1230     Myc.Close();
1231     } // endif Myc
1232 
1233   if (trace(1))
1234     htrc("MySQL CloseDB: closing %s rc=%d\n", Name, m_Rc);
1235 
1236   } // end of CloseDB
1237 
1238 // ------------------------ MYSQLCOL functions --------------------------
1239 
1240 /***********************************************************************/
1241 /*  MYSQLCOL public constructor.                                       */
1242 /***********************************************************************/
MYSQLCOL(PCOLDEF cdp,PTDB tdbp,PCOL cprec,int i,PCSZ am)1243 MYSQLCOL::MYSQLCOL(PCOLDEF cdp, PTDB tdbp, PCOL cprec, int i, PCSZ am)
1244         : COLBLK(cdp, tdbp, i)
1245   {
1246   if (cprec) {
1247     Next = cprec->GetNext();
1248     cprec->SetNext(this);
1249   } else {
1250     Next = tdbp->GetColumns();
1251     tdbp->SetColumns(this);
1252   } // endif cprec
1253 
1254   // Set additional MySQL access method information for column.
1255   Precision = Long = cdp->GetLong();
1256   Bind = NULL;
1257   To_Val = NULL;
1258   Slen = 0;
1259   Rank = -1;            // Not known yet
1260 
1261   if (trace(1))
1262     htrc(" making new %sCOL C%d %s at %p\n", am, Index, Name, this);
1263 
1264   } // end of MYSQLCOL constructor
1265 
1266 /***********************************************************************/
1267 /*  MYSQLCOL public constructor.                                       */
1268 /***********************************************************************/
MYSQLCOL(MYSQL_FIELD * fld,PTDB tdbp,int i,PCSZ am)1269 MYSQLCOL::MYSQLCOL(MYSQL_FIELD *fld, PTDB tdbp, int i, PCSZ am)
1270         : COLBLK(NULL, tdbp, i)
1271   {
1272   const char *chset = get_charset_name(fld->charsetnr);
1273 //char  v = (!strcmp(chset, "binary")) ? 'B' : 0;
1274 	char  v = 0;
1275 
1276   Name = fld->name;
1277   Opt = 0;
1278   Precision = Long = fld->length;
1279   Buf_Type = MYSQLtoPLG(fld->type, &v);
1280   strcpy(Format.Type, GetFormatType(Buf_Type));
1281   Format.Length = Long;
1282   Format.Prec = fld->decimals;
1283   ColUse = U_P;
1284   Nullable = !IS_NOT_NULL(fld->flags);
1285 
1286   // Set additional MySQL access method information for column.
1287   Bind = NULL;
1288   To_Val = NULL;
1289   Slen = 0;
1290   Rank = i;
1291 
1292   if (trace(1))
1293     htrc(" making new %sCOL C%d %s at %p\n", am, Index, Name, this);
1294 
1295   } // end of MYSQLCOL constructor
1296 
1297 /***********************************************************************/
1298 /*  MYSQLCOL constructor used for copying columns.                     */
1299 /*  tdbp is the pointer to the new table descriptor.                   */
1300 /***********************************************************************/
MYSQLCOL(MYSQLCOL * col1,PTDB tdbp)1301 MYSQLCOL::MYSQLCOL(MYSQLCOL *col1, PTDB tdbp) : COLBLK(col1, tdbp)
1302   {
1303   Long = col1->Long;
1304   Bind = NULL;
1305   To_Val = NULL;
1306   Slen = col1->Slen;
1307   Rank = col1->Rank;
1308   } // end of MYSQLCOL copy constructor
1309 
1310 /***********************************************************************/
1311 /*  FindRank: Find the rank of this column in the result set.          */
1312 /***********************************************************************/
FindRank(PGLOBAL g)1313 bool MYSQLCOL::FindRank(PGLOBAL g)
1314 {
1315   int    n;
1316   MYSQLC myc = ((PTDBMY)To_Tdb)->Myc;
1317 
1318   for (n = 0; n < myc.m_Fields; n++)
1319     if (!stricmp(Name, myc.m_Res->fields[n].name)) {
1320       Rank = n;
1321       return false;
1322       } // endif Name
1323 
1324   sprintf(g->Message, "Column %s not in result set", Name);
1325   return true;
1326 } // end of FindRank
1327 
1328 /***********************************************************************/
1329 /*  SetBuffer: prepare a column block for write operation.             */
1330 /***********************************************************************/
SetBuffer(PGLOBAL g,PVAL value,bool ok,bool check)1331 bool MYSQLCOL::SetBuffer(PGLOBAL g, PVAL value, bool ok, bool check)
1332   {
1333   if (!(To_Val = value)) {
1334     sprintf(g->Message, MSG(VALUE_ERROR), Name);
1335     return true;
1336   } else if (Buf_Type == value->GetType()) {
1337     // Values are of the (good) column type
1338     if (Buf_Type == TYPE_DATE) {
1339       // If any of the date values is formatted
1340       // output format must be set for the receiving table
1341       if (GetDomain() || ((DTVAL *)value)->IsFormatted())
1342         goto newval;          // This will make a new value;
1343 
1344     } else if (Buf_Type == TYPE_DOUBLE)
1345       // Float values must be written with the correct (column) precision
1346       // Note: maybe this should be forced by ShowValue instead of this ?
1347       value->SetPrec(GetScale());
1348 
1349     Value = value;            // Directly access the external value
1350   } else {
1351     // Values are not of the (good) column type
1352     if (check) {
1353       sprintf(g->Message, MSG(TYPE_VALUE_ERR), Name,
1354               GetTypeName(Buf_Type), GetTypeName(value->GetType()));
1355       return true;
1356       } // endif check
1357 
1358  newval:
1359     if (InitValue(g))         // Allocate the matching value block
1360       return true;
1361 
1362   } // endif's Value, Buf_Type
1363 
1364   // Because Colblk's have been made from a copy of the original TDB in
1365   // case of Update, we must reset them to point to the original one.
1366   if (To_Tdb->GetOrig())
1367     To_Tdb = (PTDB)To_Tdb->GetOrig();
1368 
1369   // Set the Column
1370   Status = (ok) ? BUF_EMPTY : BUF_NO;
1371   return false;
1372   } // end of SetBuffer
1373 
1374 /***********************************************************************/
1375 /*  InitBind: Initialize the bind structure according to type.         */
1376 /***********************************************************************/
InitBind(PGLOBAL g)1377 void MYSQLCOL::InitBind(PGLOBAL g)
1378   {
1379   PTDBMY tdbp = (PTDBMY)To_Tdb;
1380 
1381   assert(tdbp->Bind && Rank < tdbp->Nparm);
1382 
1383   Bind = &tdbp->Bind[Rank];
1384   memset(Bind, 0, sizeof(MYSQL_BIND));
1385 
1386   if (Buf_Type == TYPE_DATE) {
1387     Bind->buffer_type = PLGtoMYSQL(TYPE_STRING, false);
1388     Bind->buffer = (char *)PlugSubAlloc(g,NULL, 20);
1389     Bind->buffer_length = 20;
1390     Bind->length = &Slen;
1391   } else {
1392     Bind->buffer_type = PLGtoMYSQL(Buf_Type, false);
1393     Bind->buffer = (char *)Value->GetTo_Val();
1394     Bind->buffer_length = Value->GetClen();
1395     Bind->length = (IsTypeChar(Buf_Type)) ? &Slen : NULL;
1396   } // endif Buf_Type
1397 
1398   } // end of InitBind
1399 
1400 /***********************************************************************/
1401 /*  ReadColumn:                                                        */
1402 /***********************************************************************/
ReadColumn(PGLOBAL g)1403 void MYSQLCOL::ReadColumn(PGLOBAL g)
1404   {
1405   char  *p, *buf, tim[20];
1406   int    rc;
1407   PTDBMY tdbp = (PTDBMY)To_Tdb;
1408 
1409   /*********************************************************************/
1410   /*  If physical fetching of the line was deferred, do it now.        */
1411   /*********************************************************************/
1412   if (!tdbp->Fetched)
1413     if ((rc = tdbp->Myc.Fetch(g, tdbp->N)) != RC_OK) {
1414       if (rc == RC_EF)
1415         sprintf(g->Message, MSG(INV_DEF_READ), rc);
1416 
1417 			throw 11;
1418 		} else
1419       tdbp->Fetched = true;
1420 
1421   if ((buf = ((PTDBMY)To_Tdb)->Myc.GetCharField(Rank))) {
1422     if (trace(2))
1423       htrc("MySQL ReadColumn: name=%s buf=%s\n", Name, buf);
1424 
1425     // TODO: have a true way to differenciate temporal values
1426     if (Buf_Type == TYPE_DATE && strlen(buf) == 8)
1427       // This is a TIME value
1428       p = strcat(strcpy(tim, "1970-01-01 "), buf);
1429     else
1430       p = buf;
1431 
1432     if (Value->SetValue_char(p, strlen(p))) {
1433       sprintf(g->Message, "Out of range value for column %s at row %d",
1434               Name, tdbp->RowNumber(g));
1435       PushWarning(g, tdbp);
1436       } // endif SetValue_char
1437 
1438   } else {
1439     if (Nullable)
1440       Value->SetNull(true);
1441 
1442     Value->Reset();              // Null value
1443   } // endif buf
1444 
1445   } // end of ReadColumn
1446 
1447 /***********************************************************************/
1448 /*  WriteColumn: make sure the bind buffer is updated.                 */
1449 /***********************************************************************/
WriteColumn(PGLOBAL)1450 void MYSQLCOL::WriteColumn(PGLOBAL)
1451   {
1452   /*********************************************************************/
1453   /*  Do convert the column value if necessary.                        */
1454   /*********************************************************************/
1455   if (Value != To_Val)
1456     Value->SetValue_pval(To_Val, false);   // Convert the inserted value
1457 
1458 #if defined(MYSQL_PREPARED_STATEMENTS)
1459   if (((PTDBMY)To_Tdb)->Prep) {
1460     if (Buf_Type == TYPE_DATE) {
1461       Value->ShowValue((char *)Bind->buffer, (int)Bind->buffer_length);
1462       Slen = strlen((char *)Bind->buffer);
1463     } else if (IsTypeChar(Buf_Type))
1464       Slen = strlen(Value->GetCharValue());
1465 
1466     } // endif Prep
1467 #endif   // MYSQL_PREPARED_STATEMENTS
1468 
1469   } // end of WriteColumn
1470 
1471 /* ------------------------------------------------------------------- */
1472 
1473 /***********************************************************************/
1474 /*  Implementation of the TDBMYEXC class.                              */
1475 /***********************************************************************/
TDBMYEXC(PMYDEF tdp)1476 TDBMYEXC::TDBMYEXC(PMYDEF tdp) : TDBMYSQL(tdp)
1477 {
1478   Cmdlist = NULL;
1479   Cmdcol = NULL;
1480   Shw = false;
1481   Havew = false;
1482   Isw = false;
1483   Warnings = 0;
1484   Mxr = tdp->Maxerr;
1485   Nerr = 0;
1486 } // end of TDBMYEXC constructor
1487 
TDBMYEXC(PTDBMYX tdbp)1488 TDBMYEXC::TDBMYEXC(PTDBMYX tdbp) : TDBMYSQL(tdbp)
1489 {
1490   Cmdlist = tdbp->Cmdlist;
1491   Cmdcol = tdbp->Cmdcol;
1492   Shw = tdbp->Shw;
1493   Havew = tdbp->Havew;
1494   Isw = tdbp->Isw;
1495   Mxr = tdbp->Mxr;
1496   Nerr = tdbp->Nerr;
1497 } // end of TDBMYEXC copy constructor
1498 
1499 // Is this really useful ???
Clone(PTABS t)1500 PTDB TDBMYEXC::Clone(PTABS t)
1501   {
1502   PTDB    tp;
1503   PCOL    cp1, cp2;
1504   PGLOBAL g = t->G;
1505 
1506   tp = new(g) TDBMYEXC(this);
1507 
1508   for (cp1 = Columns; cp1; cp1 = cp1->GetNext()) {
1509     cp2 = new(g) MYXCOL((PMYXCOL)cp1, tp);
1510 
1511     NewPointer(t, cp1, cp2);
1512     } // endfor cp1
1513 
1514   return tp;
1515   } // end of Clone
1516 
1517 /***********************************************************************/
1518 /*  Allocate MYSQL column description block.                           */
1519 /***********************************************************************/
MakeCol(PGLOBAL g,PCOLDEF cdp,PCOL cprec,int n)1520 PCOL TDBMYEXC::MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n)
1521   {
1522   PMYXCOL colp = new(g) MYXCOL(cdp, this, cprec, n);
1523 
1524   if (!colp->Flag)
1525     Cmdcol = colp->GetName();
1526 
1527   return colp;
1528   } // end of MakeCol
1529 
1530 /***********************************************************************/
1531 /*  MakeCMD: make the SQL statement to send to MYSQL connection.       */
1532 /***********************************************************************/
MakeCMD(PGLOBAL g)1533 PCMD TDBMYEXC::MakeCMD(PGLOBAL g)
1534   {
1535   PCMD xcmd = NULL;
1536 
1537   if (To_CondFil) {
1538     if (Cmdcol) {
1539       if (!stricmp(Cmdcol, To_CondFil->Body) &&
1540           (To_CondFil->Op == OP_EQ || To_CondFil->Op == OP_IN)) {
1541         xcmd = To_CondFil->Cmds;
1542       } else
1543         strcpy(g->Message, "Invalid command specification filter");
1544 
1545     } else
1546       strcpy(g->Message, "No command column in select list");
1547 
1548   } else if (!Srcdef)
1549     strcpy(g->Message, "No Srcdef default command");
1550   else
1551     xcmd = new(g) CMD(g, Srcdef);
1552 
1553   return xcmd;
1554   } // end of MakeCMD
1555 
1556 /***********************************************************************/
1557 /*  EXC GetMaxSize: returns the maximum number of rows in the table.   */
1558 /***********************************************************************/
GetMaxSize(PGLOBAL)1559 int TDBMYEXC::GetMaxSize(PGLOBAL)
1560   {
1561   if (MaxSize < 0) {
1562     MaxSize = 10;                 // a guess
1563     } // endif MaxSize
1564 
1565   return MaxSize;
1566   } // end of GetMaxSize
1567 
1568 /***********************************************************************/
1569 /*  MySQL Exec Access Method opening routine.                          */
1570 /***********************************************************************/
OpenDB(PGLOBAL g)1571 bool TDBMYEXC::OpenDB(PGLOBAL g)
1572   {
1573   if (Use == USE_OPEN) {
1574     strcpy(g->Message, "Multiple execution is not allowed");
1575     return true;
1576     } // endif use
1577 
1578   /*********************************************************************/
1579   /*  Open a MySQL connection for this table.                          */
1580   /*  Note: this may not be the proper way to do. Perhaps it is better */
1581   /*  to test whether a connection is already open for this server     */
1582   /*  and if so to allocate just a new result set. But this only for   */
1583   /*  servers allowing concurency in getting results ???               */
1584   /*********************************************************************/
1585   if (!Myc.Connected())
1586     if (Myc.Open(g, Host, Schema, User, Pwd, Port))
1587       return true;
1588 
1589   Use = USE_OPEN;       // Do it now in case we are recursively called
1590 
1591   if (Mode != MODE_READ && Mode != MODE_READX) {
1592     strcpy(g->Message, "No INSERT/DELETE/UPDATE of MYSQL EXEC tables");
1593     return true;
1594     } // endif Mode
1595 
1596   /*********************************************************************/
1597   /*  Get the command to execute.                                      */
1598   /*********************************************************************/
1599   if (!(Cmdlist = MakeCMD(g))) {
1600 		// Next lines commented out because of CHECK TABLE
1601 		//Myc.Close();
1602     //return true;
1603     } // endif Cmdlist
1604 
1605   return false;
1606   } // end of OpenDB
1607 
1608 /***********************************************************************/
1609 /*  Data Base read routine for MYSQL access method.                    */
1610 /***********************************************************************/
ReadDB(PGLOBAL g)1611 int TDBMYEXC::ReadDB(PGLOBAL g)
1612   {
1613   if (Havew) {
1614     // Process result set from SHOW WARNINGS
1615     if (Myc.Fetch(g, -1) != RC_OK) {
1616       Myc.FreeResult();
1617       Havew = Isw = false;
1618     } else {
1619       N++;
1620       Isw = true;
1621       return RC_OK;
1622     } // endif Fetch
1623 
1624     } // endif m_Res
1625 
1626   if (Cmdlist) {
1627     // Process query to send
1628     int rc;
1629 
1630     do {
1631       if (Query)
1632         Query->Set(Cmdlist->Cmd);
1633       else
1634         Query = new(g) STRING(g, 0, Cmdlist->Cmd);
1635 
1636       switch (rc = Myc.ExecSQLcmd(g, Query->GetStr(), &Warnings)) {
1637         case RC_NF:
1638           AftRows = Myc.m_Afrw;
1639           strcpy(g->Message, "Affected rows");
1640           break;
1641         case RC_OK:
1642           AftRows = Myc.m_Fields;
1643           strcpy(g->Message, "Result set columns");
1644           break;
1645         case RC_FX:
1646           AftRows = Myc.m_Afrw;
1647           Nerr++;
1648           break;
1649         case RC_INFO:
1650           Shw = true;
1651         } // endswitch rc
1652 
1653       Cmdlist = (Nerr > Mxr) ? NULL : Cmdlist->Next;
1654       } while (rc == RC_INFO);
1655 
1656     if (Shw && Warnings)
1657       Havew = (Myc.ExecSQL(g, "SHOW WARNINGS") == RC_OK);
1658 
1659     ++N;
1660     return RC_OK;
1661 	} else {
1662 		PushWarning(g, this, 1);
1663 		return RC_EF;
1664 	}	// endif Cmdlist
1665 
1666   } // end of ReadDB
1667 
1668 /***********************************************************************/
1669 /*  WriteDB: Data Base write routine for Exec MYSQL access methods.    */
1670 /***********************************************************************/
WriteDB(PGLOBAL g)1671 int TDBMYEXC::WriteDB(PGLOBAL g)
1672   {
1673   strcpy(g->Message, "EXEC MYSQL tables are read only");
1674   return RC_FX;
1675   } // end of WriteDB
1676 
1677 // ------------------------- MYXCOL functions ---------------------------
1678 
1679 /***********************************************************************/
1680 /*  MYXCOL public constructor.                                         */
1681 /***********************************************************************/
MYXCOL(PCOLDEF cdp,PTDB tdbp,PCOL cprec,int i,PCSZ am)1682 MYXCOL::MYXCOL(PCOLDEF cdp, PTDB tdbp, PCOL cprec, int i, PCSZ am)
1683       : MYSQLCOL(cdp, tdbp, cprec, i, am)
1684   {
1685   // Set additional EXEC MYSQL access method information for column.
1686   Flag = cdp->GetOffset();
1687   } // end of MYSQLCOL constructor
1688 
1689 /***********************************************************************/
1690 /*  MYSQLCOL public constructor.                                       */
1691 /***********************************************************************/
MYXCOL(MYSQL_FIELD * fld,PTDB tdbp,int i,PCSZ am)1692 MYXCOL::MYXCOL(MYSQL_FIELD *fld, PTDB tdbp, int i, PCSZ am)
1693       : MYSQLCOL(fld, tdbp, i, am)
1694   {
1695   if (trace(1))
1696     htrc(" making new %sCOL C%d %s at %p\n", am, Index, Name, this);
1697 
1698   } // end of MYSQLCOL constructor
1699 
1700 /***********************************************************************/
1701 /*  MYXCOL constructor used for copying columns.                       */
1702 /*  tdbp is the pointer to the new table descriptor.                   */
1703 /***********************************************************************/
MYXCOL(MYXCOL * col1,PTDB tdbp)1704 MYXCOL::MYXCOL(MYXCOL *col1, PTDB tdbp) : MYSQLCOL(col1, tdbp)
1705   {
1706   Flag = col1->Flag;
1707   } // end of MYXCOL copy constructor
1708 
1709 /***********************************************************************/
1710 /*  ReadColumn:                                                        */
1711 /***********************************************************************/
ReadColumn(PGLOBAL g)1712 void MYXCOL::ReadColumn(PGLOBAL g)
1713   {
1714   PTDBMYX tdbp = (PTDBMYX)To_Tdb;
1715 
1716   if (tdbp->Isw) {
1717     char *buf = NULL;
1718 
1719     if (Flag < 3) {
1720       buf = tdbp->Myc.GetCharField(Flag);
1721       Value->SetValue_psz(buf);
1722     } else
1723       Value->Reset();
1724 
1725   } else
1726     switch (Flag) {
1727       case  0: Value->SetValue_psz(tdbp->Query->GetStr()); break;
1728       case  1: Value->SetValue(tdbp->AftRows);             break;
1729       case  2: Value->SetValue_psz(g->Message);            break;
1730       case  3: Value->SetValue(tdbp->Warnings);            break;
1731       default: Value->SetValue_psz("Invalid Flag");        break;
1732       } // endswitch Flag
1733 
1734   } // end of ReadColumn
1735 
1736 /***********************************************************************/
1737 /*  WriteColumn: should never be called.                               */
1738 /***********************************************************************/
WriteColumn(PGLOBAL)1739 void MYXCOL::WriteColumn(PGLOBAL)
1740   {
1741   assert(false);
1742   } // end of WriteColumn
1743 
1744 /* ---------------------------TDBMCL class --------------------------- */
1745 
1746 /***********************************************************************/
1747 /*  TDBMCL class constructor.                                          */
1748 /***********************************************************************/
TDBMCL(PMYDEF tdp)1749 TDBMCL::TDBMCL(PMYDEF tdp) : TDBCAT(tdp)
1750   {
1751   Host = tdp->Hostname;
1752   Db   = tdp->Tabschema;
1753   Tab  = tdp->Tabname;
1754   User = tdp->Username;
1755   Pwd  = tdp->Password;
1756   Port = tdp->Portnumber;
1757   } // end of TDBMCL constructor
1758 
1759 /***********************************************************************/
1760 /*  GetResult: Get the list the MYSQL table columns.                   */
1761 /***********************************************************************/
GetResult(PGLOBAL g)1762 PQRYRES TDBMCL::GetResult(PGLOBAL g)
1763   {
1764   return MyColumns(g, NULL, Host, Db, User, Pwd, Tab, NULL, Port, false);
1765   } // end of GetResult
1766