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